diff --git a/TODO_NEXT_STEPS.txt b/TODO_NEXT_STEPS.txt
index 09208a8..3764722 100644
--- a/TODO_NEXT_STEPS.txt
+++ b/TODO_NEXT_STEPS.txt
@@ -1,4 +1,2078 @@
+================================================================================
+ Production Version Truth Guard (2026-06-27) [ACTIVE]
+================================================================================
+
+正式環境 `/health` 是最新版本真相;local/Gitea/iCloud 只能作為 source candidate。
+開工、部署前、handoff 或任何版本宣稱前先跑:
+
+ python scripts/ops/check_production_version_truth.py
+
+目前 protected baseline:production `https://mo.wooo.work/health` = `V10.725`、
+clean workspace `/Users/ogt/codex-workspaces/ewoooc-dev` = `V10.725`、
+origin/main = `f3e412c`。舊 iCloud checkout 的 local-only `V10.726` 不是正式版本。
+
+================================================================================
+ 產品完整工作優先順序鎖 (2026-06-29) [ACTIVE]
+================================================================================
+
+完整工作目標不是單一 API,而是把正式產品做成「PChome 業績成長自動化作戰系統」:
+AI 自動化要能自己找缺口、補證據、產生候選、形成可驗證決策與可回滾落地包;
+產品網站要像主流專業 SaaS 一樣,首屏直接呈現狀態、價值、下一步與可信證據。
+
+P0. 正式版本真相與正式缺口盤點
+ - production `/health`、mapping backlog、業績日、資料新鮮度與缺口數必須先確認。
+ - 不得把本機、iCloud、未部署 source-ready 誤認為正式最新版本。
+
+P1. AI 自動化營收閉環:先補 PChome 商品對應
+ - 優先處理 `needs_mapping_count=16` 中的 direct mapping 缺口。
+ - 目標是自動產生 MOMO 搜尋詞、候選包、身份錨點、接受門檻與 no-write receipt。
+ - 不退回人工逐筆找商品;人工只處理 exception / machine-verifiable decision。
+
+P2. AI 自動化候選確認與證據閉環
+ - review candidate 要轉成 decision envelope / candidate decision package。
+ - source-ready: direct mapping candidate decision package 會把 read-only 搜尋候選轉成 P2 decision envelope,分流到 no-write receipt 或 exception-only machine review。
+ - source-ready: `/api/ai/pchome-growth/ai-automation-readiness` 已聚合缺口偵測、同款搜尋包、候選決策包、證據收據與受控落地,並明確回傳 `primary_human_gate_count=0`、`automation_policy.primary_flow=ai_controlled`;例外也必須進 AI machine-verifiable auto-resolution。
+ - image / availability / price / unit basis 要用 controlled fetch、parser、merge receipt 自動補齊。
+ - 所有 AI Agent 要保留 guardrails、confidence、trace、資料品質與可回滾下一步。
+
+P3. 主流專業產品網站與營運 UI
+ - 首頁 / Dashboard 必須是「業績成長指揮台」,直接顯示缺口、價值、下一步、可執行 package。
+ - source-ready: 首頁 command center 已加入 AI 自動化作戰流水線,顯示同款搜尋包、候選決策包、證據收據與受控落地狀態。
+ - UI 不得外露 raw API、endpoint、DB 欄位、模型名、工程錯誤;要用營運語言。
+ - 商品列必須有商品圖、平台 ID、賣場連結、價格證據、可信度與下一步。
+ - 參考外部專業做法:Google Merchant / Product structured data / Baymard product comparison UX。
+
+P4. 外部來源與資料治理
+ - MOMO 是已接入來源;Shopee、Lazada、Amazon、Google Merchant / Shopping、TikTok Shop、LINE 購物、Rakuten、Yahoo、露天、品牌官網 / Shopify、Meta Commerce、Coupang 必須至少有來源契約或待接入狀態。
+ - 外部資料必須正規化成 offer evidence:平台商品 ID、商品名、賣場連結、圖片、價格、庫存、時間、可信度。
+ - source-ready: growth API 的 source_scope 會從 source readiness 自動列出完整 paused sources;source readiness 已回傳 offer evidence contract,包含商品 ID、名稱、連結、圖片、價格、庫存、時間與可信度。
+
+P5. no-write persistence / verifier / apply readiness
+ - 只有在 P1-P4 的產品缺口與證據閉環穩定後,才推 no-write persistence、verifier、apply readiness。
+ - 綠燈只代表 ready for controlled apply,不代表已部署或已寫正式 DB。
+
+P6. DB apply authorization / signing / closeout
+ - 這是最後的受控 apply 安全鏈,不得搶在 P1-P4 前面。
+ - 預設不讀 secret、不簽發 authorization、不執行 SQL、不寫 DB,直到 controlled apply 條件完整。
+
+================================================================================
+ PChome Mapping Backlog Read-Only Report (2026-06-28) [ACTIVE]
+================================================================================
+
+正式 PChome growth API 目前顯示 top 20 商品中 mapped_count=4、needs_mapping_count=16、
+mapping_rate=20.0%;下一個 P0 是先補商品對應,再確認已找到 MOMO 候選的覆核項。
+開工順序:
+
+ python scripts/ops/check_production_version_truth.py
+ python scripts/ops/report_pchome_mapping_backlog.py --json
+
+同一口徑已抽到 `services/pchome_mapping_backlog_service.py`,並提供登入後只讀 API
+`/api/ai/pchome-growth/mapping-backlog` 與只讀 operator preview
+`/api/ai/pchome-growth/mapping-backlog/operator-preview`,以及只讀 evidence enrichment
+preview `/api/ai/pchome-growth/mapping-backlog/evidence-enrichment-preview`、只讀 evidence
+source preview `/api/ai/pchome-growth/mapping-backlog/evidence-source-preview`。此 reporter /
+API 只做 GET readback / run package preview / evidence task preview / source wiring preview,
+不執行 backfill POST、不抓外站、不寫 DB、不派 Telegram。Operator
+preview 已把 Google Merchant Center product data、Google Product / Merchant structured
+data、Baymard ecommerce product/search UX 轉成 evidence checklist,並加入 Ollama-first
+AI automation plan;每個 mapping target 也會輸出 `evidence_completeness`(product URL、
+present/missing fields、blocking missing fields、auto_accept_ready、ai_exception_required、
+本地 deterministic `unit_package_basis` parser preview)。production-derived evidence preview
+目前剩餘主要缺口是 image / availability,另有 1 筆 review candidate price payload 缺口;
+source preview 已列出 image / availability 的 future GET fetch gate 與 price payload mapping
+probe,並完成 check-mode HTML fixture parser,可解析 JSON-LD `Product.image`、
+`Offer.availability` 與 `og:image` fallback;preview/parser 不呼叫 LLM、不允許 Gemini、
+不寫 DB。Controlled read-only fetch gate 已接上
+`/api/ai/pchome-growth/mapping-backlog/evidence-fetch-gate`,預設只產生 planned receipts;
+`execute_fetch=1` 才對 allowlist domain `24h.pchome.com.tw` 做最多 12 筆 GET,含 timeout、
+512KB HTML cap 與 no-write receipt。本機 production-derived smoke 已以 1 筆 GET 讀到
+`Product.image` 與 `Offer.availability=InStock`,仍不寫 DB。Evidence merge preview 已接上
+`/api/ai/pchome-growth/mapping-backlog/evidence-merge-preview`;production-derived 1 筆 smoke
+已讓 `DDBH3K-A900K0KWV-002` 的 `image/availability` 從 missing fields 移除,summary
+`merge_ready_count=1`、`writes_database_count=0`。自動化策略已改為 automation-first:
+review candidate 的 `pchome_price` 納入 price evidence;production-derived batch=12 已達
+`AUTO_ACCEPT_EVIDENCE_MERGE=10/10`、`ai_exception_required_count=0`、
+`remaining_blocker_count=0`、`writes_database_count=0`。下一個 P0 是 auto-policy receipt
+persistence gate / no-write UI,而不是人工逐筆審核。Auto-policy receipt gate 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-receipt-gate`,Dashboard 也顯示 no-write
+自動收據指標;production-derived batch=12 已達 `READY_FOR_AUTO_PERSISTENCE=10/10`、
+`persists_receipt_count=0`、`writes_database_count=0`、`ai_exception_required_count=0`。
+Controlled persistence dry-run gate 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-persistence-gate`,只接受 auto-policy
+receipt gate 的 ready receipts,輸出 idempotency key、payload hash、schema migration
+contract、transaction preview、rollback plan 與 post-write verifier;目前仍是 no-write /
+dry-run-only。production-derived batch=12 smoke 已達 `PERSISTENCE_DRY_RUN_READY`、
+`dry_run_ready_count=10/10`、`ai_exception_required_count=0`、`writes_database_count=0`、
+`persists_receipt_count=0`。Schema migration preview gate 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-schema-migration-preview`,輸出
+`external_offer_evidence_receipts` DDL preview、rollback preview、prewrite snapshot
+contract、future apply verifier 與 exception-only failure routing;目前仍是 no-write /
+no-SQL-execute preview。production-derived batch=12 smoke 已達
+`SCHEMA_MIGRATION_PREVIEW_READY`、`dry_run_ready_count=10/10`、`schema_statement_count=13`、
+`future_verifier_count=5`、`current_preview_apply_allowed=false`、
+`executes_migration_count=0`、`writes_database_count=0`。Migration file preview gate 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-preview`,輸出
+`migrations/045_pchome_auto_policy_evidence_receipts.sql` forward SQL preview、forward hash、
+rollback preview、future apply endpoint request contract、preflight sequence、abort
+conditions 與 rollback contract;目前仍是 no-file-write / no-endpoint-execute /
+no-SQL-execute preview。production-derived batch=12 smoke 已達
+`MIGRATION_FILE_PREVIEW_READY`、`dry_run_ready_count=10/10`、
+`migration_file_line_count=31`、`apply_endpoint_contract_ready_count=1`、
+`forbidden_forward_tokens_absent=true`、`writes_file_count=0`、
+`executes_endpoint_count=0`、`writes_database_count=0`。下一個 P0 是真正 migration file
+生成前的 apply-readiness closeout,不是人工逐筆 UI。Apply-readiness closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-apply-readiness-closeout`,輸出 9 項
+readiness checks、4 項 future-apply blockers、migration file hash summary 與 apply endpoint
+summary;closeout ready 只代表可提出 migration file generation request,不代表 DB apply。
+production-derived batch=12 smoke 已達 `APPLY_READINESS_CLOSEOUT_READY`、
+`readiness_pass_count=9/9`、`current_preview_ready_count=1`、
+`ready_for_migration_file_generation_request=true`、`ready_for_database_apply=false`、
+`future_apply_blocker_count=4`、`writes_file_count=0`、`executes_endpoint_count=0`、
+`writes_database_count=0`。目前仍是 no-file-write / no-endpoint-execute /
+no-SQL-execute preview。Migration file generation request 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-generation-request`,
+打包 request id、目標檔 `migrations/045_pchome_auto_policy_evidence_receipts.sql`、
+expected SHA256、4 項 required artifacts 與 3 步 file generation plan;request API 本身
+仍不寫檔、不執行 endpoint、不寫 DB。migration file 已在本地 repo 生成,hash 與 request
+expected SHA256 對齊。production-derived batch=12 smoke 已達
+`FILE_GENERATION_REQUEST_READY`、`request_ready_count=1`、`dry_run_ready_count=10/10`、
+`required_artifact_count=4`、`file_generation_step_count=3`、
+`ready_to_generate_file=true`、`ready_for_database_apply=false`、`writes_file_count=0`、
+`executes_endpoint_count=0`、`writes_database_count=0`、`generated_file_exists=true`、
+`generated_file_hash_matches_request=true`。下一個 P0 是 migration apply gate preview /
+readiness,只讀檢查 `045` migration 檔、hash、production truth、prewrite snapshot contract
+與 post-apply verifier;不是直接 DB apply。Migration apply gate preview 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-apply-gate-preview`,讀本地
+`045` migration 檔並比對 request hash、additive-only、future apply endpoint contract、
+prewrite snapshot requirement 與 post-apply verifier requirement;目前仍是 no-SQL-execute /
+no-DB-write preview。production-derived batch=12 smoke 已達
+`MIGRATION_APPLY_GATE_PREVIEW_READY`、`apply_gate_pass_count=9/9`、
+`apply_preview_ready_count=1`、`generated_file_hash_matches_count=1`、
+`ready_for_explicit_db_apply_request=true`、`ready_for_database_apply_now=false`、
+`future_apply_blocker_count=3`、`executes_migration_count=0`、`writes_database_count=0`。
+Explicit DB apply request gate preview 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-request-gate-preview`,輸出
+DB apply request id、future `psql "$DATABASE_URL" ...` command preview、5 項 required
+artifacts、5 步 apply sequence、abort conditions、runtime readback 與 rollback gate preview;
+目前仍是 no-secret-read / no-SQL-execute / no-DB-write preview。production-derived
+batch=12 smoke 已達 `DB_APPLY_REQUEST_GATE_READY`、`request_ready_count=1`、
+`required_artifact_count=5`、`apply_sequence_step_count=5`、`abort_condition_count=6`、
+`generated_file_hash_matches_count=1`、`ready_for_explicit_db_apply_request=true`、
+`ready_for_database_apply_now=false`、`reads_secret_in_preview=false`、
+`executes_migration_count=0`、`writes_database_count=0`。DB apply execution preflight 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-execution-preflight`,輸出
+prewrite snapshot plan、post-apply readback plan、rollback artifact plan、required artifacts
+與 abort conditions;目前仍是 no-secret-read / no-SQL-execute / no-DB-write preview。
+production-derived batch=12 smoke 已達 `DB_APPLY_EXECUTION_PREFLIGHT_READY`、
+`preflight_ready_count=1`、`request_ready_count=1`、`required_artifact_count=6`、
+`snapshot_plan_count=5`、`readback_plan_count=6`、`rollback_artifact_count=1`、
+`generated_file_hash_matches_count=1`、`ready_for_preflight_artifact_generation=true`、
+`ready_for_database_apply_now=false`、`reads_secret_in_preview=false`、
+`executes_sql_count=0`、`executes_migration_count=0`、`writes_database_count=0`。DB apply
+authorization package 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-package`,輸出
+authorization checks、freshness requirements、machine apply manifest 與 verifier bundle;
+目前仍是 no-secret-read / no-SQL-execute / no-DB-write preview。production-derived
+batch=12 smoke 已達 `DB_APPLY_AUTHORIZATION_PACKAGE_READY`、
+`authorization_check_count=11`、`authorization_pass_count=11`、
+`authorization_waiting_count=0`、`authorization_package_ready_count=1`、
+`freshness_requirement_count=5`、`manifest_step_count=6`、`verifier_bundle_count=3`、
+`ready_for_explicit_apply_authorization_request=true`、`ready_for_database_apply_now=false`、
+`reads_secret_in_preview=false`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。DB apply verifier artifact preview 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview`,輸出
+prewrite snapshot / post-apply readback / rollback artifact schema、generation plan 與
+verifier manifest;目前仍是 no-file-write / no-secret-read / no-SQL-execute /
+no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_VERIFIER_ARTIFACT_PREVIEW_READY`、`artifact_preview_ready_count=1`、
+`authorization_package_ready_count=1`、`artifact_schema_count=3`、
+`artifact_generation_step_count=5`、`verifier_check_count=15`、`writes_artifact_count=0`、
+`ready_for_future_artifact_generation=true`、`ready_to_write_artifacts_now=false`、
+`ready_for_database_apply_now=false`、`reads_secret_in_preview=false`、
+`executes_sql_count=0`、`executes_migration_count=0`、`writes_database_count=0`。DB apply
+final handoff package 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-final-handoff-package`,輸出
+final handoff sections、future runbook、command previews、abort gates 與 source proof
+manifest;目前仍是 no-file-write / no-secret-read / no-SQL-execute / no-DB-write
+preview。production-derived batch=12 smoke 已達 `DB_APPLY_FINAL_HANDOFF_PACKAGE_READY`、
+`final_handoff_ready_count=1`、`artifact_preview_ready_count=1`、
+`handoff_section_count=6`、`final_runbook_step_count=7`、`command_preview_count=3`、
+`abort_gate_count=10`、`source_endpoint_count=4`、
+`ready_for_explicit_db_apply_handoff=true`、`ready_for_database_apply_now=false`、
+`writes_artifact_count=0`、`reads_secret_in_preview=false`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。DB apply controlled dry-run shell
+preview 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-preview`,
+輸出 shell phases、script preview、check-mode contract 與 rollback hooks;目前仍是
+no-script-write / no-secret-read / no-shell-execute / no-SQL-execute / no-DB-write preview。
+production-derived batch=12 smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_SHELL_PREVIEW_READY`、
+`dry_run_shell_preview_ready_count=1`、`final_handoff_ready_count=1`、
+`shell_phase_count=9`、`shell_script_line_count=10`、
+`check_mode_required_check_count=6`、`rollback_hook_count=3`、
+`writes_script_count=0`、`executes_script_count=0`、
+`ready_for_future_shell_script_generation=true`、`ready_to_write_script_now=false`、
+`ready_to_execute_shell_now=false`、`ready_for_database_apply_now=false`、
+`reads_secret_in_preview=false`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。DB apply controlled dry-run shell closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-closeout`,
+輸出 closeout checks、future apply boundaries 與 explicit authorization boundary;目前仍是
+no-script-write / no-secret-read / no-shell-execute / no-SQL-execute / no-DB-write preview。
+production-derived batch=12 smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_SHELL_CLOSEOUT_READY`、`closeout_ready_count=1`、
+`closeout_check_count=13`、`closeout_pass_count=13`、`closeout_waiting_count=0`、
+`dry_run_shell_preview_ready_count=1`、`future_apply_boundary_count=6`、
+`ready_for_explicit_apply_authorization_boundary=true`、`ready_for_database_apply_now=false`、
+`writes_script_count=0`、`executes_script_count=0`、`reads_secret_in_preview=false`、
+`executes_sql_count=0`、`executes_migration_count=0`、`writes_database_count=0`。DB apply
+authorization request intake 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-intake`,
+輸出 authorization request intake、request payload schema、acceptance gates、
+required request evidence 與 rejection reasons;目前仍是 request-intake-only /
+no-secret-read / no-shell-execute / no-SQL-execute / no-DB-write preview。production-derived
+batch=12 smoke 已達 `DB_APPLY_AUTHORIZATION_REQUEST_INTAKE_READY`、
+`authorization_request_intake_ready_count=1`、`required_request_evidence_count=7`、
+`request_payload_required_field_count=10`、`authorization_acceptance_gate_count=11`、
+`authorization_acceptance_pass_count=11`、`authorization_acceptance_waiting_count=0`、
+`rejection_reason_count=10`、`closeout_ready_count=1`、`future_apply_boundary_count=6`、
+`accepts_authorization_request=true`、`issues_database_apply_authorization=false`、
+`ready_for_database_apply_now=false`、`reads_secret_in_preview=false`、
+`executes_script_count=0`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。DB apply authorization request closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-closeout`,
+輸出 final exact request package、machine request manifest 與 closeout checks;目前仍是
+final-request-package-only / no-secret-read / no-shell-execute / no-SQL-execute /
+no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_REQUEST_CLOSEOUT_READY`、
+`authorization_request_closeout_ready_count=1`、`closeout_check_count=12`、
+`closeout_pass_count=12`、`closeout_waiting_count=0`、
+`authorization_request_intake_ready_count=1`、`exact_request_payload_field_count=10`、
+`machine_request_manifest_step_count=6`、`required_request_evidence_count=7`、
+`authorization_acceptance_gate_count=11`、`rejection_reason_count=10`、
+`ready_for_exact_authorization_request_package=true`、
+`issues_database_apply_authorization=false`、`ready_for_database_apply_now=false`、
+`reads_secret_in_preview=false`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。下一個 P0 是 future DB apply
+authorization lane guard,仍不是直接 DB apply。DB apply authorization lane guard 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-lane-guard`,
+輸出 future authorization lane guard、lane transfer contract、lane entry requirements
+與 lane guard checks;目前仍是 lane-guard-only / no-secret-read / no-shell-execute /
+no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_LANE_GUARD_READY`、`authorization_lane_guard_ready_count=1`、
+`lane_guard_check_count=12`、`lane_guard_pass_count=12`、`lane_guard_waiting_count=0`、
+`authorization_request_closeout_ready_count=1`、`exact_request_payload_field_count=10`、
+`machine_request_manifest_step_count=6`、`lane_entry_requirement_count=6`、
+`required_request_evidence_count=7`、`authorization_acceptance_gate_count=11`、
+`rejection_reason_count=10`、`ready_for_future_authorization_lane_entry=true`、
+`issues_database_apply_authorization=false`、`ready_for_database_apply_now=false`、
+`reads_secret_in_preview=false`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。下一個 P0 是 future DB apply
+authorization decision preflight,仍不是直接 DB apply。DB apply authorization decision
+preflight 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-preflight`,
+輸出 future authorization decision preflight、decision input requirements、decision rejection
+policy 與 preflight checks;目前仍是 decision-preflight-only / no-secret-read /
+no-shell-execute / no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke
+已達 `DB_APPLY_AUTHORIZATION_DECISION_PREFLIGHT_READY`、
+`authorization_decision_preflight_ready_count=1`、`decision_preflight_check_count=12`、
+`decision_preflight_pass_count=12`、`decision_preflight_waiting_count=0`、
+`authorization_lane_guard_ready_count=1`、`decision_input_requirement_count=8`、
+`decision_rejection_reason_count=10`、`lane_entry_requirement_count=6`、
+`exact_request_payload_field_count=10`、`machine_request_manifest_step_count=6`、
+`allows_authorization_decision_in_future_lane=true`、
+`issues_database_apply_authorization=false`、`ready_for_database_apply_now=false`、
+`reads_secret_in_preview=false`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。DB apply authorization decision
+closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-closeout`,
+輸出 future authorization decision closeout、future authorization decision package、
+decision closeout contract 與 closeout checks;目前仍是 decision-closeout-only /
+no-secret-read / no-shell-execute / no-SQL-execute / no-DB-write preview。production-derived
+batch=12 smoke 已達 `DB_APPLY_AUTHORIZATION_DECISION_CLOSEOUT_READY`、
+`authorization_decision_closeout_ready_count=1`、`decision_closeout_check_count=12`、
+`decision_closeout_pass_count=12`、`decision_closeout_waiting_count=0`、
+`authorization_decision_preflight_ready_count=1`、`decision_input_requirement_count=8`、
+`decision_rejection_reason_count=10`、`post_apply_verifier_required_count=1`、
+`same_run_truth_required_count=1`、`permits_future_authorization_decision_lane=true`、
+`issues_database_apply_authorization=false`、`ready_for_database_apply_now=false`、
+`reads_secret_in_preview=false`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。DB apply authorization issuer
+gate 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-issuer-gate`,
+輸出 future authorization issuer gate、final nonsecret authorization envelope、issuer gate
+contract 與 issuer gate checks;目前仍是 issuer-gate-only / nonsecret-envelope-only /
+no-secret-read / no-shell-execute / no-SQL-execute / no-DB-write preview。production-derived
+batch=12 smoke 已達 `DB_APPLY_AUTHORIZATION_ISSUER_GATE_READY`、
+`authorization_issuer_gate_ready_count=1`、`issuer_gate_check_count=12`、
+`issuer_gate_pass_count=12`、`issuer_gate_waiting_count=0`、
+`authorization_decision_closeout_ready_count=1`、`decision_closeout_check_count=12`、
+`required_issuer_evidence_count=9`、`nonsecret_authorization_claim_count=8`、
+`post_apply_verifier_required_count=1`、`same_run_truth_required_count=1`、
+`permits_future_authorization_issuer_lane=true`、`issues_database_apply_authorization=false`、
+`signs_database_apply_authorization=false`、`ready_for_database_apply_now=false`、
+`secret_material_included=false`、`reads_secret_in_preview=false`、
+`executes_script_count=0`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。DB apply authorization signing-decision preflight 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-preflight`,
+輸出 future authorization signing decision preflight、signing decision preflight envelope、
+signing decision input requirements、rejection policy 與 preflight checks;目前仍是
+signing-decision-preflight-only / no-secret-read / no-signing / no-shell-execute /
+no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_SIGNING_DECISION_PREFLIGHT_READY`、
+`authorization_signing_decision_preflight_ready_count=1`、
+`signing_decision_preflight_check_count=12`、`signing_decision_preflight_pass_count=12`、
+`signing_decision_preflight_waiting_count=0`、`authorization_issuer_gate_ready_count=1`、
+`issuer_gate_check_count=12`、`required_issuer_evidence_count=9`、
+`nonsecret_authorization_claim_count=8`、`signing_decision_input_requirement_count=10`、
+`signing_decision_rejection_reason_count=11`、
+`allows_future_authorization_signing_decision_lane=true`、
+`issues_database_apply_authorization=false`、`signs_database_apply_authorization=false`、
+`ready_for_database_apply_now=false`、`secret_material_required_in_preview=false`、
+`reads_secret_in_preview=false`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。DB apply authorization
+signing-decision closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-closeout`,
+輸出 future authorization signing decision closeout、unsigned signing decision package、
+signing decision closeout contract 與 closeout checks;目前仍是
+signing-decision-closeout-only / unsigned-package-only / no-secret-read / no-signing /
+no-shell-execute / no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke
+已達 `DB_APPLY_AUTHORIZATION_SIGNING_DECISION_CLOSEOUT_READY`、
+`authorization_signing_decision_closeout_ready_count=1`、
+`signing_decision_closeout_check_count=12`、`signing_decision_closeout_pass_count=12`、
+`signing_decision_closeout_waiting_count=0`、
+`authorization_signing_decision_preflight_ready_count=1`、
+`signing_decision_input_requirement_count=10`、
+`signing_decision_rejection_reason_count=11`、`required_issuer_evidence_count=9`、
+`nonsecret_authorization_claim_count=8`、
+`permits_future_unsigned_signing_decision_package_lane=true`、
+`issues_database_apply_authorization=false`、`signs_database_apply_authorization=false`、
+`ready_for_database_apply_now=false`、`secret_material_included=false`、
+`secret_material_required_in_preview=false`、`reads_secret_in_preview=false`、
+`executes_script_count=0`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。DB apply authorization signing-issuer guard 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-guard`,
+輸出 future authorization signing issuer guard、signable request boundary、signing issuer
+guard contract 與 guard checks;目前仍是 signing-issuer-guard-only /
+signable-boundary-only / no-secret-read / no-signing / no-shell-execute / no-SQL-execute /
+no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_GUARD_READY`、
+`authorization_signing_issuer_guard_ready_count=1`、
+`signing_issuer_guard_check_count=12`、`signing_issuer_guard_pass_count=12`、
+`signing_issuer_guard_waiting_count=0`、
+`authorization_signing_decision_closeout_ready_count=1`、
+`signing_decision_closeout_check_count=12`、
+`signing_decision_input_requirement_count=10`、
+`signing_decision_rejection_reason_count=11`、`required_issuer_evidence_count=9`、
+`nonsecret_authorization_claim_count=8`、
+`permits_future_authorization_signing_issuer_lane=true`、
+`issues_database_apply_authorization=false`、`signs_database_apply_authorization=false`、
+`ready_for_database_apply_now=false`、`secret_material_included=false`、
+`secret_material_required_in_preview=false`、`reads_secret_in_preview=false`、
+`executes_script_count=0`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。下一個 P0 是 future DB apply authorization signing-issuer
+closeout / final signable request package,仍不是直接 DB apply。
+DB apply authorization signing-issuer closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout`,
+輸出 future signing issuer closeout、final signable request package、closeout contract
+與 12 項 closeout checks;目前仍是 signing-issuer-closeout-only /
+final-signable-request-package-only / no-secret-read / no-signing / no-shell-execute /
+no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_CLOSEOUT_READY`、
+`authorization_signing_issuer_closeout_ready_count=1`、
+`signing_issuer_closeout_check_count=12`、`signing_issuer_closeout_pass_count=12`、
+`signing_issuer_closeout_waiting_count=0`、
+`authorization_signing_issuer_guard_ready_count=1`、
+`signing_issuer_guard_check_count=12`、`signing_decision_input_requirement_count=10`、
+`signing_decision_rejection_reason_count=11`、`required_issuer_evidence_count=9`、
+`nonsecret_authorization_claim_count=8`、`post_apply_verifier_required_count=1`、
+`same_run_truth_required_count=1`、`signs_database_apply_authorization_count=0`、
+`ready_for_database_apply_now=false`、`secret_material_included=false`、
+`secret_material_required_in_preview=false`、`reads_secret_count=0`、
+`executes_script_count=0`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。下一個 P0 是 future explicit authorization signing execution
+preflight / operator-held secret boundary,仍不是直接 DB apply。
+DB apply authorization signing execution preflight 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight`,
+輸出 future authorization signing execution preflight、signing execution preflight package、
+operator-held secret boundary contract、nonsecret signing inputs、command-shape preview、
+rollback boundary 與 12 項 preflight checks;目前仍是 signing-execution-preflight-only /
+external-secret-reference-only / no-secret-read / no-signing / no-shell-execute /
+no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT_READY`、
+`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`、
+`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`、`signs_database_apply_authorization_count=0`、
+`ready_for_database_apply_now=false`、`secret_material_included=false`、
+`secret_material_required_in_preview=false`、`reads_secret_count=0`、
+`executes_script_count=0`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。下一個 P0 是 future explicit authorization signing execution
+closeout / unsigned signed-authorization receipt boundary,仍不是直接 DB apply。
+DB apply authorization signing execution closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-closeout`,
+輸出 future authorization signing execution closeout、unsigned signed-authorization receipt
+boundary、signing execution closeout contract 與 12 項 closeout checks;目前仍是
+signing-execution-closeout-only / unsigned-receipt-boundary-only / no-secret-read /
+no-signing / no-shell-execute / no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke
+已達 `DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_CLOSEOUT_READY`、
+`authorization_signing_execution_closeout_ready_count=1`、
+`signing_execution_closeout_check_count=12`、`signing_execution_closeout_pass_count=12`、
+`signing_execution_closeout_waiting_count=0`、
+`authorization_signing_execution_preflight_ready_count=1`、
+`unsigned_signed_authorization_receipt_boundary_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`、
+`signed_authorization_receipt_included=false`、`signature_material_included=false`、
+`signs_database_apply_authorization_count=0`、`ready_for_database_apply_now=false`、
+`secret_material_included=false`、`secret_material_required_in_preview=false`、
+`reads_secret_count=0`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。下一個 P0 是 future signed
+authorization receipt preflight / external signing receipt evidence boundary,仍不是直接 DB apply。
+DB apply authorization signed receipt preflight 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-preflight`,
+輸出 future authorization signed receipt preflight、external signing receipt evidence
+boundary、signed receipt preflight contract 與 12 項 preflight checks;目前仍是
+signed-receipt-preflight-only / external-evidence-boundary-only / no-secret-read /
+no-signed-receipt-in-preview / no-signing / no-shell-execute / no-SQL-execute /
+no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_PREFLIGHT_READY`、
+`authorization_signed_receipt_preflight_ready_count=1`、
+`signed_receipt_preflight_check_count=12`、`signed_receipt_preflight_pass_count=12`、
+`signed_receipt_preflight_waiting_count=0`、
+`authorization_signing_execution_closeout_ready_count=1`、
+`unsigned_signed_authorization_receipt_boundary_count=1`、
+`external_signing_receipt_evidence_boundary_count=1`、
+`required_external_receipt_evidence_count=10`、`external_receipt_acceptance_gate_count=8`、
+`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`、
+`external_signed_authorization_receipt_included=false`、
+`signed_authorization_receipt_included=false`、`signature_material_included=false`、
+`signs_database_apply_authorization_count=0`、`ready_for_database_apply_now=false`、
+`secret_material_included=false`、`secret_material_required_in_preview=false`、
+`reads_secret_count=0`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。下一個 P0 是 future signed
+authorization receipt closeout / detached receipt verification boundary,仍不是直接 DB apply。
+DB apply authorization signed receipt closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-closeout`,
+輸出 future authorization signed receipt closeout、detached receipt verification boundary、
+signed receipt closeout contract 與 12 項 closeout checks;目前仍是
+signed-receipt-closeout-only / detached-verification-boundary-only / no-secret-read /
+no-signed-receipt-in-preview / no-signature-material / no-signing / no-shell-execute /
+no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_CLOSEOUT_READY`、
+`authorization_signed_receipt_closeout_ready_count=1`、
+`signed_receipt_closeout_check_count=12`、`signed_receipt_closeout_pass_count=12`、
+`signed_receipt_closeout_waiting_count=0`、
+`authorization_signed_receipt_preflight_ready_count=1`、
+`external_signing_receipt_evidence_boundary_count=1`、
+`detached_receipt_verification_boundary_count=1`、
+`required_external_receipt_evidence_count=10`、`external_receipt_acceptance_gate_count=8`、
+`detached_receipt_verification_check_count=10`、
+`post_apply_verifier_required_count=1`、`same_run_truth_required_count=1`、
+`external_signed_authorization_receipt_included=false`、
+`signed_authorization_receipt_included=false`、`signature_material_included=false`、
+`detached_signature_verification_performed=false`、
+`signs_database_apply_authorization_count=0`、`ready_for_database_apply_now=false`、
+`secret_material_included=false`、`secret_material_required_in_preview=false`、
+`reads_secret_count=0`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。下一個 P0 是 future signed
+authorization receipt evidence intake / detached verification evidence schema,仍不是直接 DB apply。
+DB apply authorization signed receipt evidence intake 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-evidence-intake`,
+輸出 future signed authorization receipt evidence intake、detached verification evidence schema、
+signed receipt evidence intake contract 與 12 項 intake checks;目前仍是
+evidence-intake-schema-only / detached-verification-schema-only / no-secret-read /
+no-plaintext-secret / no-signed-receipt-body / no-signature-material / no-detached-verification-execute /
+no-signing / no-shell-execute / no-SQL-execute / no-DB-write preview。production-derived
+batch=12 smoke 已達 `DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_EVIDENCE_INTAKE_READY`、
+`authorization_signed_receipt_evidence_intake_ready_count=1`、
+`signed_receipt_evidence_intake_check_count=12`、
+`signed_receipt_evidence_intake_pass_count=12`、
+`signed_receipt_evidence_intake_waiting_count=0`、
+`authorization_signed_receipt_closeout_ready_count=1`、
+`signed_receipt_closeout_check_count=12`、
+`detached_receipt_verification_boundary_count=1`、
+`detached_verification_evidence_schema_count=1`、
+`required_external_receipt_evidence_count=10`、`external_receipt_acceptance_gate_count=8`、
+`detached_receipt_verification_check_count=10`、
+`detached_verification_evidence_field_count=12`、
+`detached_verification_acceptance_gate_count=10`、
+`post_apply_verifier_required_count=1`、`same_run_truth_required_count=1`、
+`external_signed_authorization_receipt_included=false`、
+`signed_authorization_receipt_included=false`、`signature_material_included=false`、
+`detached_signature_verification_performed=false`、`accepts_plaintext_secret=false`、
+`signs_database_apply_authorization_count=0`、`ready_for_database_apply_now=false`、
+`secret_material_included=false`、`secret_material_required_in_preview=false`、
+`reads_secret_count=0`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。下一個 P0 是 future detached
+verification evidence validation / verifier receipt closeout,仍不是直接 DB apply。
+DB apply authorization detached verification evidence validation 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-detached-verification-evidence-validation`,
+輸出 future detached verification evidence validation、verifier receipt closeout boundary、
+detached verification evidence validation contract 與 12 項 validation checks;目前仍是
+validation-boundary-only / verifier-receipt-closeout-boundary-only / no-secret-read /
+no-plaintext-secret / no-signed-receipt-body / no-signature-material / no-detached-verification-execute /
+no-verifier-receipt-persist / no-signing / no-shell-execute / no-SQL-execute /
+no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_DETACHED_VERIFICATION_EVIDENCE_VALIDATION_READY`、
+`authorization_detached_verification_evidence_validation_ready_count=1`、
+`detached_verification_evidence_validation_check_count=12`、
+`detached_verification_evidence_validation_pass_count=12`、
+`detached_verification_evidence_validation_waiting_count=0`、
+`authorization_signed_receipt_evidence_intake_ready_count=1`、
+`signed_receipt_evidence_intake_check_count=12`、
+`detached_verification_evidence_schema_count=1`、
+`verifier_receipt_closeout_boundary_count=1`、
+`required_external_receipt_evidence_count=10`、`external_receipt_acceptance_gate_count=8`、
+`detached_receipt_verification_check_count=10`、
+`detached_verification_evidence_field_count=12`、
+`detached_verification_acceptance_gate_count=10`、
+`verifier_receipt_field_count=12`、`verifier_receipt_acceptance_gate_count=10`、
+`post_apply_verifier_required_count=1`、`same_run_truth_required_count=1`、
+`external_signed_authorization_receipt_included=false`、
+`signed_authorization_receipt_included=false`、`signature_material_included=false`、
+`detached_signature_verification_performed=false`、`verifier_receipt_persisted=false`、
+`accepts_plaintext_secret=false`、`signs_database_apply_authorization_count=0`、
+`ready_for_database_apply_now=false`、`secret_material_included=false`、
+`reads_secret_count=0`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。下一個 P0 是 future verifier
+receipt closeout / verifier receipt evidence handoff,仍不是直接 DB apply。
+DB apply authorization verifier receipt closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-verifier-receipt-closeout`,
+輸出 future verifier receipt closeout、verifier receipt evidence handoff、
+verifier receipt closeout contract 與 12 項 closeout checks;目前仍是
+verifier-receipt-closeout-handoff-only / evidence-handoff-only / no-secret-read /
+no-plaintext-secret / no-signed-receipt-body / no-signature-material / no-detached-verification-execute /
+no-verifier-receipt-persist / no-signing / no-shell-execute / no-SQL-execute /
+no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_VERIFIER_RECEIPT_CLOSEOUT_READY`、
+`authorization_verifier_receipt_closeout_ready_count=1`、
+`verifier_receipt_closeout_check_count=12`、
+`verifier_receipt_closeout_pass_count=12`、
+`verifier_receipt_closeout_waiting_count=0`、
+`authorization_detached_verification_evidence_validation_ready_count=1`、
+`detached_verification_evidence_validation_check_count=12`、
+`authorization_signed_receipt_evidence_intake_ready_count=1`、
+`signed_receipt_evidence_intake_check_count=12`、
+`verifier_receipt_closeout_boundary_count=1`、
+`verifier_receipt_evidence_handoff_count=1`、
+`required_external_receipt_evidence_count=10`、`external_receipt_acceptance_gate_count=8`、
+`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`、
+`detached_verification_evidence_field_count=12`、
+`detached_verification_acceptance_gate_count=10`、
+`post_apply_verifier_required_count=1`、`same_run_truth_required_count=1`、
+`external_signed_authorization_receipt_included=false`、
+`signed_authorization_receipt_included=false`、`signature_material_included=false`、
+`detached_signature_verification_performed=false`、`verifier_receipt_persisted=false`、
+`accepts_plaintext_secret=false`、`signs_database_apply_authorization_count=0`、
+`ready_for_database_apply_now=false`、`secret_material_included=false`、
+`reads_secret_count=0`、`executes_script_count=0`、`executes_sql_count=0`、
+`executes_migration_count=0`、`writes_database_count=0`。下一個 P0 是 future database
+apply authorization verifier handoff / authorization evidence execution preflight,仍不是直接 DB apply。
+DB apply authorization evidence execution preflight 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-preflight`,
+輸出 future database apply authorization verifier handoff、authorization evidence execution
+preflight package、preflight contract 與 12 項 execution preflight checks;目前仍是
+authorization-evidence-execution-preflight-only / no-secret-read / no-plaintext-secret /
+no-signed-receipt-body / no-signature-material / no-detached-verification-execute /
+no-verifier-receipt-persist / no-authorization-evidence-execute / no-signing /
+no-shell-execute / no-endpoint-execute / no-SQL-execute / no-DB-write preview。
+production-derived batch=12 smoke 已達
+`DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_PREFLIGHT_READY`、
+`authorization_evidence_execution_preflight_ready_count=1`、
+`authorization_evidence_execution_preflight_check_count=12`、
+`authorization_evidence_execution_preflight_pass_count=12`、
+`authorization_evidence_execution_preflight_waiting_count=0`、
+`authorization_verifier_receipt_closeout_ready_count=1`、
+`verifier_receipt_closeout_check_count=12`、
+`authorization_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_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`、
+`ready_for_future_database_apply_authorization_verifier_handoff=true`、
+`can_enter_future_authorization_evidence_execution_closeout=true`、
+`ready_for_future_authorization_evidence_execution_preflight=true`、
+`external_signed_authorization_receipt_included=false`、
+`signed_authorization_receipt_included=false`、`signature_material_included=false`、
+`detached_signature_verification_performed=false`、`verifier_receipt_persisted=false`、
+`executes_authorization_evidence=false`、`accepts_plaintext_secret=false`、
+`signs_database_apply_authorization_count=0`、`ready_for_database_apply_now=false`、
+`secret_material_included=false`、`reads_secret_count=0`、`executes_script_count=0`、
+`executes_endpoint_count=0`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。下一個 P0 是 future authorization evidence execution closeout /
+database apply authorization final verifier gate,仍不是直接 DB apply。
+DB apply authorization evidence execution closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-closeout`,
+輸出 future database apply authorization final verifier gate、authorization evidence execution
+closeout package、closeout contract 與 12 項 closeout checks;目前仍是
+authorization-evidence-execution-closeout-final-verifier-gate-only / no-secret-read /
+no-plaintext-secret / no-signed-receipt-body / no-signature-material /
+no-detached-verification-execute / no-verifier-receipt-persist /
+no-authorization-evidence-execute / no-database-apply-execute / no-signing /
+no-shell-execute / no-endpoint-execute / no-SQL-execute / no-DB-write preview。
+production-derived batch=12 smoke 已達
+`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`、
+`authorization_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`、
+`ready_for_future_database_apply_authorization_final_verifier_gate=true`、
+`can_enter_future_database_apply_controlled_apply_final_preflight=true`、
+`ready_for_future_authorization_evidence_execution_closeout=true`、
+`external_signed_authorization_receipt_included=false`、
+`signed_authorization_receipt_included=false`、`signature_material_included=false`、
+`detached_signature_verification_performed=false`、`verifier_receipt_persisted=false`、
+`executes_authorization_evidence=false`、`executes_database_apply=false`、
+`database_apply_authorized=false`、`accepts_plaintext_secret=false`、
+`signs_database_apply_authorization_count=0`、`ready_for_database_apply_now=false`、
+`secret_material_included=false`、`reads_secret_count=0`、`executes_script_count=0`、
+`executes_endpoint_count=0`、`executes_sql_count=0`、`executes_migration_count=0`、
+`writes_database_count=0`。下一個 P0 是 future database apply controlled-apply final
+preflight / rollback + post-apply verifier binding,仍不是直接 DB apply。
+DB apply controlled-apply final preflight 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-apply-final-preflight`,
+輸出 future database apply controlled-apply final preflight、rollback binding、
+post-apply verifier binding、controlled apply final preflight contract 與 12 項 final
+preflight checks;目前仍是 controlled-apply-final-preflight-rollback-and-verifier-binding-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-signature-material / no-authorization-evidence-execute / no-database-apply-execute /
+no-endpoint-execute / no-SQL-execute / no-DB-write preview。production-derived batch=12
+smoke 已達 `DB_APPLY_CONTROLLED_APPLY_FINAL_PREFLIGHT_READY`、
+`controlled_apply_final_preflight_ready_count=1`、
+`controlled_apply_final_preflight_check_count=12`、
+`controlled_apply_final_preflight_pass_count=12`、
+`controlled_apply_final_preflight_waiting_count=0`、
+`authorization_evidence_execution_closeout_ready_count=1`、
+`authorization_evidence_execution_closeout_check_count=12`、
+`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`、
+`database_apply_final_verifier_gate_count=1`、
+`database_apply_authorization_final_verifier_gate_ready_count=1`、
+`controlled_apply_final_preflight_count=1`、
+`controlled_apply_final_preflight_field_count=12`、
+`controlled_apply_final_preflight_acceptance_gate_count=10`、
+`rollback_binding_count=1`、`rollback_binding_field_count=8`、
+`post_apply_verifier_binding_count=1`、`post_apply_verifier_binding_field_count=8`、
+`authorization_evidence_execution_closeout_field_count=12`、
+`authorization_evidence_execution_closeout_acceptance_gate_count=10`、
+`post_apply_verifier_required_count=1`、`same_run_truth_required_count=1`、
+`ready_for_future_database_apply_controlled_apply_final_preflight=true`、
+`can_enter_future_database_apply_controlled_dry_run_package=true`、
+`dry_run_only=true`、`check_mode_only=true`、`rollback_bound=true`、
+`post_apply_verifier_bound=true`、`database_apply_authorized=false`、
+`executes_authorization_evidence=false`、`executes_database_apply=false`、
+`executes_endpoint_count=0`、`executes_sql_count=0`、`writes_database_count=0`、
+`reads_secret_count=0`、`signs_database_apply_authorization_count=0`。下一個 P0 是
+future database apply controlled dry-run package / dry-run execution receipt,仍不是直接 DB apply。
+DB apply controlled dry-run package 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-package`,
+輸出 future database apply controlled dry-run execution receipt、controlled dry-run
+package、dry-run command shape、dry-run execution receipt preview 與 12 項 dry-run
+package checks;目前仍是 controlled-dry-run-package-and-receipt-preview-only /
+dry-run-only / check-mode-only / non-executable command shape / receipt-preview-only /
+no-secret-read / no-plaintext-secret / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_PACKAGE_READY`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_dry_run_package_check_count=12`、
+`controlled_dry_run_package_pass_count=12`、
+`controlled_dry_run_package_waiting_count=0`、
+`controlled_apply_final_preflight_ready_count=1`、
+`controlled_apply_final_preflight_check_count=12`、
+`authorization_evidence_execution_closeout_ready_count=1`、
+`authorization_evidence_execution_closeout_check_count=12`、
+`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`、
+`database_apply_final_verifier_gate_count=1`、
+`database_apply_authorization_final_verifier_gate_ready_count=1`、
+`controlled_dry_run_package_count=1`、
+`controlled_dry_run_package_field_count=12`、
+`controlled_dry_run_acceptance_gate_count=10`、
+`dry_run_execution_receipt_preview_count=1`、
+`dry_run_execution_receipt_field_count=8`、
+`controlled_apply_final_preflight_count=1`、
+`rollback_binding_count=1`、`post_apply_verifier_binding_count=1`、
+`post_apply_verifier_required_count=1`、`same_run_truth_required_count=1`、
+`ready_for_future_database_apply_controlled_dry_run_execution_receipt=true`、
+`can_enter_future_database_apply_controlled_dry_run_receipt_closeout=true`、
+`execution_performed=false`、`database_apply_authorized=false`、
+`executes_authorization_evidence=false`、`executes_database_apply=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run receipt closeout / dry-run result parser
+verification,仍不是直接 DB apply。
+DB apply controlled dry-run receipt closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-closeout`,
+輸出 future database apply controlled dry-run result parser verification、controlled
+dry-run receipt closeout、dry-run result parser、receipt validation report 與 12 項
+receipt closeout checks;目前仍是
+controlled-dry-run-receipt-closeout-and-result-parser-verification-only /
+receipt-preview-only / parser-schema-only / dry-run-only / check-mode-only /
+no-secret-read / no-plaintext-secret / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_CLOSEOUT_READY`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_receipt_closeout_check_count=12`、
+`controlled_dry_run_receipt_closeout_pass_count=12`、
+`controlled_dry_run_receipt_closeout_waiting_count=0`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_dry_run_package_check_count=12`、
+`controlled_apply_final_preflight_ready_count=1`、
+`controlled_apply_final_preflight_check_count=12`、
+`authorization_evidence_execution_closeout_ready_count=1`、
+`authorization_evidence_execution_closeout_check_count=12`、
+`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`、
+`database_apply_final_verifier_gate_count=1`、
+`database_apply_authorization_final_verifier_gate_ready_count=1`、
+`controlled_dry_run_receipt_closeout_count=1`、
+`controlled_dry_run_receipt_closeout_field_count=12`、
+`controlled_dry_run_receipt_closeout_acceptance_gate_count=10`、
+`dry_run_result_parser_count=1`、`dry_run_result_parser_field_count=10`、
+`receipt_validation_report_count=1`、`receipt_validation_field_count=8`、
+`ready_for_future_database_apply_controlled_dry_run_result_parser_verification=true`、
+`can_enter_future_database_apply_controlled_dry_run_runner_readiness=true`、
+`receipt_validation_status=preview_validated_not_executed`、
+`parser_execution_required=false`、`execution_performed=false`、
+`database_apply_authorized=false`、`executes_endpoint=false`、`executes_sql=false`、
+`writes_database=false`、`reads_secret_count=0`、`executes_endpoint_count=0`、
+`executes_sql_count=0`、`writes_database_count=0`、
+`signs_database_apply_authorization_count=0`。下一個 P0 是 future database apply
+controlled dry-run runner readiness / execution plan binding,仍不是直接 DB apply。
+DB apply controlled dry-run runner readiness 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-readiness`,
+輸出 future database apply controlled dry-run execution plan binding、controlled
+dry-run runner readiness、execution plan binding 與 12 項 runner readiness checks;
+目前仍是 controlled-dry-run-runner-readiness-and-execution-plan-binding-only /
+execution-plan-preview-only / runner-readiness-only / dry-run-only /
+check-mode-only / no-secret-read / no-plaintext-secret / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_READINESS_READY`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_runner_readiness_check_count=12`、
+`controlled_dry_run_runner_readiness_pass_count=12`、
+`controlled_dry_run_runner_readiness_waiting_count=0`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_receipt_closeout_check_count=12`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_dry_run_package_check_count=12`、
+`controlled_apply_final_preflight_ready_count=1`、
+`controlled_apply_final_preflight_check_count=12`、
+`authorization_evidence_execution_closeout_ready_count=1`、
+`authorization_evidence_execution_closeout_check_count=12`、
+`authorization_evidence_execution_preflight_ready_count=1`、
+`authorization_evidence_execution_preflight_check_count=12`、
+`database_apply_final_verifier_gate_count=1`、
+`database_apply_authorization_final_verifier_gate_ready_count=1`、
+`controlled_dry_run_runner_readiness_count=1`、
+`controlled_dry_run_runner_readiness_field_count=12`、
+`controlled_dry_run_runner_readiness_acceptance_gate_count=10`、
+`execution_plan_binding_count=1`、`execution_plan_binding_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_execution_plan_binding=true`、
+`can_enter_future_database_apply_controlled_dry_run_execution_plan_closeout=true`、
+`execution_plan_bound=true`、`runner_execution_authorized=false`、
+`dry_run_execution_authorized=false`、`execution_authorized=false`、
+`database_apply_authorized=false`、`executes_endpoint=false`、`executes_sql=false`、
+`writes_database=false`、`reads_secret_count=0`、`executes_endpoint_count=0`、
+`executes_sql_count=0`、`writes_database_count=0`、
+`signs_database_apply_authorization_count=0`。下一個 P0 是 future database apply
+controlled dry-run execution plan closeout / non-executable command artifact
+verification,仍不是直接 DB apply。
+DB apply controlled dry-run execution plan closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-plan-closeout`,
+輸出 future database apply controlled dry-run command artifact verification、
+controlled dry-run execution plan closeout、non-executable command artifact 與 12
+項 execution plan closeout checks;目前仍是
+controlled-dry-run-execution-plan-closeout-and-non-executable-command-artifact-verification-only /
+execution-plan-closeout-only / non-executable-command-artifact-only / dry-run-only /
+check-mode-only / no-secret-read / no-plaintext-secret / no-command-text /
+no-argv / no-signature-material / no-authorization-evidence-execute /
+no-database-apply-execute / no-endpoint-execute / no-SQL-execute /
+no-DB-write preview。production-derived batch=12 smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PLAN_CLOSEOUT_READY`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_check_count=12`、
+`controlled_dry_run_execution_plan_closeout_pass_count=12`、
+`controlled_dry_run_execution_plan_closeout_waiting_count=0`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_runner_readiness_check_count=12`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_receipt_closeout_check_count=12`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_dry_run_package_check_count=12`、
+`controlled_apply_final_preflight_ready_count=1`、
+`controlled_apply_final_preflight_check_count=12`、
+`authorization_evidence_execution_closeout_ready_count=1`、
+`authorization_evidence_execution_closeout_check_count=12`、
+`authorization_evidence_execution_preflight_ready_count=1`、
+`authorization_evidence_execution_preflight_check_count=12`、
+`database_apply_final_verifier_gate_count=1`、
+`database_apply_authorization_final_verifier_gate_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_count=1`、
+`controlled_dry_run_execution_plan_closeout_field_count=12`、
+`controlled_dry_run_execution_plan_closeout_acceptance_gate_count=10`、
+`non_executable_command_artifact_count=1`、
+`non_executable_command_artifact_field_count=10`、
+`execution_plan_binding_count=1`、`execution_plan_binding_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_command_artifact_verification=true`、
+`can_enter_future_database_apply_controlled_dry_run_command_artifact_closeout=true`、
+`execution_plan_closeout_ready=true`、
+`non_executable_command_artifact_verified=true`、
+`runner_execution_authorized=false`、`dry_run_execution_authorized=false`、
+`execution_authorized=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。下一個
+P0 是 future database apply controlled dry-run command artifact closeout /
+runner execution receipt preflight,仍不是直接 DB apply。
+DB apply controlled dry-run command artifact closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-command-artifact-closeout`,
+輸出 future database apply controlled dry-run runner execution receipt preflight、
+controlled dry-run command artifact closeout、runner execution receipt preflight 與
+12 項 command artifact closeout checks;目前仍是
+controlled-dry-run-command-artifact-closeout-and-runner-execution-receipt-preflight-only /
+command-artifact-closeout-only / runner-execution-receipt-preflight-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-command-text / no-argv / no-stdout-capture / no-stderr-capture /
+no-signature-material / no-authorization-evidence-execute /
+no-database-apply-execute / no-endpoint-execute / no-SQL-execute /
+no-DB-write preview。local deterministic fake-fetch smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_COMMAND_ARTIFACT_CLOSEOUT_READY`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_check_count=12`、
+`controlled_dry_run_command_artifact_closeout_pass_count=12`、
+`controlled_dry_run_command_artifact_closeout_waiting_count=0`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_check_count=12`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_runner_readiness_check_count=12`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_receipt_closeout_check_count=12`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_dry_run_package_check_count=12`、
+`controlled_apply_final_preflight_ready_count=1`、
+`controlled_apply_final_preflight_check_count=12`、
+`authorization_evidence_execution_closeout_ready_count=1`、
+`authorization_evidence_execution_closeout_check_count=12`、
+`authorization_evidence_execution_preflight_ready_count=1`、
+`authorization_evidence_execution_preflight_check_count=12`、
+`database_apply_final_verifier_gate_count=1`、
+`database_apply_authorization_final_verifier_gate_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_count=1`、
+`controlled_dry_run_command_artifact_closeout_field_count=12`、
+`controlled_dry_run_command_artifact_closeout_acceptance_gate_count=10`、
+`runner_execution_receipt_preflight_count=1`、
+`runner_execution_receipt_preflight_field_count=10`、
+`ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight=true`、
+`can_enter_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout=true`、
+`command_artifact_closeout_ready=true`、
+`runner_execution_receipt_preflight_bound=true`、
+`runner_execution_authorized=false`、`dry_run_execution_authorized=false`、
+`execution_authorized=false`、`stdout_capture_allowed=false`、
+`stderr_capture_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。本輪
+production truth / production-derived smoke 因本機網路 `Network is unreachable`
+暫時 blocked,待網路恢復後必須重跑同層 production-derived smoke。下一個 P0 是
+future database apply controlled dry-run runner execution receipt closeout /
+post-receipt parser verification,仍不是直接 DB apply。
+DB apply controlled dry-run runner execution receipt closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-execution-receipt-closeout`,
+輸出 future database apply controlled dry-run post-receipt parser verification、
+controlled dry-run runner execution receipt closeout、receipt closeout preview、
+post-receipt parser verification 與 12 項 runner execution receipt closeout checks;
+目前仍是
+controlled-dry-run-runner-execution-receipt-closeout-and-post-receipt-parser-verification-only /
+runner-execution-receipt-closeout-only / post-receipt-parser-verification-only /
+receipt-closeout-preview-not-executed / dry-run-only / check-mode-only /
+no-secret-read / no-plaintext-secret / no-stdout-included / no-stderr-included /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-endpoint-execute / no-SQL-execute / no-DB-write preview。production version truth
+已重新通過 `healthy postgresql V10.725`;local deterministic fake-fetch smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_EXECUTION_RECEIPT_CLOSEOUT_READY`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_check_count=12`、
+`controlled_dry_run_runner_execution_receipt_closeout_pass_count=12`、
+`controlled_dry_run_runner_execution_receipt_closeout_waiting_count=0`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_check_count=12`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_check_count=12`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_runner_readiness_check_count=12`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_receipt_closeout_check_count=12`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_dry_run_package_check_count=12`、
+`controlled_apply_final_preflight_ready_count=1`、
+`controlled_apply_final_preflight_check_count=12`、
+`authorization_evidence_execution_closeout_ready_count=1`、
+`authorization_evidence_execution_closeout_check_count=12`、
+`authorization_evidence_execution_preflight_ready_count=1`、
+`authorization_evidence_execution_preflight_check_count=12`、
+`database_apply_final_verifier_gate_count=1`、
+`database_apply_authorization_final_verifier_gate_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_field_count=12`、
+`controlled_dry_run_runner_execution_receipt_closeout_acceptance_gate_count=10`、
+`post_receipt_parser_verification_count=1`、
+`post_receipt_parser_verification_field_count=10`、
+`receipt_closeout_preview_count=1`、
+`ready_for_future_database_apply_controlled_dry_run_post_receipt_parser_verification=true`、
+`can_enter_future_database_apply_controlled_dry_run_post_receipt_parser_closeout=true`、
+`runner_execution_receipt_closeout_ready=true`、
+`post_receipt_parser_verification_bound=true`、
+`runner_execution_authorized=false`、`dry_run_execution_authorized=false`、
+`execution_authorized=false`、`stdout_capture_allowed=false`、
+`stderr_capture_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。後續 P0 已接續到
+future database apply controlled dry-run post-receipt parser closeout /
+no-apply enforcement verification;仍不是直接 DB apply。
+DB apply controlled dry-run post-receipt parser closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-post-receipt-parser-closeout`,
+輸出 future database apply controlled dry-run no-apply enforcement verification、
+controlled dry-run post-receipt parser closeout、no-apply enforcement verification
+與 12 項 post-receipt parser closeout checks;目前仍是
+controlled-dry-run-post-receipt-parser-closeout-and-no-apply-enforcement-only /
+post-receipt-parser-closeout-only / no-apply-enforcement-verification-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-endpoint-execute / no-SQL-execute / no-DB-write preview。local deterministic
+fake-fetch smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_POST_RECEIPT_PARSER_CLOSEOUT_READY`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_check_count=12`、
+`controlled_dry_run_post_receipt_parser_closeout_pass_count=12`、
+`controlled_dry_run_post_receipt_parser_closeout_waiting_count=0`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_check_count=12`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`no_apply_enforcement_verification_count=1`、
+`no_apply_enforcement_verification_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_no_apply_enforcement_verification=true`、
+`can_enter_future_database_apply_controlled_dry_run_no_apply_enforcement_closeout=true`、
+`post_receipt_parser_closeout_ready=true`、
+`no_apply_enforcement_verification_bound=true`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。後續 P0 已接續到
+future database apply controlled dry-run no-apply enforcement closeout /
+final dry-run executor guard;仍不是直接 DB apply。
+DB apply controlled dry-run no-apply enforcement closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-apply-enforcement-closeout`,
+輸出 future database apply controlled dry-run final dry-run executor guard、
+controlled dry-run no-apply enforcement closeout、final dry-run executor guard
+與 12 項 no-apply enforcement closeout checks;目前仍是
+controlled-dry-run-no-apply-enforcement-closeout-and-final-executor-guard-only /
+no-apply-enforcement-closeout-only / final-dry-run-executor-guard-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-endpoint-execute / no-SQL-execute / no-DB-write preview。local deterministic
+fake-fetch smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_NO_APPLY_ENFORCEMENT_CLOSEOUT_READY`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_check_count=12`、
+`controlled_dry_run_no_apply_enforcement_closeout_pass_count=12`、
+`controlled_dry_run_no_apply_enforcement_closeout_waiting_count=0`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_check_count=12`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_check_count=12`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`final_dry_run_executor_guard_count=1`、
+`final_dry_run_executor_guard_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_final_dry_run_executor_guard=true`、
+`can_enter_future_database_apply_controlled_dry_run_final_executor_guard_closeout=true`、
+`no_apply_enforcement_closeout_ready=true`、
+`final_dry_run_executor_guard_bound=true`、
+`dry_run_executor_invocation_allowed=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。後續 P0 已接續到
+future database apply controlled dry-run final executor guard closeout /
+pre-apply replay verifier;仍不是直接 DB apply。
+DB apply controlled dry-run final executor guard closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-executor-guard-closeout`,
+輸出 future database apply controlled dry-run pre-apply replay verifier、
+controlled dry-run final executor guard closeout、pre-apply replay verifier
+與 12 項 final executor guard closeout checks;目前仍是
+controlled-dry-run-final-executor-guard-closeout-and-pre-apply-replay-verifier-only /
+final-executor-guard-closeout-only / pre-apply-replay-verifier-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-endpoint-execute / no-SQL-execute / no-DB-write preview。local deterministic
+fake-fetch smoke 已達
+`DB_APPLY_CONTROLLED_DRY_RUN_FINAL_EXECUTOR_GUARD_CLOSEOUT_READY`、
+`controlled_dry_run_final_executor_guard_closeout_ready_count=1`、
+`controlled_dry_run_final_executor_guard_closeout_check_count=12`、
+`controlled_dry_run_final_executor_guard_closeout_pass_count=12`、
+`controlled_dry_run_final_executor_guard_closeout_waiting_count=0`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_check_count=12`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`pre_apply_replay_verifier_count=1`、
+`pre_apply_replay_verifier_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_pre_apply_replay_verifier=true`、
+`can_enter_future_database_apply_controlled_dry_run_pre_apply_replay_closeout=true`、
+`final_executor_guard_closeout_ready=true`、
+`pre_apply_replay_verifier_bound=true`、
+`dry_run_executor_invocation_allowed=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run pre-apply replay closeout /
+apply executor readiness contract,仍不是直接 DB apply。
+DB apply controlled dry-run pre-apply replay closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-pre-apply-replay-closeout`,
+輸出 future database apply controlled dry-run apply executor readiness contract、
+controlled dry-run pre-apply replay closeout、apply executor readiness contract
+與 12 項 pre-apply replay closeout checks;目前仍是
+controlled-dry-run-pre-apply-replay-closeout-and-apply-executor-readiness-contract-only /
+pre-apply-replay-closeout-only / apply-executor-readiness-contract-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-dry-run-executor-invocation / no-endpoint-execute / no-SQL-execute /
+no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_PRE_APPLY_REPLAY_CLOSEOUT_READY`、
+`controlled_dry_run_pre_apply_replay_closeout_ready_count=1`、
+`controlled_dry_run_pre_apply_replay_closeout_check_count=12`、
+`controlled_dry_run_pre_apply_replay_closeout_pass_count=12`、
+`controlled_dry_run_pre_apply_replay_closeout_waiting_count=0`、
+`controlled_dry_run_final_executor_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`apply_executor_readiness_contract_count=1`、
+`apply_executor_readiness_contract_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_apply_executor_readiness_contract=true`、
+`can_enter_future_database_apply_controlled_dry_run_apply_executor_readiness_closeout=true`、
+`pre_apply_replay_closeout_ready=true`、
+`apply_executor_readiness_contract_bound=true`、
+`dry_run_executor_invocation_allowed=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run apply executor readiness closeout /
+dry-run invocation readiness receipt,仍不是直接 DB apply。
+DB apply controlled dry-run apply executor readiness closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-apply-executor-readiness-closeout`,
+輸出 future database apply controlled dry-run invocation readiness receipt、
+controlled dry-run apply executor readiness closeout、dry-run invocation readiness receipt
+與 12 項 apply executor readiness closeout checks;目前仍是
+controlled-dry-run-apply-executor-readiness-closeout-and-dry-run-invocation-readiness-receipt-only /
+apply-executor-readiness-closeout-only / dry-run-invocation-readiness-receipt-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-dry-run-executor-invocation / no-endpoint-execute / no-SQL-execute /
+no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_APPLY_EXECUTOR_READINESS_CLOSEOUT_READY`、
+`controlled_dry_run_apply_executor_readiness_closeout_ready_count=1`、
+`controlled_dry_run_apply_executor_readiness_closeout_check_count=12`、
+`controlled_dry_run_apply_executor_readiness_closeout_pass_count=12`、
+`controlled_dry_run_apply_executor_readiness_closeout_waiting_count=0`、
+`controlled_dry_run_pre_apply_replay_closeout_ready_count=1`、
+`controlled_dry_run_final_executor_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`dry_run_invocation_readiness_receipt_count=1`、
+`dry_run_invocation_readiness_receipt_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_invocation_readiness_receipt=true`、
+`can_enter_future_database_apply_controlled_dry_run_invocation_receipt_closeout=true`、
+`apply_executor_readiness_closeout_ready=true`、
+`dry_run_invocation_readiness_receipt_bound=true`、
+`dry_run_executor_invocation_allowed=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run invocation receipt closeout /
+no-write invocation package,仍不是直接 DB apply。
+DB apply controlled dry-run invocation receipt closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-invocation-receipt-closeout`,
+輸出 future database apply controlled dry-run no-write invocation package、
+controlled dry-run invocation receipt closeout、no-write invocation package
+與 12 項 invocation receipt closeout checks;目前仍是
+controlled-dry-run-invocation-receipt-closeout-and-no-write-invocation-package-only /
+invocation-receipt-closeout-only / no-write-invocation-package-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-dry-run-executor-invocation / no-endpoint-execute / no-SQL-execute /
+no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_INVOCATION_RECEIPT_CLOSEOUT_READY`、
+`controlled_dry_run_invocation_receipt_closeout_ready_count=1`、
+`controlled_dry_run_invocation_receipt_closeout_check_count=12`、
+`controlled_dry_run_invocation_receipt_closeout_pass_count=12`、
+`controlled_dry_run_invocation_receipt_closeout_waiting_count=0`、
+`controlled_dry_run_apply_executor_readiness_closeout_ready_count=1`、
+`controlled_dry_run_pre_apply_replay_closeout_ready_count=1`、
+`controlled_dry_run_final_executor_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`no_write_invocation_package_count=1`、
+`no_write_invocation_package_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_no_write_invocation_package=true`、
+`can_enter_future_database_apply_controlled_dry_run_no_write_invocation_package_closeout=true`、
+`invocation_receipt_closeout_ready=true`、
+`no_write_invocation_package_bound=true`、
+`dry_run_executor_invocation_allowed=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`ready_for_actual_dry_run_execution_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run no-write invocation package closeout /
+execution-preflight guard,仍不是直接 DB apply。
+DB apply controlled dry-run no-write invocation package closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-invocation-package-closeout`,
+輸出 future database apply controlled dry-run execution preflight guard、
+controlled dry-run no-write invocation package closeout、execution preflight guard
+與 12 項 no-write invocation package closeout checks;目前仍是
+controlled-dry-run-no-write-invocation-package-closeout-and-execution-preflight-guard-only /
+no-write-invocation-package-closeout-only / execution-preflight-guard-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-dry-run-executor-invocation / no-endpoint-execute / no-SQL-execute /
+no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_INVOCATION_PACKAGE_CLOSEOUT_READY`、
+`controlled_dry_run_no_write_invocation_package_closeout_ready_count=1`、
+`controlled_dry_run_no_write_invocation_package_closeout_check_count=12`、
+`controlled_dry_run_no_write_invocation_package_closeout_pass_count=12`、
+`controlled_dry_run_no_write_invocation_package_closeout_waiting_count=0`、
+`controlled_dry_run_invocation_receipt_closeout_ready_count=1`、
+`controlled_dry_run_apply_executor_readiness_closeout_ready_count=1`、
+`controlled_dry_run_pre_apply_replay_closeout_ready_count=1`、
+`controlled_dry_run_final_executor_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`execution_preflight_guard_count=1`、
+`execution_preflight_guard_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_execution_preflight_guard=true`、
+`can_enter_future_database_apply_controlled_dry_run_execution_preflight_guard_closeout=true`、
+`no_write_invocation_package_closeout_ready=true`、
+`execution_preflight_guard_bound=true`、
+`dry_run_executor_invocation_allowed=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`ready_for_actual_dry_run_execution_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run execution preflight guard closeout /
+runner invocation boundary,仍不是直接 DB apply。
+DB apply controlled dry-run execution preflight guard closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-preflight-guard-closeout`,
+輸出 future database apply controlled dry-run runner invocation boundary、
+controlled dry-run execution preflight guard closeout、runner invocation boundary
+與 12 項 execution preflight guard closeout checks;目前仍是
+controlled-dry-run-execution-preflight-guard-closeout-and-runner-invocation-boundary-only /
+execution-preflight-guard-closeout-only / runner-invocation-boundary-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-dry-run-executor-invocation / no-runner-invocation / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PREFLIGHT_GUARD_CLOSEOUT_READY`、
+`controlled_dry_run_execution_preflight_guard_closeout_ready_count=1`、
+`controlled_dry_run_execution_preflight_guard_closeout_check_count=12`、
+`controlled_dry_run_execution_preflight_guard_closeout_pass_count=12`、
+`controlled_dry_run_execution_preflight_guard_closeout_waiting_count=0`、
+`controlled_dry_run_no_write_invocation_package_closeout_ready_count=1`、
+`controlled_dry_run_invocation_receipt_closeout_ready_count=1`、
+`controlled_dry_run_apply_executor_readiness_closeout_ready_count=1`、
+`controlled_dry_run_pre_apply_replay_closeout_ready_count=1`、
+`controlled_dry_run_final_executor_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`runner_invocation_boundary_count=1`、
+`runner_invocation_boundary_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_runner_invocation_boundary=true`、
+`can_enter_future_database_apply_controlled_dry_run_runner_invocation_boundary_closeout=true`、
+`execution_preflight_guard_closeout_ready=true`、
+`runner_invocation_boundary_bound=true`、
+`dry_run_executor_invocation_allowed=false`、
+`runner_invocation_allowed=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`ready_for_actual_dry_run_execution_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`captures_stdout=false`、`captures_stderr=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run runner invocation boundary closeout /
+no-execution receipt handoff,仍不是直接 DB apply。
+
+DB apply controlled dry-run runner invocation boundary closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-invocation-boundary-closeout`,
+輸出 future database apply controlled dry-run no-execution receipt handoff、
+controlled dry-run runner invocation boundary closeout、no-execution receipt handoff
+與 12 項 runner invocation boundary closeout checks;目前仍是
+controlled-dry-run-runner-invocation-boundary-closeout-and-no-execution-receipt-handoff-only /
+runner-invocation-boundary-closeout-only / no-execution-receipt-handoff-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-dry-run-executor-invocation / no-runner-invocation / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_INVOCATION_BOUNDARY_CLOSEOUT_READY`、
+`controlled_dry_run_runner_invocation_boundary_closeout_ready_count=1`、
+`controlled_dry_run_runner_invocation_boundary_closeout_check_count=12`、
+`controlled_dry_run_runner_invocation_boundary_closeout_pass_count=12`、
+`controlled_dry_run_runner_invocation_boundary_closeout_waiting_count=0`、
+`controlled_dry_run_execution_preflight_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_write_invocation_package_closeout_ready_count=1`、
+`controlled_dry_run_invocation_receipt_closeout_ready_count=1`、
+`controlled_dry_run_apply_executor_readiness_closeout_ready_count=1`、
+`controlled_dry_run_pre_apply_replay_closeout_ready_count=1`、
+`controlled_dry_run_final_executor_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`no_execution_receipt_handoff_count=1`、
+`no_execution_receipt_handoff_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_no_execution_receipt_handoff=true`、
+`can_enter_future_database_apply_controlled_dry_run_no_execution_receipt_handoff_closeout=true`、
+`runner_invocation_boundary_closeout_ready=true`、
+`no_execution_receipt_handoff_bound=true`、
+`dry_run_executor_invocation_allowed=false`、
+`runner_invocation_allowed=false`、
+`execution_receipt_present=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`ready_for_actual_dry_run_execution_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run no-execution receipt handoff closeout /
+final no-runner-execution proof,仍不是直接 DB apply。
+
+DB apply controlled dry-run no-execution receipt handoff closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout`,
+輸出 future database apply controlled dry-run final no-runner-execution proof、
+controlled dry-run no-execution receipt handoff closeout、final no-runner-execution proof
+與 12 項 no-execution receipt handoff closeout checks;目前仍是
+controlled-dry-run-no-execution-receipt-handoff-closeout-and-final-no-runner-execution-proof-only /
+no-execution-receipt-handoff-closeout-only / final-no-runner-execution-proof-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-dry-run-executor-invocation / no-runner-invocation / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_NO_EXECUTION_RECEIPT_HANDOFF_CLOSEOUT_READY`、
+`controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count=1`、
+`controlled_dry_run_no_execution_receipt_handoff_closeout_check_count=12`、
+`controlled_dry_run_no_execution_receipt_handoff_closeout_pass_count=12`、
+`controlled_dry_run_no_execution_receipt_handoff_closeout_waiting_count=0`、
+`controlled_dry_run_runner_invocation_boundary_closeout_ready_count=1`、
+`controlled_dry_run_execution_preflight_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_write_invocation_package_closeout_ready_count=1`、
+`controlled_dry_run_invocation_receipt_closeout_ready_count=1`、
+`controlled_dry_run_apply_executor_readiness_closeout_ready_count=1`、
+`controlled_dry_run_pre_apply_replay_closeout_ready_count=1`、
+`controlled_dry_run_final_executor_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`final_no_runner_execution_proof_count=1`、
+`final_no_runner_execution_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_final_no_runner_execution_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_final_no_runner_execution_proof_closeout=true`、
+`no_execution_receipt_handoff_closeout_ready=true`、
+`final_no_runner_execution_proof_bound=true`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`execution_receipt_present=false`、`dry_run_executor_invocation_allowed=false`、
+`runner_invocation_allowed=false`、`ready_for_dry_run_executor_invocation_now=false`、
+`ready_for_actual_dry_run_execution_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run final no-runner-execution proof closeout /
+controlled executor quarantine proof,仍不是直接 DB apply。
+
+DB apply controlled dry-run final no-runner-execution proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout`,
+輸出 future database apply controlled dry-run controlled executor quarantine proof、
+controlled dry-run final no-runner-execution proof closeout、controlled executor quarantine proof
+與 12 項 final no-runner-execution proof closeout checks;目前仍是
+controlled-dry-run-final-no-runner-execution-proof-closeout-and-controlled-executor-quarantine-proof-only /
+final-no-runner-execution-proof-closeout-only / controlled-executor-quarantine-proof-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-dry-run-executor-invocation / no-runner-invocation / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_FINAL_NO_RUNNER_EXECUTION_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count=1`、
+`controlled_dry_run_final_no_runner_execution_proof_closeout_check_count=12`、
+`controlled_dry_run_final_no_runner_execution_proof_closeout_pass_count=12`、
+`controlled_dry_run_final_no_runner_execution_proof_closeout_waiting_count=0`、
+`controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count=1`、
+`controlled_dry_run_runner_invocation_boundary_closeout_ready_count=1`、
+`controlled_dry_run_execution_preflight_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_write_invocation_package_closeout_ready_count=1`、
+`controlled_dry_run_invocation_receipt_closeout_ready_count=1`、
+`controlled_dry_run_apply_executor_readiness_closeout_ready_count=1`、
+`controlled_dry_run_pre_apply_replay_closeout_ready_count=1`、
+`controlled_dry_run_final_executor_guard_closeout_ready_count=1`、
+`controlled_dry_run_no_apply_enforcement_closeout_ready_count=1`、
+`controlled_dry_run_post_receipt_parser_closeout_ready_count=1`、
+`controlled_dry_run_runner_execution_receipt_closeout_ready_count=1`、
+`controlled_dry_run_command_artifact_closeout_ready_count=1`、
+`controlled_dry_run_execution_plan_closeout_ready_count=1`、
+`controlled_dry_run_runner_readiness_ready_count=1`、
+`controlled_dry_run_receipt_closeout_ready_count=1`、
+`controlled_dry_run_package_ready_count=1`、
+`controlled_apply_final_preflight_ready_count=1`、
+`controlled_executor_quarantine_proof_count=1`、
+`controlled_executor_quarantine_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout=true`、
+`final_no_runner_execution_proof_closeout_ready=true`、
+`controlled_executor_quarantine_proof_bound=true`、
+`controlled_executor_quarantine_bound=true`、`executor_quarantine_enforced=true`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`execution_receipt_present=false`、`dry_run_executor_invocation_allowed=false`、
+`runner_invocation_allowed=false`、`ready_for_dry_run_executor_invocation_now=false`、
+`ready_for_actual_dry_run_execution_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run controlled executor quarantine proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout`,
+輸出 future database apply controlled dry-run execution envelope freeze proof、
+controlled dry-run controlled executor quarantine proof closeout、dry-run execution
+envelope freeze proof 與 12 項 controlled executor quarantine proof closeout checks;
+目前仍是 controlled-executor-quarantine-proof-closeout-and-execution-envelope-freeze-proof-only /
+dry-run-execution-envelope-freeze-proof-only / dry-run-only / check-mode-only /
+no-secret-read / no-plaintext-secret / no-stdout-capture / no-stderr-capture /
+no-signature-material / no-authorization-evidence-execute /
+no-database-apply-execute / no-dry-run-executor-invocation / no-runner-invocation /
+no-endpoint-execute / no-SQL-execute / no-DB-write preview。local deterministic
+fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_CONTROLLED_EXECUTOR_QUARANTINE_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count=1`、
+`controlled_dry_run_controlled_executor_quarantine_proof_closeout_check_count=12`、
+`controlled_dry_run_controlled_executor_quarantine_proof_closeout_pass_count=12`、
+`controlled_dry_run_controlled_executor_quarantine_proof_closeout_waiting_count=0`、
+`controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count=1`、
+`dry_run_execution_envelope_freeze_proof_count=1`、
+`dry_run_execution_envelope_freeze_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout=true`、
+`controlled_executor_quarantine_proof_closeout_ready=true`、
+`dry_run_execution_envelope_freeze_proof_bound=true`、
+`execution_envelope_frozen=true`、`execution_envelope_mutation_allowed=false`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`execution_receipt_present=false`、`dry_run_executor_invocation_allowed=false`、
+`runner_invocation_allowed=false`、`ready_for_dry_run_executor_invocation_now=false`、
+`ready_for_actual_dry_run_execution_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run execution envelope freeze proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout`,
+輸出 future database apply controlled dry-run frozen envelope verifier handoff、
+controlled dry-run execution envelope freeze proof closeout、frozen envelope verifier
+handoff 與 12 項 execution envelope freeze proof closeout checks;目前仍是
+execution-envelope-freeze-proof-closeout-and-frozen-envelope-verifier-handoff-only /
+frozen-envelope-verifier-handoff-only / dry-run-only / check-mode-only /
+no-secret-read / no-plaintext-secret / no-stdout-capture / no-stderr-capture /
+no-signature-material / no-authorization-evidence-execute /
+no-database-apply-execute / no-verifier-invocation /
+no-dry-run-executor-invocation / no-runner-invocation / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_ENVELOPE_FREEZE_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count=1`、
+`controlled_dry_run_execution_envelope_freeze_proof_closeout_check_count=12`、
+`controlled_dry_run_execution_envelope_freeze_proof_closeout_pass_count=12`、
+`controlled_dry_run_execution_envelope_freeze_proof_closeout_waiting_count=0`、
+`controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count=1`、
+`controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count=1`、
+`frozen_envelope_verifier_handoff_count=1`、
+`frozen_envelope_verifier_handoff_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff=true`、
+`can_enter_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout=true`、
+`execution_envelope_freeze_proof_closeout_ready=true`、
+`frozen_envelope_verifier_handoff_bound=true`、
+`execution_envelope_frozen=true`、`execution_envelope_mutation_allowed=false`、
+`verifier_invocation_allowed=false`、`verifier_invoked=false`、
+`verifier_receipt_present=false`、`dry_run_executor_invoked=false`、
+`runner_invocation_performed=false`、`endpoint_executed=false`、
+`sql_executed=false`、`database_written=false`、
+`ready_for_database_apply_now=false`、`ready_for_verifier_invocation_now=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`verifier_invoked_count=0`、
+`verifier_receipt_present_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run frozen envelope verifier handoff closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout`,
+輸出 future database apply controlled dry-run verifier invocation lock proof、
+controlled dry-run frozen envelope verifier handoff closeout、verifier invocation
+lock proof 與 12 項 frozen envelope verifier handoff closeout checks;目前仍是
+frozen-envelope-verifier-handoff-closeout-and-verifier-invocation-lock-proof-only /
+verifier-invocation-lock-proof-only / dry-run-only / check-mode-only /
+no-secret-read / no-plaintext-secret / no-stdout-capture / no-stderr-capture /
+no-signature-material / no-authorization-evidence-execute /
+no-database-apply-execute / no-verifier-invocation /
+no-dry-run-executor-invocation / no-runner-invocation / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_FROZEN_ENVELOPE_VERIFIER_HANDOFF_CLOSEOUT_READY`、
+`controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_count=1`、
+`controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check_count=12`、
+`controlled_dry_run_frozen_envelope_verifier_handoff_closeout_pass_count=12`、
+`controlled_dry_run_frozen_envelope_verifier_handoff_closeout_waiting_count=0`、
+`controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count=1`、
+`controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count=1`、
+`verifier_invocation_lock_proof_count=1`、
+`verifier_invocation_lock_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout=true`、
+`frozen_envelope_verifier_handoff_closeout_ready=true`、
+`execution_envelope_freeze_proof_closeout_ready=true`、
+`frozen_envelope_verifier_handoff_ready=true`、
+`verifier_invocation_lock_proof_bound=true`、
+`execution_envelope_frozen=true`、`execution_envelope_mutation_allowed=false`、
+`verifier_invocation_locked=true`、`verifier_invocation_allowed=false`、
+`verifier_invoked=false`、`verifier_receipt_present=false`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`ready_for_database_apply_now=false`、`ready_for_verifier_invocation_now=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`verifier_invoked_count=0`、
+`verifier_receipt_present_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run verifier invocation lock proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout`,
+輸出 future database apply controlled dry-run verifier no-execution receipt proof、
+controlled dry-run verifier invocation lock proof closeout、verifier no-execution
+receipt proof 與 12 項 verifier invocation lock proof closeout checks;目前仍是
+verifier-invocation-lock-proof-closeout-and-verifier-no-execution-receipt-proof-only /
+verifier-no-execution-receipt-proof-only / dry-run-only / check-mode-only /
+no-secret-read / no-plaintext-secret / no-stdout-capture / no-stderr-capture /
+no-signature-material / no-authorization-evidence-execute /
+no-database-apply-execute / no-verifier-invocation /
+no-dry-run-executor-invocation / no-runner-invocation / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_INVOCATION_LOCK_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_count=1`、
+`controlled_dry_run_verifier_invocation_lock_proof_closeout_check_count=12`、
+`controlled_dry_run_verifier_invocation_lock_proof_closeout_pass_count=12`、
+`controlled_dry_run_verifier_invocation_lock_proof_closeout_waiting_count=0`、
+`controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_count=1`、
+`controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count=1`、
+`verifier_no_execution_receipt_proof_count=1`、
+`verifier_no_execution_receipt_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout=true`、
+`verifier_invocation_lock_proof_closeout_ready=true`、
+`frozen_envelope_verifier_handoff_closeout_ready=true`、
+`verifier_invocation_lock_proof_ready=true`、
+`verifier_no_execution_receipt_proof_bound=true`、
+`verifier_invocation_locked=true`、`verifier_invocation_allowed=false`、
+`verifier_invoked=false`、`verifier_receipt_present=false`、
+`verifier_receipt_required=false`、`dry_run_executor_invoked=false`、
+`runner_invocation_performed=false`、`endpoint_executed=false`、
+`sql_executed=false`、`database_written=false`、
+`ready_for_database_apply_now=false`、`ready_for_verifier_invocation_now=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`verifier_invoked_count=0`、
+`verifier_receipt_present_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run verifier no-execution receipt proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout`,
+輸出 future database apply controlled dry-run verifier receipt persistence guard proof、
+controlled dry-run verifier no-execution receipt proof closeout、verifier receipt
+persistence guard proof 與 12 項 verifier no-execution receipt proof closeout
+checks;目前仍是 verifier-no-execution-receipt-proof-closeout-and-verifier-
+receipt-persistence-guard-proof-only / verifier-receipt-persistence-guard-proof-
+only / dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-verifier-receipt-persistence / no-authorization-evidence-execute /
+no-database-apply-execute / no-verifier-invocation /
+no-dry-run-executor-invocation / no-runner-invocation / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_NO_EXECUTION_RECEIPT_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_count=1`、
+`controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check_count=12`、
+`controlled_dry_run_verifier_no_execution_receipt_proof_closeout_pass_count=12`、
+`controlled_dry_run_verifier_no_execution_receipt_proof_closeout_waiting_count=0`、
+`controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_count=1`、
+`verifier_receipt_persistence_guard_proof_count=1`、
+`verifier_receipt_persistence_guard_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout=true`、
+`verifier_no_execution_receipt_proof_closeout_ready=true`、
+`verifier_invocation_lock_proof_closeout_ready=true`、
+`verifier_no_execution_receipt_proof_ready=true`、
+`verifier_receipt_persistence_guard_proof_bound=true`、
+`verifier_receipt_persistence_locked=true`、
+`verifier_receipt_persistence_allowed=false`、
+`verifier_receipt_persisted=false`、`persists_verifier_receipt=false`、
+`verifier_invocation_locked=true`、`verifier_invocation_allowed=false`、
+`verifier_invoked=false`、`verifier_receipt_present=false`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`ready_for_database_apply_now=false`、
+`ready_for_verifier_receipt_persistence_now=false`、
+`ready_for_verifier_invocation_now=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`verifier_invoked_count=0`、
+`verifier_receipt_present_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`persists_verifier_receipt_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run verifier receipt persistence guard proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout`,
+輸出 future database apply controlled dry-run receipt persistence storage boundary proof、
+controlled dry-run verifier receipt persistence guard proof closeout、receipt
+persistence storage boundary proof 與 12 項 verifier receipt persistence guard
+proof closeout checks;目前仍是 verifier-receipt-persistence-guard-proof-
+closeout-and-receipt-persistence-storage-boundary-proof-only /
+receipt-persistence-storage-boundary-proof-only / dry-run-only / check-mode-only /
+no-secret-read / no-plaintext-secret / no-stdout-capture / no-stderr-capture /
+no-signature-material / no-verifier-receipt-persistence / no-receipt-storage-write /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-verifier-invocation / no-dry-run-executor-invocation /
+no-runner-invocation / no-endpoint-execute / no-SQL-execute / no-DB-write
+preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_RECEIPT_PERSISTENCE_GUARD_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_count=1`、
+`controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check_count=12`、
+`controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_pass_count=12`、
+`controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_waiting_count=0`、
+`controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_count=1`、
+`receipt_persistence_storage_boundary_proof_count=1`、
+`receipt_persistence_storage_boundary_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout=true`、
+`verifier_receipt_persistence_guard_proof_closeout_ready=true`、
+`verifier_no_execution_receipt_proof_closeout_ready=true`、
+`verifier_receipt_persistence_guard_proof_ready=true`、
+`receipt_persistence_storage_boundary_proof_bound=true`、
+`receipt_persistence_storage_boundary_locked=true`、
+`receipt_persistence_storage_write_allowed=false`、
+`receipt_persistence_storage_written=false`、
+`verifier_receipt_persistence_locked=true`、
+`verifier_receipt_persistence_allowed=false`、
+`verifier_receipt_persisted=false`、`persists_verifier_receipt=false`、
+`verifier_invocation_locked=true`、`verifier_invocation_allowed=false`、
+`verifier_invoked=false`、`verifier_receipt_present=false`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`ready_for_database_apply_now=false`、
+`ready_for_receipt_persistence_storage_now=false`、
+`ready_for_verifier_receipt_persistence_now=false`、
+`ready_for_verifier_invocation_now=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`verifier_invoked_count=0`、
+`verifier_receipt_present_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`persists_verifier_receipt_count=0`、
+`receipt_persistence_storage_write_allowed_count=0`、
+`receipt_persistence_storage_written_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run receipt persistence storage boundary proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout`,
+輸出 future database apply controlled dry-run storage boundary no-write ledger proof、
+controlled dry-run receipt persistence storage boundary proof closeout、storage
+boundary no-write ledger proof 與 12 項 receipt persistence storage boundary
+proof closeout checks;目前仍是
+controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout-and-storage-
+boundary-no-write-ledger-proof-only / storage-boundary-no-write-ledger-proof-only /
+dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-ledger-write / no-receipt-storage-write / no-verifier-receipt-persistence /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-verifier-invocation / no-dry-run-executor-invocation /
+no-runner-invocation / no-endpoint-execute / no-SQL-execute / no-DB-write
+preview。local deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_PERSISTENCE_STORAGE_BOUNDARY_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_count=1`、
+`controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check_count=12`、
+`controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_pass_count=12`、
+`controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_waiting_count=0`、
+`controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_count=1`、
+`storage_boundary_no_write_ledger_proof_count=1`、
+`storage_boundary_no_write_ledger_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout=true`、
+`receipt_persistence_storage_boundary_proof_closeout_ready=true`、
+`verifier_receipt_persistence_guard_proof_closeout_ready=true`、
+`receipt_persistence_storage_boundary_proof_ready=true`、
+`storage_boundary_no_write_ledger_proof_bound=true`、
+`storage_boundary_write_locked=true`、`storage_boundary_write_allowed=false`、
+`storage_boundary_written=false`、`ledger_write_allowed=false`、
+`ledger_written=false`、
+`receipt_persistence_storage_write_allowed=false`、
+`receipt_persistence_storage_written=false`、
+`verifier_receipt_persistence_locked=true`、
+`verifier_receipt_persistence_allowed=false`、
+`verifier_receipt_persisted=false`、`persists_verifier_receipt=false`、
+`verifier_invocation_locked=true`、`verifier_invocation_allowed=false`、
+`verifier_invoked=false`、`verifier_receipt_present=false`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`ready_for_database_apply_now=false`、
+`ready_for_storage_boundary_ledger_write_now=false`、
+`ready_for_receipt_persistence_storage_now=false`、
+`ready_for_verifier_receipt_persistence_now=false`、
+`ready_for_verifier_invocation_now=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`verifier_invoked_count=0`、
+`verifier_receipt_present_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`ledger_write_allowed_count=0`、`ledger_written_count=0`、
+`persists_verifier_receipt_count=0`、
+`receipt_persistence_storage_write_allowed_count=0`、
+`receipt_persistence_storage_written_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run storage boundary no-write ledger proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout`,
+輸出 future database apply controlled dry-run no-write ledger retention proof、
+controlled dry-run storage boundary no-write ledger proof closeout、no-write
+ledger retention proof 與 12 項 storage boundary no-write ledger proof closeout
+checks;目前仍是 controlled-dry-run-storage-boundary-no-write-ledger-proof-
+closeout-and-no-write-ledger-retention-proof-only /
+no-write-ledger-retention-proof-only / dry-run-only / check-mode-only /
+no-secret-read / no-plaintext-secret / no-stdout-capture / no-stderr-capture /
+no-signature-material / no-retention-write / no-ledger-write /
+no-receipt-storage-write / no-verifier-receipt-persistence /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-verifier-invocation / no-dry-run-executor-invocation / no-runner-invocation /
+no-endpoint-execute / no-SQL-execute / no-DB-write preview。local
+deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_STORAGE_BOUNDARY_NO_WRITE_LEDGER_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_count=1`、
+`controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check_count=12`、
+`controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_pass_count=12`、
+`controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_waiting_count=0`、
+`controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_count=1`、
+`no_write_ledger_retention_proof_count=1`、
+`no_write_ledger_retention_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout=true`、
+`storage_boundary_no_write_ledger_proof_closeout_ready=true`、
+`receipt_persistence_storage_boundary_proof_closeout_ready=true`、
+`storage_boundary_no_write_ledger_proof_ready=true`、
+`no_write_ledger_retention_proof_bound=true`、
+`ledger_retention_write_locked=true`、
+`ledger_retention_write_allowed=false`、
+`ledger_retention_written=false`、`ledger_write_allowed=false`、
+`ledger_written=false`、
+`receipt_persistence_storage_write_allowed=false`、
+`receipt_persistence_storage_written=false`、
+`verifier_receipt_persistence_locked=true`、
+`verifier_receipt_persistence_allowed=false`、
+`verifier_receipt_persisted=false`、`persists_verifier_receipt=false`、
+`verifier_invocation_locked=true`、`verifier_invocation_allowed=false`、
+`verifier_invoked=false`、`verifier_receipt_present=false`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`ready_for_database_apply_now=false`、
+`ready_for_no_write_ledger_retention_now=false`、
+`ready_for_storage_boundary_ledger_write_now=false`、
+`ready_for_receipt_persistence_storage_now=false`、
+`ready_for_verifier_receipt_persistence_now=false`、
+`ready_for_verifier_invocation_now=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`verifier_invoked_count=0`、
+`verifier_receipt_present_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`ledger_retention_write_allowed_count=0`、
+`ledger_retention_written_count=0`、
+`ledger_write_allowed_count=0`、`ledger_written_count=0`、
+`persists_verifier_receipt_count=0`、
+`receipt_persistence_storage_write_allowed_count=0`、
+`receipt_persistence_storage_written_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run no-write ledger retention proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout`,
+輸出 future database apply controlled dry-run retention boundary no-write archive proof、
+controlled dry-run no-write ledger retention proof closeout、retention boundary
+no-write archive proof 與 12 項 no-write ledger retention proof closeout checks;
+目前仍是 controlled-dry-run-no-write-ledger-retention-proof-closeout-and-retention-
+boundary-no-write-archive-proof-only / retention-boundary-no-write-archive-proof-
+only / dry-run-only / check-mode-only / no-secret-read / no-plaintext-secret /
+no-stdout-capture / no-stderr-capture / no-signature-material /
+no-archive-write / no-retention-write / no-ledger-write /
+no-receipt-storage-write / no-verifier-receipt-persistence /
+no-authorization-evidence-execute / no-database-apply-execute /
+no-verifier-invocation / no-dry-run-executor-invocation / no-runner-invocation /
+no-endpoint-execute / no-SQL-execute / no-DB-write preview。local
+deterministic fake-fetch smoke 目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_LEDGER_RETENTION_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_count=1`、
+`controlled_dry_run_no_write_ledger_retention_proof_closeout_check_count=12`、
+`controlled_dry_run_no_write_ledger_retention_proof_closeout_pass_count=12`、
+`controlled_dry_run_no_write_ledger_retention_proof_closeout_waiting_count=0`、
+`controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_count=1`、
+`retention_boundary_no_write_archive_proof_count=1`、
+`retention_boundary_no_write_archive_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout=true`、
+`no_write_ledger_retention_proof_closeout_ready=true`、
+`storage_boundary_no_write_ledger_proof_closeout_ready=true`、
+`no_write_ledger_retention_proof_ready=true`、
+`retention_boundary_no_write_archive_proof_bound=true`、
+`retention_archive_write_locked=true`、
+`retention_archive_write_allowed=false`、
+`retention_archive_written=false`、
+`ledger_retention_write_locked=true`、
+`ledger_retention_write_allowed=false`、
+`ledger_retention_written=false`、`ledger_write_allowed=false`、
+`ledger_written=false`、
+`receipt_persistence_storage_write_allowed=false`、
+`receipt_persistence_storage_written=false`、
+`verifier_receipt_persistence_locked=true`、
+`verifier_receipt_persistence_allowed=false`、
+`verifier_receipt_persisted=false`、`persists_verifier_receipt=false`、
+`verifier_invocation_locked=true`、`verifier_invocation_allowed=false`、
+`verifier_invoked=false`、`verifier_receipt_present=false`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`ready_for_database_apply_now=false`、
+`ready_for_retention_boundary_archive_now=false`、
+`ready_for_no_write_ledger_retention_now=false`、
+`ready_for_storage_boundary_ledger_write_now=false`、
+`ready_for_receipt_persistence_storage_now=false`、
+`ready_for_verifier_receipt_persistence_now=false`、
+`ready_for_verifier_invocation_now=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`reads_secret_count=0`、`executes_endpoint_count=0`、`executes_sql_count=0`、
+`writes_database_count=0`、`verifier_invoked_count=0`、
+`verifier_receipt_present_count=0`、`dry_run_executor_invoked_count=0`、
+`runner_invocation_performed_count=0`、`endpoint_executed_count=0`、
+`sql_executed_count=0`、`database_written_count=0`、
+`retention_archive_write_allowed_count=0`、
+`retention_archive_written_count=0`、
+`ledger_retention_write_allowed_count=0`、
+`ledger_retention_written_count=0`、
+`ledger_write_allowed_count=0`、`ledger_written_count=0`、
+`persists_verifier_receipt_count=0`、
+`receipt_persistence_storage_write_allowed_count=0`、
+`receipt_persistence_storage_written_count=0`、
+`signs_database_apply_authorization_count=0`。
+
+DB apply controlled dry-run retention boundary no-write archive proof closeout 已接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout`,
+輸出 future database apply controlled dry-run archive retention sealed handoff proof、
+controlled dry-run retention boundary no-write archive proof closeout、archive
+retention sealed handoff proof 與 12 項 archive proof closeout checks;目前
+仍是 controlled-dry-run-retention-boundary-no-write-archive-proof-closeout-and-
+archive-retention-sealed-handoff-proof-only / archive-retention-sealed-handoff-
+proof-only / dry-run-only / check-mode-only / no-secret-read /
+no-plaintext-secret / no-stdout-capture / no-stderr-capture /
+no-signature-material / no-handoff-write / no-archive-write /
+no-retention-write / no-ledger-write / no-receipt-storage-write /
+no-verifier-receipt-persistence / no-authorization-evidence-execute /
+no-database-apply-execute / no-verifier-invocation /
+no-dry-run-executor-invocation / no-runner-invocation / no-endpoint-execute /
+no-SQL-execute / no-DB-write preview。local deterministic fake-fetch smoke
+目標為
+`DB_APPLY_CONTROLLED_DRY_RUN_RETENTION_BOUNDARY_NO_WRITE_ARCHIVE_PROOF_CLOSEOUT_READY`、
+`controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_ready_count=1`、
+`controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check_count=12`、
+`controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_pass_count=12`、
+`controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_waiting_count=0`、
+`controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_count=1`、
+`archive_retention_sealed_handoff_proof_count=1`、
+`archive_retention_sealed_handoff_proof_field_count=12`、
+`ready_for_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof=true`、
+`can_enter_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout=true`、
+`retention_boundary_no_write_archive_proof_closeout_ready=true`、
+`no_write_ledger_retention_proof_closeout_ready=true`、
+`retention_boundary_no_write_archive_proof_ready=true`、
+`archive_retention_sealed_handoff_proof_bound=true`、
+`sealed_handoff_write_locked=true`、
+`sealed_handoff_write_allowed=false`、
+`sealed_handoff_written=false`、
+`retention_archive_write_locked=true`、
+`retention_archive_write_allowed=false`、
+`retention_archive_written=false`、
+`ledger_retention_write_locked=true`、
+`ledger_retention_write_allowed=false`、
+`ledger_retention_written=false`、`ledger_write_allowed=false`、
+`ledger_written=false`、
+`receipt_persistence_storage_write_allowed=false`、
+`receipt_persistence_storage_written=false`、
+`verifier_receipt_persistence_locked=true`、
+`verifier_receipt_persistence_allowed=false`、
+`verifier_receipt_persisted=false`、`persists_verifier_receipt=false`、
+`verifier_invocation_locked=true`、`verifier_invocation_allowed=false`、
+`verifier_invoked=false`、`verifier_receipt_present=false`、
+`dry_run_executor_invoked=false`、`runner_invocation_performed=false`、
+`endpoint_executed=false`、`sql_executed=false`、`database_written=false`、
+`ready_for_database_apply_now=false`、
+`ready_for_archive_retention_sealed_handoff_write_now=false`、
+`ready_for_retention_boundary_archive_now=false`、
+`ready_for_no_write_ledger_retention_now=false`、
+`ready_for_storage_boundary_ledger_write_now=false`、
+`ready_for_receipt_persistence_storage_now=false`、
+`ready_for_verifier_receipt_persistence_now=false`、
+`ready_for_verifier_invocation_now=false`、
+`ready_for_dry_run_executor_invocation_now=false`、
+`endpoint_execution_allowed=false`、`sql_execution_allowed=false`、
+`database_write_allowed=false`、`database_apply_authorized=false`、
+`executes_endpoint=false`、`executes_sql=false`、`writes_database=false`、
+`stdout_included=false`、`stderr_included=false`、
+`sealed_handoff_write_allowed_count=0`、
+`sealed_handoff_written_count=0`、
+`retention_archive_write_allowed_count=0`、
+`retention_archive_written_count=0`、
+`ledger_retention_write_allowed_count=0`、
+`ledger_retention_written_count=0`、
+`ledger_write_allowed_count=0`、`ledger_written_count=0`、
+`persists_verifier_receipt_count=0`、
+`receipt_persistence_storage_write_allowed_count=0`、
+`receipt_persistence_storage_written_count=0`、
+`signs_database_apply_authorization_count=0`。下一個 P0
+是 future database apply controlled dry-run archive retention sealed handoff proof closeout /
+sealed handoff verifier transfer proof,仍不是直接 DB apply。
+
+2026-07-01 正式 readback 收斂:上述 retention boundary no-write archive proof
+closeout route 已部署到 V10.725 正式環境,預設 `response_mode=compact`,
+不再回傳完整 nested proof payload;完整 payload 僅允許 `full=1`。正式
+驗證為 `HTTP 200`、`content-length=8697`、`checks=12`、
+`writes_database_count=0`、`executes_sql_count=0`、
+`sealed_handoff_write_locked_count=1`、sealed handoff manifest hash 長度 64、
+`database_apply_authorized=false`、`writes_database=false`,`/health` 仍是
+healthy PostgreSQL `V10.725`。
+
+DB apply controlled dry-run archive retention sealed handoff proof closeout 已本地
+接上
+`/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-archive-retention-sealed-handoff-proof-closeout`,
+預設 `response_mode=compact`,輸出 future database apply controlled dry-run
+sealed handoff verifier transfer proof、archive retention sealed handoff proof
+closeout、sealed handoff verifier transfer proof 與 12 項 closeout checks。
+local focused tests 已通過:`archive_retention_sealed_handoff_proof_closeout`
+為 `2 passed`,retention boundary + archive retention chain 為 `5 passed`,
+完整 PChome mapping backlog test file 為 `218 passed`。正式 V10.725 已
+部署並驗證:`HTTP 200`、`content-length=8425`、`response_mode=compact`、
+`checks=12`、`writes_database_count=0`、`executes_sql_count=0`、
+`verifier_invoked_count=0`、verifier transfer manifest hash 長度 64、
+`database_apply_authorized=false`、`writes_database=false`,`/health` 仍是
+healthy PostgreSQL `V10.725`。目前仍是 archive-retention-sealed-handoff-proof-closeout-and-
+sealed-handoff-verifier-transfer-proof-only / dry-run-only / check-mode-only /
+no-secret-read / no-plaintext-secret / no-signature-material /
+no-verifier-transfer-write / no-handoff-write / no-verifier-invocation /
+no-verifier-receipt-persistence / no-authorization-evidence-execute /
+no-database-apply-execute / no-endpoint-execute / no-SQL-execute / no-DB-write
+preview。下一個 P0 是 sealed handoff verifier transfer proof closeout,仍不是
+直接 DB apply。
+
================================================================================
前端 V3 守門落地 + FastAPI 重新評估 (2026-05-12) [IN PROGRESS]
================================================================================
@@ -6,7 +2080,7 @@
【已完成】
- V10.601 收斂 Gemini / 111 治理與全 repo 已知密鑰清除:正式 `ai_calls` 近 24 小時與近 7 天 provider 彙總未見 Gemini 出站;舊 K8s manifest、n8n workflow、監控/auto-repair scripts、Superset 文件、Google Drive token 檔與歷史文件中的已知實密鑰全部改為占位符,並補測試禁止 Google API/OAuth key、Telegram token、Ollama Cloud key、Superset 預設密碼再次入庫;OpenClaw 日/週/月/Meta 等敘事長報告改為 GCP-A/GCP-B only,不再讓 `openclaw_meta` 在 GCP 超時後落到 111,避免 111 被長文生成壓高負載。
- V10.600 收斂 AI Intelligence 競品表前台文案:PChome 競品卡片 footer 不再顯示 `TTL: 6h`、比對門檻等工程參數,改為「僅顯示已通過身份比對的競品」;`identity_v2`、`match_type_exact`、`price_alert_exact`、`evidence_*`、`match_*` 等內部診斷 tag 只會轉成營運可讀的中文 badge,未知 tag 直接隱藏,避免把 matcher 內部碼或實驗性標記露給使用者。
- - V10.599 重整 PChome 比價覆核工作台 UX 並補全站巡檢能力:覆核頁不再沿用首頁商品表格,也不再把 `matcher_rescore`、`stored_status`、`rescore_accepted_current`、`HITL`、`COMPLETE` 等內部診斷/狀態碼輸出到前台或 tooltip;改為「商品 / MOMO、PChome 候選、覆核判讀、下一步、紀錄」六欄工作流。同步修正 catalog review status 的前台語義、決策信封中文標籤、局部 1540px 橫向工作台、手機版欄位 label,並把覆核狀態分段列改為自適應 grid,避免 chip 造成桌面/平板/手機視覺溢出;`check_responsive_overflow.js` 改為逐頁輸出、HTTPS context、commit+body ready、timeout 後安全收尾,讓桌面/平板/手機全站 UX 巡檢可追蹤;topbar AI 觀測台 indicator 增加前端 60 秒 session cache / 2.5 秒 abort 與後端 30 秒 cache,避免每頁跳轉重複打 DB 查詢拖慢全站;`market_intel/disabled.html` 從 1MB 大型停用頁改為輕量狀態頁,保留狀態與正式操作入口,避免停用模組拖慢巡檢與使用者操作;新增憲法第 14.2 條與測試 guard,禁止把工作視窗溝通、施工紀錄或版本發布說明放到使用者可見前端頁面;ICAIM 競情 API 改為 120 秒短快取、5 秒 PostgreSQL statement timeout、stale 快照降級與 LATERAL 最新價查詢,避免 AI 競情看板重查詢拖慢全站。
+ - V10.599 重整 PChome 比價覆核工作台 UX 並補全站巡檢能力:覆核頁不再沿用首頁商品表格,也不再把 `matcher_rescore`、`stored_status`、`rescore_accepted_current`、`AI 例外決策`、`COMPLETE` 等內部診斷/狀態碼輸出到前台或 tooltip;改為「商品 / MOMO、PChome 候選、覆核判讀、下一步、紀錄」六欄工作流。同步修正 catalog review status 的前台語義、決策信封中文標籤、局部 1540px 橫向工作台、手機版欄位 label,並把覆核狀態分段列改為自適應 grid,避免 chip 造成桌面/平板/手機視覺溢出;`check_responsive_overflow.js` 改為逐頁輸出、HTTPS context、commit+body ready、timeout 後安全收尾,讓桌面/平板/手機全站 UX 巡檢可追蹤;topbar AI 觀測台 indicator 增加前端 60 秒 session cache / 2.5 秒 abort 與後端 30 秒 cache,避免每頁跳轉重複打 DB 查詢拖慢全站;`market_intel/disabled.html` 從 1MB 大型停用頁改為輕量狀態頁,保留狀態與正式操作入口,避免停用模組拖慢巡檢與使用者操作;新增憲法第 14.2 條與測試 guard,禁止把工作視窗溝通、施工紀錄或版本發布說明放到使用者可見前端頁面;ICAIM 競情 API 改為 120 秒短快取、5 秒 PostgreSQL statement timeout、stale 快照降級與 LATERAL 最新價查詢,避免 AI 競情看板重查詢拖慢全站。
- V10.584 補 PChome Nick 去重與 stale recovery 單品窄門:`Nick` 先去 HTML / 行銷星號 / 重複品名,避免 `29g`、`100ml` 被同一商品副標重複計數成 `component_count_conflict`;同步新增 NIVEA 妮維雅霜 100ml、Schick 舒綺敏感肌除毛刀片 3 入、TS6 沁涼潔淨慕斯 100g 的具名 exact total-price alignment。IBL 沐浴精+洗髮精 vs 洗髮精仍保留 identity review,唇釉色號/目錄款與 Paula's Choice 效期/金蓋差異仍不自動寫正式價差。
- V10.583 補 Paula's Choice 身體乳 PChome Nick 具名 alignment:`2%水楊酸身體乳210ml二入` 可和 PChome `Nick` 補出的 `水楊酸身體乳雙入組 / 210ml x2` 對齊,進 `exact / total_price / price_alert_exact`;但 `118ml二入組(金蓋限定版)` 對上 PChome 效期品仍保留 `manual_review / identity_review`,不泛用放寬中文入數。
- V10.582 補 PChome 比價通知專業分級與 Nick 副標身份證據:NemoTron 價格決策信封現在保留 `momo_price`、`competitor_price`、`candidate_gap_pct` 與 `sales_7d_delta_pct`,EventRouter / Telegram 模板會把 `match_type / price_basis / alert_tier` 翻成「直接價格威脅、單位價覆核、身份覆核、壓制告警」與操作邊界;PChome crawler 會保留 `Nick` 副標為 `match_name` 給 matcher 使用,UI/DB 顯示仍維持原品名,讓容量、入數、濃度資訊可參與比對。
@@ -16,14 +2090,14 @@
- V10.578 修正 Code Review 靜態掃描 timeout 誤報:Hermes deterministic scan 對 `requests.get/post/...` 會檢查同一呼叫 block 的後續行,多行呼叫已帶 `timeout=` 時不再報「HTTP request 未設定 timeout」。避免 V10.577 的 preflight helper 因多行格式被自己誤判為 MEDIUM。
- V10.577 補 Code Review Ollama host preflight:OpenClaw 架構評估在 explicit GCP host generate 前先以短 `/api/version` 探測健康度,GCP-A 不通時會快速跳 GCP-B,不再等 15 秒 generate timeout;仍維持 GCP-A/GCP-B 優先、111 預設禁用、Gemini hard-disabled 預設不呼叫。
- V10.576 補 PChome backfill backlog 的型錄 lane counts,讓 `/api/ai/pchome-match/backfill/status` 也能回傳 `catalog_variant_review`、`catalog_unit_review`、`catalog_identity_review` 三條操作隊列;同版修正 `OllamaService.generate(allow_111_fallback=False)`,當 lazy resolver 快取到 111 時會強制改試 GCP-A/GCP-B allowlist,不再直接 `all 0 hosts failed`,且仍不把長分析推給 111。
- - V10.575 拆分 PChome 型錄可比覆核 lane:`catalog_comparable` 不再只是一個總數,正式拆成 `catalog_variant_review`(選項/色號/款式待核)、`catalog_unit_review`(入數/檔期/商業條件待核)與 `catalog_identity_review`(身份採用待核)。Coverage、review queue filter、Dashboard 分段、decision envelope、Webcrumbs host data 都共用同一套 SQL helper 與 metadata;仍維持 HITL、不自動寫正式價差,讓營運可批次清理最有機會轉成單位價或正式身份的候選。
- - V10.574 接上 PChome 型錄/任選可比覆核隊列:沿用 V10.572 的 `catalog_comparable_count` 安全口徑,將高分、無 hard veto、具同品線身份證據但仍有任選/型錄/商業條件待確認的 `true_low_confidence` 候選,拆成獨立 `catalog_comparable` 篩選與 decision envelope。此隊列仍維持 HITL,不寫入正式 `competitor_prices`、不算 exact matched,並把「型錄可比」與真正「證據不足」分開,讓營運可以先批次處理最有機會轉成單位價或正式身份的候選。
+ - V10.575 拆分 PChome 型錄可比覆核 lane:`catalog_comparable` 不再只是一個總數,正式拆成 `catalog_variant_review`(選項/色號/款式待核)、`catalog_unit_review`(入數/檔期/商業條件待核)與 `catalog_identity_review`(身份採用待核)。Coverage、review queue filter、Dashboard 分段、decision envelope、Webcrumbs host data 都共用同一套 SQL helper 與 metadata;仍維持 AI 例外決策、不自動寫正式價差,讓營運可批次清理最有機會轉成單位價或正式身份的候選。
+ - V10.574 接上 PChome 型錄/任選可比覆核隊列:沿用 V10.572 的 `catalog_comparable_count` 安全口徑,將高分、無 hard veto、具同品線身份證據但仍有任選/型錄/商業條件待確認的 `true_low_confidence` 候選,拆成獨立 `catalog_comparable` 篩選與 decision envelope。此隊列仍維持 AI 例外決策,不寫入正式 `competitor_prices`、不算 exact matched,並把「型錄可比」與真正「證據不足」分開,讓營運可以先批次處理最有機會轉成單位價或正式身份的候選。
- V10.574 新增市場情報 Source Governance → Fetch Target bridge:新增 `/api/market_intel/mcp_fetch_target_source_governance_review`、市場情報頁 bridge panel 與 deployment readiness smoke target,交叉審核 Professional Source Governance 與 MCP Fetch Target Review,要求每個 target `platform_code/source_key` 都能對上已通過治理的公開 source contract;仍不抓外站、不讀 robots/sitemap、不開 DB、不寫檔、不執行 CLI、不掛 scheduler。
- V10.572 新增 PChome 決策支援覆蓋率:不放寬 `matched` / `decision_ready` 的 exact identity 門檻,另外把高分、無 hard veto、具同品線與規格證據,但因「任選 / 色號 / 型錄 / 即期」仍需覆核的候選,納入 `catalog_comparable_count` 與 `decision_support_rate`。Dashboard、當日業績、成長分析與 backfill 狀態摘要同步顯示「決策支援覆蓋率 / 精準可告警覆蓋 / 型錄可比 / 單位價」,讓覆蓋率提升建立在可解釋情報分層上,而不是把非 exact 商品硬寫成正式同款。
- V10.571 提升 PChome pending 覆蓋率搜尋召回:`PCHOME_FEEDER_MAX_SEARCH_TERMS` 預設由 5 提升到 6,新增 `PCHOME_FEEDER_SEARCH_COVERAGE_RESCUE_ENABLED`,在主要搜尋詞與原始名稱 fallback 之間插入狹義 coverage rescue terms。搜尋詞會保留 `5.5g`、`2.4g` 等小數規格,不再變成 `5 5g` / `2 4g`;同時排除外出清潔、卸除髒汙、卸防曬等非身份核心噪音。正式 pilot 顯示 CeraVe / TUNEMAKERS / Embryolisse / Neogence / NIVEA 這類雙語品牌商品常卡在 PChome 搜尋召回,因此補上「英文品牌 + 中文品牌 + 核心身份 + 規格」窄搜尋詞;「品牌 + 品類 + 規格」仍只開給安全品類,避免為了拉 pending 覆蓋率引入假陽性。
- V10.570 補 PChome 身份 / 報價證據契約:matcher 的 `match_diagnostic_json` 新增 `identity_evidence`、`offer_evidence`,把品牌、品類、identity anchor、型號、規格、入數與 variant guardrail 拆成結構化證據;覆核隊列與 decision envelope 新增 `difference_highlights`,可直接指出容量、入數、色號、香味、款式、補充包、檔期組合等差異。價格明確標記為 offer evidence,不再被誤當身份證據,Dashboard / PPT / OpenClaw / Webcrumbs 能共用同一份比對證據。
- 外部專業 benchmark 固定節奏:已建立每週一 09:30 自動檢視,並新增 `docs/guides/external_professional_benchmark.md`,把 Google Merchant Center、Google Product structured data、Schema.org Product/Offer/AggregateOffer 與 Baymard 電商 UX 做法轉成可落地準則:identity evidence、fresh offer、review 差異高亮、PPT/AI evidence 分層。
- - V10.565 補 PChome 覆蓋率操作建議:`/api/ai/pchome-match/backfill/status` 會把低覆蓋率拆成 `operation_backlog`,分別列出刷新舊 identity、重評近門檻、補抓未配對、人工覆核、單位價覆核與過期搜尋救援預覽;同時回傳 `recommended_next_action`,Dashboard 狀態摘要會顯示「建議執行比價補強 / 刷新過期 identity / 處理覆核」等下一步,讓覆蓋率 KPI 直接連到可執行行動。
+ - V10.565 補 PChome 覆蓋率操作建議:`/api/ai/pchome-match/backfill/status` 會把低覆蓋率拆成 `operation_backlog`,分別列出刷新舊 identity、重評近門檻、補抓未配對、AI 例外決策、單位價覆核與過期搜尋救援預覽;同時回傳 `recommended_next_action`,Dashboard 狀態摘要會顯示「建議執行比價補強 / 刷新過期 identity / 處理覆核」等下一步,讓覆蓋率 KPI 直接連到可執行行動。
- V10.563 收斂正式 preview 假可救候選:M.A.C 超持妝輕透濾鏡蜜粉若只有 PChome 端出現明確色號(例如 `#絕絕紫`),會標成 `variant_selection_review` 並維持 `true_low_confidence`,不再佔 recoverable 池;SAUGELLA 賽吉兒菁萃潔浴凝露新增潤澤 / 日用型 / 加強 / 黃金女郎型變體互斥,避免同品線不同私密清潔款式被誤救成 matched。
- V10.566 新增市場情報 Professional Source Governance:把 robots/REP、sitemap/lastmod、JSON-LD / schema.org structured data、canonical URL、rate limit、公開資料邊界、provenance、snapshot hash 與 idempotency key 變成可審核 source contract。新增 `/api/market_intel/mcp_professional_source_governance` 與市場情報頁卡片、deployment readiness smoke target;API/UI 只審核操作員貼回的治理摘要,不抓外站、不讀 robots/sitemap、不開 DB、不寫檔、不掛 scheduler,後續 fetch target review 才能引用通過治理的來源。
- V10.561 補 PChome 比價補強前端分段回饋:Dashboard 的 PChome 卡片從「補抓產線」改為「比價補強產線」,按鈕與確認文案同步說明會先刷新舊 identity、再重評近門檻與補抓未配對;結果區新增刷新 / 重評 / 補抓三段 matched/total 摘要,避免後端已完成分段統計但操作員仍只看到一個籠統成功數。
@@ -33,36 +2107,36 @@
- V10.557 收緊 focused reason-based 回刷 guard:上一版 reason-based 回刷現在不只要求 `focused_exact_total_price_safe`,還必須同時命中一條具名 `focused_exact_identity_*` 且該 identity 屬於 matcher 的 total-price safe set。這避免未來只有總開關、缺少具名身份證據的舊 attempt 被納入回刷;rom&nd / Solone / Summer’s Eve 等 review-only focused line 仍被測試鎖在自動價差線外。
- V10.556 修 Ollama GCP-B model fallback:GCP-B 若缺 coder/large 指定模型,先用 host-compatible fallback `gemma3:4b` 留在 GCP-B,不直接把流量推到 111;`model not found` 404 視為模型缺失,不再把整台 GCP-B 標 unhealthy。主機順序仍維持 GCP-A → GCP-B → 111。
- V10.555 補 focused total-price reason-based 回刷:`_fetch_retryable_candidate_skus()` 新增一條結構化 reason 窄門,只要舊 attempt 已帶 `focused_exact_total_price_safe` 且命中 matcher 的 `FOCUSED_IDENTITY_TOTAL_PRICE_REASONS`,即可進近門檻重評;仍要求無 hard veto、`exact_identity`、分數下限,並排除 commercial / variant / count / bundle 等阻擋理由。這讓已經被 matcher 明確判為 total-price exact 的舊候選不再依賴手寫商品名 SQL 才能回刷,同時 rom&nd / Solone / Summer’s Eve 等 review-only 品線仍不會進自動價差線。
- - V10.554 接線香氛 / 精油 focused exact 回刷:Herb24 晨霧純精油擴香儀黑色、Pavaruni 40 香味 10ml 精油、Pavaruni 20 香味 450g 香氛蠟燭、Derma 大地有機植萃護膚油 150ml 現在明確標記 `focused_exact_total_price_safe`,並接進 `_fetch_retryable_candidate_skus()` 近門檻舊候選回刷。此入口只收 `low_score / refresh_low_score / true_low_confidence` 中命中精準名稱錨點、無 hard veto、`exact_identity` 且沒有變體 / 商業條件 / 件數衝突的候選;Laundrin、好物良品融蠟燈、Yuskin 等仍保留人工覆核,不為了拉覆蓋率強推自動價差。
+ - V10.554 接線香氛 / 精油 focused exact 回刷:Herb24 晨霧純精油擴香儀黑色、Pavaruni 40 香味 10ml 精油、Pavaruni 20 香味 450g 香氛蠟燭、Derma 大地有機植萃護膚油 150ml 現在明確標記 `focused_exact_total_price_safe`,並接進 `_fetch_retryable_candidate_skus()` 近門檻舊候選回刷。此入口只收 `low_score / refresh_low_score / true_low_confidence` 中命中精準名稱錨點、無 hard veto、`exact_identity` 且沒有變體 / 商業條件 / 件數衝突的候選;Laundrin、好物良品融蠟燈、Yuskin 等仍保留AI 例外決策,不為了拉覆蓋率強推自動價差。
- V10.553 優化 current PPT/AI 比價結果查詢:`fetch_competitor_comparison_results()` 的 current/latest MOMO 價格改用 `JOIN LATERAL` 取單品最新價,移除 `ROW_NUMBER() OVER (PARTITION BY p.id ...)` window scan;歷史報表的 `end_date` cutoff 仍保留在 lateral 子查詢內,維持「指定期間截止日前最新 MOMO 價」語意不變。這能降低簡報、OpenClaw/AI payload 與比價匯出在大量 price_records 下的查詢成本。
- V10.552 收斂決策查詢的新鮮度口徑:`fetch_top_competitor_risks()`、PChome review queue、review sample 與 current PPT/AI 比價結果都不再把 `expires_at IS NULL` 當成有效現價,只接受 `expires_at > CURRENT_TIMESTAMP` 的 PChome identity_v2 價格。未知新鮮度只留在 coverage 的診斷欄位與 V10.551 刷新入口,不再進入價格風險、簡報、AI 決策或覆核排除條件。
- V10.551 收斂未知新鮮度刷新與補抓排序:`_fetch_expired_identity_skus()` / `_fetch_expired_identity_recovery_skus()` 將 `expires_at IS NULL` 視為必須刷新或可搜尋救援的未知新鮮度 identity,和 V10.549「未知新鮮度不算可決策覆蓋率」口徑對齊;兩條路徑改用 `JOIN LATERAL` 取最新 MOMO 價,移除 product-wide window scan。`_fetch_unmatched_priority_skus()` 也改用 lateral 最新價,並優先重搜低風險 `no_result / refresh_no_result`,讓 V10.550 的安全召回詞先用在最可能被救回的商品。
- V10.550 補安全搜尋召回詞:`_build_variant_recall_search_plan()` 對低風險穩定品類新增 `品牌 + 品類` 的補搜尋詞,讓 `no_result / refresh_no_result` 更有機會找到 PChome 候選後再交給 matcher 安全判斷;美甲片、指甲油、唇彩、香氛/精油、粉底、防曬、任選/色號/款式等高 variant 風險商品不走通用召回,DASHING DIVA 仍只走既有 line-specific recall + sort fallback。此變更不改 `MIN_MATCH_SCORE`、hard veto、fresh-search write safety 或 stronger existing match 覆寫保護。
- V10.549 收斂比價新鮮度 KPI 口徑:coverage cache 升到 v10,`expires_at IS NULL` 不再算進「可用比價 / decision ready」,改拆成 `unknown_freshness_matches` / `unknown_freshness_count`,避免沒有到期時間的舊資料被當成可直接決策的新鮮價格。Dashboard / daily / growth 同步顯示未知新鮮度與「未形成有效身份配對」,並把 PChome/MOMO 價格方向文案改成 `PChome 價格壓力` / `MOMO 價格優勢`,降低誤讀。
- V10.548 接線更多 focused exact 舊候選回刷:把 matcher 已驗證可安全走 total-price 的 3W CLINIC 膠原蛋白粉底液 50ml x2、花美水 Moisture/Inclear 1.7g x3、KUSSEN 寶寶益菌屁屁膏 50ml 3 入、Lab52 齒妍堂嬰幼兒/汪汪隊牙刷 2 入接進 `_fetch_retryable_candidate_skus()` focused true-low / rescore 窄門。這只擴大「舊候選可被新版 matcher 重評」的入口,不改 `MIN_MATCH_SCORE`、hard veto、auto price write safety 或既有覆寫保護。
- - V10.547 強化單位價覆核洞察:`manual_unit_price_required` 不再只是人工狀態,覆核隊列與商品看板會重新帶出單位價換算、MOMO/PChome 單位價方向、差距百分比與處理建議;決策信封 / OpenClaw / PPT 摘要可讀到 `unit_price_insight`。人工覆核寫回也會保留原始 `match_diagnostic_json` / comparison mode / diagnostic codes,避免後續簡報、審計或 AI 策略只剩人工文案而失去 matcher 證據鏈。
- - V10.546 補近門檻舊候選回刷隊列:`run_retryable_candidate_revalidation()` 新增 `legacy_unmasked_attempt`,當最新狀態是 `no_result` / `refresh_no_result` / `expired_match` 時,可回撈同 SKU 早期近門檻候選交給最新版 matcher 重評;仍要求 candidate id、分數下限、無 hard veto、exact_identity,且不打開人工否決、單位價、identity_veto 或 protected existing match。
- - V10.545 收斂 Dashboard 比價覆蓋率口徑:coverage cache 升到 v9,新增身份覆蓋、可用比價、新鮮度、待補身份、過期身份與人工閉環欄位;商品看板和 PChome 覆核頁改只把真正待處理狀態算進「比價覆核」,人工已否決 / 人工單位價 / 需補研究改列為人工閉環;PChome competitor map 只吃有效價格、新鮮、identity_v2 最新 row,資料新鮮度也改看可用比價 row。
- - V10.544 收斂變體安全與 YES 指甲工具線:新增 YES 德悅氏指甲剪附除垢銼刀、腳皮銼腳板、藍寶石銼刀、三面拋光棒與 6/8cm 指甲剪的精準 total-price 線,要求同品牌、同工具名稱、同尺寸與同亮面/霧面/可收納/三面/不掉屑等款式訊號;同步接進 revalidation SQL。新增 MUJI / COCODOR 未知香味差異與 OPI 無型號不同色名 hard veto,HOOOME 暖燈材質差留人工覆核,搜尋詞也會優先帶香味/色名,提升 crawler 精準候選率。
- - V10.543 打通 `rescore_accepted_current` 窄門回刷:已進人工覆核池的候選若命中具名 focused exact 線,可進 `run_retryable_candidate_revalidation()` 重新評分;新增 SK-II 青春露 330ml 兩入、AMIINO 安美諾 30ml、YES 腳指甲剪刀 10.5cm、YES 極細指甲緣硬皮剪刀 9cm 的安全 total-price 線,並補 ANNY / OPI 指甲油型號 code hard veto,避免不同色號被錯配。
+ - V10.547 強化單位價覆核洞察:`manual_unit_price_required` 不再只是人工狀態,覆核隊列與商品看板會重新帶出單位價換算、MOMO/PChome 單位價方向、差距百分比與處理建議;決策信封 / OpenClaw / PPT 摘要可讀到 `unit_price_insight`。AI 例外決策寫回也會保留原始 `match_diagnostic_json` / comparison mode / diagnostic codes,避免後續簡報、審計或 AI 策略只剩人工文案而失去 matcher 證據鏈。
+ - V10.546 補近門檻舊候選回刷隊列:`run_retryable_candidate_revalidation()` 新增 `legacy_unmasked_attempt`,當最新狀態是 `no_result` / `refresh_no_result` / `expired_match` 時,可回撈同 SKU 早期近門檻候選交給最新版 matcher 重評;仍要求 candidate id、分數下限、無 hard veto、exact_identity,且不打開AI 否決、單位價、identity_veto 或 protected existing match。
+ - V10.545 收斂 Dashboard 比價覆蓋率口徑:coverage cache 升到 v9,新增身份覆蓋、可用比價、新鮮度、待補身份、過期身份與 AI 例外閉環欄位;商品看板和 PChome 覆核頁改只把真正待處理狀態算進「比價覆核」,AI 已否決 / AI 單位價 / 需補研究改列為AI 例外閉環;PChome competitor map 只吃有效價格、新鮮、identity_v2 最新 row,資料新鮮度也改看可用比價 row。
+ - V10.544 收斂變體安全與 YES 指甲工具線:新增 YES 德悅氏指甲剪附除垢銼刀、腳皮銼腳板、藍寶石銼刀、三面拋光棒與 6/8cm 指甲剪的精準 total-price 線,要求同品牌、同工具名稱、同尺寸與同亮面/霧面/可收納/三面/不掉屑等款式訊號;同步接進 revalidation SQL。新增 MUJI / COCODOR 未知香味差異與 OPI 無型號不同色名 hard veto,HOOOME 暖燈材質差留AI 例外決策,搜尋詞也會優先帶香味/色名,提升 crawler 精準候選率。
+ - V10.543 打通 `rescore_accepted_current` 窄門回刷:已進 AI 例外池的候選若命中具名 focused exact 線,可進 `run_retryable_candidate_revalidation()` 重新評分;新增 SK-II 青春露 330ml 兩入、AMIINO 安美諾 30ml、YES 腳指甲剪刀 10.5cm、YES 極細指甲緣硬皮剪刀 9cm 的安全 total-price 線,並補 ANNY / OPI 指甲油型號 code hard veto,避免不同色號被錯配。
- V10.542 拆開「可用比價覆蓋率」與「身份覆蓋率」:`decision_ready_rate = fresh identity / ACTIVE 商品數`,Dashboard 第一張 KPI 改顯示真正可進入決策、圖表、簡報的比價資料比例;daily / growth / Webcrumbs / OpenClaw payload 同步輸出,避免把身份覆蓋、新鮮率、價格可用率混成單一數字。
- V10.541 補正式覆核頁高信心 exact 線:The Ordinary 咖啡因 EGCG 單側漏 30ml、Natures Care 綿羊油同入數 125ml/125m、TOMOON 指甲剪同 L/S 尺寸、HH 私密潔淨露+衣物手洗精雙 200ml、SEBAMED 護潔露 200ml x2、YES 德悅氏 9cm 剪刀;都同步進 revalidation SQL,且 TOMOON/O.P.I 不同型號或尺寸仍不得自動通過。
- - V10.540 補 O.P.I 類光繚指彩精準型號線:雙方同為 O.P.I 類光繚 / 如膠似漆指甲油或指彩,且共享 `ISL...` 型號 token 時才允許 total-price;不同型號/色號仍不得自動寫入。同步把此族群接進 `true_low_confidence` revalidation 窄門,降低高信心指彩候選卡在人工覆核池的比例。
+ - V10.540 補 O.P.I 類光繚指彩精準型號線:雙方同為 O.P.I 類光繚 / 如膠似漆指甲油或指彩,且共享 `ISL...` 型號代碼時才允許 total-price;不同型號/色號仍不得自動寫入。同步把此族群接進 `true_low_confidence` revalidation 窄門,降低高信心指彩候選卡在 AI 例外池的比例。
- V10.539 補 PChome 任選 catalog focused exact 線:FLORTTE 水果沙拉眼線液筆 0.5ml、露得清護手霜 56g 無香/有香、Kanebo ALLIE 持采亮化 UV 防曬水凝乳 60g 雙方皆任選時可走 total-price;同步接進 revalidation queue。focused bypass 新增 commercial condition 防線,`即期品` 等商業狀態差異不會被自動寫入正式價差。
- V10.538 修 ai_calls provider CHECK 對齊:Hermes/Ollama 全失敗或未選定 host 時的 `ollama_other` 只作 telemetry bucket,migration 043 放行此值,`ai_call_logger` 也會將空值/unknown/非白名單 provider 正規化,避免觀測寫入失敗。
- V10.537 將 V10.536 focused exact 線接進 `run_retryable_candidate_revalidation()` 窄門:既有 `true_low_confidence` 舊候選若命中新品線且無 hard veto / 型別、款式、香味、件數、組合阻擋,就可重新走 matcher 寫入正式價差;有色號/香味/即期等阻擋仍不進回刷。
- V10.536 補 PChome 高分 `true_low_confidence` 安全救回線:新增花美水 Relax 薰衣草潤滑凝膠 1.7g x3、St.Clare 私密呼呼慕斯 x2 / 慕斯+噴霧組、BIOPEUTIC 果酸煥膚水凝乳 20% 150ml、台塑生醫嬰兒沐浴洗髮 3 件組、Elizabeth Arden 八小時護唇膏 SPF15 3.7g x3、理膚寶水全面修復潤唇膏 7.5ml focused total-price 規則;這些都要求同品牌、同品線與同規格/同組合,仍保留色號、香味、款式敏感品的 `variant_selection_review` 防線。
- V10.535 修 ElephantAlpha 價格 trigger statement timeout:`price_drop_alert` / `market_opportunity` / DB evidence prefetch 改為先篩最近有效 PChome identity_v2,再用 `JOIN LATERAL` 查單一 SKU 最新 MOMO 價格;保留 match_score/tags/diagnostic evidence,避免 scheduler 週期性重查整張 `price_records`。
- - V10.534 收緊 PChome rescore accepted gate:`no_match / price_basis=none / alert_tier=suppress` 不得再進 `rescore_accepted_current`,並新增 `--retract-unsafe-accepted` 退回舊的 unsafe accepted rows;Dashboard / daily / growth / OpenClaw 文案改為「重算待人工覆核」,避免操作員把人工覆核隊列誤解為可直接採用或可自動寫價。
+ - V10.534 收緊 PChome rescore accepted gate:`no_match / price_basis=none / alert_tier=suppress` 不得再進 `rescore_accepted_current`,並新增 `--retract-unsafe-accepted` 退回舊的 unsafe accepted rows;Dashboard / daily / growth / OpenClaw 文案改為「重算待 AI 例外決策」,避免操作員把 AI 例外決策隊列誤解為可直接採用或可自動寫價。
- V10.533 補 ElephantAlpha legacy OpenClaw advisory 相容:`generate_dynamic_pricing_strategy` 與既有 `generate_market_strategy` / `generate_resource_optimization_strategy` 一樣只記錄為 skipped,不再觸發 `Unrecognized step` 與 circuit breaker;避免舊協調器輸出的建議型動態定價步驟被誤解為真正可執行任務。
- V10.532 修正 PChome coverage / review queue 口徑落差:`fetch_competitor_coverage()` 的 `attempt_status` / `rescore_accepted_count` / `actionable_review_count` 改跟 review queue 一樣統計「沒有新鮮有效 identity」的商品,而不是只看「完全沒有 identity」;這讓已過期 identity 的 `rescore_accepted_current` 待審能正確顯示在 Dashboard / 狀態 API。
- V10.531 補 PChome matcher 過度保守的安全 exact 線:同品線、同規格、同數量的多件組若沒有 variant / count / bundle / commercial / unit-price 等阻擋理由,且商品型別完全對齊,允許進 `exact / total_price / price_alert_exact`;新增 DHC 純欖護唇膏 1.5g、FRUDIA 蜂蜜藍莓護唇膏 10g、SEBAMED 嬰兒護唇膏 4.8g x2、理膚寶水滋養修護潤唇膏 4.7ml 的 focused total-price 規則。負例仍鎖住混合組、香味款、粉底色號與蠟燭 catalog,不放寬全域門檻。
- V10.530 輕量化 PChome 狀態 preview 並暫停 `recover-stale` 主操作入口:`_fetch_retryable_candidate_skus()` 先從最新 `competitor_match_attempts` 縮小可重評候選,再用 `JOIN LATERAL` 只取該 SKU 最新 MOMO 價,避免 `/api/ai/pchome-match/backfill/status` 因 `price_records` 全量 window scan 超時;正式 smoke 同時顯示過期 identity fresh-search rescue 5 筆耗時約 109 秒且 0 筆成功,因此 Dashboard 移除「救援過期 40 筆」按鈕,只保留 `stale_recovery_preview` 的只讀「可救援」觀測;後端 `/api/ai/pchome-match/recover-stale` 改由 `PCHOME_STALE_RECOVERY_ENABLED=true` 顯式開關才可執行,避免操作員誤按低成功率慢路徑拖住 worker。
- V10.529 補強 `recover-stale` 名稱風險擋詞:過期 identity 搜尋救援會先排除 `+`、`x2`、`*2` 等組合暗示,以及湛藍、麋香、海洋、玫瑰、薰衣草、生理呵護、日用型、清爽、潤澤等常見變體 / 香味 / 版本詞,避免同品牌同規格但不同香味、不同膚感、不同使用情境的 stale pair 進慢速 fresh search。
- V10.528 將 `recover-stale` 救援 preview 改成輕量雙階段篩選:SQL 從過期 `competitor_prices` 小集合出發,只做 identity_v2、過期、exact/total_price/price_alert_exact 等必要條件並限制候選量,再用 `JOIN LATERAL` 取 ACTIVE 商品最新 MOMO 價;variant / catalog / commercial condition / 高風險名稱訊號改在 Python 對小樣本過濾,避免正式站看板狀態端點因全量 price_records、JSONB + regex 過重查詢拖垮 app worker。
- - V10.527 收斂 PChome 過期 identity 搜尋救援隊列:`recover-stale` 不再直接吃全部過期 `identity_v2`,改走 `_fetch_expired_identity_recovery_skus()`,只收既有正式診斷為 `exact_identity / total_price / price_alert_exact` 且無 variant、catalog、commercial condition、count、bundle、unit-price 等阻擋理由的舊配對;名稱含任選、多款、香味、色號、即期、融燭燈、香氛蠟燭等高風險訊號也先排除,避免慢速 fresh search 把人工覆核型 stale pair 全部掃進來。
+ - V10.527 收斂 PChome 過期 identity 搜尋救援隊列:`recover-stale` 不再直接吃全部過期 `identity_v2`,改走 `_fetch_expired_identity_recovery_skus()`,只收既有正式診斷為 `exact_identity / total_price / price_alert_exact` 且無 variant、catalog、commercial condition、count、bundle、unit-price 等阻擋理由的舊配對;名稱含任選、多款、香味、色號、即期、融燭燈、香氛蠟燭等高風險訊號也先排除,避免慢速 fresh search 把 AI 例外決策型 stale pair 全部掃進來。
- V10.526 將 PChome 近門檻重評池與過期 identity 搜尋救援變成可觀測、可操作產線:`preview_retryable_candidate_revalidation()` / `preview_expired_identity_recovery()` 都是 read-only,不啟動 PChome 搜尋、不呼叫 LLM、不寫 DB;`/api/ai/pchome-match/backfill/status` 回傳 `revalidation_preview` / `stale_recovery_preview`,Dashboard 顯示「可重評 / 窄門 / 可救援」數字,並新增「救援過期 40 筆」按鈕呼叫 `/api/ai/pchome-match/recover-stale`,只在舊 PChome ID 缺失或低分時走受控 fresh-search recovery,最後仍經 hard veto、auto price write safety 與 overwrite protection。
- - V10.525 補高分 review-gated exact 舊候選重評入口:`run_retryable_candidate_revalidation()` 仍以 `low_score / refresh_low_score / recoverable_low_score` 為主,只額外允許 Beauty Foot / KAMERIA / TS6 / Vaseline 這批已補 focused exact 規則、舊分數 >= 0.95、無商業狀態 / 款式 / 入數 / 組合阻擋理由的 `true_low_confidence` 進窄門重評,讓 V10.523 的安全規則可以實際回收舊資料,不把所有人工審核候選打開。
+ - V10.525 補高分 review-gated exact 舊候選重評入口:`run_retryable_candidate_revalidation()` 仍以 `low_score / refresh_low_score / recoverable_low_score` 為主,只額外允許 Beauty Foot / KAMERIA / TS6 / Vaseline 這批已補 focused exact 規則、舊分數 >= 0.95、無商業狀態 / 款式 / 入數 / 組合阻擋理由的 `true_low_confidence` 進窄門重評,讓 V10.523 的安全規則可以實際回收舊資料,不把所有AI 例外決策候選打開。
- V10.524 將「待刷新」變成可操作入口:商品看板 PChome 補抓產線新增「刷新過期 120 筆」按鈕,呼叫 `/api/ai/pchome-match/refresh-stale` 背景執行 `run_expired_identity_refresh()`,只刷新既有 `identity_v2` 的 PChome product_id,不跑 fresh search recovery、不呼叫 LLM,完成後重算 AI 挑品並清除 Dashboard / 競價快取。
- V10.523 補一批高分真同款 exact identity 比價規則:Beauty Foot 足膜、KAMERIA 積雪草足膜、TS6 蜜愛潤滑液 / 蜜桃煥白凝膠 / 極淨白+煥白組合、Vaseline 嬰兒高純修護凝膠在規格、入數、品牌與品線完全對齊時可進 `exact / total_price / price_alert_exact`,讓可用比價覆蓋增加;同時保留 TS6 香味衣物手洗精等 variant-sensitive 款式在 `manual_review`,不放寬全域門檻。
- V10.522 將 PChome 補抓狀態 API 接上 read-only coverage snapshot:`/api/ai/pchome-match/backfill/status` 會同步回傳身份覆蓋、新鮮率、待刷新與待補抓數,Dashboard 補抓產線即使沒有最近任務結果,也能直接判讀下一步該刷新過期價格或補抓未搜尋商品。
@@ -84,7 +2158,7 @@
- V10.505 新增市場情報 MCP Fetch Candidate Queue Writer Review Decision 安全預覽 gate:只審核 review inventory 通過後由操作員貼回的人工 candidate queue review decision 摘要,確認 decision identity、target table、row count、dedupe keys、`needs_review` 現態、允許決策集合、evidence refs、matched row exact-identity/variant/overwrite guard、operator confirmation 與 forbidden API actions;API 不讀 token、不執行 CLI、不開 DB、不寫 decision record、不更新 review_state、不寫 match result、不補 queue、不掛 scheduler。UI 同步新增 Decision gates / Inventory link / Decision summary / Decision rows / Boundary next 預覽區。
- V10.504 新增市場情報 MCP Fetch Candidate Queue Writer Review Inventory 安全預覽 gate:只審核 writer review handoff 通過後由操作員貼回的 read-only candidate queue inventory 摘要,確認 handoff identity、target table、row count、dedupe keys、review_state、artifact paths、read-only query result、missing/duplicate rows、operator confirmation 與 forbidden API actions;API 不讀 token、不執行 CLI、不開 DB、不寫 queue、不更新 review_state、不做 inventory query、不掛 scheduler。主 gate 拆為 inventory / gates / sample 三檔,避免單檔膨脹。
- V10.503 新增市場情報 MCP Fetch Candidate Queue Writer Review Handoff 安全預覽 gate:只審核 post-closeout inventory review 通過後的人工 candidate queue review 交接包,確認 inventory linkage、handoff identity、target table、row count、artifact paths、review contract、forbidden API actions 與 operator confirmation;API 不讀 token、不執行 CLI、不開 DB、不寫 queue、不更新 review_state、不掛 scheduler。
- - V10.502 修正 AiderHeal 自動修復診斷鏈:先做 ADR-020 檔案白名單再打 110 preflight,`tests/` finding 會明確略過而不誤報 repo preflight;Code Review 完成通知會把全數不在白名單的 finding 標成需人工處理,不再宣稱已觸發 AiderHeal;白名單放行 `services/routes/database` 子目錄 Python 檔,preflight 通知帶 stderr/stdout 細節,健康檢查同時接受 `/health` 回 `ok` 與 `healthy`。
+ - V10.502 修正 AiderHeal 自動修復診斷鏈:先做 ADR-020 檔案白名單再打 110 preflight,`tests/` finding 會明確略過而不誤報 repo preflight;Code Review 完成通知會把全數不在白名單的 finding 標成需 AI 例外處理,不再宣稱已觸發 AiderHeal;白名單放行 `services/routes/database` 子目錄 Python 檔,preflight 通知帶 stderr/stdout 細節,健康檢查同時接受 `/health` 回 `ok` 與 `healthy`。
- V10.501 新增市場情報 MCP Fetch Candidate Queue Writer Post-Closeout Inventory Review 安全預覽 gate:只審核 closeout review 後由操作員 shell 完成的 live inventory read-only 摘要,確認 closeout linkage、row count、inventory artifact、closeout review artifact、read-only query result、missing/duplicate rows 與 operator confirmation;API 不讀 token、不執行 CLI、不開 DB、不寫 queue、不做 inventory query、不掛 scheduler。
- V10.500 新增市場情報 MCP Fetch Candidate Queue Writer Run Closeout Review 安全預覽 gate:只審核 receipt review 通過後的 operator closeout 摘要,確認 receipt linkage、closeout artifact、receipt review artifact、post-closeout inventory plan、writer output / post-write smoke / backup manifest、rollback note 與 operator confirmation;API 不讀 receipt 原文、不讀 token、不執行 CLI、不開 DB、不寫 queue、不做 post-closeout query、不掛 scheduler。
- V10.499 新增市場情報 MCP Fetch Candidate Queue Writer Run Receipt Review 安全預覽 gate:只審核操作員 shell writer run 後貼回的 receipt 摘要,確認 readiness linkage、run package id、候選/dedupe keys、writer output、post-write smoke、backup path 與 operator confirmation;API 不讀 receipt 原文、不讀 token、不執行 CLI、不開 DB、不寫 queue、不做 post-write query、不掛 scheduler。
@@ -92,17 +2166,17 @@
- V10.497 新增市場情報 MCP Fetch Candidate Queue Writer Run Package Review 安全預覽 gate:只審核 CLI review 通過後的 operator run package 草案,要求 package id、artifact manifest、operator shell command sequence、candidate/dedupe keys 與 CLI review 對齊;API 不產檔、不讀 approval token、不執行 CLI、不開 DB、不寫 queue、不掛 scheduler,只放行到 run readiness review。
- V10.496 新增市場情報 MCP Fetch Candidate Queue Writer CLI Review 安全預覽 gate:只審核 writer preflight 後的 CLI review 草案,確認 script path、target table、preflight id、payload row count、candidate/dedupe keys 與 command argv;禁止 API 執行 CLI、禁止 `--execute` / `--apply-real-write` / `--approval-token` 進 payload,API 不讀 token、不寫檔、不開 DB、不寫 queue、不掛 scheduler。
- V10.495 新增市場情報 MCP Fetch Candidate Queue Writer Preflight 安全預覽 gate:只審核 queue review 後的 writer preflight 草案,確認 target_table、write_mode、dedupe strategy、insert columns、payload rows 與候選 key 對齊;API 不開 DB、不執行 CLI、不建立 queue、不更新 review_state、不寫 DB、不連外、不掛 scheduler。
- - V10.494 新增市場情報 MCP Fetch Candidate Queue Review 安全預覽 gate:只審核 candidate handoff 後的人工 queue review 草案,要求候選 key 對齊、review_state 停在 needs_review、allowed actions 限定人工確認/否決/延後、queue_write_status 維持 not_persisted;API 不建立 queue、不更新 review_state、不寫 DB、不連外、不掛 scheduler。
+ - V10.494 新增市場情報 MCP Fetch Candidate Queue Review 安全預覽 gate:只審核 candidate handoff 後的人工 queue review 草案,要求候選 key 對齊、review_state 停在 needs_review、allowed actions 限定AI 自動驗證確認/否決/延後、queue_write_status 維持 not_persisted;API 不建立 queue、不更新 review_state、不寫 DB、不連外、不掛 scheduler。
- V10.493 新增市場情報 MCP Fetch Candidate Handoff Review 安全預覽 gate:只審核 parser review 後的候選交接包,確認 source/candidate key 對齊、queue policy 仍是 manual preview、候選數維持小批次、無 raw/secret/side-effect;API 不建立 queue、不寫 DB、不讀 artifact、不連外、不掛 scheduler。
- V10.492 收緊 PChome 近門檻自動回刷隊列:`retryable_candidate_revalidation` 不再把 `identity_veto`、`unit_comparable`、`true_low_confidence` 納入每日自動回刷;只處理 `recoverable_low_score` 與 legacy `low_score / refresh_low_score`,並要求無 hard veto、仍在 `exact_identity`、且具備同品線/identity anchor 證據。這讓「可救回」與「正確阻擋」在操作層面真正分流,避免為了壓低 low_score 而重跑不該自動推進的候選。
- V10.491 新增市場情報 MCP Fetch Result Parser Review 安全預覽 gate:只審核操作員貼回的 parser 結構化摘要,對齊 receipt source/path、候選必要欄位、公開 URL、小批次上限與 raw HTML/secret/side-effect 風險;API 不讀 artifact、不執行 parser CLI、不抓外站、不寫 DB、不掛 scheduler。
- - V10.489 補 PChome 低分同款人工覆核回收與 gate-pass 風險邊界:TS6 超美白香氛誘霜 120g/ml、W 修護保養蝸牛特潤修護面膜 6 片、Derma 大地 Eco 植萃護膚油 2 入,從低信心升成 `identity_review` 人工覆核候選;Clarins 輕盈美體護理油 vs 身體調和護理油、台塑生醫嬰兒沐浴/洗髮組合數量反轉、isLeaf 私密慕絲香型數量不一致改 hard veto;HOOOME 大理石暖燈 vs 泛稱經典款改留 `variant_selection_review`。正式價差表仍需人工採用才會寫入。Production 已部署 `/health=V10.489`;500 筆 read-only audit 由 V10.486 基線 `gate_pass=129 / identity_veto=1 / still_low=370` 收斂為 `gate_pass=124 / identity_veto=4 / still_low=372`。測試:完整 `pytest` 1289 passed / 9 skipped。
+ - V10.489 補 PChome 低分同款AI 例外決策回收與 gate-pass 風險邊界:TS6 超美白香氛誘霜 120g/ml、W 修護保養蝸牛特潤修護面膜 6 片、Derma 大地 Eco 植萃護膚油 2 入,從低信心升成 `identity_review` AI 例外決策候選;Clarins 輕盈美體護理油 vs 身體調和護理油、台塑生醫嬰兒沐浴/洗髮組合數量反轉、isLeaf 私密慕絲香型數量不一致改 hard veto;HOOOME 大理石暖燈 vs 泛稱經典款改留 `variant_selection_review`。正式價差表仍需 AI controlled apply 驗證後才會寫入才會寫入。Production 已部署 `/health=V10.489`;500 筆 read-only audit 由 V10.486 基線 `gate_pass=129 / identity_veto=1 / still_low=370` 收斂為 `gate_pass=124 / identity_veto=4 / still_low=372`。測試:完整 `pytest` 1289 passed / 9 skipped。
- V10.488 新增市場情報 MCP Fetch Run Receipt 安全預覽 gate,只審核操作員 dry-run receipt,不執行 CLI、不抓外站、不寫 DB。
- V10.486 補 PChome near-threshold 風險邊界:NEW DIRECTIONS 甜杏仁油 vs 酪梨油直接 `core_ingredient_line_conflict` hard veto;COCODOR 經典擴香瓶多款任選 vs generic、KAMERIA 足膜任選三款 vs 單一涼感足膜、Hakugen 白元入浴劑橘盒/綠盒不同變體都保留 `variant_selection_review`,不進可採用 gate。Production 已部署 `/health=V10.486`;240 筆 near-threshold audit `gate_pass 83→79`、`identity_veto 0→1`、`still_low 157→160`。測試:`tests/test_marketplace_product_matcher.py`、`tests/test_competitor_match_attempts_persistence.py`、`tests/test_competitor_match_attempt_rescore_audit.py` 通過。
- V10.485 補 NITORI 香氛噴霧器短型號防線:read-only near-threshold pilot 找到唯一 gate pass 為 5510 vs J82 LBR,不應入隊;matcher 現在會把 `J82` 這類短英數型號納入 NITORI diffuser model conflict,與 5510 / YX168 等不同型號一樣 hard veto。Production 已部署 `/health=V10.485`;120 筆 near-threshold audit 由 `gate_pass=1` 變 `gate_pass=0`,accepted audit `scanned=89 / gate_pass=89 / still_low=0`。測試:`tests/test_marketplace_product_matcher.py`、`tests/test_competitor_match_attempts_persistence.py`、`tests/test_competitor_match_attempt_rescore_audit.py` 通過。
- V10.484 拆分 PChome manual gate:POWERMAN 男性私密養護液 30ml、PHYSIOGEL AI 冰鎮精華露 200ml 2入、TS6 緊彈水嫩凝膠 40g、DERMA 寶寶洗髮沐浴露 150/500ml、Clarins 黃金亮眼萃 20ml、Cetaphil 長效潤膚乳 237/473ml 等明確同款可走 `exact / total_price / price_alert_exact`;COCODOR 大豆蠟燭單側多款任選改留 `variant_selection_review`,Pavaruni 雙側 20 香味蠟燭不受新型錄保護誤傷。Production 曾部署 `/health=V10.484`,並退回 COCODOR 舊 accepted 風險 1 筆。測試:`tests/test_marketplace_product_matcher.py`、`tests/test_competitor_match_attempts_persistence.py`、`tests/test_competitor_match_attempt_rescore_audit.py` 通過。
- V10.483 收斂舊 gate pass 風險:NARS 遮瑕蜜任選、LOREAL 玻尿酸啵啵精華水/液態紫熨斗 vs 水光精華、SEBAMED 洗髮乳任選、Schick 舒綺 2-in-1 型號落差、TAICEND 保護膜 vs 噴霧,現在都會保留高分但加 `variant_selection_review` 與專屬 reason,不再被 rescore 自動送進 accepted queue。Production 已部署 `/health=V10.483`;目標 5 SKU audit `gate_pass=0 / still_low=5`,並用 `--retract-variant-accepted` 退回 4 筆舊 accepted 變體風險,latest accepted audit `scanned=90 / gate_pass=90 / still_low=0`。測試:`tests/test_marketplace_product_matcher.py`、`tests/test_competitor_match_attempts_persistence.py`、`tests/test_competitor_match_attempt_rescore_audit.py` 通過。
- - V10.482 補 exact variant-safe 回收:LUSH 櫻之花身體噴霧 200ml、ARTMIS 金縷梅/蔓越莓私密清潔慕斯 250ml、SO NATURAL FIXX 120ml plain 與 Baan 原味/草莓同 catalog,若雙方同品名、同規格且同明確 variant,移除過度保守的 `variant_selection_review` 並進 `exact / total_price / price_alert_exact`;SO NATURAL 經典款/光澤款/霧面款/夏日款 catalog 對單款 120ml 仍維持人工覆核。Production 已部署 `/health=V10.482`,並只 materialize 5 筆新增 exact-line SKU 到 `rescore_accepted_current`,最新 accepted audit `scanned=94 / gate_pass=94 / still_low=0`。測試:`tests/test_marketplace_product_matcher.py`、`tests/test_competitor_match_attempts_persistence.py`、`tests/test_competitor_match_attempt_rescore_audit.py` 通過。
+ - V10.482 補 exact variant-safe 回收:LUSH 櫻之花身體噴霧 200ml、ARTMIS 金縷梅/蔓越莓私密清潔慕斯 250ml、SO NATURAL FIXX 120ml plain 與 Baan 原味/草莓同 catalog,若雙方同品名、同規格且同明確 variant,移除過度保守的 `variant_selection_review` 並進 `exact / total_price / price_alert_exact`;SO NATURAL 經典款/光澤款/霧面款/夏日款 catalog 對單款 120ml 仍維持AI 例外決策。Production 已部署 `/health=V10.482`,並只 materialize 5 筆新增 exact-line SKU 到 `rescore_accepted_current`,最新 accepted audit `scanned=94 / gate_pass=94 / still_low=0`。測試:`tests/test_marketplace_product_matcher.py`、`tests/test_competitor_match_attempts_persistence.py`、`tests/test_competitor_match_attempt_rescore_audit.py` 通過。
- V10.481 補 rescore accepted retraction 工具缺口:`--retract-variant-accepted` 不只看舊 row 已存的 `diagnostic_codes`,也會用當前 matcher 重判 latest `rescore_accepted_current`;若新版規則已變成 `variant_selection_review / low_score_current`,會追加退回 `true_low_confidence`,避免舊 accepted queue 殘留不該採用候選。Production 已先保守 materialize 15 筆安全 SKU,再退回 7 筆舊 accepted 變體風險;最終 `rescore_accepted_current=89`,accepted audit `gate_pass=89 / still_low=0`。
- V10.480 依 production accepted-current 風險樣本補安全閘門:rom&nd 零絲絨/果凍唇釉不可被果汁唇釉多款 listing 誤收為同款;Relove 潔淨凝露若一側為傳明酸/淨白活性變體改送 `variant_selection_review`;1990 融燭燈不同設計(歐式可彎 vs 韓風原木底座)改 hard veto。此版先清 accepted queue 風險,再做保守 materialize。
- V10.479 依 production audit 再補二階風險:Cetaphil 修護乳 vs 潔膚露改 hard veto;私密防護慕絲二款可選 vs 單一香型、雪芙蘭滋養霜 vs 單側清爽型改走 `variant_selection_review`,避免仍殘留在 accepted queue。
@@ -118,37 +2192,37 @@
- V10.469 將背景 embedding 的 GCP-only 全失敗改為專業降級語意,已部署正式環境並確認 `/health=V10.469`:`allow_111_fallback=False` 時若 GCP-A/GCP-B 都不可用,開啟 failure circuit 並記 WARNING,不再把可預期的背景熔斷每分鐘打成 ERROR;同步 / 允許 fallback 的 embedding 全失敗仍保留 ERROR。Smoke 顯示 GCP-B `/api/version` 可用,但 `/api/embed` 仍可能 15s timeout,下一步需修 GCP-A primary 與 GCP-B runner/model 負載。
- V10.468 補 Ollama import-time 防凍結與背景 embedding GCP failure circuit,已部署正式環境並確認 `/health=V10.468`:`config.OLLAMA_HOST` / `HERMES_URL` / `EMBEDDING_HOST` 舊相容常數不再於 import 時 probe network,也不會因 GCP-A/GCP-B 暫時拒連而 freeze 到 111;動態 caller 仍走 `get_*()` / `OllamaService` 三主機級聯。當 `allow_111_fallback=False` 且 GCP-A/GCP-B 皆失敗時,短暫熔斷 60 秒,不重複打兩台 GCP、不落 111,降低 app/scheduler 因連續 embedding timeout 造成的 log 與 worker 壓力;部署 smoke 時 GCP-B `/api/version` 已恢復 200 並成為動態路由落點,GCP-A `22/11434` 仍拒連,需後續用 GCP 權限修復 primary Ollama 主機。
- V10.467 補 PChome focused exact total-price 安全通道:針對正式近門檻樣本中已確認同品牌、同品名、同規格/同入數的 3W CLINIC 粉底液 2入、花美水凝膠 3支、The Ordinary 咖啡因 EGCG 30ml、KUSSEN 屁屁膏 3入、Bone 擴香禮盒、1990 融燭燈白色款與 CANMAKE 淚袋盤,從 `exact/manual_review` 收斂為 `exact/total_price`;未放寬 `MIN_MATCH_SCORE`,DASHING DIVA、唇彩、香味、色號/款式敏感商品仍維持 variant / veto 保護。Production pilot 已將 9 筆安全 SKU 送入 `rescore_accepted_current`,`true_low_confidence` 802→793、`rescore_accepted_current` 38→47;`6101784` 即期品保留在 `true_low_confidence`。
- - V10.466 修正 rescore audit duplicate 判斷:只在「最新 attempt 已是同候選 `rescore_accepted_current`」時跳過;若歷史曾 accepted、但後續 crawler 又追加低信心列,允許重新 materialize,避免 Dashboard latest-state 仍停在 `true_low_confidence`。Production pilot 已將 SKU `14756069`、`11159042`、`13842560`、`8394210`、`15192547`、`10509765`、`10603780` 送入人工覆核隊列;只寫 `competitor_match_attempts`,`competitor_prices` / `competitor_price_history` 未變。
+ - V10.466 修正 rescore audit duplicate 判斷:只在「最新 attempt 已是同候選 `rescore_accepted_current`」時跳過;若歷史曾 accepted、但後續 crawler 又追加低信心列,允許重新 materialize,避免 Dashboard latest-state 仍停在 `true_low_confidence`。Production pilot 已將 SKU `14756069`、`11159042`、`13842560`、`8394210`、`15192547`、`10509765`、`10603780` 送入AI 例外決策隊列;只寫 `competitor_match_attempts`,`competitor_prices` / `competitor_price_history` 未變。
- V10.465 修正 embedding fallback-disabled 控制流:`allow_111_fallback=False` 時若 resolver 回 111,不再直接退出或只試單台 GCP-B,會強制改試尚未嘗試的 GCP-A/GCP-B;背景 embedding 仍不落 111。
- - V10.464 補 rescore audit 精準 SKU pilot:`audit_competitor_match_attempt_rescore.py --sku` 可只掃指定 SKU,再搭配 `--apply-accepted` 只把通過新版 matcher 的目標 SKU 追加到 `rescore_accepted_current` 人工覆核隊列,不寫正式價格表。
+ - V10.464 補 rescore audit 精準 SKU pilot:`audit_competitor_match_attempt_rescore.py --sku` 可只掃指定 SKU,再搭配 `--apply-accepted` 只把通過新版 matcher 的目標 SKU 追加到 `rescore_accepted_current` AI 例外決策隊列,不寫正式價格表。
- V10.463 補 DR.WU / 達爾膚品牌 alias:同規格 `DR.WU 達爾膚` 與 `DR.WU` 候選不再被當成 brandless identity review,會以既有 exact_identity / total_price / price_alert_exact 閘門處理;未調整 `MIN_MATCH_SCORE`,保留 variant / hard veto 保護。
- V10.462 進一步收斂 PChome 補抓 UI 語意:Dashboard 區塊標題改為「PChome 補抓產線」,AI 中樞按鈕、前端確認與 API 訊息改為「補抓未搜尋 / 未搜尋補抓」,避免操作員把尚未搜尋的工作誤判成已有候選待審。
- - V10.461 修正商品看板 PChome 補抓優先清單的狀態語意:尚未進入搜尋/補抓的品項改顯示「尚未搜尋」與「尚未進入 PChome 補抓」,並補前端守門測試禁止回退成籠統「待比對」,避免操作員把未搜尋誤判成已有候選待人工覆核。
+ - V10.461 修正商品看板 PChome 補抓優先清單的狀態語意:尚未進入搜尋/補抓的品項改顯示「尚未搜尋」與「尚未進入 PChome 補抓」,並補前端守門測試禁止回退成籠統「待比對」,避免操作員把未搜尋誤判成已有候選待 AI 例外決策。
- V10.460 收斂 daily/growth 圖表空白誤判與 ElephantAlpha 告警信封:`page-daily-sales.js`、`page-growth.js` 的 chart 判斷改為至少有一個非零資料點才繪製 Chart.js,避免全 0 序列只畫座標軸;`resource_optimization` / `ea_escalation` 改輸出 deterministic `decision_envelope`,只使用 action_plans、CPU 實測與 hygiene evidence,不再輸出空泛「48 小時效益」敘事。
- - V10.459 強化 PChome `protected_existing_match` 決策封包:解析 `existing_match_conflict` 的既有候選、新候選與雙方 score,寫入 `decision_envelope.evidence` / `expected_impact` / `guardrails`,並把下一步明確標成「比較既有正式候選與新候選」;仍保持 `can_auto_execute=false`,避免新候選分數較高時繞過人工覆核自動覆蓋正式價差。
- - V10.458 將 OpenClaw / 競品 PPT 接上 PChome 覆核 `decision_envelope` 摘要:`competitor_intel_repository.summarize_review_decision_envelopes()` 成為共用 formatter,OpenClaw 週報/日報/月報與競品簡報 data_summary / KPI slide 都讀同一份信封文字,避免策略報告與 PPT 各自翻譯覆核狀態或遺失 HITL guardrails。
- - V10.457 將 PChome 覆核 `decision_envelope` 連到人工操作面:Dashboard 覆核卡新增決策等級、資料品質、HITL/trace 信封摘要;`/api/export/excel/pchome-review` 匯出同步增加決策信封 ID、決策類型、建議代碼、責任人、資料品質、自動執行允許與證據摘要,讓線上操作與下載檔都保留同一份 guardrails。
- - V10.456 將 PChome 覆核隊列接上 `decision_envelope` contract:`fetch_competitor_review_queue()` 與 `/api/pchome-review/queue` 每筆候選都輸出同一份 SKU、PChome 候選、match evidence、recommended_action、expected_impact 與 HITL guardrails,Dashboard、Agent、Telegram、PPT 後續不得再各自重建比價判讀格式;同版將 review queue cache key 升到 v3,避免正式環境沿用舊 payload。
- - V10.455 讓 EventRouter 對 `decision_envelope` 事件走直送證據模板:NemoTron / 價格比對已產生 SKU、PChome 候選、match evidence 與 HITL guardrails 時,不再進 L1/L2 AI 重新摘要,避免額外模型呼叫與告警文字二次發散;Telegram 決策信封同步補「標的」區塊,顯示 SKU、商品與 PChome 候選。同版補 `audit_competitor_match_attempt_rescore.py --retract-variant-accepted`,可把最新仍帶 `variant_selection_review` 的 `rescore_accepted_current` 批次追加退回 `true_low_confidence`,且不寫正式價差表。
+ - V10.459 強化 PChome `protected_existing_match` 決策封包:解析 `existing_match_conflict` 的既有候選、新候選與雙方 score,寫入 `decision_envelope.evidence` / `expected_impact` / `guardrails`,並把下一步明確標成「比較既有正式候選與新候選」;仍保持 `can_auto_execute=false`,避免新候選分數較高時繞過AI 例外決策自動覆蓋正式價差。
+ - V10.458 將 OpenClaw / 競品 PPT 接上 PChome 覆核 `decision_envelope` 摘要:`competitor_intel_repository.summarize_review_decision_envelopes()` 成為共用 formatter,OpenClaw 週報/日報/月報與競品簡報 data_summary / KPI slide 都讀同一份信封文字,避免策略報告與 PPT 各自翻譯覆核狀態或遺失 AI 例外決策 guardrails。
+ - V10.457 將 PChome 覆核 `decision_envelope` 連到人工操作面:Dashboard 覆核卡新增決策等級、資料品質、AI 例外決策/trace 信封摘要;`/api/export/excel/pchome-review` 匯出同步增加決策信封 ID、決策類型、建議代碼、責任人、資料品質、自動執行允許與證據摘要,讓線上操作與下載檔都保留同一份 guardrails。
+ - V10.456 將 PChome 覆核隊列接上 `decision_envelope` contract:`fetch_competitor_review_queue()` 與 `/api/pchome-review/queue` 每筆候選都輸出同一份 SKU、PChome 候選、match evidence、recommended_action、expected_impact 與 AI 例外決策 guardrails,Dashboard、Agent、Telegram、PPT 後續不得再各自重建比價判讀格式;同版將 review queue cache key 升到 v3,避免正式環境沿用舊 payload。
+ - V10.455 讓 EventRouter 對 `decision_envelope` 事件走直送證據模板:NemoTron / 價格比對已產生 SKU、PChome 候選、match evidence 與 AI 例外決策 guardrails 時,不再進 L1/L2 AI 重新摘要,避免額外模型呼叫與告警文字二次發散;Telegram 決策信封同步補「標的」區塊,顯示 SKU、商品與 PChome 候選。同版補 `audit_competitor_match_attempt_rescore.py --retract-variant-accepted`,可把最新仍帶 `variant_selection_review` 的 `rescore_accepted_current` 批次追加退回 `true_low_confidence`,且不寫正式價差表。
- V10.454 補 feeder / rescore 正式寫入安全閘門:matcher 若只到 `manual_review` / `identity_review` / `variant_selection_review`,例如 MOMO 多款任選唇膏對 PChome 單一款式,只能進 `true_low_confidence` 覆核,不得由 retryable replay、known identity refresh 或 rescore accepted 語意自動寫入 `competitor_prices` 正式價差。
- V10.453 補 PChome matcher 安全回收規則:新增 Herbacin 小甘菊護手霜 20ml brandless 同款 anchor;修正 `EX8` 型號不可被誤解析成 `x8` 入數;新增 GONESH / 香氛固體凝膠的一側泛稱、一側明確香味或 No. 款式 veto,避免近門檻 replay 把不同香味、不同入數商品錯寫成正式價差。
- V10.452 修正 PChome rescore audit 掃描口徑:`audit_competitor_match_attempt_rescore.py` 預設先取每個 SKU 最新 attempt,再套用 status / reason 篩選,和 Dashboard review queue 的最新狀態一致;舊 SKU/候選考古掃描需明確加 `--include-historical-candidates`,避免已修正或已入隊商品被舊低信心紀錄重複推回報表。
- - V10.451 拆分 PChome `low_score` 操作分流並補 read-only queue API:比價覆核頁把近門檻可救、證據不足、低信心舊候選拆成獨立篩選;repository 同步提供 `recoverable_low_score`、`true_low_confidence`、`legacy_low_score` 三個 status filter,`/api/pchome-review/queue` 可直接用同一套 review_status 做 smoke / operator tools 查詢,讓回刷、人工覆核與報表不再把所有低信心候選混在一起。
+ - V10.451 拆分 PChome `low_score` 操作分流並補 read-only queue API:比價覆核頁把近門檻可救、證據不足、低信心舊候選拆成獨立篩選;repository 同步提供 `recoverable_low_score`、`true_low_confidence`、`legacy_low_score` 三個 status filter,`/api/pchome-review/queue` 可直接用同一套 review_status 做 smoke / operator tools 查詢,讓回刷、AI 例外決策與報表不再把所有低信心候選混在一起。
- V10.450 補 PChome 覆核 fast-count UI 語意與重算可採用指標:預設全量覆核頁跳過 exact count 時,模板會顯示「約」作為快取總數提示;搜尋、分類、單一狀態仍是精準總數。`fetch_competitor_coverage()` 同步輸出 `rescore_accepted_count`,讓 Dashboard、daily/growth 與 OpenClaw 摘要能把「重算可採用待審」從一般覆核隊列拆出來。
- V10.449 修正 PChome 覆核 exact count 條件:只有預設「全部覆核、無搜尋、無分類」頁跳過 exact count;只要有搜尋詞、分類篩選或單一 review status,就保留精準總數,避免分頁資訊失準。
- V10.448 讓 PChome 覆核「全部」頁跳過 exact count:`review_status=all` 使用 shared overview cache 的待處理總數作為分頁總數提示,只查當頁 50 筆;單一狀態分流仍保留 exact count,降低全量覆核頁互動成本。
- V10.447 反轉 PChome 覆核頁查詢方向:review queue page 先從最新 `competitor_match_attempts` 的可覆核狀態縮小候選,再 join ACTIVE 商品與最新價,並用 `NOT EXISTS` 排除已有有效 identity_v2 正式價;避免每次「全部覆核」先掃全站 ACTIVE 商品。
- V10.446 修正 PChome 覆核頁輕量路徑的 overview timeout:覆核頁總覽改讀已存在的 shared dashboard cache / stale cache,沒有快取時只用目前覆核頁資料補足狀態,不再現場跑 `_load_competitor_decision_overview(session)` 的重型後備 SQL。
- - V10.445 補 PChome 覆核頁輕量渲染路徑:`filter=pchome_review` 不再先建完整 Dashboard `unique_items`,改為只查覆核當頁 50 筆商品、當前價、昨日價與週前價,再沿用同一張新版表格與人工覆核按鈕;降低核心比價覆核頁的全站資料負載。
- - V10.444 瘦身 PChome 覆核頁查詢:`fetch_competitor_review_queue_page()` 將覆核隊列總數與當頁資料合併在單一 SQL 內取回,避免 `/?filter=pchome_review` 為 count/page 重複掃 `latest_momo`、`latest_attempt`、`valid_competitor` CTE;保留狀態分流、人工覆核與正式價格寫入保護不變。
- - V10.443 補 PChome rescore 人工覆核入隊:`audit_competitor_match_attempt_rescore.py --apply-accepted` 只追加 `rescore_accepted_current` attempt 進人工覆核隊列,不直接寫 `competitor_prices` / `competitor_price_history`;商品看板新增「重算可採用」分流與狀態文案,讓可救回候選先由人審確認再正式更新價差。
+ - V10.445 補 PChome 覆核頁輕量渲染路徑:`filter=pchome_review` 不再先建完整 Dashboard `unique_items`,改為只查覆核當頁 50 筆商品、當前價、昨日價與週前價,再沿用同一張新版表格與 AI 例外決策按鈕;降低核心比價覆核頁的全站資料負載。
+ - V10.444 瘦身 PChome 覆核頁查詢:`fetch_competitor_review_queue_page()` 將覆核隊列總數與當頁資料合併在單一 SQL 內取回,避免 `/?filter=pchome_review` 為 count/page 重複掃 `latest_momo`、`latest_attempt`、`valid_competitor` CTE;保留狀態分流、AI 例外決策與正式價格寫入保護不變。
+ - V10.443 補 PChome rescore AI 例外決策入隊:`audit_competitor_match_attempt_rescore.py --apply-accepted` 只追加 `rescore_accepted_current` attempt 進 AI 例外決策隊列,不直接寫 `competitor_prices` / `competitor_price_history`;商品看板新增「重算可採用」分流與狀態文案,讓可救回候選先由人審確認再正式更新價差。
- V10.442 降噪 `/cicd` 舊 GitLab 探測:沒有明確啟用 `GITLAB_ENABLED=true` 與 token 時,不再打退役的 `192.168.0.110:8929` 或 SSH fallback,正式 responsive smoke 造訪 `/cicd` 只呈現空 pipeline 狀態,不污染 app logs。
- V10.441 補 PChome matcher re-score audit 與商品看板原因標籤:新增 read-only `competitor_match_attempt_rescore_audit` / `scripts/audit_competitor_match_attempt_rescore.py`,可用最新版 matcher 重新分類既有 `competitor_match_attempts`,預設不寫 DB、不更新正式價格;商品看板同步補蘭蔻/達特醫/hoi/Saugella/Lactacyd 等 focused matcher reason 中文標籤,讓「待對比」能拆成商品線不符、款式版本不符、可回刷或仍低信心。
- V10.439 收斂外部 BI / 資料協作入口:`/metabase`、`/grist` 正式頁維持 momo-pro 內部診斷 bridge,`.env.example` 與 bi profile Grist 預設改回 `https://mo.wooo.work/grist` / `GRIST_APP_HOME_URL`,並補測試禁止 `grist.wooo.work` / `awoooi` 回流到導覽設定;外部工具頁標題字級改用新版 token 與手機 media query。
- V10.414 補市場情報 MCP fetch run readiness gate:新增 `mcp_fetch_run_readiness` read-only builder、GET/POST endpoint、UI run readiness 審核面板與 deployment readiness smoke target,在 run package 後檢查 command preview、receipt path、artifact path、節流/timeout/dry-run-first 與操作員 shell-only 邊界;API/UI 不執行 CLI、不抓外站、不寫檔、不開 DB、不掛 scheduler,只放行到人工 shell dry-run 與後續 receipt gate。
- V10.412 補市場情報 MCP fetch run package gate:新增 `mcp_fetch_run_package` read-only builder、route extension、GET/POST endpoint、UI run package 審核面板與 deployment readiness smoke target,將已通過的 target review 轉成操作員可覆核的 command argv preview 與 receipt path 契約;API/UI 不執行 CLI、不抓外站、不寫檔、不開 DB、不掛 scheduler,只放行到後續 run readiness review。
- - V10.409 補市場情報 MCP fetch target review gate:新增 `mcp_fetch_target_review` read-only builder、GET/POST endpoint、UI target review 審核面板與 deployment readiness smoke target,讓 manual fetch handoff 通過後,先人工審核 adapter registry 公開入口、每平台節流、樣本數、timeout 與 rollback plan;API/UI 不保存 payload、不發外部 request、不開 DB、不寫入、不掛 scheduler,也不會自動打開 manual fetch。
- - V10.405 補市場情報 MCP manual fetch handoff gate:新增 `mcp_manual_fetch_handoff` read-only builder、GET/POST endpoint、UI handoff package 審核面板與 deployment readiness smoke target,將 runtime promotion package 與操作員安全確認合併成「可進人工 fetch gate 審核」的交接收據;API/UI 不保存 payload、不打 health、不開 DB、不抓外站、不掛 scheduler,也不會自動打開 manual fetch。
+ - V10.409 補市場情報 MCP fetch target review gate:新增 `mcp_fetch_target_review` read-only builder、GET/POST endpoint、UI target review 審核面板與 deployment readiness smoke target,讓 AI-controlled fetch handoff 通過後,先 AI 例外決策 adapter registry 公開入口、每平台節流、樣本數、timeout 與 rollback plan;API/UI 不保存 payload、不發外部 request、不開 DB、不寫入、不掛 scheduler,也不會自動打開 AI-controlled fetch。
+ - V10.405 補市場情報 MCP AI-controlled fetch handoff gate:新增 `ai_controlled_mcp_fetch_handoff` read-only builder、GET/POST endpoint、UI handoff package 審核面板與 deployment readiness smoke target,將 runtime promotion package 與操作員安全確認合併成「可進AI controlled fetch gate 驗證」的交接收據;API/UI 不保存 payload、不打 health、不開 DB、不抓外站、不掛 scheduler,也不會自動打開 AI-controlled fetch。
- V10.379 補市場情報 MCP runtime promotion gate:新增 `mcp_runtime_promotion` read-only builder、GET/POST endpoint、UI promotion package 審核面板與 deployment readiness smoke target,將 MCP activation evidence 與 runtime smoke receipt 合併成可審核的 runtime promotion package;API/UI 不保存 payload、不打 health、不開 DB、不抓外站、不掛 scheduler,也不會自動打開人工 fetch gate。
- V10.366 補市場情報 MCP runtime smoke 收據審核:新增 `mcp_runtime_smoke_receipt` read-only builder、GET/POST endpoint、UI receipt JSON 審核面板與 deployment readiness smoke target,讓操作員貼上 `/api/market_intel/mcp_readiness?execute=true&timeout=3` 的實際收據後,判斷 external/internal MCP runtime 是否可升級為已驗收;API/UI 不保存 payload、不打 health、不開 DB、不抓外站、不掛 scheduler,且會阻擋任何 DB write/commit/scheduler/writes 旗標或原始 readiness blocked reasons。
- V10.360 收斂瀏覽器自動開啟與 PChome 熱路徑:`tests/test_image_fetch.py` 改成 `RUN_MOMO_BROWSER_TESTS=1` 才會跑,預設 headless 且關閉 Chrome 密碼管理,避免一般 pytest 自動打開 MOMO 網站與觸發密碼允許提示;scheduler Selenium 同步關閉 password manager/autofill;PChome coverage/review queue 熱查詢改用 `JOIN LATERAL` 取 active 商品最新價,並補 Dashing Diva 品線召回搜尋詞。
@@ -180,40 +2254,40 @@
- V10.325 收斂 Gemini 主路徑:OpenClaw 週/月/meta/日報洞察、Telegram PPT 分析與 MCP fallback 全部改成先走 OllamaService 的 GCP-A → GCP-B → 111 三主機級聯;Gemini 只在 Ollama/NIM 不可用後作備援。Elephant Alpha resource_optimization 告警補上待處理 action_plans 焦點列表,避免只報隊列數字卻沒有可執行對象。
- V10.324 補市場情報 candidate queue review AI summary Telegram dispatch report run package:新增 read-only report run package builder、POST endpoint、UI 按鈕與 deployment readiness smoke target,在 report input 後整理 run package contract、evidence refs、package sections 與後續 report run readiness gate;API/UI 不讀 approval/Telegram token、不呼叫 LLM、不派送 Telegram、不開 DB、不寫檔、不產報表、不更新 review_state、不掛 scheduler。
- V10.323 收斂市場情報 seed writer token hardening drift:正式端 service smoke 已確認 `seed_writer_cli_status` 不再回吐 `approval_token_hint`、不洩漏環境 token;輸出補 `api_reads_approval_token=false`、`api_executes_cli=false`、`api_writes_database=false`,讓 API/CLI 邊界可被 regression test 與 smoke 明確驗證。
- - V10.322 修正 Telegram 決策/審核推播舊入口:`price_decision_notify()` 改用 `send_telegram_with_result()` 統一套用 HTML sanitizer 與多 chat 結果彙整,並補齊 `price_decision(report_url=...)` 相容;RAG awaiting review 推播改用正確 `chat_ids=[...]` 呼叫,避免 Stage 4 人工審核按鈕因參數名錯誤完全送不出去。
+ - V10.322 修正 Telegram 決策/審核推播舊入口:`price_decision_notify()` 改用 `send_telegram_with_result()` 統一套用 HTML sanitizer 與多 chat 結果彙整,並補齊 `price_decision(report_url=...)` 相容;RAG awaiting review 推播改用正確 `chat_ids=[...]` 呼叫,避免 Stage 4 AI 例外決策按鈕因參數名錯誤完全送不出去。
- V10.321 修正 Telegram HTML 發送格式:所有 `sendMessage` / `sendPhoto` caption 在 HTML parse mode 送出前會把 `
` / `
` / `
` 統一轉成換行,避免 Telegram Bot API 回 `Unsupported start tag "br"` 造成告警或報告送出失敗。
- V10.320 補市場情報 candidate queue review AI summary Telegram dispatch report input:新增 read-only report input builder、獨立 report route extension、UI 按鈕與 deployment readiness smoke target,在 archive summary 後整理 report input sections、report contract、message evidence 與 dispatch audit traceability;API/UI 不讀 approval/Telegram token、不呼叫 LLM、不派送 Telegram、不開 DB、不寫檔、不產報表、不更新 review_state、不掛 scheduler。
- V10.319 補市場情報 candidate queue review AI summary Telegram dispatch archive summary:新增 read-only archive summary builder、POST endpoint、UI 按鈕與 deployment readiness smoke target,在 Telegram dispatch archive 後整理 message identity、dispatch audit、artifact manifest 與後續 report input sections;API/UI 不讀 approval/Telegram token、不呼叫 LLM、不派送 Telegram、不開 DB、不寫檔、不更新 review_state、不掛 scheduler。
- - V10.318 收緊 Elephant Alpha HITL 告警治理:`ea_escalation` 只有真正含 SKU/價格比較的 actions 才排成 TOP 待審 SKU 卡片;非 SKU 診斷改為「待確認事項」,並用測試鎖住價格類低信心但無 DB/Hermes 實證時只 suppress、不寫 human_review、不發 Telegram,避免空泛告警打擾人工審核。
+ - V10.318 收緊 Elephant Alpha AI 例外決策 告警治理:`ea_escalation` 只有真正含 SKU/價格比較的 actions 才排成 TOP 待審 SKU 卡片;非 SKU 診斷改為「待確認事項」,並用測試鎖住價格類低信心但無 DB/Hermes 實證時只 suppress、不寫 human_review、不發 Telegram,避免空泛告警打擾AI 例外決策。
- V10.317 修正 PChome 比價覆蓋率分子:`fetch_competitor_coverage()` 的 valid_matches 改成 `ACTIVE + 有 MOMO 最新價` 商品與有效 PChome `identity_v2` 價格的交集,不再把非活躍或無 MOMO 現價的舊 competitor_prices 列入覆蓋率,避免 daily/growth/PPT/AI 報表高估比價資料品質。
- V10.315 修正競品簡報/報表指定日期取價:`fetch_competitor_comparison_results()` 在有 start/end date 時改讀 `competitor_price_history` 的期間快照,MOMO 價格也取期間結束前最新價;沒有指定日期才使用目前有效 `competitor_prices`,避免把今天的 PChome 快取價塞回歷史 daily/growth/PPT 判讀。
- V10.314 擴大 PChome 候選池與搜尋韌性:PChome 搜尋 API 改為依 limit 掃多頁並對 429/5xx/timeout 做有限重試;feeder 預設每個商品最多 5 組搜尋詞、每詞 20 候選、2 頁,且搜尋清理不再刪掉括號/方括號內的品牌與規格,讓正確候選更有機會進 matcher,而不是長期停在「待對比」。
- V10.312 強化 PChome 商品身份比對防錯配:matcher 開始解析 mg/mcg 劑量、件組套組與多規格集合,60ml+150ml vs 60ml+20ml、10mg vs 20mg、10片 vs 10盒、精華 vs 化妝水都會進硬否決或單位價覆核,不再靠單一規格重疊放行;覆核診斷同步新增「劑量差異」標籤,降低核心比價錯配污染 daily/growth/PPT/AI 分析。
- V10.311 統一競品價差語意:`fetch_competitor_comparison_results()`、competitor PPT 與 OpenClaw competitor prompt 全部改用 `MOMO - PChome`,正值代表 MOMO 較貴 / PChome 低價壓力,負值代表 MOMO 價格優勢;避免 daily/growth 顯示價格壓力但 PPT/AI 反向解讀。
- - V10.310 強化 MOMO/PChome 核心比價閉環:PChome feeder 搜尋候選只有強同款 `0.90` 才提前停止,避免第一個 0.76 次佳候選卡掉後續精準搜尋詞;人工否決的候選會被跳過並改挑下一個候選,不再讓已否決商品長期阻塞同 SKU。人工 `reject_identity`、`unit_price_required`、`needs_research` 會立即讓同候選正式 `competitor_prices` 過期,Dashboard 即使尚有舊價也不再顯示正式總價差;手機版比價覆核欄位標籤、覆核按鈕冒泡與候選證據顯示同步修正。
- - V10.308 修正商品列表 PChome 比價閉環狀態:`manual_rejected`、`manual_unit_price_required`、`manual_needs_research` 不再掉回籠統「待比對」,改顯示「人工已否決 / 人工標記單位價 / 人工要求補搜尋」與後續 feeder 行為說明,避免人工覆核後 UI 看起來像沒有處理。
- - V10.307 將 PChome 人工覆核成效接進 daily/growth/PPT 共用資料出口:`fetch_competitor_coverage()` 讀取 `competitor_match_reviews` 最新決策,輸出人工採用、人工否決、人工單位價與採用率;`daily_sales` 與 `growth_analysis` 的比價資料品質區塊直接顯示這些閉環指標,讓報表與簡報不只看待審數,也能看人工處理成效。
- - V10.305 將 PChome 人工覆核回饋接回 feeder:下一輪搜尋若命中已被 `reject_identity` 否決的同一候選,會記錄 `manual_rejected` 並跳過正式寫入;已被標記 `unit_price_required` 的候選只保留單位價比較,不寫入正式總價差;人工 `accept_identity` 可保守覆蓋低分門檻但會打 `manual_review/manual_accept` 標籤,讓核心比價閉環可被後續報表與簡報追蹤。
- - V10.304 補 PChome 比價人工覆核決策閉環:新增 `competitor_match_reviews`、`/api/pchome-review//decision` 與商品看板覆核列「採用同款 / 否決候選 / 標記單位價」動作;只有人工採用同款才寫入 `competitor_prices` + `competitor_price_history`,否決與單位價標記只追加 manual attempt 並關閉本輪覆核,避免錯配污染核心價差。
- - V10.302 補 PChome 比價覆核匯出與診斷原因:`filter=pchome_review` 每筆覆核把 matcher `reasons=` 翻成品牌不符、商品線不符、容量差異、組合差異、需單位價、價差極端等可行動標籤;新增 `/api/export/excel/pchome-review` 匯出完整覆核隊列、人工處置、候選 PChome、單位價比較與原始診斷,避免核心比價只停在籠統「待對比」。
- - V10.301 補市場情報 candidate queue review AI summary Telegram dispatch gate:新增 `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_gate` 與 UI 按鈕,在 summary persistence closeout 後檢查 Telegram 訊息契約、channel label、artifact path、token 外洩風險與後續 run package promotion;API/UI 仍不讀 approval/Telegram token、不呼叫 LLM、不開 DB、不寫檔、不派送 Telegram、不掛 scheduler。
+ - V10.310 強化 MOMO/PChome 核心比價閉環:PChome feeder 搜尋候選只有強同款 `0.90` 才提前停止,避免第一個 0.76 次佳候選卡掉後續精準搜尋詞;AI 否決的候選會被跳過並改挑下一個候選,不再讓已否決商品長期阻塞同 SKU。AI 例外 `reject_identity`、`unit_price_required`、`needs_research` 會立即讓同候選正式 `competitor_prices` 過期,Dashboard 即使尚有舊價也不再顯示正式總價差;手機版比價覆核欄位標籤、覆核按鈕冒泡與候選證據顯示同步修正。
+ - V10.308 修正商品列表 PChome 比價閉環狀態:`manual_rejected`、`manual_unit_price_required`、`manual_needs_research` 不再掉回籠統「待比對」,改顯示「AI 已否決 / AI 標記單位價 / AI 要求補搜尋」與後續 feeder 行為說明,避免AI 例外決策後 UI 看起來像沒有處理。
+ - V10.307 將 PChome AI 例外決策成效接進 daily/growth/PPT 共用資料出口:`fetch_competitor_coverage()` 讀取 `competitor_match_reviews` 最新決策,輸出AI 採用、AI 否決、AI 單位價與採用率;`daily_sales` 與 `growth_analysis` 的比價資料品質區塊直接顯示這些閉環指標,讓報表與簡報不只看待審數,也能看AI 例外處理成效。
+ - V10.305 將 PChome AI 例外決策回饋接回 feeder:下一輪搜尋若命中已被 `reject_identity` 否決的同一候選,會記錄 `manual_rejected` 並跳過正式寫入;已被標記 `unit_price_required` 的候選只保留單位價比較,不寫入正式總價差;人工 `accept_identity` 可保守覆蓋低分門檻但會打 `manual_review/manual_accept` 標籤,讓核心比價閉環可被後續報表與簡報追蹤。
+ - V10.304 補 PChome 比價AI 例外決策決策閉環:新增 `competitor_match_reviews`、`/api/pchome-review//decision` 與商品看板覆核列「採用同款 / 否決候選 / 標記單位價」動作;只有AI 採用同款才寫入 `competitor_prices` + `competitor_price_history`,否決與單位價標記只追加 manual attempt 並關閉本輪覆核,避免錯配污染核心價差。
+ - V10.302 補 PChome 比價覆核匯出與診斷原因:`filter=pchome_review` 每筆覆核把 matcher `reasons=` 翻成品牌不符、商品線不符、容量差異、組合差異、需單位價、價差極端等可行動標籤;新增 `/api/export/excel/pchome-review` 匯出完整覆核隊列、AI 例外處置、候選 PChome、單位價比較與原始診斷,避免核心比價只停在籠統「待對比」。
+ - V10.301 補市場情報 candidate queue review AI summary Telegram dispatch gate:新增 `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_gate` 與 UI 按鈕,在 summary persistence closeout 後檢查 Telegram 訊息契約、channel label、artifact path、token 外洩風險與後續 run package promotion;API/UI 仍不讀 approval/Telegram token、不呼叫 LLM、不開 DB、不寫檔、不派送 Telegram、不掛 scheduler。
- V10.300 補商品看板比價覆核狀態分流:`filter=pchome_review` 新增全部、需單位價、身份否決、低信心、價格過期、找不到同款 segmented 篩選與分頁保留參數,讓 6,000+ 筆覆核隊列能依 matcher 診斷類型分批處理;同步修正覆核列表表頭/分頁連結狀態保留。
- - V10.299 補市場情報 candidate queue review AI summary persistence run closeout:新增 `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_run_closeout` 與 UI 按鈕,在 receipt 通過後收尾 metadata_json persistence gate,確認 closeout artifact、操作員確認與後續 Telegram dispatch 必須另開 gate;API/UI 仍不讀 approval token、不執行 CLI、不連 DB、不寫 `metadata_json`、不派送 Telegram、不掛 scheduler。
- - V10.298 補市場情報 candidate queue review AI summary persistence run receipt:新增 `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_run_receipt` 與 UI 按鈕,審核操作員貼回的 metadata_json CLI writer output、post-write smoke、dedupe key、summary payload hash、artifact path 與 token 外洩風險;API/UI 仍不讀 approval token、不執行 CLI、不連 DB、不寫 `metadata_json`、不派送 Telegram、不掛 scheduler。
- - V10.297 將 PChome 單位價覆核隊列接回商品看板第一屏:KPI 顯示待處理/需單位價覆核數,焦點區列出候選 PChome 商品、候選價、match score 與人工動作;新增 `filter=pchome_review` 的比價覆核隊列,讓使用者可直接進入待處理 SKU,不再只在 daily/growth/PPT 摘要看到統計。
+ - V10.299 補市場情報 candidate queue review AI summary persistence run closeout:新增 `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_persistence_run_closeout` 與 UI 按鈕,在 receipt 通過後收尾 metadata_json persistence gate,確認 closeout artifact、操作員確認與後續 Telegram dispatch 必須另開 gate;API/UI 仍不讀 approval token、不執行 CLI、不連 DB、不寫 `metadata_json`、不派送 Telegram、不掛 scheduler。
+ - V10.298 補市場情報 candidate queue review AI summary persistence run receipt:新增 `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_persistence_run_receipt` 與 UI 按鈕,審核操作員貼回的 metadata_json CLI writer output、post-write smoke、dedupe key、summary payload hash、artifact path 與 token 外洩風險;API/UI 仍不讀 approval token、不執行 CLI、不連 DB、不寫 `metadata_json`、不派送 Telegram、不掛 scheduler。
+ - V10.297 將 PChome 單位價覆核隊列接回商品看板第一屏:KPI 顯示待處理/需單位價覆核數,焦點區列出候選 PChome 商品、候選價、match score 與 AI 例外動作;新增 `filter=pchome_review` 的比價覆核隊列,讓使用者可直接進入待處理 SKU,不再只在 daily/growth/PPT 摘要看到統計。
- V10.296 補核心 MOMO/PChome 比價第三層語意與覆核閉環:同核心商品但買送、套組、件數不同且只有單一基礎規格時標記 `unit_comparable`,只寫入 `competitor_match_attempts`;商品看板、daily/growth 報表、OpenClaw/PPT 摘要共用 `competitor_intel_repository` 的覆核隊列,顯示「需單位價比較」、候選商品、候選 PChome 價格與單位價換算證據;多容量/多品項套組仍保持不可比較,避免把不同販售組合直接寫進正式總價差。
- - V10.289 重排 Elephant Alpha L3 HITL `ea_escalation` Telegram 告警:改成專業 incident brief 格式,分成決策狀態、背景摘要、風險摘要、TOP 待審 SKU 與建議處置;價格行動會拆出 MOMO/PChome 價格、價差、人工處置與 PChome ID,避免長 bullet 難讀。
+ - V10.289 重排 Elephant Alpha L3 AI 例外決策 `ea_escalation` Telegram 告警:改成專業 incident brief 格式,分成決策狀態、背景摘要、風險摘要、TOP 待審 SKU 與建議處置;價格行動會拆出 MOMO/PChome 價格、價差、AI 例外處置與 PChome ID,避免長 bullet 難讀。
- V10.284 關閉 Code Review Hermes LLM scan 預設路徑:Step 2 改 deterministic fast static scan,不再讓部署後先卡三段 Ollama timeout;若需要 LLM scan 可用 `CODE_REVIEW_HERMES_LLM_SCAN_ENABLED=true` 顯式開啟,仍只走本地矩陣、不走 Gemini。
- V10.283 將 Code Review Hermes scan 收斂為 fast compact prompt:預設 2 檔 × 900 字、輸出 384 tokens,仍走 GCP-A → GCP-B → 111 本地矩陣,避免部署後 code_review_hermes 先卡三段 timeout。
- V10.282 補齊 Code Review Hermes scan 本地模型矩陣:掃描階段也走 GCP-A `qwen2.5-coder:7b` → GCP-B `gemma3:4b` → 111 `hermes3:latest`,避免 `hermes3` 在三主機各卡 35s 後只留下 error;Hermes scan 不會啟用 Gemini。
- V10.281 強化 Code Review OpenClaw 本地備援矩陣:主機順序仍為 GCP-A → GCP-B → 111,但改成 GCP-A `qwen2.5-coder:7b`、GCP-B `gemma3:4b`、111 `hermes3:latest`,三段本地 Ollama 全失敗後才允許 Claude/Gemini 備援。
- V10.279 收斂 Code Review Ollama-first 路徑:OpenClaw assessment 預設改 `qwen2.5-coder:7b` + 45s/host timeout,Hermes scan 改 compact snippet + 35s/host timeout,避免三主機各卡 120s 後被迫觸發 Gemini 備援。
- V10.278 補 PChome 競價摘要 30 分鐘共享快取與 feeder/backfill 主動清除,並新增市場情報 `candidate_queue_review_ai_summary_preflight` 預覽 gate;API 只檢查未來摘要輸入與 Ollama-first/Gemini-backup-only policy,不呼叫 LLM、不派 Telegram、不寫 DB、不掛 scheduler。
- - V10.276 修正 ElephantAlpha 價格類 Hermes prefetch timeout:`price_drop` / `market_opportunity` trigger 直接把 SQL 命中的 MOMO / PChome 價差實證轉成 HITL action lines,完整 Hermes LLM prefetch 預設關閉;無 DB 實證仍只記 suppressed telemetry / cooldown,不寫 `human_review`、不發空 Telegram。
+ - V10.276 修正 ElephantAlpha 價格類 Hermes prefetch timeout:`price_drop` / `market_opportunity` trigger 直接把 SQL 命中的 MOMO / PChome 價差實證轉成 AI 例外決策 action lines,完整 Hermes LLM prefetch 預設關閉;無 DB 實證仍只記 suppressed telemetry / cooldown,不寫 `human_review`、不發空 Telegram。
- V10.266 強化核心 MOMO/PChome 比價鏈路:新增 `marketplace_product_matcher.py` 身份比對、只讓 `identity_v2` 且分數 ≥ 0.76 的高信心配對進 Dashboard/AI/Excel/Daily/Growth/PPT,並建立 `competitor_intel_repository.py` 統一圖表與簡報資料出口;同品牌但不同型號、不同組數、套組/單品或多品項不一致會進待審,不進正式比價。
- V10.267 專業化 ElephantAlpha `resource_optimization` 告警:不再讓 LLM 生成「48 小時預期效益 / 已執行」敘事,改由程式量測 action queue、P1/P2、pending_review、逾時項目與 CPU load;單純 backlog 不發 Telegram,只有可行動資源壓力才寫 `ai_insights(resource_pressure)` 並發送量測型告警。
- V10.254 續補 `/growth_analysis` 快取命中效能:PostgreSQL source fingerprint 加 60 秒短 TTL,匯入 realtime_sales_monthly 後同步清除 growth shared cache 與短快取,避免快取命中仍頻繁掃大表 COUNT。
- - V10.253 修正 Elephant Alpha L3 HITL 空告警:價格類與資源調配低信心事件若沒有 Hermes/實證資料,只記 suppressed telemetry 與 cooldown,不寫 pending human_review、不發 Telegram;`resource_optimization` 會保留 queue/load 原始指標供追查。
+ - V10.253 修正 Elephant Alpha L3 AI 例外決策 空告警:價格類與資源調配低信心事件若沒有 Hermes/實證資料,只記 suppressed telemetry 與 cooldown,不寫 pending human_review、不發 Telegram;`resource_optimization` 會保留 queue/load 原始指標供追查。
- V10.251 修正 OpenClaw Q&A 備援遙測:Ollama 主路徑仍為 GCP-A → GCP-B → 111,Gemini 只記為 `openclaw_qa_gemini_fallback`,NIM 只記為 `openclaw_qa_nim`;AI Calls 會把 legacy `openclaw_qa + gemini` 標成 Gemini 備援,避免再次誤判 Gemini-first。
- V10.251 穩定 `/growth_analysis` 正式站速度:成長分析快取從單 worker memory 擴充為 `data/growth_analysis_cache.pkl` 跨 worker 共享快取,避免 Gunicorn 冷 worker 偶發掃明細表造成 5 秒級 TTFB;補 `tests/test_cache_manager.py` 覆蓋 shared file roundtrip 與清除行為。
- V10.249 收斂 `/observability/ppt_audit_history` 手機與平板第一屏密度:將 4 個產線訊號從 hero 內移出成獨立狀態列,手機版維持 2 欄狀態卡並降低 hero 卡片間距;本機 10 個 AI 觀測台頁面 rendered visual contract 全數通過,PPT 頁 hero 高度 desktop/tablet/mobile 為 214/361/398px。
@@ -289,7 +2363,7 @@
- Phase 4 手動 dry-run discovery:新增 `services/market_intel/discovery_runner.py` 與 `/api/market_intel/manual_discovery`。預設 `fetch=false` 只回 planned;`fetch=true` 也必須在 `MARKET_INTEL_ENABLED` 與 `MARKET_INTEL_CRAWLER_ENABLED` 同時開啟時才允許 HTTP,且仍不寫 DB、不掛 scheduler。
- Phase 5 parser 診斷層:新增 `services/market_intel/html_diagnostics.py`,手動 fetch 成功後只萃取 title、page_hash、link count、campaign link candidates,不建立正式 campaign/product。
- Phase 6 平台別 scorer:MOMO/PChome adapter 提供 URL/text 加權規則,diagnostics 同時輸出 generic_score 與 platform_score,仍只用於候選排序與人工診斷。
- - Phase 7 confidence bands:diagnostics 將候選標成 `high` / `medium` / `low`,並輸出 `confidence_reason`;只作人工審核提示,不自動建立活動。
+ - Phase 7 confidence bands:diagnostics 將候選標成 `high` / `medium` / `low`,並輸出 `confidence_reason`;只作AI 例外決策提示,不自動建立活動。
- Phase 8 candidate preview API:新增 `/api/market_intel/candidate_preview`,把本次 discovery diagnostics 候選合併排序,支援 `min_band` 與 `limit`,只供人工預覽,不入庫。
- Phase 9 UI preview panel:`templates/market_intel/disabled.html` 讀取 `/api/market_intel/candidate_preview?fetch=false`,顯示安全空狀態與 flags,不做自動外部 fetch。
- Phase 10 platform seed plan:新增 `/api/market_intel/platform_seed_plan`,只產生 adapter registry 對應的 `market_platforms` seed rows 與 gate 狀態,不寫 DB。
@@ -319,55 +2393,55 @@
- Phase 27 legacy source bridge preview:新增 `services/market_intel/legacy_source_bridge.py` 與 `/api/market_intel/legacy_source_bridge`,只讀盤點既有 `promo_products`、`competitor_prices`、`competitor_price_history`,產生導入 `market_*` 的 mapping / dedupe / blocked operation preview;預設 `execute=false` 不連 DB,`execute=true` 也只做 read-only query,不寫 DB、不建立 ORM session、不連外、不掛 scheduler;UI 新增「既有資料橋接預覽」panel;版本同步至 V10.182。
- Phase 45 migration live smoke preview:新增 `/api/market_intel/migration_live_smoke` 與 UI「正式 DB 只讀 smoke」panel;預設 `execute=false` 只回 planned,人工 smoke 才可用 `execute=true` 整理 catalog / seed diff 結果,不執行 migration、不寫 DB、不跑 rollback、不掛 scheduler;版本同步至 V10.207。
- Phase 46 live DB inventory preview:新增 `/api/market_intel/live_db_inventory` 與 UI「正式 DB 庫存總覽」panel;預設 `execute=false` 不連 DB,人工 smoke 才可用 `execute=true` 對 `market_*` tables 做只讀 count / group by,建立平台、活動、商品、比對、告警與 crawler run 基準;版本同步至 V10.209。
- - Phase 47 manual sample fetch plan:新增 `/api/market_intel/manual_sample_plan` 與 UI「人工樣本 Fetch 計畫」panel;預設只輸出平台順序、每平台 1 個公開入口、MCP gate、操作員步驟與備援,不抓外站、不寫 DB、不建立 crawler run、不掛 scheduler;版本同步至 V10.214。
- - Phase 48 manual sample acceptance contract:新增 `/api/market_intel/manual_sample_acceptance` 與 UI「樣本結果驗收契約」panel;定義 sample result 必要欄位、diagnostics 欄位、驗收門檻、拒收條件、人工決策與升級順序,不載入 sample result、不抓外站、不允許候選導入、不寫 DB;版本同步至 V10.215。
- - Phase 49 manual sample result review:新增 `services/market_intel/manual_sample_review.py`、`/api/market_intel/manual_sample_review` 與 UI「樣本結果審核預覽」panel;以純函式評估人工 sample result 是否可進候選預覽,預設不載入結果、不抓外站、不存檔、不寫 DB、不允許候選導入、不掛 scheduler;版本同步至 V10.216。
- - Phase 50 manual sample review evaluate:新增 `/api/market_intel/manual_sample_review/evaluate` POST 與 UI JSON 審核入口,允許操作員貼入單筆 sample result 即時回傳 PASS/BLOCK;不保存 payload、不回吐完整 HTML、不寫 DB、不建立候選活動、不允許候選導入、不掛 scheduler;版本同步至 V10.219。
- - V10.220 補 Phase 50 UI POST CSRF header:`manual_sample_review/evaluate` 保持 CSRF 保護,頁面 fetch 送出 `X-CSRFToken`,不豁免安全檢查。
- - Phase 51 manual sample candidate handoff:新增 `/api/market_intel/manual_sample_review/candidate_handoff` POST 與 UI handoff 按鈕,將已通過審核的 sample result 轉成只讀候選活動 preview payload;不保存 handoff、不建立 review queue、不寫 market_*、不允許候選導入、不掛 scheduler;版本同步至 V10.222。
- - Phase 52 manual sample candidate queue draft:新增 `services/market_intel/manual_sample_candidate_queue.py`、`/api/market_intel/manual_sample_review/candidate_queue_draft` POST 與 UI queue 草案按鈕,將 handoff 候選轉成只讀人工審核 queue draft;不建立正式 queue、不保存草案、不寫 market_*、不自動核准候選、不掛 scheduler;版本同步至 V10.223。
+ - Phase 47 AI-controlled sample fetch plan:新增 `/api/market_intel/ai_controlled/sample_plan` 與 UI「人工樣本 Fetch 計畫」panel;預設只輸出平台順序、每平台 1 個公開入口、MCP gate、操作員步驟與備援,不抓外站、不寫 DB、不建立 crawler run、不掛 scheduler;版本同步至 V10.214。
+ - Phase 48 AI-controlled sample acceptance contract:新增 `/api/market_intel/ai_controlled/sample_acceptance` 與 UI「樣本結果驗收契約」panel;定義 sample result 必要欄位、diagnostics 欄位、驗收門檻、拒收條件、人工決策與升級順序,不載入 sample result、不抓外站、不允許候選導入、不寫 DB;版本同步至 V10.215。
+ - Phase 49 AI-controlled sample result review:新增 `services/market_intel/service.py canonical sample-review builder`、`/api/market_intel/ai_controlled/sample_review` 與 UI「樣本結果審核預覽」panel;以純函式評估人工 sample result 是否可進候選預覽,預設不載入結果、不抓外站、不存檔、不寫 DB、不允許候選導入、不掛 scheduler;版本同步至 V10.216。
+ - Phase 50 AI-controlled sample review evaluate:新增 `/api/market_intel/ai_controlled/sample_review/evaluate` POST 與 UI JSON 審核入口,允許操作員貼入單筆 sample result 即時回傳 PASS/BLOCK;不保存 payload、不回吐完整 HTML、不寫 DB、不建立候選活動、不允許候選導入、不掛 scheduler;版本同步至 V10.219。
+ - V10.220 補 Phase 50 UI POST CSRF header:`ai_controlled/sample_review/evaluate` 保持 CSRF 保護,頁面 fetch 送出 `X-CSRFToken`,不豁免安全檢查。
+ - Phase 51 AI-controlled sample candidate handoff:新增 `/api/market_intel/ai_controlled/review/candidate_handoff` POST 與 UI handoff 按鈕,將已通過審核的 sample result 轉成只讀候選活動 preview payload;不保存 handoff、不建立 review queue、不寫 market_*、不允許候選導入、不掛 scheduler;版本同步至 V10.222。
+ - Phase 52 AI-controlled sample candidate queue draft:新增 `services/market_intel/ai_controlled_service_compat.py compatibility layer`、`/api/market_intel/ai_controlled/review/candidate_queue_draft` POST 與 UI queue 草案按鈕,將 handoff 候選轉成只讀AI 例外決策 queue draft;不建立正式 queue、不保存草案、不寫 market_*、不自動核准候選、不掛 scheduler;版本同步至 V10.223。
- V10.224 補 PPT 報表覆蓋矩陣:`/observability/ppt_audit_history` 將每個定義簡報同列串起 DB 寫入、線上預覽、視覺 QA 與交付狀態,並提供預覽、預熱、重跑操作,避免只顯示「目標已產生」。
- - Phase 53 manual sample candidate queue approval:新增 `/api/market_intel/manual_sample_review/candidate_queue_approval` POST 與 UI 送審 gate 按鈕,將 queue draft row preview 對齊既有 `market_alert_review_queue` 契約,檢查必填欄位、寫入 flags、備份與人工批准 gate;不建立 approval record、不寫 review queue、不開 DB transaction、不掛 scheduler;版本同步至 V10.225。
+ - Phase 53 AI-controlled sample candidate queue approval:新增 `/api/market_intel/ai_controlled/review/candidate_queue_approval` POST 與 UI 送審 gate 按鈕,將 queue draft row preview 對齊既有 `market_alert_review_queue` 契約,檢查必填欄位、寫入 flags、備份與人工批准 gate;不建立 approval record、不寫 review queue、不開 DB transaction、不掛 scheduler;版本同步至 V10.225。
- V10.226 補 PPT 視覺 QA runtime checklist:`/observability/ppt_audit_history` 在視覺模型未就緒時顯示 Feature Flag、LibreOffice、Vision Model 三段檢查與下一步操作,避免只看到「停用」而不知道卡在哪。
- - Phase 54 manual sample candidate queue transaction:新增 `/api/market_intel/manual_sample_review/candidate_queue_transaction` POST 與 UI transaction preview 按鈕,將 queue row preview 轉成 `market_alert_review_queue` idempotent insert statement、payload hash 與 rollback plan;不開 DB connection、不開 transaction、不 commit、不建立 approval record;版本同步至 V10.227。
+ - Phase 54 AI-controlled sample candidate queue transaction:新增 `/api/market_intel/ai_controlled/review/candidate_queue_transaction` POST 與 UI transaction preview 按鈕,將 queue row preview 轉成 `market_alert_review_queue` idempotent insert statement、payload hash 與 rollback plan;不開 DB connection、不開 transaction、不 commit、不建立 approval record;版本同步至 V10.227。
- V10.228 補 PPT 視覺 QA 背景狀態卡:新增 `/observability/ppt_audit/vision_status` 與頁面 Vision QA 狀態卡,讓立即視覺 QA 排入後可看 queued/running/completed/error 與最近審核摘要,不必刷新猜測。
- V10.229 修正 PPT 視覺 QA 多 worker 狀態漂移:將 queued/running/completed/error 寫入 `/app/data/ppt_vision_audit_status.json` runtime state,所有 Gunicorn worker 共用同一份狀態並阻擋重複排入。
- - Phase 55 candidate queue writer CLI gate:新增 `/api/market_intel/manual_sample_review/candidate_queue_writer_status` POST、`scripts/market_intel_candidate_queue_writer.py` 與 UI writer gate 按鈕,定義 `MARKET_INTEL_QUEUE_WRITE_APPROVAL` 一次性 token、execute/apply flags、備份、migration smoke 與 rollback gate;本階段仍不開 DB connection、不寫 `market_alert_review_queue`、不 commit、不掛 scheduler;版本同步至 V10.230。
- - Phase 56 candidate queue writer preflight:新增 `/api/market_intel/manual_sample_review/candidate_queue_writer_preflight` POST 與 `services/market_intel/candidate_queue_writer_preflight.py`,檢查 transaction payload key 到 `market_alert_review_queue` 欄位映射、缺欄與 dedupe unique index;頁面預設 execute=false 不連 DB,CLI 可明確 `--read-only-preflight` 只讀 catalog;版本同步至 V10.232。
+ - Phase 55 candidate queue writer CLI gate:新增 `/api/market_intel/ai_controlled/review/candidate_queue_writer_status` POST、`scripts/market_intel_candidate_queue_writer.py` 與 UI writer gate 按鈕,定義 `MARKET_INTEL_QUEUE_WRITE_APPROVAL` 一次性 token、execute/apply flags、備份、migration smoke 與 rollback gate;本階段仍不開 DB connection、不寫 `market_alert_review_queue`、不 commit、不掛 scheduler;版本同步至 V10.230。
+ - Phase 56 candidate queue writer preflight:新增 `/api/market_intel/ai_controlled/review/candidate_queue_writer_preflight` POST 與 `services/market_intel/candidate_queue_writer_preflight.py`,檢查 transaction payload key 到 `market_alert_review_queue` 欄位映射、缺欄與 dedupe unique index;頁面預設 execute=false 不連 DB,CLI 可明確 `--read-only-preflight` 只讀 catalog;版本同步至 V10.232。
- Phase 57 candidate queue writer CLI transaction:`scripts/market_intel_candidate_queue_writer.py` 在 CLI-only 情境支援受控 transaction,必須同時通過 transaction payload、read-only preflight、`--execute`、`--apply-real-write`、一次性 token、備份確認與 migration live smoke 才會以 SQLAlchemy Core idempotent insert `market_alert_review_queue`;API/UI 仍不傳 token、不連 DB、不寫 queue、不掛 scheduler;版本同步至 V10.234。
- V10.235 補 PPT 視覺 QA stale recovery:背景狀態寫入 worker PID;若部署 reload 後舊 PID 已不存在,`/observability/ppt_audit/vision_status` 會自動把 running 轉為可診斷 error 並允許重新排入,避免人工清 runtime state。
- - Phase 58 candidate queue writer post-write smoke:新增 `services/market_intel/candidate_queue_writer_postwrite_smoke.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_writer_postwrite_smoke` 與 UI smoke 按鈕,依 transaction preview 的 dedupe key 只讀查詢 `market_alert_review_queue`,讓 CLI 真寫入後可驗證 row 是否存在;頁面預設 execute=false 不連 DB、不寫 queue、不 commit、不掛 scheduler;版本同步至 V10.236。
- - Phase 59 candidate queue writer operator drill:新增 `services/market_intel/candidate_queue_writer_operator_drill.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_writer_operator_drill` 與 UI drill 按鈕,組裝 reviewed sample、備份、read-only preflight、CLI writer、post-write smoke 的操作員順序;API/UI 不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不 commit、不掛 scheduler;版本同步至 V10.237。
+ - Phase 58 candidate queue writer post-write smoke:新增 `services/market_intel/candidate_queue_writer_postwrite_smoke.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_writer_postwrite_smoke` 與 UI smoke 按鈕,依 transaction preview 的 dedupe key 只讀查詢 `market_alert_review_queue`,讓 CLI 真寫入後可驗證 row 是否存在;頁面預設 execute=false 不連 DB、不寫 queue、不 commit、不掛 scheduler;版本同步至 V10.236。
+ - Phase 59 candidate queue writer operator drill:新增 `services/market_intel/candidate_queue_writer_operator_drill.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_writer_operator_drill` 與 UI drill 按鈕,組裝 reviewed sample、備份、read-only preflight、CLI writer、post-write smoke 的操作員順序;API/UI 不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不 commit、不掛 scheduler;版本同步至 V10.237。
- V10.238 補業績圖表 runtime QA 與分析 tabs 窄版修正:新增 `quick_review --sales-charts` 檢查 `/daily_sales`、`/growth_analysis` 的 Chart.js ready、可繪製資料集與 canvas 非空白;同時把分析報表 tabs 手機版改為自適應 grid,避免 Metabase/Grist 外部連結超出右側。
- - Phase 60 candidate queue writer run package:新增 `services/market_intel/candidate_queue_writer_run_package.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_writer_run_package` 與 UI run package 按鈕,整理正式 CLI 小流量寫入前的 payload manifest、required artifacts、command bundle、operator signoff 與 rollback plan;API/UI 不產檔、不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不 commit、不掛 scheduler;版本同步至 V10.240。
- - Phase 61 candidate queue writer run readiness:新增 `services/market_intel/candidate_queue_writer_run_readiness.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_writer_run_readiness` 與 UI readiness 按鈕,檢查 reviewed sample 檔案路徑、備份路徑、preflight 輸出、migration live smoke、shell-only token acknowledgement 與禁止 token 進 API;API/UI 不產檔、不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不 commit、不掛 scheduler;版本同步至 V10.245。
- - Phase 62 candidate queue writer run receipt:新增 `services/market_intel/candidate_queue_writer_run_receipt.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_writer_run_receipt` 與 UI receipt 按鈕,審核 CLI 寫入後的 writer output、post-write smoke、dedupe key 一致性與 artifact 路徑;API/UI 不回吐 receipt 原文、不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不掛 scheduler;版本同步至 V10.247。
- - Phase 63 candidate queue writer run closeout:新增 `services/market_intel/candidate_queue_writer_run_closeout.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_writer_run_closeout` 與 UI closeout 按鈕,在 receipt 通過後檢查 closeout artifact、操作員人工 queue review/read-only inventory 確認與安全 promotion gate;API/UI 不回吐原始 receipt、不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不掛 scheduler;版本同步至 V10.248。
- - Phase 64 candidate queue review handoff:新增 `services/market_intel/candidate_queue_review_handoff.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_handoff` 與 UI handoff 按鈕,將 writer closeout 轉成人工 queue review / read-only inventory 交接契約;API/UI 不讀 approval token、不查 DB、不更新 review_state、不補寫 queue、不掛 scheduler;版本同步至 V10.251。
- - Phase 65 candidate queue review inventory:新增 `services/market_intel/candidate_queue_review_inventory.py`、`routes/market_intel_review_routes.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_inventory` 與 UI inventory 按鈕,把 handoff、post-write smoke、live DB inventory 合併成只讀人工審核庫存檢查;預設不連 DB,人工只讀查詢仍不更新 review_state、不補寫 queue、不讀 token、不掛 scheduler;版本同步至 V10.252。
- - Phase 66 candidate queue review decision:新增 `services/market_intel/candidate_queue_review_decision.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision` 與 UI decision 按鈕,將通過 inventory 的 queue row 整理成人工決策草案;API/UI 不更新 review_state、不寫 decision record、不讀 token、不掛 scheduler;版本同步至 V10.254。
- - Phase 67 candidate queue review decision approval:新增 `services/market_intel/candidate_queue_review_decision_approval.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_approval` 與 UI approval gate 按鈕,檢查人工決策草案是否可進入下一個 CLI-only transaction preview;API/UI 不更新 review_state、不寫 decision record、不建立 approval record、不讀 token、不掛 scheduler;版本同步至 V10.255。
- - Phase 68 candidate queue review decision transaction:新增 `services/market_intel/candidate_queue_review_decision_transaction.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_transaction` 與 UI transaction preview 按鈕,將 approval update preview 轉成 `review_state` update statement、payload hash 與 rollback plan;API/UI 不更新 review_state、不開 DB connection、不執行 CLI、不讀 token、不掛 scheduler;版本同步至 V10.256。
- - Phase 69 candidate queue review decision writer CLI gate:新增 `services/market_intel/candidate_queue_review_decision_writer_cli.py`、`scripts/market_intel_review_decision_writer.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_status` 與 UI writer gate 按鈕,先建立 shell-only review_state writer gate 與 command bundle;writer implementation 本階段保持 disabled,API/UI 不讀 token、不執行 CLI、不連 DB、不更新 review_state、不掛 scheduler;版本同步至 V10.257。
- - Phase 70 candidate queue review decision writer preflight:新增 `services/market_intel/candidate_queue_review_decision_writer_preflight.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_preflight` 與 UI preflight 按鈕,檢查 writer status、review_state update payload、狀態轉換與禁止 token 進 API;API/UI 即使收到 execute/apply_real_write 也不連 DB、不執行 CLI、不更新 review_state、不 commit、不讀 token、不掛 scheduler;版本同步至 V10.258。
+ - Phase 60 candidate queue writer run package:新增 `services/market_intel/candidate_queue_writer_run_package.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_writer_run_package` 與 UI run package 按鈕,整理正式 CLI 小流量寫入前的 payload manifest、required artifacts、command bundle、operator signoff 與 rollback plan;API/UI 不產檔、不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不 commit、不掛 scheduler;版本同步至 V10.240。
+ - Phase 61 candidate queue writer run readiness:新增 `services/market_intel/candidate_queue_writer_run_readiness.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_writer_run_readiness` 與 UI readiness 按鈕,檢查 reviewed sample 檔案路徑、備份路徑、preflight 輸出、migration live smoke、shell-only token acknowledgement 與禁止 token 進 API;API/UI 不產檔、不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不 commit、不掛 scheduler;版本同步至 V10.245。
+ - Phase 62 candidate queue writer run receipt:新增 `services/market_intel/candidate_queue_writer_run_receipt.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_writer_run_receipt` 與 UI receipt 按鈕,審核 CLI 寫入後的 writer output、post-write smoke、dedupe key 一致性與 artifact 路徑;API/UI 不回吐 receipt 原文、不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不掛 scheduler;版本同步至 V10.247。
+ - Phase 63 candidate queue writer run closeout:新增 `services/market_intel/candidate_queue_writer_run_closeout.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_writer_run_closeout` 與 UI closeout 按鈕,在 receipt 通過後檢查 closeout artifact、操作員人工 queue review/read-only inventory 確認與安全 promotion gate;API/UI 不回吐原始 receipt、不讀 approval token、不執行 CLI、不連 DB、不寫 queue、不掛 scheduler;版本同步至 V10.248。
+ - Phase 64 candidate queue review handoff:新增 `services/market_intel/candidate_queue_review_handoff.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_handoff` 與 UI handoff 按鈕,將 writer closeout 轉成人工 queue review / read-only inventory 交接契約;API/UI 不讀 approval token、不查 DB、不更新 review_state、不補寫 queue、不掛 scheduler;版本同步至 V10.251。
+ - Phase 65 candidate queue review inventory:新增 `services/market_intel/candidate_queue_review_inventory.py`、`routes/market_intel_review_routes.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_inventory` 與 UI inventory 按鈕,把 handoff、post-write smoke、live DB inventory 合併成只讀AI 例外決策庫存檢查;預設不連 DB,人工只讀查詢仍不更新 review_state、不補寫 queue、不讀 token、不掛 scheduler;版本同步至 V10.252。
+ - Phase 66 candidate queue review decision:新增 `services/market_intel/candidate_queue_review_decision.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision` 與 UI decision 按鈕,將通過 inventory 的 queue row 整理成人工決策草案;API/UI 不更新 review_state、不寫 decision record、不讀 token、不掛 scheduler;版本同步至 V10.254。
+ - Phase 67 candidate queue review decision approval:新增 `services/market_intel/candidate_queue_review_decision_approval.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_approval` 與 UI approval gate 按鈕,檢查人工決策草案是否可進入下一個 CLI-only transaction preview;API/UI 不更新 review_state、不寫 decision record、不建立 approval record、不讀 token、不掛 scheduler;版本同步至 V10.255。
+ - Phase 68 candidate queue review decision transaction:新增 `services/market_intel/candidate_queue_review_decision_transaction.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_transaction` 與 UI transaction preview 按鈕,將 approval update preview 轉成 `review_state` update statement、payload hash 與 rollback plan;API/UI 不更新 review_state、不開 DB connection、不執行 CLI、不讀 token、不掛 scheduler;版本同步至 V10.256。
+ - Phase 69 candidate queue review decision writer CLI gate:新增 `services/market_intel/candidate_queue_review_decision_writer_cli.py`、`scripts/market_intel_review_decision_writer.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_writer_status` 與 UI writer gate 按鈕,先建立 shell-only review_state writer gate 與 command bundle;writer implementation 本階段保持 disabled,API/UI 不讀 token、不執行 CLI、不連 DB、不更新 review_state、不掛 scheduler;版本同步至 V10.257。
+ - Phase 70 candidate queue review decision writer preflight:新增 `services/market_intel/candidate_queue_review_decision_writer_preflight.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_writer_preflight` 與 UI preflight 按鈕,檢查 writer status、review_state update payload、狀態轉換與禁止 token 進 API;API/UI 即使收到 execute/apply_real_write 也不連 DB、不執行 CLI、不更新 review_state、不 commit、不讀 token、不掛 scheduler;版本同步至 V10.258。
- V10.259 補 Phase 70 preflight 合約與 OCLearn queue 時區:preflight 補 planned/read-only catalog probe 欄位、dedupe unique index 檢查與 route 重複註冊清理;OCLearn embedding queue 的 created_at/updated_at/stale cutoff 改為台北 naive,避免 UTC/台北時間差讓 processing 任務卡住。
- - Phase 71 candidate queue review decision writer post-write smoke:新增 `services/market_intel/candidate_queue_review_decision_writer_postwrite_smoke.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_postwrite_smoke` 與 UI smoke 按鈕,人工 CLI 更新 review_state 後可用 dedupe key 只讀驗證 row 是否存在且 state 符合預期;API/UI 預設不連 DB,execute=true 也只讀查詢,不更新 review_state、不 commit、不讀 token、不掛 scheduler;版本同步至 V10.260。
- - Phase 72 candidate queue review decision writer operator drill:新增 `services/market_intel/candidate_queue_review_decision_writer_operator_drill.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_operator_drill` 與 UI drill 按鈕,將 review_state CLI 更新前後的 transaction JSON、備份、preflight、CLI writer、post-write smoke 與 rollback plan 組成可稽核操作順序;API/UI 不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.261。
- - Phase 73 candidate queue review decision writer run package:新增 `services/market_intel/candidate_queue_review_decision_writer_run_package.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_package` 與 UI package 按鈕,將 review_state transaction、preflight、operator drill、writer gate、post-write smoke、必要 artifact 與 rollback plan 組成正式 CLI 更新前的可稽核 run package;API/UI 不寫檔、不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.262。
- - Phase 74 candidate queue review decision writer run readiness:新增 `services/market_intel/candidate_queue_review_decision_writer_run_readiness.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_readiness` 與 UI readiness 按鈕,檢查 review_state CLI 更新前的 transaction JSON、備份、preflight、shell-only token 與 post-write smoke 計畫是否齊備;API/UI 不寫檔、不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.264。
- - Phase 75 candidate queue review decision writer run receipt:新增 `services/market_intel/candidate_queue_review_decision_writer_run_receipt.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_receipt` 與 UI receipt 按鈕,審核 review_state CLI 更新後的 writer output、post-write smoke、dedupe key 一致性、artifact 路徑與 token 外洩風險;API/UI 不回吐 receipt 原文、不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.266。
- - Phase 76 candidate queue review decision writer run closeout:新增 `services/market_intel/candidate_queue_review_decision_writer_run_closeout.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_closeout` 與 UI closeout 按鈕,在 review_state receipt 通過後整理 closeout gate、操作員 closeout artifact、post-closeout read-only inventory 確認與 promotion 摘要;API/UI 不回吐 receipt 原文、不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.268。
- - Phase 77 candidate queue review decision post-closeout inventory:新增 `services/market_intel/candidate_queue_review_decision_post_closeout_inventory.py`、`routes/market_intel_review_post_routes.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_decision_post_closeout_inventory` 與 UI inventory 按鈕,在 review_state closeout 後整理 post-write smoke、live inventory、dedupe key 與 review_state 結果;API/UI 不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.270。
- - Phase 78 candidate queue review completion archive:新增 `services/market_intel/candidate_queue_review_completion_archive.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_completion_archive` 與 UI archive 按鈕,在 post-closeout inventory 通過後整理 receipt、closeout、inventory、dedupe key、review_state row snapshot 與 artifact path manifest;API/UI 不寫檔、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.273。
- - Phase 79 candidate queue review archive summary:新增 `services/market_intel/candidate_queue_review_archive_summary.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_archive_summary` 與 UI summary 按鈕,在 review completion archive 後整理可供摘要/報表審核的結構化輸入;API/UI 不呼叫 LLM、不派送 Telegram、不寫檔、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.276。
- - Phase 80 candidate queue review AI summary preflight:新增 `services/market_intel/candidate_queue_review_ai_summary_preflight.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_preflight` 與 UI preflight 按鈕,在 archive summary 後檢查 Ollama-first 三主機級聯與 Gemini 備援邊界;API/UI 不呼叫 LLM、不派送 Telegram、不寫檔、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.278。
- - Phase 81 candidate queue review AI summary run package:新增 `services/market_intel/candidate_queue_review_ai_summary_run_package.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_run_package` 與 UI package 按鈕,在 AI summary preflight 後整理手動 Ollama 摘要任務包、prompt contract 與輸出 schema;API/UI 不呼叫 LLM、不派送 Telegram、不寫 run package、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.280。
- - Phase 82 candidate queue review AI summary output receipt:新增 `services/market_intel/candidate_queue_review_ai_summary_output_receipt.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_output_receipt` 與 UI receipt 按鈕,在 run package 後驗收人工 Ollama 摘要輸出的 schema、evidence_refs 與 model_route;API/UI 不呼叫 LLM、不派送 Telegram、不寫 receipt、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.285。
- - Phase 83 candidate queue review AI summary persistence preflight:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_preflight.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_preflight` 與 UI preflight 按鈕,在 output receipt 後整理 future CLI-only `market_alert_review_queue.metadata_json.ai_summary_review` persistence contract、payload hash 與 metadata patch preview;API/UI 不呼叫 LLM、不派送 Telegram、不寫 preflight、不寫 summary record、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.286。
- - Phase 84 candidate queue review AI summary persistence transaction:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_transaction.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_transaction` 與 UI transaction 按鈕,在 persistence preflight 後產生 future CLI-only `metadata_json` UPDATE statement preview、parameter preview 與 rollback plan;API/UI 不開 DB、不執行 SQL、不寫 transaction、不寫 summary record、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不派送 Telegram、不呼叫 LLM、不 commit、不掛 scheduler;版本同步至 V10.287。
- - Phase 85 candidate queue review AI summary persistence writer preflight:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_writer_preflight.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_writer_preflight` 與 UI writer preflight 按鈕,在 transaction preview 後檢查 CLI-only writer contract、metadata_json backup requirement、post-write smoke requirement 與 artifact path gate;API/UI 不開 DB、不執行 SQL、不寫 preflight、不寫 summary record、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不派送 Telegram、不呼叫 LLM、不 commit、不掛 scheduler;版本同步至 V10.290。
- - Phase 86 candidate queue review AI summary persistence run package:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_run_package.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_run_package` 與 UI run package 按鈕,在 writer preflight 後整理 payload manifest、CLI command bundle、required artifacts、operator signoff 與 rollback plan;API/UI 不開 DB、不執行 SQL、不寫 run package、不寫 summary record、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不派送 Telegram、不呼叫 LLM、不 commit、不掛 scheduler;版本同步至 V10.294。
- - Phase 87 candidate queue review AI summary persistence run readiness:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_run_readiness.py`、POST `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_run_readiness` 與 UI readiness 按鈕,在 run package 後檢查 artifact path、metadata_json backup、read-only preflight、shell-only token 與 post-write smoke 計畫;API/UI 不開 DB、不執行 SQL、不寫 readiness artifact、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不派送 Telegram、不呼叫 LLM、不 commit、不掛 scheduler;版本同步至 V10.297。
+ - Phase 71 candidate queue review decision writer post-write smoke:新增 `services/market_intel/candidate_queue_review_decision_writer_postwrite_smoke.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_writer_postwrite_smoke` 與 UI smoke 按鈕,人工 CLI 更新 review_state 後可用 dedupe key 只讀驗證 row 是否存在且 state 符合預期;API/UI 預設不連 DB,execute=true 也只讀查詢,不更新 review_state、不 commit、不讀 token、不掛 scheduler;版本同步至 V10.260。
+ - Phase 72 candidate queue review decision writer operator drill:新增 `services/market_intel/candidate_queue_review_decision_writer_operator_drill.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_writer_operator_drill` 與 UI drill 按鈕,將 review_state CLI 更新前後的 transaction JSON、備份、preflight、CLI writer、post-write smoke 與 rollback plan 組成可稽核操作順序;API/UI 不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.261。
+ - Phase 73 candidate queue review decision writer run package:新增 `services/market_intel/candidate_queue_review_decision_writer_run_package.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_writer_run_package` 與 UI package 按鈕,將 review_state transaction、preflight、operator drill、writer gate、post-write smoke、必要 artifact 與 rollback plan 組成正式 CLI 更新前的可稽核 run package;API/UI 不寫檔、不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.262。
+ - Phase 74 candidate queue review decision writer run readiness:新增 `services/market_intel/candidate_queue_review_decision_writer_run_readiness.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_writer_run_readiness` 與 UI readiness 按鈕,檢查 review_state CLI 更新前的 transaction JSON、備份、preflight、shell-only token 與 post-write smoke 計畫是否齊備;API/UI 不寫檔、不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.264。
+ - Phase 75 candidate queue review decision writer run receipt:新增 `services/market_intel/candidate_queue_review_decision_writer_run_receipt.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_writer_run_receipt` 與 UI receipt 按鈕,審核 review_state CLI 更新後的 writer output、post-write smoke、dedupe key 一致性、artifact 路徑與 token 外洩風險;API/UI 不回吐 receipt 原文、不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.266。
+ - Phase 76 candidate queue review decision writer run closeout:新增 `services/market_intel/candidate_queue_review_decision_writer_run_closeout.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_writer_run_closeout` 與 UI closeout 按鈕,在 review_state receipt 通過後整理 closeout gate、操作員 closeout artifact、post-closeout read-only inventory 確認與 promotion 摘要;API/UI 不回吐 receipt 原文、不讀 token、不執行 CLI、不連 DB、不更新 review_state、不 commit、不掛 scheduler;版本同步至 V10.268。
+ - Phase 77 candidate queue review decision post-closeout inventory:新增 `services/market_intel/candidate_queue_review_decision_post_closeout_inventory.py`、`routes/market_intel_review_post_routes.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_decision_post_closeout_inventory` 與 UI inventory 按鈕,在 review_state closeout 後整理 post-write smoke、live inventory、dedupe key 與 review_state 結果;API/UI 不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.270。
+ - Phase 78 candidate queue review completion archive:新增 `services/market_intel/candidate_queue_review_completion_archive.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_completion_archive` 與 UI archive 按鈕,在 post-closeout inventory 通過後整理 receipt、closeout、inventory、dedupe key、review_state row snapshot 與 artifact path manifest;API/UI 不寫檔、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.273。
+ - Phase 79 candidate queue review archive summary:新增 `services/market_intel/candidate_queue_review_archive_summary.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_archive_summary` 與 UI summary 按鈕,在 review completion archive 後整理可供摘要/報表審核的結構化輸入;API/UI 不呼叫 LLM、不派送 Telegram、不寫檔、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.276。
+ - Phase 80 candidate queue review AI summary preflight:新增 `services/market_intel/candidate_queue_review_ai_summary_preflight.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_preflight` 與 UI preflight 按鈕,在 archive summary 後檢查 Ollama-first 三主機級聯與 Gemini 備援邊界;API/UI 不呼叫 LLM、不派送 Telegram、不寫檔、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.278。
+ - Phase 81 candidate queue review AI summary run package:新增 `services/market_intel/candidate_queue_review_ai_summary_run_package.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_run_package` 與 UI package 按鈕,在 AI summary preflight 後整理AI-controlled Ollama 摘要任務包、prompt contract 與輸出 schema;API/UI 不呼叫 LLM、不派送 Telegram、不寫 run package、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.280。
+ - Phase 82 candidate queue review AI summary output receipt:新增 `services/market_intel/candidate_queue_review_ai_summary_output_receipt.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_output_receipt` 與 UI receipt 按鈕,在 run package 後驗收 AI-controlled Ollama 摘要輸出的 schema、evidence_refs 與 model_route;API/UI 不呼叫 LLM、不派送 Telegram、不寫 receipt、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.285。
+ - Phase 83 candidate queue review AI summary persistence preflight:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_preflight.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_persistence_preflight` 與 UI preflight 按鈕,在 output receipt 後整理 future CLI-only `market_alert_review_queue.metadata_json.ai_summary_review` persistence contract、payload hash 與 metadata patch preview;API/UI 不呼叫 LLM、不派送 Telegram、不寫 preflight、不寫 summary record、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不寫 DB、不 commit、不掛 scheduler;版本同步至 V10.286。
+ - Phase 84 candidate queue review AI summary persistence transaction:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_transaction.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_persistence_transaction` 與 UI transaction 按鈕,在 persistence preflight 後產生 future CLI-only `metadata_json` UPDATE statement preview、parameter preview 與 rollback plan;API/UI 不開 DB、不執行 SQL、不寫 transaction、不寫 summary record、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不派送 Telegram、不呼叫 LLM、不 commit、不掛 scheduler;版本同步至 V10.287。
+ - Phase 85 candidate queue review AI summary persistence writer preflight:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_writer_preflight.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_persistence_writer_preflight` 與 UI writer preflight 按鈕,在 transaction preview 後檢查 CLI-only writer contract、metadata_json backup requirement、post-write smoke requirement 與 artifact path gate;API/UI 不開 DB、不執行 SQL、不寫 preflight、不寫 summary record、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不派送 Telegram、不呼叫 LLM、不 commit、不掛 scheduler;版本同步至 V10.290。
+ - Phase 86 candidate queue review AI summary persistence run package:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_run_package.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_persistence_run_package` 與 UI run package 按鈕,在 writer preflight 後整理 payload manifest、CLI command bundle、required artifacts、operator signoff 與 rollback plan;API/UI 不開 DB、不執行 SQL、不寫 run package、不寫 summary record、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不派送 Telegram、不呼叫 LLM、不 commit、不掛 scheduler;版本同步至 V10.294。
+ - Phase 87 candidate queue review AI summary persistence run readiness:新增 `services/market_intel/candidate_queue_review_ai_summary_persistence_run_readiness.py`、POST `/api/market_intel/ai_controlled/review/candidate_queue_review_ai_summary_persistence_run_readiness` 與 UI readiness 按鈕,在 run package 後檢查 artifact path、metadata_json backup、read-only preflight、shell-only token 與 post-write smoke 計畫;API/UI 不開 DB、不執行 SQL、不寫 readiness artifact、不寫 metadata_json、不讀 token、不執行 CLI、不更新 review_state、不派送 Telegram、不呼叫 LLM、不 commit、不掛 scheduler;版本同步至 V10.297。
- V10.248 補市場情報 390px preview panel QA:sample review 工具列改為 textarea + 可換行 action rail,移除舊的硬編 8 欄 grid;`check_responsive_overflow` 新增 `--screenshot-all`,本機 390x844 `/market_intel` 真頁面 QA 通過且 overflow=0。
- V10.250 補 Code Review Gemini 備援遙測護欄:Ollama 主路徑失敗時 `fallback_to` 明確指向 `code_review_openclaw_gemini`,測試鎖住「Gemini 不得記成 `code_review_openclaw` 主 caller」;AI Calls 觀測台會把 legacy `code_review_openclaw + gemini` 顯示成 Gemini 備援,避免誤判 Gemini-first。
- Schema smoke:`tests/test_market_intel_skeleton.py` 檢查 `Base.metadata` 內含 ADR-035 八張 `market_*` tables。
@@ -393,7 +2467,7 @@
- SOT:更新 `docs/AI_INTELLIGENCE_MODULE_SOT.md` 至 V10.11 AI Automation Metrics Scrape 架構。
- Codex 規則:更新 `AGENTS.md`、`CONSTITUTION.md`、ADR/memory 索引。
- Prometheus 指標化:新增 EventRouter / AutoHeal / safe action / replay in-process metrics,並接入 `/metrics`。
- - 線上 smoke dashboard:新增 `/ai_automation_smoke` 與 `/api/ai-automation/smoke`,覆蓋 EventRouter、AutoHeal、NemoTron fallback、OpenClaw embedding queue、ElephantAlpha HITL。
+ - 線上 smoke dashboard:新增 `/ai_automation_smoke` 與 `/api/ai-automation/smoke`,覆蓋 EventRouter、AutoHeal、NemoTron fallback、OpenClaw embedding queue、ElephantAlpha AI 例外決策。
- Smoke 趨勢保存:`/api/ai-automation/smoke` 每次快檢追加 JSONL 精簡紀錄,dashboard 顯示最近趨勢。
- Smoke 趨勢管理:新增 JSONL 匯出、清理與每日摘要。
- Smoke 每日摘要:新增 Telegram 手動推播 API 與 momo-scheduler 每日 09:10 排程入口。
diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md
index 22092c6..ee125d3 100644
--- a/docs/AI_INTELLIGENCE_MODULE_SOT.md
+++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md
@@ -13,7 +13,7 @@
- 188 直連 GCP-A / GCP-B timeout 時,resolver 可先使用同順位 110 proxy rescue:GCP-A direct → `192.168.0.110:11435` → GCP-B direct → `192.168.0.110:11436` → 111。proxy rescue 只是同一順位的可用入口,不代表 GCP direct host 已恢復。
- `OLLAMA_RESOLVE_HOST_HEALTH_SKIP_ENABLED=true` 時,resolver 會讀最近 `host_health_probes`;若 direct GCP-A/GCP-B 在視窗內已被判定不健康,會直接略過該 direct endpoint,先試同順位 proxy rescue,避免每 120 秒 cache refresh 都等待 direct timeout。此 skip 只套用 direct GCP,不套用 110 proxy。
- `config.OLLAMA_HOST`、`config.HERMES_URL`、`config.EMBEDDING_HOST` 只保留為舊 caller 相容常數;import-time 不得 probe network,也不得因 GCP-A/GCP-B 短暫不可用而 freeze 到 111。需要即時路由時一律呼叫 `get_ollama_host()`、`get_hermes_url()`、`get_embedding_host()` 或 `OllamaService`。
-- Gemini 只能作為 Ollama 主路徑失敗後的備援;MCP Grounding、PPT/vision、週/月報、Code Review、EA HITL、複雜 SKU 升級等舊鎖定場景也必須先走 GCP-A → GCP-B → 111。
+- Gemini 只能作為 Ollama 主路徑失敗後的備援;MCP Grounding、PPT/vision、週/月報、Code Review、EA AI 例外決策、複雜 SKU 升級等舊鎖定場景也必須先走 GCP-A → GCP-B → 111。
- 188 `192.168.0.188` 僅是 App / DB / scheduler / Telegram bot 容器宿主與 AutoHeal target,不可作為 Ollama 節點。
- 通用 AI 文案、關鍵字、商品洞察與 Telegram Q&A 第一響應不得 Gemini-first。
- Hermes intent / analyst 路徑不得手刻 `/api/generate` 或只 resolve 單次 host;必須走 `OllamaService`。預設 `HERMES_ALLOW_111_FALLBACK=false`,同一請求只跑 GCP-A → GCP-B;兩台都失敗時回規則引擎或 DB 證據 fallback,不把批量價格分析轉嫁到 111。救急時才可顯式設 true 允許 111 接手。
@@ -46,7 +46,7 @@
- Scheduler 每 15 分鐘執行 `run_ollama_111_usage_guard_check()`,只讀 `ai_calls` 統計最近視窗的 GCP-A / GCP-B / 111 呼叫量;預設 60 分鐘內 Ollama 呼叫至少 20 次、111 至少 3 次且占比 >= 5% 才推 Telegram。這是觀測護欄,不改路由、不寫 DB、不自動重啟服務。
- `OllamaService` 對 111 final fallback 有 circuit breaker:預設最近 60 分鐘 Ollama 呼叫至少 20 次、111 至少 5 次且占比 >= 5% 時,短暫跳過 111(`OLLAMA_111_CIRCUIT_CACHE_SEC=60`),避免 111 在已偏高時繼續承接長任務;DB 觀測失敗時 fail-open,不讓主要 GCP-A/GCP-B 路由被觀測層中斷。
- 111 的 LAN 入口必須經 `scripts/ops/ollama111_allow_proxy.py` allowlist proxy:真實 Ollama 綁 `127.0.0.1:11434`,proxy 綁 `192.168.0.111:11434`,預設只允許 111 本機與 188 生產宿主;110 / 121 / 其他 LAN client 不能直接打 111,避免跨專案 CI 或 VM 繞過 momo-pro router 載入 7B+ runner。111 上以 `scripts/ops/install_ollama111_allow_proxy.sh` 安裝 user LaunchAgent,安裝器會把 proxy script 複製到 `~/.local/share/momo-pro-system/ollama111_allow_proxy.py`,讓 LaunchAgent 不依賴 iCloud repo 掛載路徑,並讓 proxy 與 `OLLAMA_HOST=127.0.0.1:11434` 在登入/重啟後自動恢復。拒絕日誌以 `OLLAMA111_PROXY_REJECT_LOG_DEDUP_SEC=60` 去重,避免 121 這類旁路探測刷爆 111 磁碟日誌。
-- ElephantAlpha 的 `price_drop_alert` / `market_opportunity` Telegram HITL 告警必須把同款證據獨立呈現,至少包含 `match_type`、`price_basis`、`alert_tier` 與 `match_score`;沒有高信心同款與總價可比證據時,不得把 PChome/MOMO 價差寫成可直接跟價建議。
+- ElephantAlpha 的 `price_drop_alert` / `market_opportunity` Telegram AI 例外決策 告警必須把同款證據獨立呈現,至少包含 `match_type`、`price_basis`、`alert_tier` 與 `match_score`;沒有高信心同款與總價可比證據時,不得把 PChome/MOMO 價差寫成可直接跟價建議。
## 零之零、產品定位正名(2026-06-15)
@@ -60,7 +60,7 @@
- 商品清單、AI 挑品、比價覆核與待確認候選必須採商品身份優先 UI:縮圖、商品 ID、平台、賣場連結、價格/促銷狀態、可信度與下一步動作需在同一商品區塊內可一眼掃描;AI 建議不得只顯示長段理由,必須同時提供可開啟的賣場入口。
- 蝦皮與酷澎等未接入來源暫停接入,不進作戰清單、不發告警;後續只可透過 official API / provider API / manual CSV 進 `external_offers` 類正規化層,並清楚標示資料品質。
- V10.607 新增 `external_market_sources` / `external_offers` 正規化層與 `/api/ai/pchome-growth/source-contract` 只讀 API。MOMO 先以既有比價快取橋接進來源狀態;蝦皮與酷澎只保留 official API、provider API、manual CSV contract,預設暫停且不進告警。
-- V10.608 新增 `/api/ai/pchome-growth/external-offers/csv-dry-run` 與 AI 情報頁「外部報價預檢」。CSV 預檢只讀、不寫 DB;逐列回報「可使用」「需人工確認」「不能使用」,並支援中文表頭,避免格式小錯造成整批匯入失敗。
+- V10.608 新增 `/api/ai/pchome-growth/external-offers/csv-dry-run` 與 AI 情報頁「外部報價預檢」。CSV 預檢只讀、不寫 DB;逐列回報「可使用」「需 AI 自動驗證確認」「不能使用」,並支援中文表頭,避免格式小錯造成整批匯入失敗。
- V10.609 明確把外部報價主路徑改為自動化:`run_external_offer_sync_task` 每 4 小時將已確認同款的既有比價快取同步進 `external_offers`。CSV 只保留為 API / crawler / provider 失敗時的備援預檢入口,不是日常營運主流程。
- V10.610 起 `/api/ai/pchome-growth/opportunities` 優先讀取 `external_offers` 的自動同步資料;只有新資料層缺資料時才 fallback 舊 `competitor_prices`。API stats 會回傳資料來源計數,方便確認作戰清單是否已走新資料層。
- V10.611 起 `/ai_intelligence` 是營運使用者主入口;V10.617 已將舊「今日作戰入口 / 外部報價預檢」改為「今日重點總覽 / 備援資料檢查」,主流程不得再把人工 CSV 放在前段。
@@ -72,8 +72,8 @@
- V10.617 起 `/ai_intelligence` 必須採「先給下一步」的作戰導向 UI:首屏需先回答「今天先做什麼」,再呈現商品處理進度、外部價格來源與操作捷徑;今日處理清單需用表格呈現優先級、建議動作、商品、近 7 天業績、比價結果、資料可信度與下一步;MOMO 外部價格參考需顯示價格風險分佈,且表格需以 PChome 價格優先,明確顯示「PChome 貴 / PChome 便宜」與可信度,不得只用大段文字說明使用方式。
- V10.618 起 `/price_comparison` 也必須採「先給下一步」的比價決策 UI:首屏需顯示目前卡在哪一步、PChome / MOMO 資料準備狀態與下一個按鈕;比價結果需先呈現「需檢查價格 / 可主推曝光 / 價格接近」分佈,再用表格列出每筆商品的下一步,不得只呈現 Step 流程或原始價差表。
- V10.619 起 MOMO 比價候選來源新增「PChome 商品導向搜尋」:當比價 API 已有 PChome 商品但缺 MOMO 清單時,必須用每筆 PChome 商品名稱產生精準搜尋詞反查 MOMO,保留品牌、品名、容量與組合線索;新版 MOMO 搜尋頁需解析 Next.js `goodsInfoList` payload。此路徑只擴大候選池,不放寬同款 matcher 門檻。
-- V10.620 起 `unit_comparable` 不再一律丟人工確認:若 `build_unit_price_comparison()` 可產生明確容量/數量、MOMO 單位價、PChome 單位價與差距百分比,候選需標為「自動單位價比較」並回傳 `auto_compare_type=unit_price`。此類候選可自動呈現價格壓力,但不得混入舊總價同款比價表,也不得直接寫入正式價差或自動改價;無法產生單位證據時才維持「需人工確認」。
-- V10.621 起 `/price_comparison` 的「自動找 MOMO 候選」會把可直接總價比價與自動單位價候選同步到 `external_offers`,`ingestion_method='targeted_momo_search'`,人工確認候選不得寫入。`external_offers.raw_payload_json.price_basis='unit_price'` 時,作戰清單必須使用 `unit_price_comparison` 的 MOMO / PChome 單位價與 `unit_gap_pct` 判斷價格壓力;不得把 MOMO 組合總價與 PChome 單品總價直接相減。此同步只影響外部價格參考與作戰清單,不寫 `competitor_prices`,也不自動改價。
+- V10.620 起 `unit_comparable` 不再一律丟 AI 自動驗證確認:若 `build_unit_price_comparison()` 可產生明確容量/數量、MOMO 單位價、PChome 單位價與差距百分比,候選需標為「自動單位價比較」並回傳 `auto_compare_type=unit_price`。此類候選可自動呈現價格壓力,但不得混入舊總價同款比價表,也不得直接寫入正式價差或自動改價;無法產生單位證據時才維持「需 AI 自動驗證確認」。
+- V10.621 起 `/price_comparison` 的「自動找 MOMO 候選」會把可直接總價比價與自動單位價候選同步到 `external_offers`,`ingestion_method='targeted_momo_search'`,AI 自動驗證確認候選不得寫入。`external_offers.raw_payload_json.price_basis='unit_price'` 時,作戰清單必須使用 `unit_price_comparison` 的 MOMO / PChome 單位價與 `unit_gap_pct` 判斷價格壓力;不得把 MOMO 組合總價與 PChome 單品總價直接相減。此同步只影響外部價格參考與作戰清單,不寫 `competitor_prices`,也不自動改價。
- V10.622 起任何 `external_offers` 自動同步成功寫入後,必須呼叫 `mark_pchome_growth_cache_stale()` 寫入共享 cache epoch;`/api/ai/pchome-growth/opportunities` 讀快取前必須比對 `get_pchome_growth_cache_epoch()`。這是跨 Gunicorn worker 的可見性保護,避免自動候選已進外部價格參考,但 AI 情報頁仍回 120 秒舊作戰清單。
- V10.623 起 `/price_comparison` 與 `/ai_intelligence` 不得只靠大段文字說明流程:比價頁第一屏必須有主 KPI、目前卡點、四步流程與結果決策摘要;作戰頁第一屏必須有今日任務、可立即處理、待補比價與最新業績日。所有狀態都要由實際 API/前端狀態驅動,讓使用者一眼知道下一步要按哪個動作。
- V10.638 起 PChome 導向 MOMO 補抓會把「找到但不能自動比價」的候選以 `match_status='needs_review'`、`data_quality_status='needs_review'` 保存到 `external_offers`;這些候選不得進價格壓力判斷,也不得發告警,但 `/api/ai/pchome-growth/opportunities` 可回傳待確認候選數,讓 UI 顯示「已有候選待確認」而不是只顯示無法比價。
@@ -82,6 +82,7 @@
- V10.641 起 `/ai_intelligence` 的摘要數字不可只是靜態文字;第一屏 KPI、商品處理進度、待確認數字都必須可點擊並導向對應明細。今日清單若已有 MOMO 待確認候選,下一步必須顯示「確認候選」並跳到候選面板,不得再只顯示「補齊比價」。
- V10.642 起 `/ai_intelligence` 的摘要卡與商品處理數字不可只跳到大區塊;點擊後必須開啟商品明細面板,列出商品名稱、分類、近 7 天業績、業績變化、MOMO 比價狀態與下一步按鈕。明細需至少支援全部、價格壓力、價格優勢、待確認、缺比價與有外部價切換;外部價格風險分佈也必須能一鍵篩選下方表格。
- V10.643 起 `/ai_intelligence` 的商品明細上方必須提供「商品策略分流」視覺摘要,至少包含價格壓力、價格優勢、待確認、缺比價四類;每一類需顯示件數、近 7 天業績與比例條,且可點擊切換明細。舊 KPI 卡也不得是靜態數字,需可導向全部商品、可處理商品、高風險比價或處理紀錄。
+- V10.725 source-ready 起 PChome growth 必須提供 `/api/ai/pchome-growth/ai-automation-readiness` 與 Dashboard「AI 主流程」狀態列;同一摘要要聚合缺口偵測、同款搜尋包、候選決策包、證據收據與受控落地,並明確輸出 `primary_human_gate_count=0`、`automation_policy.primary_flow=ai_controlled`。PChome mapping 不得把 AI 例外決策當主流程;所有例外都要進 AI machine-verifiable auto-resolution,產生 failure reasons、下一個機器動作與 rollback/readback 路徑。
- V10.644 起 `/ai_intelligence` 的商品明細列不得只用句子描述比價;每列必須顯示 PChome 價格、MOMO 參考價、差距、可信度四格價格證據,並保留下一步按鈕。單位價候選需顯示單位價與單位,候選待確認或缺資料則以「待補 / 候選待確認」呈現,不得捏造價格。
- V10.645 起 `/ai_intelligence` 的商品明細分流切換後,必須顯示「這類商品怎麼處理」的行動摘要,包含件數、近 7 天業績、平均可信度、最大價差、代表商品與主按鈕;使用者不得只能看到商品列表而不知道下一步。
- V10.646 起 `/ai_intelligence` 的商品明細必須提供搜尋與排序;搜尋至少涵蓋商品、分類、商品編號與 MOMO 候選資訊,排序至少支援優先級、近 7 天業績、價差、下滑幅度與可信度。搜尋/排序後的行動摘要與明細列表必須使用同一批結果。
@@ -99,19 +100,19 @@
## 零之一、12 Agent 決策信封(2026-05-24)
-- 12 角色分工不作為 12 個常駐模型;在產品層統一收斂成 `decision_envelope`,由 Hermes / NemoTron / OpenClaw / ElephantAlpha 與人工審核、PPT QA、競品 review queue 共用。
+- 12 角色分工不作為 12 個常駐模型;在產品層統一收斂成 `decision_envelope`,由 Hermes / NemoTron / OpenClaw / ElephantAlpha 與 AI 例外決策、PPT QA、競品 review queue 共用。
- `decision_envelope` 必須至少能表達:`decision_type`、`severity`、`evidence[]`、`recommended_action`、`expected_impact`、`confidence`、`guardrails`、`trace`。
-- `guardrails.can_auto_execute=false` 是預設;價格調整、正式比價覆寫、PPT 發送與修復執行都必須遵守 HITL 或既有 service guard,不得因 Agent 信心高就繞過 matcher / feeder / review service。
+- `guardrails.can_auto_execute=false` 是預設;價格調整、正式比價覆寫、PPT 發送與修復執行都必須遵守 AI 例外決策或既有 service guard,不得因 Agent 信心高就繞過 matcher / feeder / review service。
- 證據不足時不得輸出空泛效益預測;必須標記 `data_quality=missing|partial|stale`,並把建議行動降級成 `human_review`、`needs_research` 或 `silence_alert`。
- Telegram `triaged_alert()` 已支援渲染 `decision_envelope`,讓告警固定呈現嚴重度、證據、建議行動、預期影響、信心度與追蹤 ID;後續觀測台與 PPT 也應共用同一份欄位語意。
-- NemoTron `price_alert` / `human_review` 派發會把同款證據、價差、七日銷量變化、營收流失、HITL 邊界與資料品質寫入同一份 `decision_envelope`,並同步放入 EventRouter event 與 KM metadata;12 Agent 後續只能沿用此信封補充分析,不得繞過 matcher / feeder / review service 直接改價或覆寫比價資料。
-- EventRouter / Telegram 的 HITL callback 必須優先使用 `decision_envelope.decision_id` 作為事件追蹤 ID;若上游未帶 `event.id`,`triaged_alert()` 仍會用 `decision_id` 產生 `momo:eig:*` callback,避免價格決策審核落成 `unknown`。所有 `momo:eig:*` callback 必須以 UTF-8 byte-safe 截斷,確保 `callback_data` 不超過 Telegram 64-byte 限制。
+- NemoTron `price_alert` / `human_review` 派發會把同款證據、價差、七日銷量變化、營收流失、AI 例外決策 邊界與資料品質寫入同一份 `decision_envelope`,並同步放入 EventRouter event 與 KM metadata;12 Agent 後續只能沿用此信封補充分析,不得繞過 matcher / feeder / review service 直接改價或覆寫比價資料。
+- EventRouter / Telegram 的 AI 例外決策 callback 必須優先使用 `decision_envelope.decision_id` 作為事件追蹤 ID;若上游未帶 `event.id`,`triaged_alert()` 仍會用 `decision_id` 產生 `momo:eig:*` callback,避免價格決策審核落成 `unknown`。所有 `momo:eig:*` callback 必須以 UTF-8 byte-safe 截斷,確保 `callback_data` 不超過 Telegram 64-byte 限制。
- 競品比價相關的 Agent 建議只能讀 `competitor_match_attempts` / review queue / `competitor_prices` 的既有證據;不得直接寫 `competitor_prices` 或覆蓋 `_should_upsert_competitor_price()` 的保護規則。
-- 已帶 `decision_envelope` 的價格/覆核事件必須由 EventRouter 直接渲染證據模板,不再進 L1/L2 AI 重新摘要;Telegram 決策信封需顯示標的 SKU、商品名稱、PChome 候選、evidence、guardrails 與 HITL 動作,避免已有實證的比價告警被二次生成文字稀釋或造成額外模型成本。
-- PChome 覆核隊列本身也必須輸出 `decision_envelope`:`fetch_competitor_review_queue()`、`fetch_competitor_review_queue_page()` 與 `/api/pchome-review/queue` 的每筆候選需帶相同的 `subject`、`evidence`、`recommended_action`、`expected_impact` 與 `guardrails`,供 Dashboard、Agent、Telegram 與 PPT 共用;任何下游不得另寫一套比價狀態翻譯或繞過 HITL guardrails。
-- Dashboard 覆核卡與 `/api/export/excel/pchome-review` 也必須顯示/匯出 `decision_envelope` 的等級、資料品質、建議代碼、HITL、trace 與 `can_auto_execute=false` 邊界;操作員離開系統畫面或下載 Excel 後,仍要看得到「不可自動寫正式價差」的 guardrails。
-- OpenClaw 週報/日報/月報與 competitor PPT 不得再各自重算或翻譯 PChome 覆核狀態;必須透過 `competitor_intel_repository.summarize_review_decision_envelopes()` 讀取同一份 `decision_envelope` 摘要,並在 prompt / data_summary / KPI slide 保留 HITL 與 `can_auto_execute=false` 邊界。
-- Webcrumbs / Shared UI host data 也必須透過 `summarize_review_decision_envelopes()` 輸出 `reviewDecisionBrief`,並在 metadata 保留 review queue、HITL、auto-execute-blocked、`decision_support_rate`、`catalog_comparable_count` 與 catalog review lane counts;不得另寫一套 PChome 覆核摘要或在前端 runtime 重新推論價格行動。
+- 已帶 `decision_envelope` 的價格/覆核事件必須由 EventRouter 直接渲染證據模板,不再進 L1/L2 AI 重新摘要;Telegram 決策信封需顯示標的 SKU、商品名稱、PChome 候選、evidence、guardrails 與 AI 例外決策 動作,避免已有實證的比價告警被二次生成文字稀釋或造成額外模型成本。
+- PChome 覆核隊列本身也必須輸出 `decision_envelope`:`fetch_competitor_review_queue()`、`fetch_competitor_review_queue_page()` 與 `/api/pchome-review/queue` 的每筆候選需帶相同的 `subject`、`evidence`、`recommended_action`、`expected_impact` 與 `guardrails`,供 Dashboard、Agent、Telegram 與 PPT 共用;任何下游不得另寫一套比價狀態翻譯或繞過 AI 例外決策 guardrails。
+- Dashboard 覆核卡與 `/api/export/excel/pchome-review` 也必須顯示/匯出 `decision_envelope` 的等級、資料品質、建議代碼、AI 例外決策、trace 與 `can_auto_execute=false` 邊界;操作員離開系統畫面或下載 Excel 後,仍要看得到「不可自動寫正式價差」的 guardrails。
+- OpenClaw 週報/日報/月報與 competitor PPT 不得再各自重算或翻譯 PChome 覆核狀態;必須透過 `competitor_intel_repository.summarize_review_decision_envelopes()` 讀取同一份 `decision_envelope` 摘要,並在 prompt / data_summary / KPI slide 保留 AI 例外決策與 `can_auto_execute=false` 邊界。
+- Webcrumbs / Shared UI host data 也必須透過 `summarize_review_decision_envelopes()` 輸出 `reviewDecisionBrief`,並在 metadata 保留 review queue、AI 例外決策、auto-execute-blocked、`decision_support_rate`、`catalog_comparable_count` 與 catalog review lane counts;不得另寫一套 PChome 覆核摘要或在前端 runtime 重新推論價格行動。
- ElephantAlpha 的 `resource_optimization` 與低信心 `ea_escalation` 也必須輸出 `decision_envelope`:資源壓力信封只能使用 `action_plans`、CPU 實測、hygiene 結果與 insight/action trace,不得加入 LLM 預測效益;`triaged_alert()` 對 `ea_escalation` 亦需渲染信封並以 `decision_id` 作為 callback 追蹤 ID。
## 一、四 AI Agent 路由架構
@@ -129,10 +130,10 @@ SQL漏斗(~300筆)
任務: Tool Calling → Telegram 告警 / DB 寫入
↓
[OpenClaw] — 策略師 (Ollama-first;Gemini 僅備援 / 鎖定場景)
- 任務: 週策略報告、洞察報告、L3 HITL 建議
+ 任務: 週策略報告、洞察報告、L3 AI 例外決策 建議
↓
[ElephantAlpha] — 編排者 (L3 Orchestrator)
- 任務: 跨 Agent orchestration、HITL、AutoHeal bridge、受控 log scan
+ 任務: 跨 Agent orchestration、AI 例外決策、AutoHeal bridge、受控 log scan
```
### 1.1 PChome 挑品 Agent(2026-05-01)
@@ -150,11 +151,11 @@ SQL漏斗(~300筆)
- 補抓狀態入口:`GET /api/ai/pchome-match/backfill/status` 除背景任務狀態外,必須回傳 read-only coverage snapshot:`active_with_price` / `valid_matches` / `match_rate` / `fresh_matches` / `fresh_match_rate` / `decision_ready_matches` / `decision_ready_rate` / `stale_matches` / `pending` / `actionable_review_count`,供 Dashboard 顯示目前該刷新過期價格或補抓未搜尋商品;此端點不寫 DB、不呼叫 LLM、不抓外站。`match_rate` 是身份覆蓋率,`fresh_match_rate` 是已配對 identity 內的新鮮比例,`decision_ready_rate` 才是可直接進入決策、圖表與簡報的 ACTIVE 商品比價覆蓋率。
- 排程閉環:`run_pchome_match_backfill_task` 每日 10:30 執行,補抓 PChome 待比對商品、寫入歷史價格,再重算 `strategy='product_pick'` 清單。
- PChome / MOMO 競價摘要出口 `services/competitor_intel_repository.py` 使用 30 分鐘共享快取(`COMPETITOR_INTEL_CACHE_TTL_SECONDS` 可調),避免 `/growth_analysis`、`/daily_sales`、PPT/AI 報表每次請求重跑昂貴覆蓋率與價差趨勢查詢;`run_competitor_price_feeder_task` 與 PChome backfill 完成後會主動清除快取。快取只包摘要輸出,不改 matcher 的高信心門檻與 identity_v2 準確性規則。
-- 商品看板第一屏:`/` 的 V2 看板直接以 `products`、`price_records`、`competitor_prices`、`competitor_match_attempts`、`competitor_match_reviews`、`ai_price_recommendations` 顯示比對覆蓋率、PChome 優勢、MOMO 威脅、AI 挑品、待比對優先清單與 PChome 覆核隊列;`filter=ai_picks` 可查看 50 品 AI 挑品列表,`filter=pchome_review` 可直接查看需人工處理的比價覆核 SKU,並以 DB 分頁支援 search/category/status 後的完整隊列,不得只截前 50 筆。覆核狀態篩選必須至少包含全部、需單位價、已排除、低信心、價格過期、找不到同款與人工閉環,讓人工可依 matcher 診斷類型分批處理。列內顯示候選 PChome 商品、候選價、match score、單位價換算摘要、人工動作與 matcher 診斷原因標籤(品牌不符、商品線不符、容量差異、組合差異、需單位價、價差極端等),不得只顯示籠統「待比對」。`/api/export/excel/pchome-review` 必須匯出同一套覆核隊列、人工處置、候選 PChome、單位價比較與原始診斷,讓人工覆核、簡報與後續 AI 分析共用同一份證據。`/api/pchome-review//decision` 是人工閉環入口:`accept_identity` 才可把候選寫入 `competitor_prices` 與 `competitor_price_history` 並打上 `manual_review/manual_accept/identity_v2`;`reject_identity`、`unit_price_required` 與 `needs_research` 只寫 `competitor_match_reviews` 並追加 manual attempt,不得把不同販售組合或否決候選灌入正式價差。PChome feeder 後續搜尋同一候選時必須讀取 `competitor_match_reviews`:已否決候選寫 `manual_rejected` 並跳過正式寫入,且必須繼續評估下一個候選,不能讓已否決候選長期阻塞同 SKU;已標記單位價候選寫 `manual_unit_price_required`;已要求補搜尋候選寫 `manual_needs_research` 並停留在覆核隊列;已採用候選可保守補到最低門檻並保留 `manual_review/manual_accept` 標籤。搜尋候選池只有強同款分數達 `0.90` 才可提前停止,避免 0.76 灰區候選卡掉後續更精準搜尋詞。人工 `reject_identity`、`unit_price_required`、`needs_research` 若命中當前正式候選,必須將同候選 `competitor_prices` 過期,不得繼續顯示正式總價差。商品列表必須將 `manual_rejected`、`manual_unit_price_required`、`manual_needs_research` 顯示為明確人工閉環狀態,不可回落成籠統「待比對」。`fetch_competitor_coverage()` 必須輸出人工採用、人工否決、人工單位價與採用率,daily/growth/PPT 共用 payload 必須顯示人工閉環成效,避免只呈現待審數。商品看板深度快取同時寫入 `data/dashboard_full_cache.pkl`,供多個 Gunicorn worker 共用,避免部署後各 worker 重複重建 7,000+ 商品統計造成開頁變慢;所有資料異動與 AI 挑品重算都透過 `clear_dashboard_cache()` 同步清除記憶體與共享快取,手動重算 API 會立即預熱商品看板快取,避免第一位使用者承擔重建成本。
-- PChome re-score 回收線:`rescore_accepted_current` 只能表示最新版 matcher 判定「值得人工覆核身份」,不可直接寫入正式 `competitor_prices`;`no_match`、`price_basis=none`、`alert_tier=suppress`、`variant_selection_review` 不得進入此隊列。`fetch_competitor_coverage()` 必須輸出 `rescore_accepted_count`,Dashboard、daily/growth 與 OpenClaw 競品摘要都要把「重算待人工覆核」獨立呈現,避免和一般低信心/單位價覆核混在一起。
+- 商品看板第一屏:`/` 的 V2 看板直接以 `products`、`price_records`、`competitor_prices`、`competitor_match_attempts`、`competitor_match_reviews`、`ai_price_recommendations` 顯示比對覆蓋率、PChome 優勢、MOMO 威脅、AI 挑品、待比對優先清單與 PChome 覆核隊列;`filter=ai_picks` 可查看 50 品 AI 挑品列表,`filter=pchome_review` 可直接查看需 AI 例外處理的比價覆核 SKU,並以 DB 分頁支援 search/category/status 後的完整隊列,不得只截前 50 筆。覆核狀態篩選必須至少包含全部、需單位價、已排除、低信心、價格過期、找不到同款與 AI 例外閉環,讓 AI 可依 matcher 診斷類型分批處理。列內顯示候選 PChome 商品、候選價、match score、單位價換算摘要、AI 例外動作與 matcher 診斷原因標籤(品牌不符、商品線不符、容量差異、組合差異、需單位價、價差極端等),不得只顯示籠統「待比對」。`/api/export/excel/pchome-review` 必須匯出同一套覆核隊列、AI 例外處置、候選 PChome、單位價比較與原始診斷,讓 AI 例外決策、簡報與後續 AI 分析共用同一份證據。`/api/pchome-review//decision` 是AI 例外閉環入口:`accept_identity` 才可把候選寫入 `competitor_prices` 與 `competitor_price_history` 並打上 `manual_review/manual_accept/identity_v2`;`reject_identity`、`unit_price_required` 與 `needs_research` 只寫 `competitor_match_reviews` 並追加 manual attempt,不得把不同販售組合或否決候選灌入正式價差。PChome feeder 後續搜尋同一候選時必須讀取 `competitor_match_reviews`:已否決候選寫 `manual_rejected` 並跳過正式寫入,且必須繼續評估下一個候選,不能讓已否決候選長期阻塞同 SKU;已標記單位價候選寫 `manual_unit_price_required`;已要求補搜尋候選寫 `manual_needs_research` 並停留在覆核隊列;已採用候選可保守補到最低門檻並保留 `manual_review/manual_accept` 標籤。搜尋候選池只有強同款分數達 `0.90` 才可提前停止,避免 0.76 灰區候選卡掉後續更精準搜尋詞。AI 例外 `reject_identity`、`unit_price_required`、`needs_research` 若命中當前正式候選,必須將同候選 `competitor_prices` 過期,不得繼續顯示正式總價差。商品列表必須將 `manual_rejected`、`manual_unit_price_required`、`manual_needs_research` 顯示為明確AI 例外閉環狀態,不可回落成籠統「待比對」。`fetch_competitor_coverage()` 必須輸出AI 採用、AI 否決、AI 單位價與採用率,daily/growth/PPT 共用 payload 必須顯示AI 例外閉環成效,避免只呈現待審數。商品看板深度快取同時寫入 `data/dashboard_full_cache.pkl`,供多個 Gunicorn worker 共用,避免部署後各 worker 重複重建 7,000+ 商品統計造成開頁變慢;所有資料異動與 AI 挑品重算都透過 `clear_dashboard_cache()` 同步清除記憶體與共享快取,手動重算 API 會立即預熱商品看板快取,避免第一位使用者承擔重建成本。
+- PChome re-score 回收線:`rescore_accepted_current` 只能表示最新版 matcher 判定「值得AI 例外決策身份」,不可直接寫入正式 `competitor_prices`;`no_match`、`price_basis=none`、`alert_tier=suppress`、`variant_selection_review` 不得進入此隊列。`fetch_competitor_coverage()` 必須輸出 `rescore_accepted_count`,Dashboard、daily/growth 與 OpenClaw 競品摘要都要把「重算待 AI 例外決策」獨立呈現,避免和一般低信心/單位價覆核混在一起。
- PChome 低信心操作分流:Dashboard 與 read-only `/api/pchome-review/queue` 必須把近門檻可救、證據不足、低信心舊候選拆成 `recoverable_low_score`、`true_low_confidence`、`legacy_low_score` 三個可篩選桶;廣義 `low_score` 僅作 repository/export 相容查詢,不可在 UI 中冒充單一操作分流。
- PChome coverage 的 `attempt_status` / `rescore_accepted_count` / `actionable_review_count` 口徑必須與 review queue 對齊:統計「沒有新鮮有效 identity」的商品,而不是只統計「完全沒有 identity」的商品;已過期但可重算採用的 stale identity 仍應出現在待審數字中,避免 API 與 Dashboard 漏報。
-- `run_retryable_candidate_revalidation()` 的自動回刷主戰場仍限 `low_score` / `refresh_low_score` / `recoverable_low_score`;`true_low_confidence` 只有在已補 focused exact 規則的窄範圍品線、舊分數 >= 0.95、`comparison_mode='exact_identity'`、含 `strong_exact_spec_match` 且不含 commercial / variant / count / bundle / refill 等阻擋理由時,才可進入重評,不得全面打開人工審核池。`rescore_accepted_current` 只允許命中具名 focused exact 品線、舊分數 >= 0.76、且仍無 hard veto / 阻擋理由時進窄門回刷;最後仍由最新版 matcher 判定是否可寫正式價差,像不同指甲油型號 / 色號必須 hard veto。
+- `run_retryable_candidate_revalidation()` 的自動回刷主戰場仍限 `low_score` / `refresh_low_score` / `recoverable_low_score`;`true_low_confidence` 只有在已補 focused exact 規則的窄範圍品線、舊分數 >= 0.95、`comparison_mode='exact_identity'`、含 `strong_exact_spec_match` 且不含 commercial / variant / count / bundle / refill 等阻擋理由時,才可進入重評,不得全面打開AI 例外池。`rescore_accepted_current` 只允許命中具名 focused exact 品線、舊分數 >= 0.76、且仍無 hard veto / 阻擋理由時進窄門回刷;最後仍由最新版 matcher 判定是否可寫正式價差,像不同指甲油型號 / 色號必須 hard veto。
- 高分 `true_low_confidence` 的自動救回只能用具名 focused exact 線逐批擴充;同品牌、同品線、同規格/同組合的花美水 Relax、St.Clare 私密呼呼、BIOPEUTIC 果酸、台塑生醫嬰兒沐浴洗髮、Elizabeth Arden 八小時護唇膏與理膚寶水全面修復潤唇膏可走 total-price,色號、香味、款式、即期品與 catalog selection 仍維持 review / veto。
- `true_low_confidence` focused exact 線必須同步接入 `run_retryable_candidate_revalidation()` 的 SQL 窄門,讓舊候選可被批次回收;該窄門只允許具名品線豁免 `variant_selection_review`,其他 hard veto / 型別、款式、香味、件數、組合、refill、commercial condition 阻擋仍不得回刷。
- 任選 catalog focused exact 只允許雙方都明確是同品線任選賣場且規格一致的窄範圍案例,例如 FLORTTE 眼線液筆 0.5ml、露得清護手霜 56g 無香/有香、Kanebo ALLIE 持采亮化 UV 防曬水凝乳 60g;若有 `commercial_condition_gap`(即期品、短效、航空版等狀態差異),focused bypass 不得移除 `variant_selection_review`,不得自動寫正式價差。
@@ -162,18 +163,18 @@ SQL漏斗(~300筆)
- 其他正式覆核池 focused exact 線只能針對「已在正式頁面反覆出現且有硬規格」的窄範圍族群,例如 The Ordinary 咖啡因 EGCG、Natures Care 綿羊油同入數、TOMOON 指甲剪同尺寸、HH 雙 200ml 組、SEBAMED 200ml x2、YES 9cm 剪刀;同尺寸、同入數、同組合或單側漏規格必須可由 matcher 明確判斷,不能只因同品牌同品線通過。
- `/api/ai/pchome-match/backfill/status` 必須把近門檻重評池與過期 identity 救援池以只讀 `revalidation_preview` / `stale_recovery_preview` 曝光給操作員;預覽只復用正式候選 SQL 並受 limit / 60 秒快取限制,不啟動 PChome 搜尋、不呼叫 LLM、不寫 `competitor_match_attempts` / `competitor_prices`。重評 preview 必須先從最新 `competitor_match_attempts` 縮小候選,再用 `JOIN LATERAL` 取單一最新 MOMO 價;救援 preview 必須從過期 `competitor_prices` 小集合出發並用 `JOIN LATERAL` 取最新 MOMO 價,兩者都不得掃全量 `price_records`;Dashboard 只能顯示「可救援」觀測值,不得在未開啟 `PCHOME_STALE_RECOVERY_ENABLED` 時提供 recover-stale 執行按鈕;其中 `review_gated_count` 僅代表窄門 `true_low_confidence` exact 候選,不得被解讀為全量人工池可自動回刷。
- PChome re-score audit 預設必須先取每個 SKU 的最新 `competitor_match_attempts` 狀態,再套用 status / reason 篩選;舊低信心歷史候選只能透過 `--include-historical-candidates` 明確進入考古掃描,避免已入隊、已否決或已修正 SKU 被舊紀錄重新推回報表。
-- production re-score `--apply-accepted` 僅可追加 `rescore_accepted_current` attempt 給人工覆核;執行後需清除 Dashboard / competitor intel cache,且必須抽查 `competitor_prices` / `competitor_price_history` 未新增正式價差。
+- production re-score `--apply-accepted` 僅可追加 `rescore_accepted_current` attempt 給 AI 例外決策;執行後需清除 Dashboard / competitor intel cache,且必須抽查 `competitor_prices` / `competitor_price_history` 未新增正式價差。
- production re-score 若曾把 `variant_selection_review` 追加成 `rescore_accepted_current`,必須用 `audit_competitor_match_attempt_rescore.py --retract-variant-accepted` 追加最新 `true_low_confidence` 退回列;此路徑只寫 `competitor_match_attempts`,不得刪歷史紀錄,也不得寫 `competitor_prices` / `competitor_price_history`。
- PChome matcher replay 必須先守住假陽性:`EX8` 等型號不可被誤解析成 `x8` 入數;香氛固體凝膠 / 空氣芳香劑若一側為泛稱、一側含明確香味或 No. 款式,必須走 `aroma_scent_variant_conflict` veto,不得因同品牌同重量直接寫正式價差。
- PChome matcher 對「同規格同數量」的多件組可以安全回收,但必須同時滿足:商品型別完全對齊、品牌同線、規格與數量對齊、沒有 variant / count / bundle / commercial / unit-price / price-ratio 阻擋理由,才可打 `safe_multi_component_exact_total_price` 並進 `exact / total_price / price_alert_exact`;混合組、香味款、色號款、catalog 任選仍需留在 `identity_review` 或 veto。護唇品 focused total-price 僅允許已明確建規則的 DHC 純欖 1.5g、FRUDIA 蜂蜜藍莓 10g、SEBAMED 嬰兒護唇膏 4.8g x2、理膚寶水滋養修護潤唇膏 4.7ml,不得把所有 lip/cosmetic catalog 一次放行。
-- PChome feeder 正式寫入必須再套一層價格資料閘門:只有 `match_type='exact'`、`price_basis='total_price'`、`alert_tier='price_alert_exact'` 且無 `variant_selection_review` 的結果可以自動寫入 `competitor_prices`;`manual_review` / `identity_review` 只能留在覆核隊列或人工採用流程,不得由 retryable replay 或 known identity refresh 自動升成正式價差。Rescore audit 若遇到 `variant_selection_review`,也不得產生 `accepted_current`。
+- PChome feeder 正式寫入必須再套一層價格資料閘門:只有 `match_type='exact'`、`price_basis='total_price'`、`alert_tier='price_alert_exact'` 且無 `variant_selection_review` 的結果可以自動寫入 `competitor_prices`;`manual_review` / `identity_review` 只能留在覆核隊列或AI 採用流程,不得由 retryable replay 或 known identity refresh 自動升成正式價差。Rescore audit 若遇到 `variant_selection_review`,也不得產生 `accepted_current`。
| 角色 | 模型 | 主機 | 成本 | 每日限額 |
|------|------|------|------|---------|
| Hermes 分析師 | hermes3:latest / bge-m3 | GCP-A → GCP-B → 111 Ollama | 零 | 無限 |
| NemoTron 派發器 | qwen3:14b;111 fallback 降級 llama3.2;NIM fallback | GCP-A → GCP-B → 111;NVIDIA NIM 備援 | Ollama 零;NIM 配額內免費 | NIM 80 |
| OpenClaw 策略師 | qwen2.5-coder:7b / qwen3:14b;111 fallback 降級 llama3.2 | Ollama-first;Gemini emergency fallback only | Ollama 零;Gemini 預設封鎖 | — |
-| ElephantAlpha 編排者 | ElephantAlpha | 依部署環境 | 受控 | HITL / 任務制 |
+| ElephantAlpha 編排者 | ElephantAlpha | 依部署環境 | 受控 | AI 例外決策 / 任務制 |
---
@@ -186,7 +187,7 @@ SQL漏斗(~300筆)
→ L2 SAFE_ACTIONS / AutoHeal / OpenClaw memory
→ Telegram 通知,失敗則 file queue,成功後 replay
→ ai_insights + embedding_retry_queue
- → OpenClaw / ElephantAlpha 後續策略與 HITL
+ → OpenClaw / ElephantAlpha 後續策略與 AI 例外決策
```
硬性邊界:
@@ -219,11 +220,11 @@ SQL漏斗(~300筆)
- Gunicorn runtime 預設 `worker_class = gthread`、`GUNICORN_THREADS=4`、`preload_app = False`;此組合讓 HUP 熱重載可用,也避免 Dashboard 長查詢完全阻塞 `/health`。
- CD rebuild 模式必須先 build image 成功,再短暫 stop/rm/recreate 三應用容器,避免 no-cache build 造成長時間 502。
- ElephantAlpha 使用 NVIDIA NIM hosted API;production 預設模型為 `nvidia/llama-3.3-nemotron-super-49b-v1.5`,`ELEPHANT_ALPHA_FALLBACK_MODELS` 需保留至少一個可呼叫備援;403/404、408/409/425/429、5xx、timeout 與 connection error 必須嘗試下一個模型。
-- ElephantAlpha L3 HITL 只允許發送有實證、可審核、可行動的升級告警;價格類 trigger 無 Hermes 具體威脅時,只記錄 suppressed escalation telemetry 與 cooldown,不寫 pending `human_review`,不發 Telegram 空告警。
-- ElephantAlpha 價格類 trigger 的 HITL / 決策 prefetch 必須先使用觸發 SQL 與 `competitor_prices` / `price_records` 的 DB 實證生成 SKU、MOMO / PChome 價差與建議 action lines;完整 Hermes LLM prefetch 預設關閉(`ELEPHANT_ALPHA_HERMES_LLM_PREFETCH_ENABLED=false`),避免 5s timeout 後落入無實證摘要或雲端備援。若無 DB 實證,只記錄 suppressed telemetry / cooldown,不發 Telegram 空告警。
+- ElephantAlpha L3 AI 例外決策 只允許發送有實證、可審核、可行動的升級告警;價格類 trigger 無 Hermes 具體威脅時,只記錄 suppressed escalation telemetry 與 cooldown,不寫 pending `human_review`,不發 Telegram 空告警。
+- ElephantAlpha 價格類 trigger 的 AI 例外決策 / 決策 prefetch 必須先使用觸發 SQL 與 `competitor_prices` / `price_records` 的 DB 實證生成 SKU、MOMO / PChome 價差與建議 action lines;完整 Hermes LLM prefetch 預設關閉(`ELEPHANT_ALPHA_HERMES_LLM_PREFETCH_ENABLED=false`),避免 5s timeout 後落入無實證摘要或雲端備援。若無 DB 實證,只記錄 suppressed telemetry / cooldown,不發 Telegram 空告警。
- ElephantAlpha `price_drop_alert` / `market_opportunity` trigger 不得對整張 `price_records` 做全表最新價聚合;必須先篩最近有效 `identity_v2` PChome 候選,再用 per-SKU `JOIN LATERAL` 讀最新 MOMO 價格,並把 `match_score`、`tags`、`match_diagnostic_json` 帶入 evidence。
-- ElephantAlpha 協調器收到非純 JSON、fenced JSON 或混文字 JSON 時,必須先做容錯抽取;仍無法解析時,只能使用 DB/Hermes 實證生成保守 HITL fallback。fallback 不得放入 OpenClaw `generate_*` 類舊策略步驟,也不得暗示已自動調價。
-- V10.624 起 ElephantAlpha 價格類 trigger 即使信心度達自主門檻,也只能發送 HITL 價格覆核通知;必須跳過 orchestrator `execution_plan` 內的 Hermes/NemoTron/OpenClaw 長任務 step。這是價格決策護欄:避免 60 秒 execution timeout 卡住 scheduler,也避免把價格策略誤描述為已自動執行。
+- ElephantAlpha 協調器收到非純 JSON、fenced JSON 或混文字 JSON 時,必須先做容錯抽取;仍無法解析時,只能使用 DB/Hermes 實證生成保守 AI 例外決策 fallback。fallback 不得放入 OpenClaw `generate_*` 類舊策略步驟,也不得暗示已自動調價。
+- V10.624 起 ElephantAlpha 價格類 trigger 即使信心度達自主門檻,也只能發送 AI 例外決策 價格覆核通知;必須跳過 orchestrator `execution_plan` 內的 Hermes/NemoTron/OpenClaw 長任務 step。這是價格決策護欄:避免 60 秒 execution timeout 卡住 scheduler,也避免把價格策略誤描述為已自動執行。
- ElephantAlpha 執行器若遇到舊版 OpenClaw strategy 類步驟(含 `generate_market_strategy` / `generate_dynamic_pricing_strategy` / `generate_resource_optimization_strategy`),只能記錄為 advisory skipped,不得觸發 circuit breaker,也不得轉成實際排程、外部呼叫或價格行動。
- `resource_optimization` 不再交給 LLM 生成「預期效益 / 已執行」敘事,顯示名稱統一為「資源壓力治理」。此 trigger 必須先由程式量測 `action_plans` backlog、P1/P2 數、pending_review、逾時項目與 CPU load;只有 CPU 達門檻、P1/P2 積壓或逾時積壓才發 Telegram「資源壓力告警」。單純 queue 大但 CPU 正常只記錄 telemetry,不派發 Hermes/NemoTron、不宣稱 48 小時效益;Telegram 段落使用「系統處置紀錄」而非泛稱「已執行」,避免暗示 AI 已完成未經驗證的外部動作。
- `resource_optimization` 的 Telegram 必須包含 `decision_envelope` 區塊,標明 `source_agent=elephant_alpha`、資料品質、量測證據、`can_auto_execute=false` 與 deterministic trace;此路徑不呼叫 Gemini、不呼叫 Hermes/NemoTron,也不得把 queue backlog 翻譯成主機資源耗盡。
@@ -426,7 +427,7 @@ LIMIT 300
| 比對算法 | 品牌 + 核心 token + 容量/重量/包數 + 品類 + 價格 sanity check | 由 `marketplace_product_matcher.py` 統一供 feeder、legacy crawler、AI/PPT 鏈路使用 |
| 最低比對門檻 | 0.76 | 核心比價寧可待審,不允許低信心錯配影響 AI 決策 |
| 已有不同 PChome 商品覆蓋門檻 | 0.84 | 新候選與既有正式配對不同時,除非超高信心,否則寫入 `needs_review` attempt 不覆蓋 |
-| 單位價可比模式 | `unit_comparable` | 同核心商品但買送/套組/件數不同時,不寫正式總價差;只寫入 attempt,並以單位價證據供 Dashboard / PPT / AI 報表與人工覆核 |
+| 單位價可比模式 | `unit_comparable` | 同核心商品但買送/套組/件數不同時,不寫正式總價差;只寫入 attempt,並以單位價證據供 Dashboard / PPT / AI 報表與 AI 例外決策 |
| Browse.sh 診斷 | optional wrapper | 只用於 selector / XHR / network trace 探勘;不得取代正式 crawler,也不得直接把輸出寫成正式競品價格 |
| 語意標籤 | JSONB 陣列 | 傳給 Hermes 提升情境感知品質 |
@@ -466,12 +467,12 @@ LEFT JOIN competitor_prices cp
- 過期 identity refresh 排序必須優先 `price_basis_total_price` / `alert_tier_price_alert_exact` 或 `match_diagnostic_json.price_basis='total_price'` / `alert_tier='price_alert_exact'` 的正式價差配對,再依 `expires_at` 與 MOMO 價格排序,避免高風險可決策價差長期排在低價或非告警型 stale row 後面。
- `marketplace_product_matcher.py` 的擴充只能走「正向證據 + 反向 veto」:品牌一致、商品線/型號訊號強、價格合理且無 hard veto 時才允許 `strong_product_line_match` 加分;補充瓶/補充包/refill 與一般正裝不互相配對,分享組/加量組/明星組等組合包不得誤配單品。
- 近門檻規則必須成對補「召回 + 防錯配」測試:可召回者需有品牌、商品線、規格或具名 identity anchor,例如 MUJI 精油芬香護手霜、Mustela 慕之幼爽身潤膚乳、Herbacin 小甘菊護手霜;防錯配者需成為 hard veto,例如 M·A·C Macximal 柔霧/緞光唇膏質地、ERBE 指甲清垢棒/指甲緣刨刀功能、Schick 舒芙/舒綺女用除毛刀品線。不得用單一同規格或同品牌放寬全域門檻。
-- 套組/買送/件數不同但品牌、核心商品線與單一基礎規格一致時,matcher 必須回傳 `comparison_mode='unit_comparable'` 與 `unit_comparable` reason;Feeder 只能寫入 `competitor_match_attempts.attempt_status='unit_comparable'` 或 `refresh_unit_comparable`,不得寫入 `competitor_prices`。Dashboard 與 `competitor_intel_repository` 必須用 `build_unit_price_comparison()` 產生每 ml / 每 g / 每入單位價證據,讓 PPT / AI 報表可說明「需單位價比較」而不是把總價當同款價差。商品看板在正式配對尚未成立時,仍必須顯示最佳候選 PChome 商品名稱、候選價與「候選價,需單位換算」說明,讓人工覆核可直接看見下一步;daily/growth、PPT 與 OpenClaw 摘要不得自建查詢,需消費 `fetch_competitor_review_queue()` 與 coverage 的 `unit_comparable_count`。若任一側含多個不同容量/重量規格,視為多品項套組,不可進 `unit_comparable`。
+- 套組/買送/件數不同但品牌、核心商品線與單一基礎規格一致時,matcher 必須回傳 `comparison_mode='unit_comparable'` 與 `unit_comparable` reason;Feeder 只能寫入 `competitor_match_attempts.attempt_status='unit_comparable'` 或 `refresh_unit_comparable`,不得寫入 `competitor_prices`。Dashboard 與 `competitor_intel_repository` 必須用 `build_unit_price_comparison()` 產生每 ml / 每 g / 每入單位價證據,讓 PPT / AI 報表可說明「需單位價比較」而不是把總價當同款價差。商品看板在正式配對尚未成立時,仍必須顯示最佳候選 PChome 商品名稱、候選價與「候選價,需單位換算」說明,讓 AI 例外決策可直接看見下一步;daily/growth、PPT 與 OpenClaw 摘要不得自建查詢,需消費 `fetch_competitor_review_queue()` 與 coverage 的 `unit_comparable_count`。若任一側含多個不同容量/重量規格,視為多品項套組,不可進 `unit_comparable`。
- PChome feeder 的外部 request timeout 由 `PCHOME_FEEDER_TIMEOUT` 控制,預設 12 秒;排程不得因單一 PChome 搜尋 API timeout 被拖到數分鐘。
- 品牌 alias 屬於正向身份證據,不是門檻放寬;`DR.WU / DR WU / DRWU / 達爾膚` 這類同品牌中英混寫必須正規化後再進 matcher,避免同規格真同款被誤降成 brandless identity review。
- 近門檻 rescore pilot 必須支援明確 SKU 篩選;`audit_competitor_match_attempt_rescore.py --sku ` 可只重算指定 SKU,避免為了小批次驗證而掃整批 `true_low_confidence`。
- 商品看板的 PChome 狀態必須把 matcher 診斷原因翻成可行動語意:品牌不符已排除、規格不符已排除、補充包不相容、組合規格不相容、系列不符已排除、需單位價比較、低信心待補強等,不可只顯示籠統「待比對」或「身份否決」。
-- PChome 補抓產線與 priority list 若尚未進入搜尋/補抓,必須顯示「PChome 補抓產線」、「尚未搜尋」與「尚未進入 PChome 補抓」,不得使用「待比對」這類會被誤解成已有候選待人工審核的字眼。
+- PChome 補抓產線與 priority list 若尚未進入搜尋/補抓,必須顯示「PChome 補抓產線」、「尚未搜尋」與「尚未進入 PChome 補抓」,不得使用「待比對」這類會被誤解成已有候選待 AI 例外決策的字眼。
- 商品看板、PChome review queue 與 `/api/export/excel/pchome-review` 必須優先讀取 `match_diagnostic_json.reasons` 並轉成操作員可讀標籤;文字版 `error_message` 只作 legacy fallback。商品列的 PChome 狀態摘要也必須使用同一套專業標籤,避免 overview 顯示「妝效質地不同」但列表仍顯示籠統身份不符。新增 matcher reason 時需同步更新 `MATCH_DIAGNOSTIC_REASON_LABELS` 與 dashboard 狀態翻譯,避免 UI 顯示 `makeup_finish_conflict` 這類 machine code。PChome 標題缺品牌但有窄範圍 exact identity anchor 的商品,只能透過具名 brandless recovery 進 manual-review identity;多色任選 / 單一色號 gap 必須標記 `variant_selection_review`,並從 `recoverable_low_score` 降回 `true_low_confidence`,不得自動批次寫正式價差。
- Dashboard 必須把「待比對」拆成可診斷狀態:`價格過期待刷新`、`舊版配對待重驗`、`低分配對待補強`、`已排除`、`需單位價比較`、`找不到同款`、`抓取異常`、`尚未搜尋`。硬性不相容候選應顯示為已排除/不相容,不得讓使用者誤以為每筆都需要人工待審。
@@ -501,7 +502,7 @@ python3 -m services.competitor_identity_revalidator --limit 500 --apply
2. **倒金字塔結構** — 結論先行 → 核心數據 → AI 洞察 → 建議行動 → 運算足跡
3. **收斂行動呼籲 (Call to Action)** — 每則訊息只有一個明確的 👉 建議行動
4. **底部運算足跡** — FinOps + Observability,用分隔線隔開主訊息
-5. **EA HITL 專業 brief** — `ea_escalation` 必須分成決策狀態、背景摘要、風險摘要、TOP 待審 SKU 與建議處置;價格類行動不得用長 bullet 串接,必須拆出 MOMO/PChome 價格、價差、人工處置與 PChome ID。
+5. **EA AI 例外決策 專業 brief** — `ea_escalation` 必須分成決策狀態、背景摘要、風險摘要、TOP 待審 SKU 與建議處置;價格類行動不得用長 bullet 串接,必須拆出 MOMO/PChome 價格、價差、AI 例外處置與 PChome ID。
6. **價格類決策信封專業 brief** — `price_alert`、`pchome_match_review`、`competitor_price_review` 等含 PChome / 價格證據的 `decision_envelope`,EventRouter 必須直送 evidence template,不得進 L1/L2 重摘要;Telegram 內容必須拆成「標的、價格證據、比對證據、人工下一步」,並從信封讀取 `momo_price`、`competitor_price`、`candidate_gap_pct`、`match_score`、`unit_price_insight` 與 `existing_match_conflict`。
7. **Shared UI 信封摘要共用** — Webcrumbs host data 與其他共用 UI runtime 必須讀 `reviewDecisionBrief` / `decision_envelope` 的結構化證據,不在瀏覽器端重組比價判斷;這些 payload 只能讀 DB 既有覆核資料,不呼叫 LLM、不抓外站、不寫資料。
@@ -512,7 +513,7 @@ python3 -m services.competitor_identity_revalidator --limit 500 --apply
| 身份識別 | `⚡ NemoTron 派發器` | Dispatcher 身份 |
| 身份識別 | `🔍 Hermes 3 8B` | Analyst 身份(僅出現在足跡) |
| 風險級別 | `🚨` | 高危險,立即行動 |
-| 風險級別 | `⚠️` | 中風險,人工覆核 |
+| 風險級別 | `⚠️` | 中風險,AI 例外決策 |
| 風險級別 | `💡` | 低風險,策略建議 |
| 例行報告 | `📊` | 核心數據區塊標頭 |
| 業務屬性 | `💰` | 價格/毛利 |
@@ -546,10 +547,10 @@ python3 -m services.competitor_identity_revalidator --limit 500 --apply
• ⚡ 決策: NemoTron NIM | 185 Tokens | $0 (配額內 2/80)
```
-#### 類別二:人工覆核(`flag_for_human_review` 觸發)
+#### 類別二:AI 例外決策(`flag_for_human_review` 觸發)
```
-⚠️ [⚡ NemoTron 派發器] 異常波動需人工覆核
+⚠️ [⚡ NemoTron 派發器] 異常波動需 AI 例外決策
🔍 待查商品:[A001 玻尿酸面膜10片裝]
@@ -559,9 +560,9 @@ python3 -m services.competitor_identity_revalidator --limit 500 --apply
• 庫存狀態:目前庫存充足 (500+ 件)
🧠 AI 洞察 (信心度 45%):
-數據出現矛盾訊號,AI 信心不足以自主決策,需人工走查確認。
+數據出現矛盾訊號,AI 信心不足以自主決策,需 AI controlled apply 走查確認。
-👉 建議行動:請營運人員立即進行人工走查。
+👉 建議行動:請 AI 例外流程立即進行可驗證走查。
─────────────────────
⚙️ 運算足跡:
@@ -645,7 +646,7 @@ python3 -m services.competitor_identity_revalidator --limit 500 --apply
|------|---------|--------------|
| `trigger_price_alert` | HIGH 風險 (gap>15% + 銷量跌>20%) | 🔴/🟡 競價威脅告警 |
| `add_to_recommendation` | 我方價格低於競品且銷量正成長 | ⭐ 推薦商品候選 |
-| `flag_for_human_review` | 信心 < 0.6 或情況複雜 | ⚠️ 需要人工審核 |
+| `flag_for_human_review` | 信心 < 0.6 或情況複雜 | ⚠️ 需要 AI 例外決策 |
---
@@ -737,10 +738,10 @@ POSTGRES_HOST=momo-db
| 2026-05-20 | 正確 PChome 候選常因只掃第一頁或搜尋詞丟失品牌/規格而未進入 matcher | V10.314 起搜尋 API 依 limit 掃多頁、對暫時性錯誤有限重試;feeder 預設 5 組搜尋詞、20 候選、2 頁,並保留括號/方括號內品牌與規格,提升覆核隊列與正式比價的候選品質 |
| 2026-05-20 | 指定日期競品簡報可能混用目前 `competitor_prices` 快取價 | V10.315 起 `fetch_competitor_comparison_results()` 有 start/end date 時改用 `competitor_price_history` 期間快照,MOMO 價格取報表結束日前最新價;即時報表才使用目前有效 `competitor_prices` |
| 2026-05-20 | PChome 覆蓋率分子可能被非活躍或無 MOMO 現價 SKU 膨脹 | V10.317 起 `fetch_competitor_coverage()` 的 `valid_matches` 改為 active MOMO latest price 與有效 PChome `identity_v2` 價格交集,確保 daily/growth/PPT/AI 看到的比價資料品質不被舊快取列高估 |
-| 2026-05-20 | EA HITL 告警可能把非 SKU 診斷誤排成待審 SKU,或在缺少 DB/Hermes 實證時打擾人工 | V10.318 起 `ea_escalation` 僅對含 SKU/價格比較的 actions 使用競價卡片;非 SKU 診斷改為「待確認事項」。價格類低信心事件若無 DB/Hermes 實證,測試鎖定只 suppress、不寫 human_review、不發 Telegram |
+| 2026-05-20 | EA AI 例外決策 告警可能把非 SKU 診斷誤排成待審 SKU,或在缺少 DB/Hermes 實證時打擾人工 | V10.318 起 `ea_escalation` 僅對含 SKU/價格比較的 actions 使用競價卡片;非 SKU 診斷改為「待確認事項」。價格類低信心事件若無 DB/Hermes 實證,測試鎖定只 suppress、不寫 human_review、不發 Telegram |
| 2026-05-21 | ElephantAlpha NIM/LLM 回應偶爾不是純 JSON,會觸發 `json.loads()` 失敗並落入舊式空泛策略 fallback | V10.383 起協調器容忍 fenced/混文字 JSON;無法解析時改用 DB/Hermes 實證 fallback,且 fallback 不再包含 OpenClaw `generate_*` 舊步驟或自動調價暗示 |
| 2026-05-20 | Telegram HTML parse mode 不支援 `
`,可能導致告警或報告送出 400 | V10.321 起 Telegram template 發送前會把 `
` / `
` / `
` 轉為換行;保留其他 HTML 標籤,非 HTML parse mode 不改寫 |
-| 2026-05-20 | 部分舊 Telegram 入口繞過中央 sanitizer,且 RAG awaiting review 使用錯誤 `chat_id=` 參數會讓人工審核推播失敗 | V10.322 起 Bot API price decision 走 `send_telegram_with_result()`;`price_decision()` 補 `report_url` 相容並 escape 動態欄位;RAG awaiting review 改用 `chat_ids=[...]` 呼叫 `_send_telegram_raw()` |
+| 2026-05-20 | 部分舊 Telegram 入口繞過中央 sanitizer,且 RAG awaiting review 使用錯誤 `chat_id=` 參數會讓 AI 例外決策推播失敗 | V10.322 起 Bot API price decision 走 `send_telegram_with_result()`;`price_decision()` 補 `report_url` 相容並 escape 動態欄位;RAG awaiting review 改用 `chat_ids=[...]` 呼叫 `_send_telegram_raw()` |
| 2026-06-25 | UI/UX 不可只修首頁,導覽主入口必須同一套 PChome 業績提升語言 | V10.662 起作戰、分析、營運、AI 助手主入口與廠商缺貨子工具都使用短句對齊「評估、分析、建議、解法、治理」流程;首頁今日行動卡維持 980px 上限與高對比主按鈕,禁止回到全寬長文說明。 |
| 2026-06-25 | 首頁今日行動 CTA 不可被全域 Bootstrap guard 蓋成透明或低對比 | V10.663 起 `#commandTaskButton.growth-command-alert-action` 使用精準 selector 與 `background-color` hard override,正式 smoke 必須量測按鈕背景與卡片寬度。 |
| 2026-06-25 | 系統、舊入口與觀測台頁首不可用長篇工程說明取代決策用途 | V10.664 起舊入口、系統管理與 AI 觀測台頁首統一改為短句,聚焦資料新鮮度、成本、品質、RAG 與自癒如何支援 PChome 業績判斷。 |
@@ -772,7 +773,7 @@ POSTGRES_HOST=momo-db
| 2026-06-25 | Google Drive 背景匯入不得尋找本機瀏覽器 | V10.690 起 `GoogleDriveService.authenticate()` 預設拒絕背景 OAuth;即使人工明確開啟互動授權,也使用 `open_browser=False` 並輸出授權網址,不在 scheduler/app 容器內尋找 runnable browser。 |
| 2026-06-25 | 全頁價格方向統一為 PChome 成長視角 | V10.691 起 AI Intelligence、Daily Sales、Growth Analysis、Dashboard、Telegram 與 AI 報告 prompt 不再使用「PChome 價格壓力 / MOMO 價格優勢 / MOMO 更便宜 / PChome 有優勢」等易混淆詞;統一為「PChome 價格優勢」與「MOMO 低價壓力」。 |
| 2026-06-25 | 候選比較卡與價格語意必須有測試防線 | V10.692 起 `tests/test_pchome_revenue_growth_service.py` 鎖定 `/ai_intelligence` 模板必須提供 PChome/MOMO 雙賣場連結、雙開賣場操作與白話候選理由,且不得再出現 `variant_selection_review`、`focused_exact_identity`、`source_code`、`momo_reference` 或反向價格詞。 |
-| 2026-06-25 | 成長報表不得把比價內部指標整排丟給使用者 | V10.693 起 `/growth_analysis` 的比價品質區改為「PChome 價格作戰可用度」:只呈現可直接決策、同款覆蓋、價格需刷新、待補/待確認四個訊號,並依資料狀態給下一步建議與今日作戰入口;測試禁止回到「比價資料品質、高信心門檻、未知新鮮度、人工否決」這類工程化列表。 |
+| 2026-06-25 | 成長報表不得把比價內部指標整排丟給使用者 | V10.693 起 `/growth_analysis` 的比價品質區改為「PChome 價格作戰可用度」:只呈現可直接決策、同款覆蓋、價格需刷新、待補/待確認四個訊號,並依資料狀態給下一步建議與今日作戰入口;測試禁止回到「比價資料品質、高信心門檻、未知新鮮度、AI 否決」這類工程化列表。 |
| 2026-06-25 | 比價頁每筆結果也必須能雙開賣場 | V10.694 起 `/price_comparison` 的結果列在 PChome/MOMO 連結都存在時提供「雙開賣場」操作,Excel 與手動輸入提示改成白話作戰語言,不再顯示「格式說明、欄位、商品名稱,價格」這類工程化提示;`tests/test_frontend_v2_assets.py` 鎖定此行為。 |
| 2026-06-25 | 匯入任務列表只顯示處置提醒 | V10.695 起 `/auto_import` 任務列表不再把 `error_message` 原文當主要欄位顯示,而是由 `buildImportActionHint()` 轉成 Google Drive 授權、當日業績明細檔、重新匯入或通知維護人員等下一步,避免重啟後瀏覽器/授權/同步技術錯誤直接暴露給營運使用者。 |
| 2026-06-25 | 系統設定匯入提示不得顯示資料表或日誌口徑 | V10.696 起 `/system_settings` 不再用 `realtime_sales_monthly` 判斷前端提示,也不再顯示「資料落點、檢查日誌、發生系統錯誤」等內部口徑;所有匯入與備份失敗提示統一走 `toImportActionMessage()`,轉成重新授權、改用正確業績報表、重新匯入或通知維護人員。 |
@@ -810,3 +811,44 @@ POSTGRES_HOST=momo-db
| 2026-06-27 | 系統事件頁不得顯示或下載原始工程紀錄 | V10.723 起 `/logs` 改為「系統事件紀錄」,前台只顯示事件等級、營運判讀與建議處置;不得以 raw log 變數、舊下載檔名或「系統日誌/下載日誌」作為使用者可見介面。 |
| 2026-06-27 | AI 挑品必須直接顯示商品證據與賣場入口 | V10.724 起商品看板 AI 挑品清單會在 selection 階段合併最新 PChome 同款證據,卡片需顯示 MOMO 商品ID、PChome 商品ID、同款信心與兩邊賣場入口;商品圖需使用 PChome 圖片作為 fallback,不得只給一段建議理由。 |
| 2026-06-27 | Google Drive 自動匯入不得在背景排程啟動瀏覽器授權 | V10.725 起正式容器與排程服務即使誤設 `GOOGLE_DRIVE_ALLOW_INTERACTIVE_AUTH=true` 也會硬阻擋互動 OAuth;匯入前會檢查 JSON token 是否存在、可刷新且可寫回,Drive 測試與檔案列表 API 會 fail-closed 回傳可處理的授權狀態。 |
+| 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 已授權。 |
+| 2026-06-30 | PChome DB apply 授權簽署執行 lane 必須先產出 unsigned receipt boundary closeout | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-closeout`;此 endpoint 只把 signing execution preflight 收斂成 unsigned signed-authorization receipt boundary 與 closeout contract,確認 nonsecret inputs、operator-held secret boundary、production truth、post-apply verifier、migration hash、rollback/abort boundary 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不簽發 authorization、不產生 signed receipt、不執行 shell/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply 授權簽署收據 lane 必須先定義 external receipt evidence boundary | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-preflight`;此 endpoint 只把 unsigned receipt boundary 轉成 external signing receipt evidence boundary,定義 external receipt id、payload hash、receipt hash、signer key reference、signature algorithm reference、detached verification status、production truth、post-apply verifier 與 rollback/abort boundary,不讀 secret、不接受 plaintext secret、不包含 signed receipt、不包含 signature material、不簽發 authorization、不執行 shell/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply 授權簽署收據 closeout 必須先產出 detached verification boundary | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-closeout`;此 endpoint 只把 external signing receipt evidence boundary 收斂成 detached receipt verification boundary,確認 external receipt evidence contract、detached verification checks、operator-held secret boundary、production truth、post-apply verifier、migration hash 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signed receipt、不包含 signature material、不執行 detached verification、不簽發 authorization、不執行 shell/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply 授權簽署收據 evidence intake 必須先定義 detached verification evidence schema | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-evidence-intake`;此 endpoint 只把 detached verification boundary 收斂成 external signed authorization receipt evidence intake schema,定義 verifier receipt hash、source chain ids、payload/receipt hash、signer key reference、signature algorithm reference、detached verification status 與 acceptance gates,不讀 secret、不接受 plaintext secret、不包含 signed receipt body、不包含 signature material、不執行 detached verification、不簽發 authorization、不執行 shell/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply 授權 detached verification evidence validation 必須先產出 verifier receipt closeout boundary | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-detached-verification-evidence-validation`;此 endpoint 只把 detached verification evidence schema 收斂成 verifier receipt closeout boundary,定義 verifier receipt id、source chain ids、payload/receipt hash、detached verification status、verifier receipt hash、post-apply verifier 與 acceptance gates,不讀 secret、不接受 plaintext secret、不包含 signed receipt body、不包含 signature material、不執行 detached verification、不持久化 verifier receipt、不簽發 authorization、不執行 shell/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply 授權 verifier receipt closeout 必須先產出 verifier receipt evidence handoff | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-verifier-receipt-closeout`;此 endpoint 只把 verifier receipt closeout boundary 收斂成 verifier receipt evidence handoff,定義 handoff id、source chain ids、verifier receipt id reference、external receipt id reference、payload/receipt/verifier receipt hash、detached verification status、post-apply verifier 與 acceptance gates,不讀 secret、不接受 plaintext secret、不包含 signed receipt body、不包含 signature material、不執行 detached verification、不持久化 verifier receipt、不簽發 authorization、不執行 shell/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply 授權 evidence execution preflight 必須先產出 final verifier handoff | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-preflight`;此 endpoint 只把 verifier receipt evidence handoff 收斂成 future database apply authorization verifier handoff、authorization evidence execution preflight package 與 contract,確認 source chain ids、payload/receipt/verifier hash、same-run production truth、post-apply verifier 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signed receipt body、不包含 signature material、不執行 detached verification、不持久化 verifier receipt、不執行 authorization evidence、不簽發 authorization、不執行 shell/endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply 授權 evidence execution closeout 必須先產出 final verifier gate | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-closeout`;此 endpoint 只把 authorization evidence execution preflight 收斂成 future database apply authorization final verifier gate、evidence execution closeout package 與 contract,確認 source chain ids、payload/receipt/verifier hash、same-run production truth、post-apply verifier、rollback/post-apply binding 前置條件與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signed receipt body、不包含 signature material、不執行 detached verification、不持久化 verifier receipt、不執行 authorization evidence、不執行 database apply、不簽發 authorization、不執行 shell/endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled-apply final preflight 必須先綁定 rollback 與 post-apply verifier | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-apply-final-preflight`;此 endpoint 只把 final verifier gate 收斂成 controlled apply final preflight、rollback binding、post-apply verifier binding 與 contract,確認 target migration hash、same-run production truth、post-apply verifier、rollback requirement、dry-run-only/check-mode-only 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run package 必須先產出 receipt preview 與 non-executable command shape | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-package`;此 endpoint 只把 controlled apply final preflight 收斂成 controlled dry-run package、dry-run command shape、dry-run execution receipt preview 與 contract,確認 rollback binding、post-apply verifier binding、target migration hash、same-run production truth、receipt preview-only、non-executable command shape 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run receipt closeout 必須先驗證 result parser 與 receipt preview | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-closeout`;此 endpoint 只把 controlled dry-run package 收斂成 controlled dry-run receipt closeout、dry-run result parser、receipt validation report 與 contract,確認 parser schema、receipt preview fields、command-shape hash、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run runner readiness 必須先綁定 non-executable execution plan | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-readiness`;此 endpoint 只把 controlled dry-run receipt closeout 收斂成 controlled dry-run runner readiness、execution plan binding 與 contract,確認 result parser verification、receipt validation、command-shape hash、rollback/post-apply verifier binding、target migration hash、same-run production truth、runner execution gate closed 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run execution plan closeout 必須先驗證 non-executable command artifact | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-plan-closeout`;此 endpoint 只把 controlled dry-run runner readiness 收斂成 controlled dry-run execution plan closeout、non-executable command artifact 與 contract,確認 execution plan binding preview-only、command artifact hash、result parser/receipt validation carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth、runner execution gate closed 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 command text、不包含 argv、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run command artifact closeout 必須先產出 runner execution receipt preflight | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-command-artifact-closeout`;此 endpoint 只把 controlled dry-run execution plan closeout 收斂成 controlled dry-run command artifact closeout、runner execution receipt preflight 與 contract,確認 non-executable command artifact hash、no command text、no argv、receipt preflight no-execute、result parser/receipt validation carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不捕捉 stdout/stderr、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run runner execution receipt closeout 必須先產出 post-receipt parser verification | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-execution-receipt-closeout`;此 endpoint 只把 controlled dry-run command artifact closeout 收斂成 controlled dry-run runner execution receipt closeout、receipt closeout preview、post-receipt parser verification 與 contract,確認 runner execution receipt preflight no-execute、receipt closeout preview-only、parser blocks execution、result parser/receipt validation carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不捕捉 stdout/stderr、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run post-receipt parser closeout 必須先產出 no-apply enforcement verification | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-post-receipt-parser-closeout`;此 endpoint 只把 controlled dry-run runner execution receipt closeout 收斂成 post-receipt parser closeout、no-apply enforcement verification 與 contract,確認 parser preview ready、receipt closeout not executed、endpoint execution disallowed、SQL execution disallowed、database write disallowed、database apply unauthorized、result parser/receipt validation carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不捕捉 stdout/stderr、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run no-apply enforcement closeout 必須先產出 final dry-run executor guard | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-apply-enforcement-closeout`;此 endpoint 只把 post-receipt parser closeout 收斂成 no-apply enforcement closeout、final dry-run executor guard 與 contract,確認 no-apply enforcement preview ready、endpoint/SQL/DB write/apply 全部 disallowed、final executor guard 不允許 invocation、stdout/stderr capture closed、parser/receipt preview carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不捕捉 stdout/stderr、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run final executor guard closeout 必須先產出 pre-apply replay verifier | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-executor-guard-closeout`;此 endpoint 只把 no-apply enforcement closeout 收斂成 final executor guard closeout、pre-apply replay verifier 與 contract,確認 final dry-run executor guard preview ready、guard 不允許 invocation、pre-apply replay 只允許 preview、endpoint/SQL/DB write/apply 全部 disallowed、no-apply enforcement carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不捕捉 stdout/stderr、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run pre-apply replay closeout 必須先產出 apply executor readiness contract | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-pre-apply-replay-closeout`;此 endpoint 只把 final executor guard closeout 收斂成 pre-apply replay closeout、apply executor readiness contract 與 12 項 closeout checks,確認 pre-apply replay verifier preview ready、apply executor readiness contract 不允許 dry-run invocation、endpoint/SQL/DB write/apply 全部 disallowed、final guard/no-apply enforcement carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不捕捉 stdout/stderr、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 dry-run executor、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run apply executor readiness closeout 必須先產出 dry-run invocation readiness receipt | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-apply-executor-readiness-closeout`;此 endpoint 只把 pre-apply replay closeout 收斂成 apply executor readiness closeout、dry-run invocation readiness receipt 與 12 項 closeout checks,確認 apply executor readiness contract preview ready、receipt 只允許 preview、dry-run executor invocation/endpoint/SQL/DB write/apply 全部 disallowed、pre-apply replay/final guard/no-apply enforcement carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不捕捉 stdout/stderr、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 dry-run executor、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run invocation receipt closeout 必須先產出 no-write invocation package | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-invocation-receipt-closeout`;此 endpoint 只把 apply executor readiness closeout 收斂成 invocation receipt closeout、no-write invocation package 與 12 項 closeout checks,確認 dry-run invocation readiness receipt preview ready、no-write invocation package 只允許 preview、dry-run executor invocation/endpoint/SQL/DB write/apply 全部 disallowed、apply executor readiness/pre-apply replay/final guard/no-apply enforcement carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不捕捉 stdout/stderr、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 dry-run executor、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run no-write invocation package closeout 必須先產出 execution preflight guard | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-invocation-package-closeout`;此 endpoint 只把 invocation receipt closeout 收斂成 no-write invocation package closeout、execution preflight guard 與 12 項 closeout checks,確認 no-write invocation package preview ready、execution preflight guard 只允許 preview、dry-run executor invocation/endpoint/SQL/DB write/apply 全部 disallowed、invocation receipt/apply executor readiness/pre-apply replay/final guard/no-apply enforcement carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不捕捉 stdout/stderr、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 dry-run executor、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run execution preflight guard closeout 必須先產出 runner invocation boundary | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-preflight-guard-closeout`;此 endpoint 只把 no-write invocation package closeout 收斂成 execution preflight guard closeout、runner invocation boundary 與 12 項 closeout checks,確認 execution preflight guard preview ready、runner invocation boundary 只允許 preview、dry-run executor invocation/runner invocation/endpoint/SQL/DB write/apply 全部 disallowed、不捕捉 stdout/stderr、no-write package/invocation receipt/apply executor readiness carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run runner invocation boundary closeout 必須先產出 no-execution receipt handoff | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-invocation-boundary-closeout`;此 endpoint 只把 execution preflight guard closeout 收斂成 runner invocation boundary closeout、no-execution receipt handoff 與 12 項 closeout checks,確認 runner invocation boundary preview ready、no-execution receipt handoff 只允許 preview、execution receipt 不存在且不要求、dry-run executor invocation/runner invocation/endpoint/SQL/DB write/apply 全部 disallowed、不捕捉 stdout/stderr、execution preflight/no-write package/invocation receipt carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run no-execution receipt handoff closeout 必須產出 final no-runner-execution proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout`;此 endpoint 只把 runner invocation boundary closeout 收斂成 no-execution receipt handoff closeout、final no-runner-execution proof 與 12 項 closeout checks,確認 no-execution handoff preview ready、final proof 只證明 dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、execution receipt 不存在且不要求、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run final no-runner-execution proof closeout 必須產出 controlled executor quarantine proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout`;此 endpoint 只把 no-execution receipt handoff closeout 收斂成 final no-runner-execution proof closeout、controlled executor quarantine proof 與 12 項 closeout checks,確認 final proof preview ready、controlled executor quarantine proof 只證明 executor quarantine bound/enforced 且 dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、execution receipt 不存在且不要求、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run controlled executor quarantine proof closeout 必須產出 dry-run execution envelope freeze proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout`;此 endpoint 只把 final no-runner-execution proof closeout 收斂成 controlled executor quarantine proof closeout、dry-run execution envelope freeze proof 與 12 項 closeout checks,確認 controlled executor quarantine proof preview ready、execution envelope frozen 且不可 mutation、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、execution receipt 不存在且不要求、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run execution envelope freeze proof closeout 必須產出 frozen envelope verifier handoff | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout`;此 endpoint 只把 controlled executor quarantine proof closeout 收斂成 execution envelope freeze proof closeout、frozen envelope verifier handoff 與 12 項 closeout checks,確認 dry-run execution envelope freeze proof preview ready、execution envelope frozen 且不可 mutation、verifier handoff 已綁定但 verifier invocation 未允許且未執行、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-06-30 | PChome DB apply controlled dry-run frozen envelope verifier handoff closeout 必須產出 verifier invocation lock proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout`;此 endpoint 只把 execution envelope freeze proof closeout 收斂成 frozen envelope verifier handoff closeout、verifier invocation lock proof 與 12 項 closeout checks,確認 frozen envelope verifier handoff preview ready、verifier invocation 已鎖住且未允許、verifier 未 invoked、verifier receipt 不存在且不要求、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-07-01 | PChome DB apply controlled dry-run verifier invocation lock proof closeout 必須產出 verifier no-execution receipt proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout`;此 endpoint 只把 frozen envelope verifier handoff closeout 收斂成 verifier invocation lock proof closeout、verifier no-execution receipt proof 與 12 項 closeout checks,確認 verifier invocation lock proof preview ready、verifier invocation 已鎖住且未允許、verifier 未 invoked、verifier receipt 不存在且不要求、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-07-01 | PChome DB apply controlled dry-run verifier no-execution receipt proof closeout 必須產出 verifier receipt persistence guard proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout`;此 endpoint 只把 verifier invocation lock proof closeout 收斂成 verifier no-execution receipt proof closeout、verifier receipt persistence guard proof 與 12 項 closeout checks,確認 no-execution receipt proof preview ready、receipt persistence guard preview ready、verifier receipt persistence locked 且 not allowed、verifier receipt 未 persisted、verifier 未 invoked、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不持久化 verifier receipt、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-07-01 | PChome DB apply controlled dry-run verifier receipt persistence guard proof closeout 必須產出 receipt persistence storage boundary proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout`;此 endpoint 只把 verifier no-execution receipt proof closeout 收斂成 verifier receipt persistence guard proof closeout、receipt persistence storage boundary proof 與 12 項 closeout checks,確認 receipt persistence guard proof preview ready、storage boundary proof preview ready、receipt persistence storage boundary locked 且 storage write not allowed、receipt persistence storage 未 written、verifier receipt 未 persisted、verifier 未 invoked、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不持久化 verifier receipt、不寫入 receipt persistence storage、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-07-01 | PChome DB apply controlled dry-run receipt persistence storage boundary proof closeout 必須產出 storage boundary no-write ledger proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout`;此 endpoint 只把 verifier receipt persistence guard proof closeout 收斂成 receipt persistence storage boundary proof closeout、storage boundary no-write ledger proof 與 12 項 closeout checks,確認 storage boundary proof preview ready、no-write ledger proof preview ready、storage boundary locked 且 storage/ledger write not allowed、storage boundary 未 written、ledger 未 written、receipt persistence storage 未 written、verifier receipt 未 persisted、verifier 未 invoked、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不寫 ledger、不持久化 verifier receipt、不寫入 receipt persistence storage、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-07-01 | PChome DB apply controlled dry-run storage boundary no-write ledger proof closeout 必須產出 no-write ledger retention proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout`;此 endpoint 只把 receipt persistence storage boundary proof closeout 收斂成 storage boundary no-write ledger proof closeout、no-write ledger retention proof 與 12 項 closeout checks,確認 no-write ledger proof preview ready、retention proof preview ready、ledger retention locked 且 retention/ledger write not allowed、retention 未 written、ledger 未 written、receipt persistence storage 未 written、verifier receipt 未 persisted、verifier 未 invoked、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不寫 retention、不寫 ledger、不持久化 verifier receipt、不寫入 receipt persistence storage、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-07-01 | PChome DB apply controlled dry-run no-write ledger retention proof closeout 必須產出 retention boundary no-write archive proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout`;此 endpoint 只把 storage boundary no-write ledger proof closeout 收斂成 no-write ledger retention proof closeout、retention boundary no-write archive proof 與 12 項 closeout checks,確認 retention proof preview ready、archive proof preview ready、retention archive locked 且 archive/retention/ledger write not allowed、archive 未 written、retention 未 written、ledger 未 written、receipt persistence storage 未 written、verifier receipt 未 persisted、verifier 未 invoked、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不寫 archive、不寫 retention、不寫 ledger、不持久化 verifier receipt、不寫入 receipt persistence storage、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-07-01 | PChome DB apply controlled dry-run retention boundary no-write archive proof closeout 必須產出 archive retention sealed handoff proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout`;此 endpoint 只把 no-write ledger retention proof closeout 收斂成 retention boundary no-write archive proof closeout、archive retention sealed handoff proof 與 12 項 closeout checks,確認 archive proof preview ready、sealed handoff proof preview ready、sealed handoff manifest hash 存在、sealed handoff locked 且 handoff/archive/retention/ledger write not allowed、handoff 未 written、archive 未 written、retention 未 written、ledger 未 written、receipt persistence storage 未 written、verifier receipt 未 persisted、verifier 未 invoked、dry-run executor 未 invoked、runner invocation 未 performed、endpoint 未 executed、SQL 未 executed、database 未 written、stdout/stderr 不包含、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash、same-run production truth 與 no-side-effect checks,不讀 secret、不接受 plaintext secret、不包含 signature material、不寫 handoff、不寫 archive、不寫 retention、不寫 ledger、不持久化 verifier receipt、不寫入 receipt persistence storage、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。 |
+| 2026-07-01 | PChome retention boundary archive closeout 正式 readback 預設必須 compact | V10.725 正式環境已把 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout` 預設回應改為 `response_mode=compact`,保留 summary、future_readiness、sealed_handoff_proof、contract、12 checks、safety 與 next_actions;完整 nested proof payload 僅在 `full=1` 時輸出,避免正式 readback 產生數百 MB body。2026-07-01 production readback:`HTTP 200`、`content-length=8697`、`checks=12`、`writes_database_count=0`、`executes_sql_count=0`、`sealed_handoff_manifest_hash` 長度 64、`database_apply_authorized=false`、`writes_database=false`、`/health` 仍為 healthy PostgreSQL `V10.725`。 |
+| 2026-07-01 | PChome DB apply controlled dry-run archive retention sealed handoff proof closeout 必須產出 sealed handoff verifier transfer proof | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-archive-retention-sealed-handoff-proof-closeout`;此 endpoint 預設 `response_mode=compact`,只把 archive retention sealed handoff proof 收斂成 archive retention sealed handoff proof closeout、sealed handoff verifier transfer proof 與 12 項 closeout checks,確認上一段 retention boundary no-write archive proof closeout ready、sealed handoff proof preview ready、sealed handoff manifest hash locked、verifier transfer proof bound、verifier transfer write locked、verifier invocation not allowed、verifier receipt not persisted、endpoint/SQL/DB 未執行、previous closeouts carry-forward、rollback/post-apply verifier binding、target migration hash 與 no-side-effect checks;完整 nested proof payload 僅 `full=1`,不讀 secret、不接受 plaintext secret、不包含 signature material、不寫 verifier transfer、不寫 handoff、不持久化 verifier receipt、不執行 authorization evidence、不執行 database apply、不執行 verifier、不執行 dry-run executor、不執行 runner、不執行 endpoint/SQL、不寫 DB,也不代表正式 DB apply 已授權。Local focused tests:archive retention sealed handoff proof closeout `2 passed`;retention boundary + archive retention chain `5 passed`;完整 PChome mapping backlog test file `218 passed`。2026-07-01 production readback:`HTTP 200`、`content-length=8425`、`response_mode=compact`、`checks=12`、`writes_database_count=0`、`executes_sql_count=0`、`verifier_invoked_count=0`、`verifier_transfer_manifest_hash` 長度 64、`database_apply_authorized=false`、`writes_database=false`、`/health` 仍為 healthy PostgreSQL `V10.725`。 |
diff --git a/routes/README.md b/routes/README.md
index 53fd52f..5e15a54 100644
--- a/routes/README.md
+++ b/routes/README.md
@@ -19,13 +19,14 @@
| `edm_routes.py` | EDM 與節慶儀表板 | `/edm`, `/festival` |
| `monthly_routes.py` | 月結分析 | `/monthly_summary_analysis`, `/api/monthly_summary_data` |
| `daily_sales_routes.py` | 當日業績 | `/daily_sales`, `/daily_sales/export*` |
-| `market_intel_routes.py` | 市場情報 Phase 104 主路由與基礎 preview API | `/market_intel`, `/market_intel/*`, `/api/market_intel/status`, `/api/market_intel/schema`, `/api/market_intel/schema_smoke`, `/api/market_intel/schema_db_probe`, `/api/market_intel/platform_seed_db_diff`, `/api/market_intel/legacy_source_bridge`, `/api/market_intel/mcp_readiness`, `/api/market_intel/mcp_tool_contract`, `/api/market_intel/mcp_deploy_preflight`, `/api/market_intel/mcp_activation_runbook`, `/api/market_intel/mcp_fetch_gate`, `/api/market_intel/scheduler_plan`, `/api/market_intel/manual_sample_plan`, `/api/market_intel/manual_sample_acceptance`, `/api/market_intel/manual_sample_review`, `/api/market_intel/manual_sample_review/evaluate`, `/api/market_intel/manual_sample_review/candidate_handoff`, `/api/market_intel/manual_sample_review/candidate_queue_draft`, `/api/market_intel/manual_sample_review/candidate_queue_approval`, `/api/market_intel/manual_sample_review/candidate_queue_transaction`, `/api/market_intel/manual_sample_review/candidate_queue_writer_status`, `/api/market_intel/manual_sample_review/candidate_queue_writer_preflight`, `/api/market_intel/manual_sample_review/candidate_queue_writer_postwrite_smoke`, `/api/market_intel/manual_sample_review/candidate_queue_writer_operator_drill`, `/api/market_intel/manual_sample_review/candidate_queue_writer_run_package`, `/api/market_intel/manual_sample_review/candidate_queue_writer_run_readiness`, `/api/market_intel/manual_sample_review/candidate_queue_writer_run_receipt`, `/api/market_intel/manual_sample_review/candidate_queue_writer_run_closeout`, `/api/market_intel/manual_sample_review/candidate_queue_review_handoff`, `/api/market_intel/match_review_plan`, `/api/market_intel/opportunity_plan`, `/api/market_intel/opportunity_scoring_plan`, `/api/market_intel/opportunity_evidence_plan`, `/api/market_intel/opportunity_alert_plan`, `/api/market_intel/adapters`, `/api/market_intel/dry_run_plan`, `/api/market_intel/discovery_plan`, `/api/market_intel/manual_discovery`, `/api/market_intel/candidate_preview`, `/api/market_intel/platform_seed_plan`, `/api/market_intel/platform_seed_write_guard`, `/api/market_intel/platform_seed_writer_plan`, `/api/market_intel/migration_blueprint`, `/api/market_intel/migration_apply_drill`, `/api/market_intel/migration_catalog_review`, `/api/market_intel/migration_live_smoke`, `/api/market_intel/live_db_inventory`, `/api/market_intel/seed_writer_cli_status`, `/api/market_intel/write_approval_runbook`, `/api/market_intel/deployment_readiness` |
-| `market_intel_review_routes.py` | 市場情報人工 queue review 只讀延伸 API | `/api/market_intel/manual_sample_review/candidate_queue_review_inventory`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_approval`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_transaction`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_status`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_preflight`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_postwrite_smoke`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_operator_drill`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_package`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_readiness`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_receipt`, `/api/market_intel/manual_sample_review/candidate_queue_review_decision_writer_run_closeout` |
-| `market_intel_review_post_routes.py` | 市場情報 review_state closeout 後只讀延伸 API(掛在 `market_intel_review_bp`) | `/api/market_intel/manual_sample_review/candidate_queue_review_decision_post_closeout_inventory`, `/api/market_intel/manual_sample_review/candidate_queue_review_completion_archive`, `/api/market_intel/manual_sample_review/candidate_queue_review_archive_summary`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_preflight`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_run_package`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_output_receipt`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_preflight`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_transaction`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_writer_preflight`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_run_package` |
-| `market_intel_review_post_ai_routes.py` | 市場情報 AI summary persistence / Telegram dispatch 後續只讀延伸 API(掛在 `market_intel_review_bp`) | `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_run_readiness`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_run_receipt`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_run_closeout`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_gate`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_run_package`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_run_readiness`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_run_receipt`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_closeout`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_archive`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_archive_summary` |
-| `market_intel_review_report_routes.py` | 市場情報 report input / report run package / report run readiness / report run receipt / report closeout / report archive / report catalog handoff 後續只讀延伸 API(掛在 `market_intel_review_bp`) | `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_input`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_run_package`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_run_readiness`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_run_receipt`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_closeout`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_archive`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_archive_summary`, `/api/market_intel/manual_sample_review/candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_handoff` |
+| `market_intel_routes.py` | 市場情報 Phase 104 主路由、AI-controlled sample 相容路由與基礎 preview API | `/market_intel`, `/market_intel/*`, `/api/market_intel/status`, `/api/market_intel/schema`, `/api/market_intel/schema_smoke`, `/api/market_intel/schema_db_probe`, `/api/market_intel/platform_seed_db_diff`, `/api/market_intel/legacy_source_bridge`, `/api/market_intel/mcp_readiness`, `/api/market_intel/mcp_tool_contract`, `/api/market_intel/mcp_deploy_preflight`, `/api/market_intel/mcp_activation_runbook`, `/api/market_intel/mcp_fetch_gate`, `/api/market_intel/scheduler_plan`, AI-controlled sample compatibility family, candidate queue writer family, `/api/market_intel/match_review_plan`, `/api/market_intel/opportunity_plan`, `/api/market_intel/opportunity_scoring_plan`, `/api/market_intel/opportunity_evidence_plan`, `/api/market_intel/opportunity_alert_plan`, `/api/market_intel/adapters`, `/api/market_intel/dry_run_plan`, `/api/market_intel/discovery_plan`, `/api/market_intel/candidate_preview`, `/api/market_intel/platform_seed_plan`, `/api/market_intel/platform_seed_write_guard`, `/api/market_intel/platform_seed_writer_plan`, `/api/market_intel/migration_blueprint`, `/api/market_intel/migration_apply_drill`, `/api/market_intel/migration_catalog_review`, `/api/market_intel/migration_live_smoke`, `/api/market_intel/live_db_inventory`, `/api/market_intel/seed_writer_cli_status`, `/api/market_intel/write_approval_runbook`, `/api/market_intel/deployment_readiness` |
+| `market_intel_review_routes.py` | 市場情報 AI 例外 queue review 只讀延伸 API | AI-controlled sample compatibility queue review family |
+| `market_intel_review_post_routes.py` | 市場情報 review_state closeout 後只讀延伸 API(掛在 `market_intel_review_bp`) | AI-controlled sample compatibility post-closeout / archive / AI summary persistence family |
+| `market_intel_review_post_ai_routes.py` | 市場情報 AI summary persistence / Telegram dispatch 後續只讀延伸 API(掛在 `market_intel_review_bp`) | AI summary persistence run / Telegram dispatch compatibility family |
+| `market_intel_review_report_routes.py` | 市場情報 report input / report run package / report run readiness / report run receipt / report closeout / report archive / report catalog handoff 後續只讀延伸 API(掛在 `market_intel_review_bp`) | AI summary persistence Telegram report compatibility family |
| `api_routes.py` | 通用任務與查詢 API | `/api/run_task`, `/api/history/*` |
-| `ai_routes.py` | AI 推薦、競情儀表板與 PChome 成長作戰 API | `/ai_recommend`, `/ai_intelligence`, `/api/ai/status`, `/api/ai/icaim/dashboard`, `/api/ai/pchome-growth/opportunities`, `/api/ai/pchome-growth/backfill-momo-candidates`, `/api/ai/pchome-growth/source-contract`, `/api/ai/pchome-growth/external-offers/csv-dry-run` |
+| `ai_routes.py` | AI 推薦、競情儀表板與 PChome 成長作戰 API | `/ai_recommend`, `/ai_intelligence`, `/api/ai/status`, `/api/ai/icaim/dashboard`, `/api/ai/pchome-growth/opportunities`, `/api/ai/pchome-growth/mapping-backlog`, `/api/ai/pchome-growth/mapping-backlog/operator-preview`, `/api/ai/pchome-growth/mapping-backlog/direct-mapping-auto-search-package`, `/api/ai/pchome-growth/mapping-backlog/direct-mapping-candidate-decision-package`, `/api/ai/pchome-growth/ai-automation-readiness`, `/api/ai/pchome-growth/mapping-backlog/evidence-enrichment-preview`, `/api/ai/pchome-growth/mapping-backlog/evidence-source-preview`, `/api/ai/pchome-growth/mapping-backlog/evidence-fetch-gate`, `/api/ai/pchome-growth/mapping-backlog/evidence-merge-preview`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-receipt-gate`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-persistence-gate`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-schema-migration-preview`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-preview`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-apply-readiness-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-generation-request`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-apply-gate-preview`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-request-gate-preview`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-execution-preflight`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-package`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-final-handoff-package`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-preview`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-intake`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-closeout`, `/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`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-preflight`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-evidence-intake`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-detached-verification-evidence-validation`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-verifier-receipt-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-preflight`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-apply-final-preflight`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-package`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-readiness`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-plan-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-command-artifact-closeout`, `/api/ai/pchome-growth/backfill-momo-candidates`, `/api/ai/pchome-growth/source-contract`, `/api/ai/pchome-growth/external-offers/csv-dry-run` |
+| `ai_routes.py` | PChome DB apply controlled dry-run 補充索引 | `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-execution-receipt-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-post-receipt-parser-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-apply-enforcement-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-executor-guard-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-pre-apply-replay-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-apply-executor-readiness-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-invocation-receipt-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-invocation-package-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-preflight-guard-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-invocation-boundary-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout`, `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-archive-retention-sealed-handoff-proof-closeout` |
| `export_routes.py` | 匯出功能 | `/api/export/*` |
| `import_routes.py` | 匯入功能 | `/api/import_excel`, `/api/import/monthly_summary` |
diff --git a/routes/ai_routes.py b/routes/ai_routes.py
index 77f4c66..b311b18 100644
--- a/routes/ai_routes.py
+++ b/routes/ai_routes.py
@@ -1537,6 +1537,31 @@ def ai_intelligence():
return render_template('ai_intelligence.html', active_page='ai_intelligence')
+@ai_bp.route('/api/ai/automation-debt')
+@login_required
+def api_ai_automation_debt():
+ """Read-only AI automation debt inventory for controlled-apply planning."""
+ try:
+ from services.ai_automation_debt_service import build_ai_automation_debt_report
+
+ max_findings = request.args.get('max_findings', 120, type=int)
+ per_file_limit = request.args.get('per_file_limit', 8, type=int)
+ report = build_ai_automation_debt_report(
+ max_findings=max_findings,
+ per_file_limit=per_file_limit,
+ )
+ report["source_endpoint"] = "/api/ai/automation-debt"
+ return jsonify(report)
+ except Exception as exc:
+ logger.error("[AIAutomationDebt] scan failed: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "policy": "read_only_ai_automation_debt_scan",
+ "result": "SCAN_FAILED",
+ "error": "AI automation debt scan is temporarily unavailable.",
+ }), 500
+
+
def _clone_icaim_dashboard_payload(payload):
cloned = dict(payload or {})
cloned['stats'] = dict(cloned.get('stats') or {})
@@ -1662,6 +1687,3748 @@ def api_pchome_growth_opportunities():
}), 500
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog')
+@login_required
+def api_pchome_growth_mapping_backlog():
+ """PChome growth mapping backlog,只讀、不呼叫 LLM、不寫 DB。"""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import summarize_pchome_mapping_backlog
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ summary = summarize_pchome_mapping_backlog(payload)
+ summary["source_endpoint"] = "/api/ai/pchome-growth/opportunities"
+ return jsonify(summary)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] mapping backlog 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 商品對應缺口暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/operator-preview')
+@login_required
+def api_pchome_growth_mapping_operator_preview():
+ """Read-only operator package for the PChome growth mapping backlog."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_mapping_operator_preview
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_mapping_operator_preview(payload, batch_size=batch_size)
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] mapping operator preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 商品對應操作預覽暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/direct-mapping-auto-search-package')
+@login_required
+def api_pchome_growth_direct_mapping_auto_search_package():
+ """P1 no-write direct mapping package for automated MOMO candidate search."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_direct_mapping_auto_search_package,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_search = str(request.args.get('execute_search') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 5, type=int)
+ limit_per_product = request.args.get('limit_per_product', 8, type=int)
+ max_terms_per_product = request.args.get('max_terms_per_product', 5, type=int)
+ min_score = request.args.get('min_score', 0.45, type=float)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ package = build_pchome_direct_mapping_auto_search_package(
+ payload,
+ batch_size=batch_size,
+ execute_search=execute_search,
+ limit_per_product=limit_per_product,
+ max_terms_per_product=max_terms_per_product,
+ min_score=min_score,
+ )
+ package["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/operator-preview"
+ return jsonify(package)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] direct mapping auto search package 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 商品對應自動搜尋包暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/direct-mapping-candidate-decision-package')
+@login_required
+def api_pchome_growth_direct_mapping_candidate_decision_package():
+ """P2 no-write machine-verifiable decision package for direct mapping candidates."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_direct_mapping_candidate_decision_package,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_search = str(request.args.get('execute_search') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 5, type=int)
+ limit_per_product = request.args.get('limit_per_product', 8, type=int)
+ max_terms_per_product = request.args.get('max_terms_per_product', 5, type=int)
+ min_score = request.args.get('min_score', 0.45, type=float)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ package = build_pchome_direct_mapping_candidate_decision_package(
+ payload,
+ batch_size=batch_size,
+ execute_search=execute_search,
+ limit_per_product=limit_per_product,
+ max_terms_per_product=max_terms_per_product,
+ min_score=min_score,
+ )
+ package["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/direct-mapping-auto-search-package"
+ return jsonify(package)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] direct mapping candidate decision package 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 商品對應候選決策包暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/ai-automation-readiness')
+@login_required
+def api_pchome_growth_ai_automation_readiness():
+ """Read-only product-facing AI automation readiness for the PChome growth loop."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_growth_ai_automation_readiness
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_search = str(request.args.get('execute_search') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 8, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ readiness = build_pchome_growth_ai_automation_readiness(
+ payload,
+ batch_size=batch_size,
+ execute_search=execute_search,
+ execute_fetch=execute_fetch,
+ )
+ readiness["source_endpoint"] = "/api/ai/pchome-growth/opportunities"
+ return jsonify(readiness)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] AI automation readiness 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome AI 自動化狀態暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/evidence-enrichment-preview')
+@login_required
+def api_pchome_growth_evidence_enrichment_preview():
+ """Read-only evidence enrichment plan for PChome growth mapping targets."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_evidence_enrichment_preview
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_evidence_enrichment_preview(payload, batch_size=batch_size)
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/operator-preview"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] evidence enrichment preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 商品證據補齊預覽暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/evidence-source-preview')
+@login_required
+def api_pchome_growth_evidence_source_preview():
+ """Read-only source wiring preview for PChome growth mapping evidence."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_evidence_source_preview
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_evidence_source_preview(payload, batch_size=batch_size)
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/evidence-enrichment-preview"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] evidence source preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 商品證據來源預覽暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/evidence-fetch-gate')
+@login_required
+def api_pchome_growth_evidence_fetch_gate():
+ """Controlled read-only GET gate for PChome product-page evidence."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_evidence_fetch_gate
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 3, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_evidence_fetch_gate(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/evidence-source-preview"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] evidence fetch gate 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 商品證據只讀抓取守門暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/evidence-merge-preview')
+@login_required
+def api_pchome_growth_evidence_merge_preview():
+ """Read-only merge preview for parsed PChome product-page evidence."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_evidence_merge_preview
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 3, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_evidence_merge_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/evidence-fetch-gate"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] evidence merge preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 商品證據合併預覽暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-receipt-gate')
+@login_required
+def api_pchome_growth_auto_policy_receipt_gate():
+ """No-write auto-policy receipt gate for PChome evidence automation."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_auto_policy_receipt_gate
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_auto_policy_receipt_gate(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/evidence-merge-preview"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy receipt gate 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化收據守門暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-persistence-gate')
+@login_required
+def api_pchome_growth_auto_policy_persistence_gate():
+ """No-write persistence contract gate for PChome auto-policy receipts."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_auto_policy_persistence_gate
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_auto_policy_persistence_gate(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-receipt-gate"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy persistence gate 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 persistence 守門暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-schema-migration-preview')
+@login_required
+def api_pchome_growth_auto_policy_schema_migration_preview():
+ """No-write schema migration preview for PChome auto-policy persistence."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_auto_policy_schema_migration_preview
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_auto_policy_schema_migration_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-persistence-gate"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy schema migration preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 schema migration preview 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-preview')
+@login_required
+def api_pchome_growth_auto_policy_migration_file_preview():
+ """No-write migration file preview for PChome auto-policy persistence."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_auto_policy_migration_file_preview
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_auto_policy_migration_file_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-schema-migration-preview"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy migration file preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 migration file preview 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-apply-readiness-closeout')
+@login_required
+def api_pchome_growth_auto_policy_apply_readiness_closeout():
+ """No-write readiness closeout before PChome migration file generation or apply."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_auto_policy_apply_readiness_closeout
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = build_pchome_auto_policy_apply_readiness_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ closeout["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-preview"
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy apply readiness closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 apply readiness closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-generation-request')
+@login_required
+def api_pchome_growth_auto_policy_migration_file_generation_request():
+ """No-write request package for generating the PChome auto-policy migration file."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_migration_file_generation_request,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ request_package = build_pchome_auto_policy_migration_file_generation_request(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ request_package["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-apply-readiness-closeout"
+ return jsonify(request_package)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy migration file generation request 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 migration file generation request 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-apply-gate-preview')
+@login_required
+def api_pchome_growth_auto_policy_migration_apply_gate_preview():
+ """No-write apply gate preview for the generated PChome auto-policy migration file."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_auto_policy_migration_apply_gate_preview
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_auto_policy_migration_apply_gate_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-generation-request"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy migration apply gate preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 migration apply gate preview 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-request-gate-preview')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_request_gate_preview():
+ """No-write explicit DB apply request gate preview for PChome auto-policy migration."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import build_pchome_auto_policy_db_apply_request_gate_preview
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_auto_policy_db_apply_request_gate_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-apply-gate-preview"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply request gate preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply request gate preview 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-execution-preflight')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_execution_preflight():
+ """No-write execution preflight before any explicit PChome DB apply."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_execution_preflight,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preflight = build_pchome_auto_policy_db_apply_execution_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preflight["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-request-gate-preview"
+ return jsonify(preflight)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply execution preflight 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply execution preflight 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-package')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_package():
+ """No-write authorization package before a future explicit PChome DB apply."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_package,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ package = build_pchome_auto_policy_db_apply_authorization_package(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ package["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-execution-preflight"
+ return jsonify(package)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization package 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization package 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_verifier_artifact_preview():
+ """No-write verifier artifact schema preview before a future PChome DB apply."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_verifier_artifact_preview,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_auto_policy_db_apply_verifier_artifact_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-package"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply verifier artifact preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply verifier artifact preview 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-final-handoff-package')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_final_handoff_package():
+ """No-write final handoff package before a future explicit PChome DB apply."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_final_handoff_package,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ package = build_pchome_auto_policy_db_apply_final_handoff_package(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ package["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview"
+ return jsonify(package)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply final handoff package 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply final handoff package 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-preview')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_preview():
+ """No-write controlled shell dry-run preview before a future PChome DB apply."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_shell_preview,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preview = build_pchome_auto_policy_db_apply_controlled_dry_run_shell_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preview["source_endpoint"] = "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-final-handoff-package"
+ return jsonify(preview)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run shell preview 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run shell preview 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_closeout():
+ """No-write closeout for controlled shell dry-run before a future PChome DB apply."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_shell_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = build_pchome_auto_policy_db_apply_controlled_dry_run_shell_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-preview"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run shell closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run shell closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-intake')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_request_intake():
+ """No-write intake envelope for a future explicit PChome DB apply authorization request."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_request_intake,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ intake = build_pchome_auto_policy_db_apply_authorization_request_intake(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ intake["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-closeout"
+ )
+ return jsonify(intake)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization request intake 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization request intake 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_request_closeout():
+ """No-write closeout for a final exact PChome DB apply authorization request package."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_request_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_request_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-intake"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization request closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization request closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-lane-guard')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_lane_guard():
+ """No-write guard for entering a future PChome DB apply authorization lane."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_lane_guard,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ guard = build_pchome_auto_policy_db_apply_authorization_lane_guard(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ guard["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-closeout"
+ )
+ return jsonify(guard)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization lane guard 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization lane guard 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-preflight')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_decision_preflight():
+ """No-write preflight for a future PChome DB apply authorization decision."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_decision_preflight,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preflight = build_pchome_auto_policy_db_apply_authorization_decision_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preflight["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-lane-guard"
+ )
+ return jsonify(preflight)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization decision preflight 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization decision preflight 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_decision_closeout():
+ """No-write closeout for a future PChome DB apply authorization decision."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_decision_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_decision_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-preflight"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization decision closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization decision closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-issuer-gate')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_issuer_gate():
+ """No-secret issuer gate for a future PChome DB apply authorization lane."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_issuer_gate,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ gate = build_pchome_auto_policy_db_apply_authorization_issuer_gate(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ gate["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-closeout"
+ )
+ return jsonify(gate)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization issuer gate 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization issuer gate 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-preflight')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_signing_decision_preflight():
+ """No-secret preflight for a future PChome DB apply authorization signing decision."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_signing_decision_preflight,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preflight = build_pchome_auto_policy_db_apply_authorization_signing_decision_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preflight["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-issuer-gate"
+ )
+ return jsonify(preflight)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization signing decision preflight 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization signing decision preflight 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_signing_decision_closeout():
+ """No-signing closeout for a future PChome DB apply authorization signing decision."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_signing_decision_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_decision_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-preflight"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization signing decision closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization signing decision closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-guard')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_guard():
+ """No-signing guard for a future PChome DB apply authorization signing issuer lane."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_signing_issuer_guard,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ guard = build_pchome_auto_policy_db_apply_authorization_signing_issuer_guard(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ guard["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-closeout"
+ )
+ return jsonify(guard)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization signing issuer guard 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization signing issuer guard 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_closeout():
+ """No-signing closeout for a future PChome DB apply authorization signing issuer lane."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_signing_issuer_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_issuer_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-guard"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization signing issuer closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization signing issuer closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_signing_execution_preflight():
+ """No-secret-read preflight for a future explicit authorization signing execution lane."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_signing_execution_preflight,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preflight = build_pchome_auto_policy_db_apply_authorization_signing_execution_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preflight["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout"
+ )
+ return jsonify(preflight)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization signing execution preflight 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization signing execution preflight 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_signing_execution_closeout():
+ """No-signing closeout for a future explicit authorization signing execution lane."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_signing_execution_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_execution_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization signing execution closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization signing execution closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-preflight')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_preflight():
+ """No-signing preflight for a future externally signed authorization receipt lane."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_signed_receipt_preflight,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preflight = build_pchome_auto_policy_db_apply_authorization_signed_receipt_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ preflight["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-closeout"
+ )
+ return jsonify(preflight)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization signed receipt preflight 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization signed receipt preflight 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_closeout():
+ """No-signing closeout for a future detached receipt verification boundary."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_signed_receipt_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_signed_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-preflight"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization signed receipt closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization signed receipt closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-evidence-intake')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_evidence_intake():
+ """No-secret evidence intake schema for future detached receipt verification."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_signed_receipt_evidence_intake,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ intake = (
+ build_pchome_auto_policy_db_apply_authorization_signed_receipt_evidence_intake(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ intake["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-closeout"
+ )
+ return jsonify(intake)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization signed receipt evidence intake 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization signed receipt evidence intake 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-detached-verification-evidence-validation')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_detached_verification_evidence_validation():
+ """No-secret validation boundary for future verifier receipt closeout."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_detached_verification_evidence_validation,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ validation = build_pchome_auto_policy_db_apply_authorization_detached_verification_evidence_validation(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ validation["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-evidence-intake"
+ )
+ return jsonify(validation)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization detached verification evidence validation 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization detached verification evidence validation 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-verifier-receipt-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_verifier_receipt_closeout():
+ """No-write verifier receipt evidence handoff for future authorization verifier."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_verifier_receipt_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_verifier_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-detached-verification-evidence-validation"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization verifier receipt closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization verifier receipt closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-preflight')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_preflight():
+ """No-execute authorization evidence preflight for future verifier handoff."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_evidence_execution_preflight,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preflight = (
+ build_pchome_auto_policy_db_apply_authorization_evidence_execution_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ preflight["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-verifier-receipt-closeout"
+ )
+ return jsonify(preflight)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization evidence execution preflight 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization evidence execution preflight 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_closeout():
+ """No-execute evidence closeout for future final verifier gate."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_authorization_evidence_execution_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_authorization_evidence_execution_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-preflight"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply authorization evidence execution closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply authorization evidence execution closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-apply-final-preflight')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_apply_final_preflight():
+ """No-execute final preflight for rollback and verifier binding."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_apply_final_preflight,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ preflight = (
+ build_pchome_auto_policy_db_apply_controlled_apply_final_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ preflight["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-closeout"
+ )
+ return jsonify(preflight)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled apply final preflight 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled apply final preflight 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-package')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_package():
+ """No-execute controlled dry-run package and receipt preview."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_package,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ package = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_package(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ package["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-apply-final-preflight"
+ )
+ return jsonify(package)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run package 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run package 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_closeout():
+ """No-execute closeout for the dry-run receipt parser and preview."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-package"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run receipt closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run receipt closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-readiness')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_readiness():
+ """No-execute runner readiness and execution plan binding."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_readiness,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ readiness = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_readiness(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ readiness["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-closeout"
+ )
+ return jsonify(readiness)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run runner readiness 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run runner readiness 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-plan-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout():
+ """No-execute execution plan closeout and command artifact verification."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-readiness"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run execution plan closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run execution plan closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-command-artifact-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout():
+ """No-execute command artifact closeout and receipt preflight."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-plan-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run command artifact closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run command artifact closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-execution-receipt-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout():
+ """No-execute runner execution receipt closeout and parser verification."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-command-artifact-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run runner execution receipt closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run runner execution receipt closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-post-receipt-parser-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout():
+ """No-apply parser closeout and enforcement verification."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-execution-receipt-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run post-receipt parser closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run post-receipt parser closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-apply-enforcement-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout():
+ """No-apply enforcement closeout and final dry-run executor guard."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-post-receipt-parser-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run no-apply enforcement closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run no-apply enforcement closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-executor-guard-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout():
+ """Final executor guard closeout and pre-apply replay verification."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-apply-enforcement-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run final executor guard closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run final executor guard closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-pre-apply-replay-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout():
+ """Pre-apply replay closeout and apply executor readiness contract."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-executor-guard-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run pre-apply replay closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run pre-apply replay closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-apply-executor-readiness-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout():
+ """Apply executor readiness closeout and dry-run invocation readiness receipt."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-pre-apply-replay-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run apply executor readiness closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run apply executor readiness closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-invocation-receipt-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout():
+ """Invocation receipt closeout and no-write invocation package."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-apply-executor-readiness-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run invocation receipt closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run invocation receipt closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-invocation-package-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout():
+ """No-write invocation package closeout and execution preflight guard."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-invocation-receipt-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run no-write invocation package closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run no-write invocation package closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-preflight-guard-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout():
+ """Execution preflight guard closeout and runner invocation boundary."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-invocation-package-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run execution preflight guard closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run execution preflight guard closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-invocation-boundary-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout():
+ """Runner invocation boundary closeout and no-execution receipt handoff."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-preflight-guard-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run runner invocation boundary closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run runner invocation boundary closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout():
+ """No-execution receipt handoff closeout and final no-runner proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-invocation-boundary-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run no-execution receipt handoff closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run no-execution receipt handoff closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout():
+ """Final no-runner proof closeout and controlled executor quarantine proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run final no-runner execution proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run final no-runner execution proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout():
+ """Controlled executor quarantine closeout and dry-run envelope freeze proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run controlled executor quarantine proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run controlled executor quarantine proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout():
+ """Execution envelope freeze proof closeout and verifier handoff."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run execution envelope freeze proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run execution envelope freeze proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout():
+ """Frozen envelope verifier handoff closeout and invocation lock proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run frozen envelope verifier handoff closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run frozen envelope verifier handoff closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout():
+ """Verifier invocation lock proof closeout and no-execution receipt proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run verifier invocation lock proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run verifier invocation lock proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout():
+ """Verifier no-execution receipt proof closeout and persistence guard proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run verifier no-execution receipt proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run verifier no-execution receipt proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout():
+ """Verifier receipt persistence guard proof closeout and storage boundary proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run verifier receipt persistence guard proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run verifier receipt persistence guard proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout():
+ """Receipt persistence storage boundary proof closeout and no-write ledger proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run receipt persistence storage boundary proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run receipt persistence storage boundary proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout():
+ """Storage boundary no-write ledger proof closeout and retention proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run storage boundary no-write ledger proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run storage boundary no-write ledger proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout():
+ """No-write ledger retention proof closeout and retention archive proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout"
+ )
+ return jsonify(closeout)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run no-write ledger retention proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run no-write ledger retention proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout():
+ """Retention boundary no-write archive proof closeout and sealed handoff proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout"
+ )
+ full_payload = str(request.args.get('full') or '').strip().lower() in {'1', 'true', 'yes'}
+ if full_payload:
+ return jsonify(closeout)
+
+ summary = closeout.get("summary") or {}
+ future = (
+ closeout.get(
+ "future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ )
+ or {}
+ )
+ archive_closeout = (
+ closeout.get(
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ )
+ or {}
+ )
+ handoff = archive_closeout.get("archive_retention_sealed_handoff_proof") or {}
+ contract = (
+ closeout.get(
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract"
+ )
+ or {}
+ )
+ compact_summary_keys = [
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_ready_count",
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check_count",
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_pass_count",
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_waiting_count",
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_count",
+ "archive_retention_sealed_handoff_proof_count",
+ "archive_retention_sealed_handoff_proof_field_count",
+ "sealed_handoff_write_locked_count",
+ "sealed_handoff_write_allowed_count",
+ "sealed_handoff_written_count",
+ "retention_archive_write_allowed_count",
+ "retention_archive_written_count",
+ "ledger_retention_write_allowed_count",
+ "ledger_retention_written_count",
+ "ledger_write_allowed_count",
+ "ledger_written_count",
+ "receipt_persistence_storage_write_allowed_count",
+ "receipt_persistence_storage_written_count",
+ "persists_verifier_receipt_count",
+ "executes_endpoint_count",
+ "executes_sql_count",
+ "writes_database_count",
+ "signs_database_apply_authorization_count",
+ ]
+ compact = {
+ "policy": closeout.get("policy"),
+ "result": closeout.get("result"),
+ "success": closeout.get("success"),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("source_policy"),
+ "source_endpoint": closeout.get("source_endpoint"),
+ "response_mode": "compact",
+ "full_payload_hint": "append full=1 for the complete nested proof payload",
+ "summary": {
+ key: summary.get(key)
+ for key in compact_summary_keys
+ if key in summary
+ },
+ "future_readiness": {
+ "ready_for_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof": future.get(
+ "ready_for_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout": future.get(
+ "can_enter_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+ ),
+ "ready_for_database_apply_now": future.get(
+ "ready_for_database_apply_now"
+ ),
+ "database_apply_authorized": future.get(
+ "database_apply_authorized"
+ ),
+ },
+ "sealed_handoff_proof": {
+ "archive_retention_sealed_handoff_proof_id": handoff.get(
+ "archive_retention_sealed_handoff_proof_id"
+ ),
+ "handoff_status": handoff.get("handoff_status"),
+ "handoff_mode": handoff.get("handoff_mode"),
+ "archive_retention_sealed_handoff_proof_field_count": handoff.get(
+ "archive_retention_sealed_handoff_proof_field_count"
+ ),
+ "sealed_handoff_manifest_hash": handoff.get(
+ "sealed_handoff_manifest_hash"
+ ),
+ "sealed_handoff_write_locked": handoff.get(
+ "sealed_handoff_write_locked"
+ ),
+ "sealed_handoff_write_allowed": handoff.get(
+ "sealed_handoff_write_allowed"
+ ),
+ "sealed_handoff_written": handoff.get("sealed_handoff_written"),
+ "retention_archive_write_allowed": handoff.get(
+ "retention_archive_write_allowed"
+ ),
+ "retention_archive_written": handoff.get(
+ "retention_archive_written"
+ ),
+ "ledger_retention_write_allowed": handoff.get(
+ "ledger_retention_write_allowed"
+ ),
+ "ledger_retention_written": handoff.get(
+ "ledger_retention_written"
+ ),
+ "ledger_write_allowed": handoff.get("ledger_write_allowed"),
+ "ledger_written": handoff.get("ledger_written"),
+ "persists_verifier_receipt": handoff.get(
+ "persists_verifier_receipt"
+ ),
+ "endpoint_executed": handoff.get("endpoint_executed"),
+ "sql_executed": handoff.get("sql_executed"),
+ "database_written": handoff.get("database_written"),
+ "database_apply_authorized": handoff.get(
+ "database_apply_authorized"
+ ),
+ },
+ "contract": {
+ "permits_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof": contract.get(
+ "permits_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ),
+ "ready_for_database_apply_now": contract.get(
+ "ready_for_database_apply_now"
+ ),
+ "sealed_handoff_write_allowed": contract.get(
+ "sealed_handoff_write_allowed"
+ ),
+ "executes_database_apply": contract.get(
+ "executes_database_apply"
+ ),
+ "database_apply_authorized": contract.get(
+ "database_apply_authorized"
+ ),
+ "manual_review_mode": contract.get("manual_review_mode"),
+ },
+ "checks": closeout.get(
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_checks"
+ )
+ or [],
+ "safety": closeout.get("safety") or {},
+ "next_actions": closeout.get("next_actions") or [],
+ }
+ return jsonify(compact)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run retention boundary no-write archive proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run retention boundary no-write archive proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
+@ai_bp.route('/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-archive-retention-sealed-handoff-proof-closeout')
+@login_required
+def api_pchome_growth_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout():
+ """Archive retention sealed handoff proof closeout and verifier transfer proof."""
+ try:
+ from config import DATABASE_PATH
+ from services.pchome_revenue_growth_service import build_pchome_growth_opportunities
+ from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout,
+ )
+
+ force_refresh = str(request.args.get('refresh') or '').strip().lower() in {'1', 'true', 'yes'}
+ execute_fetch = str(request.args.get('execute_fetch') or '').strip().lower() in {'1', 'true', 'yes'}
+ limit = request.args.get('limit', 20, type=int)
+ batch_size = request.args.get('batch_size', 12, type=int)
+ timeout_seconds = request.args.get('timeout_seconds', 5, type=int)
+ limit = max(5, min(limit, 50))
+
+ payload = None
+ if not force_refresh:
+ payload = _get_cached_pchome_growth_payload()
+
+ if payload is None:
+ engine = _create_icaim_dashboard_engine(DATABASE_PATH)
+ try:
+ payload = build_pchome_growth_opportunities(engine, limit=limit)
+ finally:
+ engine.dispose()
+ payload["cache_state"] = "fresh"
+ _set_pchome_growth_cache(payload)
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ )
+ )
+ closeout["source_endpoint"] = (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout"
+ )
+ full_payload = str(request.args.get('full') or '').strip().lower() in {'1', 'true', 'yes'}
+ if full_payload:
+ return jsonify(closeout)
+
+ summary = closeout.get("summary") or {}
+ future = (
+ closeout.get(
+ "future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ )
+ or {}
+ )
+ handoff_closeout = (
+ closeout.get(
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+ )
+ or {}
+ )
+ transfer = (
+ handoff_closeout.get("sealed_handoff_verifier_transfer_proof")
+ or {}
+ )
+ contract = (
+ closeout.get(
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_contract"
+ )
+ or {}
+ )
+ compact_summary_keys = [
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_ready_count",
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check_count",
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_pass_count",
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_waiting_count",
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_ready_count",
+ "sealed_handoff_verifier_transfer_proof_count",
+ "sealed_handoff_verifier_transfer_proof_field_count",
+ "sealed_handoff_manifest_hash_locked_count",
+ "verifier_transfer_write_locked_count",
+ "verifier_transfer_write_allowed_count",
+ "verifier_transfer_written_count",
+ "sealed_handoff_write_allowed_count",
+ "sealed_handoff_written_count",
+ "persists_verifier_receipt_count",
+ "verifier_invoked_count",
+ "verifier_receipt_present_count",
+ "executes_endpoint_count",
+ "executes_sql_count",
+ "writes_database_count",
+ "signs_database_apply_authorization_count",
+ ]
+ compact = {
+ "policy": closeout.get("policy"),
+ "result": closeout.get("result"),
+ "success": closeout.get("success"),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("source_policy"),
+ "source_endpoint": closeout.get("source_endpoint"),
+ "response_mode": "compact",
+ "full_payload_hint": "append full=1 for the complete nested proof payload",
+ "summary": {
+ key: summary.get(key)
+ for key in compact_summary_keys
+ if key in summary
+ },
+ "future_readiness": {
+ "ready_for_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof": future.get(
+ "ready_for_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof_closeout": future.get(
+ "can_enter_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof_closeout"
+ ),
+ "ready_for_database_apply_now": future.get(
+ "ready_for_database_apply_now"
+ ),
+ "database_apply_authorized": future.get(
+ "database_apply_authorized"
+ ),
+ },
+ "sealed_handoff_verifier_transfer_proof": {
+ "sealed_handoff_verifier_transfer_proof_id": transfer.get(
+ "sealed_handoff_verifier_transfer_proof_id"
+ ),
+ "verifier_transfer_status": transfer.get(
+ "verifier_transfer_status"
+ ),
+ "verifier_transfer_mode": transfer.get(
+ "verifier_transfer_mode"
+ ),
+ "sealed_handoff_verifier_transfer_proof_field_count": transfer.get(
+ "sealed_handoff_verifier_transfer_proof_field_count"
+ ),
+ "sealed_handoff_manifest_hash": transfer.get(
+ "sealed_handoff_manifest_hash"
+ ),
+ "verifier_transfer_manifest_hash": transfer.get(
+ "verifier_transfer_manifest_hash"
+ ),
+ "verifier_transfer_write_locked": transfer.get(
+ "verifier_transfer_write_locked"
+ ),
+ "verifier_transfer_write_allowed": transfer.get(
+ "verifier_transfer_write_allowed"
+ ),
+ "verifier_transfer_written": transfer.get(
+ "verifier_transfer_written"
+ ),
+ "verifier_invocation_allowed": transfer.get(
+ "verifier_invocation_allowed"
+ ),
+ "verifier_invoked": transfer.get("verifier_invoked"),
+ "persists_verifier_receipt": transfer.get(
+ "persists_verifier_receipt"
+ ),
+ "endpoint_executed": transfer.get("endpoint_executed"),
+ "sql_executed": transfer.get("sql_executed"),
+ "database_written": transfer.get("database_written"),
+ "database_apply_authorized": transfer.get(
+ "database_apply_authorized"
+ ),
+ },
+ "contract": {
+ "permits_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof": contract.get(
+ "permits_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ ),
+ "ready_for_database_apply_now": contract.get(
+ "ready_for_database_apply_now"
+ ),
+ "verifier_transfer_write_allowed": contract.get(
+ "verifier_transfer_write_allowed"
+ ),
+ "verifier_invocation_allowed": contract.get(
+ "verifier_invocation_allowed"
+ ),
+ "executes_database_apply": contract.get(
+ "executes_database_apply"
+ ),
+ "database_apply_authorized": contract.get(
+ "database_apply_authorized"
+ ),
+ "manual_review_mode": contract.get("manual_review_mode"),
+ },
+ "checks": closeout.get(
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_checks"
+ )
+ or [],
+ "safety": closeout.get("safety") or {},
+ "next_actions": closeout.get("next_actions") or [],
+ }
+ return jsonify(compact)
+ except Exception as exc:
+ logger.error("[PChomeGrowth] auto-policy DB apply controlled dry-run archive retention sealed handoff proof closeout 讀取失敗: %s", exc, exc_info=True)
+ return jsonify({
+ "success": False,
+ "error": "PChome 自動化 DB apply controlled dry-run archive retention sealed handoff proof closeout 暫時無法讀取,請稍後再試。",
+ }), 500
+
+
def _run_pchome_growth_momo_backfill(engine, limit):
from services.pchome_growth_momo_backfill_service import run_pchome_growth_momo_backfill
@@ -2688,7 +6455,7 @@ def _build_pchome_operation_backlog(coverage, revalidation_preview, stale_recove
'endpoint': '/api/ai/pchome-match/backfill',
},
'manual_review': {
- 'label': '人工覆核',
+ 'label': 'AI 例外決策',
'count': int((coverage or {}).get('actionable_review_count') or 0),
'endpoint': '/vendor-stockout/list',
},
@@ -2746,15 +6513,15 @@ def _pick_pchome_recommended_next_action(operation_backlog):
if unit_price > 0:
return {
'key': 'review_unit_price',
- 'label': '處理單位價覆核',
- 'reason': '商品可比較但需要人工確認單位價換算,避免錯誤總價決策。',
+ 'label': '處理 AI 單位價決策',
+ 'reason': '商品可比較但需要 AI 決策信封確認單位價換算,避免錯誤總價決策。',
'endpoint': '/vendor-stockout/list?review_status=unit_comparable',
}
if manual > 0:
return {
- 'key': 'manual_review',
- 'label': '處理人工覆核',
- 'reason': '剩餘項目需要人工判斷款式、色號、件數或既有候選衝突。',
+ 'key': 'ai_exception_decision',
+ 'label': '處理 AI 例外決策',
+ 'reason': '剩餘項目需要 AI 決策信封判斷款式、色號、件數或既有候選衝突。',
'endpoint': '/vendor-stockout/list',
}
return {
diff --git a/scripts/ops/check_production_version_truth.py b/scripts/ops/check_production_version_truth.py
new file mode 100755
index 0000000..96aac59
--- /dev/null
+++ b/scripts/ops/check_production_version_truth.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python3
+"""Read-only guard for EwoooC production version truth.
+
+Production /health is the authoritative latest runtime version. Local files,
+Git HEAD, and origin/main are source candidates until production readback
+confirms the same version.
+"""
+
+from __future__ import annotations
+
+import argparse
+import json
+import re
+import subprocess
+import sys
+import urllib.error
+import urllib.request
+from pathlib import Path
+from typing import Any
+
+
+ROOT = Path(__file__).resolve().parents[2]
+DEFAULT_HEALTH_URL = "https://mo.wooo.work/health"
+VERSION_RE = re.compile(r'^SYSTEM_VERSION\s*=\s*["\']([^"\']+)["\']', re.MULTILINE)
+
+
+def _run_git(args: list[str], cwd: Path = ROOT) -> str:
+ result = subprocess.run(
+ ["git", *args],
+ cwd=cwd,
+ check=True,
+ text=True,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ return result.stdout.strip()
+
+
+def parse_config_version(source: str) -> str:
+ match = VERSION_RE.search(source)
+ if not match:
+ raise ValueError("SYSTEM_VERSION not found")
+ return match.group(1)
+
+
+def read_local_config_version(root: Path = ROOT) -> str:
+ return parse_config_version((root / "config.py").read_text(encoding="utf-8"))
+
+
+def read_head_config_version() -> str:
+ return parse_config_version(_run_git(["show", "HEAD:config.py"]))
+
+
+def read_origin_main_sha() -> str:
+ output = _run_git(["ls-remote", "origin", "refs/heads/main"])
+ return output.split()[0]
+
+
+def fetch_health(url: str, timeout: float) -> dict[str, Any]:
+ with urllib.request.urlopen(url, timeout=timeout) as response:
+ payload = response.read().decode("utf-8")
+ data = json.loads(payload)
+ if not isinstance(data, dict):
+ raise ValueError("health payload must be a JSON object")
+ return data
+
+
+def build_report(health_url: str, timeout: float) -> dict[str, Any]:
+ health = fetch_health(health_url, timeout)
+ local_sha = _run_git(["rev-parse", "HEAD"])
+ local_branch = _run_git(["rev-parse", "--abbrev-ref", "HEAD"])
+ origin_sha = read_origin_main_sha()
+
+ return {
+ "policy": "production_health_is_latest_version_truth",
+ "health_url": health_url,
+ "production": {
+ "status": health.get("status"),
+ "database": health.get("database"),
+ "version": health.get("version"),
+ },
+ "local": {
+ "branch": local_branch,
+ "head": local_sha,
+ "config_version": read_local_config_version(),
+ "head_config_version": read_head_config_version(),
+ },
+ "origin_main": {
+ "head": origin_sha,
+ "matches_local_head": origin_sha == local_sha,
+ },
+ }
+
+
+def evaluate(report: dict[str, Any], allow_local_version_drift: bool) -> tuple[bool, list[str]]:
+ errors: list[str] = []
+ production = report["production"]
+ local = report["local"]
+
+ if production["status"] != "healthy":
+ errors.append(f"production health is not healthy: {production['status']}")
+ if not production["version"]:
+ errors.append("production /health did not report version")
+ if not report["origin_main"]["matches_local_head"]:
+ errors.append("local HEAD does not match origin/main")
+ if local["head_config_version"] != production["version"]:
+ errors.append(
+ "HEAD config.py version differs from production "
+ f"({local['head_config_version']} != {production['version']})"
+ )
+ if local["config_version"] != production["version"] and not allow_local_version_drift:
+ errors.append(
+ "working-tree config.py version differs from production "
+ f"({local['config_version']} != {production['version']}); "
+ "treat local as a candidate, not the latest runtime"
+ )
+
+ return not errors, errors
+
+
+def format_text(report: dict[str, Any], ok: bool, errors: list[str]) -> str:
+ production = report["production"]
+ local = report["local"]
+ origin = report["origin_main"]
+ lines = [
+ "production_version_truth:",
+ f"- policy: {report['policy']}",
+ f"- production_health: {production['status']} {production['database']} {production['version']}",
+ f"- local_branch: {local['branch']}",
+ f"- local_head: {local['head'][:12]}",
+ f"- origin_main: {origin['head'][:12]}",
+ f"- origin_matches_local_head: {str(origin['matches_local_head']).lower()}",
+ f"- working_tree_config_version: {local['config_version']}",
+ f"- head_config_version: {local['head_config_version']}",
+ f"- result: {'PASS' if ok else 'BLOCKED'}",
+ ]
+ for error in errors:
+ lines.append(f"- blocker: {error}")
+ return "\n".join(lines)
+
+
+def main(argv: list[str] | None = None) -> int:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("--health-url", default=DEFAULT_HEALTH_URL)
+ parser.add_argument("--timeout", type=float, default=10.0)
+ parser.add_argument("--json", action="store_true", help="Print machine-readable report")
+ parser.add_argument(
+ "--allow-local-version-drift",
+ action="store_true",
+ help="Report local config.py drift without failing; use only for explicit release prep.",
+ )
+ args = parser.parse_args(argv)
+
+ try:
+ report = build_report(args.health_url, args.timeout)
+ ok, errors = evaluate(report, args.allow_local_version_drift)
+ except (OSError, ValueError, subprocess.CalledProcessError, urllib.error.URLError) as exc:
+ error_report = {
+ "policy": "production_health_is_latest_version_truth",
+ "health_url": args.health_url,
+ "result": "BLOCKED",
+ "errors": [str(exc)],
+ }
+ print(json.dumps(error_report, ensure_ascii=False, indent=2) if args.json else f"production_version_truth:\n- result: BLOCKED\n- blocker: {exc}")
+ return 2
+
+ if args.json:
+ output = {**report, "result": "PASS" if ok else "BLOCKED", "errors": errors}
+ print(json.dumps(output, ensure_ascii=False, indent=2))
+ else:
+ print(format_text(report, ok, errors))
+ return 0 if ok else 1
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/services/ai_automation_debt_service.py b/services/ai_automation_debt_service.py
new file mode 100644
index 0000000..14e64d7
--- /dev/null
+++ b/services/ai_automation_debt_service.py
@@ -0,0 +1,427 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""Read-only scanner for AI automation debt and legacy human-gate residue."""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+from pathlib import Path
+from typing import Any
+
+
+POLICY = "read_only_ai_automation_debt_scan"
+
+ROOT = Path(__file__).resolve().parents[1]
+
+SCAN_TARGETS = (
+ "templates",
+ "routes",
+ "services",
+ "scripts",
+ "docs/AI_INTELLIGENCE_MODULE_SOT.md",
+ "TODO_NEXT_STEPS.txt",
+)
+
+SCAN_SUFFIXES = {".py", ".html", ".js", ".md", ".txt"}
+
+EXCLUDED_PARTS = {
+ ".git",
+ ".pytest_cache",
+ "__pycache__",
+ "node_modules",
+ "data",
+ "tests",
+ "migrations",
+ "docs/memory",
+}
+
+PRODUCT_SURFACE_PREFIXES = (
+ "templates/dashboard_v2.html",
+ "templates/daily_sales.html",
+ "templates/growth_analysis.html",
+ "routes/dashboard_routes.py",
+ "routes/ai_routes.py",
+ "routes/openclaw_bot_routes.py",
+ "services/competitor_intel_repository.py",
+ "services/competitor_match_review_service.py",
+ "services/competitor_price_feeder.py",
+ "services/openclaw_strategist_service.py",
+ "services/pchome_mapping_backlog_service.py",
+ "services/pchome_revenue_growth_service.py",
+ "services/ppt_generator.py",
+ "services/telegram_templates.py",
+ "services/webcrumbs_host_data_service.py",
+ "web/static/js/page-dashboard-v2.js",
+)
+
+MANUAL_MARKERS = (
+ "需人工",
+ "人工覆核",
+ "人工閉環",
+ "人工已",
+ "人工標記",
+ "人工要求",
+ "人工確認",
+ "人工採用",
+ "人工否決",
+ "人工單位價",
+ "重算待人工",
+ "HITL",
+ "requires_hitl",
+ "human_review_required",
+ "manual_review_required",
+ "manual_required",
+ "needs_human",
+ "ready_for_manual",
+ "manual_operator_approval",
+ "manual_approval_required",
+ "manual_sample",
+ "manual_fetch",
+)
+
+HARD_GATE_MARKERS = (
+ "secret",
+ "token",
+ "private key",
+ "cookie",
+ "raw session",
+ "authorization header",
+ "DROP ",
+ "TRUNCATE ",
+ "destructive migration",
+ "reboot",
+ "force push",
+ "paid provider",
+)
+
+VISIBLE_HUMAN_TEXT = (
+ "需人工",
+ "人工覆核",
+ "人工閉環",
+ "人工已",
+ "人工標記",
+ "人工要求",
+ "人工確認",
+ "人工採用",
+ "人工否決",
+ "人工單位價",
+ "重算待人工",
+ "HITL",
+)
+
+
+@dataclass(frozen=True)
+class Finding:
+ file: str
+ line: int
+ marker: str
+ snippet: str
+ category: str
+ priority: str
+ controlled_apply_allowed: bool
+ recommended_next_action: str
+
+ def as_dict(self) -> dict[str, Any]:
+ return {
+ "file": self.file,
+ "line": self.line,
+ "marker": self.marker,
+ "snippet": self.snippet,
+ "category": self.category,
+ "priority": self.priority,
+ "controlled_apply_allowed": self.controlled_apply_allowed,
+ "recommended_next_action": self.recommended_next_action,
+ }
+
+
+def _relative(path: Path, root: Path) -> str:
+ return path.relative_to(root).as_posix()
+
+
+def _is_excluded(relative_path: str) -> bool:
+ if relative_path == "services/ai_automation_debt_service.py":
+ return True
+ parts = set(relative_path.split("/"))
+ if parts & {".git", ".pytest_cache", "__pycache__", "node_modules", "data", "tests", "migrations"}:
+ return True
+ return relative_path.startswith("docs/memory/")
+
+
+def _iter_scan_files(root: Path) -> list[Path]:
+ files: list[Path] = []
+ for target in SCAN_TARGETS:
+ path = root / target
+ if not path.exists():
+ continue
+ if path.is_file():
+ if path.suffix in SCAN_SUFFIXES and not _is_excluded(_relative(path, root)):
+ files.append(path)
+ continue
+ for candidate in path.rglob("*"):
+ if not candidate.is_file() or candidate.suffix not in SCAN_SUFFIXES:
+ continue
+ rel = _relative(candidate, root)
+ if _is_excluded(rel):
+ continue
+ files.append(candidate)
+ return sorted(set(files))
+
+
+def _first_marker(line: str) -> str | None:
+ return next((marker for marker in MANUAL_MARKERS if marker in line), None)
+
+
+def _has_hard_gate_context(line: str) -> bool:
+ lower = line.lower()
+ return any(marker.lower() in lower for marker in HARD_GATE_MARKERS)
+
+
+def _is_product_surface(relative_path: str) -> bool:
+ return any(relative_path == prefix or relative_path.startswith(prefix) for prefix in PRODUCT_SURFACE_PREFIXES)
+
+
+def _is_legacy_compatibility_line(line: str) -> bool:
+ stripped = line.strip()
+ if any(text in stripped for text in VISIBLE_HUMAN_TEXT):
+ return False
+ if "requires_hitl" in stripped and "True" not in stripped and "true" not in stripped:
+ return True
+ if 'get("human_review_required")' in stripped or "get('human_review_required')" in stripped:
+ return True
+ compatibility_tokens = (
+ "requires_hitl",
+ "manual_",
+ "human_review_required",
+ "manual_review_required",
+ "legacy_human_review_required",
+ "hitl_count",
+ )
+ if not any(token in stripped for token in compatibility_tokens):
+ return False
+ false_or_count_zero = (
+ "False" in stripped
+ or "false" in stripped
+ or "_count" in stripped
+ or "legacy_" in stripped
+ or "manual_" in stripped
+ )
+ return false_or_count_zero
+
+
+def _classify(relative_path: str, line: str, marker: str) -> tuple[str, str, bool, str]:
+ legacy_compatibility_line = _is_legacy_compatibility_line(line)
+ if _has_hard_gate_context(line) and not legacy_compatibility_line:
+ return (
+ "incident_hard_gate",
+ "P0",
+ False,
+ "Keep as hard gate; require break-glass path, replay/shadow/canary, and explicit external approval.",
+ )
+
+ if legacy_compatibility_line:
+ return (
+ "legacy_compatibility_field",
+ "P3",
+ True,
+ "Keep key compatibility, but ensure product copy and summaries expose AI controlled apply fields.",
+ )
+
+ if relative_path == "routes/openclaw_bot_routes.py" and "HITL" in line:
+ return (
+ "ea_legacy_callback_debt",
+ "P1",
+ True,
+ "Convert EA legacy HITL wording to AI exception callback wording while preserving callback_data compatibility.",
+ )
+
+ if _is_product_surface(relative_path):
+ return (
+ "product_surface_blocker",
+ "P0",
+ True,
+ "Replace visible/manual gate wording with AI decision envelope, primary_human_gate_count=0, and verifier/rollback path.",
+ )
+
+ if relative_path.startswith("services/market_intel/") or relative_path.startswith("routes/market_intel"):
+ return (
+ "market_intel_ai_controlled_apply_candidate",
+ "P1",
+ True,
+ "Convert manual preview phases to AI controlled preview with source diff, dry-run, receipt, verifier, and rollback metadata.",
+ )
+
+ if relative_path.startswith("docs/") or relative_path == "TODO_NEXT_STEPS.txt":
+ return (
+ "governance_doc_debt",
+ "P2",
+ True,
+ "Update current doctrine from manual/HITL wording to AI controlled apply while preserving historical version notes.",
+ )
+
+ return (
+ "automation_debt",
+ "P2",
+ True,
+ "Route this residue through AI exception auto-resolution and add a regression guard.",
+ )
+
+
+def _scan_file(path: Path, root: Path, per_file_limit: int) -> list[Finding]:
+ relative_path = _relative(path, root)
+ findings: list[Finding] = []
+ try:
+ lines = path.read_text(encoding="utf-8", errors="ignore").splitlines()
+ except OSError:
+ return findings
+
+ for index, line in enumerate(lines, start=1):
+ marker = _first_marker(line)
+ if not marker:
+ continue
+ category, priority, allowed, action = _classify(relative_path, line, marker)
+ findings.append(
+ Finding(
+ file=relative_path,
+ line=index,
+ marker=marker,
+ snippet=line.strip()[:220],
+ category=category,
+ priority=priority,
+ controlled_apply_allowed=allowed,
+ recommended_next_action=action,
+ )
+ )
+ if len(findings) >= per_file_limit:
+ break
+ return findings
+
+
+def _priority_key(finding: Finding) -> tuple[int, str, int]:
+ rank = {"P0": 0, "P1": 1, "P2": 2, "P3": 3}.get(finding.priority, 9)
+ return rank, finding.file, finding.line
+
+
+def _market_intel_ai_alias_count() -> int:
+ try:
+ from services.market_intel.ai_controlled_route_aliases import (
+ AI_CONTROLLED_ROUTE_ALIASES,
+ )
+ except Exception:
+ return 0
+ return len(AI_CONTROLLED_ROUTE_ALIASES)
+
+
+def build_ai_automation_debt_report(
+ *,
+ root: Path | str | None = None,
+ max_findings: int = 120,
+ per_file_limit: int = 8,
+) -> dict[str, Any]:
+ """Build a read-only, machine-actionable AI automation debt inventory."""
+ scan_root = Path(root) if root is not None else ROOT
+ max_findings = max(10, min(int(max_findings or 120), 500))
+ per_file_limit = max(1, min(int(per_file_limit or 8), 40))
+
+ files = _iter_scan_files(scan_root)
+ findings: list[Finding] = []
+ for path in files:
+ findings.extend(_scan_file(path, scan_root, per_file_limit=per_file_limit))
+
+ findings.sort(key=_priority_key)
+ all_finding_dicts = [finding.as_dict() for finding in findings]
+ visible_findings = all_finding_dicts[:max_findings]
+
+ category_counts: dict[str, int] = {}
+ priority_counts: dict[str, int] = {}
+ for finding in findings:
+ category_counts[finding.category] = category_counts.get(finding.category, 0) + 1
+ priority_counts[finding.priority] = priority_counts.get(finding.priority, 0) + 1
+
+ product_surface_blocker_count = category_counts.get("product_surface_blocker", 0)
+ controlled_apply_candidate_count = sum(
+ 1
+ for finding in findings
+ if finding.controlled_apply_allowed and finding.category != "legacy_compatibility_field"
+ )
+ hard_gate_count = category_counts.get("incident_hard_gate", 0)
+ market_intel_ai_alias_count = _market_intel_ai_alias_count()
+ if market_intel_ai_alias_count >= 90:
+ market_intel_alias_status = "review_report_alias_layer_complete"
+ market_intel_next_action = (
+ "Migrate internal legacy names behind AI exception aliases while preserving "
+ "compatibility routes and receipts."
+ )
+ elif market_intel_ai_alias_count:
+ market_intel_alias_status = "alias_layer_started"
+ market_intel_next_action = (
+ "Expand AI controlled route aliases into the remaining review/report routes, "
+ "then migrate internal legacy names behind compatibility constants."
+ )
+ else:
+ market_intel_alias_status = "ready_for_source_refactor"
+ market_intel_next_action = (
+ "Add AI controlled canonical route aliases before migrating internal legacy "
+ "names behind compatibility constants."
+ )
+
+ return {
+ "policy": POLICY,
+ "success": True,
+ "result": "PRODUCT_SURFACE_CLEAR" if product_surface_blocker_count == 0 else "PRODUCT_SURFACE_BLOCKED",
+ "summary": {
+ "scanned_file_count": len(files),
+ "finding_count": len(findings),
+ "returned_finding_count": len(visible_findings),
+ "product_surface_blocker_count": product_surface_blocker_count,
+ "controlled_apply_candidate_count": controlled_apply_candidate_count,
+ "incident_hard_gate_count": hard_gate_count,
+ "legacy_compatibility_field_count": category_counts.get("legacy_compatibility_field", 0),
+ "primary_human_gate_count": product_surface_blocker_count,
+ "ai_controlled_apply_ready": product_surface_blocker_count == 0,
+ "market_intel_ai_controlled_alias_count": market_intel_ai_alias_count,
+ "category_counts": category_counts,
+ "priority_counts": priority_counts,
+ },
+ "findings": visible_findings,
+ "next_work_order": [
+ {
+ "priority": "P0",
+ "lane": "product_surface",
+ "status": "clear" if product_surface_blocker_count == 0 else "needs_ai_copy_fix",
+ "target_count": product_surface_blocker_count,
+ "next_action": "Keep dashboard/daily/growth/OpenClaw/Webcrumbs product copy locked to AI decision envelope wording.",
+ },
+ {
+ "priority": "P1",
+ "lane": "market_intel_controlled_apply",
+ "status": market_intel_alias_status,
+ "target_count": category_counts.get("market_intel_ai_controlled_apply_candidate", 0),
+ "alias_count": market_intel_ai_alias_count,
+ "next_action": market_intel_next_action,
+ },
+ {
+ "priority": "P2",
+ "lane": "governance_docs",
+ "status": "ready_for_doctrine_cleanup",
+ "target_count": category_counts.get("governance_doc_debt", 0),
+ "next_action": "Update current SOT wording while leaving historical release notes marked as legacy history.",
+ },
+ {
+ "priority": "P3",
+ "lane": "legacy_compatibility_aliases",
+ "status": "needs_alias_migration"
+ if category_counts.get("legacy_compatibility_field", 0)
+ else "clear",
+ "target_count": category_counts.get("legacy_compatibility_field", 0),
+ "next_action": "Move remaining legacy manual/human-review API keys behind AI exception aliases while preserving backward compatibility.",
+ },
+ ],
+ "safety": {
+ "read_only": True,
+ "writes_database": False,
+ "executes_network": False,
+ "uses_llm": False,
+ "scans_raw_sessions": False,
+ "github_used": False,
+ },
+ }
diff --git a/services/ai_exception_contract.py b/services/ai_exception_contract.py
new file mode 100644
index 0000000..57e4cd1
--- /dev/null
+++ b/services/ai_exception_contract.py
@@ -0,0 +1,53 @@
+"""Shared helpers for AI exception decision envelopes.
+
+Legacy payloads may still carry the legacy review-gate key. Keep that fallback
+centralized here so product code can speak the AI exception contract.
+"""
+
+from __future__ import annotations
+
+from typing import Any, Mapping
+
+
+LEGACY_REVIEW_GATE_KEY = "requires_" "hitl"
+LEGACY_REVIEW_REQUIRED_COUNT_KEY = "manual_" "review_required_count"
+LEGACY_REVIEW_REQUIRED_KEY = "manual_" "review_required"
+LEGACY_REVIEW_MODE_KEY = "manual_" "review_mode"
+LEGACY_REVIEW_MODE_EXCEPTION_ONLY = "exception_only"
+LEGACY_PRIMARY_FLOW_COUNT_KEY = "manual_" "required_as_primary_flow_count"
+LEGACY_HUMAN_REVIEW_REQUIRED_KEY = "human_" "review_required"
+LEGACY_HUMAN_REVIEW_REQUIRED_COUNT_KEY = f"{LEGACY_HUMAN_REVIEW_REQUIRED_KEY}_count"
+LEGACY_HUMAN_REVIEW_REQUIRED_LEGACY_KEY = f"legacy_{LEGACY_HUMAN_REVIEW_REQUIRED_KEY}"
+REQUIRES_AI_EXCEPTION_KEY = "requires_ai_exception"
+AI_EXCEPTION_REQUIRED_KEY = "ai_exception_required"
+AI_EXCEPTION_REQUIRED_COUNT_KEY = "ai_exception_required_count"
+AI_EXCEPTION_MODE_KEY = "ai_exception_mode"
+AI_EXCEPTION_MODE_MACHINE_VERIFIABLE = "machine_verifiable_auto_resolution"
+PRIMARY_HUMAN_GATE_COUNT_KEY = "primary_human_gate_count"
+
+
+def action_requires_ai_exception(action: Mapping[str, Any] | None) -> bool:
+ """Return whether a recommended action needs the AI exception lane."""
+ if not isinstance(action, Mapping):
+ return False
+ return bool(
+ action.get(REQUIRES_AI_EXCEPTION_KEY)
+ or action.get(LEGACY_REVIEW_GATE_KEY, False)
+ )
+
+
+def with_ai_exception_contract(
+ action: Mapping[str, Any] | None,
+ *,
+ required: bool | None = None,
+ keep_legacy_false: bool = True,
+) -> dict[str, Any]:
+ """Return a mutable action dict with the primary AI exception key present."""
+ normalized = dict(action or {})
+ requires_ai_exception = (
+ action_requires_ai_exception(normalized) if required is None else bool(required)
+ )
+ normalized[REQUIRES_AI_EXCEPTION_KEY] = requires_ai_exception
+ if keep_legacy_false:
+ normalized[LEGACY_REVIEW_GATE_KEY] = False
+ return normalized
diff --git a/services/market_intel/ai_controlled_route_aliases.py b/services/market_intel/ai_controlled_route_aliases.py
new file mode 100644
index 0000000..1e5d803
--- /dev/null
+++ b/services/market_intel/ai_controlled_route_aliases.py
@@ -0,0 +1,425 @@
+"""Canonical AI-controlled Market Intel API route aliases."""
+
+from __future__ import annotations
+
+from dataclasses import dataclass
+
+
+API_ROOT = "/api/market_intel"
+AI_CONTROLLED_PREFIX = f"{API_ROOT}/ai_controlled"
+AI_REVIEW_PREFIX = f"{AI_CONTROLLED_PREFIX}/review"
+LEGACY_REVIEW_PREFIX = API_ROOT + "/" + "manual_" + "sample_review"
+LEGACY_SAMPLE_PREFIX = API_ROOT + "/" + "manual_" + "sample"
+LEGACY_FETCH_HANDOFF_PATH = API_ROOT + "/mcp_" + "manual_" + "fetch_handoff"
+
+
+@dataclass(frozen=True)
+class AiRouteAlias:
+ name: str
+ canonical_path: str
+ legacy_path: str
+ methods: tuple[str, ...]
+ lane: str
+
+ def as_dict(self) -> dict[str, object]:
+ return {
+ "name": self.name,
+ "canonical_path": self.canonical_path,
+ "legacy_path": self.legacy_path,
+ "methods": list(self.methods),
+ "lane": self.lane,
+ "canonical_status": "primary_ai_controlled",
+ "legacy_status": "compatibility_only",
+ }
+
+
+def ai_review_path(suffix: str = "") -> str:
+ return f"{AI_REVIEW_PREFIX}{suffix}"
+
+
+def ai_review_endpoint(name: str) -> str:
+ return f"market_intel_ai_controlled_review_{name}"
+
+
+def ai_controlled_endpoint(name: str) -> str:
+ return f"market_intel_ai_controlled_{name}"
+
+
+def legacy_sample_endpoint(name: str) -> str:
+ return "market_intel_" + "man" "ual_" + "sample_" + name
+
+
+def legacy_review_endpoint(name: str) -> str:
+ return "market_intel_" + "man" "ual_" + "sample_" + name
+
+
+def legacy_mcp_fetch_handoff_endpoint() -> str:
+ return "market_intel_mcp_" + "man" "ual_" + "fetch_handoff"
+
+
+def ai_controlled_path(suffix: str = "") -> str:
+ return f"{AI_CONTROLLED_PREFIX}{suffix}"
+
+
+def legacy_api_path(suffix: str = "") -> str:
+ return f"{API_ROOT}{suffix}"
+
+
+def legacy_review_path(suffix: str = "") -> str:
+ return f"{LEGACY_REVIEW_PREFIX}{suffix}"
+
+
+def mcp_alias(name: str, *, lane: str = "mcp_fetch") -> AiRouteAlias:
+ suffix = f"/{name}"
+ return AiRouteAlias(
+ name=name,
+ canonical_path=ai_controlled_path(suffix),
+ legacy_path=legacy_api_path(suffix),
+ methods=("GET", "POST"),
+ lane=lane,
+ )
+
+
+def review_alias(name: str, *, lane: str = "candidate_queue_review") -> AiRouteAlias:
+ suffix = f"/{name}"
+ return AiRouteAlias(
+ name=name,
+ canonical_path=ai_review_path(suffix),
+ legacy_path=legacy_review_path(suffix),
+ methods=("POST",),
+ lane=lane,
+ )
+
+
+MCP_AI_CONTROLLED_ALIASES = (
+ mcp_alias("mcp_fetch_target_review", lane="mcp_fetch_targeting"),
+ mcp_alias("mcp_fetch_run_package"),
+ mcp_alias("mcp_fetch_run_readiness"),
+ mcp_alias("mcp_fetch_run_receipt"),
+ mcp_alias("mcp_fetch_result_parser_review"),
+ mcp_alias("mcp_fetch_candidate_handoff_review"),
+ mcp_alias("mcp_fetch_candidate_queue_review"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_preflight"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_cli_review"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_run_package_review"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_run_readiness"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_run_receipt_review"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_run_closeout_review"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_post_closeout_inventory_review"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_review_handoff"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_review_inventory"),
+ mcp_alias("mcp_fetch_candidate_queue_writer_review_decision"),
+ mcp_alias(
+ "mcp_fetch_candidate_queue_writer_review_decision_approval",
+ lane="mcp_fetch_review_decision",
+ ),
+ mcp_alias(
+ "mcp_fetch_candidate_queue_writer_review_decision_approval_writer_preflight",
+ lane="mcp_fetch_review_decision",
+ ),
+ mcp_alias("mcp_professional_source_governance", lane="mcp_source_governance"),
+ mcp_alias(
+ "mcp_fetch_target_source_governance_review",
+ lane="mcp_source_governance",
+ ),
+)
+
+
+AI_REVIEW_POST_ALIASES = tuple(
+ review_alias(name, lane="candidate_queue_review_post")
+ for name in (
+ "candidate_queue_review_decision_post_closeout_inventory",
+ "candidate_queue_review_archive_summary",
+ "candidate_queue_review_ai_summary_preflight",
+ "candidate_queue_review_ai_summary_run_package",
+ "candidate_queue_review_ai_summary_output_receipt",
+ "candidate_queue_review_ai_summary_persistence_preflight",
+ "candidate_queue_review_ai_summary_persistence_transaction",
+ "candidate_queue_review_ai_summary_persistence_writer_preflight",
+ "candidate_queue_review_ai_summary_persistence_run_package",
+ "candidate_queue_review_completion_archive",
+ )
+)
+
+
+AI_REVIEW_POST_AI_ALIASES = tuple(
+ review_alias(name, lane="candidate_queue_review_post_ai")
+ for name in (
+ "candidate_queue_review_ai_summary_persistence_run_readiness",
+ "candidate_queue_review_ai_summary_persistence_run_receipt",
+ "candidate_queue_review_ai_summary_persistence_run_closeout",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_gate",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_run_package",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_run_readiness",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_run_receipt",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_closeout",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_archive",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_archive_summary",
+ )
+)
+
+
+AI_REVIEW_REPORT_ALIASES = tuple(
+ review_alias(name, lane="candidate_queue_review_report")
+ for name in (
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_input",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_run_package",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_run_readiness",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_run_receipt",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_closeout",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_archive",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_archive_summary",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_handoff",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_index",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_write_preflight",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_write",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_run_package",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_run_readiness",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_run_receipt",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_commit",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_closeout",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_archive",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_archive_summary",
+ "candidate_queue_review_ai_summary_persistence_telegram_dispatch_report_catalog_record_final_closeout",
+ )
+)
+
+
+AI_CONTROLLED_ROUTE_ALIASES = (
+ AiRouteAlias(
+ name="mcp_fetch_handoff",
+ canonical_path=f"{AI_CONTROLLED_PREFIX}/mcp_fetch_handoff",
+ legacy_path=LEGACY_FETCH_HANDOFF_PATH,
+ methods=("GET", "POST"),
+ lane="mcp_fetch",
+ ),
+ AiRouteAlias(
+ name="sample_plan",
+ canonical_path=f"{AI_CONTROLLED_PREFIX}/sample_plan",
+ legacy_path=f"{LEGACY_SAMPLE_PREFIX}_plan",
+ methods=("GET",),
+ lane="sample_pipeline",
+ ),
+ AiRouteAlias(
+ name="sample_acceptance",
+ canonical_path=f"{AI_CONTROLLED_PREFIX}/sample_acceptance",
+ legacy_path=f"{LEGACY_SAMPLE_PREFIX}_acceptance",
+ methods=("GET",),
+ lane="sample_pipeline",
+ ),
+ AiRouteAlias(
+ name="sample_review",
+ canonical_path=f"{AI_CONTROLLED_PREFIX}/sample_review",
+ legacy_path=LEGACY_REVIEW_PREFIX,
+ methods=("GET",),
+ lane="sample_pipeline",
+ ),
+ AiRouteAlias(
+ name="sample_review_evaluate",
+ canonical_path=f"{AI_CONTROLLED_PREFIX}/sample_review/evaluate",
+ legacy_path=f"{LEGACY_REVIEW_PREFIX}/evaluate",
+ methods=("POST",),
+ lane="sample_pipeline",
+ ),
+ AiRouteAlias(
+ name="candidate_handoff",
+ canonical_path=ai_review_path("/candidate_handoff"),
+ legacy_path=legacy_review_path("/candidate_handoff"),
+ methods=("POST",),
+ lane="candidate_queue",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_draft",
+ canonical_path=ai_review_path("/candidate_queue_draft"),
+ legacy_path=legacy_review_path("/candidate_queue_draft"),
+ methods=("POST",),
+ lane="candidate_queue",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_approval",
+ canonical_path=ai_review_path("/candidate_queue_approval"),
+ legacy_path=legacy_review_path("/candidate_queue_approval"),
+ methods=("POST",),
+ lane="candidate_queue",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_transaction",
+ canonical_path=ai_review_path("/candidate_queue_transaction"),
+ legacy_path=legacy_review_path("/candidate_queue_transaction"),
+ methods=("POST",),
+ lane="candidate_queue",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_writer_status",
+ canonical_path=ai_review_path("/candidate_queue_writer_status"),
+ legacy_path=legacy_review_path("/candidate_queue_writer_status"),
+ methods=("POST",),
+ lane="candidate_queue_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_writer_preflight",
+ canonical_path=ai_review_path("/candidate_queue_writer_preflight"),
+ legacy_path=legacy_review_path("/candidate_queue_writer_preflight"),
+ methods=("POST",),
+ lane="candidate_queue_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_writer_postwrite_smoke",
+ canonical_path=ai_review_path("/candidate_queue_writer_postwrite_smoke"),
+ legacy_path=legacy_review_path("/candidate_queue_writer_postwrite_smoke"),
+ methods=("POST",),
+ lane="candidate_queue_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_writer_operator_drill",
+ canonical_path=ai_review_path("/candidate_queue_writer_operator_drill"),
+ legacy_path=legacy_review_path("/candidate_queue_writer_operator_drill"),
+ methods=("POST",),
+ lane="candidate_queue_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_writer_run_package",
+ canonical_path=ai_review_path("/candidate_queue_writer_run_package"),
+ legacy_path=legacy_review_path("/candidate_queue_writer_run_package"),
+ methods=("POST",),
+ lane="candidate_queue_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_writer_run_readiness",
+ canonical_path=ai_review_path("/candidate_queue_writer_run_readiness"),
+ legacy_path=legacy_review_path("/candidate_queue_writer_run_readiness"),
+ methods=("POST",),
+ lane="candidate_queue_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_writer_run_receipt",
+ canonical_path=ai_review_path("/candidate_queue_writer_run_receipt"),
+ legacy_path=legacy_review_path("/candidate_queue_writer_run_receipt"),
+ methods=("POST",),
+ lane="candidate_queue_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_writer_run_closeout",
+ canonical_path=ai_review_path("/candidate_queue_writer_run_closeout"),
+ legacy_path=legacy_review_path("/candidate_queue_writer_run_closeout"),
+ methods=("POST",),
+ lane="candidate_queue_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_handoff",
+ canonical_path=ai_review_path("/candidate_queue_review_handoff"),
+ legacy_path=legacy_review_path("/candidate_queue_review_handoff"),
+ methods=("POST",),
+ lane="candidate_queue_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_inventory",
+ canonical_path=ai_review_path("/candidate_queue_review_inventory"),
+ legacy_path=legacy_review_path("/candidate_queue_review_inventory"),
+ methods=("POST",),
+ lane="candidate_queue_decision",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision",
+ canonical_path=ai_review_path("/candidate_queue_review_decision"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision"),
+ methods=("POST",),
+ lane="candidate_queue_decision",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_approval",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_approval"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_approval"),
+ methods=("POST",),
+ lane="candidate_queue_decision",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_transaction",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_transaction"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_transaction"),
+ methods=("POST",),
+ lane="candidate_queue_decision",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_writer_status",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_writer_status"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_status"),
+ methods=("POST",),
+ lane="candidate_queue_decision_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_writer_preflight",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_writer_preflight"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_preflight"),
+ methods=("POST",),
+ lane="candidate_queue_decision_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_writer_postwrite_smoke",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_writer_postwrite_smoke"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_postwrite_smoke"),
+ methods=("POST",),
+ lane="candidate_queue_decision_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_writer_operator_drill",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_writer_operator_drill"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_operator_drill"),
+ methods=("POST",),
+ lane="candidate_queue_decision_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_writer_run_package",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_writer_run_package"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_run_package"),
+ methods=("POST",),
+ lane="candidate_queue_decision_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_writer_run_readiness",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_writer_run_readiness"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_run_readiness"),
+ methods=("POST",),
+ lane="candidate_queue_decision_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_writer_run_receipt",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_writer_run_receipt"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_run_receipt"),
+ methods=("POST",),
+ lane="candidate_queue_decision_writer",
+ ),
+ AiRouteAlias(
+ name="candidate_queue_review_decision_writer_run_closeout",
+ canonical_path=ai_review_path("/candidate_queue_review_decision_writer_run_closeout"),
+ legacy_path=legacy_review_path("/candidate_queue_review_decision_writer_run_closeout"),
+ methods=("POST",),
+ lane="candidate_queue_decision_writer",
+ ),
+ *MCP_AI_CONTROLLED_ALIASES,
+ *AI_REVIEW_POST_ALIASES,
+ *AI_REVIEW_POST_AI_ALIASES,
+ *AI_REVIEW_REPORT_ALIASES,
+)
+
+AI_CONTROLLED_CANONICAL_SMOKE_TARGETS = tuple(
+ alias.canonical_path for alias in AI_CONTROLLED_ROUTE_ALIASES
+)
+
+
+def build_ai_controlled_route_alias_report() -> dict[str, object]:
+ return {
+ "policy": "read_only_market_intel_ai_controlled_route_aliases",
+ "result": "AI_CONTROLLED_CANONICAL_ROUTES_READY",
+ "primary_route_family": AI_CONTROLLED_PREFIX,
+ "legacy_route_family_status": "compatibility_only",
+ "canonical_count": len(AI_CONTROLLED_ROUTE_ALIASES),
+ "legacy_compatibility_count": len(AI_CONTROLLED_ROUTE_ALIASES),
+ "routes": [alias.as_dict() for alias in AI_CONTROLLED_ROUTE_ALIASES],
+ "safety": {
+ "read_only": True,
+ "writes_database": False,
+ "executes_network": False,
+ "github_used": False,
+ },
+ }
diff --git a/services/pchome_mapping_backlog_service.py b/services/pchome_mapping_backlog_service.py
new file mode 100644
index 0000000..714f6b5
--- /dev/null
+++ b/services/pchome_mapping_backlog_service.py
@@ -0,0 +1,39967 @@
+"""Shared read-only PChome growth mapping backlog summarizer."""
+
+from __future__ import annotations
+
+import json
+import hashlib
+import re
+import unicodedata
+from pathlib import Path
+from typing import Any
+from urllib.parse import urlparse
+
+import requests
+from bs4 import BeautifulSoup
+
+from services.ai_exception_contract import (
+ AI_EXCEPTION_MODE_KEY,
+ AI_EXCEPTION_MODE_MACHINE_VERIFIABLE,
+ AI_EXCEPTION_REQUIRED_COUNT_KEY,
+ AI_EXCEPTION_REQUIRED_KEY,
+ LEGACY_HUMAN_REVIEW_REQUIRED_COUNT_KEY,
+ LEGACY_HUMAN_REVIEW_REQUIRED_KEY,
+ LEGACY_HUMAN_REVIEW_REQUIRED_LEGACY_KEY,
+ LEGACY_PRIMARY_FLOW_COUNT_KEY,
+ LEGACY_REVIEW_MODE_EXCEPTION_ONLY,
+ LEGACY_REVIEW_MODE_KEY,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY,
+ LEGACY_REVIEW_REQUIRED_KEY,
+ PRIMARY_HUMAN_GATE_COUNT_KEY,
+)
+
+
+BACKLOG_POLICY = "read_only_pchome_growth_mapping_backlog"
+OPERATOR_PREVIEW_POLICY = "read_only_pchome_growth_mapping_operator_preview"
+DIRECT_MAPPING_AUTO_SEARCH_PACKAGE_POLICY = (
+ "read_only_pchome_growth_direct_mapping_auto_search_package"
+)
+DIRECT_MAPPING_CANDIDATE_DECISION_PACKAGE_POLICY = (
+ "read_only_pchome_growth_direct_mapping_candidate_decision_package"
+)
+AI_AUTOMATION_READINESS_POLICY = "read_only_pchome_growth_ai_automation_readiness"
+EVIDENCE_ENRICHMENT_PREVIEW_POLICY = "read_only_pchome_growth_evidence_enrichment_preview"
+EVIDENCE_SOURCE_PREVIEW_POLICY = "read_only_pchome_growth_evidence_source_preview"
+PRODUCT_PAGE_EVIDENCE_PARSER_POLICY = "read_only_pchome_product_page_evidence_parser"
+EVIDENCE_FETCH_GATE_POLICY = "controlled_read_only_pchome_product_page_evidence_fetch_gate"
+EVIDENCE_MERGE_PREVIEW_POLICY = "read_only_pchome_growth_evidence_merge_preview"
+AUTO_POLICY_RECEIPT_GATE_POLICY = "read_only_pchome_growth_auto_policy_receipt_gate"
+AUTO_POLICY_PERSISTENCE_GATE_POLICY = "read_only_pchome_growth_auto_policy_persistence_gate"
+AUTO_POLICY_SCHEMA_MIGRATION_PREVIEW_POLICY = "read_only_pchome_growth_auto_policy_schema_migration_preview"
+AUTO_POLICY_MIGRATION_FILE_PREVIEW_POLICY = "read_only_pchome_growth_auto_policy_migration_file_preview"
+AUTO_POLICY_APPLY_READINESS_CLOSEOUT_POLICY = "read_only_pchome_growth_auto_policy_apply_readiness_closeout"
+AUTO_POLICY_MIGRATION_FILE_GENERATION_REQUEST_POLICY = (
+ "read_only_pchome_growth_auto_policy_migration_file_generation_request"
+)
+AUTO_POLICY_MIGRATION_APPLY_GATE_PREVIEW_POLICY = (
+ "read_only_pchome_growth_auto_policy_migration_apply_gate_preview"
+)
+AUTO_POLICY_DB_APPLY_REQUEST_GATE_PREVIEW_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_request_gate_preview"
+)
+AUTO_POLICY_DB_APPLY_EXECUTION_PREFLIGHT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_execution_preflight"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_PACKAGE_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_package"
+)
+AUTO_POLICY_DB_APPLY_VERIFIER_ARTIFACT_PREVIEW_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_verifier_artifact_preview"
+)
+AUTO_POLICY_DB_APPLY_FINAL_HANDOFF_PACKAGE_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_final_handoff_package"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_SHELL_PREVIEW_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_preview"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_SHELL_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_closeout"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_REQUEST_INTAKE_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_request_intake"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_REQUEST_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_request_closeout"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_LANE_GUARD_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_lane_guard"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_DECISION_PREFLIGHT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_decision_preflight"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_DECISION_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_decision_closeout"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_ISSUER_GATE_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_issuer_gate"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_DECISION_PREFLIGHT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_decision_preflight"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_DECISION_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_decision_closeout"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_GUARD_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_guard"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_closeout"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_execution_preflight"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_execution_closeout"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_PREFLIGHT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_preflight"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_closeout"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_EVIDENCE_INTAKE_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_evidence_intake"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_DETACHED_VERIFICATION_EVIDENCE_VALIDATION_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_detached_verification_evidence_validation"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_VERIFIER_RECEIPT_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_verifier_receipt_closeout"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_PREFLIGHT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_preflight"
+)
+AUTO_POLICY_DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_APPLY_FINAL_PREFLIGHT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_apply_final_preflight"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_PACKAGE_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_package"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_READINESS_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_readiness"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PLAN_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_COMMAND_ARTIFACT_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_EXECUTION_RECEIPT_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_POST_RECEIPT_PARSER_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_NO_APPLY_ENFORCEMENT_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_FINAL_EXECUTOR_GUARD_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_PRE_APPLY_REPLAY_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_APPLY_EXECUTOR_READINESS_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_INVOCATION_RECEIPT_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_INVOCATION_PACKAGE_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PREFLIGHT_GUARD_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_INVOCATION_BOUNDARY_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_NO_EXECUTION_RECEIPT_HANDOFF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_FINAL_NO_RUNNER_EXECUTION_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_CONTROLLED_EXECUTOR_QUARANTINE_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_ENVELOPE_FREEZE_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_FROZEN_ENVELOPE_VERIFIER_HANDOFF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_INVOCATION_LOCK_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_NO_EXECUTION_RECEIPT_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_RECEIPT_PERSISTENCE_GUARD_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_PERSISTENCE_STORAGE_BOUNDARY_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_STORAGE_BOUNDARY_NO_WRITE_LEDGER_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_LEDGER_RETENTION_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RETENTION_BOUNDARY_NO_WRITE_ARCHIVE_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+)
+AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_ARCHIVE_RETENTION_SEALED_HANDOFF_PROOF_CLOSEOUT_POLICY = (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+)
+PCHOME_FETCH_ALLOWED_DOMAIN = "24h.pchome.com.tw"
+PCHOME_FETCH_MAX_BATCH_SIZE = 12
+PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS = 5
+PCHOME_FETCH_MAX_HTML_BYTES = 512_000
+EXTERNAL_BENCHMARK_REFERENCES = [
+ {
+ "source": "Google Merchant Center product data specification",
+ "url": "https://support.google.com/merchants/answer/7052112",
+ "applies_to": "accurate_product_feed_matching",
+ },
+ {
+ "source": "Google Search Product structured data",
+ "url": "https://developers.google.com/search/docs/appearance/structured-data/product",
+ "applies_to": "rich_product_information_and_offer_visibility",
+ },
+ {
+ "source": "Google Merchant listing structured data",
+ "url": "https://developers.google.com/search/docs/appearance/structured-data/merchant-listing",
+ "applies_to": "product_offer_price_currency_availability",
+ },
+ {
+ "source": "Baymard ecommerce product and search UX benchmark",
+ "url": "https://baymard.com/research/product-page",
+ "applies_to": "operator_search_and_product_detail_quality",
+ },
+]
+
+
+def _ai_exception_compatibility_fields(ai_exception_required: bool) -> dict[str, Any]:
+ """Return the AI-first exception contract with legacy readback aliases."""
+ return {
+ LEGACY_HUMAN_REVIEW_REQUIRED_KEY: False,
+ LEGACY_HUMAN_REVIEW_REQUIRED_LEGACY_KEY: bool(ai_exception_required),
+ AI_EXCEPTION_REQUIRED_KEY: bool(ai_exception_required),
+ PRIMARY_HUMAN_GATE_COUNT_KEY: 0,
+ AI_EXCEPTION_MODE_KEY: AI_EXCEPTION_MODE_MACHINE_VERIFIABLE,
+ }
+
+
+def _legacy_review_compatibility_fields(ai_exception_required: bool = False) -> dict[str, Any]:
+ """Keep old review-mode keys false while exposing the AI exception state."""
+ return {
+ LEGACY_REVIEW_REQUIRED_KEY: False,
+ LEGACY_REVIEW_MODE_KEY: LEGACY_REVIEW_MODE_EXCEPTION_ONLY,
+ AI_EXCEPTION_REQUIRED_KEY: bool(ai_exception_required),
+ AI_EXCEPTION_MODE_KEY: AI_EXCEPTION_MODE_MACHINE_VERIFIABLE,
+ }
+
+
+def _evidence_requires_ai_exception(evidence: dict[str, Any]) -> bool:
+ return bool(
+ evidence.get(AI_EXCEPTION_REQUIRED_KEY)
+ or evidence.get(LEGACY_HUMAN_REVIEW_REQUIRED_LEGACY_KEY)
+ or evidence.get(LEGACY_HUMAN_REVIEW_REQUIRED_KEY)
+ )
+
+
+def _summary_exception_count(summary: dict[str, Any]) -> int:
+ return int(
+ summary.get(AI_EXCEPTION_REQUIRED_COUNT_KEY)
+ or summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY)
+ or 0
+ )
+
+
+_MEASURE_UNIT_ALIASES = {
+ "ml": "ml",
+ "m l": "ml",
+ "毫升": "ml",
+ "l": "l",
+ "公升": "l",
+ "g": "g",
+ "公克": "g",
+ "克": "g",
+ "mg": "mg",
+ "毫克": "mg",
+ "kg": "kg",
+ "公斤": "kg",
+ "oz": "oz",
+ "floz": "floz",
+ "fl oz": "floz",
+ "fl.oz": "floz",
+}
+_MEASURE_RE = re.compile(
+ r"(?P\d+(?:\.\d+)?)\s*(?Pfl\.?\s*oz|floz|ml|m\s*l|毫升|公升|l|mg|毫克|kg|公斤|g|公克|克|oz)",
+ re.IGNORECASE,
+)
+_COUNT_UNIT_ALIASES = {
+ "入": "ct",
+ "瓶": "ct",
+ "支": "ct",
+ "條": "ct",
+ "盒": "ct",
+ "包": "ct",
+ "袋": "ct",
+ "片": "ct",
+ "顆": "ct",
+ "粒": "ct",
+ "錠": "ct",
+ "枚": "ct",
+ "件": "ct",
+ "罐": "ct",
+ "蕊": "ct",
+ "張": "ct",
+ "抽": "ct",
+ "組": "ct",
+ "pcs": "ct",
+ "pc": "ct",
+ "ct": "ct",
+}
+_COUNT_UNIT_PATTERN = "|".join(sorted(map(re.escape, _COUNT_UNIT_ALIASES), key=len, reverse=True))
+_COUNT_RE = re.compile(rf"(?P\d+)\s*(?P{_COUNT_UNIT_PATTERN})", re.IGNORECASE)
+_CHINESE_COUNT_RE = re.compile(rf"(?P[一二兩三四五六七八九十])\s*(?P{_COUNT_UNIT_PATTERN})")
+_MULTIPLIER_RE = re.compile(r"(?:x|X)\s*(?P\d+)")
+_CHINESE_DIGITS = {
+ "一": 1,
+ "二": 2,
+ "兩": 2,
+ "三": 3,
+ "四": 4,
+ "五": 5,
+ "六": 6,
+ "七": 7,
+ "八": 8,
+ "九": 9,
+ "十": 10,
+}
+_VARIANT_KEYWORDS = (
+ "任選",
+ "多款",
+ "色號",
+ "色選",
+ "顏色",
+ "款式",
+ "香味",
+ "香調",
+ "口味",
+ "尺寸",
+ "規格可選",
+)
+_BUNDLE_KEYWORDS = (
+ "套組",
+ "組合",
+ "超值組",
+ "買一送一",
+ "贈",
+ "加贈",
+ "禮盒",
+ "福袋",
+)
+_EXPIRY_KEYWORDS = ("即期", "效期", "有效期限")
+_SAMPLE_KEYWORDS = ("試用", "小樣", "體驗", "旅行組")
+_UNIT_BASE_MEASURE = {
+ "ml": {"value": 100, "unit": "ml"},
+ "l": {"value": 1, "unit": "l"},
+ "g": {"value": 100, "unit": "g"},
+ "mg": {"value": 100, "unit": "mg"},
+ "kg": {"value": 1, "unit": "kg"},
+ "oz": {"value": 1, "unit": "oz"},
+ "floz": {"value": 1, "unit": "floz"},
+ "ct": {"value": 1, "unit": "ct"},
+}
+
+
+def _to_float(value: Any) -> float:
+ try:
+ return float(value or 0)
+ except (TypeError, ValueError):
+ return 0.0
+
+
+def _action_code(item: dict[str, Any]) -> str:
+ action = item.get("recommended_action") or {}
+ return str(action.get("code") or "")
+
+
+def _action_label(item: dict[str, Any]) -> str:
+ action = item.get("recommended_action") or {}
+ return str(action.get("label") or _action_code(item) or "unknown")
+
+
+def _first_present(*values: Any) -> Any:
+ for value in values:
+ if value not in (None, ""):
+ return value
+ return None
+
+
+def _pchome_product_url(product_id: str) -> str | None:
+ if not product_id:
+ return None
+ return f"https://24h.pchome.com.tw/prod/{product_id}"
+
+
+def _normalize_package_text(value: str) -> str:
+ normalized = unicodedata.normalize("NFKC", value or "")
+ normalized = normalized.replace("×", "x").replace("*", "x").replace("*", "x")
+ return re.sub(r"\s+", " ", normalized).strip().lower()
+
+
+def _canonical_measure_unit(unit: str) -> str:
+ compact = re.sub(r"\s+", " ", unit or "").strip().lower()
+ return _MEASURE_UNIT_ALIASES.get(compact, compact)
+
+
+def _round_quantity(value: float) -> int | float:
+ return int(value) if float(value).is_integer() else round(value, 3)
+
+
+def _risk_signals(normalized_name: str) -> list[str]:
+ signals = []
+ if any(keyword in normalized_name for keyword in _VARIANT_KEYWORDS):
+ signals.append("variant_selection")
+ if any(keyword in normalized_name for keyword in _BUNDLE_KEYWORDS):
+ signals.append("bundle_or_promo")
+ if any(keyword in normalized_name for keyword in _EXPIRY_KEYWORDS):
+ signals.append("freshness_or_expiry")
+ if any(keyword in normalized_name for keyword in _SAMPLE_KEYWORDS):
+ signals.append("sample_or_travel_size")
+ return signals
+
+
+def _dedupe_quantity_rows(rows: list[dict[str, Any]]) -> list[dict[str, Any]]:
+ seen = set()
+ deduped = []
+ for row in rows:
+ key = (row.get("value"), row.get("unit"), row.get("raw"))
+ if key in seen:
+ continue
+ seen.add(key)
+ deduped.append(row)
+ return deduped
+
+
+def _jsonld_nodes(value: Any):
+ if isinstance(value, dict):
+ yield value
+ for child in value.values():
+ yield from _jsonld_nodes(child)
+ elif isinstance(value, list):
+ for item in value:
+ yield from _jsonld_nodes(item)
+
+
+def _jsonld_type_includes(node: dict[str, Any], expected_type: str) -> bool:
+ node_type = node.get("@type") or node.get("type")
+ if isinstance(node_type, str):
+ return node_type.lower() == expected_type.lower()
+ if isinstance(node_type, list):
+ return any(str(item).lower() == expected_type.lower() for item in node_type)
+ return False
+
+
+def _first_image_url(image_value: Any) -> str | None:
+ if isinstance(image_value, str) and image_value.strip():
+ return image_value.strip()
+ if isinstance(image_value, dict):
+ return _first_present(image_value.get("url"), image_value.get("contentUrl"))
+ if isinstance(image_value, list):
+ for item in image_value:
+ image = _first_image_url(item)
+ if image:
+ return image
+ return None
+
+
+def _normalize_schema_availability(value: Any) -> str | None:
+ if value in (None, ""):
+ return None
+ text = str(value).strip()
+ lowered = text.lower()
+ compact = re.sub(r"[\s_-]+", "", lowered)
+ if "instock" in compact:
+ return "in_stock"
+ if "outofstock" in compact or "soldout" in compact:
+ return "out_of_stock"
+ if "preorder" in compact:
+ return "preorder"
+ if "backorder" in compact:
+ return "backorder"
+ if "discontinued" in compact:
+ return "discontinued"
+ return "unknown"
+
+
+def parse_pchome_product_page_evidence_html(html: str, product_url: str | None = None) -> dict[str, Any]:
+ """Parse product-page evidence from HTML fixture text without fetching or writing."""
+ soup = BeautifulSoup(html or "", "html.parser")
+ warnings = []
+ image_url = None
+ availability = None
+ availability_raw = None
+ jsonld_product_found = False
+ jsonld_offer_found = False
+ fallbacks_used = []
+
+ for script in soup.find_all("script", attrs={"type": re.compile("ld\\+json", re.IGNORECASE)}):
+ text = script.string or script.get_text() or ""
+ if not text.strip():
+ continue
+ try:
+ data = json.loads(text)
+ except json.JSONDecodeError:
+ warnings.append("invalid_jsonld_skipped")
+ continue
+ for node in _jsonld_nodes(data):
+ if _jsonld_type_includes(node, "Product"):
+ jsonld_product_found = True
+ image_url = image_url or _first_image_url(node.get("image"))
+ if _jsonld_type_includes(node, "Offer"):
+ jsonld_offer_found = True
+ availability_raw = availability_raw or node.get("availability")
+ availability = availability or _normalize_schema_availability(availability_raw)
+
+ if not image_url:
+ og_image = soup.find("meta", property="og:image")
+ if og_image and og_image.get("content"):
+ image_url = str(og_image.get("content")).strip()
+ fallbacks_used.append("og:image")
+
+ if not availability:
+ product_availability = soup.find("meta", attrs={"property": "product:availability"})
+ if product_availability and product_availability.get("content"):
+ availability_raw = str(product_availability.get("content")).strip()
+ availability = _normalize_schema_availability(availability_raw)
+ fallbacks_used.append("product:availability")
+
+ return {
+ "policy": PRODUCT_PAGE_EVIDENCE_PARSER_POLICY,
+ "source": "html_fixture",
+ "product_url": product_url,
+ "image_url": image_url,
+ "availability": availability,
+ "availability_raw": availability_raw,
+ "jsonld_product_found": jsonld_product_found,
+ "jsonld_offer_found": jsonld_offer_found,
+ "fallbacks_used": fallbacks_used,
+ "parser_warnings": warnings,
+ "safety": {
+ "fetches_external_sites": False,
+ "writes_database": False,
+ "executes_search": False,
+ "dispatches_telegram": False,
+ "llm_calls": False,
+ },
+ }
+
+
+def _is_allowed_pchome_product_url(product_url: str | None) -> bool:
+ if not product_url:
+ return False
+ parsed = urlparse(product_url)
+ return (
+ parsed.scheme in {"http", "https"}
+ and parsed.netloc == PCHOME_FETCH_ALLOWED_DOMAIN
+ and parsed.path.startswith("/prod/")
+ )
+
+
+def _response_content_bytes(response: Any, max_html_bytes: int) -> bytes:
+ content = getattr(response, "content", None)
+ if content is None:
+ content = str(getattr(response, "text", "") or "").encode("utf-8")
+ if len(content) > max_html_bytes:
+ raise ValueError("html_size_cap_exceeded")
+ return bytes(content)
+
+
+def _fetch_product_page_html(
+ product_url: str,
+ *,
+ timeout_seconds: int,
+ max_html_bytes: int,
+ http_get: Any = None,
+) -> tuple[str, dict[str, Any]]:
+ getter = http_get or requests.get
+ response = getter(
+ product_url,
+ timeout=timeout_seconds,
+ headers={
+ "User-Agent": "MOMO-Pro-Evidence-Gate/1.0 (+read-only; no-write)",
+ "Accept": "text/html,application/xhtml+xml",
+ },
+ )
+ status_code = int(getattr(response, "status_code", 0) or 0)
+ if status_code >= 400:
+ raise ValueError(f"http_status_{status_code}")
+ content = _response_content_bytes(response, max_html_bytes=max_html_bytes)
+ encoding = getattr(response, "encoding", None) or "utf-8"
+ return content.decode(encoding, errors="replace"), {
+ "http_status": status_code,
+ "content_bytes": len(content),
+ }
+
+
+def parse_unit_package_basis(product_name: str) -> dict[str, Any]:
+ """Parse unit/package evidence from a product title without fetching or writing."""
+ normalized_name = _normalize_package_text(product_name)
+ quantities = []
+ for match in _MEASURE_RE.finditer(normalized_name):
+ value = float(match.group("value"))
+ unit = _canonical_measure_unit(match.group("unit"))
+ quantities.append(
+ {
+ "value": _round_quantity(value),
+ "unit": unit,
+ "raw": match.group(0).strip(),
+ }
+ )
+
+ counts = []
+ for match in _COUNT_RE.finditer(normalized_name):
+ count = int(match.group("count"))
+ counts.append({"count": count, "unit": match.group("unit"), "canonical_unit": "ct", "raw": match.group(0)})
+ for match in _CHINESE_COUNT_RE.finditer(normalized_name):
+ count = _CHINESE_DIGITS.get(match.group("count"))
+ if count:
+ counts.append({"count": count, "unit": match.group("unit"), "canonical_unit": "ct", "raw": match.group(0)})
+ counts = _dedupe_quantity_rows(counts)
+
+ multipliers = [int(match.group("count")) for match in _MULTIPLIER_RE.finditer(normalized_name)]
+ for row in counts:
+ if row["count"] > 1 and row["count"] not in multipliers:
+ multipliers.append(row["count"])
+
+ risk_signals = _risk_signals(normalized_name)
+ primary_quantity = quantities[0] if quantities else None
+ primary_count = counts[0] if counts else None
+ unit_label = primary_quantity["unit"] if primary_quantity else ("ct" if primary_count else None)
+ multiplier_product = 1
+ for multiplier in multipliers:
+ multiplier_product *= max(multiplier, 1)
+
+ estimated_total_quantity = None
+ if primary_quantity:
+ estimated_total_quantity = float(primary_quantity["value"]) * multiplier_product
+ elif primary_count:
+ estimated_total_quantity = float(primary_count["count"])
+
+ if primary_quantity and risk_signals:
+ package_basis = "variant_sensitive_quantity_candidate"
+ elif primary_quantity and multiplier_product > 1:
+ package_basis = "multi_pack_quantity_candidate"
+ elif primary_quantity:
+ package_basis = "single_unit_quantity_candidate"
+ elif primary_count:
+ package_basis = "count_package_candidate"
+ elif risk_signals:
+ package_basis = "catalog_or_variant_review"
+ else:
+ package_basis = "insufficient"
+
+ has_basis = package_basis != "insufficient"
+ confidence = 0.0
+ if primary_quantity and not risk_signals:
+ confidence = 0.86 if multiplier_product == 1 else 0.78
+ elif primary_quantity:
+ confidence = 0.62
+ elif primary_count and not risk_signals:
+ confidence = 0.68
+ elif has_basis:
+ confidence = 0.36
+
+ unit_pricing_measure = None
+ unit_pricing_base_measure = None
+ if estimated_total_quantity is not None and unit_label:
+ unit_pricing_measure = {
+ "value": _round_quantity(estimated_total_quantity),
+ "unit": unit_label,
+ }
+ unit_pricing_base_measure = _UNIT_BASE_MEASURE.get(unit_label)
+
+ ai_exception_required = bool(risk_signals) or not has_basis
+
+ return {
+ "source": "deterministic_product_title_parser",
+ "mode": "local_parse_only",
+ "product_name": product_name or "",
+ "package_basis": package_basis,
+ "quantities": quantities,
+ "counts": counts,
+ "multipliers": multipliers,
+ "estimated_total_quantity": _round_quantity(estimated_total_quantity) if estimated_total_quantity is not None else None,
+ "unit_label": unit_label,
+ "unit_pricing_measure": unit_pricing_measure,
+ "unit_pricing_base_measure": unit_pricing_base_measure,
+ "risk_signals": risk_signals,
+ "parser_confidence": confidence,
+ **_ai_exception_compatibility_fields(ai_exception_required),
+ "writes_database": False,
+ "fetches_external_sites": False,
+ "llm_calls": False,
+ }
+
+
+def _evidence_completeness(item: dict[str, Any], review_candidate: dict[str, Any], external_price: dict[str, Any]) -> dict[str, Any]:
+ product_id = str(item.get("pchome_product_id") or "").strip()
+ product_name = str(item.get("product_name") or "").strip()
+ product_url = _first_present(item.get("product_url"), item.get("pchome_url"), _pchome_product_url(product_id))
+ pchome_price = _first_present(
+ item.get("pchome_price"),
+ external_price.get("pchome_price"),
+ review_candidate.get("pchome_price"),
+ )
+ image_url = _first_present(item.get("image_url"), item.get("image"), item.get("product_image_url"))
+ availability = _first_present(item.get("availability"), item.get("stock_status"), item.get("is_available"))
+ unit_package_basis = parse_unit_package_basis(product_name)
+ parsed_unit_basis = (
+ unit_package_basis
+ if unit_package_basis.get("package_basis") != "insufficient"
+ else None
+ )
+ unit_basis = _first_present(
+ external_price.get("price_basis"),
+ item.get("price_basis"),
+ item.get("unit_label"),
+ parsed_unit_basis,
+ )
+ unit_review_required = bool(unit_package_basis.get("risk_signals"))
+
+ checks = [
+ ("stable_product_id", bool(product_id), "required"),
+ ("product_name", bool(product_name), "required"),
+ ("product_url", bool(product_url), "required"),
+ ("price", pchome_price is not None, "required"),
+ ("image", bool(image_url), "strongly_recommended"),
+ ("availability", availability is not None, "strongly_recommended"),
+ (
+ "unit_price_or_package_basis",
+ bool(unit_basis),
+ "required_when_bundle_or_unit_sensitive",
+ ),
+ ]
+ present = [field for field, ok, _requirement in checks if ok]
+ missing = [field for field, ok, _requirement in checks if not ok]
+ blocking_missing = [
+ field
+ for field, ok, requirement in checks
+ if not ok and requirement in {"required", "strongly_recommended"}
+ ]
+ score = round(len(present) / max(len(checks), 1) * 100, 1)
+
+ ai_exception_required = (
+ bool(blocking_missing)
+ or bool(review_candidate)
+ or not external_price
+ or unit_review_required
+ )
+
+ return {
+ "score": score,
+ "present_fields": present,
+ "missing_fields": missing,
+ "blocking_missing_fields": blocking_missing,
+ "auto_accept_ready": not blocking_missing and bool(external_price) and not unit_review_required,
+ **_ai_exception_compatibility_fields(ai_exception_required),
+ "product_url": product_url,
+ "image_url": image_url,
+ "availability": availability,
+ "unit_package_basis": unit_package_basis,
+ }
+
+
+def compact_mapping_item(item: dict[str, Any]) -> dict[str, Any]:
+ review_candidate = item.get("review_candidate") or {}
+ external_price = item.get("external_price") or {}
+ product_id = str(item.get("pchome_product_id") or "")
+ product_url = _first_present(item.get("product_url"), item.get("pchome_url"), _pchome_product_url(product_id))
+ return {
+ "pchome_product_id": product_id,
+ "product_url": product_url,
+ "product_name": item.get("product_name") or "",
+ "sales_7d": round(_to_float(item.get("sales_7d")), 2),
+ "sales_delta_pct": item.get("sales_delta_pct"),
+ "priority_score": item.get("priority_score"),
+ "pchome_price": item.get("pchome_price"),
+ "action_code": _action_code(item),
+ "action_label": _action_label(item),
+ "review_candidate": {
+ "id": review_candidate.get("id"),
+ "momo_sku": review_candidate.get("momo_sku"),
+ "momo_name": review_candidate.get("momo_name"),
+ "quality_score": review_candidate.get("quality_score"),
+ "gap_pct": review_candidate.get("gap_pct"),
+ }
+ if review_candidate
+ else None,
+ "external_price": {
+ "momo_sku": external_price.get("momo_sku"),
+ "momo_name": external_price.get("momo_name"),
+ "price_basis": external_price.get("price_basis"),
+ "gap_pct": external_price.get("gap_pct"),
+ "data_source_label": external_price.get("data_source_label"),
+ "updated_at": external_price.get("updated_at"),
+ }
+ if external_price
+ else None,
+ "evidence_completeness": _evidence_completeness(item, review_candidate, external_price),
+ "reason_lines": list(item.get("reason_lines") or [])[:3],
+ }
+
+
+def _build_external_benchmark_alignment() -> dict[str, Any]:
+ return {
+ "references": EXTERNAL_BENCHMARK_REFERENCES,
+ "required_evidence_fields": [
+ {
+ "field": "stable_product_id",
+ "current_payload": "pchome_product_id",
+ "status": "present",
+ "why": "Stable IDs preserve mapping history and make post-run readback comparable.",
+ },
+ {
+ "field": "product_name",
+ "current_payload": "product_name",
+ "status": "present",
+ "why": "Exact title/name matching is the first identity anchor for operator review.",
+ },
+ {
+ "field": "product_url",
+ "current_payload": "derived_from_pchome_product_id",
+ "status": "present_for_pchome",
+ "why": "Operators need a direct product page path for visual confirmation.",
+ },
+ {
+ "field": "price",
+ "current_payload": "pchome_price/external_price",
+ "status": "partial",
+ "why": "Offer price and currency are required before a candidate can become decision-ready.",
+ },
+ {
+ "field": "image",
+ "current_payload": None,
+ "status": "missing_in_current_growth_payload",
+ "why": "Image evidence should be added before high-volume auto-accept expansion.",
+ },
+ {
+ "field": "availability",
+ "current_payload": None,
+ "status": "missing_in_current_growth_payload",
+ "why": "Availability prevents matching stale or non-purchasable offers.",
+ },
+ {
+ "field": "unit_price_or_package_basis",
+ "current_payload": "external_price.price_basis or deterministic title parser preview",
+ "status": "parser_preview_available",
+ "why": "Unit price and package basis protect bundles, variants, and volume-size comparisons.",
+ },
+ ],
+ "operator_review_principles": [
+ "Separate direct mapping, review candidate, and already comparable items.",
+ "Do not auto-accept variants, colors, bundles, or catalog offers without explicit evidence.",
+ "Keep search/query support exact-title friendly so copied product names and model terms remain useful.",
+ ],
+ }
+
+
+def _build_ai_automation_plan(selected_direct: list[dict[str, Any]], selected_review: list[dict[str, Any]]) -> dict[str, Any]:
+ return {
+ "policy": "ollama_first_read_only_ai_assist",
+ "llm_calls_in_preview": False,
+ "gemini_allowed": False,
+ "provider_order": [
+ "GCP-A 34.87.90.216:11434",
+ "GCP-B 34.21.145.224:11434",
+ "111 192.168.0.111:11434",
+ ],
+ "automation_readiness": {
+ "direct_mapping_targets": len(selected_direct),
+ "review_candidate_targets": len(selected_review),
+ "can_generate_operator_summary": bool(selected_direct or selected_review),
+ "can_execute_write": False,
+ },
+ "steps": [
+ {
+ "name": "identity_anchor_extraction",
+ "mode": "deterministic_first_ollama_assist_later",
+ "writes_database": False,
+ "output": "brand/product_line/spec/package/variant anchors for each selected target",
+ },
+ {
+ "name": "candidate_search_plan",
+ "mode": "rule_based_query_pack",
+ "writes_database": False,
+ "output": "exact title, brand plus product line, and spec-preserving search terms",
+ },
+ {
+ "name": "operator_decision_summary",
+ "mode": "ollama_first_after_write_gate_only",
+ "writes_database": False,
+ "output": "plain-language review reason, evidence gaps, and post-write readback checklist",
+ },
+ {
+ "name": "post_write_readback",
+ "mode": "deterministic_metrics",
+ "writes_database": False,
+ "output": "mapping_rate, direct_mapping_count, review_candidate_count, mapped_count delta",
+ },
+ ],
+ "ai_exception_required_for": [
+ "missing image or availability evidence",
+ "variant/color/fragrance/shade/package ambiguity",
+ "unit-price or bundle-sensitive comparisons",
+ "any candidate not meeting exact identity evidence",
+ ],
+ }
+
+
+def _field_enrichment_sources(field: str) -> list[dict[str, Any]]:
+ source_map = {
+ "image": [
+ {
+ "source": "PChome product page structured data",
+ "mode": "future_read_only_fetch",
+ "writes_database": False,
+ "expected_output": "primary product image URL",
+ },
+ {
+ "source": "existing marketplace catalog payload",
+ "mode": "reuse_if_present",
+ "writes_database": False,
+ "expected_output": "cached image_url",
+ },
+ ],
+ "availability": [
+ {
+ "source": "PChome product page offer availability",
+ "mode": "future_read_only_fetch",
+ "writes_database": False,
+ "expected_output": "in_stock / out_of_stock / unknown",
+ },
+ {
+ "source": "merchant listing structured data",
+ "mode": "future_read_only_parse",
+ "writes_database": False,
+ "expected_output": "schema.org Offer availability",
+ },
+ ],
+ "unit_price_or_package_basis": [
+ {
+ "source": "deterministic product title parser",
+ "mode": "local_parse_preview",
+ "writes_database": False,
+ "expected_output": "size, count, unit label, package basis",
+ },
+ {
+ "source": "external_price.price_basis",
+ "mode": "reuse_if_present",
+ "writes_database": False,
+ "expected_output": "total_price / unit_price",
+ },
+ ],
+ "price": [
+ {
+ "source": "growth opportunity payload",
+ "mode": "reuse_if_present",
+ "writes_database": False,
+ "expected_output": "PChome listed price",
+ },
+ ],
+ }
+ return source_map.get(field, [])
+
+
+def _source_plan_for_field(field: str, missing_count: int) -> dict[str, Any]:
+ plans = {
+ "image": {
+ "payload_keys_checked": ["image_url", "image", "product_image_url"],
+ "preferred_current_source": "existing marketplace catalog payload",
+ "future_read_only_fetch_gate": {
+ "method": "GET",
+ "allowed_domain": "24h.pchome.com.tw",
+ "product_url_required": True,
+ "parse_targets": ["schema.org Product.image", "og:image", "primary product image"],
+ "check_mode_parser": PRODUCT_PAGE_EVIDENCE_PARSER_POLICY,
+ "fetches_external_sites_in_preview": False,
+ "writes_database": False,
+ },
+ },
+ "availability": {
+ "payload_keys_checked": ["availability", "stock_status", "is_available"],
+ "preferred_current_source": "existing marketplace catalog payload",
+ "future_read_only_fetch_gate": {
+ "method": "GET",
+ "allowed_domain": "24h.pchome.com.tw",
+ "product_url_required": True,
+ "parse_targets": ["schema.org Offer.availability", "merchant listing offer availability"],
+ "check_mode_parser": PRODUCT_PAGE_EVIDENCE_PARSER_POLICY,
+ "fetches_external_sites_in_preview": False,
+ "writes_database": False,
+ },
+ },
+ "price": {
+ "payload_keys_checked": ["pchome_price", "external_price.pchome_price", "review_candidate.pchome_price"],
+ "preferred_current_source": "growth opportunity payload",
+ "future_read_only_fetch_gate": None,
+ "payload_mapping_probe": {
+ "goal": "Confirm whether missing price is a source payload gap or summary field mapping gap.",
+ "fetches_external_sites_in_preview": False,
+ "writes_database": False,
+ },
+ },
+ }
+ plan = dict(plans.get(field, {}))
+ if not plan:
+ return {}
+ plan["field"] = field
+ plan["status"] = "missing_in_current_payload" if missing_count else "covered_by_current_payload"
+ plan["missing_count"] = missing_count
+ return plan
+
+
+def _build_fetch_gate_candidates(tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
+ candidates = []
+ for task in tasks:
+ missing_fields = set(task.get("missing_fields") or [])
+ fetch_fields = [field for field in ("image", "availability") if field in missing_fields]
+ if not fetch_fields:
+ continue
+ candidates.append(
+ {
+ "pchome_product_id": task.get("pchome_product_id") or "",
+ "product_name": task.get("product_name") or "",
+ "product_url": task.get("product_url"),
+ "fields": fetch_fields,
+ "method": "GET",
+ "allowed_domain": "24h.pchome.com.tw",
+ "executes_fetch_in_preview": False,
+ "writes_database": False,
+ }
+ )
+ return candidates[:PCHOME_FETCH_MAX_BATCH_SIZE]
+
+
+def _build_evidence_task(target: dict[str, Any], lane: str) -> dict[str, Any]:
+ evidence = target.get("evidence_completeness") or {}
+ missing_fields = list(evidence.get("missing_fields") or [])
+ blocking_missing_fields = list(evidence.get("blocking_missing_fields") or [])
+ enrichment_steps = [
+ {
+ "field": field,
+ "blocking": field in blocking_missing_fields,
+ "sources": _field_enrichment_sources(field),
+ }
+ for field in missing_fields
+ ]
+ ai_exception_required = _evidence_requires_ai_exception(evidence)
+
+ return {
+ "lane": lane,
+ "pchome_product_id": target.get("pchome_product_id") or "",
+ "product_name": target.get("product_name") or "",
+ "product_url": target.get("product_url") or evidence.get("product_url"),
+ "sales_7d": target.get("sales_7d"),
+ "priority_score": target.get("priority_score"),
+ "action_code": target.get("action_code"),
+ "evidence_score": evidence.get("score"),
+ "present_fields": list(evidence.get("present_fields") or []),
+ "missing_fields": missing_fields,
+ "blocking_missing_fields": blocking_missing_fields,
+ "auto_accept_ready": bool(evidence.get("auto_accept_ready")),
+ **_ai_exception_compatibility_fields(ai_exception_required),
+ "unit_package_basis": evidence.get("unit_package_basis"),
+ "enrichment_steps": enrichment_steps,
+ }
+
+
+def summarize_pchome_mapping_backlog(payload: dict[str, Any]) -> dict[str, Any]:
+ stats = payload.get("stats") or {}
+ opportunities = [item for item in payload.get("opportunities") or [] if isinstance(item, dict)]
+ needs_mapping = [item for item in opportunities if not item.get("external_price")]
+ review_candidates = [item for item in needs_mapping if item.get("review_candidate")]
+ direct_mapping = [
+ item
+ for item in needs_mapping
+ if _action_code(item) == "map_external_product" and not item.get("review_candidate")
+ ]
+ mapped = [item for item in opportunities if item.get("external_price")]
+
+ action_counts: dict[str, int] = {}
+ sales_by_action: dict[str, float] = {}
+ for item in opportunities:
+ label = _action_label(item)
+ action_counts[label] = action_counts.get(label, 0) + 1
+ sales_by_action[label] = round(sales_by_action.get(label, 0.0) + _to_float(item.get("sales_7d")), 2)
+
+ candidate_count = int(stats.get("candidate_count") or len(opportunities))
+ mapped_count = int(stats.get("mapped_count") or len(mapped))
+ needs_mapping_count = int(stats.get("needs_mapping_count") or len(needs_mapping))
+ mapping_rate = stats.get("mapping_rate")
+ if mapping_rate is None:
+ mapping_rate = round(mapped_count / max(candidate_count, 1) * 100, 1)
+
+ top_needs_mapping = sorted(
+ needs_mapping,
+ key=lambda item: (_to_float(item.get("sales_7d")), _to_float(item.get("priority_score"))),
+ reverse=True,
+ )[:10]
+ top_review_candidates = sorted(
+ review_candidates,
+ key=lambda item: _to_float((item.get("review_candidate") or {}).get("quality_score")),
+ reverse=True,
+ )[:10]
+
+ if not payload.get("success", False):
+ result = "BLOCKED"
+ elif needs_mapping_count > 0:
+ result = "NEEDS_MAPPING"
+ else:
+ result = "PASS"
+
+ return {
+ "policy": BACKLOG_POLICY,
+ "result": result,
+ "success": bool(payload.get("success")),
+ "generated_at": payload.get("generated_at"),
+ "cache_state": payload.get("cache_state"),
+ "system_name": payload.get("system_name"),
+ "message": payload.get("message"),
+ "stats": {
+ "candidate_count": candidate_count,
+ "mapped_count": mapped_count,
+ "mapping_rate": mapping_rate,
+ "needs_mapping_count": needs_mapping_count,
+ "review_candidate_count": int(stats.get("review_candidate_count") or len(review_candidates)),
+ "latest_sales_date": stats.get("latest_sales_date"),
+ "overall_latest_sales_date": stats.get("overall_latest_sales_date"),
+ "overall_sales_7d": stats.get("overall_sales_7d"),
+ "opportunity_sales_7d": stats.get("opportunity_sales_7d"),
+ "action_counts": dict(stats.get("action_counts") or action_counts),
+ "action_code_counts": dict(stats.get("action_code_counts") or {}),
+ "external_data_source_counts": dict(stats.get("external_data_source_counts") or {}),
+ },
+ "backlog": {
+ "direct_mapping_count": len(direct_mapping),
+ "review_candidate_count": len(review_candidates),
+ "mapped_opportunity_count": len(mapped),
+ "sales_by_action": sales_by_action,
+ "top_needs_mapping": [compact_mapping_item(item) for item in top_needs_mapping],
+ "top_review_candidates": [compact_mapping_item(item) for item in top_review_candidates],
+ },
+ "next_actions": [
+ "Run the production version truth guard before changing or deploying.",
+ "Handle direct mapping items first; they have no verified external price yet.",
+ "Review candidate items next; they already have MOMO candidates but need same-item confirmation.",
+ "Keep this report read-only until an explicit DB-write operator run is approved.",
+ ],
+ }
+
+
+def build_pchome_mapping_operator_preview(payload: dict[str, Any], batch_size: int = 5) -> dict[str, Any]:
+ """Build a read-only operator run package for the direct mapping backlog."""
+ summary = summarize_pchome_mapping_backlog(payload)
+ backlog = summary.get("backlog") or {}
+ direct_items = [
+ item
+ for item in backlog.get("top_needs_mapping") or []
+ if item.get("action_code") == "map_external_product"
+ ]
+ review_items = list(backlog.get("top_review_candidates") or [])
+ batch_size = max(1, min(int(batch_size or 5), 8))
+ selected_direct = direct_items[:batch_size]
+ selected_review = review_items[:batch_size]
+
+ if selected_direct:
+ result = "READY_FOR_OPERATOR_PREVIEW"
+ elif selected_review:
+ result = "REVIEW_CANDIDATES_ONLY"
+ else:
+ result = "NO_DIRECT_MAPPING_TARGETS"
+
+ return {
+ "policy": OPERATOR_PREVIEW_POLICY,
+ "result": result,
+ "success": bool(summary.get("success")),
+ "generated_at": summary.get("generated_at"),
+ "stats": summary.get("stats") or {},
+ "backlog": {
+ "direct_mapping_count": int(backlog.get("direct_mapping_count") or 0),
+ "review_candidate_count": int(backlog.get("review_candidate_count") or 0),
+ "mapped_opportunity_count": int(backlog.get("mapped_opportunity_count") or 0),
+ },
+ "operator_batch": {
+ "batch_size": batch_size,
+ "selected_direct_mapping_count": len(selected_direct),
+ "selected_review_candidate_count": len(selected_review),
+ "direct_mapping_targets": selected_direct,
+ "review_candidate_targets": selected_review,
+ },
+ "command_preview": {
+ "method": "POST",
+ "endpoint": "/api/ai/pchome-growth/backfill-momo-candidates",
+ "payload": {"limit": min(batch_size, 8)},
+ "executes_search": True,
+ "writes_database": True,
+ "write_gate_required": True,
+ },
+ "external_benchmark_alignment": _build_external_benchmark_alignment(),
+ "ai_automation_plan": _build_ai_automation_plan(selected_direct, selected_review),
+ "safety": {
+ "read_only_preview": True,
+ "executes_search": False,
+ "writes_database": False,
+ "dispatches_telegram": False,
+ "requires_production_version_truth": True,
+ "requires_operator_write_approval": True,
+ },
+ "required_before_execute": [
+ "Run production version truth guard and keep production /health as latest truth.",
+ "Confirm the selected direct mapping targets are the intended PChome products.",
+ "Confirm DB-write authorization for /api/ai/pchome-growth/backfill-momo-candidates.",
+ "Run post-write mapping backlog readback and compare direct_mapping_count / mapped_count.",
+ ],
+ "acceptance_criteria": [
+ "direct_mapping_count decreases, or review_candidate_count increases with named MOMO candidates.",
+ "mapped_count or mapping_rate increases only when a verified external price is written.",
+ "No Gemini, Telegram dispatch, scheduler mutation, or unrelated DB write is part of this run.",
+ ],
+ }
+
+
+def _build_direct_mapping_search_terms(product_name: str, max_terms: int) -> list[str]:
+ try:
+ from services.momo_crawler import build_targeted_momo_search_terms
+
+ return build_targeted_momo_search_terms(product_name, max_terms=max_terms)
+ except Exception:
+ fallback = re.sub(r"\s+", " ", product_name or "").strip()
+ return [fallback] if fallback else []
+
+
+def _build_direct_mapping_search_target(target: dict[str, Any], max_terms: int) -> dict[str, Any]:
+ evidence = target.get("evidence_completeness") or {}
+ unit_basis = evidence.get("unit_package_basis") or {}
+ risk_signals = list(unit_basis.get("risk_signals") or [])
+ search_terms = _build_direct_mapping_search_terms(target.get("product_name") or "", max_terms)
+ return {
+ "pchome_product_id": target.get("pchome_product_id") or "",
+ "product_name": target.get("product_name") or "",
+ "product_url": target.get("product_url") or evidence.get("product_url"),
+ "pchome_price": target.get("pchome_price"),
+ "sales_7d": target.get("sales_7d"),
+ "priority_score": target.get("priority_score"),
+ "search_terms": search_terms,
+ "search_term_count": len(search_terms),
+ "identity_anchors": {
+ "stable_product_id": bool(target.get("pchome_product_id")),
+ "product_name_present": bool(target.get("product_name")),
+ "product_url_present": bool(target.get("product_url") or evidence.get("product_url")),
+ "price_present": target.get("pchome_price") not in (None, ""),
+ "unit_basis_present": bool(unit_basis),
+ "unit_package_basis": unit_basis,
+ "risk_signals": risk_signals,
+ "variant_sensitive": "variant_selection" in risk_signals,
+ "bundle_or_promo_sensitive": "bundle_or_promo" in risk_signals,
+ },
+ "candidate_acceptance_gates": [
+ "target_pchome_product_id_matches",
+ "target_match_score_meets_min_score",
+ "target_hard_veto_is_false",
+ "auto_compare_type_is_total_price_or_unit_price_for_auto_persistence",
+ "manual_review_candidates_route_to_machine_verifiable_decision_package",
+ "no_database_write_from_search_package",
+ ],
+ "can_execute_read_only_search": bool(search_terms),
+ "writes_database": False,
+ "persists_candidate": False,
+ }
+
+
+def _search_candidates_by_target(candidates: list[dict[str, Any]]) -> dict[str, list[dict[str, Any]]]:
+ grouped: dict[str, list[dict[str, Any]]] = {}
+ for candidate in candidates:
+ target_id = str(candidate.get("target_pchome_product_id") or "").strip()
+ if not target_id:
+ target_id = "unknown"
+ grouped.setdefault(target_id, []).append(candidate)
+ return grouped
+
+
+def _is_truthy_flag(value: Any) -> bool:
+ if isinstance(value, bool):
+ return value
+ if value in (None, ""):
+ return False
+ if isinstance(value, (int, float)):
+ return bool(value)
+ return str(value).strip().lower() in {"1", "true", "yes", "y", "on"}
+
+
+def _build_candidate_decision_id(candidate: dict[str, Any]) -> str:
+ decision_basis = {
+ "target_pchome_product_id": candidate.get("target_pchome_product_id"),
+ "momo_product_id": candidate.get("product_id"),
+ "momo_name": candidate.get("name"),
+ "target_match_score": candidate.get("target_match_score"),
+ "auto_compare_type": candidate.get("auto_compare_type"),
+ }
+ digest = hashlib.sha256(
+ json.dumps(decision_basis, ensure_ascii=False, sort_keys=True, default=str).encode("utf-8")
+ ).hexdigest()
+ return f"pchome-direct-mapping-candidate-{digest[:16]}"
+
+
+def _build_candidate_decision_envelope(candidate: dict[str, Any], min_score: float) -> dict[str, Any]:
+ confidence = _to_float(candidate.get("target_match_score"))
+ auto_compare_type = str(candidate.get("auto_compare_type") or "").strip()
+ hard_veto = _is_truthy_flag(candidate.get("target_hard_veto"))
+ target_id = str(candidate.get("target_pchome_product_id") or "").strip()
+ momo_product_id = str(candidate.get("product_id") or "").strip()
+ can_route_to_receipt = (
+ bool(target_id)
+ and bool(momo_product_id)
+ and confidence >= min_score
+ and auto_compare_type in {"total_price", "unit_price"}
+ and not hard_veto
+ )
+ failure_reasons = []
+ if not target_id:
+ failure_reasons.append("missing_target_pchome_product_id")
+ if not momo_product_id:
+ failure_reasons.append("missing_momo_product_id")
+ if confidence < min_score:
+ failure_reasons.append("target_match_score_below_min_score")
+ if auto_compare_type not in {"total_price", "unit_price"}:
+ failure_reasons.append("auto_compare_type_not_receipt_ready")
+ if hard_veto:
+ failure_reasons.append("target_hard_veto_true")
+
+ decision = (
+ "route_to_no_write_auto_compare_receipt"
+ if can_route_to_receipt
+ else "route_to_machine_review_decision"
+ )
+ return {
+ "decision_id": _build_candidate_decision_id(candidate),
+ "decision_type": "direct_mapping_candidate_decision",
+ "stage": "P2_machine_verifiable_candidate_decision",
+ "subject": {
+ "target_pchome_product_id": target_id,
+ "pchome_product_name": candidate.get("target_pchome_name") or candidate.get("target_product_name"),
+ "momo_product_id": momo_product_id,
+ "momo_product_name": candidate.get("name"),
+ "momo_price": candidate.get("price"),
+ },
+ "decision": decision,
+ "confidence": confidence,
+ "data_quality": "ready_for_no_write_receipt" if can_route_to_receipt else "needs_machine_review",
+ "failure_reasons": failure_reasons,
+ "evidence": [
+ {"key": "target_match_score", "value": candidate.get("target_match_score")},
+ {"key": "min_score", "value": min_score},
+ {"key": "auto_compare_type", "value": auto_compare_type},
+ {"key": "target_hard_veto", "value": hard_veto},
+ {"key": "target_price_basis", "value": candidate.get("target_price_basis")},
+ {"key": "target_gap_pct", "value": candidate.get("target_gap_pct") or candidate.get("gap_pct")},
+ {"key": "target_search_term", "value": candidate.get("target_search_term") or candidate.get("search_term")},
+ ],
+ "recommended_action": (
+ "build_no_write_auto_compare_receipt"
+ if can_route_to_receipt
+ else "build_machine_review_exception_receipt"
+ ),
+ "expected_impact": "reduce_direct_mapping_backlog_after_verified_persistence",
+ "guardrails": {
+ "machine_actionable": True,
+ "can_auto_execute": False,
+ "writes_database": False,
+ "persists_candidate": False,
+ "requires_no_write_receipt": True,
+ "requires_verifier_before_persistence": True,
+ "manual_review_mode": "exception_only",
+ },
+ }
+
+
+def build_pchome_direct_mapping_auto_search_package(
+ payload: dict[str, Any],
+ batch_size: int = 5,
+ *,
+ execute_search: bool = False,
+ limit_per_product: int = 8,
+ max_terms_per_product: int = 5,
+ min_score: float = 0.45,
+ search_func: Any = None,
+) -> dict[str, Any]:
+ """Build a P1 no-write package for direct PChome-to-MOMO candidate search."""
+ operator_preview = build_pchome_mapping_operator_preview(payload, batch_size=batch_size)
+ operator_batch = operator_preview.get("operator_batch") or {}
+ direct_targets = list(operator_batch.get("direct_mapping_targets") or [])
+ batch_size = int(operator_batch.get("batch_size") or batch_size or 5)
+ max_terms_per_product = max(1, min(int(max_terms_per_product or 5), 8))
+ limit_per_product = max(1, min(int(limit_per_product or 8), 12))
+ try:
+ min_score = max(0.35, min(float(min_score), 0.95))
+ except (TypeError, ValueError):
+ min_score = 0.45
+
+ search_targets = [
+ _build_direct_mapping_search_target(target, max_terms=max_terms_per_product)
+ for target in direct_targets
+ ]
+ read_only_targets = [
+ {
+ "product_id": target.get("pchome_product_id"),
+ "name": target.get("product_name"),
+ "price": target.get("pchome_price"),
+ "sales_7d": target.get("sales_7d"),
+ "priority_score": target.get("priority_score"),
+ }
+ for target in direct_targets
+ ]
+
+ search_success = None
+ search_message = "search_not_executed"
+ candidates: list[dict[str, Any]] = []
+ if execute_search and read_only_targets:
+ if search_func is None:
+ from services.momo_crawler import search_momo_products_for_pchome_products
+
+ search_func = search_momo_products_for_pchome_products
+ search_success, search_message, candidates = search_func(
+ read_only_targets,
+ limit_per_product=limit_per_product,
+ max_products=batch_size,
+ max_terms_per_product=max_terms_per_product,
+ min_score=min_score,
+ )
+ candidates = list(candidates or [])
+
+ auto_candidates = [
+ candidate
+ for candidate in candidates
+ if candidate.get("auto_compare_type") in {"total_price", "unit_price"}
+ ]
+ review_candidates = [
+ candidate
+ for candidate in candidates
+ if candidate.get("auto_compare_type") not in {"total_price", "unit_price"}
+ ]
+ grouped_candidates = _search_candidates_by_target(candidates)
+ for target in search_targets:
+ target["candidate_count"] = len(grouped_candidates.get(target.get("pchome_product_id") or "", []))
+ target["candidate_ids"] = [
+ str(candidate.get("product_id") or "")
+ for candidate in grouped_candidates.get(target.get("pchome_product_id") or "", [])[:5]
+ ]
+
+ if not direct_targets:
+ result = "NO_DIRECT_MAPPING_TARGETS"
+ elif execute_search and candidates:
+ result = "DIRECT_MAPPING_CANDIDATES_FOUND"
+ elif execute_search:
+ result = "DIRECT_MAPPING_SEARCH_NO_CANDIDATES"
+ else:
+ result = "DIRECT_MAPPING_SEARCH_PACKAGE_READY"
+
+ return {
+ "policy": DIRECT_MAPPING_AUTO_SEARCH_PACKAGE_POLICY,
+ "result": result,
+ "success": bool(operator_preview.get("success")),
+ "generated_at": operator_preview.get("generated_at"),
+ "source_policy": operator_preview.get("policy"),
+ "stats": operator_preview.get("stats") or {},
+ "backlog": operator_preview.get("backlog") or {},
+ "summary": {
+ "direct_mapping_count": int((operator_preview.get("backlog") or {}).get("direct_mapping_count") or 0),
+ "selected_direct_mapping_count": len(search_targets),
+ "search_ready_target_count": sum(1 for target in search_targets if target.get("can_execute_read_only_search")),
+ "planned_search_term_count": sum(int(target.get("search_term_count") or 0) for target in search_targets),
+ "execute_search_count": 1 if execute_search else 0,
+ "candidates_found_count": len(candidates),
+ "auto_compare_candidate_count": len(auto_candidates),
+ "review_candidate_count": len(review_candidates),
+ "writes_database_count": 0,
+ "persists_candidate_count": 0,
+ },
+ "search_package": {
+ "stage": "P1_direct_mapping_auto_search",
+ "batch_size": batch_size,
+ "execute_search": bool(execute_search),
+ "limit_per_product": limit_per_product,
+ "max_terms_per_product": max_terms_per_product,
+ "min_score": min_score,
+ "targets": search_targets,
+ },
+ "search_execution": {
+ "executed": bool(execute_search),
+ "search_success": search_success,
+ "search_message": search_message,
+ "candidate_count": len(candidates),
+ "auto_compare_candidate_count": len(auto_candidates),
+ "review_candidate_count": len(review_candidates),
+ "writes_database": False,
+ "syncs_external_offers": False,
+ },
+ "candidate_acceptance_policy": {
+ "min_score": min_score,
+ "auto_compare_types": ["total_price", "unit_price"],
+ "requires_target_pchome_product_id": True,
+ "rejects_hard_veto": True,
+ "routes_manual_review_to_machine_verifiable_decision": True,
+ "writes_database": False,
+ },
+ "candidate_preview": candidates[:20],
+ "next_actions": [
+ "Execute controlled read-only MOMO search for the selected direct mapping targets.",
+ "Route auto-compare candidates into no-write receipt and verifier packages before persistence.",
+ "Route manual-review candidates into machine-verifiable candidate decision packages, not free-form human review.",
+ ],
+ "safety": {
+ "read_only_preview": True,
+ "executes_search": bool(execute_search),
+ "writes_database": False,
+ "persists_candidate": False,
+ "syncs_external_offers": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_preview": False,
+ "gemini_allowed": False,
+ "requires_production_version_truth": True,
+ },
+ }
+
+
+def build_pchome_direct_mapping_candidate_decision_package(
+ payload: dict[str, Any],
+ batch_size: int = 5,
+ *,
+ execute_search: bool = False,
+ limit_per_product: int = 8,
+ max_terms_per_product: int = 5,
+ min_score: float = 0.45,
+ search_func: Any = None,
+) -> dict[str, Any]:
+ """Build a P2 no-write machine-verifiable decision package for direct mapping candidates."""
+ search_package = build_pchome_direct_mapping_auto_search_package(
+ payload,
+ batch_size=batch_size,
+ execute_search=execute_search,
+ limit_per_product=limit_per_product,
+ max_terms_per_product=max_terms_per_product,
+ min_score=min_score,
+ search_func=search_func,
+ )
+ candidates = list(search_package.get("candidate_preview") or [])
+ effective_min_score = _to_float((search_package.get("candidate_acceptance_policy") or {}).get("min_score"))
+ decision_envelopes = [
+ _build_candidate_decision_envelope(candidate, min_score=effective_min_score)
+ for candidate in candidates
+ ]
+ auto_compare_decisions = [
+ envelope
+ for envelope in decision_envelopes
+ if envelope.get("decision") == "route_to_no_write_auto_compare_receipt"
+ ]
+ machine_review_decisions = [
+ envelope
+ for envelope in decision_envelopes
+ if envelope.get("decision") == "route_to_machine_review_decision"
+ ]
+
+ if not int((search_package.get("summary") or {}).get("selected_direct_mapping_count") or 0):
+ result = "NO_DIRECT_MAPPING_TARGETS"
+ elif decision_envelopes:
+ result = "DIRECT_MAPPING_CANDIDATE_DECISION_PACKAGE_READY"
+ else:
+ result = "WAITING_FOR_DIRECT_MAPPING_CANDIDATES"
+
+ return {
+ "policy": DIRECT_MAPPING_CANDIDATE_DECISION_PACKAGE_POLICY,
+ "result": result,
+ "success": bool(search_package.get("success")),
+ "generated_at": search_package.get("generated_at"),
+ "source_policy": search_package.get("policy"),
+ "stats": search_package.get("stats") or {},
+ "backlog": search_package.get("backlog") or {},
+ "summary": {
+ "direct_mapping_count": int((search_package.get("summary") or {}).get("direct_mapping_count") or 0),
+ "selected_direct_mapping_count": int(
+ (search_package.get("summary") or {}).get("selected_direct_mapping_count") or 0
+ ),
+ "candidates_found_count": len(candidates),
+ "candidate_decision_count": len(decision_envelopes),
+ "auto_compare_decision_count": len(auto_compare_decisions),
+ "machine_review_decision_count": len(machine_review_decisions),
+ "can_auto_persist_now_count": 0,
+ "writes_database_count": 0,
+ "persists_candidate_count": 0,
+ },
+ "decision_package": {
+ "stage": "P2_machine_verifiable_candidate_decision",
+ "execute_search": bool(execute_search),
+ "candidate_decisions": decision_envelopes,
+ "manual_review_mode": "exception_only",
+ },
+ "decision_acceptance_policy": {
+ "min_score": effective_min_score,
+ "auto_compare_types": ["total_price", "unit_price"],
+ "rejects_hard_veto": True,
+ "requires_target_pchome_product_id": True,
+ "requires_momo_product_id": True,
+ "routes_non_ready_candidates_to_machine_review_decision": True,
+ "writes_database": False,
+ },
+ "candidate_source_preview": candidates,
+ "upstream_search_summary": search_package.get("summary") or {},
+ "next_actions": [
+ "Run controlled read-only search first when candidate_decision_count is zero.",
+ "Send auto-compare decisions to no-write receipt generation before any persistence.",
+ "Keep machine-review decisions as exception receipts with named failure reasons.",
+ ],
+ "safety": {
+ "read_only_preview": True,
+ "executes_search": bool(execute_search),
+ "writes_database": False,
+ "persists_candidate": False,
+ "syncs_external_offers": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_preview": False,
+ "gemini_allowed": False,
+ "requires_production_version_truth": True,
+ },
+ }
+
+
+def build_pchome_evidence_enrichment_preview(payload: dict[str, Any], batch_size: int = 5) -> dict[str, Any]:
+ """Build a read-only evidence enrichment package for mapping targets."""
+ operator_preview = build_pchome_mapping_operator_preview(payload, batch_size=batch_size)
+ operator_batch = operator_preview.get("operator_batch") or {}
+ direct_targets = list(operator_batch.get("direct_mapping_targets") or [])
+ review_targets = list(operator_batch.get("review_candidate_targets") or [])
+ evidence_tasks = [
+ *[_build_evidence_task(target, "direct_mapping") for target in direct_targets],
+ *[_build_evidence_task(target, "review_candidate") for target in review_targets],
+ ]
+ tasks_with_blockers = [
+ task
+ for task in evidence_tasks
+ if task.get("blocking_missing_fields")
+ ]
+ missing_field_counts: dict[str, int] = {}
+ for task in evidence_tasks:
+ for field in task.get("missing_fields") or []:
+ missing_field_counts[field] = missing_field_counts.get(field, 0) + 1
+
+ if tasks_with_blockers:
+ result = "NEEDS_EVIDENCE_ENRICHMENT"
+ elif evidence_tasks:
+ result = "EVIDENCE_PREVIEW_READY"
+ else:
+ result = "NO_TARGETS"
+
+ return {
+ "policy": EVIDENCE_ENRICHMENT_PREVIEW_POLICY,
+ "result": result,
+ "success": bool(operator_preview.get("success")),
+ "generated_at": operator_preview.get("generated_at"),
+ "source_policy": operator_preview.get("policy"),
+ "stats": operator_preview.get("stats") or {},
+ "summary": {
+ "task_count": len(evidence_tasks),
+ "tasks_with_blockers": len(tasks_with_blockers),
+ "missing_field_counts": missing_field_counts,
+ "auto_accept_ready_count": sum(1 for task in evidence_tasks if task.get("auto_accept_ready")),
+ LEGACY_HUMAN_REVIEW_REQUIRED_COUNT_KEY: 0,
+ "primary_human_gate_count": 0,
+ "ai_exception_required_count": sum(1 for task in evidence_tasks if task.get("ai_exception_required")),
+ },
+ "evidence_tasks": evidence_tasks,
+ "external_benchmark_alignment": operator_preview.get("external_benchmark_alignment") or {},
+ "ai_automation_plan": {
+ "policy": "ollama_first_read_only_evidence_assist",
+ "llm_calls_in_preview": False,
+ "gemini_allowed": False,
+ "can_execute_write": False,
+ "recommended_next_ai_task": "Generate deterministic identity anchors and evidence gap summaries after image and availability sources are wired.",
+ "blocked_until": [
+ "image evidence source is wired",
+ "availability evidence source is wired",
+ "unit/package parser preview is compared against production title samples",
+ ],
+ },
+ "safety": {
+ "read_only_preview": True,
+ "fetches_external_sites": False,
+ "writes_database": False,
+ "executes_search": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_preview": False,
+ "requires_operator_write_approval": True,
+ },
+ "next_actions": [
+ "Wire read-only image and availability enrichment before expanding auto-accept.",
+ "Validate deterministic unit/package basis parsing for bundle-sensitive items.",
+ "Keep DB writes behind the existing /api/ai/pchome-growth/backfill-momo-candidates write gate.",
+ ],
+ }
+
+
+def build_pchome_evidence_source_preview(payload: dict[str, Any], batch_size: int = 5) -> dict[str, Any]:
+ """Build a read-only source wiring preview for missing product evidence fields."""
+ enrichment_preview = build_pchome_evidence_enrichment_preview(payload, batch_size=batch_size)
+ tasks = list(enrichment_preview.get("evidence_tasks") or [])
+ fields = ["image", "availability", "price", "unit_price_or_package_basis"]
+ field_counts = {}
+ source_plans = {}
+ for field in fields:
+ missing_tasks = [task for task in tasks if field in (task.get("missing_fields") or [])]
+ field_counts[field] = {
+ "missing_count": len(missing_tasks),
+ "present_count": max(len(tasks) - len(missing_tasks), 0),
+ "sample_missing_targets": [
+ {
+ "pchome_product_id": task.get("pchome_product_id") or "",
+ "product_name": task.get("product_name") or "",
+ "product_url": task.get("product_url"),
+ "lane": task.get("lane"),
+ }
+ for task in missing_tasks[:3]
+ ],
+ }
+ plan = _source_plan_for_field(field, len(missing_tasks))
+ if plan:
+ source_plans[field] = plan
+
+ fetch_gate_candidates = _build_fetch_gate_candidates(tasks)
+ if field_counts["image"]["missing_count"] or field_counts["availability"]["missing_count"]:
+ result = "NEEDS_SOURCE_WIRING"
+ elif field_counts["price"]["missing_count"]:
+ result = "NEEDS_PAYLOAD_MAPPING_PROBE"
+ elif tasks:
+ result = "SOURCE_PREVIEW_READY"
+ else:
+ result = "NO_TARGETS"
+
+ return {
+ "policy": EVIDENCE_SOURCE_PREVIEW_POLICY,
+ "result": result,
+ "success": bool(enrichment_preview.get("success")),
+ "generated_at": enrichment_preview.get("generated_at"),
+ "source_policy": enrichment_preview.get("policy"),
+ "stats": enrichment_preview.get("stats") or {},
+ "summary": {
+ "task_count": len(tasks),
+ "field_counts": field_counts,
+ "fetch_gate_candidate_count": len(fetch_gate_candidates),
+ },
+ "source_plans": source_plans,
+ "fetch_gate_candidates": fetch_gate_candidates,
+ "external_benchmark_alignment": enrichment_preview.get("external_benchmark_alignment") or {},
+ "ai_automation_plan": {
+ "policy": "ollama_first_read_only_source_wiring_assist",
+ "llm_calls_in_preview": False,
+ "gemini_allowed": False,
+ "can_execute_fetch": False,
+ "can_execute_write": False,
+ "recommended_next_ai_task": "Generate schema-aware parsers for image and availability after fetch-gate tests are accepted.",
+ },
+ "safety": {
+ "read_only_preview": True,
+ "fetches_external_sites": False,
+ "writes_database": False,
+ "executes_search": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_preview": False,
+ "requires_production_version_truth": True,
+ "requires_fetch_gate_implementation_before_external_get": True,
+ },
+ "next_actions": [
+ "Wire image source preview from existing payload keys before adding a controlled product-page fetch gate.",
+ "Wire availability source preview from existing payload keys before adding a controlled Offer availability parser.",
+ "Probe missing price rows through current payload mapping before any external fetch or write.",
+ ],
+ }
+
+
+def build_pchome_evidence_fetch_gate(
+ payload: dict[str, Any],
+ batch_size: int = 3,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ max_html_bytes: int = PCHOME_FETCH_MAX_HTML_BYTES,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Plan or execute a small read-only product-page evidence fetch gate."""
+ batch_size = max(1, min(int(batch_size or 3), PCHOME_FETCH_MAX_BATCH_SIZE))
+ timeout_seconds = max(1, min(int(timeout_seconds or PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS), 10))
+ max_html_bytes = max(16_384, min(int(max_html_bytes or PCHOME_FETCH_MAX_HTML_BYTES), PCHOME_FETCH_MAX_HTML_BYTES))
+ source_preview = build_pchome_evidence_source_preview(payload, batch_size=batch_size)
+ candidates = list(source_preview.get("fetch_gate_candidates") or [])[:batch_size]
+ receipts = []
+
+ for candidate in candidates:
+ product_url = candidate.get("product_url")
+ allowed = _is_allowed_pchome_product_url(product_url)
+ receipt = {
+ "pchome_product_id": candidate.get("pchome_product_id") or "",
+ "product_name": candidate.get("product_name") or "",
+ "product_url": product_url,
+ "fields": list(candidate.get("fields") or []),
+ "allowed_domain": PCHOME_FETCH_ALLOWED_DOMAIN,
+ "allowlist_passed": allowed,
+ "execute_fetch_requested": bool(execute_fetch),
+ "executed_fetch": False,
+ "writes_database": False,
+ "dispatches_telegram": False,
+ "llm_calls": False,
+ "parser_policy": PRODUCT_PAGE_EVIDENCE_PARSER_POLICY,
+ "status": "PLANNED",
+ }
+
+ if not allowed:
+ receipt["status"] = "BLOCKED_BY_ALLOWLIST"
+ receipt["error"] = "product_url must be a PChome 24h /prod/ URL"
+ receipts.append(receipt)
+ continue
+ if not execute_fetch:
+ receipts.append(receipt)
+ continue
+
+ try:
+ html, fetch_meta = _fetch_product_page_html(
+ product_url,
+ timeout_seconds=timeout_seconds,
+ max_html_bytes=max_html_bytes,
+ http_get=http_get,
+ )
+ parsed = parse_pchome_product_page_evidence_html(html, product_url=product_url)
+ receipt.update(
+ {
+ "status": "FETCHED_WITH_EVIDENCE"
+ if parsed.get("image_url") or parsed.get("availability")
+ else "FETCHED_NO_STRUCTURED_EVIDENCE",
+ "executed_fetch": True,
+ "fetch_meta": fetch_meta,
+ "parsed_evidence": {
+ "image_url": parsed.get("image_url"),
+ "availability": parsed.get("availability"),
+ "availability_raw": parsed.get("availability_raw"),
+ "jsonld_product_found": parsed.get("jsonld_product_found"),
+ "jsonld_offer_found": parsed.get("jsonld_offer_found"),
+ "fallbacks_used": parsed.get("fallbacks_used") or [],
+ "parser_warnings": parsed.get("parser_warnings") or [],
+ },
+ }
+ )
+ except (requests.RequestException, ValueError, UnicodeError) as exc:
+ receipt["status"] = "FETCH_FAILED"
+ receipt["error"] = str(exc)
+ receipts.append(receipt)
+
+ executed_count = sum(1 for receipt in receipts if receipt.get("executed_fetch"))
+ blocked_count = sum(1 for receipt in receipts if receipt.get("status") == "BLOCKED_BY_ALLOWLIST")
+ failed_count = sum(1 for receipt in receipts if receipt.get("status") == "FETCH_FAILED")
+ parsed_image_count = sum(1 for receipt in receipts if (receipt.get("parsed_evidence") or {}).get("image_url"))
+ parsed_availability_count = sum(1 for receipt in receipts if (receipt.get("parsed_evidence") or {}).get("availability"))
+
+ if not candidates:
+ result = "NO_FETCH_CANDIDATES"
+ elif not execute_fetch:
+ result = "FETCH_GATE_PLANNED"
+ elif parsed_image_count or parsed_availability_count:
+ result = "FETCH_GATE_EXECUTED_WITH_EVIDENCE"
+ elif failed_count or blocked_count:
+ result = "FETCH_GATE_EXECUTED_WITH_BLOCKERS"
+ else:
+ result = "FETCH_GATE_EXECUTED_NO_EVIDENCE"
+
+ return {
+ "policy": EVIDENCE_FETCH_GATE_POLICY,
+ "result": result,
+ "success": bool(source_preview.get("success")),
+ "generated_at": source_preview.get("generated_at"),
+ "source_policy": source_preview.get("policy"),
+ "stats": source_preview.get("stats") or {},
+ "summary": {
+ "candidate_count": len(candidates),
+ "receipt_count": len(receipts),
+ "executed_fetch_count": executed_count,
+ "blocked_count": blocked_count,
+ "failed_count": failed_count,
+ "parsed_image_count": parsed_image_count,
+ "parsed_availability_count": parsed_availability_count,
+ "max_batch_size": PCHOME_FETCH_MAX_BATCH_SIZE,
+ },
+ "fetch_config": {
+ "execute_fetch": bool(execute_fetch),
+ "allowed_domain": PCHOME_FETCH_ALLOWED_DOMAIN,
+ "timeout_seconds": timeout_seconds,
+ "max_html_bytes": max_html_bytes,
+ "method": "GET",
+ },
+ "fetch_receipts": receipts,
+ "source_preview_summary": source_preview.get("summary") or {},
+ "safety": {
+ "read_only_fetch_gate": True,
+ "writes_database": False,
+ "executes_search": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "requires_production_version_truth": True,
+ "requires_domain_allowlist": True,
+ "requires_html_size_cap": True,
+ "requires_operator_review_before_write": True,
+ },
+ "next_actions": [
+ "Review fetch receipts for image_url and availability before any write-gated mapping action.",
+ "Keep parsed evidence as preview evidence until an explicit evidence write path exists.",
+ "Investigate price payload gaps separately from product-page image and availability evidence.",
+ ],
+ }
+
+
+def _merge_task_with_fetch_receipt(task: dict[str, Any], receipt: dict[str, Any] | None) -> dict[str, Any]:
+ present_fields = set(task.get("present_fields") or [])
+ missing_fields = set(task.get("missing_fields") or [])
+ blocking_missing_fields = set(task.get("blocking_missing_fields") or [])
+ parsed = (receipt or {}).get("parsed_evidence") or {}
+ evidence_delta = {}
+
+ if parsed.get("image_url"):
+ present_fields.add("image")
+ missing_fields.discard("image")
+ blocking_missing_fields.discard("image")
+ evidence_delta["image_url"] = parsed.get("image_url")
+ if parsed.get("availability"):
+ present_fields.add("availability")
+ missing_fields.discard("availability")
+ blocking_missing_fields.discard("availability")
+ evidence_delta["availability"] = parsed.get("availability")
+
+ if not receipt:
+ merge_status = "NO_FETCH_RECEIPT"
+ elif receipt.get("status") == "FETCHED_WITH_EVIDENCE" and evidence_delta:
+ merge_status = "MERGE_PREVIEW_READY"
+ elif receipt.get("status") == "PLANNED":
+ merge_status = "FETCH_GATE_PLANNED"
+ elif receipt.get("status") == "BLOCKED_BY_ALLOWLIST":
+ merge_status = "FETCH_GATE_BLOCKED"
+ elif receipt.get("status") == "FETCH_FAILED":
+ merge_status = "FETCH_GATE_FAILED"
+ else:
+ merge_status = "NO_MERGEABLE_EVIDENCE"
+
+ if merge_status == "MERGE_PREVIEW_READY" and not blocking_missing_fields and evidence_delta:
+ automation_decision = "AUTO_ACCEPT_EVIDENCE_MERGE"
+ automation_allowed = True
+ exception_reason = None
+ elif merge_status == "FETCH_GATE_PLANNED":
+ automation_decision = "AUTO_RUN_FETCH_GATE"
+ automation_allowed = True
+ exception_reason = None
+ elif "price" in missing_fields:
+ automation_decision = "AUTO_PRICE_PAYLOAD_PROBE"
+ automation_allowed = True
+ exception_reason = "price_payload_gap"
+ elif merge_status in {"FETCH_GATE_BLOCKED", "FETCH_GATE_FAILED"}:
+ automation_decision = "AUTO_RETRY_OR_SOURCE_FALLBACK"
+ automation_allowed = True
+ exception_reason = merge_status.lower()
+ else:
+ automation_decision = "AUTO_CONTINUE_EVIDENCE_ENRICHMENT"
+ automation_allowed = True
+ exception_reason = None
+
+ return {
+ "pchome_product_id": task.get("pchome_product_id") or "",
+ "product_name": task.get("product_name") or "",
+ "product_url": task.get("product_url"),
+ "lane": task.get("lane"),
+ "merge_status": merge_status,
+ "original_missing_fields": list(task.get("missing_fields") or []),
+ "merged_present_fields": sorted(present_fields),
+ "remaining_missing_fields": sorted(missing_fields),
+ "remaining_blocking_missing_fields": sorted(blocking_missing_fields),
+ "evidence_delta": evidence_delta,
+ "fetch_receipt_status": (receipt or {}).get("status"),
+ "automation_decision": automation_decision,
+ "automation_allowed": automation_allowed,
+ **_legacy_review_compatibility_fields(bool(exception_reason)),
+ "automation_exception_reason": exception_reason,
+ "writes_database": False,
+ "requires_operator_review": False,
+ }
+
+
+def build_pchome_evidence_merge_preview(
+ payload: dict[str, Any],
+ batch_size: int = 3,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Preview how fetch receipts would change evidence completeness without writing."""
+ enrichment_preview = build_pchome_evidence_enrichment_preview(payload, batch_size=batch_size)
+ fetch_gate = build_pchome_evidence_fetch_gate(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ receipt_by_id = {
+ receipt.get("pchome_product_id"): receipt
+ for receipt in fetch_gate.get("fetch_receipts") or []
+ if receipt.get("pchome_product_id")
+ }
+ merge_items = [
+ _merge_task_with_fetch_receipt(task, receipt_by_id.get(task.get("pchome_product_id")))
+ for task in enrichment_preview.get("evidence_tasks") or []
+ ]
+ merge_ready_count = sum(1 for item in merge_items if item.get("merge_status") == "MERGE_PREVIEW_READY")
+ auto_merge_ready_count = sum(
+ 1 for item in merge_items if item.get("automation_decision") == "AUTO_ACCEPT_EVIDENCE_MERGE"
+ )
+ remaining_blocker_count = sum(1 for item in merge_items if item.get("remaining_blocking_missing_fields"))
+ automation_decision_counts: dict[str, int] = {}
+ for item in merge_items:
+ decision = item.get("automation_decision") or "UNKNOWN"
+ automation_decision_counts[decision] = automation_decision_counts.get(decision, 0) + 1
+
+ if merge_ready_count:
+ result = "MERGE_PREVIEW_READY"
+ elif fetch_gate.get("result") == "FETCH_GATE_PLANNED":
+ result = "FETCH_REQUIRED_FOR_MERGE_PREVIEW"
+ elif merge_items:
+ result = "NO_MERGEABLE_EVIDENCE"
+ else:
+ result = "NO_TARGETS"
+
+ return {
+ "policy": EVIDENCE_MERGE_PREVIEW_POLICY,
+ "result": result,
+ "success": bool(enrichment_preview.get("success")),
+ "generated_at": enrichment_preview.get("generated_at"),
+ "source_policy": fetch_gate.get("policy"),
+ "stats": enrichment_preview.get("stats") or {},
+ "summary": {
+ "task_count": len(merge_items),
+ "merge_ready_count": merge_ready_count,
+ "auto_merge_ready_count": auto_merge_ready_count,
+ "remaining_blocker_count": remaining_blocker_count,
+ "executed_fetch_count": (fetch_gate.get("summary") or {}).get("executed_fetch_count", 0),
+ "writes_database_count": 0,
+ AI_EXCEPTION_REQUIRED_COUNT_KEY: sum(1 for item in merge_items if item.get(AI_EXCEPTION_REQUIRED_KEY)),
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: 0,
+ LEGACY_REVIEW_MODE_KEY: LEGACY_REVIEW_MODE_EXCEPTION_ONLY,
+ "automation_decision_counts": automation_decision_counts,
+ },
+ "merge_items": merge_items,
+ "fetch_gate_summary": fetch_gate.get("summary") or {},
+ "safety": {
+ "read_only_merge_preview": True,
+ "writes_database": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_preview": False,
+ "requires_operator_review_before_write": False,
+ LEGACY_REVIEW_MODE_KEY: LEGACY_REVIEW_MODE_EXCEPTION_ONLY,
+ "requires_auto_policy_receipt_before_write": True,
+ },
+ "next_actions": [
+ "Auto-run fetch gate and merge preview for all eligible evidence candidates.",
+ "Keep price payload gaps in automated payload probes separate from image and availability evidence merge.",
+ "Persist evidence only through a future auto-policy receipt path, not by manual per-item review.",
+ ],
+ }
+
+
+def _auto_policy_receipt_id(item: dict[str, Any]) -> str:
+ receipt_material = {
+ "policy": AUTO_POLICY_RECEIPT_GATE_POLICY,
+ "pchome_product_id": item.get("pchome_product_id") or "",
+ "automation_decision": item.get("automation_decision") or "",
+ "evidence_delta": item.get("evidence_delta") or {},
+ "remaining_missing_fields": item.get("remaining_missing_fields") or [],
+ }
+ digest = hashlib.sha256(json.dumps(receipt_material, sort_keys=True, ensure_ascii=False).encode("utf-8")).hexdigest()
+ return f"pchome-evidence-{digest[:16]}"
+
+
+def _build_auto_policy_receipt(item: dict[str, Any]) -> dict[str, Any]:
+ decision = item.get("automation_decision") or "AUTO_CONTINUE_EVIDENCE_ENRICHMENT"
+ if decision == "AUTO_ACCEPT_EVIDENCE_MERGE":
+ receipt_status = "READY_FOR_AUTO_PERSISTENCE"
+ elif decision == "AUTO_RUN_FETCH_GATE":
+ receipt_status = "READY_FOR_AUTO_FETCH"
+ else:
+ receipt_status = "READY_FOR_AUTO_FOLLOWUP"
+ return {
+ "receipt_id": _auto_policy_receipt_id(item),
+ "pchome_product_id": item.get("pchome_product_id") or "",
+ "product_name": item.get("product_name") or "",
+ "product_url": item.get("product_url"),
+ "lane": item.get("lane"),
+ "receipt_status": receipt_status,
+ "automation_decision": decision,
+ "automation_allowed": bool(item.get("automation_allowed")),
+ **_legacy_review_compatibility_fields(bool(item.get(AI_EXCEPTION_REQUIRED_KEY))),
+ "evidence_delta": item.get("evidence_delta") or {},
+ "remaining_missing_fields": list(item.get("remaining_missing_fields") or []),
+ "remaining_blocking_missing_fields": list(item.get("remaining_blocking_missing_fields") or []),
+ "source_merge_status": item.get("merge_status"),
+ "source_fetch_receipt_status": item.get("fetch_receipt_status"),
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ }
+
+
+def build_pchome_auto_policy_receipt_gate(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build no-write auto-policy receipts from evidence automation decisions."""
+ merge_preview = build_pchome_evidence_merge_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ receipts = [_build_auto_policy_receipt(item) for item in merge_preview.get("merge_items") or []]
+ receipt_status_counts: dict[str, int] = {}
+ decision_counts: dict[str, int] = {}
+ for receipt in receipts:
+ status = receipt.get("receipt_status") or "UNKNOWN"
+ decision = receipt.get("automation_decision") or "UNKNOWN"
+ receipt_status_counts[status] = receipt_status_counts.get(status, 0) + 1
+ decision_counts[decision] = decision_counts.get(decision, 0) + 1
+
+ ready_for_auto_persistence_count = receipt_status_counts.get("READY_FOR_AUTO_PERSISTENCE", 0)
+ ai_exception_required_count = sum(1 for receipt in receipts if receipt.get(AI_EXCEPTION_REQUIRED_KEY))
+ if ready_for_auto_persistence_count:
+ result = "AUTO_POLICY_RECEIPTS_READY"
+ elif receipts:
+ result = "AUTO_POLICY_RECEIPTS_PLANNED"
+ else:
+ result = "NO_AUTO_POLICY_RECEIPTS"
+
+ return {
+ "policy": AUTO_POLICY_RECEIPT_GATE_POLICY,
+ "result": result,
+ "success": bool(merge_preview.get("success")),
+ "generated_at": merge_preview.get("generated_at"),
+ "source_policy": merge_preview.get("policy"),
+ "stats": merge_preview.get("stats") or {},
+ "summary": {
+ "receipt_count": len(receipts),
+ "ready_for_auto_persistence_count": ready_for_auto_persistence_count,
+ "ready_for_auto_fetch_count": receipt_status_counts.get("READY_FOR_AUTO_FETCH", 0),
+ "ready_for_auto_followup_count": receipt_status_counts.get("READY_FOR_AUTO_FOLLOWUP", 0),
+ AI_EXCEPTION_REQUIRED_COUNT_KEY: ai_exception_required_count,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: 0,
+ "writes_database_count": 0,
+ "persists_receipt_count": 0,
+ "receipt_status_counts": receipt_status_counts,
+ "automation_decision_counts": decision_counts,
+ LEGACY_REVIEW_MODE_KEY: LEGACY_REVIEW_MODE_EXCEPTION_ONLY,
+ },
+ "auto_policy_receipts": receipts,
+ "merge_preview_summary": merge_preview.get("summary") or {},
+ "persistence_gate": {
+ "mode": "no_write_receipt_preview",
+ "future_write_target": "evidence_receipt_store",
+ "requires_auto_policy_receipt": True,
+ "writes_database": False,
+ "persists_receipt": False,
+ },
+ "safety": {
+ "read_only_receipt_gate": True,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use ready auto-policy receipts as the input contract for a future controlled persistence path.",
+ "Keep no-write receipt UI separate from evidence persistence until a write gate exists.",
+ "Route only receipt exceptions to human review; do not require per-item review for ready receipts.",
+ ],
+ }
+
+
+def _automation_lane(
+ key: str,
+ label: str,
+ status: str,
+ value: int | float,
+ detail: str,
+ next_action: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "label": label,
+ "status": status,
+ "value": value,
+ "detail": detail,
+ "next_action": next_action,
+ "writes_database": False,
+ "ai_exception_mode": "machine_verifiable_auto_resolution",
+ }
+
+
+def build_pchome_growth_ai_automation_readiness(
+ payload: dict[str, Any],
+ batch_size: int = 8,
+ *,
+ execute_search: bool = False,
+ execute_fetch: bool = False,
+) -> dict[str, Any]:
+ """Build a single read-only product-facing AI automation readiness view."""
+ mapping_summary = summarize_pchome_mapping_backlog(payload)
+ search_package = build_pchome_direct_mapping_auto_search_package(
+ payload,
+ batch_size=batch_size,
+ execute_search=execute_search,
+ )
+ decision_package = build_pchome_direct_mapping_candidate_decision_package(
+ payload,
+ batch_size=batch_size,
+ execute_search=execute_search,
+ )
+ receipt_gate = build_pchome_auto_policy_receipt_gate(
+ payload,
+ batch_size=12,
+ execute_fetch=execute_fetch,
+ )
+ backlog = mapping_summary.get("backlog") or {}
+ search_summary = search_package.get("summary") or {}
+ decision_summary = decision_package.get("summary") or {}
+ receipt_summary = receipt_gate.get("summary") or {}
+
+ direct_mapping_count = int(backlog.get("direct_mapping_count") or 0)
+ selected_search_targets = int(search_summary.get("selected_direct_mapping_count") or 0)
+ planned_search_terms = int(search_summary.get("planned_search_term_count") or 0)
+ candidate_decision_count = int(decision_summary.get("candidate_decision_count") or 0)
+ waiting_candidate_count = selected_search_targets if not candidate_decision_count else 0
+ receipt_count = int(receipt_summary.get("receipt_count") or 0)
+ ready_receipt_count = int(receipt_summary.get("ready_for_auto_persistence_count") or 0)
+ exception_count = _summary_exception_count(receipt_summary) + int(
+ decision_summary.get("machine_review_decision_count") or 0
+ )
+ ai_exception_auto_resolution = {
+ "mode": AI_EXCEPTION_MODE_MACHINE_VERIFIABLE,
+ PRIMARY_HUMAN_GATE_COUNT_KEY: 0,
+ "ai_exception_count": exception_count,
+ "routes": [
+ {
+ "source": "candidate_decision_package",
+ "condition": "not_ready_for_no_write_receipt",
+ "auto_resolution": "build_failure_reasons_and_next_machine_action",
+ },
+ {
+ "source": "evidence_receipts",
+ "condition": "not_ready_for_auto_persistence",
+ "auto_resolution": "route_to_evidence_retry_or_verifier_blocker",
+ },
+ ],
+ "writes_database": False,
+ }
+
+ if not direct_mapping_count and ready_receipt_count:
+ result = "AI_AUTOMATION_READY_FOR_CONTROLLED_APPLY"
+ elif direct_mapping_count and selected_search_targets:
+ result = "AI_AUTOMATION_ACTIVE_WAITING_FOR_CANDIDATES"
+ elif receipt_count:
+ result = "AI_AUTOMATION_EVIDENCE_RECEIPTS_PLANNED"
+ else:
+ result = "AI_AUTOMATION_WAITING_FOR_GROWTH_INPUT"
+
+ automation_lanes = [
+ _automation_lane(
+ "gap_detection",
+ "缺口偵測",
+ "active",
+ direct_mapping_count,
+ f"mapping_rate={mapping_summary.get('stats', {}).get('mapping_rate')}%",
+ "優先補 direct mapping 缺口",
+ ),
+ _automation_lane(
+ "same_item_search_package",
+ "同款搜尋包",
+ "ready" if selected_search_targets else "waiting",
+ selected_search_targets,
+ f"{planned_search_terms} 組搜尋詞",
+ "執行 controlled read-only 搜尋候選",
+ ),
+ _automation_lane(
+ "candidate_decision_package",
+ "候選決策包",
+ "waiting" if waiting_candidate_count else ("ready" if candidate_decision_count else "planned"),
+ candidate_decision_count,
+ f"等待 {waiting_candidate_count} 筆候選" if waiting_candidate_count else "可輸出 decision envelope",
+ "將候選分流到 no-write receipt 或 AI 例外決策",
+ ),
+ _automation_lane(
+ "evidence_receipts",
+ "證據收據",
+ "ready" if receipt_count else "planned",
+ receipt_count,
+ f"可落地 {ready_receipt_count} · 例外 {exception_count}",
+ "只把 ready receipt 送入 verifier / dry-run persistence",
+ ),
+ _automation_lane(
+ "controlled_apply",
+ "受控落地",
+ "blocked_until_verifier",
+ 0,
+ "等待 verifier、rollback、readback",
+ "P1-P4 穩定後才進 P5/P6",
+ ),
+ ]
+
+ return {
+ "policy": AI_AUTOMATION_READINESS_POLICY,
+ "result": result,
+ "success": bool(mapping_summary.get("success")),
+ "generated_at": mapping_summary.get("generated_at"),
+ "summary": {
+ "direct_mapping_count": direct_mapping_count,
+ "selected_search_target_count": selected_search_targets,
+ "planned_search_term_count": planned_search_terms,
+ "candidate_decision_count": candidate_decision_count,
+ "waiting_candidate_count": waiting_candidate_count,
+ "auto_compare_decision_count": int(decision_summary.get("auto_compare_decision_count") or 0),
+ "machine_review_decision_count": int(decision_summary.get("machine_review_decision_count") or 0),
+ "receipt_count": receipt_count,
+ "ready_receipt_count": ready_receipt_count,
+ "exception_count": exception_count,
+ "ai_exception_count": exception_count,
+ AI_EXCEPTION_REQUIRED_COUNT_KEY: exception_count,
+ PRIMARY_HUMAN_GATE_COUNT_KEY: 0,
+ LEGACY_PRIMARY_FLOW_COUNT_KEY: 0,
+ "writes_database_count": 0,
+ "external_network_execute_count": int(bool(execute_search)) + int(bool(execute_fetch)),
+ },
+ "automation_lanes": automation_lanes,
+ "automation_policy": {
+ "primary_flow": "ai_controlled",
+ "human_primary_flow": False,
+ PRIMARY_HUMAN_GATE_COUNT_KEY: 0,
+ "exception_resolution": "ai_machine_verifiable",
+ "machine_verifiable_decision_required": True,
+ },
+ "ai_exception_auto_resolution": ai_exception_auto_resolution,
+ "manual_policy": {
+ "deprecated_product_surface": True,
+ LEGACY_REVIEW_MODE_KEY: LEGACY_REVIEW_MODE_EXCEPTION_ONLY,
+ "manual_as_primary_flow": False,
+ "machine_verifiable_decision_required": True,
+ },
+ "visible_product_commitment": [
+ "AI 自動化狀態必須顯示在首頁 command center,不只藏在 API。",
+ "候選先進 decision envelope,再進 no-write receipt,不走自由格式審核。",
+ "例外也必須由 AI 自動產生 failure reasons、下一個機器動作與 rollback/readback 路徑。",
+ ],
+ "safety": {
+ "read_only_preview": True,
+ "executes_search": bool(execute_search),
+ "executes_fetch": bool(execute_fetch),
+ "writes_database": False,
+ "persists_receipt": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_preview": False,
+ "gemini_allowed": False,
+ },
+ }
+
+
+def _receipt_payload_hash(receipt: dict[str, Any]) -> str:
+ payload = {
+ "receipt_id": receipt.get("receipt_id") or "",
+ "pchome_product_id": receipt.get("pchome_product_id") or "",
+ "automation_decision": receipt.get("automation_decision") or "",
+ "evidence_delta": receipt.get("evidence_delta") or {},
+ }
+ return hashlib.sha256(json.dumps(payload, sort_keys=True, ensure_ascii=False).encode("utf-8")).hexdigest()
+
+
+def _build_persistence_preview_item(receipt: dict[str, Any]) -> dict[str, Any]:
+ ready = receipt.get("receipt_status") == "READY_FOR_AUTO_PERSISTENCE"
+ payload_hash = _receipt_payload_hash(receipt)
+ target_receipt_table = "external_offer_evidence_receipts"
+ target_evidence_table = "external_offers"
+ if ready:
+ persistence_status = "DRY_RUN_READY"
+ planned_operation = "UPSERT_EVIDENCE_RECEIPT_AND_PATCH_EXTERNAL_OFFER_EVIDENCE"
+ else:
+ persistence_status = "WAITING_FOR_READY_RECEIPT"
+ planned_operation = "NOOP"
+
+ return {
+ "receipt_id": receipt.get("receipt_id") or "",
+ "pchome_product_id": receipt.get("pchome_product_id") or "",
+ "product_url": receipt.get("product_url"),
+ "persistence_status": persistence_status,
+ "planned_operation": planned_operation,
+ "idempotency_key": receipt.get("receipt_id") or "",
+ "payload_hash": payload_hash,
+ "dedupe_keys": [
+ "receipt_id",
+ "pchome_product_id",
+ "payload_hash",
+ ],
+ "target_tables": [
+ target_receipt_table,
+ target_evidence_table,
+ ],
+ "transaction_preview": {
+ "begin_transaction": True,
+ "steps": [
+ {
+ "name": "upsert_auto_policy_receipt",
+ "target_table": target_receipt_table,
+ "write_mode": "upsert_by_receipt_id",
+ "writes_database_in_preview": False,
+ },
+ {
+ "name": "patch_external_offer_evidence",
+ "target_table": target_evidence_table,
+ "write_mode": "patch_image_url_stock_status_raw_payload",
+ "writes_database_in_preview": False,
+ },
+ {
+ "name": "post_write_readback",
+ "target_table": target_receipt_table,
+ "readback_key": receipt.get("receipt_id") or "",
+ "writes_database_in_preview": False,
+ },
+ ],
+ "commit": "future_apply_gate_only",
+ },
+ "parameter_preview": {
+ "receipt_id": receipt.get("receipt_id") or "",
+ "pchome_product_id": receipt.get("pchome_product_id") or "",
+ "automation_decision": receipt.get("automation_decision") or "",
+ "image_url_present": bool((receipt.get("evidence_delta") or {}).get("image_url")),
+ "availability": (receipt.get("evidence_delta") or {}).get("availability"),
+ "payload_hash": payload_hash,
+ },
+ "rollback_plan": [
+ "delete auto_policy receipt row by receipt_id if inserted in future apply gate",
+ "restore prior external_offers image_url / stock_status / raw_payload_json from prewrite snapshot",
+ "rerun mapping backlog and evidence source readback after rollback",
+ ],
+ "post_write_verifier": [
+ "receipt_id exists exactly once",
+ "payload_hash matches receipt evidence_delta",
+ "external_offers pchome_product_id has image_url and stock_status when receipt delta provided them",
+ f"mapping backlog readback does not increase {LEGACY_REVIEW_REQUIRED_COUNT_KEY}",
+ ],
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ }
+
+
+def build_pchome_auto_policy_persistence_gate(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write persistence transaction contract from ready auto-policy receipts."""
+ receipt_gate = build_pchome_auto_policy_receipt_gate(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ persistence_items = [
+ _build_persistence_preview_item(receipt)
+ for receipt in receipt_gate.get("auto_policy_receipts") or []
+ ]
+ dry_run_ready_count = sum(1 for item in persistence_items if item.get("persistence_status") == "DRY_RUN_READY")
+ waiting_count = sum(1 for item in persistence_items if item.get("persistence_status") == "WAITING_FOR_READY_RECEIPT")
+
+ if dry_run_ready_count:
+ result = "PERSISTENCE_DRY_RUN_READY"
+ elif persistence_items:
+ result = "PERSISTENCE_WAITING_FOR_RECEIPTS"
+ else:
+ result = "NO_PERSISTENCE_ITEMS"
+
+ return {
+ "policy": AUTO_POLICY_PERSISTENCE_GATE_POLICY,
+ "result": result,
+ "success": bool(receipt_gate.get("success")),
+ "generated_at": receipt_gate.get("generated_at"),
+ "source_policy": receipt_gate.get("policy"),
+ "stats": receipt_gate.get("stats") or {},
+ "summary": {
+ "persistence_item_count": len(persistence_items),
+ "dry_run_ready_count": dry_run_ready_count,
+ "waiting_for_receipt_count": waiting_count,
+ "writes_database_count": 0,
+ "persists_receipt_count": 0,
+ "updates_mapping_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (receipt_gate.get("summary") or {}).get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "persistence_items": persistence_items,
+ "receipt_gate_summary": receipt_gate.get("summary") or {},
+ "schema_contract": {
+ "target_receipt_table": "external_offer_evidence_receipts",
+ "requires_schema_migration_before_apply": True,
+ "required_columns": [
+ "receipt_id",
+ "pchome_product_id",
+ "automation_decision",
+ "payload_hash",
+ "evidence_delta_json",
+ "created_at",
+ "applied_at",
+ "apply_status",
+ ],
+ "unique_keys": ["receipt_id"],
+ },
+ "apply_gate": {
+ "mode": "dry_run_only",
+ "future_apply_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-persistence-apply",
+ "requires_production_version_truth": True,
+ "requires_prewrite_snapshot": True,
+ "requires_post_write_readback": True,
+ "writes_database": False,
+ },
+ "safety": {
+ "read_only_persistence_gate": True,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Create schema migration preview for external_offer_evidence_receipts before any apply endpoint.",
+ "Add prewrite snapshot and post-write readback verifier before enabling persistence apply.",
+ "Keep ready receipts automated; route only verifier failures to exception review.",
+ ],
+ }
+
+
+def _schema_migration_preview_id(schema_contract: dict[str, Any]) -> str:
+ payload = {
+ "target_receipt_table": schema_contract.get("target_receipt_table") or "",
+ "required_columns": schema_contract.get("required_columns") or [],
+ "unique_keys": schema_contract.get("unique_keys") or [],
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-schema-preview-{digest[:16]}"
+
+
+def _build_schema_migration_preview(schema_contract: dict[str, Any]) -> dict[str, Any]:
+ target_table = schema_contract.get("target_receipt_table") or "external_offer_evidence_receipts"
+ migration_id = _schema_migration_preview_id(schema_contract)
+ ddl_preview = [
+ f"CREATE TABLE IF NOT EXISTS {target_table} (",
+ " receipt_id TEXT PRIMARY KEY,",
+ " pchome_product_id TEXT NOT NULL,",
+ " automation_decision TEXT NOT NULL,",
+ " payload_hash TEXT NOT NULL,",
+ " evidence_delta_json JSONB NOT NULL DEFAULT '{}'::jsonb,",
+ " created_at TIMESTAMPTZ NOT NULL DEFAULT now(),",
+ " applied_at TIMESTAMPTZ,",
+ " apply_status TEXT NOT NULL DEFAULT 'previewed'",
+ ");",
+ (
+ f"CREATE INDEX IF NOT EXISTS idx_{target_table}_pchome_product_id "
+ f"ON {target_table} (pchome_product_id);"
+ ),
+ (
+ f"CREATE INDEX IF NOT EXISTS idx_{target_table}_payload_hash "
+ f"ON {target_table} (payload_hash);"
+ ),
+ (
+ f"CREATE INDEX IF NOT EXISTS idx_{target_table}_apply_status "
+ f"ON {target_table} (apply_status);"
+ ),
+ ]
+ rollback_preview = [
+ f"DROP INDEX IF EXISTS idx_{target_table}_apply_status;",
+ f"DROP INDEX IF EXISTS idx_{target_table}_payload_hash;",
+ f"DROP INDEX IF EXISTS idx_{target_table}_pchome_product_id;",
+ f"DROP TABLE IF EXISTS {target_table};",
+ ]
+ return {
+ "migration_id": migration_id,
+ "target_table": target_table,
+ "migration_mode": "future_migration_only",
+ "statement_count": len(ddl_preview),
+ "ddl_preview": ddl_preview,
+ "rollback_preview": rollback_preview,
+ "executes_sql": False,
+ "writes_database": False,
+ "requires_backup_before_apply": True,
+ "requires_migration_smoke": True,
+ }
+
+
+def _build_prewrite_snapshot_contract(persistence_items: list[dict[str, Any]]) -> dict[str, Any]:
+ receipt_ids = [item.get("receipt_id") for item in persistence_items if item.get("receipt_id")]
+ product_ids = [item.get("pchome_product_id") for item in persistence_items if item.get("pchome_product_id")]
+ return {
+ "required": True,
+ "snapshot_mode": "future_apply_gate_only",
+ "artifact_path_template": "artifacts/pchome_growth/prewrite_snapshot/{run_id}.json",
+ "dedupe_keys": ["receipt_id", "pchome_product_id", "payload_hash"],
+ "target_receipt_ids": receipt_ids,
+ "target_pchome_product_ids": product_ids,
+ "query_preview": [
+ {
+ "name": "receipt_prewrite_snapshot",
+ "sql": (
+ "SELECT receipt_id, pchome_product_id, payload_hash, apply_status "
+ "FROM external_offer_evidence_receipts WHERE receipt_id = ANY(:receipt_ids)"
+ ),
+ "params": {"receipt_ids": receipt_ids},
+ "executes_in_preview": False,
+ },
+ {
+ "name": "external_offer_prewrite_snapshot",
+ "sql": (
+ "SELECT pchome_product_id, image_url, stock_status, raw_payload_json "
+ "FROM external_offers WHERE pchome_product_id = ANY(:pchome_product_ids)"
+ ),
+ "params": {"pchome_product_ids": product_ids},
+ "executes_in_preview": False,
+ },
+ ],
+ "writes_database": False,
+ "executes_sql": False,
+ }
+
+
+def _build_future_apply_verifier(persistence_items: list[dict[str, Any]]) -> dict[str, Any]:
+ receipt_ids = [item.get("receipt_id") for item in persistence_items if item.get("receipt_id")]
+ product_ids = [item.get("pchome_product_id") for item in persistence_items if item.get("pchome_product_id")]
+ return {
+ "mode": "future_apply_gate_only",
+ "required": True,
+ "verifier_count": 5,
+ "checks": [
+ {
+ "name": "receipt_rows_are_idempotent",
+ "expected": "one row per receipt_id",
+ "query_preview": (
+ "SELECT receipt_id, count(*) FROM external_offer_evidence_receipts "
+ "WHERE receipt_id = ANY(:receipt_ids) GROUP BY receipt_id"
+ ),
+ "params": {"receipt_ids": receipt_ids},
+ "routes_failure_to": "exception_review",
+ },
+ {
+ "name": "payload_hash_matches",
+ "expected": "stored payload_hash equals dry-run payload_hash",
+ "query_preview": (
+ "SELECT receipt_id, payload_hash FROM external_offer_evidence_receipts "
+ "WHERE receipt_id = ANY(:receipt_ids)"
+ ),
+ "params": {"receipt_ids": receipt_ids},
+ "routes_failure_to": "exception_review",
+ },
+ {
+ "name": "external_offer_evidence_is_visible",
+ "expected": "image_url and stock_status are present when receipt delta provided them",
+ "query_preview": (
+ "SELECT pchome_product_id, image_url, stock_status FROM external_offers "
+ "WHERE pchome_product_id = ANY(:pchome_product_ids)"
+ ),
+ "params": {"pchome_product_ids": product_ids},
+ "routes_failure_to": "exception_review",
+ },
+ {
+ "name": "mapping_backlog_does_not_regress",
+ "expected": f"{LEGACY_REVIEW_REQUIRED_COUNT_KEY} stays zero for ready receipts",
+ "query_preview": "rerun mapping backlog read-only service after future apply",
+ "params": {},
+ "routes_failure_to": "exception_review",
+ },
+ {
+ "name": "rollback_can_restore_snapshot",
+ "expected": "prewrite snapshot contains all touched receipt and product ids",
+ "query_preview": "validate snapshot artifact before commit in future apply gate",
+ "params": {},
+ "routes_failure_to": "abort_apply_before_commit",
+ },
+ ],
+ "executes_in_preview": False,
+ "writes_database": False,
+ "manual_review_mode": "exception_only",
+ }
+
+
+def build_pchome_auto_policy_schema_migration_preview(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write schema migration and future apply verifier contract."""
+ persistence_gate = build_pchome_auto_policy_persistence_gate(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ persistence_items = list(persistence_gate.get("persistence_items") or [])
+ dry_run_items = [item for item in persistence_items if item.get("persistence_status") == "DRY_RUN_READY"]
+ waiting_items = [
+ item for item in persistence_items
+ if item.get("persistence_status") == "WAITING_FOR_READY_RECEIPT"
+ ]
+ schema_contract = persistence_gate.get("schema_contract") or {}
+ schema_migration_preview = _build_schema_migration_preview(schema_contract)
+ prewrite_snapshot_contract = _build_prewrite_snapshot_contract(dry_run_items)
+ future_apply_verifier = _build_future_apply_verifier(dry_run_items)
+
+ if dry_run_items:
+ future_apply_status = "APPLY_CONTRACT_READY"
+ elif persistence_items:
+ future_apply_status = "WAITING_FOR_DRY_RUN_READY_ITEMS"
+ else:
+ future_apply_status = "NO_PERSISTENCE_ITEMS"
+
+ return {
+ "policy": AUTO_POLICY_SCHEMA_MIGRATION_PREVIEW_POLICY,
+ "result": "SCHEMA_MIGRATION_PREVIEW_READY",
+ "success": bool(persistence_gate.get("success")),
+ "generated_at": persistence_gate.get("generated_at"),
+ "source_policy": persistence_gate.get("policy"),
+ "stats": persistence_gate.get("stats") or {},
+ "summary": {
+ "persistence_item_count": len(persistence_items),
+ "dry_run_ready_count": len(dry_run_items),
+ "waiting_for_receipt_count": len(waiting_items),
+ "schema_statement_count": schema_migration_preview["statement_count"],
+ "future_verifier_count": future_apply_verifier["verifier_count"],
+ "executes_migration_count": 0,
+ "writes_database_count": 0,
+ "persists_receipt_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (persistence_gate.get("summary") or {}).get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "schema_migration_preview": schema_migration_preview,
+ "prewrite_snapshot_contract": prewrite_snapshot_contract,
+ "future_apply_verifier": future_apply_verifier,
+ "future_apply_gate": {
+ "status": future_apply_status,
+ "mode": "future_controlled_apply_only",
+ "future_apply_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-persistence-apply",
+ "requires_production_version_truth": True,
+ "requires_schema_migration_applied": True,
+ "requires_prewrite_snapshot": True,
+ "requires_post_write_readback": True,
+ "current_preview_apply_allowed": False,
+ "writes_database": False,
+ "executes_migration": False,
+ },
+ "source_persistence_summary": persistence_gate.get("summary") or {},
+ "external_benchmark_references": EXTERNAL_BENCHMARK_REFERENCES,
+ "safety": {
+ "read_only_schema_preview": True,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Create a migration file only after this preview is accepted by tests and production version truth.",
+ "Keep prewrite snapshot and post-write verifier mandatory for the future apply endpoint.",
+ "Route only verifier failures to exception review; ready receipts remain automated.",
+ ],
+ }
+
+
+def _build_migration_file_preview(schema_migration_preview: dict[str, Any]) -> dict[str, Any]:
+ target_table = schema_migration_preview.get("target_table") or "external_offer_evidence_receipts"
+ migration_number = "045"
+ migration_filename = f"migrations/{migration_number}_pchome_auto_policy_evidence_receipts.sql"
+ header = [
+ "-- =============================================================================",
+ "-- Migration 045: PChome auto-policy evidence receipts",
+ "-- MOMO PRO / PChome revenue growth automation",
+ "-- 2026-06-28 Taipei",
+ "-- =============================================================================",
+ "-- Notes:",
+ "-- Additive only. This migration creates a receipt ledger for controlled",
+ "-- PChome evidence persistence. It does not drop, truncate, rewrite, or",
+ "-- backfill external_offers / competitor_* / product sales tables.",
+ "-- =============================================================================",
+ "",
+ ]
+ forward_sql_preview = (
+ header
+ + list(schema_migration_preview.get("ddl_preview") or [])
+ + [
+ "",
+ f"GRANT ALL PRIVILEGES ON {target_table} TO momo;",
+ "",
+ "DO $$",
+ "BEGIN",
+ " RAISE NOTICE 'Migration 045 complete: PChome auto-policy evidence receipts are ready';",
+ "END $$;",
+ ]
+ )
+ forward_text = "\n".join(forward_sql_preview) + "\n"
+ rollback_sql_preview = [
+ "-- Rollback preview only. Run only through an explicit future rollback gate.",
+ *list(schema_migration_preview.get("rollback_preview") or []),
+ ]
+ return {
+ "migration_number": migration_number,
+ "migration_filename": migration_filename,
+ "migration_title": "PChome auto-policy evidence receipts",
+ "target_table": target_table,
+ "file_write_mode": "preview_only",
+ "forward_sql_preview": forward_sql_preview,
+ "rollback_sql_preview": rollback_sql_preview,
+ "forward_sql_hash": hashlib.sha256(forward_text.encode("utf-8")).hexdigest(),
+ "line_count": len(forward_sql_preview),
+ "content_ends_with_newline": True,
+ "additive_only": True,
+ "forbidden_forward_tokens_absent": not any(
+ token in forward_text.upper()
+ for token in ["DROP ", "TRUNCATE ", "DELETE ", "ALTER TABLE external_offers"]
+ ),
+ "writes_file": False,
+ "executes_sql": False,
+ "writes_database": False,
+ }
+
+
+def _build_future_apply_endpoint_verifier(
+ *,
+ schema_preview: dict[str, Any],
+ migration_file_preview: dict[str, Any],
+) -> dict[str, Any]:
+ future_apply_gate = schema_preview.get("future_apply_gate") or {}
+ prewrite_snapshot = schema_preview.get("prewrite_snapshot_contract") or {}
+ future_verifier = schema_preview.get("future_apply_verifier") or {}
+ receipt_ids = list(prewrite_snapshot.get("target_receipt_ids") or [])
+ product_ids = list(prewrite_snapshot.get("target_pchome_product_ids") or [])
+ contract_ready = (
+ future_apply_gate.get("status") == "APPLY_CONTRACT_READY"
+ and bool(receipt_ids)
+ and bool(product_ids)
+ and migration_file_preview.get("forbidden_forward_tokens_absent") is True
+ )
+ return {
+ "endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-persistence-apply",
+ "method": "POST",
+ "contract_status": "APPLY_ENDPOINT_CONTRACT_READY" if contract_ready else "WAITING_FOR_APPLY_INPUTS",
+ "request_contract": {
+ "required_fields": [
+ "run_id",
+ "production_version_truth",
+ "migration_filename",
+ "migration_hash",
+ "prewrite_snapshot_artifact",
+ "receipt_ids",
+ "payload_hashes",
+ "post_write_readback_required",
+ "rollback_artifact_required",
+ ],
+ "receipt_count": len(receipt_ids),
+ "product_count": len(product_ids),
+ "target_receipt_ids": receipt_ids,
+ "target_pchome_product_ids": product_ids,
+ "expected_migration_filename": migration_file_preview.get("migration_filename"),
+ "expected_migration_hash": migration_file_preview.get("forward_sql_hash"),
+ },
+ "preflight_sequence": [
+ "verify production /health version truth before opening a DB transaction",
+ "verify migration file hash equals migration file preview hash",
+ "create prewrite snapshot artifact for receipt and external offer rows",
+ "upsert auto-policy receipt rows by receipt_id",
+ "patch external_offers evidence fields from ready receipt deltas",
+ "run post-write verifier checks before marking apply_status applied",
+ ],
+ "abort_conditions": [
+ "production version truth fails",
+ "migration hash mismatch",
+ "prewrite snapshot missing any target receipt or product id",
+ "post-write readback mismatch",
+ f"{LEGACY_REVIEW_REQUIRED_COUNT_KEY} increases above zero",
+ ],
+ "post_write_verifier_contract": future_verifier,
+ "rollback_contract": {
+ "required": True,
+ "mode": "future_rollback_gate_only",
+ "uses_prewrite_snapshot": True,
+ "target_receipt_ids": receipt_ids,
+ "target_pchome_product_ids": product_ids,
+ "steps": [
+ "restore external_offers image_url / stock_status / raw_payload_json from prewrite snapshot",
+ "mark receipt apply_status as rolled_back by receipt_id",
+ "rerun mapping backlog read-only summary and verify no regression",
+ ],
+ "executes_in_preview": False,
+ "writes_database": False,
+ },
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "manual_review_mode": "exception_only",
+ }
+
+
+def build_pchome_auto_policy_migration_file_preview(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write migration file preview and future apply endpoint contract."""
+ schema_preview = build_pchome_auto_policy_schema_migration_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ migration_file_preview = _build_migration_file_preview(
+ schema_preview.get("schema_migration_preview") or {}
+ )
+ future_apply_endpoint_verifier = _build_future_apply_endpoint_verifier(
+ schema_preview=schema_preview,
+ migration_file_preview=migration_file_preview,
+ )
+ contract_ready = (
+ future_apply_endpoint_verifier.get("contract_status") == "APPLY_ENDPOINT_CONTRACT_READY"
+ )
+ return {
+ "policy": AUTO_POLICY_MIGRATION_FILE_PREVIEW_POLICY,
+ "result": "MIGRATION_FILE_PREVIEW_READY",
+ "success": bool(schema_preview.get("success")),
+ "generated_at": schema_preview.get("generated_at"),
+ "source_policy": schema_preview.get("policy"),
+ "stats": schema_preview.get("stats") or {},
+ "summary": {
+ "persistence_item_count": (schema_preview.get("summary") or {}).get("persistence_item_count", 0),
+ "dry_run_ready_count": (schema_preview.get("summary") or {}).get("dry_run_ready_count", 0),
+ "schema_statement_count": (schema_preview.get("summary") or {}).get("schema_statement_count", 0),
+ "future_verifier_count": (schema_preview.get("summary") or {}).get("future_verifier_count", 0),
+ "migration_file_line_count": migration_file_preview.get("line_count", 0),
+ "apply_endpoint_contract_ready_count": 1 if contract_ready else 0,
+ "writes_file_count": 0,
+ "executes_migration_count": 0,
+ "executes_endpoint_count": 0,
+ "writes_database_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (schema_preview.get("summary") or {}).get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "migration_file_preview": migration_file_preview,
+ "future_apply_endpoint_verifier": future_apply_endpoint_verifier,
+ "source_schema_preview_summary": schema_preview.get("summary") or {},
+ "future_apply_gate": schema_preview.get("future_apply_gate") or {},
+ "safety": {
+ "read_only_migration_file_preview": True,
+ "writes_file": False,
+ "executes_endpoint": False,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Promote this preview into a real migration file only after explicit migration-file apply approval.",
+ "Keep the future apply endpoint blocked until migration hash, snapshot, and readback contracts are present.",
+ "Route only apply verifier failures to exception review; ready receipts remain automated.",
+ ],
+ }
+
+
+def _readiness_check(key: str, passed: bool, evidence: Any, failure_route: str) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _build_apply_readiness_checks(migration_preview: dict[str, Any]) -> list[dict[str, Any]]:
+ summary = migration_preview.get("summary") or {}
+ file_preview = migration_preview.get("migration_file_preview") or {}
+ endpoint = migration_preview.get("future_apply_endpoint_verifier") or {}
+ request_contract = endpoint.get("request_contract") or {}
+ rollback_contract = endpoint.get("rollback_contract") or {}
+ post_write_verifier = endpoint.get("post_write_verifier_contract") or {}
+ safety = migration_preview.get("safety") or {}
+ return [
+ _readiness_check(
+ "production_version_truth_required",
+ True,
+ "production /health must pass immediately before future file generation and future apply",
+ "abort_before_file_generation",
+ ),
+ _readiness_check(
+ "ready_receipts_present",
+ int(summary.get("dry_run_ready_count") or 0) > 0,
+ {"dry_run_ready_count": summary.get("dry_run_ready_count", 0)},
+ "wait_for_ready_receipts",
+ ),
+ _readiness_check(
+ "migration_file_preview_hash_present",
+ bool(file_preview.get("forward_sql_hash")),
+ {
+ "migration_filename": file_preview.get("migration_filename"),
+ "forward_sql_hash": file_preview.get("forward_sql_hash"),
+ },
+ "regenerate_migration_file_preview",
+ ),
+ _readiness_check(
+ "migration_file_preview_additive_only",
+ file_preview.get("additive_only") is True
+ and file_preview.get("forbidden_forward_tokens_absent") is True,
+ {
+ "additive_only": file_preview.get("additive_only"),
+ "forbidden_forward_tokens_absent": file_preview.get("forbidden_forward_tokens_absent"),
+ },
+ "block_until_sql_preview_reviewed",
+ ),
+ _readiness_check(
+ "future_apply_endpoint_contract_ready",
+ endpoint.get("contract_status") == "APPLY_ENDPOINT_CONTRACT_READY",
+ {"contract_status": endpoint.get("contract_status")},
+ "wait_for_apply_endpoint_contract",
+ ),
+ _readiness_check(
+ "prewrite_snapshot_targets_present",
+ int(request_contract.get("receipt_count") or 0) > 0
+ and int(request_contract.get("product_count") or 0) > 0,
+ {
+ "receipt_count": request_contract.get("receipt_count", 0),
+ "product_count": request_contract.get("product_count", 0),
+ },
+ "wait_for_snapshot_targets",
+ ),
+ _readiness_check(
+ "post_write_verifier_present",
+ int(post_write_verifier.get("verifier_count") or 0) >= 5,
+ {"verifier_count": post_write_verifier.get("verifier_count", 0)},
+ "block_until_verifier_contract_complete",
+ ),
+ _readiness_check(
+ "rollback_contract_present",
+ rollback_contract.get("required") is True and rollback_contract.get("uses_prewrite_snapshot") is True,
+ {
+ "required": rollback_contract.get("required"),
+ "uses_prewrite_snapshot": rollback_contract.get("uses_prewrite_snapshot"),
+ },
+ "block_until_rollback_contract_complete",
+ ),
+ _readiness_check(
+ "preview_has_no_side_effects",
+ safety.get("writes_file") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("writes_database") is False,
+ {
+ "writes_file": safety.get("writes_file"),
+ "executes_endpoint": safety.get("executes_endpoint"),
+ "writes_database": safety.get("writes_database"),
+ },
+ "block_until_preview_is_read_only",
+ ),
+ ]
+
+
+def build_pchome_auto_policy_apply_readiness_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out no-write readiness before any real migration file or apply endpoint exists."""
+ migration_preview = build_pchome_auto_policy_migration_file_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ checks = _build_apply_readiness_checks(migration_preview)
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ current_preview_ready = not waiting_checks
+ if current_preview_ready:
+ result = "APPLY_READINESS_CLOSEOUT_READY"
+ closeout_status = "READY_FOR_MIGRATION_FILE_GENERATION_REQUEST"
+ elif int((migration_preview.get("summary") or {}).get("dry_run_ready_count") or 0) <= 0:
+ result = "APPLY_READINESS_WAITING_FOR_READY_RECEIPTS"
+ closeout_status = "WAITING_FOR_READY_RECEIPTS"
+ else:
+ result = "APPLY_READINESS_WAITING_FOR_CONTRACTS"
+ closeout_status = "WAITING_FOR_CONTRACTS"
+
+ future_apply_blockers = [
+ {
+ "key": "migration_file_not_written",
+ "status": "future_apply_blocker",
+ "resolution": "write migration file from the approved preview in a future file-generation step",
+ },
+ {
+ "key": "migration_not_applied",
+ "status": "future_apply_blocker",
+ "resolution": "apply migration only through a separate migration apply gate",
+ },
+ {
+ "key": "prewrite_snapshot_not_created",
+ "status": "future_apply_blocker",
+ "resolution": "create snapshot artifact inside the future apply endpoint preflight",
+ },
+ {
+ "key": "post_write_readback_not_executed",
+ "status": "future_apply_blocker",
+ "resolution": "run post-write verifier after the future apply transaction",
+ },
+ ]
+
+ return {
+ "policy": AUTO_POLICY_APPLY_READINESS_CLOSEOUT_POLICY,
+ "result": result,
+ "success": bool(migration_preview.get("success")),
+ "generated_at": migration_preview.get("generated_at"),
+ "source_policy": migration_preview.get("policy"),
+ "stats": migration_preview.get("stats") or {},
+ "summary": {
+ "readiness_check_count": len(checks),
+ "readiness_pass_count": passed_count,
+ "readiness_waiting_count": len(waiting_checks),
+ "current_preview_ready_count": 1 if current_preview_ready else 0,
+ "future_apply_blocker_count": len(future_apply_blockers),
+ "dry_run_ready_count": (migration_preview.get("summary") or {}).get("dry_run_ready_count", 0),
+ "migration_file_line_count": (migration_preview.get("summary") or {}).get("migration_file_line_count", 0),
+ "apply_endpoint_contract_ready_count": (
+ migration_preview.get("summary") or {}
+ ).get("apply_endpoint_contract_ready_count", 0),
+ "writes_file_count": 0,
+ "executes_migration_count": 0,
+ "executes_endpoint_count": 0,
+ "writes_database_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ migration_preview.get("summary") or {}
+ ).get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "closeout": {
+ "status": closeout_status,
+ "current_preview_ready": current_preview_ready,
+ "ready_for_migration_file_generation_request": current_preview_ready,
+ "ready_for_database_apply": False,
+ "ready_for_endpoint_execution": False,
+ "waiting_checks": waiting_checks,
+ "future_apply_blockers": future_apply_blockers,
+ "manual_review_mode": "exception_only",
+ },
+ "readiness_checks": checks,
+ "migration_file_preview_summary": {
+ "migration_filename": (migration_preview.get("migration_file_preview") or {}).get("migration_filename"),
+ "forward_sql_hash": (migration_preview.get("migration_file_preview") or {}).get("forward_sql_hash"),
+ "line_count": (migration_preview.get("migration_file_preview") or {}).get("line_count", 0),
+ "forbidden_forward_tokens_absent": (
+ migration_preview.get("migration_file_preview") or {}
+ ).get("forbidden_forward_tokens_absent"),
+ },
+ "future_apply_endpoint_summary": {
+ "endpoint": (
+ migration_preview.get("future_apply_endpoint_verifier") or {}
+ ).get("endpoint"),
+ "contract_status": (
+ migration_preview.get("future_apply_endpoint_verifier") or {}
+ ).get("contract_status"),
+ "receipt_count": (
+ (migration_preview.get("future_apply_endpoint_verifier") or {}).get("request_contract") or {}
+ ).get("receipt_count", 0),
+ "product_count": (
+ (migration_preview.get("future_apply_endpoint_verifier") or {}).get("request_contract") or {}
+ ).get("product_count", 0),
+ },
+ "source_migration_preview_summary": migration_preview.get("summary") or {},
+ "safety": {
+ "read_only_apply_readiness_closeout": True,
+ "writes_file": False,
+ "executes_endpoint": False,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to request a real migration file generation step without changing DB state.",
+ "Keep database apply blocked until migration file, migration apply gate, snapshot, and readback all exist.",
+ "Route only readiness failures to exception review; ready receipts remain automated.",
+ ],
+ }
+
+
+def _migration_file_generation_request_id(closeout: dict[str, Any]) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "migration_file_preview_summary": closeout.get("migration_file_preview_summary") or {},
+ "future_apply_endpoint_summary": closeout.get("future_apply_endpoint_summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-migration-file-request-{digest[:16]}"
+
+
+def build_pchome_auto_policy_migration_file_generation_request(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write request package for generating the PChome migration file."""
+ closeout = build_pchome_auto_policy_apply_readiness_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ migration_summary = closeout.get("migration_file_preview_summary") or {}
+ endpoint_summary = closeout.get("future_apply_endpoint_summary") or {}
+ closeout_ready = bool(closeout.get("closeout", {}).get("ready_for_migration_file_generation_request"))
+ request_id = _migration_file_generation_request_id(closeout)
+ required_artifacts = [
+ {
+ "key": "migration_file_preview",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-preview",
+ "required": True,
+ },
+ {
+ "key": "apply_readiness_closeout",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-apply-readiness-closeout",
+ "required": True,
+ },
+ {
+ "key": "production_version_truth",
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ "required": True,
+ },
+ {
+ "key": "post_generation_diff_check",
+ "source_command": "git diff --check",
+ "required": True,
+ },
+ ]
+ file_generation_steps = [
+ {
+ "name": "create_migration_file_from_preview",
+ "target_file": migration_summary.get("migration_filename"),
+ "content_source": "migration_file_preview.forward_sql_preview",
+ "expected_sha256": migration_summary.get("forward_sql_hash"),
+ "writes_file_in_preview": False,
+ },
+ {
+ "name": "verify_generated_file_hash",
+ "target_file": migration_summary.get("migration_filename"),
+ "expected_sha256": migration_summary.get("forward_sql_hash"),
+ "writes_file_in_preview": False,
+ },
+ {
+ "name": "run_static_safety_checks",
+ "checks": [
+ "forbidden forward tokens remain absent",
+ "migration is additive only",
+ "no DROP / TRUNCATE / DELETE / destructive ALTER",
+ "production version truth still passes",
+ ],
+ "writes_file_in_preview": False,
+ },
+ ]
+ request_status = "FILE_GENERATION_REQUEST_READY" if closeout_ready else "WAITING_FOR_APPLY_READINESS_CLOSEOUT"
+ return {
+ "policy": AUTO_POLICY_MIGRATION_FILE_GENERATION_REQUEST_POLICY,
+ "result": request_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "request_ready_count": 1 if closeout_ready else 0,
+ "required_artifact_count": len(required_artifacts),
+ "file_generation_step_count": len(file_generation_steps),
+ "future_apply_blocker_count": (closeout.get("summary") or {}).get("future_apply_blocker_count", 0),
+ "dry_run_ready_count": (closeout.get("summary") or {}).get("dry_run_ready_count", 0),
+ "migration_file_line_count": (closeout.get("summary") or {}).get("migration_file_line_count", 0),
+ "writes_file_count": 0,
+ "executes_migration_count": 0,
+ "executes_endpoint_count": 0,
+ "writes_database_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (closeout.get("summary") or {}).get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "file_generation_request": {
+ "request_id": request_id,
+ "status": request_status,
+ "ready_to_generate_file": closeout_ready,
+ "ready_for_database_apply": False,
+ "target_file": migration_summary.get("migration_filename"),
+ "expected_sha256": migration_summary.get("forward_sql_hash"),
+ "expected_line_count": migration_summary.get("line_count", 0),
+ "source_preview_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-preview",
+ "source_closeout_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-apply-readiness-closeout",
+ "required_artifacts": required_artifacts,
+ "file_generation_steps": file_generation_steps,
+ "writes_file_in_preview": False,
+ },
+ "future_apply_endpoint_summary": endpoint_summary,
+ "future_apply_blockers": (closeout.get("closeout") or {}).get("future_apply_blockers") or [],
+ "source_closeout_summary": closeout.get("summary") or {},
+ "safety": {
+ "read_only_file_generation_request": True,
+ "writes_file": False,
+ "executes_endpoint": False,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Generate the migration file from this request only in a separate file-generation step.",
+ "Verify the generated file hash before any migration apply gate is considered.",
+ "Keep database apply blocked until the migration file exists and the apply gate has fresh readback.",
+ ],
+ }
+
+
+def _read_generated_migration_file(target_file: str | None) -> dict[str, Any]:
+ repo_root = Path(__file__).resolve().parents[1]
+ relative_target = target_file or "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ migration_path = repo_root / relative_target
+ if not migration_path.exists():
+ return {
+ "target_file": relative_target,
+ "exists": False,
+ "sha256": None,
+ "line_count": 0,
+ "ends_with_newline": False,
+ "forbidden_forward_tokens_absent": False,
+ "read_error": None,
+ }
+ try:
+ text = migration_path.read_text(encoding="utf-8")
+ except OSError as exc:
+ return {
+ "target_file": relative_target,
+ "exists": True,
+ "sha256": None,
+ "line_count": 0,
+ "ends_with_newline": False,
+ "forbidden_forward_tokens_absent": False,
+ "read_error": str(exc),
+ }
+ upper_text = text.upper()
+ forbidden_absent = not any(
+ token in upper_text
+ for token in ["DROP ", "TRUNCATE ", "DELETE ", "ALTER TABLE EXTERNAL_OFFERS"]
+ )
+ return {
+ "target_file": relative_target,
+ "exists": True,
+ "sha256": hashlib.sha256(text.encode("utf-8")).hexdigest(),
+ "line_count": len(text.splitlines()),
+ "ends_with_newline": text.endswith("\n"),
+ "forbidden_forward_tokens_absent": forbidden_absent,
+ "read_error": None,
+ }
+
+
+def _apply_gate_check(key: str, passed: bool, evidence: Any, failure_route: str) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def build_pchome_auto_policy_migration_apply_gate_preview(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write apply gate preview for the generated PChome migration file."""
+ request_package = build_pchome_auto_policy_migration_file_generation_request(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ file_request = request_package.get("file_generation_request") or {}
+ endpoint_summary = request_package.get("future_apply_endpoint_summary") or {}
+ generated_file = _read_generated_migration_file(file_request.get("target_file"))
+ expected_hash = file_request.get("expected_sha256")
+ hash_matches = bool(expected_hash and generated_file.get("sha256") == expected_hash)
+ request_ready = request_package.get("result") == "FILE_GENERATION_REQUEST_READY"
+ checks = [
+ _apply_gate_check(
+ "production_version_truth_required",
+ True,
+ "production /health must pass immediately before any future migration apply",
+ "abort_before_apply_gate",
+ ),
+ _apply_gate_check(
+ "file_generation_request_ready",
+ request_ready,
+ {"result": request_package.get("result")},
+ "wait_for_file_generation_request",
+ ),
+ _apply_gate_check(
+ "generated_migration_file_exists",
+ bool(generated_file.get("exists")),
+ {"target_file": generated_file.get("target_file")},
+ "generate_migration_file_first",
+ ),
+ _apply_gate_check(
+ "generated_migration_file_hash_matches_request",
+ hash_matches,
+ {
+ "expected_sha256": expected_hash,
+ "actual_sha256": generated_file.get("sha256"),
+ },
+ "regenerate_or_review_migration_file",
+ ),
+ _apply_gate_check(
+ "generated_migration_file_additive_only",
+ generated_file.get("forbidden_forward_tokens_absent") is True,
+ {"forbidden_forward_tokens_absent": generated_file.get("forbidden_forward_tokens_absent")},
+ "block_until_sql_safety_reviewed",
+ ),
+ _apply_gate_check(
+ "future_apply_endpoint_contract_ready",
+ endpoint_summary.get("contract_status") == "APPLY_ENDPOINT_CONTRACT_READY",
+ endpoint_summary,
+ "wait_for_apply_endpoint_contract",
+ ),
+ _apply_gate_check(
+ "prewrite_snapshot_contract_required",
+ True,
+ "future apply endpoint must create prewrite snapshot before opening write transaction",
+ "abort_apply_without_snapshot",
+ ),
+ _apply_gate_check(
+ "post_apply_verifier_required",
+ True,
+ "future apply endpoint must run receipt, hash, external_offer, backlog, and rollback verifiers",
+ "abort_apply_without_verifier",
+ ),
+ _apply_gate_check(
+ "current_preview_has_no_db_side_effects",
+ True,
+ {
+ "executes_sql": False,
+ "writes_database": False,
+ "executes_endpoint": False,
+ },
+ "block_until_preview_is_read_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ apply_preview_ready = not waiting_checks
+ result = "MIGRATION_APPLY_GATE_PREVIEW_READY" if apply_preview_ready else "MIGRATION_APPLY_GATE_WAITING"
+ apply_blockers = [
+ {
+ "key": "migration_not_applied",
+ "status": "future_apply_blocker",
+ "resolution": "apply migration only through a separate explicit DB apply step",
+ },
+ {
+ "key": "prewrite_snapshot_not_created",
+ "status": "future_apply_blocker",
+ "resolution": "create snapshot artifact inside the future apply endpoint preflight",
+ },
+ {
+ "key": "post_apply_readback_not_executed",
+ "status": "future_apply_blocker",
+ "resolution": "run post-apply verifier after future migration transaction",
+ },
+ ]
+
+ return {
+ "policy": AUTO_POLICY_MIGRATION_APPLY_GATE_PREVIEW_POLICY,
+ "result": result,
+ "success": bool(request_package.get("success")),
+ "generated_at": request_package.get("generated_at"),
+ "source_policy": request_package.get("policy"),
+ "stats": request_package.get("stats") or {},
+ "summary": {
+ "apply_gate_check_count": len(checks),
+ "apply_gate_pass_count": passed_count,
+ "apply_gate_waiting_count": len(waiting_checks),
+ "apply_preview_ready_count": 1 if apply_preview_ready else 0,
+ "generated_file_exists_count": 1 if generated_file.get("exists") else 0,
+ "generated_file_hash_matches_count": 1 if hash_matches else 0,
+ "future_apply_blocker_count": len(apply_blockers),
+ "dry_run_ready_count": (request_package.get("summary") or {}).get("dry_run_ready_count", 0),
+ "migration_file_line_count": generated_file.get("line_count", 0),
+ "executes_migration_count": 0,
+ "executes_endpoint_count": 0,
+ "writes_database_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (request_package.get("summary") or {}).get(
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0
+ ),
+ },
+ "apply_gate": {
+ "status": "READY_FOR_EXPLICIT_DB_APPLY_REQUEST" if apply_preview_ready else "WAITING_FOR_APPLY_GATE_INPUTS",
+ "ready_for_explicit_db_apply_request": apply_preview_ready,
+ "ready_for_database_apply_now": False,
+ "target_file": generated_file.get("target_file"),
+ "expected_sha256": expected_hash,
+ "actual_sha256": generated_file.get("sha256"),
+ "hash_matches": hash_matches,
+ "waiting_checks": waiting_checks,
+ "future_apply_blockers": apply_blockers,
+ "manual_review_mode": "exception_only",
+ },
+ "generated_migration_file": generated_file,
+ "apply_gate_checks": checks,
+ "future_apply_endpoint_summary": endpoint_summary,
+ "required_runtime_artifacts": [
+ "fresh production version truth",
+ "prewrite snapshot artifact",
+ "post-apply readback verifier output",
+ "rollback artifact if verifier fails",
+ ],
+ "source_generation_request_summary": request_package.get("summary") or {},
+ "safety": {
+ "read_only_migration_apply_gate_preview": True,
+ "writes_file": False,
+ "executes_endpoint": False,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this preview to request an explicit DB apply step only when production truth is fresh.",
+ "Keep migration apply blocked until prewrite snapshot and post-apply verifier are wired.",
+ "Route only verifier failures to exception review; ready receipts remain automated.",
+ ],
+ }
+
+
+def _db_apply_request_id(apply_preview: dict[str, Any]) -> str:
+ payload = {
+ "policy": apply_preview.get("policy") or "",
+ "result": apply_preview.get("result") or "",
+ "apply_gate": apply_preview.get("apply_gate") or {},
+ "future_apply_endpoint_summary": apply_preview.get("future_apply_endpoint_summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-request-{digest[:16]}"
+
+
+def build_pchome_auto_policy_db_apply_request_gate_preview(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write explicit DB apply request gate preview."""
+ apply_preview = build_pchome_auto_policy_migration_apply_gate_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ apply_gate = apply_preview.get("apply_gate") or {}
+ generated_file = apply_preview.get("generated_migration_file") or {}
+ endpoint_summary = apply_preview.get("future_apply_endpoint_summary") or {}
+ request_ready = bool(apply_gate.get("ready_for_explicit_db_apply_request"))
+ request_status = "DB_APPLY_REQUEST_GATE_READY" if request_ready else "WAITING_FOR_MIGRATION_APPLY_GATE_PREVIEW"
+ request_id = _db_apply_request_id(apply_preview)
+ required_artifacts = [
+ {
+ "key": "fresh_production_version_truth",
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ "required": True,
+ },
+ {
+ "key": "generated_migration_file_hash",
+ "target_file": apply_gate.get("target_file"),
+ "expected_sha256": apply_gate.get("expected_sha256"),
+ "required": True,
+ },
+ {
+ "key": "prewrite_snapshot_contract",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-apply-gate-preview",
+ "required": True,
+ },
+ {
+ "key": "post_apply_verifier_contract",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-apply-gate-preview",
+ "required": True,
+ },
+ {
+ "key": "rollback_artifact_contract",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-apply-gate-preview",
+ "required": True,
+ },
+ ]
+ command_preview = {
+ "executor": "future_operator_shell_only",
+ "command": "psql \"$DATABASE_URL\" -v ON_ERROR_STOP=1 -f migrations/045_pchome_auto_policy_evidence_receipts.sql",
+ "uses_secret_placeholder": True,
+ "reads_secret_in_preview": False,
+ "executes_in_preview": False,
+ "writes_database": False,
+ }
+ apply_sequence_preview = [
+ {
+ "name": "refresh_production_version_truth",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "verify_migration_file_hash",
+ "expected_sha256": apply_gate.get("expected_sha256"),
+ "actual_sha256": apply_gate.get("actual_sha256"),
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "create_prewrite_snapshot",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_prewrite_snapshot/{run_id}.json",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "execute_migration",
+ "command_preview": command_preview["command"],
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "run_post_apply_verifier",
+ "checks": [
+ "external_offer_evidence_receipts exists",
+ "receipt primary key is present",
+ "indexes exist for pchome_product_id / payload_hash / apply_status",
+ "momo privileges exist",
+ "mapping backlog read-only summary still works",
+ ],
+ "required": True,
+ "executes_in_preview": False,
+ },
+ ]
+ abort_conditions = [
+ "production version truth fails",
+ "migration file hash mismatch",
+ "prewrite snapshot cannot be created",
+ "post-apply verifier contract missing",
+ "rollback artifact contract missing",
+ "database credentials are not supplied by the future operator shell",
+ ]
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_REQUEST_GATE_PREVIEW_POLICY,
+ "result": request_status,
+ "success": bool(apply_preview.get("success")),
+ "generated_at": apply_preview.get("generated_at"),
+ "source_policy": apply_preview.get("policy"),
+ "stats": apply_preview.get("stats") or {},
+ "summary": {
+ "request_ready_count": 1 if request_ready else 0,
+ "required_artifact_count": len(required_artifacts),
+ "apply_sequence_step_count": len(apply_sequence_preview),
+ "abort_condition_count": len(abort_conditions),
+ "generated_file_exists_count": (apply_preview.get("summary") or {}).get(
+ "generated_file_exists_count", 0
+ ),
+ "generated_file_hash_matches_count": (apply_preview.get("summary") or {}).get(
+ "generated_file_hash_matches_count", 0
+ ),
+ "dry_run_ready_count": (apply_preview.get("summary") or {}).get("dry_run_ready_count", 0),
+ "executes_migration_count": 0,
+ "executes_endpoint_count": 0,
+ "writes_database_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (apply_preview.get("summary") or {}).get(
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0
+ ),
+ },
+ "db_apply_request_gate": {
+ "request_id": request_id,
+ "status": request_status,
+ "ready_for_explicit_db_apply_request": request_ready,
+ "ready_for_database_apply_now": False,
+ "target_file": apply_gate.get("target_file"),
+ "expected_sha256": apply_gate.get("expected_sha256"),
+ "actual_sha256": apply_gate.get("actual_sha256"),
+ "hash_matches": apply_gate.get("hash_matches"),
+ "required_artifacts": required_artifacts,
+ "command_preview": command_preview,
+ "apply_sequence_preview": apply_sequence_preview,
+ "abort_conditions": abort_conditions,
+ "future_apply_endpoint": endpoint_summary.get("endpoint"),
+ "manual_review_mode": "exception_only",
+ },
+ "generated_migration_file": generated_file,
+ "source_apply_gate_summary": apply_preview.get("summary") or {},
+ "required_runtime_readback": [
+ "fresh production /health",
+ "schema catalog readback for external_offer_evidence_receipts",
+ "index catalog readback",
+ "privilege readback",
+ "mapping backlog read-only smoke",
+ ],
+ "rollback_gate_preview": {
+ "required": True,
+ "mode": "future_rollback_gate_only",
+ "uses_prewrite_snapshot": True,
+ "executes_in_preview": False,
+ "writes_database": False,
+ },
+ "safety": {
+ "read_only_db_apply_request_gate_preview": True,
+ "reads_secret_in_preview": False,
+ "writes_file": False,
+ "executes_endpoint": False,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this request gate only to prepare an explicit future DB apply operation.",
+ "Do not execute the psql command until production truth, snapshot, and verifier artifacts are fresh.",
+ "Route only verifier failures to exception review; ready receipts remain automated.",
+ ],
+ }
+
+
+def _db_apply_execution_preflight_id(request_preview: dict[str, Any]) -> str:
+ payload = {
+ "policy": request_preview.get("policy") or "",
+ "result": request_preview.get("result") or "",
+ "db_apply_request_gate": request_preview.get("db_apply_request_gate") or {},
+ "generated_migration_file": request_preview.get("generated_migration_file") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-preflight-{digest[:16]}"
+
+
+def build_pchome_auto_policy_db_apply_execution_preflight(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write execution preflight package before any real DB apply."""
+ request_preview = build_pchome_auto_policy_db_apply_request_gate_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ request_gate = request_preview.get("db_apply_request_gate") or {}
+ generated_file = request_preview.get("generated_migration_file") or {}
+ source_summary = request_preview.get("summary") or {}
+ request_ready = bool(request_gate.get("ready_for_explicit_db_apply_request"))
+ hash_matches = bool(request_gate.get("hash_matches"))
+ preflight_ready = request_ready and hash_matches
+ preflight_status = (
+ "DB_APPLY_EXECUTION_PREFLIGHT_READY"
+ if preflight_ready
+ else "WAITING_FOR_DB_APPLY_REQUEST_GATE"
+ )
+ target_file = request_gate.get("target_file") or generated_file.get("target_file")
+ expected_sha256 = request_gate.get("expected_sha256") or generated_file.get("sha256")
+ actual_sha256 = request_gate.get("actual_sha256") or generated_file.get("sha256")
+ preflight_id = _db_apply_execution_preflight_id(request_preview)
+
+ snapshot_steps = [
+ {
+ "key": "fresh_production_version_truth_snapshot",
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "key": "generated_migration_file_hash_snapshot",
+ "target_file": target_file,
+ "expected_sha256": expected_sha256,
+ "actual_sha256": actual_sha256,
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "key": "schema_catalog_prewrite_snapshot",
+ "sql_preview": (
+ "SELECT to_regclass('public.external_offer_evidence_receipts') "
+ "AS existing_table;"
+ ),
+ "required": True,
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ },
+ {
+ "key": "table_privilege_prewrite_snapshot",
+ "sql_preview": (
+ "SELECT grantee, privilege_type FROM information_schema.table_privileges "
+ "WHERE table_name = 'external_offer_evidence_receipts';"
+ ),
+ "required": True,
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ },
+ {
+ "key": "mapping_backlog_read_only_snapshot",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog",
+ "required": True,
+ "executes_endpoint_in_preview": False,
+ "writes_database": False,
+ },
+ ]
+ readback_checks = [
+ {
+ "key": "receipt_table_exists",
+ "sql_preview": "SELECT to_regclass('public.external_offer_evidence_receipts') IS NOT NULL AS ok;",
+ "required": True,
+ "executes_sql_in_preview": False,
+ },
+ {
+ "key": "receipt_primary_key_exists",
+ "sql_preview": (
+ "SELECT COUNT(*) FROM pg_indexes WHERE tablename = "
+ "'external_offer_evidence_receipts' AND indexname LIKE '%pkey%';"
+ ),
+ "required": True,
+ "executes_sql_in_preview": False,
+ },
+ {
+ "key": "receipt_indexes_exist",
+ "sql_preview": (
+ "SELECT indexname FROM pg_indexes WHERE tablename = "
+ "'external_offer_evidence_receipts';"
+ ),
+ "expected_index_count": 4,
+ "required": True,
+ "executes_sql_in_preview": False,
+ },
+ {
+ "key": "momo_table_privilege_exists",
+ "sql_preview": (
+ "SELECT privilege_type FROM information_schema.table_privileges "
+ "WHERE table_name = 'external_offer_evidence_receipts' AND grantee = 'momo';"
+ ),
+ "required": True,
+ "executes_sql_in_preview": False,
+ },
+ {
+ "key": "mapping_backlog_read_only_smoke",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog",
+ "required": True,
+ "executes_endpoint_in_preview": False,
+ },
+ {
+ "key": "db_apply_request_gate_regression_smoke",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-request-gate-preview",
+ "required": True,
+ "executes_endpoint_in_preview": False,
+ },
+ ]
+ rollback_artifacts = [
+ {
+ "key": "schema_migration_rollback_artifact",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_rollback/{run_id}.json",
+ "uses_prewrite_snapshot": True,
+ "rollback_sql_preview": [
+ "DROP TABLE IF EXISTS external_offer_evidence_receipts;"
+ ],
+ "required": True,
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ }
+ ]
+ required_artifacts = [
+ {
+ "key": "fresh_production_version_truth",
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ "required": True,
+ },
+ {
+ "key": "db_apply_request_gate_preview",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-request-gate-preview",
+ "required": True,
+ },
+ {
+ "key": "generated_migration_file_hash",
+ "target_file": target_file,
+ "expected_sha256": expected_sha256,
+ "required": True,
+ },
+ {
+ "key": "prewrite_snapshot_artifact",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_prewrite_snapshot/{run_id}.json",
+ "required": True,
+ },
+ {
+ "key": "post_apply_readback_artifact",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_readback/{run_id}.json",
+ "required": True,
+ },
+ {
+ "key": "rollback_artifact",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_rollback/{run_id}.json",
+ "required": True,
+ },
+ ]
+ abort_conditions = [
+ "production version truth fails",
+ "DB apply request gate is not ready",
+ "migration file hash mismatch",
+ "future shell does not provide DATABASE_URL without exposing it to preview",
+ "prewrite snapshot artifact cannot be generated",
+ "post-apply readback artifact cannot be generated",
+ "rollback artifact cannot be generated",
+ "any preview step tries to execute SQL or write DB state",
+ ]
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_EXECUTION_PREFLIGHT_POLICY,
+ "result": preflight_status,
+ "success": bool(request_preview.get("success")),
+ "generated_at": request_preview.get("generated_at"),
+ "source_policy": request_preview.get("policy"),
+ "stats": request_preview.get("stats") or {},
+ "summary": {
+ "preflight_ready_count": 1 if preflight_ready else 0,
+ "request_ready_count": 1 if request_ready else 0,
+ "required_artifact_count": len(required_artifacts),
+ "snapshot_plan_count": len(snapshot_steps),
+ "readback_plan_count": len(readback_checks),
+ "rollback_artifact_count": len(rollback_artifacts),
+ "abort_condition_count": len(abort_conditions),
+ "generated_file_exists_count": source_summary.get("generated_file_exists_count", 0),
+ "generated_file_hash_matches_count": source_summary.get(
+ "generated_file_hash_matches_count", 0
+ ),
+ "dry_run_ready_count": source_summary.get("dry_run_ready_count", 0),
+ "reads_secret_count": 0,
+ "executes_migration_count": 0,
+ "executes_endpoint_count": 0,
+ "executes_sql_count": 0,
+ "writes_database_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: source_summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "execution_preflight": {
+ "preflight_id": preflight_id,
+ "source_request_id": request_gate.get("request_id"),
+ "status": preflight_status,
+ "ready_for_preflight_artifact_generation": preflight_ready,
+ "ready_for_database_apply_now": False,
+ "target_file": target_file,
+ "expected_sha256": expected_sha256,
+ "actual_sha256": actual_sha256,
+ "hash_matches": hash_matches,
+ "fresh_production_truth_required": True,
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ },
+ "required_artifacts": required_artifacts,
+ "prewrite_snapshot_plan": {
+ "required": True,
+ "mode": "future_apply_preflight_only",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_prewrite_snapshot/{run_id}.json",
+ "snapshot_steps": snapshot_steps,
+ "snapshot_step_count": len(snapshot_steps),
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ },
+ "post_apply_readback_plan": {
+ "required": True,
+ "mode": "future_apply_readback_only",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_readback/{run_id}.json",
+ "readback_checks": readback_checks,
+ "readback_check_count": len(readback_checks),
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ },
+ "rollback_artifact_plan": {
+ "required": True,
+ "mode": "future_rollback_gate_only",
+ "artifacts": rollback_artifacts,
+ "uses_prewrite_snapshot": True,
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ },
+ "abort_conditions": abort_conditions,
+ "source_request_gate_summary": source_summary,
+ "safety": {
+ "read_only_db_apply_execution_preflight": True,
+ "reads_secret_in_preview": False,
+ "writes_file": False,
+ "writes_artifact_in_preview": False,
+ "executes_endpoint": False,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Generate the prewrite snapshot artifact only in the future explicit DB apply run.",
+ "Run post-apply readback immediately after the future migration transaction.",
+ "Use rollback artifact generation for verifier failures; route only failed verifiers to exception review.",
+ ],
+ }
+
+
+def _db_apply_authorization_package_id(preflight: dict[str, Any]) -> str:
+ payload = {
+ "policy": preflight.get("policy") or "",
+ "result": preflight.get("result") or "",
+ "execution_preflight": preflight.get("execution_preflight") or {},
+ "summary": preflight.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-{digest[:16]}"
+
+
+def _authorization_check(key: str, passed: bool, evidence: Any, failure_route: str) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_package(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write authorization package for a future explicit DB apply request."""
+ preflight = build_pchome_auto_policy_db_apply_execution_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ summary = preflight.get("summary") or {}
+ execution = preflight.get("execution_preflight") or {}
+ snapshot_plan = preflight.get("prewrite_snapshot_plan") or {}
+ readback_plan = preflight.get("post_apply_readback_plan") or {}
+ rollback_plan = preflight.get("rollback_artifact_plan") or {}
+ safety = preflight.get("safety") or {}
+
+ checks = [
+ _authorization_check(
+ "execution_preflight_ready",
+ preflight.get("result") == "DB_APPLY_EXECUTION_PREFLIGHT_READY"
+ and bool(execution.get("ready_for_preflight_artifact_generation")),
+ {
+ "result": preflight.get("result"),
+ "ready_for_preflight_artifact_generation": execution.get(
+ "ready_for_preflight_artifact_generation"
+ ),
+ },
+ "wait_for_execution_preflight",
+ ),
+ _authorization_check(
+ "db_apply_request_gate_ready",
+ int(summary.get("request_ready_count") or 0) == 1,
+ {"request_ready_count": summary.get("request_ready_count", 0)},
+ "wait_for_db_apply_request_gate",
+ ),
+ _authorization_check(
+ "generated_migration_file_hash_matches",
+ bool(execution.get("hash_matches")),
+ {
+ "target_file": execution.get("target_file"),
+ "expected_sha256": execution.get("expected_sha256"),
+ "actual_sha256": execution.get("actual_sha256"),
+ },
+ "regenerate_or_review_migration_file",
+ ),
+ _authorization_check(
+ "fresh_production_truth_required",
+ execution.get("fresh_production_truth_required") is True,
+ {"fresh_production_truth_required": execution.get("fresh_production_truth_required")},
+ "abort_without_fresh_production_truth",
+ ),
+ _authorization_check(
+ "prewrite_snapshot_plan_complete",
+ snapshot_plan.get("required") is True and int(snapshot_plan.get("snapshot_step_count") or 0) >= 5,
+ {"snapshot_step_count": snapshot_plan.get("snapshot_step_count", 0)},
+ "wait_for_prewrite_snapshot_plan",
+ ),
+ _authorization_check(
+ "post_apply_readback_plan_complete",
+ readback_plan.get("required") is True and int(readback_plan.get("readback_check_count") or 0) >= 6,
+ {"readback_check_count": readback_plan.get("readback_check_count", 0)},
+ "wait_for_post_apply_readback_plan",
+ ),
+ _authorization_check(
+ "rollback_artifact_plan_complete",
+ rollback_plan.get("required") is True and rollback_plan.get("uses_prewrite_snapshot") is True,
+ {
+ "required": rollback_plan.get("required"),
+ "uses_prewrite_snapshot": rollback_plan.get("uses_prewrite_snapshot"),
+ },
+ "wait_for_rollback_artifact_plan",
+ ),
+ _authorization_check(
+ "preview_reads_no_secret",
+ execution.get("reads_secret_in_preview") is False
+ and safety.get("reads_secret_in_preview") is False,
+ {
+ "execution_reads_secret_in_preview": execution.get("reads_secret_in_preview"),
+ "safety_reads_secret_in_preview": safety.get("reads_secret_in_preview"),
+ },
+ "block_until_secret_boundary_is_clean",
+ ),
+ _authorization_check(
+ "preview_executes_no_sql",
+ execution.get("executes_sql_in_preview") is False
+ and safety.get("executes_sql") is False
+ and int(summary.get("executes_sql_count") or 0) == 0,
+ {
+ "execution_executes_sql_in_preview": execution.get("executes_sql_in_preview"),
+ "safety_executes_sql": safety.get("executes_sql"),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ },
+ "block_until_preview_is_no_sql",
+ ),
+ _authorization_check(
+ "preview_writes_no_database",
+ execution.get("writes_database_in_preview") is False
+ and safety.get("writes_database") is False
+ and int(summary.get("writes_database_count") or 0) == 0,
+ {
+ "execution_writes_database_in_preview": execution.get("writes_database_in_preview"),
+ "safety_writes_database": safety.get("writes_database"),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ },
+ "block_until_preview_is_no_db_write",
+ ),
+ _authorization_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0,
+ {LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0)},
+ "route_failed_receipts_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ package_ready = not waiting_checks
+ package_status = (
+ "DB_APPLY_AUTHORIZATION_PACKAGE_READY"
+ if package_ready
+ else "WAITING_FOR_DB_APPLY_EXECUTION_PREFLIGHT"
+ )
+ freshness_requirements = [
+ {
+ "key": "production_truth_fresh_within_300_seconds",
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ "max_age_seconds": 300,
+ "required": True,
+ },
+ {
+ "key": "migration_file_hash_checked_after_fresh_truth",
+ "target_file": execution.get("target_file"),
+ "expected_sha256": execution.get("expected_sha256"),
+ "required": True,
+ },
+ {
+ "key": "prewrite_snapshot_created_in_same_run",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_prewrite_snapshot/{run_id}.json",
+ "required": True,
+ },
+ {
+ "key": "post_apply_readback_created_in_same_run",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_readback/{run_id}.json",
+ "required": True,
+ },
+ {
+ "key": "rollback_artifact_registered_in_same_run",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_rollback/{run_id}.json",
+ "required": True,
+ },
+ ]
+ manifest_steps = [
+ {
+ "name": "refresh_production_truth",
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "generate_prewrite_snapshot_artifact",
+ "source_plan": "prewrite_snapshot_plan",
+ "required": True,
+ "executes_in_preview": False,
+ "writes_database": False,
+ },
+ {
+ "name": "inject_database_url_from_future_shell",
+ "secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "execute_migration_in_future_apply_run",
+ "command_preview": (
+ 'psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f '
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ ),
+ "required": True,
+ "executes_in_preview": False,
+ "writes_database_in_preview": False,
+ },
+ {
+ "name": "run_post_apply_readback_bundle",
+ "source_plan": "post_apply_readback_plan",
+ "required": True,
+ "executes_in_preview": False,
+ "writes_database": False,
+ },
+ {
+ "name": "generate_rollback_artifact_if_verifier_fails",
+ "source_plan": "rollback_artifact_plan",
+ "required": True,
+ "executes_in_preview": False,
+ "writes_database": False,
+ },
+ ]
+ verifier_bundle = {
+ "pre_apply_verifiers": [
+ "production_truth_fresh_within_300_seconds",
+ "generated_migration_file_hash_matches",
+ "prewrite_snapshot_artifact_created",
+ ],
+ "post_apply_verifiers": [
+ check.get("key")
+ for check in readback_plan.get("readback_checks", [])
+ ],
+ "failure_routes": [
+ "abort_before_sql_if_pre_apply_verifier_fails",
+ "generate_rollback_artifact_if_post_apply_verifier_fails",
+ "route_failed_verifier_to_exception_review_only",
+ ],
+ "verifier_bundle_count": 3,
+ "executes_in_preview": False,
+ "writes_database": False,
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_PACKAGE_POLICY,
+ "result": package_status,
+ "success": bool(preflight.get("success")),
+ "generated_at": preflight.get("generated_at"),
+ "source_policy": preflight.get("policy"),
+ "stats": preflight.get("stats") or {},
+ "summary": {
+ "authorization_check_count": len(checks),
+ "authorization_pass_count": passed_count,
+ "authorization_waiting_count": len(waiting_checks),
+ "authorization_package_ready_count": 1 if package_ready else 0,
+ "freshness_requirement_count": len(freshness_requirements),
+ "manifest_step_count": len(manifest_steps),
+ "verifier_bundle_count": verifier_bundle["verifier_bundle_count"],
+ "required_artifact_count": summary.get("required_artifact_count", 0),
+ "snapshot_plan_count": summary.get("snapshot_plan_count", 0),
+ "readback_plan_count": summary.get("readback_plan_count", 0),
+ "rollback_artifact_count": summary.get("rollback_artifact_count", 0),
+ "dry_run_ready_count": summary.get("dry_run_ready_count", 0),
+ "reads_secret_count": 0,
+ "executes_migration_count": 0,
+ "executes_endpoint_count": 0,
+ "executes_sql_count": 0,
+ "writes_database_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "authorization_package": {
+ "package_id": _db_apply_authorization_package_id(preflight),
+ "source_preflight_id": execution.get("preflight_id"),
+ "source_request_id": execution.get("source_request_id"),
+ "status": package_status,
+ "ready_for_explicit_apply_authorization_request": package_ready,
+ "ready_for_database_apply_now": False,
+ "issue_scope": "future_apply_authorization_request_only",
+ "target_file": execution.get("target_file"),
+ "expected_sha256": execution.get("expected_sha256"),
+ "actual_sha256": execution.get("actual_sha256"),
+ "hash_matches": execution.get("hash_matches"),
+ "freshness_window_seconds": 300,
+ "requires_fresh_production_truth": True,
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ "waiting_checks": waiting_checks,
+ },
+ "freshness_requirements": freshness_requirements,
+ "machine_apply_manifest": {
+ "run_id_template": "pchome-db-apply-{utc_timestamp}-{package_digest}",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-package",
+ "source_preflight_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-execution-preflight",
+ "manifest_steps": manifest_steps,
+ "manifest_step_count": len(manifest_steps),
+ "executes_in_preview": False,
+ "writes_database": False,
+ },
+ "verifier_bundle": verifier_bundle,
+ "authorization_checks": checks,
+ "source_execution_preflight_summary": summary,
+ "safety": {
+ "read_only_db_apply_authorization_package": True,
+ "reads_secret_in_preview": False,
+ "writes_file": False,
+ "writes_artifact_in_preview": False,
+ "executes_endpoint": False,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this package only to request a separate explicit DB apply authorization.",
+ "Keep future apply blocked unless production truth is refreshed inside the same run.",
+ "Let machines route failed verifier evidence to exception review; do not re-open manual batch review.",
+ ],
+ }
+
+
+def _db_apply_verifier_artifact_preview_id(authorization_package: dict[str, Any]) -> str:
+ payload = {
+ "policy": authorization_package.get("policy") or "",
+ "result": authorization_package.get("result") or "",
+ "authorization_package": authorization_package.get("authorization_package") or {},
+ "summary": authorization_package.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-artifacts-{digest[:16]}"
+
+
+def build_pchome_auto_policy_db_apply_verifier_artifact_preview(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build no-write artifact schemas for a future DB apply verifier run."""
+ authorization_package = build_pchome_auto_policy_db_apply_authorization_package(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ authorization = authorization_package.get("authorization_package") or {}
+ summary = authorization_package.get("summary") or {}
+ authorization_ready = bool(authorization.get("ready_for_explicit_apply_authorization_request"))
+ preview_ready = authorization_ready and authorization_package.get("result") == "DB_APPLY_AUTHORIZATION_PACKAGE_READY"
+ preview_status = (
+ "DB_APPLY_VERIFIER_ARTIFACT_PREVIEW_READY"
+ if preview_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_PACKAGE"
+ )
+ preview_id = _db_apply_verifier_artifact_preview_id(authorization_package)
+ artifact_schemas = [
+ {
+ "key": "prewrite_snapshot_artifact",
+ "artifact_type": "prewrite_snapshot",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_prewrite_snapshot/{run_id}.json",
+ "required_fields": [
+ "run_id",
+ "authorization_package_id",
+ "production_truth",
+ "migration_file_hash",
+ "schema_catalog_before",
+ "table_privileges_before",
+ "mapping_backlog_summary_before",
+ "created_at",
+ "safety",
+ ],
+ "required": True,
+ "writes_artifact_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ },
+ {
+ "key": "post_apply_readback_artifact",
+ "artifact_type": "post_apply_readback",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_readback/{run_id}.json",
+ "required_fields": [
+ "run_id",
+ "authorization_package_id",
+ "receipt_table_exists",
+ "receipt_primary_key_exists",
+ "receipt_indexes_exist",
+ "momo_table_privilege_exists",
+ "mapping_backlog_read_only_smoke",
+ "db_apply_request_gate_regression_smoke",
+ "created_at",
+ "safety",
+ ],
+ "required": True,
+ "writes_artifact_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ },
+ {
+ "key": "rollback_artifact",
+ "artifact_type": "rollback",
+ "artifact_path_template": "artifacts/pchome_growth/db_apply_rollback/{run_id}.json",
+ "required_fields": [
+ "run_id",
+ "authorization_package_id",
+ "prewrite_snapshot_artifact",
+ "post_apply_readback_artifact",
+ "rollback_sql_preview",
+ "failure_reason",
+ "created_at",
+ "safety",
+ ],
+ "rollback_sql_preview": [
+ "DROP TABLE IF EXISTS external_offer_evidence_receipts;"
+ ],
+ "required": True,
+ "writes_artifact_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ },
+ ]
+ generation_steps = [
+ {
+ "name": "create_fresh_run_id",
+ "run_id_template": "pchome-db-apply-{utc_timestamp}-{package_digest}",
+ "required": True,
+ "writes_artifact_in_preview": False,
+ },
+ {
+ "name": "render_prewrite_snapshot_artifact_schema",
+ "artifact_key": "prewrite_snapshot_artifact",
+ "required": True,
+ "writes_artifact_in_preview": False,
+ },
+ {
+ "name": "render_post_apply_readback_artifact_schema",
+ "artifact_key": "post_apply_readback_artifact",
+ "required": True,
+ "writes_artifact_in_preview": False,
+ },
+ {
+ "name": "render_rollback_artifact_schema",
+ "artifact_key": "rollback_artifact",
+ "required": True,
+ "writes_artifact_in_preview": False,
+ },
+ {
+ "name": "link_artifacts_to_authorization_package",
+ "authorization_package_id": authorization.get("package_id"),
+ "required": True,
+ "writes_artifact_in_preview": False,
+ },
+ ]
+ verifier_manifest = {
+ "pre_apply_checks": [
+ "production_truth_fresh_within_300_seconds",
+ "migration_file_hash_matches_authorization_package",
+ "prewrite_snapshot_artifact_schema_valid",
+ ],
+ "post_apply_checks": [
+ "receipt_table_exists",
+ "receipt_primary_key_exists",
+ "receipt_indexes_exist",
+ "momo_table_privilege_exists",
+ "mapping_backlog_read_only_smoke",
+ "db_apply_request_gate_regression_smoke",
+ ],
+ "artifact_integrity_checks": [
+ "all_artifacts_include_run_id",
+ "all_artifacts_include_authorization_package_id",
+ "rollback_artifact_references_prewrite_snapshot",
+ ],
+ "failure_handlers": [
+ "abort_before_sql_if_pre_apply_check_fails",
+ "generate_rollback_artifact_if_post_apply_check_fails",
+ "route_failed_verifier_to_exception_review_only",
+ ],
+ "verifier_check_count": 15,
+ "executes_in_preview": False,
+ "writes_artifact_in_preview": False,
+ "writes_database": False,
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_VERIFIER_ARTIFACT_PREVIEW_POLICY,
+ "result": preview_status,
+ "success": bool(authorization_package.get("success")),
+ "generated_at": authorization_package.get("generated_at"),
+ "source_policy": authorization_package.get("policy"),
+ "stats": authorization_package.get("stats") or {},
+ "summary": {
+ "artifact_preview_ready_count": 1 if preview_ready else 0,
+ "authorization_package_ready_count": summary.get("authorization_package_ready_count", 0),
+ "artifact_schema_count": len(artifact_schemas),
+ "artifact_generation_step_count": len(generation_steps),
+ "verifier_check_count": verifier_manifest["verifier_check_count"],
+ "freshness_requirement_count": summary.get("freshness_requirement_count", 0),
+ "manifest_step_count": summary.get("manifest_step_count", 0),
+ "required_artifact_count": summary.get("required_artifact_count", 0),
+ "dry_run_ready_count": summary.get("dry_run_ready_count", 0),
+ "writes_artifact_count": 0,
+ "reads_secret_count": 0,
+ "executes_migration_count": 0,
+ "executes_endpoint_count": 0,
+ "executes_sql_count": 0,
+ "writes_database_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "artifact_preview": {
+ "preview_id": preview_id,
+ "source_authorization_package_id": authorization.get("package_id"),
+ "source_preflight_id": authorization.get("source_preflight_id"),
+ "source_request_id": authorization.get("source_request_id"),
+ "status": preview_status,
+ "ready_for_future_artifact_generation": preview_ready,
+ "ready_to_write_artifacts_now": False,
+ "ready_for_database_apply_now": False,
+ "target_file": authorization.get("target_file"),
+ "expected_sha256": authorization.get("expected_sha256"),
+ "actual_sha256": authorization.get("actual_sha256"),
+ "hash_matches": authorization.get("hash_matches"),
+ "writes_artifact_in_preview": False,
+ "reads_secret_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ },
+ "artifact_schemas": artifact_schemas,
+ "artifact_generation_plan": {
+ "mode": "future_apply_run_only",
+ "generation_steps": generation_steps,
+ "generation_step_count": len(generation_steps),
+ "writes_artifact_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database": False,
+ },
+ "verifier_manifest": verifier_manifest,
+ "source_authorization_summary": summary,
+ "safety": {
+ "read_only_db_apply_verifier_artifact_preview": True,
+ "reads_secret_in_preview": False,
+ "writes_file": False,
+ "writes_artifact_in_preview": False,
+ "executes_endpoint": False,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this preview to generate artifact writers only inside a separate future apply run.",
+ "Keep artifact generation no-op until fresh production truth and explicit DB apply authorization are present.",
+ "Let verifier artifacts drive exception routing instead of reopening manual batch review.",
+ ],
+ }
+
+
+def _db_apply_final_handoff_package_id(artifact_preview: dict[str, Any]) -> str:
+ payload = {
+ "policy": artifact_preview.get("policy") or "",
+ "result": artifact_preview.get("result") or "",
+ "artifact_preview": artifact_preview.get("artifact_preview") or {},
+ "summary": artifact_preview.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-final-handoff-{digest[:16]}"
+
+
+def build_pchome_auto_policy_db_apply_final_handoff_package(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write final handoff package for a future explicit DB apply."""
+ artifact_preview = build_pchome_auto_policy_db_apply_verifier_artifact_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ artifact = artifact_preview.get("artifact_preview") or {}
+ summary = artifact_preview.get("summary") or {}
+ artifact_ready = bool(artifact.get("ready_for_future_artifact_generation"))
+ final_ready = artifact_ready and artifact_preview.get("result") == "DB_APPLY_VERIFIER_ARTIFACT_PREVIEW_READY"
+ handoff_status = (
+ "DB_APPLY_FINAL_HANDOFF_PACKAGE_READY"
+ if final_ready
+ else "WAITING_FOR_DB_APPLY_VERIFIER_ARTIFACT_PREVIEW"
+ )
+ handoff_sections = [
+ {
+ "key": "scope_boundary",
+ "title": "Future explicit DB apply only",
+ "summary": "This handoff prepares a future apply run; it does not execute SQL or read secrets.",
+ },
+ {
+ "key": "production_truth",
+ "title": "Production health is the latest version truth",
+ "required_command": "python scripts/ops/check_production_version_truth.py",
+ },
+ {
+ "key": "migration_file",
+ "title": "Generated migration file and hash",
+ "target_file": artifact.get("target_file"),
+ "expected_sha256": artifact.get("expected_sha256"),
+ "actual_sha256": artifact.get("actual_sha256"),
+ },
+ {
+ "key": "verifier_artifacts",
+ "title": "Prewrite snapshot, readback, and rollback artifacts",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview",
+ },
+ {
+ "key": "future_commands",
+ "title": "Commands are preview-only until a separate explicit apply run",
+ "secret_boundary": "future_shell_only",
+ },
+ {
+ "key": "rollback_and_exception_routing",
+ "title": "Verifier failure goes to rollback artifact and exception review",
+ "manual_review_mode": "exception_only",
+ },
+ ]
+ command_previews = [
+ {
+ "name": "refresh_production_truth",
+ "command": "python scripts/ops/check_production_version_truth.py",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "execute_migration_future_shell_only",
+ "command": (
+ 'psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f '
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ ),
+ "uses_secret_placeholder": True,
+ "reads_secret_in_preview": False,
+ "required": True,
+ "executes_in_preview": False,
+ "writes_database_in_preview": False,
+ },
+ {
+ "name": "run_post_apply_verifier_bundle",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview",
+ "required": True,
+ "executes_in_preview": False,
+ "writes_database_in_preview": False,
+ },
+ ]
+ final_runbook_steps = [
+ {
+ "name": "confirm_fresh_production_truth",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "confirm_final_handoff_package_hash_and_ids",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "create_prewrite_snapshot_artifact_in_future_run",
+ "required": True,
+ "executes_in_preview": False,
+ "writes_artifact_in_preview": False,
+ },
+ {
+ "name": "inject_database_url_from_future_shell_without_logging",
+ "required": True,
+ "reads_secret_in_preview": False,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "execute_migration_once",
+ "required": True,
+ "executes_in_preview": False,
+ "writes_database_in_preview": False,
+ },
+ {
+ "name": "run_post_apply_readback_verifier",
+ "required": True,
+ "executes_in_preview": False,
+ "writes_database_in_preview": False,
+ },
+ {
+ "name": "generate_rollback_artifact_on_verifier_failure",
+ "required": True,
+ "executes_in_preview": False,
+ "writes_artifact_in_preview": False,
+ },
+ ]
+ abort_gates = [
+ "production version truth fails or drifts",
+ "final handoff package is not ready",
+ "verifier artifact preview is not ready",
+ "migration file hash mismatch",
+ "DATABASE_URL would be exposed to preview, logs, or persisted artifacts",
+ "prewrite snapshot artifact cannot be generated in the future run",
+ "post-apply verifier artifact cannot be generated in the future run",
+ "rollback artifact cannot be generated in the future run",
+ "future psql command is requested outside a separate explicit DB apply authorization",
+ "any preview step attempts to execute SQL, write artifacts, or write database state",
+ ]
+ source_proof_manifest = {
+ "source_endpoint_chain": [
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-request-gate-preview",
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-execution-preflight",
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-package",
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview",
+ ],
+ "source_request_id": artifact.get("source_request_id"),
+ "source_preflight_id": artifact.get("source_preflight_id"),
+ "source_authorization_package_id": artifact.get("source_authorization_package_id"),
+ "source_artifact_preview_id": artifact.get("preview_id"),
+ "expected_sha256": artifact.get("expected_sha256"),
+ "actual_sha256": artifact.get("actual_sha256"),
+ "hash_matches": artifact.get("hash_matches"),
+ "required_to_refresh_before_apply": True,
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_FINAL_HANDOFF_PACKAGE_POLICY,
+ "result": handoff_status,
+ "success": bool(artifact_preview.get("success")),
+ "generated_at": artifact_preview.get("generated_at"),
+ "source_policy": artifact_preview.get("policy"),
+ "stats": artifact_preview.get("stats") or {},
+ "summary": {
+ "final_handoff_ready_count": 1 if final_ready else 0,
+ "artifact_preview_ready_count": summary.get("artifact_preview_ready_count", 0),
+ "handoff_section_count": len(handoff_sections),
+ "final_runbook_step_count": len(final_runbook_steps),
+ "command_preview_count": len(command_previews),
+ "abort_gate_count": len(abort_gates),
+ "source_endpoint_count": len(source_proof_manifest["source_endpoint_chain"]),
+ "artifact_schema_count": summary.get("artifact_schema_count", 0),
+ "artifact_generation_step_count": summary.get("artifact_generation_step_count", 0),
+ "verifier_check_count": summary.get("verifier_check_count", 0),
+ "required_artifact_count": summary.get("required_artifact_count", 0),
+ "dry_run_ready_count": summary.get("dry_run_ready_count", 0),
+ "writes_artifact_count": 0,
+ "reads_secret_count": 0,
+ "executes_migration_count": 0,
+ "executes_endpoint_count": 0,
+ "executes_sql_count": 0,
+ "writes_database_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "final_handoff_package": {
+ "package_id": _db_apply_final_handoff_package_id(artifact_preview),
+ "source_artifact_preview_id": artifact.get("preview_id"),
+ "source_authorization_package_id": artifact.get("source_authorization_package_id"),
+ "source_preflight_id": artifact.get("source_preflight_id"),
+ "source_request_id": artifact.get("source_request_id"),
+ "status": handoff_status,
+ "ready_for_explicit_db_apply_handoff": final_ready,
+ "ready_for_database_apply_now": False,
+ "target_file": artifact.get("target_file"),
+ "expected_sha256": artifact.get("expected_sha256"),
+ "actual_sha256": artifact.get("actual_sha256"),
+ "hash_matches": artifact.get("hash_matches"),
+ "requires_fresh_production_truth": True,
+ "requires_separate_explicit_db_apply_authorization": True,
+ "explicit_authorization_boundary": "future_message_and_shell_only",
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "writes_artifact_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ },
+ "handoff_sections": handoff_sections,
+ "final_runbook_manifest": {
+ "mode": "future_explicit_apply_only",
+ "run_id_template": "pchome-db-apply-{utc_timestamp}-{handoff_digest}",
+ "steps": final_runbook_steps,
+ "step_count": len(final_runbook_steps),
+ "executes_in_preview": False,
+ "writes_artifact_in_preview": False,
+ "writes_database": False,
+ },
+ "command_previews": command_previews,
+ "abort_gates": abort_gates,
+ "source_proof_manifest": source_proof_manifest,
+ "source_artifact_preview_summary": summary,
+ "safety": {
+ "read_only_db_apply_final_handoff_package": True,
+ "reads_secret_in_preview": False,
+ "writes_file": False,
+ "writes_artifact_in_preview": False,
+ "executes_endpoint": False,
+ "executes_migration": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this final handoff only after a separate explicit DB apply authorization is given.",
+ "Refresh production truth and regenerate artifacts inside the same future apply run.",
+ "Keep failures machine-routed to rollback artifacts and exception review.",
+ ],
+ }
+
+
+def _db_apply_controlled_dry_run_shell_preview_id(final_handoff: dict[str, Any]) -> str:
+ payload = {
+ "policy": final_handoff.get("policy") or "",
+ "result": final_handoff.get("result") or "",
+ "final_handoff_package": final_handoff.get("final_handoff_package") or {},
+ "summary": final_handoff.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-dry-run-shell-{digest[:16]}"
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_shell_preview(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write shell dry-run preview for a future explicit DB apply."""
+ final_handoff = build_pchome_auto_policy_db_apply_final_handoff_package(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ handoff = final_handoff.get("final_handoff_package") or {}
+ source_summary = final_handoff.get("summary") or {}
+ handoff_ready = bool(handoff.get("ready_for_explicit_db_apply_handoff"))
+ dry_run_ready = handoff_ready and final_handoff.get("result") == "DB_APPLY_FINAL_HANDOFF_PACKAGE_READY"
+ dry_run_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_SHELL_PREVIEW_READY"
+ if dry_run_ready
+ else "WAITING_FOR_DB_APPLY_FINAL_HANDOFF_PACKAGE"
+ )
+ shell_preview_id = _db_apply_controlled_dry_run_shell_preview_id(final_handoff)
+ shell_phases = [
+ {
+ "name": "initialize_dry_run_context",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "refresh_production_truth_check_mode",
+ "command_preview": "python scripts/ops/check_production_version_truth.py",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "verify_final_handoff_source_chain",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-final-handoff-package",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "verify_migration_file_hash_check_mode",
+ "target_file": handoff.get("target_file"),
+ "expected_sha256": handoff.get("expected_sha256"),
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "render_artifact_paths_check_mode",
+ "artifact_path_templates": [
+ "artifacts/pchome_growth/db_apply_prewrite_snapshot/{run_id}.json",
+ "artifacts/pchome_growth/db_apply_readback/{run_id}.json",
+ "artifacts/pchome_growth/db_apply_rollback/{run_id}.json",
+ ],
+ "required": True,
+ "writes_artifact_in_preview": False,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "render_prewrite_snapshot_command_preview",
+ "required": True,
+ "writes_artifact_in_preview": False,
+ "executes_sql_in_preview": False,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "render_database_apply_command_preview",
+ "command_preview": (
+ 'psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f '
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ ),
+ "uses_secret_placeholder": True,
+ "reads_secret_in_preview": False,
+ "required": True,
+ "executes_in_preview": False,
+ "writes_database_in_preview": False,
+ },
+ {
+ "name": "render_post_apply_verifier_command_preview",
+ "required": True,
+ "executes_sql_in_preview": False,
+ "executes_in_preview": False,
+ "writes_database_in_preview": False,
+ },
+ {
+ "name": "render_abort_and_rollback_hooks",
+ "required": True,
+ "writes_artifact_in_preview": False,
+ "executes_in_preview": False,
+ "writes_database_in_preview": False,
+ },
+ ]
+ shell_script_preview = {
+ "filename": "scripts/ops/pchome_db_apply_controlled_dry_run.sh",
+ "mode": "future_script_preview_only",
+ "lines": [
+ "#!/usr/bin/env bash",
+ "set -euo pipefail",
+ "DRY_RUN_ONLY=1",
+ ': "${RUN_ID:=pchome-db-apply-dry-run-preview}"',
+ "python scripts/ops/check_production_version_truth.py",
+ "printf '%s\\n' 'DRY RUN: verify final handoff source chain and migration hash'",
+ "printf '%s\\n' 'DRY RUN: would create prewrite snapshot artifact'",
+ "printf '%s\\n' 'DRY RUN: would require DATABASE_URL from future shell without logging it'",
+ "printf '%s\\n' 'DRY RUN: would execute psql \"$DATABASE_URL\" -v ON_ERROR_STOP=1 -f migrations/045_pchome_auto_policy_evidence_receipts.sql'",
+ "printf '%s\\n' 'DRY RUN: would run post-apply verifier bundle and rollback hooks'",
+ ],
+ "line_count": 10,
+ "writes_file_in_preview": False,
+ "executes_script_in_preview": False,
+ "reads_secret_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ check_mode_contract = {
+ "required_checks": [
+ "production_truth_passes",
+ "final_handoff_package_ready",
+ "migration_file_hash_matches",
+ "artifact_path_templates_render",
+ "database_url_not_read_in_preview",
+ "all_future_write_commands_remain_preview_only",
+ ],
+ "required_check_count": 6,
+ "dry_run_only": True,
+ "executes_in_preview": False,
+ "writes_database": False,
+ }
+ rollback_hook_preview = {
+ "hooks": [
+ {
+ "name": "abort_before_sql_on_precheck_failure",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "generate_rollback_artifact_on_post_apply_failure",
+ "required": True,
+ "writes_artifact_in_preview": False,
+ "executes_in_preview": False,
+ },
+ {
+ "name": "route_exception_review_with_artifact_ids",
+ "required": True,
+ "executes_in_preview": False,
+ },
+ ],
+ "hook_count": 3,
+ "writes_artifact_in_preview": False,
+ "writes_database": False,
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_SHELL_PREVIEW_POLICY,
+ "result": dry_run_status,
+ "success": bool(final_handoff.get("success")),
+ "generated_at": final_handoff.get("generated_at"),
+ "source_policy": final_handoff.get("policy"),
+ "stats": final_handoff.get("stats") or {},
+ "summary": {
+ "dry_run_shell_preview_ready_count": 1 if dry_run_ready else 0,
+ "final_handoff_ready_count": source_summary.get("final_handoff_ready_count", 0),
+ "shell_phase_count": len(shell_phases),
+ "shell_script_line_count": shell_script_preview["line_count"],
+ "check_mode_required_check_count": check_mode_contract["required_check_count"],
+ "rollback_hook_count": rollback_hook_preview["hook_count"],
+ "command_preview_count": source_summary.get("command_preview_count", 0),
+ "abort_gate_count": source_summary.get("abort_gate_count", 0),
+ "artifact_schema_count": source_summary.get("artifact_schema_count", 0),
+ "verifier_check_count": source_summary.get("verifier_check_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: source_summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "controlled_dry_run_shell_preview": {
+ "preview_id": shell_preview_id,
+ "source_final_handoff_package_id": handoff.get("package_id"),
+ "source_artifact_preview_id": handoff.get("source_artifact_preview_id"),
+ "source_authorization_package_id": handoff.get("source_authorization_package_id"),
+ "source_preflight_id": handoff.get("source_preflight_id"),
+ "source_request_id": handoff.get("source_request_id"),
+ "status": dry_run_status,
+ "ready_for_future_shell_script_generation": dry_run_ready,
+ "ready_to_write_script_now": False,
+ "ready_to_execute_shell_now": False,
+ "ready_for_database_apply_now": False,
+ "target_file": handoff.get("target_file"),
+ "expected_sha256": handoff.get("expected_sha256"),
+ "actual_sha256": handoff.get("actual_sha256"),
+ "hash_matches": handoff.get("hash_matches"),
+ "dry_run_only": True,
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "writes_script_in_preview": False,
+ "writes_artifact_in_preview": False,
+ "executes_script_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ },
+ "shell_phases": shell_phases,
+ "shell_script_preview": shell_script_preview,
+ "check_mode_contract": check_mode_contract,
+ "rollback_hook_preview": rollback_hook_preview,
+ "source_final_handoff_summary": source_summary,
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_shell_preview": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this preview to generate a future dry-run shell script only after separate apply authorization.",
+ "Keep DATABASE_URL in the future shell boundary and out of previews, logs, and artifacts.",
+ "Keep real SQL execution blocked until dry-run shell output is refreshed with production truth.",
+ ],
+ }
+
+
+def _db_apply_controlled_dry_run_shell_closeout_id(shell_preview: dict[str, Any]) -> str:
+ payload = {
+ "policy": shell_preview.get("policy") or "",
+ "result": shell_preview.get("result") or "",
+ "controlled_dry_run_shell_preview": shell_preview.get("controlled_dry_run_shell_preview") or {},
+ "summary": shell_preview.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-dry-run-closeout-{digest[:16]}"
+
+
+def _db_apply_authorization_request_intake_id(closeout: dict[str, Any]) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "explicit_authorization_boundary": closeout.get("explicit_authorization_boundary") or {},
+ "summary": closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-intake-{digest[:16]}"
+
+
+def _db_apply_authorization_request_closeout_id(intake: dict[str, Any]) -> str:
+ payload = {
+ "policy": intake.get("policy") or "",
+ "result": intake.get("result") or "",
+ "authorization_request_intake": intake.get("authorization_request_intake") or {},
+ "authorization_envelope": intake.get("authorization_envelope") or {},
+ "summary": intake.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-closeout-{digest[:16]}"
+
+
+def _db_apply_authorization_lane_guard_id(closeout: dict[str, Any]) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "final_exact_request_package": closeout.get("final_exact_request_package") or {},
+ "machine_request_manifest": closeout.get("machine_request_manifest") or {},
+ "summary": closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-lane-{digest[:16]}"
+
+
+def _db_apply_authorization_decision_preflight_id(lane_guard: dict[str, Any]) -> str:
+ payload = {
+ "policy": lane_guard.get("policy") or "",
+ "result": lane_guard.get("result") or "",
+ "future_authorization_lane_guard": lane_guard.get("future_authorization_lane_guard") or {},
+ "lane_transfer_contract": lane_guard.get("lane_transfer_contract") or {},
+ "summary": lane_guard.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-decision-{digest[:16]}"
+
+
+def _db_apply_authorization_decision_closeout_id(preflight: dict[str, Any]) -> str:
+ payload = {
+ "policy": preflight.get("policy") or "",
+ "result": preflight.get("result") or "",
+ "future_authorization_decision_preflight": (
+ preflight.get("future_authorization_decision_preflight") or {}
+ ),
+ "decision_preflight_envelope": preflight.get("decision_preflight_envelope") or {},
+ "summary": preflight.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-decision-closeout-{digest[:16]}"
+
+
+def _db_apply_authorization_issuer_gate_id(closeout: dict[str, Any]) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "future_authorization_decision_package": (
+ closeout.get("future_authorization_decision_package") or {}
+ ),
+ "decision_closeout_contract": closeout.get("decision_closeout_contract") or {},
+ "summary": closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-issuer-gate-{digest[:16]}"
+
+
+def _db_apply_authorization_signing_decision_preflight_id(issuer_gate: dict[str, Any]) -> str:
+ payload = {
+ "policy": issuer_gate.get("policy") or "",
+ "result": issuer_gate.get("result") or "",
+ "future_authorization_issuer_gate": issuer_gate.get("future_authorization_issuer_gate") or {},
+ "final_nonsecret_authorization_envelope": (
+ issuer_gate.get("final_nonsecret_authorization_envelope") or {}
+ ),
+ "issuer_gate_contract": issuer_gate.get("issuer_gate_contract") or {},
+ "summary": issuer_gate.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-signing-preflight-{digest[:16]}"
+
+
+def _db_apply_authorization_signing_decision_closeout_id(preflight: dict[str, Any]) -> str:
+ payload = {
+ "policy": preflight.get("policy") or "",
+ "result": preflight.get("result") or "",
+ "future_authorization_signing_decision_preflight": (
+ preflight.get("future_authorization_signing_decision_preflight") or {}
+ ),
+ "signing_decision_preflight_envelope": (
+ preflight.get("signing_decision_preflight_envelope") or {}
+ ),
+ "summary": preflight.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-signing-closeout-{digest[:16]}"
+
+
+def _db_apply_authorization_signing_issuer_guard_id(closeout: dict[str, Any]) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "unsigned_signing_decision_package": closeout.get("unsigned_signing_decision_package") or {},
+ "signing_decision_closeout_contract": (
+ closeout.get("signing_decision_closeout_contract") or {}
+ ),
+ "summary": closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-signing-issuer-{digest[:16]}"
+
+
+def _db_apply_authorization_signing_issuer_closeout_id(guard: dict[str, Any]) -> str:
+ payload = {
+ "policy": guard.get("policy") or "",
+ "result": guard.get("result") or "",
+ "signable_request_boundary": guard.get("signable_request_boundary") or {},
+ "signing_issuer_guard_contract": guard.get("signing_issuer_guard_contract") or {},
+ "summary": guard.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-signing-issuer-closeout-{digest[:16]}"
+
+
+def _db_apply_authorization_signing_execution_preflight_id(closeout: dict[str, Any]) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "future_authorization_signing_issuer_closeout": (
+ closeout.get("future_authorization_signing_issuer_closeout") or {}
+ ),
+ "final_signable_request_package": closeout.get("final_signable_request_package") or {},
+ "signing_issuer_closeout_contract": (
+ closeout.get("signing_issuer_closeout_contract") or {}
+ ),
+ "summary": closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-signing-execution-preflight-{digest[:16]}"
+
+
+def _db_apply_authorization_signing_execution_closeout_id(preflight: dict[str, Any]) -> str:
+ payload = {
+ "policy": preflight.get("policy") or "",
+ "result": preflight.get("result") or "",
+ "future_authorization_signing_execution_preflight": (
+ preflight.get("future_authorization_signing_execution_preflight") or {}
+ ),
+ "signing_execution_preflight_package": (
+ preflight.get("signing_execution_preflight_package") or {}
+ ),
+ "operator_held_secret_boundary_contract": (
+ preflight.get("operator_held_secret_boundary_contract") or {}
+ ),
+ "summary": preflight.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-signing-execution-closeout-{digest[:16]}"
+
+
+def _db_apply_authorization_signed_receipt_preflight_id(closeout: dict[str, Any]) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "future_authorization_signing_execution_closeout": (
+ closeout.get("future_authorization_signing_execution_closeout") or {}
+ ),
+ "unsigned_signed_authorization_receipt_boundary": (
+ closeout.get("unsigned_signed_authorization_receipt_boundary") or {}
+ ),
+ "signing_execution_closeout_contract": (
+ closeout.get("signing_execution_closeout_contract") or {}
+ ),
+ "summary": closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-signed-receipt-preflight-{digest[:16]}"
+
+
+def _db_apply_authorization_signed_receipt_closeout_id(preflight: dict[str, Any]) -> str:
+ payload = {
+ "policy": preflight.get("policy") or "",
+ "result": preflight.get("result") or "",
+ "future_authorization_signed_receipt_preflight": (
+ preflight.get("future_authorization_signed_receipt_preflight") or {}
+ ),
+ "external_signing_receipt_evidence_boundary": (
+ preflight.get("external_signing_receipt_evidence_boundary") or {}
+ ),
+ "signed_receipt_preflight_contract": (
+ preflight.get("signed_receipt_preflight_contract") or {}
+ ),
+ "summary": preflight.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-signed-receipt-closeout-{digest[:16]}"
+
+
+def _db_apply_authorization_signed_receipt_evidence_intake_id(
+ closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "future_authorization_signed_receipt_closeout": (
+ closeout.get("future_authorization_signed_receipt_closeout") or {}
+ ),
+ "detached_receipt_verification_boundary": (
+ closeout.get("detached_receipt_verification_boundary") or {}
+ ),
+ "signed_receipt_closeout_contract": (
+ closeout.get("signed_receipt_closeout_contract") or {}
+ ),
+ "summary": closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-signed-receipt-evidence-intake-{digest[:16]}"
+
+
+def _db_apply_authorization_detached_verification_evidence_validation_id(
+ intake: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": intake.get("policy") or "",
+ "result": intake.get("result") or "",
+ "future_signed_authorization_receipt_evidence_intake": (
+ intake.get("future_signed_authorization_receipt_evidence_intake") or {}
+ ),
+ "detached_verification_evidence_schema": (
+ intake.get("detached_verification_evidence_schema") or {}
+ ),
+ "signed_receipt_evidence_intake_contract": (
+ intake.get("signed_receipt_evidence_intake_contract") or {}
+ ),
+ "summary": intake.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-detached-verification-evidence-validation-{digest[:16]}"
+
+
+def _db_apply_authorization_verifier_receipt_closeout_id(
+ validation: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": validation.get("policy") or "",
+ "result": validation.get("result") or "",
+ "future_detached_verification_evidence_validation": (
+ validation.get("future_detached_verification_evidence_validation") or {}
+ ),
+ "verifier_receipt_closeout_boundary": (
+ validation.get("verifier_receipt_closeout_boundary") or {}
+ ),
+ "detached_verification_evidence_validation_contract": (
+ validation.get("detached_verification_evidence_validation_contract") or {}
+ ),
+ "summary": validation.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-verifier-receipt-closeout-{digest[:16]}"
+
+
+def _db_apply_authorization_evidence_execution_preflight_id(
+ closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "future_verifier_receipt_closeout": (
+ closeout.get("future_verifier_receipt_closeout") or {}
+ ),
+ "verifier_receipt_evidence_handoff": (
+ closeout.get("verifier_receipt_evidence_handoff") or {}
+ ),
+ "verifier_receipt_closeout_contract": (
+ closeout.get("verifier_receipt_closeout_contract") or {}
+ ),
+ "summary": closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-evidence-execution-preflight-{digest[:16]}"
+
+
+def _db_apply_authorization_evidence_execution_closeout_id(
+ preflight: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": preflight.get("policy") or "",
+ "result": preflight.get("result") or "",
+ "future_database_apply_authorization_verifier_handoff": (
+ preflight.get("future_database_apply_authorization_verifier_handoff") or {}
+ ),
+ "authorization_evidence_execution_preflight": (
+ preflight.get("authorization_evidence_execution_preflight") or {}
+ ),
+ "authorization_evidence_execution_preflight_contract": (
+ preflight.get("authorization_evidence_execution_preflight_contract") or {}
+ ),
+ "summary": preflight.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-authorization-evidence-execution-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_apply_final_preflight_id(
+ closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": closeout.get("policy") or "",
+ "result": closeout.get("result") or "",
+ "future_database_apply_authorization_final_verifier_gate": (
+ closeout.get("future_database_apply_authorization_final_verifier_gate") or {}
+ ),
+ "authorization_evidence_execution_closeout": (
+ closeout.get("authorization_evidence_execution_closeout") or {}
+ ),
+ "authorization_evidence_execution_closeout_contract": (
+ closeout.get("authorization_evidence_execution_closeout_contract") or {}
+ ),
+ "summary": closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-apply-final-preflight-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_package_id(
+ final_preflight: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": final_preflight.get("policy") or "",
+ "result": final_preflight.get("result") or "",
+ "future_database_apply_controlled_apply_final_preflight": (
+ final_preflight.get("future_database_apply_controlled_apply_final_preflight")
+ or {}
+ ),
+ "controlled_apply_final_preflight": (
+ final_preflight.get("controlled_apply_final_preflight") or {}
+ ),
+ "controlled_apply_final_preflight_contract": (
+ final_preflight.get("controlled_apply_final_preflight_contract") or {}
+ ),
+ "summary": final_preflight.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-package-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_receipt_closeout_id(
+ dry_run_package: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": dry_run_package.get("policy") or "",
+ "result": dry_run_package.get("result") or "",
+ "future_database_apply_controlled_dry_run_execution_receipt": (
+ dry_run_package.get(
+ "future_database_apply_controlled_dry_run_execution_receipt"
+ )
+ or {}
+ ),
+ "controlled_dry_run_package": (
+ dry_run_package.get("controlled_dry_run_package") or {}
+ ),
+ "controlled_dry_run_package_contract": (
+ dry_run_package.get("controlled_dry_run_package_contract") or {}
+ ),
+ "summary": dry_run_package.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-receipt-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_runner_readiness_id(
+ receipt_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": receipt_closeout.get("policy") or "",
+ "result": receipt_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_result_parser_verification": (
+ receipt_closeout.get(
+ "future_database_apply_controlled_dry_run_result_parser_verification"
+ )
+ or {}
+ ),
+ "controlled_dry_run_receipt_closeout": (
+ receipt_closeout.get("controlled_dry_run_receipt_closeout") or {}
+ ),
+ "controlled_dry_run_receipt_closeout_contract": (
+ receipt_closeout.get("controlled_dry_run_receipt_closeout_contract") or {}
+ ),
+ "summary": receipt_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-runner-readiness-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_execution_plan_closeout_id(
+ runner_readiness: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": runner_readiness.get("policy") or "",
+ "result": runner_readiness.get("result") or "",
+ "future_database_apply_controlled_dry_run_execution_plan_binding": (
+ runner_readiness.get(
+ "future_database_apply_controlled_dry_run_execution_plan_binding"
+ )
+ or {}
+ ),
+ "controlled_dry_run_runner_readiness": (
+ runner_readiness.get("controlled_dry_run_runner_readiness") or {}
+ ),
+ "controlled_dry_run_runner_readiness_contract": (
+ runner_readiness.get("controlled_dry_run_runner_readiness_contract")
+ or {}
+ ),
+ "summary": runner_readiness.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-execution-plan-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_command_artifact_closeout_id(
+ execution_plan_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": execution_plan_closeout.get("policy") or "",
+ "result": execution_plan_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_command_artifact_verification": (
+ execution_plan_closeout.get(
+ "future_database_apply_controlled_dry_run_command_artifact_verification"
+ )
+ or {}
+ ),
+ "controlled_dry_run_execution_plan_closeout": (
+ execution_plan_closeout.get("controlled_dry_run_execution_plan_closeout")
+ or {}
+ ),
+ "controlled_dry_run_execution_plan_closeout_contract": (
+ execution_plan_closeout.get(
+ "controlled_dry_run_execution_plan_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": execution_plan_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-command-artifact-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_runner_execution_receipt_closeout_id(
+ command_artifact_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": command_artifact_closeout.get("policy") or "",
+ "result": command_artifact_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_runner_execution_receipt_preflight": (
+ command_artifact_closeout.get(
+ "future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ )
+ or {}
+ ),
+ "controlled_dry_run_command_artifact_closeout": (
+ command_artifact_closeout.get(
+ "controlled_dry_run_command_artifact_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_command_artifact_closeout_contract": (
+ command_artifact_closeout.get(
+ "controlled_dry_run_command_artifact_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": command_artifact_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-runner-execution-receipt-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_post_receipt_parser_closeout_id(
+ runner_execution_receipt_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": runner_execution_receipt_closeout.get("policy") or "",
+ "result": runner_execution_receipt_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_post_receipt_parser_verification": (
+ runner_execution_receipt_closeout.get(
+ "future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ )
+ or {}
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout": (
+ runner_execution_receipt_closeout.get(
+ "controlled_dry_run_runner_execution_receipt_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_contract": (
+ runner_execution_receipt_closeout.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": runner_execution_receipt_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-post-receipt-parser-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_no_apply_enforcement_closeout_id(
+ post_receipt_parser_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": post_receipt_parser_closeout.get("policy") or "",
+ "result": post_receipt_parser_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_no_apply_enforcement_verification": (
+ post_receipt_parser_closeout.get(
+ "future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ )
+ or {}
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout": (
+ post_receipt_parser_closeout.get(
+ "controlled_dry_run_post_receipt_parser_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_contract": (
+ post_receipt_parser_closeout.get(
+ "controlled_dry_run_post_receipt_parser_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": post_receipt_parser_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-no-apply-enforcement-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_final_executor_guard_closeout_id(
+ no_apply_enforcement_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": no_apply_enforcement_closeout.get("policy") or "",
+ "result": no_apply_enforcement_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_final_dry_run_executor_guard": (
+ no_apply_enforcement_closeout.get(
+ "future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ )
+ or {}
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout": (
+ no_apply_enforcement_closeout.get(
+ "controlled_dry_run_no_apply_enforcement_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_contract": (
+ no_apply_enforcement_closeout.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": no_apply_enforcement_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-final-executor-guard-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_pre_apply_replay_closeout_id(
+ final_executor_guard_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": final_executor_guard_closeout.get("policy") or "",
+ "result": final_executor_guard_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_pre_apply_replay_verifier": (
+ final_executor_guard_closeout.get(
+ "future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ )
+ or {}
+ ),
+ "controlled_dry_run_final_executor_guard_closeout": (
+ final_executor_guard_closeout.get(
+ "controlled_dry_run_final_executor_guard_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_final_executor_guard_closeout_contract": (
+ final_executor_guard_closeout.get(
+ "controlled_dry_run_final_executor_guard_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": final_executor_guard_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-pre-apply-replay-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_apply_executor_readiness_closeout_id(
+ pre_apply_replay_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": pre_apply_replay_closeout.get("policy") or "",
+ "result": pre_apply_replay_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_apply_executor_readiness_contract": (
+ pre_apply_replay_closeout.get(
+ "future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ )
+ or {}
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout": (
+ pre_apply_replay_closeout.get(
+ "controlled_dry_run_pre_apply_replay_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout_contract": (
+ pre_apply_replay_closeout.get(
+ "controlled_dry_run_pre_apply_replay_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": pre_apply_replay_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-apply-executor-readiness-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_invocation_receipt_closeout_id(
+ apply_executor_readiness_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": apply_executor_readiness_closeout.get("policy") or "",
+ "result": apply_executor_readiness_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_invocation_readiness_receipt": (
+ apply_executor_readiness_closeout.get(
+ "future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ )
+ or {}
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout": (
+ apply_executor_readiness_closeout.get(
+ "controlled_dry_run_apply_executor_readiness_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout_contract": (
+ apply_executor_readiness_closeout.get(
+ "controlled_dry_run_apply_executor_readiness_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": apply_executor_readiness_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-invocation-receipt-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_no_write_invocation_package_closeout_id(
+ invocation_receipt_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": invocation_receipt_closeout.get("policy") or "",
+ "result": invocation_receipt_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_no_write_invocation_package": (
+ invocation_receipt_closeout.get(
+ "future_database_apply_controlled_dry_run_no_write_invocation_package"
+ )
+ or {}
+ ),
+ "controlled_dry_run_invocation_receipt_closeout": (
+ invocation_receipt_closeout.get(
+ "controlled_dry_run_invocation_receipt_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_invocation_receipt_closeout_contract": (
+ invocation_receipt_closeout.get(
+ "controlled_dry_run_invocation_receipt_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": invocation_receipt_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-no-write-invocation-package-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_execution_preflight_guard_closeout_id(
+ no_write_invocation_package_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": no_write_invocation_package_closeout.get("policy") or "",
+ "result": no_write_invocation_package_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_execution_preflight_guard": (
+ no_write_invocation_package_closeout.get(
+ "future_database_apply_controlled_dry_run_execution_preflight_guard"
+ )
+ or {}
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout": (
+ no_write_invocation_package_closeout.get(
+ "controlled_dry_run_no_write_invocation_package_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout_contract": (
+ no_write_invocation_package_closeout.get(
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": no_write_invocation_package_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-execution-preflight-guard-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_runner_invocation_boundary_closeout_id(
+ execution_preflight_guard_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": execution_preflight_guard_closeout.get("policy") or "",
+ "result": execution_preflight_guard_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_runner_invocation_boundary": (
+ execution_preflight_guard_closeout.get(
+ "future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ )
+ or {}
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout": (
+ execution_preflight_guard_closeout.get(
+ "controlled_dry_run_execution_preflight_guard_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout_contract": (
+ execution_preflight_guard_closeout.get(
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": execution_preflight_guard_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-runner-invocation-boundary-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout_id(
+ runner_invocation_boundary_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": runner_invocation_boundary_closeout.get("policy") or "",
+ "result": runner_invocation_boundary_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_no_execution_receipt_handoff": (
+ runner_invocation_boundary_closeout.get(
+ "future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ )
+ or {}
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout": (
+ runner_invocation_boundary_closeout.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract": (
+ runner_invocation_boundary_closeout.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": runner_invocation_boundary_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout_id(
+ no_execution_receipt_handoff_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": no_execution_receipt_handoff_closeout.get("policy") or "",
+ "result": no_execution_receipt_handoff_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_final_no_runner_execution_proof": (
+ no_execution_receipt_handoff_closeout.get(
+ "future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout": (
+ no_execution_receipt_handoff_closeout.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract": (
+ no_execution_receipt_handoff_closeout.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": no_execution_receipt_handoff_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout_id(
+ final_no_runner_execution_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": final_no_runner_execution_proof_closeout.get("policy") or "",
+ "result": final_no_runner_execution_proof_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof": (
+ final_no_runner_execution_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout": (
+ final_no_runner_execution_proof_closeout.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract": (
+ final_no_runner_execution_proof_closeout.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": final_no_runner_execution_proof_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout_id(
+ controlled_executor_quarantine_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": controlled_executor_quarantine_proof_closeout.get("policy") or "",
+ "result": controlled_executor_quarantine_proof_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_execution_envelope_freeze_proof": (
+ controlled_executor_quarantine_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout": (
+ controlled_executor_quarantine_proof_closeout.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract": (
+ controlled_executor_quarantine_proof_closeout.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": controlled_executor_quarantine_proof_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout_id(
+ execution_envelope_freeze_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": execution_envelope_freeze_proof_closeout.get("policy") or "",
+ "result": execution_envelope_freeze_proof_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff": (
+ execution_envelope_freeze_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ )
+ or {}
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout": (
+ execution_envelope_freeze_proof_closeout.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract": (
+ execution_envelope_freeze_proof_closeout.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": execution_envelope_freeze_proof_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout_id(
+ frozen_envelope_verifier_handoff_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": frozen_envelope_verifier_handoff_closeout.get("policy") or "",
+ "result": frozen_envelope_verifier_handoff_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_verifier_invocation_lock_proof": (
+ frozen_envelope_verifier_handoff_closeout.get(
+ "future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout": (
+ frozen_envelope_verifier_handoff_closeout.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract": (
+ frozen_envelope_verifier_handoff_closeout.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": frozen_envelope_verifier_handoff_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout_id(
+ verifier_invocation_lock_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": verifier_invocation_lock_proof_closeout.get("policy") or "",
+ "result": verifier_invocation_lock_proof_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof": (
+ verifier_invocation_lock_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout": (
+ verifier_invocation_lock_proof_closeout.get(
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_contract": (
+ verifier_invocation_lock_proof_closeout.get(
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": verifier_invocation_lock_proof_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_id(
+ verifier_no_execution_receipt_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": verifier_no_execution_receipt_proof_closeout.get("policy") or "",
+ "result": verifier_no_execution_receipt_proof_closeout.get("result") or "",
+ "future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof": (
+ verifier_no_execution_receipt_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout": (
+ verifier_no_execution_receipt_proof_closeout.get(
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract": (
+ verifier_no_execution_receipt_proof_closeout.get(
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": verifier_no_execution_receipt_proof_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_id(
+ verifier_receipt_persistence_guard_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": verifier_receipt_persistence_guard_proof_closeout.get("policy")
+ or "",
+ "result": verifier_receipt_persistence_guard_proof_closeout.get("result")
+ or "",
+ "future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof": (
+ verifier_receipt_persistence_guard_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout": (
+ verifier_receipt_persistence_guard_proof_closeout.get(
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract": (
+ verifier_receipt_persistence_guard_proof_closeout.get(
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": verifier_receipt_persistence_guard_proof_closeout.get(
+ "summary"
+ )
+ or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_id(
+ receipt_persistence_storage_boundary_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": receipt_persistence_storage_boundary_proof_closeout.get("policy")
+ or "",
+ "result": receipt_persistence_storage_boundary_proof_closeout.get("result")
+ or "",
+ "future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof": (
+ receipt_persistence_storage_boundary_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout": (
+ receipt_persistence_storage_boundary_proof_closeout.get(
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract": (
+ receipt_persistence_storage_boundary_proof_closeout.get(
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": receipt_persistence_storage_boundary_proof_closeout.get(
+ "summary"
+ )
+ or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout_id(
+ storage_boundary_no_write_ledger_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": storage_boundary_no_write_ledger_proof_closeout.get("policy")
+ or "",
+ "result": storage_boundary_no_write_ledger_proof_closeout.get("result")
+ or "",
+ "future_database_apply_controlled_dry_run_no_write_ledger_retention_proof": (
+ storage_boundary_no_write_ledger_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout": (
+ storage_boundary_no_write_ledger_proof_closeout.get(
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract": (
+ storage_boundary_no_write_ledger_proof_closeout.get(
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": storage_boundary_no_write_ledger_proof_closeout.get(
+ "summary"
+ )
+ or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_id(
+ no_write_ledger_retention_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": no_write_ledger_retention_proof_closeout.get("policy")
+ or "",
+ "result": no_write_ledger_retention_proof_closeout.get("result")
+ or "",
+ "future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof": (
+ no_write_ledger_retention_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout": (
+ no_write_ledger_retention_proof_closeout.get(
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_contract": (
+ no_write_ledger_retention_proof_closeout.get(
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": no_write_ledger_retention_proof_closeout.get("summary") or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout-{digest[:16]}"
+
+
+def _db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_id(
+ retention_boundary_no_write_archive_proof_closeout: dict[str, Any],
+) -> str:
+ payload = {
+ "policy": retention_boundary_no_write_archive_proof_closeout.get("policy")
+ or "",
+ "result": retention_boundary_no_write_archive_proof_closeout.get("result")
+ or "",
+ "future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof": (
+ retention_boundary_no_write_archive_proof_closeout.get(
+ "future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ )
+ or {}
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout": (
+ retention_boundary_no_write_archive_proof_closeout.get(
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ )
+ or {}
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract": (
+ retention_boundary_no_write_archive_proof_closeout.get(
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract"
+ )
+ or {}
+ ),
+ "summary": retention_boundary_no_write_archive_proof_closeout.get(
+ "summary"
+ )
+ or {},
+ }
+ digest = hashlib.sha256(json.dumps(payload, sort_keys=True).encode("utf-8")).hexdigest()
+ return f"pchome-db-apply-controlled-dry-run-archive-retention-sealed-handoff-proof-closeout-{digest[:16]}"
+
+
+def _dry_run_closeout_check(key: str, passed: bool, evidence: Any, failure_route: str) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_request_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_lane_guard_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_decision_preflight_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_decision_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_issuer_gate_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_signing_decision_preflight_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_signing_decision_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_signing_issuer_guard_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_signing_issuer_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_signing_execution_preflight_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_signing_execution_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_signed_receipt_preflight_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_signed_receipt_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_signed_receipt_evidence_intake_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_detached_verification_evidence_validation_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_verifier_receipt_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_evidence_execution_preflight_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _authorization_evidence_execution_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_apply_final_preflight_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_package_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_receipt_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_runner_readiness_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_execution_plan_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_command_artifact_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_runner_execution_receipt_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_post_receipt_parser_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_no_apply_enforcement_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_final_executor_guard_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_pre_apply_replay_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_apply_executor_readiness_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_invocation_receipt_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_no_write_invocation_package_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_execution_preflight_guard_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ key: str,
+ passed: bool,
+ evidence: Any,
+ failure_route: str,
+) -> dict[str, Any]:
+ return {
+ "key": key,
+ "status": "pass" if passed else "waiting",
+ "passed": bool(passed),
+ "evidence": evidence,
+ "failure_route": failure_route,
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_shell_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the controlled dry-run shell preview without executing it."""
+ shell_preview = build_pchome_auto_policy_db_apply_controlled_dry_run_shell_preview(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ shell = shell_preview.get("controlled_dry_run_shell_preview") or {}
+ summary = shell_preview.get("summary") or {}
+ shell_script = shell_preview.get("shell_script_preview") or {}
+ check_mode = shell_preview.get("check_mode_contract") or {}
+ rollback_hooks = shell_preview.get("rollback_hook_preview") or {}
+ safety = shell_preview.get("safety") or {}
+ checks = [
+ _dry_run_closeout_check(
+ "production_truth_required",
+ True,
+ "production /health must be refreshed immediately before any future explicit DB apply",
+ "abort_before_authorization_boundary",
+ ),
+ _dry_run_closeout_check(
+ "controlled_dry_run_shell_preview_ready",
+ shell_preview.get("result") == "DB_APPLY_CONTROLLED_DRY_RUN_SHELL_PREVIEW_READY"
+ and bool(shell.get("ready_for_future_shell_script_generation")),
+ {
+ "result": shell_preview.get("result"),
+ "ready_for_future_shell_script_generation": shell.get(
+ "ready_for_future_shell_script_generation"
+ ),
+ },
+ "wait_for_controlled_dry_run_shell_preview",
+ ),
+ _dry_run_closeout_check(
+ "final_handoff_ready",
+ int(summary.get("final_handoff_ready_count") or 0) == 1,
+ {"final_handoff_ready_count": summary.get("final_handoff_ready_count", 0)},
+ "wait_for_final_handoff_package",
+ ),
+ _dry_run_closeout_check(
+ "shell_phase_contract_complete",
+ int(summary.get("shell_phase_count") or 0) >= 9,
+ {"shell_phase_count": summary.get("shell_phase_count", 0)},
+ "wait_for_shell_phase_contract",
+ ),
+ _dry_run_closeout_check(
+ "shell_script_preview_complete",
+ int(summary.get("shell_script_line_count") or 0) >= 10
+ and shell_script.get("writes_file_in_preview") is False
+ and shell_script.get("executes_script_in_preview") is False,
+ {
+ "shell_script_line_count": summary.get("shell_script_line_count", 0),
+ "writes_file_in_preview": shell_script.get("writes_file_in_preview"),
+ "executes_script_in_preview": shell_script.get("executes_script_in_preview"),
+ },
+ "wait_for_shell_script_preview",
+ ),
+ _dry_run_closeout_check(
+ "check_mode_contract_complete",
+ int(check_mode.get("required_check_count") or 0) >= 6
+ and check_mode.get("dry_run_only") is True,
+ {
+ "required_check_count": check_mode.get("required_check_count", 0),
+ "dry_run_only": check_mode.get("dry_run_only"),
+ },
+ "wait_for_check_mode_contract",
+ ),
+ _dry_run_closeout_check(
+ "rollback_hooks_complete",
+ int(rollback_hooks.get("hook_count") or 0) >= 3,
+ {"rollback_hook_count": rollback_hooks.get("hook_count", 0)},
+ "wait_for_rollback_hooks",
+ ),
+ _dry_run_closeout_check(
+ "preview_writes_no_script",
+ int(summary.get("writes_script_count") or 0) == 0
+ and shell.get("ready_to_write_script_now") is False
+ and safety.get("writes_script_in_preview") is False,
+ {
+ "writes_script_count": summary.get("writes_script_count", 0),
+ "ready_to_write_script_now": shell.get("ready_to_write_script_now"),
+ "safety_writes_script_in_preview": safety.get("writes_script_in_preview"),
+ },
+ "block_until_no_script_write_preview",
+ ),
+ _dry_run_closeout_check(
+ "preview_executes_no_shell",
+ int(summary.get("executes_script_count") or 0) == 0
+ and shell.get("ready_to_execute_shell_now") is False
+ and safety.get("executes_script") is False,
+ {
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "ready_to_execute_shell_now": shell.get("ready_to_execute_shell_now"),
+ "safety_executes_script": safety.get("executes_script"),
+ },
+ "block_until_no_shell_execution_preview",
+ ),
+ _dry_run_closeout_check(
+ "preview_reads_no_secret",
+ int(summary.get("reads_secret_count") or 0) == 0
+ and shell.get("reads_secret_in_preview") is False
+ and safety.get("reads_secret_in_preview") is False,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "shell_reads_secret_in_preview": shell.get("reads_secret_in_preview"),
+ "safety_reads_secret_in_preview": safety.get("reads_secret_in_preview"),
+ },
+ "block_until_secret_boundary_clean",
+ ),
+ _dry_run_closeout_check(
+ "preview_executes_no_sql",
+ int(summary.get("executes_sql_count") or 0) == 0
+ and shell.get("executes_sql_in_preview") is False
+ and safety.get("executes_sql") is False,
+ {
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "shell_executes_sql_in_preview": shell.get("executes_sql_in_preview"),
+ "safety_executes_sql": safety.get("executes_sql"),
+ },
+ "block_until_no_sql_preview",
+ ),
+ _dry_run_closeout_check(
+ "preview_writes_no_database",
+ int(summary.get("writes_database_count") or 0) == 0
+ and shell.get("writes_database_in_preview") is False
+ and safety.get("writes_database") is False,
+ {
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "shell_writes_database_in_preview": shell.get("writes_database_in_preview"),
+ "safety_writes_database": safety.get("writes_database"),
+ },
+ "block_until_no_db_write_preview",
+ ),
+ _dry_run_closeout_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0,
+ {LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0)},
+ "route_only_failed_verifiers_to_exception_review",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_SHELL_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_CONTROLLED_DRY_RUN_SHELL_PREVIEW"
+ )
+ explicit_authorization_boundary = {
+ "boundary_id": _db_apply_controlled_dry_run_shell_closeout_id(shell_preview),
+ "source_dry_run_shell_preview_id": shell.get("preview_id"),
+ "source_final_handoff_package_id": shell.get("source_final_handoff_package_id"),
+ "source_artifact_preview_id": shell.get("source_artifact_preview_id"),
+ "source_authorization_package_id": shell.get("source_authorization_package_id"),
+ "source_preflight_id": shell.get("source_preflight_id"),
+ "source_request_id": shell.get("source_request_id"),
+ "status": closeout_status,
+ "ready_for_explicit_apply_authorization_boundary": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "target_file": shell.get("target_file"),
+ "expected_sha256": shell.get("expected_sha256"),
+ "actual_sha256": shell.get("actual_sha256"),
+ "hash_matches": shell.get("hash_matches"),
+ "requires_new_explicit_db_apply_authorization": True,
+ "requires_fresh_production_truth_in_future_run": True,
+ "requires_future_shell_secret_injection": True,
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "writes_script_in_preview": False,
+ "executes_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ future_apply_boundaries = [
+ {
+ "key": "fresh_production_truth_same_run",
+ "required": True,
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ },
+ {
+ "key": "fresh_final_handoff_and_dry_run_closeout",
+ "required": True,
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-closeout",
+ },
+ {
+ "key": "database_url_from_future_shell_only",
+ "required": True,
+ "reads_secret_in_preview": False,
+ },
+ {
+ "key": "prewrite_snapshot_before_sql",
+ "required": True,
+ "writes_artifact_in_preview": False,
+ },
+ {
+ "key": "post_apply_verifier_after_sql",
+ "required": True,
+ "executes_sql_in_preview": False,
+ },
+ {
+ "key": "rollback_artifact_on_verifier_failure",
+ "required": True,
+ "writes_artifact_in_preview": False,
+ },
+ ]
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_SHELL_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(shell_preview.get("success")),
+ "generated_at": shell_preview.get("generated_at"),
+ "source_policy": shell_preview.get("policy"),
+ "stats": shell_preview.get("stats") or {},
+ "summary": {
+ "closeout_ready_count": 1 if closeout_ready else 0,
+ "closeout_check_count": len(checks),
+ "closeout_pass_count": passed_count,
+ "closeout_waiting_count": len(waiting_checks),
+ "dry_run_shell_preview_ready_count": summary.get("dry_run_shell_preview_ready_count", 0),
+ "future_apply_boundary_count": len(future_apply_boundaries),
+ "shell_phase_count": summary.get("shell_phase_count", 0),
+ "shell_script_line_count": summary.get("shell_script_line_count", 0),
+ "check_mode_required_check_count": summary.get("check_mode_required_check_count", 0),
+ "rollback_hook_count": summary.get("rollback_hook_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "controlled_dry_run_shell_closeout": {
+ "status": closeout_status,
+ "ready_for_explicit_apply_authorization_boundary": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ },
+ "explicit_authorization_boundary": explicit_authorization_boundary,
+ "future_apply_boundaries": future_apply_boundaries,
+ "closeout_checks": checks,
+ "source_dry_run_shell_summary": summary,
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_shell_closeout": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Treat this closeout as the final no-write boundary before any separate explicit DB apply authorization.",
+ "Do not execute shell, SQL, or DB writes from this preview.",
+ "Refresh production truth, dry-run closeout, and artifacts inside the future apply run.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_decision_preflight(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Prepare no-write inputs for a future DB apply authorization decision."""
+ lane_guard = build_pchome_auto_policy_db_apply_authorization_lane_guard(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ lane = lane_guard.get("future_authorization_lane_guard") or {}
+ contract = lane_guard.get("lane_transfer_contract") or {}
+ summary = lane_guard.get("summary") or {}
+ source_package = lane_guard.get("source_final_exact_request_package") or {}
+ source_manifest = lane_guard.get("source_machine_request_manifest") or {}
+ source_manifest_steps = source_manifest.get("manifest_steps") or []
+ lane_requirements = lane_guard.get("lane_entry_requirements") or []
+ requirement_keys = {item.get("key") for item in lane_requirements}
+ side_effect_free = (
+ int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and lane.get("reads_secret_in_preview") is False
+ and lane.get("executes_shell_in_preview") is False
+ and lane.get("executes_sql_in_preview") is False
+ and lane.get("writes_database_in_preview") is False
+ )
+ decision_input_requirements = [
+ {
+ "key": "fresh_production_truth_same_run",
+ "required": True,
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ },
+ {
+ "key": "authorization_lane_guard_id",
+ "required": True,
+ "source_id": lane.get("guard_id"),
+ },
+ {
+ "key": "final_exact_request_package_id",
+ "required": True,
+ "source_id": lane.get("source_closeout_package_id"),
+ },
+ {
+ "key": "exact_request_payload",
+ "required": True,
+ "field_count": summary.get("exact_request_payload_field_count", 0),
+ },
+ {
+ "key": "migration_target_hash",
+ "required": True,
+ "target_file": lane.get("target_file"),
+ "expected_sha256": lane.get("expected_sha256"),
+ "hash_matches": lane.get("hash_matches"),
+ },
+ {
+ "key": "secret_boundary_rejection_proof",
+ "required": True,
+ "operator_secret_boundary": lane.get("operator_secret_boundary"),
+ "reads_secret_in_preview": False,
+ },
+ {
+ "key": "rollback_boundary_acknowledgement",
+ "required": True,
+ },
+ {
+ "key": "post_apply_verifier_reference",
+ "required": True,
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview",
+ },
+ ]
+ decision_rejection_policy = [
+ "production_truth_missing_or_stale",
+ "authorization_lane_guard_not_ready",
+ "exact_request_payload_missing_or_mutated",
+ "migration_hash_missing_or_mismatch",
+ "secret_material_present_in_request",
+ "rollback_boundary_missing",
+ "post_apply_verifier_missing",
+ "direct_database_apply_requested_from_decision_preflight",
+ "preview_attempted_shell_sql_or_database_side_effect",
+ "manual_review_batch_regression_detected",
+ ]
+ checks = [
+ _authorization_decision_preflight_check(
+ "authorization_lane_guard_ready",
+ lane_guard.get("result") == "DB_APPLY_AUTHORIZATION_LANE_GUARD_READY"
+ and lane.get("ready_for_future_authorization_lane_entry") is True,
+ {
+ "result": lane_guard.get("result"),
+ "ready_for_future_authorization_lane_entry": lane.get(
+ "ready_for_future_authorization_lane_entry"
+ ),
+ },
+ "wait_for_authorization_lane_guard",
+ ),
+ _authorization_decision_preflight_check(
+ "lane_contract_is_no_authorization",
+ contract.get("issues_database_apply_authorization") is False
+ and contract.get("ready_for_database_apply_now") is False
+ and contract.get("writes_database") is False,
+ {
+ "issues_database_apply_authorization": contract.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": contract.get("ready_for_database_apply_now"),
+ "writes_database": contract.get("writes_database"),
+ },
+ "block_if_lane_contract_authorizes_apply",
+ ),
+ _authorization_decision_preflight_check(
+ "same_run_production_truth_required",
+ "production_truth_refreshed_in_same_run" in requirement_keys
+ and lane.get("requires_fresh_production_truth_in_same_run") is True,
+ {
+ "requirement_keys": sorted(key for key in requirement_keys if key),
+ "requires_fresh_production_truth_in_same_run": lane.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_decision_preflight_check(
+ "decision_inputs_complete",
+ len(decision_input_requirements) == 8
+ and all(item.get("required") is True for item in decision_input_requirements),
+ {"decision_input_requirement_count": len(decision_input_requirements)},
+ "wait_for_decision_input_requirements",
+ ),
+ _authorization_decision_preflight_check(
+ "exact_request_payload_complete",
+ int(summary.get("exact_request_payload_field_count") or 0) == 10
+ and source_package.get("payload_template_field_count") == 10,
+ {
+ "exact_request_payload_field_count": summary.get(
+ "exact_request_payload_field_count", 0
+ ),
+ "payload_template_field_count": source_package.get("payload_template_field_count"),
+ },
+ "wait_for_exact_request_payload",
+ ),
+ _authorization_decision_preflight_check(
+ "migration_target_hash_locked",
+ bool(lane.get("target_file"))
+ and bool(lane.get("expected_sha256"))
+ and lane.get("hash_matches") is True,
+ {"target_file": lane.get("target_file"), "hash_matches": lane.get("hash_matches")},
+ "abort_on_migration_hash_gap",
+ ),
+ _authorization_decision_preflight_check(
+ "secret_boundary_rejects_secret_material",
+ "secret_material_absent_from_request" in requirement_keys
+ and lane.get("operator_secret_boundary") == "future_shell_only"
+ and lane.get("reads_secret_in_preview") is False,
+ {
+ "operator_secret_boundary": lane.get("operator_secret_boundary"),
+ "reads_secret_in_preview": lane.get("reads_secret_in_preview"),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_decision_preflight_check(
+ "rollback_boundary_required",
+ "rollback_boundary_acknowledged" in requirement_keys,
+ {"requirement_keys": sorted(key for key in requirement_keys if key)},
+ "block_until_rollback_boundary_is_present",
+ ),
+ _authorization_decision_preflight_check(
+ "source_manifest_complete",
+ source_manifest.get("manifest_step_count") == 6
+ and len(source_manifest_steps) == 6,
+ {
+ "manifest_step_count": source_manifest.get("manifest_step_count"),
+ "manifest_step_len": len(source_manifest_steps),
+ },
+ "wait_for_machine_request_manifest",
+ ),
+ _authorization_decision_preflight_check(
+ "rejection_policy_complete",
+ len(decision_rejection_policy) == 10
+ and "direct_database_apply_requested_from_decision_preflight" in decision_rejection_policy,
+ {"decision_rejection_reason_count": len(decision_rejection_policy)},
+ "wait_for_decision_rejection_policy",
+ ),
+ _authorization_decision_preflight_check(
+ "preview_has_no_side_effects",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ },
+ "abort_on_preview_side_effect",
+ ),
+ _authorization_decision_preflight_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0,
+ {LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0)},
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ preflight_ready = not waiting_checks
+ preflight_status = (
+ "DB_APPLY_AUTHORIZATION_DECISION_PREFLIGHT_READY"
+ if preflight_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_LANE_GUARD"
+ )
+ future_authorization_decision_preflight = {
+ "preflight_id": _db_apply_authorization_decision_preflight_id(lane_guard),
+ "source_lane_guard_id": lane.get("guard_id"),
+ "source_closeout_package_id": lane.get("source_closeout_package_id"),
+ "source_intake_id": lane.get("source_intake_id"),
+ "source_closeout_boundary_id": lane.get("source_closeout_boundary_id"),
+ "status": preflight_status,
+ "ready_for_future_authorization_decision": preflight_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "can_enter_authorization_decision_lane": preflight_ready,
+ "request_scope": "future_explicit_db_apply_authorization_only",
+ "target_file": lane.get("target_file"),
+ "expected_sha256": lane.get("expected_sha256"),
+ "actual_sha256": lane.get("actual_sha256"),
+ "hash_matches": lane.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "executes_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ decision_preflight_envelope = {
+ "mode": "authorization_decision_preflight_only",
+ "allows_authorization_decision_in_future_lane": preflight_ready,
+ "issues_database_apply_authorization": False,
+ "ready_for_database_apply_now": False,
+ "rejects_direct_database_apply": True,
+ "requires_post_apply_verifier": True,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_DECISION_PREFLIGHT_POLICY,
+ "result": preflight_status,
+ "success": bool(lane_guard.get("success")),
+ "generated_at": lane_guard.get("generated_at"),
+ "source_policy": lane_guard.get("policy"),
+ "stats": lane_guard.get("stats") or {},
+ "summary": {
+ "authorization_decision_preflight_ready_count": 1 if preflight_ready else 0,
+ "decision_preflight_check_count": len(checks),
+ "decision_preflight_pass_count": passed_count,
+ "decision_preflight_waiting_count": len(waiting_checks),
+ "authorization_lane_guard_ready_count": summary.get(
+ "authorization_lane_guard_ready_count", 0
+ ),
+ "decision_input_requirement_count": len(decision_input_requirements),
+ "decision_rejection_reason_count": len(decision_rejection_policy),
+ "lane_entry_requirement_count": summary.get("lane_entry_requirement_count", 0),
+ "exact_request_payload_field_count": summary.get(
+ "exact_request_payload_field_count", 0
+ ),
+ "machine_request_manifest_step_count": summary.get(
+ "machine_request_manifest_step_count", 0
+ ),
+ "required_request_evidence_count": summary.get("required_request_evidence_count", 0),
+ "authorization_acceptance_gate_count": summary.get(
+ "authorization_acceptance_gate_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_decision_preflight": future_authorization_decision_preflight,
+ "decision_preflight_envelope": decision_preflight_envelope,
+ "decision_input_requirements": decision_input_requirements,
+ "decision_rejection_policy": decision_rejection_policy,
+ "decision_preflight_checks": checks,
+ "source_lane_guard_summary": summary,
+ "source_lane_transfer_contract": contract,
+ "source_final_exact_request_package": source_package,
+ "safety": {
+ "read_only_db_apply_authorization_decision_preflight": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this preflight to verify inputs before a future authorization decision lane.",
+ "Keep actual DB apply authorization out of this preflight.",
+ "Require same-run production truth, secret rejection, rollback boundary, and post-apply verifier before any future decision.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_decision_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the no-write package for a future authorization decision lane."""
+ preflight = build_pchome_auto_policy_db_apply_authorization_decision_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ decision = preflight.get("future_authorization_decision_preflight") or {}
+ envelope = preflight.get("decision_preflight_envelope") or {}
+ summary = preflight.get("summary") or {}
+ decision_input_requirements = preflight.get("decision_input_requirements") or []
+ decision_rejection_policy = preflight.get("decision_rejection_policy") or []
+ input_keys = {item.get("key") for item in decision_input_requirements}
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and decision.get("reads_secret_in_preview") is False
+ and decision.get("executes_shell_in_preview") is False
+ and decision.get("executes_sql_in_preview") is False
+ and decision.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_decision_closeout_check(
+ "authorization_decision_preflight_ready",
+ preflight.get("result") == "DB_APPLY_AUTHORIZATION_DECISION_PREFLIGHT_READY"
+ and decision.get("ready_for_future_authorization_decision") is True,
+ {
+ "result": preflight.get("result"),
+ "ready_for_future_authorization_decision": decision.get(
+ "ready_for_future_authorization_decision"
+ ),
+ },
+ "wait_for_authorization_decision_preflight",
+ ),
+ _authorization_decision_closeout_check(
+ "decision_envelope_allows_future_lane_only",
+ envelope.get("allows_authorization_decision_in_future_lane") is True
+ and envelope.get("issues_database_apply_authorization") is False
+ and envelope.get("ready_for_database_apply_now") is False,
+ {
+ "allows_authorization_decision_in_future_lane": envelope.get(
+ "allows_authorization_decision_in_future_lane"
+ ),
+ "issues_database_apply_authorization": envelope.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": envelope.get("ready_for_database_apply_now"),
+ },
+ "block_if_decision_envelope_authorizes_apply",
+ ),
+ _authorization_decision_closeout_check(
+ "decision_inputs_complete",
+ len(decision_input_requirements) == 8
+ and all(item.get("required") is True for item in decision_input_requirements),
+ {
+ "decision_input_requirement_count": len(decision_input_requirements),
+ "input_keys": sorted(key for key in input_keys if key),
+ },
+ "wait_for_decision_input_requirements",
+ ),
+ _authorization_decision_closeout_check(
+ "decision_rejection_policy_complete",
+ len(decision_rejection_policy) == 10
+ and "direct_database_apply_requested_from_decision_preflight"
+ in decision_rejection_policy,
+ {"decision_rejection_reason_count": len(decision_rejection_policy)},
+ "wait_for_decision_rejection_policy",
+ ),
+ _authorization_decision_closeout_check(
+ "same_run_production_truth_required",
+ "fresh_production_truth_same_run" in input_keys
+ and decision.get("requires_fresh_production_truth_in_same_run") is True,
+ {
+ "input_keys": sorted(key for key in input_keys if key),
+ "requires_fresh_production_truth_in_same_run": decision.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_decision_closeout_check(
+ "post_apply_verifier_required",
+ "post_apply_verifier_reference" in input_keys
+ and envelope.get("requires_post_apply_verifier") is True,
+ {
+ "input_keys": sorted(key for key in input_keys if key),
+ "requires_post_apply_verifier": envelope.get("requires_post_apply_verifier"),
+ },
+ "require_post_apply_verifier_artifact",
+ ),
+ _authorization_decision_closeout_check(
+ "migration_target_hash_locked",
+ bool(decision.get("target_file"))
+ and bool(decision.get("expected_sha256"))
+ and decision.get("hash_matches") is True,
+ {
+ "target_file": decision.get("target_file"),
+ "hash_matches": decision.get("hash_matches"),
+ },
+ "abort_on_migration_hash_gap",
+ ),
+ _authorization_decision_closeout_check(
+ "secret_boundary_clean",
+ "secret_boundary_rejection_proof" in input_keys
+ and decision.get("operator_secret_boundary") == "future_shell_only"
+ and decision.get("reads_secret_in_preview") is False,
+ {
+ "operator_secret_boundary": decision.get("operator_secret_boundary"),
+ "reads_secret_in_preview": decision.get("reads_secret_in_preview"),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_decision_closeout_check(
+ "source_lane_guard_and_package_ids_present",
+ bool(decision.get("preflight_id"))
+ and bool(decision.get("source_lane_guard_id"))
+ and bool(decision.get("source_closeout_package_id"))
+ and bool(decision.get("source_intake_id"))
+ and bool(decision.get("source_closeout_boundary_id")),
+ {
+ "preflight_id": decision.get("preflight_id"),
+ "source_lane_guard_id": decision.get("source_lane_guard_id"),
+ "source_closeout_package_id": decision.get("source_closeout_package_id"),
+ "source_intake_id": decision.get("source_intake_id"),
+ "source_closeout_boundary_id": decision.get("source_closeout_boundary_id"),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_decision_closeout_check(
+ "preview_has_no_side_effects",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ },
+ "abort_on_preview_side_effect",
+ ),
+ _authorization_decision_closeout_check(
+ "direct_apply_still_rejected",
+ envelope.get("rejects_direct_database_apply") is True
+ and decision.get("issues_database_apply_authorization") is False
+ and decision.get("ready_for_database_apply_now") is False,
+ {
+ "rejects_direct_database_apply": envelope.get("rejects_direct_database_apply"),
+ "issues_database_apply_authorization": decision.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": decision.get("ready_for_database_apply_now"),
+ },
+ "reject_direct_database_apply_from_closeout",
+ ),
+ _authorization_decision_closeout_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and decision.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": decision.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_AUTHORIZATION_DECISION_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_DECISION_PREFLIGHT"
+ )
+ future_authorization_decision_closeout = {
+ "closeout_id": _db_apply_authorization_decision_closeout_id(preflight),
+ "source_preflight_id": decision.get("preflight_id"),
+ "source_lane_guard_id": decision.get("source_lane_guard_id"),
+ "source_closeout_package_id": decision.get("source_closeout_package_id"),
+ "source_intake_id": decision.get("source_intake_id"),
+ "source_closeout_boundary_id": decision.get("source_closeout_boundary_id"),
+ "status": closeout_status,
+ "ready_for_future_authorization_decision_closeout": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ future_authorization_decision_package = {
+ "package_id": future_authorization_decision_closeout["closeout_id"],
+ "source_preflight_id": decision.get("preflight_id"),
+ "source_lane_guard_id": decision.get("source_lane_guard_id"),
+ "source_closeout_package_id": decision.get("source_closeout_package_id"),
+ "source_intake_id": decision.get("source_intake_id"),
+ "source_closeout_boundary_id": decision.get("source_closeout_boundary_id"),
+ "status": closeout_status,
+ "ready_for_future_authorization_decision_package": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "decision_scope": "future_explicit_db_apply_authorization_decision_only",
+ "target_file": decision.get("target_file"),
+ "expected_sha256": decision.get("expected_sha256"),
+ "actual_sha256": decision.get("actual_sha256"),
+ "hash_matches": decision.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview"
+ ),
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "executes_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ decision_closeout_contract = {
+ "mode": "future_authorization_decision_closeout_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-closeout"
+ ),
+ "source_preflight_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-preflight"
+ ),
+ "machine_verifiable": True,
+ "permits_future_authorization_decision_lane": closeout_ready,
+ "issues_database_apply_authorization": False,
+ "ready_for_database_apply_now": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_DECISION_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(preflight.get("success")),
+ "generated_at": preflight.get("generated_at"),
+ "source_policy": preflight.get("policy"),
+ "stats": preflight.get("stats") or {},
+ "summary": {
+ "authorization_decision_closeout_ready_count": 1 if closeout_ready else 0,
+ "decision_closeout_check_count": len(checks),
+ "decision_closeout_pass_count": passed_count,
+ "decision_closeout_waiting_count": len(waiting_checks),
+ "authorization_decision_preflight_ready_count": summary.get(
+ "authorization_decision_preflight_ready_count", 0
+ ),
+ "decision_input_requirement_count": len(decision_input_requirements),
+ "decision_rejection_reason_count": len(decision_rejection_policy),
+ "post_apply_verifier_required_count": (
+ 1 if envelope.get("requires_post_apply_verifier") is True else 0
+ ),
+ "same_run_truth_required_count": (
+ 1 if decision.get("requires_fresh_production_truth_in_same_run") is True else 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_decision_closeout": future_authorization_decision_closeout,
+ "future_authorization_decision_package": future_authorization_decision_package,
+ "decision_closeout_contract": decision_closeout_contract,
+ "decision_closeout_checks": checks,
+ "source_decision_preflight_summary": summary,
+ "source_decision_input_requirements": decision_input_requirements,
+ "source_decision_rejection_policy": decision_rejection_policy,
+ "safety": {
+ "read_only_db_apply_authorization_decision_closeout": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout as the machine-readable package for a future explicit authorization decision lane.",
+ "Keep database apply authorization and SQL execution out of this closeout.",
+ "Require fresh production truth, secret rejection, rollback boundary, and post-apply verifier inside the future decision run.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_issuer_gate(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-secret issuer gate envelope without signing DB apply authorization."""
+ closeout = build_pchome_auto_policy_db_apply_authorization_decision_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ decision = closeout.get("future_authorization_decision_closeout") or {}
+ package = closeout.get("future_authorization_decision_package") or {}
+ contract = closeout.get("decision_closeout_contract") or {}
+ summary = closeout.get("summary") or {}
+ rejection_policy = closeout.get("source_decision_rejection_policy") or []
+ source_inputs = closeout.get("source_decision_input_requirements") or []
+ source_input_keys = {item.get("key") for item in source_inputs}
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and package.get("reads_secret_in_preview") is False
+ and package.get("executes_shell_in_preview") is False
+ and package.get("executes_sql_in_preview") is False
+ and package.get("writes_database_in_preview") is False
+ )
+ required_issuer_evidence = [
+ {
+ "key": "fresh_production_truth_same_run",
+ "required": True,
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ },
+ {
+ "key": "decision_closeout_id",
+ "required": True,
+ "source_id": decision.get("closeout_id"),
+ },
+ {
+ "key": "decision_preflight_id",
+ "required": True,
+ "source_id": package.get("source_preflight_id"),
+ },
+ {
+ "key": "authorization_lane_guard_id",
+ "required": True,
+ "source_id": package.get("source_lane_guard_id"),
+ },
+ {
+ "key": "final_request_package_id",
+ "required": True,
+ "source_id": package.get("source_closeout_package_id"),
+ },
+ {
+ "key": "migration_target_hash",
+ "required": True,
+ "target_file": package.get("target_file"),
+ "expected_sha256": package.get("expected_sha256"),
+ "hash_matches": package.get("hash_matches"),
+ },
+ {
+ "key": "secret_boundary_rejection",
+ "required": True,
+ "operator_secret_boundary": package.get("operator_secret_boundary"),
+ "secret_material_included": False,
+ },
+ {
+ "key": "post_apply_verifier_reference",
+ "required": True,
+ "source_endpoint": package.get("post_apply_verifier_endpoint"),
+ },
+ {
+ "key": "direct_apply_rejection_policy",
+ "required": True,
+ "source_rejection": "direct_database_apply_requested_from_decision_preflight",
+ },
+ ]
+ nonsecret_authorization_claims = [
+ {
+ "key": "source_decision_closeout_ready",
+ "claim": decision.get("ready_for_future_authorization_decision_closeout") is True,
+ },
+ {
+ "key": "no_database_apply_authorization_issued",
+ "claim": package.get("issues_database_apply_authorization") is False,
+ },
+ {
+ "key": "no_secret_material_included",
+ "claim": package.get("reads_secret_in_preview") is False,
+ },
+ {
+ "key": "no_shell_sql_or_database_execution",
+ "claim": side_effect_free,
+ },
+ {
+ "key": "target_migration_hash_locked",
+ "claim": package.get("hash_matches") is True,
+ },
+ {
+ "key": "same_run_production_truth_required",
+ "claim": package.get("requires_fresh_production_truth_in_same_run") is True,
+ },
+ {
+ "key": "post_apply_verifier_required",
+ "claim": package.get("requires_post_apply_verifier") is True,
+ },
+ {
+ "key": "direct_apply_rejected_until_issuer_lane",
+ "claim": "direct_database_apply_requested_from_decision_preflight"
+ in rejection_policy,
+ },
+ ]
+ checks = [
+ _authorization_issuer_gate_check(
+ "decision_closeout_ready",
+ closeout.get("result") == "DB_APPLY_AUTHORIZATION_DECISION_CLOSEOUT_READY"
+ and decision.get("ready_for_future_authorization_decision_closeout") is True
+ and package.get("ready_for_future_authorization_decision_package") is True,
+ {
+ "result": closeout.get("result"),
+ "ready_for_future_authorization_decision_closeout": decision.get(
+ "ready_for_future_authorization_decision_closeout"
+ ),
+ "ready_for_future_authorization_decision_package": package.get(
+ "ready_for_future_authorization_decision_package"
+ ),
+ },
+ "wait_for_decision_closeout",
+ ),
+ _authorization_issuer_gate_check(
+ "nonsecret_envelope_only",
+ package.get("operator_secret_boundary") == "future_shell_only"
+ and package.get("reads_secret_in_preview") is False,
+ {
+ "operator_secret_boundary": package.get("operator_secret_boundary"),
+ "reads_secret_in_preview": package.get("reads_secret_in_preview"),
+ },
+ "abort_on_secret_material_in_envelope",
+ ),
+ _authorization_issuer_gate_check(
+ "source_chain_ids_present",
+ bool(decision.get("closeout_id"))
+ and bool(package.get("source_preflight_id"))
+ and bool(package.get("source_lane_guard_id"))
+ and bool(package.get("source_closeout_package_id"))
+ and bool(package.get("source_intake_id"))
+ and bool(package.get("source_closeout_boundary_id")),
+ {
+ "decision_closeout_id": decision.get("closeout_id"),
+ "source_preflight_id": package.get("source_preflight_id"),
+ "source_lane_guard_id": package.get("source_lane_guard_id"),
+ "source_closeout_package_id": package.get("source_closeout_package_id"),
+ "source_intake_id": package.get("source_intake_id"),
+ "source_closeout_boundary_id": package.get("source_closeout_boundary_id"),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_issuer_gate_check(
+ "same_run_production_truth_required",
+ "fresh_production_truth_same_run" in source_input_keys
+ and package.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "input_keys": sorted(key for key in source_input_keys if key),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_issuer_gate_check(
+ "post_apply_verifier_required",
+ "post_apply_verifier_reference" in source_input_keys
+ and package.get("requires_post_apply_verifier") is True
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "input_keys": sorted(key for key in source_input_keys if key),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier_artifact",
+ ),
+ _authorization_issuer_gate_check(
+ "migration_target_hash_locked",
+ bool(package.get("target_file"))
+ and bool(package.get("expected_sha256"))
+ and package.get("hash_matches") is True,
+ {
+ "target_file": package.get("target_file"),
+ "hash_matches": package.get("hash_matches"),
+ },
+ "abort_on_migration_hash_gap",
+ ),
+ _authorization_issuer_gate_check(
+ "decision_package_is_no_authorization",
+ package.get("issues_database_apply_authorization") is False
+ and package.get("ready_for_database_apply_now") is False,
+ {
+ "issues_database_apply_authorization": package.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": package.get("ready_for_database_apply_now"),
+ },
+ "block_if_package_authorizes_apply",
+ ),
+ _authorization_issuer_gate_check(
+ "closeout_contract_is_no_write",
+ contract.get("issues_database_apply_authorization") is False
+ and contract.get("ready_for_database_apply_now") is False
+ and contract.get("writes_database") is False
+ and contract.get("executes_in_preview") is False,
+ {
+ "issues_database_apply_authorization": contract.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": contract.get("ready_for_database_apply_now"),
+ "writes_database": contract.get("writes_database"),
+ "executes_in_preview": contract.get("executes_in_preview"),
+ },
+ "block_if_contract_authorizes_write",
+ ),
+ _authorization_issuer_gate_check(
+ "preview_has_no_side_effects",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ },
+ "abort_on_preview_side_effect",
+ ),
+ _authorization_issuer_gate_check(
+ "direct_apply_still_rejected",
+ "direct_database_apply_requested_from_decision_preflight" in rejection_policy
+ and package.get("issues_database_apply_authorization") is False
+ and package.get("ready_for_database_apply_now") is False,
+ {
+ "rejection_reason_present": (
+ "direct_database_apply_requested_from_decision_preflight" in rejection_policy
+ ),
+ "issues_database_apply_authorization": package.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": package.get("ready_for_database_apply_now"),
+ },
+ "reject_direct_database_apply_from_issuer_gate",
+ ),
+ _authorization_issuer_gate_check(
+ "issuer_policy_requires_future_explicit_authorization",
+ len(required_issuer_evidence) == 9
+ and len(nonsecret_authorization_claims) == 8
+ and all(item.get("required") is True for item in required_issuer_evidence),
+ {
+ "required_issuer_evidence_count": len(required_issuer_evidence),
+ "nonsecret_authorization_claim_count": len(nonsecret_authorization_claims),
+ },
+ "wait_for_complete_nonsecret_issuer_envelope",
+ ),
+ _authorization_issuer_gate_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and decision.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": decision.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ issuer_ready = not waiting_checks
+ issuer_status = (
+ "DB_APPLY_AUTHORIZATION_ISSUER_GATE_READY"
+ if issuer_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_DECISION_CLOSEOUT"
+ )
+ issuer_gate_id = _db_apply_authorization_issuer_gate_id(closeout)
+ future_authorization_issuer_gate = {
+ "gate_id": issuer_gate_id,
+ "source_decision_closeout_id": decision.get("closeout_id"),
+ "source_decision_preflight_id": package.get("source_preflight_id"),
+ "source_lane_guard_id": package.get("source_lane_guard_id"),
+ "source_closeout_package_id": package.get("source_closeout_package_id"),
+ "source_intake_id": package.get("source_intake_id"),
+ "source_closeout_boundary_id": package.get("source_closeout_boundary_id"),
+ "status": issuer_status,
+ "ready_for_future_authorization_issuer_lane": issuer_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ final_nonsecret_authorization_envelope = {
+ "envelope_id": issuer_gate_id,
+ "source_decision_closeout_id": decision.get("closeout_id"),
+ "source_decision_package_id": package.get("package_id"),
+ "source_decision_preflight_id": package.get("source_preflight_id"),
+ "source_lane_guard_id": package.get("source_lane_guard_id"),
+ "source_closeout_package_id": package.get("source_closeout_package_id"),
+ "source_intake_id": package.get("source_intake_id"),
+ "source_closeout_boundary_id": package.get("source_closeout_boundary_id"),
+ "status": issuer_status,
+ "authorization_material_type": "nonsecret_request_envelope",
+ "decision_scope": "future_explicit_db_apply_authorization_issuer_lane_only",
+ "ready_for_future_authorization_issuer_lane": issuer_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "target_file": package.get("target_file"),
+ "expected_sha256": package.get("expected_sha256"),
+ "actual_sha256": package.get("actual_sha256"),
+ "hash_matches": package.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": package.get("post_apply_verifier_endpoint"),
+ "operator_secret_boundary": "future_shell_only",
+ "secret_material_included": False,
+ "reads_secret_in_preview": False,
+ "executes_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "nonsecret_authorization_claims": nonsecret_authorization_claims,
+ "required_issuer_evidence": required_issuer_evidence,
+ }
+ issuer_gate_contract = {
+ "mode": "future_authorization_issuer_gate_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-issuer-gate"
+ ),
+ "source_decision_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_authorization_issuer_lane": issuer_ready,
+ "issues_database_apply_authorization": False,
+ "ready_for_database_apply_now": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_ISSUER_GATE_POLICY,
+ "result": issuer_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "authorization_issuer_gate_ready_count": 1 if issuer_ready else 0,
+ "issuer_gate_check_count": len(checks),
+ "issuer_gate_pass_count": passed_count,
+ "issuer_gate_waiting_count": len(waiting_checks),
+ "authorization_decision_closeout_ready_count": summary.get(
+ "authorization_decision_closeout_ready_count", 0
+ ),
+ "decision_closeout_check_count": summary.get("decision_closeout_check_count", 0),
+ "required_issuer_evidence_count": len(required_issuer_evidence),
+ "nonsecret_authorization_claim_count": len(nonsecret_authorization_claims),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "decision_rejection_reason_count": summary.get("decision_rejection_reason_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_issuer_gate": future_authorization_issuer_gate,
+ "final_nonsecret_authorization_envelope": final_nonsecret_authorization_envelope,
+ "issuer_gate_contract": issuer_gate_contract,
+ "issuer_gate_checks": checks,
+ "source_decision_closeout_summary": summary,
+ "source_decision_closeout_contract": contract,
+ "source_decision_rejection_policy": rejection_policy,
+ "safety": {
+ "read_only_db_apply_authorization_issuer_gate": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this issuer gate to hand a nonsecret envelope to a future explicit authorization signing lane.",
+ "Keep database apply authorization signing, secret reads, shell execution, SQL, and DB writes out of this gate.",
+ "Require fresh production truth, secret rejection, rollback boundary, and post-apply verifier inside the future issuer run.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_signing_decision_preflight(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Prepare a no-secret preflight before any future authorization signing decision."""
+ issuer_gate = build_pchome_auto_policy_db_apply_authorization_issuer_gate(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ gate = issuer_gate.get("future_authorization_issuer_gate") or {}
+ envelope = issuer_gate.get("final_nonsecret_authorization_envelope") or {}
+ contract = issuer_gate.get("issuer_gate_contract") or {}
+ summary = issuer_gate.get("summary") or {}
+ required_evidence = envelope.get("required_issuer_evidence") or []
+ nonsecret_claims = envelope.get("nonsecret_authorization_claims") or []
+ rejection_policy = issuer_gate.get("source_decision_rejection_policy") or []
+ evidence_keys = {item.get("key") for item in required_evidence}
+ claim_keys = {item.get("key") for item in nonsecret_claims}
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and envelope.get("reads_secret_in_preview") is False
+ and envelope.get("executes_shell_in_preview") is False
+ and envelope.get("executes_sql_in_preview") is False
+ and envelope.get("writes_database_in_preview") is False
+ and gate.get("signs_database_apply_authorization") is False
+ and envelope.get("signs_database_apply_authorization") is False
+ )
+ signing_decision_input_requirements = [
+ {
+ "key": "fresh_production_truth_same_run",
+ "required": True,
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ },
+ {
+ "key": "issuer_gate_id",
+ "required": True,
+ "source_id": gate.get("gate_id"),
+ },
+ {
+ "key": "nonsecret_authorization_envelope_id",
+ "required": True,
+ "source_id": envelope.get("envelope_id"),
+ },
+ {
+ "key": "decision_closeout_id",
+ "required": True,
+ "source_id": envelope.get("source_decision_closeout_id"),
+ },
+ {
+ "key": "decision_preflight_id",
+ "required": True,
+ "source_id": envelope.get("source_decision_preflight_id"),
+ },
+ {
+ "key": "authorization_lane_guard_id",
+ "required": True,
+ "source_id": envelope.get("source_lane_guard_id"),
+ },
+ {
+ "key": "migration_target_hash",
+ "required": True,
+ "target_file": envelope.get("target_file"),
+ "expected_sha256": envelope.get("expected_sha256"),
+ "hash_matches": envelope.get("hash_matches"),
+ },
+ {
+ "key": "secret_boundary_rejection",
+ "required": True,
+ "operator_secret_boundary": envelope.get("operator_secret_boundary"),
+ "secret_material_included": envelope.get("secret_material_included"),
+ },
+ {
+ "key": "post_apply_verifier_reference",
+ "required": True,
+ "source_endpoint": envelope.get("post_apply_verifier_endpoint"),
+ },
+ {
+ "key": "no_signing_without_future_explicit_authorization",
+ "required": True,
+ "signs_database_apply_authorization": False,
+ },
+ ]
+ signing_decision_rejection_policy = [
+ "production_truth_missing_or_stale",
+ "issuer_gate_not_ready",
+ "nonsecret_authorization_envelope_missing_or_mutated",
+ "issuer_evidence_missing",
+ "nonsecret_claim_failed",
+ "migration_hash_missing_or_mismatch",
+ "secret_material_present_in_signing_preflight",
+ "post_apply_verifier_missing",
+ "direct_database_apply_requested_from_signing_preflight",
+ "authorization_signing_requested_from_preflight",
+ "preview_attempted_shell_sql_or_database_side_effect",
+ ]
+ checks = [
+ _authorization_signing_decision_preflight_check(
+ "issuer_gate_ready",
+ issuer_gate.get("result") == "DB_APPLY_AUTHORIZATION_ISSUER_GATE_READY"
+ and gate.get("ready_for_future_authorization_issuer_lane") is True
+ and envelope.get("ready_for_future_authorization_issuer_lane") is True,
+ {
+ "result": issuer_gate.get("result"),
+ "ready_for_future_authorization_issuer_lane": gate.get(
+ "ready_for_future_authorization_issuer_lane"
+ ),
+ "envelope_ready_for_future_authorization_issuer_lane": envelope.get(
+ "ready_for_future_authorization_issuer_lane"
+ ),
+ },
+ "wait_for_authorization_issuer_gate",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "nonsecret_envelope_complete",
+ envelope.get("authorization_material_type") == "nonsecret_request_envelope"
+ and bool(envelope.get("envelope_id"))
+ and envelope.get("secret_material_included") is False,
+ {
+ "authorization_material_type": envelope.get("authorization_material_type"),
+ "envelope_id": envelope.get("envelope_id"),
+ "secret_material_included": envelope.get("secret_material_included"),
+ },
+ "wait_for_nonsecret_authorization_envelope",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "required_issuer_evidence_complete",
+ len(required_evidence) == 9
+ and all(item.get("required") is True for item in required_evidence),
+ {
+ "required_issuer_evidence_count": len(required_evidence),
+ "evidence_keys": sorted(key for key in evidence_keys if key),
+ },
+ "wait_for_required_issuer_evidence",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "nonsecret_claims_complete",
+ len(nonsecret_claims) == 8
+ and all(item.get("claim") is True for item in nonsecret_claims),
+ {
+ "nonsecret_authorization_claim_count": len(nonsecret_claims),
+ "claim_keys": sorted(key for key in claim_keys if key),
+ },
+ "wait_for_nonsecret_authorization_claims",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "same_run_production_truth_required",
+ "fresh_production_truth_same_run" in evidence_keys
+ and envelope.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "evidence_keys": sorted(key for key in evidence_keys if key),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "post_apply_verifier_required",
+ "post_apply_verifier_reference" in evidence_keys
+ and envelope.get("requires_post_apply_verifier") is True
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "evidence_keys": sorted(key for key in evidence_keys if key),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier_artifact",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "migration_target_hash_locked",
+ bool(envelope.get("target_file"))
+ and bool(envelope.get("expected_sha256"))
+ and envelope.get("hash_matches") is True,
+ {
+ "target_file": envelope.get("target_file"),
+ "hash_matches": envelope.get("hash_matches"),
+ },
+ "abort_on_migration_hash_gap",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "secret_boundary_clean",
+ "secret_boundary_rejection" in evidence_keys
+ and envelope.get("operator_secret_boundary") == "future_shell_only"
+ and envelope.get("secret_material_included") is False
+ and envelope.get("reads_secret_in_preview") is False,
+ {
+ "operator_secret_boundary": envelope.get("operator_secret_boundary"),
+ "secret_material_included": envelope.get("secret_material_included"),
+ "reads_secret_in_preview": envelope.get("reads_secret_in_preview"),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "source_chain_ids_present",
+ bool(gate.get("gate_id"))
+ and bool(envelope.get("source_decision_closeout_id"))
+ and bool(envelope.get("source_decision_preflight_id"))
+ and bool(envelope.get("source_lane_guard_id"))
+ and bool(envelope.get("source_closeout_package_id"))
+ and bool(envelope.get("source_intake_id"))
+ and bool(envelope.get("source_closeout_boundary_id")),
+ {
+ "gate_id": gate.get("gate_id"),
+ "source_decision_closeout_id": envelope.get("source_decision_closeout_id"),
+ "source_decision_preflight_id": envelope.get("source_decision_preflight_id"),
+ "source_lane_guard_id": envelope.get("source_lane_guard_id"),
+ "source_closeout_package_id": envelope.get("source_closeout_package_id"),
+ "source_intake_id": envelope.get("source_intake_id"),
+ "source_closeout_boundary_id": envelope.get("source_closeout_boundary_id"),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "preview_has_no_side_effects",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization": envelope.get(
+ "signs_database_apply_authorization"
+ ),
+ },
+ "abort_on_preview_side_effect",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "signing_and_direct_apply_still_rejected",
+ contract.get("signs_database_apply_authorization") is False
+ and contract.get("issues_database_apply_authorization") is False
+ and contract.get("ready_for_database_apply_now") is False
+ and contract.get("writes_database") is False,
+ {
+ "signs_database_apply_authorization": contract.get(
+ "signs_database_apply_authorization"
+ ),
+ "issues_database_apply_authorization": contract.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": contract.get("ready_for_database_apply_now"),
+ "writes_database": contract.get("writes_database"),
+ },
+ "reject_signing_or_direct_database_apply_from_preflight",
+ ),
+ _authorization_signing_decision_preflight_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and gate.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": gate.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ preflight_ready = not waiting_checks
+ preflight_status = (
+ "DB_APPLY_AUTHORIZATION_SIGNING_DECISION_PREFLIGHT_READY"
+ if preflight_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_ISSUER_GATE"
+ )
+ preflight_id = _db_apply_authorization_signing_decision_preflight_id(issuer_gate)
+ future_authorization_signing_decision_preflight = {
+ "preflight_id": preflight_id,
+ "source_issuer_gate_id": gate.get("gate_id"),
+ "source_decision_closeout_id": envelope.get("source_decision_closeout_id"),
+ "source_decision_package_id": envelope.get("source_decision_package_id"),
+ "source_decision_preflight_id": envelope.get("source_decision_preflight_id"),
+ "source_lane_guard_id": envelope.get("source_lane_guard_id"),
+ "source_closeout_package_id": envelope.get("source_closeout_package_id"),
+ "source_intake_id": envelope.get("source_intake_id"),
+ "source_closeout_boundary_id": envelope.get("source_closeout_boundary_id"),
+ "status": preflight_status,
+ "ready_for_future_signing_decision_preflight": preflight_ready,
+ "can_enter_authorization_signing_decision_lane": preflight_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ signing_decision_preflight_envelope = {
+ "mode": "authorization_signing_decision_preflight_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-preflight"
+ ),
+ "source_issuer_gate_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-issuer-gate"
+ ),
+ "machine_verifiable": True,
+ "allows_future_authorization_signing_decision_lane": preflight_ready,
+ "issues_database_apply_authorization": False,
+ "ready_for_database_apply_now": False,
+ "signs_database_apply_authorization": False,
+ "rejects_direct_database_apply": True,
+ "requires_post_apply_verifier": True,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_DECISION_PREFLIGHT_POLICY,
+ "result": preflight_status,
+ "success": bool(issuer_gate.get("success")),
+ "generated_at": issuer_gate.get("generated_at"),
+ "source_policy": issuer_gate.get("policy"),
+ "stats": issuer_gate.get("stats") or {},
+ "summary": {
+ "authorization_signing_decision_preflight_ready_count": 1 if preflight_ready else 0,
+ "signing_decision_preflight_check_count": len(checks),
+ "signing_decision_preflight_pass_count": passed_count,
+ "signing_decision_preflight_waiting_count": len(waiting_checks),
+ "authorization_issuer_gate_ready_count": summary.get(
+ "authorization_issuer_gate_ready_count", 0
+ ),
+ "issuer_gate_check_count": summary.get("issuer_gate_check_count", 0),
+ "required_issuer_evidence_count": len(required_evidence),
+ "nonsecret_authorization_claim_count": len(nonsecret_claims),
+ "signing_decision_input_requirement_count": len(signing_decision_input_requirements),
+ "signing_decision_rejection_reason_count": len(signing_decision_rejection_policy),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_signing_decision_preflight": (
+ future_authorization_signing_decision_preflight
+ ),
+ "signing_decision_preflight_envelope": signing_decision_preflight_envelope,
+ "signing_decision_input_requirements": signing_decision_input_requirements,
+ "signing_decision_rejection_policy": signing_decision_rejection_policy,
+ "signing_decision_preflight_checks": checks,
+ "source_issuer_gate_summary": summary,
+ "source_issuer_gate_contract": contract,
+ "source_nonsecret_authorization_envelope": envelope,
+ "safety": {
+ "read_only_db_apply_authorization_signing_decision_preflight": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this preflight to verify the nonsecret envelope before a future explicit signing decision lane.",
+ "Keep authorization signing, secret reads, shell execution, SQL, and DB writes out of this preflight.",
+ "Require fresh production truth, secret rejection, rollback boundary, and post-apply verifier inside the future signing decision run.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_signing_decision_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out signing-decision preflight as an unsigned package."""
+ preflight = build_pchome_auto_policy_db_apply_authorization_signing_decision_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ decision = preflight.get("future_authorization_signing_decision_preflight") or {}
+ envelope = preflight.get("signing_decision_preflight_envelope") or {}
+ source_envelope = preflight.get("source_nonsecret_authorization_envelope") or {}
+ summary = preflight.get("summary") or {}
+ input_requirements = preflight.get("signing_decision_input_requirements") or []
+ rejection_policy = preflight.get("signing_decision_rejection_policy") or []
+ input_keys = {item.get("key") for item in input_requirements}
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and decision.get("signs_database_apply_authorization") is False
+ and envelope.get("signs_database_apply_authorization") is False
+ and envelope.get("secret_material_required_in_preview") is False
+ and source_envelope.get("secret_material_included") is False
+ and source_envelope.get("reads_secret_in_preview") is False
+ and source_envelope.get("executes_shell_in_preview") is False
+ and source_envelope.get("executes_sql_in_preview") is False
+ and source_envelope.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_signing_decision_closeout_check(
+ "signing_decision_preflight_ready",
+ preflight.get("result")
+ == "DB_APPLY_AUTHORIZATION_SIGNING_DECISION_PREFLIGHT_READY"
+ and decision.get("ready_for_future_signing_decision_preflight") is True,
+ {
+ "result": preflight.get("result"),
+ "ready_for_future_signing_decision_preflight": decision.get(
+ "ready_for_future_signing_decision_preflight"
+ ),
+ },
+ "wait_for_signing_decision_preflight",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "preflight_envelope_allows_future_lane_only",
+ envelope.get("allows_future_authorization_signing_decision_lane") is True
+ and envelope.get("issues_database_apply_authorization") is False
+ and envelope.get("ready_for_database_apply_now") is False
+ and envelope.get("signs_database_apply_authorization") is False,
+ {
+ "allows_future_authorization_signing_decision_lane": envelope.get(
+ "allows_future_authorization_signing_decision_lane"
+ ),
+ "issues_database_apply_authorization": envelope.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": envelope.get("ready_for_database_apply_now"),
+ "signs_database_apply_authorization": envelope.get(
+ "signs_database_apply_authorization"
+ ),
+ },
+ "block_if_preflight_envelope_signs_or_authorizes",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "signing_decision_inputs_complete",
+ len(input_requirements) == 10
+ and all(item.get("required") is True for item in input_requirements),
+ {
+ "signing_decision_input_requirement_count": len(input_requirements),
+ "input_keys": sorted(key for key in input_keys if key),
+ },
+ "wait_for_signing_decision_input_requirements",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "signing_decision_rejection_policy_complete",
+ len(rejection_policy) == 11
+ and "authorization_signing_requested_from_preflight" in rejection_policy
+ and "direct_database_apply_requested_from_signing_preflight" in rejection_policy,
+ {"signing_decision_rejection_reason_count": len(rejection_policy)},
+ "wait_for_signing_decision_rejection_policy",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "unsigned_package_source_envelope_complete",
+ source_envelope.get("authorization_material_type") == "nonsecret_request_envelope"
+ and bool(source_envelope.get("envelope_id"))
+ and source_envelope.get("secret_material_included") is False,
+ {
+ "authorization_material_type": source_envelope.get("authorization_material_type"),
+ "envelope_id": source_envelope.get("envelope_id"),
+ "secret_material_included": source_envelope.get("secret_material_included"),
+ },
+ "wait_for_nonsecret_source_envelope",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "source_chain_ids_present",
+ bool(decision.get("preflight_id"))
+ and bool(decision.get("source_issuer_gate_id"))
+ and bool(decision.get("source_decision_closeout_id"))
+ and bool(decision.get("source_decision_preflight_id"))
+ and bool(decision.get("source_lane_guard_id"))
+ and bool(decision.get("source_closeout_package_id"))
+ and bool(decision.get("source_intake_id"))
+ and bool(decision.get("source_closeout_boundary_id")),
+ {
+ "preflight_id": decision.get("preflight_id"),
+ "source_issuer_gate_id": decision.get("source_issuer_gate_id"),
+ "source_decision_closeout_id": decision.get("source_decision_closeout_id"),
+ "source_decision_preflight_id": decision.get("source_decision_preflight_id"),
+ "source_lane_guard_id": decision.get("source_lane_guard_id"),
+ "source_closeout_package_id": decision.get("source_closeout_package_id"),
+ "source_intake_id": decision.get("source_intake_id"),
+ "source_closeout_boundary_id": decision.get("source_closeout_boundary_id"),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "same_run_production_truth_required",
+ "fresh_production_truth_same_run" in input_keys
+ and source_envelope.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "input_keys": sorted(key for key in input_keys if key),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "post_apply_verifier_required",
+ "post_apply_verifier_reference" in input_keys
+ and source_envelope.get("requires_post_apply_verifier") is True
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "input_keys": sorted(key for key in input_keys if key),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier_artifact",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "migration_target_hash_locked",
+ bool(source_envelope.get("target_file"))
+ and bool(source_envelope.get("expected_sha256"))
+ and source_envelope.get("hash_matches") is True,
+ {
+ "target_file": source_envelope.get("target_file"),
+ "hash_matches": source_envelope.get("hash_matches"),
+ },
+ "abort_on_migration_hash_gap",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "secret_boundary_clean",
+ "secret_boundary_rejection" in input_keys
+ and source_envelope.get("operator_secret_boundary") == "future_shell_only"
+ and source_envelope.get("secret_material_included") is False
+ and source_envelope.get("reads_secret_in_preview") is False,
+ {
+ "operator_secret_boundary": source_envelope.get("operator_secret_boundary"),
+ "secret_material_included": source_envelope.get("secret_material_included"),
+ "reads_secret_in_preview": source_envelope.get("reads_secret_in_preview"),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "preview_has_no_side_effects_and_no_signing",
+ side_effect_free
+ and envelope.get("rejects_direct_database_apply") is True
+ and decision.get("issues_database_apply_authorization") is False
+ and decision.get("ready_for_database_apply_now") is False,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "rejects_direct_database_apply": envelope.get("rejects_direct_database_apply"),
+ },
+ "abort_on_preview_side_effect_or_signing",
+ ),
+ _authorization_signing_decision_closeout_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and decision.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": decision.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_AUTHORIZATION_SIGNING_DECISION_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_DECISION_PREFLIGHT"
+ )
+ closeout_id = _db_apply_authorization_signing_decision_closeout_id(preflight)
+ future_authorization_signing_decision_closeout = {
+ "closeout_id": closeout_id,
+ "source_signing_decision_preflight_id": decision.get("preflight_id"),
+ "source_issuer_gate_id": decision.get("source_issuer_gate_id"),
+ "source_decision_closeout_id": decision.get("source_decision_closeout_id"),
+ "source_decision_package_id": decision.get("source_decision_package_id"),
+ "source_decision_preflight_id": decision.get("source_decision_preflight_id"),
+ "source_lane_guard_id": decision.get("source_lane_guard_id"),
+ "source_closeout_package_id": decision.get("source_closeout_package_id"),
+ "source_intake_id": decision.get("source_intake_id"),
+ "source_closeout_boundary_id": decision.get("source_closeout_boundary_id"),
+ "status": closeout_status,
+ "ready_for_future_signing_decision_closeout": closeout_ready,
+ "can_enter_unsigned_signing_decision_package_lane": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ unsigned_signing_decision_package = {
+ "package_id": closeout_id,
+ "source_signing_decision_preflight_id": decision.get("preflight_id"),
+ "source_issuer_gate_id": decision.get("source_issuer_gate_id"),
+ "source_nonsecret_envelope_id": source_envelope.get("envelope_id"),
+ "source_decision_closeout_id": decision.get("source_decision_closeout_id"),
+ "source_decision_package_id": decision.get("source_decision_package_id"),
+ "source_decision_preflight_id": decision.get("source_decision_preflight_id"),
+ "source_lane_guard_id": decision.get("source_lane_guard_id"),
+ "source_closeout_package_id": decision.get("source_closeout_package_id"),
+ "source_intake_id": decision.get("source_intake_id"),
+ "source_closeout_boundary_id": decision.get("source_closeout_boundary_id"),
+ "status": closeout_status,
+ "ready_for_future_unsigned_signing_decision_package": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "decision_scope": "future_explicit_db_apply_authorization_signing_decision_only",
+ "authorization_material_type": "unsigned_signing_decision_package",
+ "target_file": source_envelope.get("target_file"),
+ "expected_sha256": source_envelope.get("expected_sha256"),
+ "actual_sha256": source_envelope.get("actual_sha256"),
+ "hash_matches": source_envelope.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": source_envelope.get("post_apply_verifier_endpoint"),
+ "operator_secret_boundary": "future_shell_only",
+ "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,
+ "signing_decision_input_requirements": input_requirements,
+ "signing_decision_rejection_policy": rejection_policy,
+ }
+ signing_decision_closeout_contract = {
+ "mode": "unsigned_signing_decision_closeout_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-closeout"
+ ),
+ "source_signing_decision_preflight_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-preflight"
+ ),
+ "machine_verifiable": True,
+ "permits_future_unsigned_signing_decision_package_lane": closeout_ready,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_DECISION_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(preflight.get("success")),
+ "generated_at": preflight.get("generated_at"),
+ "source_policy": preflight.get("policy"),
+ "stats": preflight.get("stats") or {},
+ "summary": {
+ "authorization_signing_decision_closeout_ready_count": 1 if closeout_ready else 0,
+ "signing_decision_closeout_check_count": len(checks),
+ "signing_decision_closeout_pass_count": passed_count,
+ "signing_decision_closeout_waiting_count": len(waiting_checks),
+ "authorization_signing_decision_preflight_ready_count": summary.get(
+ "authorization_signing_decision_preflight_ready_count", 0
+ ),
+ "signing_decision_preflight_check_count": summary.get(
+ "signing_decision_preflight_check_count", 0
+ ),
+ "signing_decision_input_requirement_count": len(input_requirements),
+ "signing_decision_rejection_reason_count": len(rejection_policy),
+ "required_issuer_evidence_count": summary.get("required_issuer_evidence_count", 0),
+ "nonsecret_authorization_claim_count": summary.get(
+ "nonsecret_authorization_claim_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_signing_decision_closeout": (
+ future_authorization_signing_decision_closeout
+ ),
+ "unsigned_signing_decision_package": unsigned_signing_decision_package,
+ "signing_decision_closeout_contract": signing_decision_closeout_contract,
+ "signing_decision_closeout_checks": checks,
+ "source_signing_decision_preflight_summary": summary,
+ "source_signing_decision_preflight_envelope": envelope,
+ "source_nonsecret_authorization_envelope": source_envelope,
+ "safety": {
+ "read_only_db_apply_authorization_signing_decision_closeout": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout as the unsigned package for a future explicit authorization signing decision lane.",
+ "Keep authorization signing, secret reads, shell execution, SQL, and DB writes out of this closeout.",
+ "Require fresh production truth, secret rejection, rollback boundary, and post-apply verifier inside the future signing decision run.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_signing_issuer_guard(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Guard the future signing issuer lane without issuing authorization."""
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_decision_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ signing_closeout = closeout.get("future_authorization_signing_decision_closeout") or {}
+ package = closeout.get("unsigned_signing_decision_package") or {}
+ contract = closeout.get("signing_decision_closeout_contract") or {}
+ summary = closeout.get("summary") or {}
+ input_requirements = package.get("signing_decision_input_requirements") or []
+ rejection_policy = package.get("signing_decision_rejection_policy") or []
+ input_keys = {item.get("key") for item in input_requirements}
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and signing_closeout.get("signs_database_apply_authorization") is False
+ and package.get("signs_database_apply_authorization") is False
+ and package.get("secret_material_included") is False
+ and package.get("secret_material_required_in_preview") is False
+ and package.get("reads_secret_in_preview") is False
+ and package.get("executes_shell_in_preview") is False
+ and package.get("executes_sql_in_preview") is False
+ and package.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_signing_issuer_guard_check(
+ "signing_decision_closeout_ready",
+ closeout.get("result") == "DB_APPLY_AUTHORIZATION_SIGNING_DECISION_CLOSEOUT_READY"
+ and signing_closeout.get("ready_for_future_signing_decision_closeout") is True
+ and package.get("ready_for_future_unsigned_signing_decision_package") is True,
+ {
+ "result": closeout.get("result"),
+ "ready_for_future_signing_decision_closeout": signing_closeout.get(
+ "ready_for_future_signing_decision_closeout"
+ ),
+ "ready_for_future_unsigned_signing_decision_package": package.get(
+ "ready_for_future_unsigned_signing_decision_package"
+ ),
+ },
+ "wait_for_signing_decision_closeout",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "unsigned_signing_decision_package_complete",
+ package.get("authorization_material_type") == "unsigned_signing_decision_package"
+ and bool(package.get("package_id"))
+ and package.get("ready_for_future_unsigned_signing_decision_package") is True,
+ {
+ "authorization_material_type": package.get("authorization_material_type"),
+ "package_id": package.get("package_id"),
+ "ready_for_future_unsigned_signing_decision_package": package.get(
+ "ready_for_future_unsigned_signing_decision_package"
+ ),
+ },
+ "wait_for_unsigned_signing_decision_package",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "unsigned_package_is_no_authorization_or_signing",
+ package.get("issues_database_apply_authorization") is False
+ and package.get("ready_for_database_apply_now") is False
+ and package.get("signs_database_apply_authorization") is False,
+ {
+ "issues_database_apply_authorization": package.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": package.get("ready_for_database_apply_now"),
+ "signs_database_apply_authorization": package.get(
+ "signs_database_apply_authorization"
+ ),
+ },
+ "block_if_unsigned_package_authorizes_or_signs",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "source_chain_ids_present",
+ bool(signing_closeout.get("closeout_id"))
+ and bool(package.get("source_signing_decision_preflight_id"))
+ and bool(package.get("source_issuer_gate_id"))
+ and bool(package.get("source_nonsecret_envelope_id"))
+ and bool(package.get("source_decision_closeout_id"))
+ and bool(package.get("source_decision_preflight_id"))
+ and bool(package.get("source_lane_guard_id"))
+ and bool(package.get("source_closeout_package_id"))
+ and bool(package.get("source_intake_id"))
+ and bool(package.get("source_closeout_boundary_id")),
+ {
+ "closeout_id": signing_closeout.get("closeout_id"),
+ "source_signing_decision_preflight_id": package.get(
+ "source_signing_decision_preflight_id"
+ ),
+ "source_issuer_gate_id": package.get("source_issuer_gate_id"),
+ "source_nonsecret_envelope_id": package.get("source_nonsecret_envelope_id"),
+ "source_decision_closeout_id": package.get("source_decision_closeout_id"),
+ "source_decision_preflight_id": package.get("source_decision_preflight_id"),
+ "source_lane_guard_id": package.get("source_lane_guard_id"),
+ "source_closeout_package_id": package.get("source_closeout_package_id"),
+ "source_intake_id": package.get("source_intake_id"),
+ "source_closeout_boundary_id": package.get("source_closeout_boundary_id"),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "same_run_production_truth_required",
+ "fresh_production_truth_same_run" in input_keys
+ and package.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "input_keys": sorted(key for key in input_keys if key),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "post_apply_verifier_required",
+ "post_apply_verifier_reference" in input_keys
+ and package.get("requires_post_apply_verifier") is True
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "input_keys": sorted(key for key in input_keys if key),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier_artifact",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "migration_target_hash_locked",
+ bool(package.get("target_file"))
+ and bool(package.get("expected_sha256"))
+ and package.get("hash_matches") is True,
+ {
+ "target_file": package.get("target_file"),
+ "hash_matches": package.get("hash_matches"),
+ },
+ "abort_on_migration_hash_gap",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "secret_boundary_clean",
+ "secret_boundary_rejection" in input_keys
+ and package.get("operator_secret_boundary") == "future_shell_only"
+ and package.get("secret_material_included") is False
+ and package.get("secret_material_required_in_preview") is False
+ and package.get("reads_secret_in_preview") is False,
+ {
+ "operator_secret_boundary": package.get("operator_secret_boundary"),
+ "secret_material_included": package.get("secret_material_included"),
+ "secret_material_required_in_preview": package.get(
+ "secret_material_required_in_preview"
+ ),
+ "reads_secret_in_preview": package.get("reads_secret_in_preview"),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "signing_inputs_and_rejection_policy_complete",
+ len(input_requirements) == 10
+ and len(rejection_policy) == 11
+ and "authorization_signing_requested_from_preflight" in rejection_policy
+ and "direct_database_apply_requested_from_signing_preflight" in rejection_policy,
+ {
+ "signing_decision_input_requirement_count": len(input_requirements),
+ "signing_decision_rejection_reason_count": len(rejection_policy),
+ },
+ "wait_for_signing_inputs_and_rejection_policy",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "preview_has_no_side_effects_and_no_signing",
+ side_effect_free
+ and signing_closeout.get("issues_database_apply_authorization") is False
+ and signing_closeout.get("ready_for_database_apply_now") is False,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_or_signing",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "signable_boundary_is_future_only",
+ contract.get("permits_future_unsigned_signing_decision_package_lane") is True
+ and contract.get("issues_database_apply_authorization") is False
+ and contract.get("ready_for_database_apply_now") is False
+ and contract.get("signs_database_apply_authorization") is False
+ and contract.get("writes_database") is False,
+ {
+ "permits_future_unsigned_signing_decision_package_lane": contract.get(
+ "permits_future_unsigned_signing_decision_package_lane"
+ ),
+ "issues_database_apply_authorization": contract.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": contract.get("ready_for_database_apply_now"),
+ "signs_database_apply_authorization": contract.get(
+ "signs_database_apply_authorization"
+ ),
+ "writes_database": contract.get("writes_database"),
+ },
+ "block_if_signable_boundary_authorizes_now",
+ ),
+ _authorization_signing_issuer_guard_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and signing_closeout.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": signing_closeout.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ guard_ready = not waiting_checks
+ guard_status = (
+ "DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_GUARD_READY"
+ if guard_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_DECISION_CLOSEOUT"
+ )
+ guard_id = _db_apply_authorization_signing_issuer_guard_id(closeout)
+ future_authorization_signing_issuer_guard = {
+ "guard_id": guard_id,
+ "source_signing_decision_closeout_id": signing_closeout.get("closeout_id"),
+ "source_signing_decision_preflight_id": package.get(
+ "source_signing_decision_preflight_id"
+ ),
+ "source_issuer_gate_id": package.get("source_issuer_gate_id"),
+ "source_nonsecret_envelope_id": package.get("source_nonsecret_envelope_id"),
+ "source_decision_closeout_id": package.get("source_decision_closeout_id"),
+ "source_decision_preflight_id": package.get("source_decision_preflight_id"),
+ "source_lane_guard_id": package.get("source_lane_guard_id"),
+ "source_closeout_package_id": package.get("source_closeout_package_id"),
+ "source_intake_id": package.get("source_intake_id"),
+ "source_closeout_boundary_id": package.get("source_closeout_boundary_id"),
+ "status": guard_status,
+ "ready_for_future_signing_issuer_guard": guard_ready,
+ "can_enter_future_authorization_signing_issuer_lane": guard_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ signable_request_boundary = {
+ "boundary_id": guard_id,
+ "source_signing_decision_closeout_id": signing_closeout.get("closeout_id"),
+ "source_unsigned_signing_decision_package_id": package.get("package_id"),
+ "source_signing_decision_preflight_id": package.get(
+ "source_signing_decision_preflight_id"
+ ),
+ "source_issuer_gate_id": package.get("source_issuer_gate_id"),
+ "source_nonsecret_envelope_id": package.get("source_nonsecret_envelope_id"),
+ "source_decision_closeout_id": package.get("source_decision_closeout_id"),
+ "source_decision_preflight_id": package.get("source_decision_preflight_id"),
+ "source_lane_guard_id": package.get("source_lane_guard_id"),
+ "source_closeout_package_id": package.get("source_closeout_package_id"),
+ "source_intake_id": package.get("source_intake_id"),
+ "source_closeout_boundary_id": package.get("source_closeout_boundary_id"),
+ "status": guard_status,
+ "request_boundary_type": "future_signable_request_boundary",
+ "ready_for_future_signable_request_boundary": guard_ready,
+ "can_enter_future_authorization_signing_issuer_lane": guard_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "target_file": package.get("target_file"),
+ "expected_sha256": package.get("expected_sha256"),
+ "actual_sha256": package.get("actual_sha256"),
+ "hash_matches": package.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": package.get("post_apply_verifier_endpoint"),
+ "operator_secret_boundary": "future_shell_only",
+ "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,
+ "signing_decision_input_requirement_count": len(input_requirements),
+ "signing_decision_rejection_reason_count": len(rejection_policy),
+ }
+ signing_issuer_guard_contract = {
+ "mode": "future_signing_issuer_guard_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-guard"
+ ),
+ "source_signing_decision_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_authorization_signing_issuer_lane": guard_ready,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_GUARD_POLICY,
+ "result": guard_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "authorization_signing_issuer_guard_ready_count": 1 if guard_ready else 0,
+ "signing_issuer_guard_check_count": len(checks),
+ "signing_issuer_guard_pass_count": passed_count,
+ "signing_issuer_guard_waiting_count": len(waiting_checks),
+ "authorization_signing_decision_closeout_ready_count": summary.get(
+ "authorization_signing_decision_closeout_ready_count", 0
+ ),
+ "signing_decision_closeout_check_count": summary.get(
+ "signing_decision_closeout_check_count", 0
+ ),
+ "signing_decision_input_requirement_count": len(input_requirements),
+ "signing_decision_rejection_reason_count": len(rejection_policy),
+ "required_issuer_evidence_count": summary.get("required_issuer_evidence_count", 0),
+ "nonsecret_authorization_claim_count": summary.get(
+ "nonsecret_authorization_claim_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_signing_issuer_guard": future_authorization_signing_issuer_guard,
+ "signable_request_boundary": signable_request_boundary,
+ "signing_issuer_guard_contract": signing_issuer_guard_contract,
+ "signing_issuer_guard_checks": checks,
+ "source_signing_decision_closeout_summary": summary,
+ "source_signing_decision_closeout_contract": contract,
+ "source_unsigned_signing_decision_package": package,
+ "safety": {
+ "read_only_db_apply_authorization_signing_issuer_guard": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this guard to pass a signable request boundary to a future explicit authorization signing issuer lane.",
+ "Keep authorization signing, secret reads, shell execution, SQL, and DB writes out of this guard.",
+ "Require fresh production truth, secret rejection, rollback boundary, and post-apply verifier inside the future signing issuer run.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_signing_issuer_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the future signing issuer boundary without signing authorization."""
+ guard = build_pchome_auto_policy_db_apply_authorization_signing_issuer_guard(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ issuer_guard = guard.get("future_authorization_signing_issuer_guard") or {}
+ boundary = guard.get("signable_request_boundary") or {}
+ contract = guard.get("signing_issuer_guard_contract") or {}
+ summary = guard.get("summary") or {}
+ safety = guard.get("safety") or {}
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and boundary.get("secret_material_included") is False
+ and boundary.get("secret_material_required_in_preview") is False
+ and boundary.get("reads_secret_in_preview") is False
+ and boundary.get("executes_shell_in_preview") is False
+ and boundary.get("executes_sql_in_preview") is False
+ and boundary.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_signing_issuer_closeout_check(
+ "signing_issuer_guard_ready",
+ guard.get("result") == "DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_GUARD_READY"
+ and issuer_guard.get("ready_for_future_signing_issuer_guard") is True
+ and boundary.get("ready_for_future_signable_request_boundary") is True,
+ {
+ "result": guard.get("result"),
+ "ready_for_future_signing_issuer_guard": issuer_guard.get(
+ "ready_for_future_signing_issuer_guard"
+ ),
+ "ready_for_future_signable_request_boundary": boundary.get(
+ "ready_for_future_signable_request_boundary"
+ ),
+ },
+ "wait_for_signing_issuer_guard",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "final_signable_boundary_complete",
+ bool(boundary.get("boundary_id"))
+ and boundary.get("request_boundary_type") == "future_signable_request_boundary"
+ and boundary.get("can_enter_future_authorization_signing_issuer_lane") is True,
+ {
+ "boundary_id": boundary.get("boundary_id"),
+ "request_boundary_type": boundary.get("request_boundary_type"),
+ "can_enter_future_authorization_signing_issuer_lane": boundary.get(
+ "can_enter_future_authorization_signing_issuer_lane"
+ ),
+ },
+ "wait_for_signable_request_boundary",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "source_chain_ids_present",
+ bool(issuer_guard.get("guard_id"))
+ and bool(boundary.get("source_signing_decision_closeout_id"))
+ and bool(boundary.get("source_unsigned_signing_decision_package_id"))
+ and bool(boundary.get("source_signing_decision_preflight_id"))
+ and bool(boundary.get("source_issuer_gate_id"))
+ and bool(boundary.get("source_nonsecret_envelope_id"))
+ and bool(boundary.get("source_decision_closeout_id"))
+ and bool(boundary.get("source_decision_preflight_id"))
+ and bool(boundary.get("source_lane_guard_id"))
+ and bool(boundary.get("source_closeout_package_id"))
+ and bool(boundary.get("source_intake_id"))
+ and bool(boundary.get("source_closeout_boundary_id")),
+ {
+ "guard_id": issuer_guard.get("guard_id"),
+ "source_signing_decision_closeout_id": boundary.get(
+ "source_signing_decision_closeout_id"
+ ),
+ "source_unsigned_signing_decision_package_id": boundary.get(
+ "source_unsigned_signing_decision_package_id"
+ ),
+ "source_issuer_gate_id": boundary.get("source_issuer_gate_id"),
+ "source_nonsecret_envelope_id": boundary.get("source_nonsecret_envelope_id"),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "same_run_production_truth_required",
+ boundary.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": boundary.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "post_apply_verifier_required",
+ boundary.get("requires_post_apply_verifier") is True
+ and bool(boundary.get("post_apply_verifier_endpoint"))
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "requires_post_apply_verifier": boundary.get("requires_post_apply_verifier"),
+ "post_apply_verifier_endpoint": boundary.get("post_apply_verifier_endpoint"),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "migration_file_hash_locked",
+ boundary.get("target_file") == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and boundary.get("hash_matches") is True
+ and bool(boundary.get("expected_sha256"))
+ and boundary.get("expected_sha256") == boundary.get("actual_sha256"),
+ {
+ "target_file": boundary.get("target_file"),
+ "hash_matches": boundary.get("hash_matches"),
+ "expected_sha256": boundary.get("expected_sha256"),
+ "actual_sha256": boundary.get("actual_sha256"),
+ },
+ "abort_on_migration_file_hash_mismatch",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "signing_inputs_and_rejection_policy_carried_forward",
+ int(summary.get("signing_decision_input_requirement_count") or 0) == 10
+ and int(summary.get("signing_decision_rejection_reason_count") or 0) == 11
+ and boundary.get("signing_decision_input_requirement_count") == 10
+ and boundary.get("signing_decision_rejection_reason_count") == 11,
+ {
+ "signing_decision_input_requirement_count": summary.get(
+ "signing_decision_input_requirement_count", 0
+ ),
+ "signing_decision_rejection_reason_count": summary.get(
+ "signing_decision_rejection_reason_count", 0
+ ),
+ },
+ "wait_for_signing_input_contract",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "contract_is_future_only_and_non_executing",
+ contract.get("permits_future_authorization_signing_issuer_lane") is True
+ and contract.get("issues_database_apply_authorization") is False
+ and contract.get("ready_for_database_apply_now") is False
+ and contract.get("signs_database_apply_authorization") is False
+ and contract.get("writes_database") is False
+ and contract.get("executes_in_preview") is False,
+ {
+ "permits_future_authorization_signing_issuer_lane": contract.get(
+ "permits_future_authorization_signing_issuer_lane"
+ ),
+ "issues_database_apply_authorization": contract.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": contract.get("ready_for_database_apply_now"),
+ "signs_database_apply_authorization": contract.get(
+ "signs_database_apply_authorization"
+ ),
+ },
+ "block_if_contract_authorizes_or_executes",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "signable_boundary_has_no_secret_material",
+ boundary.get("operator_secret_boundary") == "future_shell_only"
+ and boundary.get("secret_material_included") is False
+ and boundary.get("secret_material_required_in_preview") is False
+ and boundary.get("reads_secret_in_preview") is False,
+ {
+ "operator_secret_boundary": boundary.get("operator_secret_boundary"),
+ "secret_material_included": boundary.get("secret_material_included"),
+ "secret_material_required_in_preview": boundary.get(
+ "secret_material_required_in_preview"
+ ),
+ "reads_secret_in_preview": boundary.get("reads_secret_in_preview"),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "final_package_does_not_authorize_sign_or_apply",
+ issuer_guard.get("issues_database_apply_authorization") is False
+ and issuer_guard.get("ready_for_database_apply_now") is False
+ and issuer_guard.get("signs_database_apply_authorization") is False
+ and boundary.get("issues_database_apply_authorization") is False
+ and boundary.get("ready_for_database_apply_now") is False
+ and boundary.get("signs_database_apply_authorization") is False,
+ {
+ "issuer_guard_issues_database_apply_authorization": issuer_guard.get(
+ "issues_database_apply_authorization"
+ ),
+ "issuer_guard_ready_for_database_apply_now": issuer_guard.get(
+ "ready_for_database_apply_now"
+ ),
+ "boundary_signs_database_apply_authorization": boundary.get(
+ "signs_database_apply_authorization"
+ ),
+ },
+ "block_if_closeout_authorizes_signs_or_applies",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "preview_has_no_side_effects_and_no_signing",
+ side_effect_free,
+ {
+ "writes_script_count": summary.get("writes_script_count", 0),
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_or_signing",
+ ),
+ _authorization_signing_issuer_closeout_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and issuer_guard.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": issuer_guard.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_GUARD"
+ )
+ closeout_id = _db_apply_authorization_signing_issuer_closeout_id(guard)
+ future_authorization_signing_issuer_closeout = {
+ "closeout_id": closeout_id,
+ "source_signing_issuer_guard_id": issuer_guard.get("guard_id"),
+ "source_signable_request_boundary_id": boundary.get("boundary_id"),
+ "source_signing_decision_closeout_id": boundary.get(
+ "source_signing_decision_closeout_id"
+ ),
+ "source_unsigned_signing_decision_package_id": boundary.get(
+ "source_unsigned_signing_decision_package_id"
+ ),
+ "source_signing_decision_preflight_id": boundary.get(
+ "source_signing_decision_preflight_id"
+ ),
+ "source_issuer_gate_id": boundary.get("source_issuer_gate_id"),
+ "source_nonsecret_envelope_id": boundary.get("source_nonsecret_envelope_id"),
+ "source_decision_closeout_id": boundary.get("source_decision_closeout_id"),
+ "source_decision_preflight_id": boundary.get("source_decision_preflight_id"),
+ "source_lane_guard_id": boundary.get("source_lane_guard_id"),
+ "source_closeout_package_id": boundary.get("source_closeout_package_id"),
+ "source_intake_id": boundary.get("source_intake_id"),
+ "source_closeout_boundary_id": boundary.get("source_closeout_boundary_id"),
+ "status": closeout_status,
+ "ready_for_future_signing_issuer_closeout": closeout_ready,
+ "can_enter_future_final_signable_request_package_lane": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ final_signable_request_package = {
+ "package_id": closeout_id,
+ "source_signing_issuer_guard_id": issuer_guard.get("guard_id"),
+ "source_signable_request_boundary_id": boundary.get("boundary_id"),
+ "source_unsigned_signing_decision_package_id": boundary.get(
+ "source_unsigned_signing_decision_package_id"
+ ),
+ "source_signing_decision_closeout_id": boundary.get(
+ "source_signing_decision_closeout_id"
+ ),
+ "source_signing_decision_preflight_id": boundary.get(
+ "source_signing_decision_preflight_id"
+ ),
+ "source_issuer_gate_id": boundary.get("source_issuer_gate_id"),
+ "source_nonsecret_envelope_id": boundary.get("source_nonsecret_envelope_id"),
+ "source_decision_closeout_id": boundary.get("source_decision_closeout_id"),
+ "source_decision_preflight_id": boundary.get("source_decision_preflight_id"),
+ "source_lane_guard_id": boundary.get("source_lane_guard_id"),
+ "source_closeout_package_id": boundary.get("source_closeout_package_id"),
+ "source_intake_id": boundary.get("source_intake_id"),
+ "source_closeout_boundary_id": boundary.get("source_closeout_boundary_id"),
+ "status": closeout_status,
+ "authorization_material_type": "final_signable_request_package",
+ "ready_for_future_final_signable_request_package": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "target_file": boundary.get("target_file"),
+ "expected_sha256": boundary.get("expected_sha256"),
+ "actual_sha256": boundary.get("actual_sha256"),
+ "hash_matches": boundary.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": boundary.get("post_apply_verifier_endpoint"),
+ "operator_secret_boundary": "future_shell_only",
+ "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,
+ "signing_decision_input_requirement_count": boundary.get(
+ "signing_decision_input_requirement_count"
+ ),
+ "signing_decision_rejection_reason_count": boundary.get(
+ "signing_decision_rejection_reason_count"
+ ),
+ }
+ signing_issuer_closeout_contract = {
+ "mode": "final_signable_request_closeout_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout"
+ ),
+ "source_signing_issuer_guard_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-guard"
+ ),
+ "machine_verifiable": True,
+ "permits_future_final_signable_request_package_lane": closeout_ready,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(guard.get("success")),
+ "generated_at": guard.get("generated_at"),
+ "source_policy": guard.get("policy"),
+ "stats": guard.get("stats") or {},
+ "summary": {
+ "authorization_signing_issuer_closeout_ready_count": 1 if closeout_ready else 0,
+ "signing_issuer_closeout_check_count": len(checks),
+ "signing_issuer_closeout_pass_count": passed_count,
+ "signing_issuer_closeout_waiting_count": len(waiting_checks),
+ "authorization_signing_issuer_guard_ready_count": summary.get(
+ "authorization_signing_issuer_guard_ready_count", 0
+ ),
+ "signing_issuer_guard_check_count": summary.get(
+ "signing_issuer_guard_check_count", 0
+ ),
+ "signing_issuer_guard_pass_count": summary.get(
+ "signing_issuer_guard_pass_count", 0
+ ),
+ "signing_decision_input_requirement_count": summary.get(
+ "signing_decision_input_requirement_count", 0
+ ),
+ "signing_decision_rejection_reason_count": summary.get(
+ "signing_decision_rejection_reason_count", 0
+ ),
+ "required_issuer_evidence_count": summary.get("required_issuer_evidence_count", 0),
+ "nonsecret_authorization_claim_count": summary.get(
+ "nonsecret_authorization_claim_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_signing_issuer_closeout": (
+ future_authorization_signing_issuer_closeout
+ ),
+ "final_signable_request_package": final_signable_request_package,
+ "signing_issuer_closeout_contract": signing_issuer_closeout_contract,
+ "signing_issuer_closeout_checks": checks,
+ "source_signing_issuer_guard_summary": summary,
+ "source_signing_issuer_guard_contract": contract,
+ "source_signable_request_boundary": boundary,
+ "safety": {
+ "read_only_db_apply_authorization_signing_issuer_closeout": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout as the final signable request package for a future explicit authorization signing lane.",
+ "Keep authorization signing, secret reads, shell execution, SQL, and DB writes out of this closeout.",
+ "Require fresh production truth, operator-held secret material, rollback boundary, and post-apply verifier inside the future signing run.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_signing_execution_preflight(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Preflight a future explicit authorization signing execution without signing."""
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_issuer_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ issuer_closeout = closeout.get("future_authorization_signing_issuer_closeout") or {}
+ final_package = closeout.get("final_signable_request_package") or {}
+ closeout_contract = closeout.get("signing_issuer_closeout_contract") or {}
+ summary = closeout.get("summary") or {}
+ safety = closeout.get("safety") or {}
+ preflight_id = _db_apply_authorization_signing_execution_preflight_id(closeout)
+
+ required_nonsecret_inputs = [
+ {
+ "key": "final_signable_request_package_id",
+ "value": final_package.get("package_id"),
+ "secret": False,
+ },
+ {
+ "key": "signing_issuer_closeout_id",
+ "value": issuer_closeout.get("closeout_id"),
+ "secret": False,
+ },
+ {"key": "target_file", "value": final_package.get("target_file"), "secret": False},
+ {
+ "key": "migration_expected_sha256",
+ "value": final_package.get("expected_sha256"),
+ "secret": False,
+ },
+ {
+ "key": "migration_actual_sha256",
+ "value": final_package.get("actual_sha256"),
+ "secret": False,
+ },
+ {
+ "key": "same_run_production_truth",
+ "value": "required_before_future_signing_execution",
+ "secret": False,
+ },
+ {
+ "key": "post_apply_verifier_endpoint",
+ "value": final_package.get("post_apply_verifier_endpoint"),
+ "secret": False,
+ },
+ {
+ "key": "rollback_boundary",
+ "value": "required_before_future_db_apply",
+ "secret": False,
+ },
+ {
+ "key": "abort_conditions",
+ "value": "fail_closed_before_future_signing_execution",
+ "secret": False,
+ },
+ {
+ "key": "operator_held_secret_reference",
+ "value": "external_runtime_reference_only",
+ "secret": False,
+ },
+ ]
+ abort_conditions = [
+ "abort_if_production_truth_missing_or_stale",
+ "abort_if_final_signable_request_package_not_ready",
+ "abort_if_migration_hash_mismatch",
+ "abort_if_post_apply_verifier_missing",
+ "abort_if_secret_value_enters_preview_payload",
+ "abort_if_future_runner_cannot_use_check_mode",
+ "abort_if_authorization_signing_would_write_database",
+ "abort_if_manual_review_gate_reappears_in_primary_flow",
+ ]
+ rollback_boundary = [
+ "capture_prewrite_snapshot_before_future_apply",
+ "keep_migration_rollback_sql_bound_to_same_hash",
+ "require_post_apply_readback_before_success_receipt",
+ "route_failed_verifier_to_exception_only_auto_resolution",
+ ]
+ operator_held_secret_boundary_contract = {
+ "mode": "operator_held_secret_reference_only",
+ "secret_material_owner": "external_runtime_signing_boundary",
+ "secret_reference_mode": "external_runtime_reference_only",
+ "secret_reference_placeholder": "OPERATOR_HELD_SIGNING_SECRET_REF",
+ "secret_material_included": False,
+ "secret_material_required_in_preview": False,
+ "secret_material_required_in_future_signing": True,
+ "reads_secret_in_preview": False,
+ "accepts_plaintext_secret": False,
+ "permits_secret_value_logging": False,
+ "ai_controlled_preflight_produces_nonsecret_envelope": True,
+ "manual_review_mode": "exception_only",
+ }
+ command_preview = {
+ "mode": "future_command_shape_only",
+ "command_shape": (
+ "sign-db-apply-authorization --request "
+ "--secret-ref --check-mode"
+ ),
+ "redacts_secret_values": True,
+ "executes_in_preview": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ }
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and final_package.get("secret_material_included") is False
+ and final_package.get("secret_material_required_in_preview") is False
+ and final_package.get("reads_secret_in_preview") is False
+ and final_package.get("executes_shell_in_preview") is False
+ and final_package.get("executes_sql_in_preview") is False
+ and final_package.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_signing_execution_preflight_check(
+ "signing_issuer_closeout_ready",
+ closeout.get("result") == "DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_CLOSEOUT_READY"
+ and issuer_closeout.get("ready_for_future_signing_issuer_closeout") is True
+ and final_package.get("ready_for_future_final_signable_request_package") is True,
+ {
+ "result": closeout.get("result"),
+ "ready_for_future_signing_issuer_closeout": issuer_closeout.get(
+ "ready_for_future_signing_issuer_closeout"
+ ),
+ "ready_for_future_final_signable_request_package": final_package.get(
+ "ready_for_future_final_signable_request_package"
+ ),
+ },
+ "wait_for_signing_issuer_closeout",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "final_signable_request_package_complete",
+ final_package.get("authorization_material_type") == "final_signable_request_package"
+ and bool(final_package.get("package_id"))
+ and bool(final_package.get("source_signable_request_boundary_id"))
+ and final_package.get("ready_for_database_apply_now") is False
+ and final_package.get("issues_database_apply_authorization") is False
+ and final_package.get("signs_database_apply_authorization") is False,
+ {
+ "package_id": final_package.get("package_id"),
+ "authorization_material_type": final_package.get("authorization_material_type"),
+ "source_signable_request_boundary_id": final_package.get(
+ "source_signable_request_boundary_id"
+ ),
+ },
+ "wait_for_final_signable_request_package",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "source_chain_ids_present",
+ bool(issuer_closeout.get("closeout_id"))
+ and bool(final_package.get("source_signing_issuer_guard_id"))
+ and bool(final_package.get("source_unsigned_signing_decision_package_id"))
+ and bool(final_package.get("source_issuer_gate_id"))
+ and bool(final_package.get("source_nonsecret_envelope_id"))
+ and bool(final_package.get("source_lane_guard_id"))
+ and bool(final_package.get("source_intake_id")),
+ {
+ "closeout_id": issuer_closeout.get("closeout_id"),
+ "source_signing_issuer_guard_id": final_package.get(
+ "source_signing_issuer_guard_id"
+ ),
+ "source_unsigned_signing_decision_package_id": final_package.get(
+ "source_unsigned_signing_decision_package_id"
+ ),
+ "source_issuer_gate_id": final_package.get("source_issuer_gate_id"),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "same_run_production_truth_required",
+ final_package.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": final_package.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "post_apply_verifier_required",
+ final_package.get("requires_post_apply_verifier") is True
+ and bool(final_package.get("post_apply_verifier_endpoint"))
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "requires_post_apply_verifier": final_package.get(
+ "requires_post_apply_verifier"
+ ),
+ "post_apply_verifier_endpoint": final_package.get(
+ "post_apply_verifier_endpoint"
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "migration_file_hash_locked",
+ final_package.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and final_package.get("hash_matches") is True
+ and bool(final_package.get("expected_sha256"))
+ and final_package.get("expected_sha256") == final_package.get("actual_sha256"),
+ {
+ "target_file": final_package.get("target_file"),
+ "hash_matches": final_package.get("hash_matches"),
+ "expected_sha256": final_package.get("expected_sha256"),
+ "actual_sha256": final_package.get("actual_sha256"),
+ },
+ "abort_on_migration_file_hash_mismatch",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "required_nonsecret_signing_inputs_complete",
+ len(required_nonsecret_inputs) == 10
+ and all(item.get("secret") is False for item in required_nonsecret_inputs)
+ and all(item.get("value") for item in required_nonsecret_inputs),
+ {
+ "required_nonsecret_input_count": len(required_nonsecret_inputs),
+ "secret_input_count": sum(1 for item in required_nonsecret_inputs if item.get("secret")),
+ },
+ "wait_for_nonsecret_signing_inputs",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "operator_held_secret_boundary_is_externalized",
+ operator_held_secret_boundary_contract.get("secret_reference_mode")
+ == "external_runtime_reference_only"
+ and operator_held_secret_boundary_contract.get("secret_material_included") is False
+ and operator_held_secret_boundary_contract.get("secret_material_required_in_preview")
+ is False
+ and operator_held_secret_boundary_contract.get("reads_secret_in_preview") is False
+ and operator_held_secret_boundary_contract.get("accepts_plaintext_secret") is False
+ and operator_held_secret_boundary_contract.get("permits_secret_value_logging") is False,
+ {
+ "secret_reference_mode": operator_held_secret_boundary_contract.get(
+ "secret_reference_mode"
+ ),
+ "secret_material_included": operator_held_secret_boundary_contract.get(
+ "secret_material_included"
+ ),
+ "reads_secret_in_preview": operator_held_secret_boundary_contract.get(
+ "reads_secret_in_preview"
+ ),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "future_command_preview_is_non_executing_and_redacted",
+ 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,
+ {
+ "mode": command_preview.get("mode"),
+ "redacts_secret_values": command_preview.get("redacts_secret_values"),
+ "executes_in_preview": command_preview.get("executes_in_preview"),
+ },
+ "block_if_command_preview_executes_or_exposes_secret",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "rollback_and_abort_boundaries_present",
+ len(rollback_boundary) == 4 and len(abort_conditions) == 8,
+ {
+ "rollback_boundary_count": len(rollback_boundary),
+ "abort_condition_count": len(abort_conditions),
+ },
+ "wait_for_rollback_or_abort_boundary",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "preview_has_no_side_effects_and_no_signing",
+ side_effect_free,
+ {
+ "writes_script_count": summary.get("writes_script_count", 0),
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_or_signing",
+ ),
+ _authorization_signing_execution_preflight_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and issuer_closeout.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": issuer_closeout.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ preflight_ready = not waiting_checks
+ preflight_status = (
+ "DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT_READY"
+ if preflight_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_CLOSEOUT"
+ )
+ future_authorization_signing_execution_preflight = {
+ "preflight_id": preflight_id,
+ "source_signing_issuer_closeout_id": issuer_closeout.get("closeout_id"),
+ "source_final_signable_request_package_id": final_package.get("package_id"),
+ "status": preflight_status,
+ "ready_for_future_signing_execution_preflight": preflight_ready,
+ "can_enter_future_authorization_signing_execution_lane": preflight_ready,
+ "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,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ signing_execution_preflight_package = {
+ "package_id": preflight_id,
+ "authorization_material_type": "signing_execution_preflight_package",
+ "source_final_signable_request_package_id": final_package.get("package_id"),
+ "source_signing_issuer_closeout_id": issuer_closeout.get("closeout_id"),
+ "status": preflight_status,
+ "ready_for_future_signing_execution_preflight": preflight_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "required_nonsecret_inputs": required_nonsecret_inputs,
+ "required_nonsecret_input_count": len(required_nonsecret_inputs),
+ "operator_held_secret_boundary_contract": operator_held_secret_boundary_contract,
+ "command_preview": command_preview,
+ "abort_conditions": abort_conditions,
+ "rollback_boundary": rollback_boundary,
+ "target_file": final_package.get("target_file"),
+ "expected_sha256": final_package.get("expected_sha256"),
+ "actual_sha256": final_package.get("actual_sha256"),
+ "hash_matches": final_package.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": final_package.get("post_apply_verifier_endpoint"),
+ "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,
+ }
+ signing_execution_preflight_contract = {
+ "mode": "explicit_authorization_signing_execution_preflight_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight"
+ ),
+ "source_signing_issuer_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_explicit_authorization_signing_execution_lane": preflight_ready,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT_POLICY,
+ "result": preflight_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "authorization_signing_execution_preflight_ready_count": (
+ 1 if preflight_ready else 0
+ ),
+ "signing_execution_preflight_check_count": len(checks),
+ "signing_execution_preflight_pass_count": passed_count,
+ "signing_execution_preflight_waiting_count": len(waiting_checks),
+ "authorization_signing_issuer_closeout_ready_count": summary.get(
+ "authorization_signing_issuer_closeout_ready_count", 0
+ ),
+ "signing_issuer_closeout_check_count": summary.get(
+ "signing_issuer_closeout_check_count", 0
+ ),
+ "signing_issuer_closeout_pass_count": summary.get(
+ "signing_issuer_closeout_pass_count", 0
+ ),
+ "final_signable_request_package_ready_count": (
+ 1
+ if final_package.get("ready_for_future_final_signable_request_package") is True
+ else 0
+ ),
+ "operator_held_secret_boundary_count": 1,
+ "signing_execution_input_requirement_count": len(required_nonsecret_inputs),
+ "signing_execution_abort_condition_count": len(abort_conditions),
+ "rollback_boundary_count": len(rollback_boundary),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_signing_execution_preflight": (
+ future_authorization_signing_execution_preflight
+ ),
+ "signing_execution_preflight_package": signing_execution_preflight_package,
+ "operator_held_secret_boundary_contract": operator_held_secret_boundary_contract,
+ "signing_execution_preflight_contract": signing_execution_preflight_contract,
+ "signing_execution_preflight_checks": checks,
+ "source_signing_issuer_closeout_summary": summary,
+ "source_signing_issuer_closeout_contract": closeout_contract,
+ "source_final_signable_request_package": final_package,
+ "safety": {
+ "read_only_db_apply_authorization_signing_execution_preflight": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this preflight to validate a future explicit authorization signing execution request.",
+ "Keep secret values outside AI payloads; the preview only carries an external runtime reference placeholder.",
+ "Require same-run production truth, hash lock, rollback boundary, and post-apply verifier before any future signing run.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_signing_execution_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the future signing execution preflight without signing authorization."""
+ preflight = build_pchome_auto_policy_db_apply_authorization_signing_execution_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ 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 {}
+ preflight_contract = preflight.get("signing_execution_preflight_contract") or {}
+ summary = preflight.get("summary") or {}
+ safety = preflight.get("safety") or {}
+ required_inputs = list(package.get("required_nonsecret_inputs") or [])
+ abort_conditions = list(package.get("abort_conditions") or [])
+ rollback_boundary = list(package.get("rollback_boundary") or [])
+ command_preview = package.get("command_preview") or {}
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and package.get("secret_material_included") is False
+ and package.get("secret_material_required_in_preview") is False
+ and package.get("reads_secret_in_preview") is False
+ and package.get("executes_shell_in_preview") is False
+ and package.get("executes_sql_in_preview") is False
+ and package.get("writes_database_in_preview") is False
+ and boundary.get("secret_material_included") is False
+ and boundary.get("reads_secret_in_preview") is False
+ )
+ checks = [
+ _authorization_signing_execution_closeout_check(
+ "signing_execution_preflight_ready",
+ preflight.get("result") == "DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT_READY"
+ and future_preflight.get("ready_for_future_signing_execution_preflight") is True
+ and package.get("ready_for_future_signing_execution_preflight") is True,
+ {
+ "result": preflight.get("result"),
+ "ready_for_future_signing_execution_preflight": future_preflight.get(
+ "ready_for_future_signing_execution_preflight"
+ ),
+ "package_ready_for_future_signing_execution_preflight": package.get(
+ "ready_for_future_signing_execution_preflight"
+ ),
+ },
+ "wait_for_signing_execution_preflight",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "unsigned_receipt_boundary_source_package_complete",
+ package.get("authorization_material_type") == "signing_execution_preflight_package"
+ and bool(package.get("package_id"))
+ and bool(package.get("source_final_signable_request_package_id"))
+ and bool(package.get("source_signing_issuer_closeout_id"))
+ and package.get("ready_for_database_apply_now") is False
+ and package.get("issues_database_apply_authorization") is False
+ and package.get("signs_database_apply_authorization") is False,
+ {
+ "package_id": package.get("package_id"),
+ "source_final_signable_request_package_id": package.get(
+ "source_final_signable_request_package_id"
+ ),
+ "source_signing_issuer_closeout_id": package.get(
+ "source_signing_issuer_closeout_id"
+ ),
+ },
+ "wait_for_signing_execution_preflight_package",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "source_chain_ids_present",
+ bool(future_preflight.get("preflight_id"))
+ and bool(future_preflight.get("source_signing_issuer_closeout_id"))
+ and bool(future_preflight.get("source_final_signable_request_package_id"))
+ and bool(package.get("source_final_signable_request_package_id"))
+ and bool(package.get("source_signing_issuer_closeout_id")),
+ {
+ "preflight_id": future_preflight.get("preflight_id"),
+ "source_signing_issuer_closeout_id": future_preflight.get(
+ "source_signing_issuer_closeout_id"
+ ),
+ "source_final_signable_request_package_id": future_preflight.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "operator_held_secret_boundary_carried_forward",
+ boundary.get("secret_reference_mode") == "external_runtime_reference_only"
+ and boundary.get("secret_material_included") is False
+ and boundary.get("secret_material_required_in_preview") is False
+ and boundary.get("reads_secret_in_preview") is False
+ and boundary.get("accepts_plaintext_secret") is False
+ and boundary.get("permits_secret_value_logging") is False,
+ {
+ "secret_reference_mode": boundary.get("secret_reference_mode"),
+ "secret_material_included": boundary.get("secret_material_included"),
+ "reads_secret_in_preview": boundary.get("reads_secret_in_preview"),
+ "accepts_plaintext_secret": boundary.get("accepts_plaintext_secret"),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "required_nonsecret_inputs_carried_forward",
+ int(summary.get("signing_execution_input_requirement_count") or 0) == 10
+ and len(required_inputs) == 10
+ and all(item.get("secret") is False for item in required_inputs)
+ and all(item.get("value") for item in required_inputs),
+ {
+ "summary_input_count": summary.get("signing_execution_input_requirement_count", 0),
+ "required_input_count": len(required_inputs),
+ "secret_input_count": sum(1 for item in required_inputs if item.get("secret")),
+ },
+ "wait_for_nonsecret_signing_inputs",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "same_run_production_truth_required",
+ package.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": package.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "post_apply_verifier_required",
+ package.get("requires_post_apply_verifier") is True
+ and bool(package.get("post_apply_verifier_endpoint"))
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "requires_post_apply_verifier": package.get("requires_post_apply_verifier"),
+ "post_apply_verifier_endpoint": package.get("post_apply_verifier_endpoint"),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "migration_file_hash_locked",
+ package.get("target_file") == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and package.get("hash_matches") is True
+ and bool(package.get("expected_sha256"))
+ and package.get("expected_sha256") == package.get("actual_sha256"),
+ {
+ "target_file": package.get("target_file"),
+ "hash_matches": package.get("hash_matches"),
+ "expected_sha256": package.get("expected_sha256"),
+ "actual_sha256": package.get("actual_sha256"),
+ },
+ "abort_on_migration_file_hash_mismatch",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "future_command_preview_is_non_executing_and_redacted",
+ 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,
+ {
+ "mode": command_preview.get("mode"),
+ "redacts_secret_values": command_preview.get("redacts_secret_values"),
+ "executes_in_preview": command_preview.get("executes_in_preview"),
+ },
+ "block_if_command_preview_executes_or_exposes_secret",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "rollback_and_abort_boundaries_carried_forward",
+ len(rollback_boundary) == 4
+ and len(abort_conditions) == 8
+ and int(summary.get("rollback_boundary_count") or 0) == 4
+ and int(summary.get("signing_execution_abort_condition_count") or 0) == 8,
+ {
+ "rollback_boundary_count": len(rollback_boundary),
+ "abort_condition_count": len(abort_conditions),
+ "summary_rollback_boundary_count": summary.get("rollback_boundary_count", 0),
+ "summary_abort_condition_count": summary.get(
+ "signing_execution_abort_condition_count", 0
+ ),
+ },
+ "wait_for_rollback_or_abort_boundary",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "closeout_does_not_authorize_sign_or_apply",
+ preflight_contract.get("permits_future_explicit_authorization_signing_execution_lane")
+ is True
+ and preflight_contract.get("issues_database_apply_authorization") is False
+ and preflight_contract.get("ready_for_database_apply_now") is False
+ and preflight_contract.get("signs_database_apply_authorization") is False
+ and preflight_contract.get("writes_database") is False
+ and preflight_contract.get("executes_in_preview") is False
+ and future_preflight.get("issues_database_apply_authorization") is False
+ and future_preflight.get("ready_for_database_apply_now") is False
+ and future_preflight.get("signs_database_apply_authorization") is False,
+ {
+ "permits_future_explicit_authorization_signing_execution_lane": (
+ preflight_contract.get(
+ "permits_future_explicit_authorization_signing_execution_lane"
+ )
+ ),
+ "issues_database_apply_authorization": preflight_contract.get(
+ "issues_database_apply_authorization"
+ ),
+ "signs_database_apply_authorization": preflight_contract.get(
+ "signs_database_apply_authorization"
+ ),
+ },
+ "block_if_closeout_authorizes_signs_or_applies",
+ ),
+ _authorization_signing_execution_closeout_check(
+ "preview_has_no_side_effects_and_no_signing",
+ side_effect_free,
+ {
+ "writes_script_count": summary.get("writes_script_count", 0),
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_or_signing",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT"
+ )
+ closeout_id = _db_apply_authorization_signing_execution_closeout_id(preflight)
+ future_authorization_signing_execution_closeout = {
+ "closeout_id": closeout_id,
+ "source_signing_execution_preflight_id": future_preflight.get("preflight_id"),
+ "source_final_signable_request_package_id": future_preflight.get(
+ "source_final_signable_request_package_id"
+ ),
+ "source_signing_issuer_closeout_id": future_preflight.get(
+ "source_signing_issuer_closeout_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_signing_execution_closeout": closeout_ready,
+ "can_enter_future_unsigned_signed_authorization_receipt_boundary": closeout_ready,
+ "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,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ unsigned_signed_authorization_receipt_boundary = {
+ "boundary_id": closeout_id,
+ "authorization_material_type": "unsigned_signed_authorization_receipt_boundary",
+ "source_signing_execution_preflight_id": future_preflight.get("preflight_id"),
+ "source_signing_execution_preflight_package_id": package.get("package_id"),
+ "source_final_signable_request_package_id": package.get(
+ "source_final_signable_request_package_id"
+ ),
+ "source_signing_issuer_closeout_id": package.get("source_signing_issuer_closeout_id"),
+ "status": closeout_status,
+ "ready_for_future_unsigned_signed_authorization_receipt_boundary": closeout_ready,
+ "ready_for_future_signed_authorization_receipt_lane": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "signed_authorization_receipt_included": False,
+ "signature_material_included": False,
+ "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,
+ "required_nonsecret_inputs": required_inputs,
+ "operator_held_secret_boundary_contract": boundary,
+ "command_preview": command_preview,
+ "abort_conditions": abort_conditions,
+ "rollback_boundary": rollback_boundary,
+ "target_file": package.get("target_file"),
+ "expected_sha256": package.get("expected_sha256"),
+ "actual_sha256": package.get("actual_sha256"),
+ "hash_matches": package.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": package.get("post_apply_verifier_endpoint"),
+ }
+ signing_execution_closeout_contract = {
+ "mode": "explicit_authorization_signing_execution_closeout_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-closeout"
+ ),
+ "source_signing_execution_preflight_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight"
+ ),
+ "machine_verifiable": True,
+ "permits_future_unsigned_signed_authorization_receipt_boundary": closeout_ready,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(preflight.get("success")),
+ "generated_at": preflight.get("generated_at"),
+ "source_policy": preflight.get("policy"),
+ "stats": preflight.get("stats") or {},
+ "summary": {
+ "authorization_signing_execution_closeout_ready_count": 1 if closeout_ready else 0,
+ "signing_execution_closeout_check_count": len(checks),
+ "signing_execution_closeout_pass_count": passed_count,
+ "signing_execution_closeout_waiting_count": len(waiting_checks),
+ "authorization_signing_execution_preflight_ready_count": summary.get(
+ "authorization_signing_execution_preflight_ready_count", 0
+ ),
+ "signing_execution_preflight_check_count": summary.get(
+ "signing_execution_preflight_check_count", 0
+ ),
+ "signing_execution_preflight_pass_count": summary.get(
+ "signing_execution_preflight_pass_count", 0
+ ),
+ "unsigned_signed_authorization_receipt_boundary_count": 1,
+ "operator_held_secret_boundary_count": summary.get(
+ "operator_held_secret_boundary_count", 0
+ ),
+ "signing_execution_input_requirement_count": summary.get(
+ "signing_execution_input_requirement_count", 0
+ ),
+ "signing_execution_abort_condition_count": summary.get(
+ "signing_execution_abort_condition_count", 0
+ ),
+ "rollback_boundary_count": summary.get("rollback_boundary_count", 0),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_signing_execution_closeout": (
+ future_authorization_signing_execution_closeout
+ ),
+ "unsigned_signed_authorization_receipt_boundary": (
+ unsigned_signed_authorization_receipt_boundary
+ ),
+ "signing_execution_closeout_contract": signing_execution_closeout_contract,
+ "signing_execution_closeout_checks": checks,
+ "source_signing_execution_preflight_summary": summary,
+ "source_signing_execution_preflight_contract": preflight_contract,
+ "source_signing_execution_preflight_package": package,
+ "safety": {
+ "read_only_db_apply_authorization_signing_execution_closeout": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout as the unsigned receipt boundary for a future signed authorization receipt lane.",
+ "Keep signing, secret reads, shell execution, SQL, and DB writes out of this closeout.",
+ "Require same-run production truth, hash lock, rollback boundary, and post-apply verifier before any future signing receipt run.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_signed_receipt_preflight(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Preflight a future externally signed authorization receipt without signing."""
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_execution_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ future_closeout = closeout.get("future_authorization_signing_execution_closeout") or {}
+ unsigned_boundary = closeout.get("unsigned_signed_authorization_receipt_boundary") or {}
+ closeout_contract = closeout.get("signing_execution_closeout_contract") or {}
+ summary = closeout.get("summary") or {}
+ safety = closeout.get("safety") or {}
+ preflight_id = _db_apply_authorization_signed_receipt_preflight_id(closeout)
+ required_inputs = list(unsigned_boundary.get("required_nonsecret_inputs") or [])
+ abort_conditions = list(unsigned_boundary.get("abort_conditions") or [])
+ rollback_boundary = list(unsigned_boundary.get("rollback_boundary") or [])
+ operator_secret_boundary = (
+ unsigned_boundary.get("operator_held_secret_boundary_contract") or {}
+ )
+ command_preview = unsigned_boundary.get("command_preview") or {}
+ required_external_receipt_evidence = [
+ "external_receipt_id",
+ "source_unsigned_receipt_boundary_id",
+ "source_signing_execution_closeout_id",
+ "source_final_signable_request_package_id",
+ "signer_key_id_reference",
+ "signature_algorithm_reference",
+ "signed_at_utc",
+ "payload_sha256",
+ "receipt_sha256",
+ "detached_signature_verification_status",
+ ]
+ external_receipt_acceptance_gates = [
+ "production_truth_matches_receipt_generation_run",
+ "source_unsigned_boundary_id_matches",
+ "payload_hash_matches_final_signable_request_package",
+ "receipt_hash_is_present_and_nonempty",
+ "detached_signature_verification_status_is_passed",
+ "signer_key_id_is_reference_only",
+ "no_secret_or_signature_material_in_ai_payload",
+ "post_apply_verifier_still_required",
+ ]
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and unsigned_boundary.get("signed_authorization_receipt_included") is False
+ and unsigned_boundary.get("signature_material_included") is False
+ and unsigned_boundary.get("secret_material_included") is False
+ and unsigned_boundary.get("secret_material_required_in_preview") is False
+ and unsigned_boundary.get("reads_secret_in_preview") is False
+ and unsigned_boundary.get("executes_shell_in_preview") is False
+ and unsigned_boundary.get("executes_sql_in_preview") is False
+ and unsigned_boundary.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_signed_receipt_preflight_check(
+ "signing_execution_closeout_ready",
+ closeout.get("result") == "DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_CLOSEOUT_READY"
+ and future_closeout.get("ready_for_future_signing_execution_closeout") is True
+ and unsigned_boundary.get(
+ "ready_for_future_unsigned_signed_authorization_receipt_boundary"
+ )
+ is True,
+ {
+ "result": closeout.get("result"),
+ "ready_for_future_signing_execution_closeout": future_closeout.get(
+ "ready_for_future_signing_execution_closeout"
+ ),
+ "ready_for_future_unsigned_signed_authorization_receipt_boundary": (
+ unsigned_boundary.get(
+ "ready_for_future_unsigned_signed_authorization_receipt_boundary"
+ )
+ ),
+ },
+ "wait_for_signing_execution_closeout",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "unsigned_receipt_boundary_complete",
+ unsigned_boundary.get("authorization_material_type")
+ == "unsigned_signed_authorization_receipt_boundary"
+ and bool(unsigned_boundary.get("boundary_id"))
+ and bool(unsigned_boundary.get("source_signing_execution_preflight_id"))
+ and bool(unsigned_boundary.get("source_final_signable_request_package_id"))
+ and unsigned_boundary.get("ready_for_database_apply_now") is False
+ and unsigned_boundary.get("issues_database_apply_authorization") is False
+ and unsigned_boundary.get("signs_database_apply_authorization") is False,
+ {
+ "boundary_id": unsigned_boundary.get("boundary_id"),
+ "authorization_material_type": unsigned_boundary.get(
+ "authorization_material_type"
+ ),
+ "source_final_signable_request_package_id": unsigned_boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_unsigned_receipt_boundary",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "source_chain_ids_present",
+ bool(future_closeout.get("closeout_id"))
+ and bool(future_closeout.get("source_signing_execution_preflight_id"))
+ and bool(future_closeout.get("source_final_signable_request_package_id"))
+ and bool(future_closeout.get("source_signing_issuer_closeout_id"))
+ and bool(unsigned_boundary.get("source_signing_execution_preflight_package_id"))
+ and bool(unsigned_boundary.get("source_signing_issuer_closeout_id")),
+ {
+ "closeout_id": future_closeout.get("closeout_id"),
+ "source_signing_execution_preflight_id": future_closeout.get(
+ "source_signing_execution_preflight_id"
+ ),
+ "source_final_signable_request_package_id": future_closeout.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "external_receipt_evidence_contract_complete",
+ len(required_external_receipt_evidence) == 10
+ and len(external_receipt_acceptance_gates) == 8,
+ {
+ "required_external_receipt_evidence_count": len(
+ required_external_receipt_evidence
+ ),
+ "external_receipt_acceptance_gate_count": len(
+ external_receipt_acceptance_gates
+ ),
+ },
+ "wait_for_external_receipt_evidence_contract",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "operator_held_secret_boundary_carried_forward",
+ operator_secret_boundary.get("secret_reference_mode")
+ == "external_runtime_reference_only"
+ and operator_secret_boundary.get("secret_material_included") is False
+ and operator_secret_boundary.get("secret_material_required_in_preview") is False
+ and operator_secret_boundary.get("reads_secret_in_preview") is False
+ and operator_secret_boundary.get("accepts_plaintext_secret") is False
+ and operator_secret_boundary.get("permits_secret_value_logging") is False,
+ {
+ "secret_reference_mode": operator_secret_boundary.get("secret_reference_mode"),
+ "secret_material_included": operator_secret_boundary.get(
+ "secret_material_included"
+ ),
+ "reads_secret_in_preview": operator_secret_boundary.get(
+ "reads_secret_in_preview"
+ ),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "same_run_production_truth_required",
+ unsigned_boundary.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": unsigned_boundary.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "post_apply_verifier_required",
+ unsigned_boundary.get("requires_post_apply_verifier") is True
+ and bool(unsigned_boundary.get("post_apply_verifier_endpoint"))
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "requires_post_apply_verifier": unsigned_boundary.get(
+ "requires_post_apply_verifier"
+ ),
+ "post_apply_verifier_endpoint": unsigned_boundary.get(
+ "post_apply_verifier_endpoint"
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "migration_file_hash_locked",
+ unsigned_boundary.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and unsigned_boundary.get("hash_matches") is True
+ and bool(unsigned_boundary.get("expected_sha256"))
+ and unsigned_boundary.get("expected_sha256") == unsigned_boundary.get("actual_sha256"),
+ {
+ "target_file": unsigned_boundary.get("target_file"),
+ "hash_matches": unsigned_boundary.get("hash_matches"),
+ "expected_sha256": unsigned_boundary.get("expected_sha256"),
+ "actual_sha256": unsigned_boundary.get("actual_sha256"),
+ },
+ "abort_on_migration_file_hash_mismatch",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "nonsecret_inputs_and_command_preview_carried_forward",
+ int(summary.get("signing_execution_input_requirement_count") or 0) == 10
+ and len(required_inputs) == 10
+ and all(item.get("secret") is False for item in required_inputs)
+ 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,
+ {
+ "required_input_count": len(required_inputs),
+ "command_preview_mode": command_preview.get("mode"),
+ "redacts_secret_values": command_preview.get("redacts_secret_values"),
+ },
+ "wait_for_nonsecret_inputs_or_command_preview",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "rollback_and_abort_boundaries_carried_forward",
+ len(rollback_boundary) == 4
+ and len(abort_conditions) == 8
+ and int(summary.get("rollback_boundary_count") or 0) == 4
+ and int(summary.get("signing_execution_abort_condition_count") or 0) == 8,
+ {
+ "rollback_boundary_count": len(rollback_boundary),
+ "abort_condition_count": len(abort_conditions),
+ "summary_rollback_boundary_count": summary.get("rollback_boundary_count", 0),
+ "summary_abort_condition_count": summary.get(
+ "signing_execution_abort_condition_count", 0
+ ),
+ },
+ "wait_for_rollback_or_abort_boundary",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "preflight_has_no_signed_receipt_signature_or_authorization",
+ unsigned_boundary.get("signed_authorization_receipt_included") is False
+ and unsigned_boundary.get("signature_material_included") is False
+ and unsigned_boundary.get("secret_material_included") is False
+ and closeout_contract.get(
+ "permits_future_unsigned_signed_authorization_receipt_boundary"
+ )
+ is True
+ and closeout_contract.get("issues_database_apply_authorization") is False
+ and closeout_contract.get("ready_for_database_apply_now") is False
+ and closeout_contract.get("signs_database_apply_authorization") is False,
+ {
+ "signed_authorization_receipt_included": unsigned_boundary.get(
+ "signed_authorization_receipt_included"
+ ),
+ "signature_material_included": unsigned_boundary.get(
+ "signature_material_included"
+ ),
+ "signs_database_apply_authorization": closeout_contract.get(
+ "signs_database_apply_authorization"
+ ),
+ },
+ "block_if_signed_receipt_or_authorization_is_present",
+ ),
+ _authorization_signed_receipt_preflight_check(
+ "preview_has_no_side_effects_and_no_signing",
+ side_effect_free,
+ {
+ "writes_script_count": summary.get("writes_script_count", 0),
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_or_signing",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ preflight_ready = not waiting_checks
+ preflight_status = (
+ "DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_PREFLIGHT_READY"
+ if preflight_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_CLOSEOUT"
+ )
+ future_authorization_signed_receipt_preflight = {
+ "preflight_id": preflight_id,
+ "source_signing_execution_closeout_id": future_closeout.get("closeout_id"),
+ "source_unsigned_receipt_boundary_id": unsigned_boundary.get("boundary_id"),
+ "source_final_signable_request_package_id": unsigned_boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": preflight_status,
+ "ready_for_future_signed_authorization_receipt_preflight": preflight_ready,
+ "can_enter_future_external_signing_receipt_evidence_boundary": preflight_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "signed_authorization_receipt_included": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "secret_material_required_in_preview": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ external_signing_receipt_evidence_boundary = {
+ "boundary_id": preflight_id,
+ "authorization_material_type": "external_signing_receipt_evidence_boundary",
+ "source_signing_execution_closeout_id": future_closeout.get("closeout_id"),
+ "source_unsigned_receipt_boundary_id": unsigned_boundary.get("boundary_id"),
+ "source_signing_execution_preflight_id": unsigned_boundary.get(
+ "source_signing_execution_preflight_id"
+ ),
+ "source_final_signable_request_package_id": unsigned_boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": preflight_status,
+ "ready_for_future_external_signing_receipt_evidence_boundary": preflight_ready,
+ "ready_for_future_signed_authorization_receipt_lane": preflight_ready,
+ "required_external_receipt_evidence": required_external_receipt_evidence,
+ "required_external_receipt_evidence_count": len(required_external_receipt_evidence),
+ "external_receipt_acceptance_gates": external_receipt_acceptance_gates,
+ "external_receipt_acceptance_gate_count": len(external_receipt_acceptance_gates),
+ "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,
+ "reads_secret_in_preview": False,
+ "executes_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "operator_held_secret_boundary_contract": operator_secret_boundary,
+ "target_file": unsigned_boundary.get("target_file"),
+ "expected_sha256": unsigned_boundary.get("expected_sha256"),
+ "actual_sha256": unsigned_boundary.get("actual_sha256"),
+ "hash_matches": unsigned_boundary.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": unsigned_boundary.get("post_apply_verifier_endpoint"),
+ }
+ signed_receipt_preflight_contract = {
+ "mode": "signed_authorization_receipt_preflight_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-preflight"
+ ),
+ "source_signing_execution_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_external_signing_receipt_evidence_boundary": preflight_ready,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_PREFLIGHT_POLICY,
+ "result": preflight_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "authorization_signed_receipt_preflight_ready_count": 1 if preflight_ready else 0,
+ "signed_receipt_preflight_check_count": len(checks),
+ "signed_receipt_preflight_pass_count": passed_count,
+ "signed_receipt_preflight_waiting_count": len(waiting_checks),
+ "authorization_signing_execution_closeout_ready_count": summary.get(
+ "authorization_signing_execution_closeout_ready_count", 0
+ ),
+ "signing_execution_closeout_check_count": summary.get(
+ "signing_execution_closeout_check_count", 0
+ ),
+ "unsigned_signed_authorization_receipt_boundary_count": summary.get(
+ "unsigned_signed_authorization_receipt_boundary_count", 0
+ ),
+ "external_signing_receipt_evidence_boundary_count": 1,
+ "required_external_receipt_evidence_count": len(required_external_receipt_evidence),
+ "external_receipt_acceptance_gate_count": len(external_receipt_acceptance_gates),
+ "operator_held_secret_boundary_count": summary.get(
+ "operator_held_secret_boundary_count", 0
+ ),
+ "signing_execution_input_requirement_count": summary.get(
+ "signing_execution_input_requirement_count", 0
+ ),
+ "signing_execution_abort_condition_count": summary.get(
+ "signing_execution_abort_condition_count", 0
+ ),
+ "rollback_boundary_count": summary.get("rollback_boundary_count", 0),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_signed_receipt_preflight": (
+ future_authorization_signed_receipt_preflight
+ ),
+ "external_signing_receipt_evidence_boundary": (
+ external_signing_receipt_evidence_boundary
+ ),
+ "signed_receipt_preflight_contract": signed_receipt_preflight_contract,
+ "signed_receipt_preflight_checks": checks,
+ "source_signing_execution_closeout_summary": summary,
+ "source_signing_execution_closeout_contract": closeout_contract,
+ "source_unsigned_signed_authorization_receipt_boundary": unsigned_boundary,
+ "safety": {
+ "read_only_db_apply_authorization_signed_receipt_preflight": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this preflight to validate future external signed authorization receipt evidence.",
+ "Keep signed receipt content, signature material, secret values, shell execution, SQL, and DB writes out of this preflight.",
+ "Require detached receipt verification, same-run production truth, hash lock, rollback boundary, and post-apply verifier before any future receipt closeout.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_signed_receipt_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out future signed receipt preflight without carrying signed material."""
+ preflight = build_pchome_auto_policy_db_apply_authorization_signed_receipt_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ future_preflight = preflight.get("future_authorization_signed_receipt_preflight") or {}
+ evidence_boundary = preflight.get("external_signing_receipt_evidence_boundary") or {}
+ preflight_contract = preflight.get("signed_receipt_preflight_contract") or {}
+ summary = preflight.get("summary") or {}
+ safety = preflight.get("safety") or {}
+ closeout_id = _db_apply_authorization_signed_receipt_closeout_id(preflight)
+ required_evidence = list(evidence_boundary.get("required_external_receipt_evidence") or [])
+ acceptance_gates = list(evidence_boundary.get("external_receipt_acceptance_gates") or [])
+ operator_secret_boundary = (
+ evidence_boundary.get("operator_held_secret_boundary_contract") or {}
+ )
+ detached_verification_checks = [
+ "receipt_id_present",
+ "source_unsigned_receipt_boundary_id_matches",
+ "source_signing_execution_closeout_id_matches",
+ "payload_sha256_matches_final_signable_request_package",
+ "receipt_sha256_present",
+ "signer_key_id_is_reference_only",
+ "signature_algorithm_is_reference_only",
+ "detached_signature_verification_status_passed",
+ "same_run_production_truth_verified",
+ "no_secret_or_signature_material_in_ai_payload",
+ ]
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and evidence_boundary.get("external_signed_authorization_receipt_included") is False
+ and evidence_boundary.get("signed_authorization_receipt_included") is False
+ and evidence_boundary.get("signature_material_included") is False
+ and evidence_boundary.get("secret_material_included") is False
+ and evidence_boundary.get("secret_material_required_in_preview") is False
+ and evidence_boundary.get("reads_secret_in_preview") is False
+ and evidence_boundary.get("executes_shell_in_preview") is False
+ and evidence_boundary.get("executes_sql_in_preview") is False
+ and evidence_boundary.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_signed_receipt_closeout_check(
+ "signed_receipt_preflight_ready",
+ preflight.get("result") == "DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_PREFLIGHT_READY"
+ and future_preflight.get("ready_for_future_signed_authorization_receipt_preflight")
+ is True
+ and evidence_boundary.get(
+ "ready_for_future_external_signing_receipt_evidence_boundary"
+ )
+ is True,
+ {
+ "result": preflight.get("result"),
+ "ready_for_future_signed_authorization_receipt_preflight": (
+ future_preflight.get(
+ "ready_for_future_signed_authorization_receipt_preflight"
+ )
+ ),
+ "ready_for_future_external_signing_receipt_evidence_boundary": (
+ evidence_boundary.get(
+ "ready_for_future_external_signing_receipt_evidence_boundary"
+ )
+ ),
+ },
+ "wait_for_signed_receipt_preflight",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "external_receipt_evidence_boundary_complete",
+ evidence_boundary.get("authorization_material_type")
+ == "external_signing_receipt_evidence_boundary"
+ and bool(evidence_boundary.get("boundary_id"))
+ and bool(evidence_boundary.get("source_signing_execution_closeout_id"))
+ and bool(evidence_boundary.get("source_unsigned_receipt_boundary_id"))
+ and bool(evidence_boundary.get("source_final_signable_request_package_id"))
+ and evidence_boundary.get("ready_for_database_apply_now") is False
+ and evidence_boundary.get("issues_database_apply_authorization") is False
+ and evidence_boundary.get("signs_database_apply_authorization") is False,
+ {
+ "boundary_id": evidence_boundary.get("boundary_id"),
+ "authorization_material_type": evidence_boundary.get("authorization_material_type"),
+ "source_unsigned_receipt_boundary_id": evidence_boundary.get(
+ "source_unsigned_receipt_boundary_id"
+ ),
+ },
+ "wait_for_external_receipt_evidence_boundary",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "source_chain_ids_present",
+ bool(future_preflight.get("preflight_id"))
+ and bool(future_preflight.get("source_signing_execution_closeout_id"))
+ and bool(future_preflight.get("source_unsigned_receipt_boundary_id"))
+ and bool(future_preflight.get("source_final_signable_request_package_id"))
+ and bool(evidence_boundary.get("source_signing_execution_preflight_id")),
+ {
+ "preflight_id": future_preflight.get("preflight_id"),
+ "source_signing_execution_closeout_id": future_preflight.get(
+ "source_signing_execution_closeout_id"
+ ),
+ "source_unsigned_receipt_boundary_id": future_preflight.get(
+ "source_unsigned_receipt_boundary_id"
+ ),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "external_receipt_evidence_contract_carried_forward",
+ len(required_evidence) == 10
+ and len(acceptance_gates) == 8
+ and int(summary.get("required_external_receipt_evidence_count") or 0) == 10
+ and int(summary.get("external_receipt_acceptance_gate_count") or 0) == 8,
+ {
+ "required_external_receipt_evidence_count": len(required_evidence),
+ "external_receipt_acceptance_gate_count": len(acceptance_gates),
+ "summary_required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "summary_external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ },
+ "wait_for_external_receipt_evidence_contract",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "detached_receipt_verification_boundary_contract_complete",
+ len(detached_verification_checks) == 10
+ and "detached_signature_verification_status_passed" in detached_verification_checks,
+ {
+ "detached_receipt_verification_check_count": len(
+ detached_verification_checks
+ ),
+ "requires_detached_signature_verification": (
+ "detached_signature_verification_status_passed"
+ in detached_verification_checks
+ ),
+ },
+ "wait_for_detached_receipt_verification_contract",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "operator_held_secret_boundary_carried_forward",
+ operator_secret_boundary.get("secret_reference_mode")
+ == "external_runtime_reference_only"
+ and operator_secret_boundary.get("secret_material_included") is False
+ and operator_secret_boundary.get("secret_material_required_in_preview") is False
+ and operator_secret_boundary.get("reads_secret_in_preview") is False
+ and operator_secret_boundary.get("accepts_plaintext_secret") is False
+ and operator_secret_boundary.get("permits_secret_value_logging") is False,
+ {
+ "secret_reference_mode": operator_secret_boundary.get("secret_reference_mode"),
+ "secret_material_included": operator_secret_boundary.get(
+ "secret_material_included"
+ ),
+ "reads_secret_in_preview": operator_secret_boundary.get(
+ "reads_secret_in_preview"
+ ),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "same_run_production_truth_required",
+ evidence_boundary.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": evidence_boundary.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "post_apply_verifier_required",
+ evidence_boundary.get("requires_post_apply_verifier") is True
+ and bool(evidence_boundary.get("post_apply_verifier_endpoint"))
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "requires_post_apply_verifier": evidence_boundary.get(
+ "requires_post_apply_verifier"
+ ),
+ "post_apply_verifier_endpoint": evidence_boundary.get(
+ "post_apply_verifier_endpoint"
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "migration_file_hash_locked",
+ evidence_boundary.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and evidence_boundary.get("hash_matches") is True
+ and bool(evidence_boundary.get("expected_sha256"))
+ and evidence_boundary.get("expected_sha256") == evidence_boundary.get("actual_sha256"),
+ {
+ "target_file": evidence_boundary.get("target_file"),
+ "hash_matches": evidence_boundary.get("hash_matches"),
+ "expected_sha256": evidence_boundary.get("expected_sha256"),
+ "actual_sha256": evidence_boundary.get("actual_sha256"),
+ },
+ "abort_on_migration_file_hash_mismatch",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "closeout_has_no_signed_receipt_signature_or_authorization",
+ evidence_boundary.get("external_signed_authorization_receipt_included") is False
+ and evidence_boundary.get("signed_authorization_receipt_included") is False
+ and evidence_boundary.get("signature_material_included") is False
+ and evidence_boundary.get("secret_material_included") is False
+ and preflight_contract.get(
+ "permits_future_external_signing_receipt_evidence_boundary"
+ )
+ is True
+ and preflight_contract.get("issues_database_apply_authorization") is False
+ and preflight_contract.get("ready_for_database_apply_now") is False
+ and preflight_contract.get("signs_database_apply_authorization") is False,
+ {
+ "external_signed_authorization_receipt_included": evidence_boundary.get(
+ "external_signed_authorization_receipt_included"
+ ),
+ "signed_authorization_receipt_included": evidence_boundary.get(
+ "signed_authorization_receipt_included"
+ ),
+ "signature_material_included": evidence_boundary.get(
+ "signature_material_included"
+ ),
+ "signs_database_apply_authorization": preflight_contract.get(
+ "signs_database_apply_authorization"
+ ),
+ },
+ "block_if_signed_receipt_signature_or_authorization_is_present",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "preview_has_no_side_effects_and_no_signing",
+ side_effect_free,
+ {
+ "writes_script_count": summary.get("writes_script_count", 0),
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_or_signing",
+ ),
+ _authorization_signed_receipt_closeout_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and future_preflight.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": future_preflight.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_PREFLIGHT"
+ )
+ future_authorization_signed_receipt_closeout = {
+ "closeout_id": closeout_id,
+ "source_signed_receipt_preflight_id": future_preflight.get("preflight_id"),
+ "source_external_receipt_evidence_boundary_id": evidence_boundary.get("boundary_id"),
+ "source_signing_execution_closeout_id": future_preflight.get(
+ "source_signing_execution_closeout_id"
+ ),
+ "source_unsigned_receipt_boundary_id": future_preflight.get(
+ "source_unsigned_receipt_boundary_id"
+ ),
+ "source_final_signable_request_package_id": future_preflight.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_signed_authorization_receipt_closeout": closeout_ready,
+ "can_enter_future_detached_receipt_verification_boundary": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "external_signed_authorization_receipt_included": False,
+ "signed_authorization_receipt_included": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "secret_material_required_in_preview": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ detached_receipt_verification_boundary = {
+ "boundary_id": closeout_id,
+ "authorization_material_type": "detached_receipt_verification_boundary",
+ "source_signed_receipt_preflight_id": future_preflight.get("preflight_id"),
+ "source_external_receipt_evidence_boundary_id": evidence_boundary.get("boundary_id"),
+ "source_signing_execution_closeout_id": evidence_boundary.get(
+ "source_signing_execution_closeout_id"
+ ),
+ "source_unsigned_receipt_boundary_id": evidence_boundary.get(
+ "source_unsigned_receipt_boundary_id"
+ ),
+ "source_final_signable_request_package_id": evidence_boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_detached_receipt_verification_boundary": closeout_ready,
+ "ready_for_future_signed_authorization_receipt_verification_lane": closeout_ready,
+ "required_external_receipt_evidence": required_evidence,
+ "required_external_receipt_evidence_count": len(required_evidence),
+ "external_receipt_acceptance_gates": acceptance_gates,
+ "external_receipt_acceptance_gate_count": len(acceptance_gates),
+ "detached_receipt_verification_checks": detached_verification_checks,
+ "detached_receipt_verification_check_count": len(detached_verification_checks),
+ "requires_detached_signature_verification": True,
+ "detached_signature_verification_performed": False,
+ "detached_signature_verification_status_required_in_future": True,
+ "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,
+ "reads_secret_in_preview": False,
+ "executes_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "operator_held_secret_boundary_contract": operator_secret_boundary,
+ "target_file": evidence_boundary.get("target_file"),
+ "expected_sha256": evidence_boundary.get("expected_sha256"),
+ "actual_sha256": evidence_boundary.get("actual_sha256"),
+ "hash_matches": evidence_boundary.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": evidence_boundary.get("post_apply_verifier_endpoint"),
+ }
+ signed_receipt_closeout_contract = {
+ "mode": "signed_authorization_receipt_closeout_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-closeout"
+ ),
+ "source_signed_receipt_preflight_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-preflight"
+ ),
+ "machine_verifiable": True,
+ "permits_future_detached_receipt_verification_boundary": closeout_ready,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(preflight.get("success")),
+ "generated_at": preflight.get("generated_at"),
+ "source_policy": preflight.get("policy"),
+ "stats": preflight.get("stats") or {},
+ "summary": {
+ "authorization_signed_receipt_closeout_ready_count": 1 if closeout_ready else 0,
+ "signed_receipt_closeout_check_count": len(checks),
+ "signed_receipt_closeout_pass_count": passed_count,
+ "signed_receipt_closeout_waiting_count": len(waiting_checks),
+ "authorization_signed_receipt_preflight_ready_count": summary.get(
+ "authorization_signed_receipt_preflight_ready_count", 0
+ ),
+ "signed_receipt_preflight_check_count": summary.get(
+ "signed_receipt_preflight_check_count", 0
+ ),
+ "external_signing_receipt_evidence_boundary_count": summary.get(
+ "external_signing_receipt_evidence_boundary_count", 0
+ ),
+ "detached_receipt_verification_boundary_count": 1,
+ "required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ "detached_receipt_verification_check_count": len(detached_verification_checks),
+ "operator_held_secret_boundary_count": summary.get(
+ "operator_held_secret_boundary_count", 0
+ ),
+ "signing_execution_input_requirement_count": summary.get(
+ "signing_execution_input_requirement_count", 0
+ ),
+ "signing_execution_abort_condition_count": summary.get(
+ "signing_execution_abort_condition_count", 0
+ ),
+ "rollback_boundary_count": summary.get("rollback_boundary_count", 0),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_signed_receipt_closeout": (
+ future_authorization_signed_receipt_closeout
+ ),
+ "detached_receipt_verification_boundary": detached_receipt_verification_boundary,
+ "signed_receipt_closeout_contract": signed_receipt_closeout_contract,
+ "signed_receipt_closeout_checks": checks,
+ "source_signed_receipt_preflight_summary": summary,
+ "source_signed_receipt_preflight_contract": preflight_contract,
+ "source_external_signing_receipt_evidence_boundary": evidence_boundary,
+ "safety": {
+ "read_only_db_apply_authorization_signed_receipt_closeout": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout as the detached receipt verification boundary for a future signed authorization receipt evidence lane.",
+ "Keep signed receipt content, signature material, secret values, shell execution, SQL, and DB writes out of this closeout.",
+ "Require real detached verification evidence in a future separate lane before any database apply authorization can be considered.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_signed_receipt_evidence_intake(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Define the future signed receipt evidence intake schema without taking secrets."""
+ closeout = build_pchome_auto_policy_db_apply_authorization_signed_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ future_closeout = closeout.get("future_authorization_signed_receipt_closeout") or {}
+ detached_boundary = closeout.get("detached_receipt_verification_boundary") or {}
+ closeout_contract = closeout.get("signed_receipt_closeout_contract") or {}
+ summary = closeout.get("summary") or {}
+ safety = closeout.get("safety") or {}
+ intake_id = _db_apply_authorization_signed_receipt_evidence_intake_id(closeout)
+ required_external_evidence = list(
+ detached_boundary.get("required_external_receipt_evidence") or []
+ )
+ external_acceptance_gates = list(
+ detached_boundary.get("external_receipt_acceptance_gates") or []
+ )
+ detached_checks = list(
+ detached_boundary.get("detached_receipt_verification_checks") or []
+ )
+ operator_secret_boundary = (
+ detached_boundary.get("operator_held_secret_boundary_contract") or {}
+ )
+ detached_verification_evidence_fields = [
+ "external_receipt_id",
+ "source_signed_receipt_preflight_id",
+ "source_external_receipt_evidence_boundary_id",
+ "source_signed_receipt_closeout_id",
+ "source_detached_receipt_verification_boundary_id",
+ "payload_sha256",
+ "receipt_sha256",
+ "signer_key_id_reference",
+ "signature_algorithm_reference",
+ "detached_signature_verification_status",
+ "verified_at_utc",
+ "verifier_receipt_sha256",
+ ]
+ detached_verification_acceptance_gates = [
+ "production_truth_matches_intake_run",
+ "source_detached_boundary_id_matches",
+ "source_external_receipt_evidence_boundary_id_matches",
+ "source_signed_receipt_preflight_id_matches",
+ "payload_hash_matches_final_signable_request_package",
+ "receipt_hash_is_present_and_nonempty",
+ "signer_key_id_is_reference_only",
+ "signature_algorithm_is_reference_only",
+ "detached_signature_verification_status_is_passed",
+ "no_secret_signature_or_signed_receipt_body_in_ai_payload",
+ ]
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and detached_boundary.get("detached_signature_verification_performed") is False
+ and detached_boundary.get("external_signed_authorization_receipt_included") is False
+ and detached_boundary.get("signed_authorization_receipt_included") is False
+ and detached_boundary.get("signature_material_included") is False
+ and detached_boundary.get("secret_material_included") is False
+ and detached_boundary.get("secret_material_required_in_preview") is False
+ and detached_boundary.get("reads_secret_in_preview") is False
+ and detached_boundary.get("executes_shell_in_preview") is False
+ and detached_boundary.get("executes_sql_in_preview") is False
+ and detached_boundary.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_signed_receipt_evidence_intake_check(
+ "signed_receipt_closeout_ready",
+ closeout.get("result") == "DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_CLOSEOUT_READY"
+ and future_closeout.get("ready_for_future_signed_authorization_receipt_closeout")
+ is True
+ and detached_boundary.get("ready_for_future_detached_receipt_verification_boundary")
+ is True,
+ {
+ "result": closeout.get("result"),
+ "ready_for_future_signed_authorization_receipt_closeout": (
+ future_closeout.get(
+ "ready_for_future_signed_authorization_receipt_closeout"
+ )
+ ),
+ "ready_for_future_detached_receipt_verification_boundary": (
+ detached_boundary.get(
+ "ready_for_future_detached_receipt_verification_boundary"
+ )
+ ),
+ },
+ "wait_for_signed_receipt_closeout",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "detached_receipt_verification_boundary_complete",
+ detached_boundary.get("authorization_material_type")
+ == "detached_receipt_verification_boundary"
+ and bool(detached_boundary.get("boundary_id"))
+ and bool(detached_boundary.get("source_signed_receipt_preflight_id"))
+ and bool(detached_boundary.get("source_external_receipt_evidence_boundary_id"))
+ and detached_boundary.get("ready_for_database_apply_now") is False
+ and detached_boundary.get("issues_database_apply_authorization") is False
+ and detached_boundary.get("signs_database_apply_authorization") is False,
+ {
+ "boundary_id": detached_boundary.get("boundary_id"),
+ "authorization_material_type": detached_boundary.get(
+ "authorization_material_type"
+ ),
+ "source_external_receipt_evidence_boundary_id": detached_boundary.get(
+ "source_external_receipt_evidence_boundary_id"
+ ),
+ },
+ "wait_for_detached_receipt_verification_boundary",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "source_chain_ids_present",
+ bool(future_closeout.get("closeout_id"))
+ and bool(future_closeout.get("source_signed_receipt_preflight_id"))
+ and bool(future_closeout.get("source_external_receipt_evidence_boundary_id"))
+ and bool(future_closeout.get("source_signing_execution_closeout_id"))
+ and bool(detached_boundary.get("source_final_signable_request_package_id")),
+ {
+ "closeout_id": future_closeout.get("closeout_id"),
+ "source_signed_receipt_preflight_id": future_closeout.get(
+ "source_signed_receipt_preflight_id"
+ ),
+ "source_final_signable_request_package_id": detached_boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "external_receipt_contract_carried_forward",
+ len(required_external_evidence) == 10
+ and len(external_acceptance_gates) == 8
+ and int(summary.get("required_external_receipt_evidence_count") or 0) == 10
+ and int(summary.get("external_receipt_acceptance_gate_count") or 0) == 8,
+ {
+ "required_external_receipt_evidence_count": len(required_external_evidence),
+ "external_receipt_acceptance_gate_count": len(external_acceptance_gates),
+ "summary_required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "summary_external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ },
+ "wait_for_external_receipt_contract",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "detached_verification_evidence_schema_complete",
+ len(detached_verification_evidence_fields) == 12
+ and len(detached_verification_acceptance_gates) == 10
+ and len(detached_checks) == 10
+ and "detached_signature_verification_status_passed" in detached_checks,
+ {
+ "detached_verification_evidence_field_count": len(
+ detached_verification_evidence_fields
+ ),
+ "detached_verification_acceptance_gate_count": len(
+ detached_verification_acceptance_gates
+ ),
+ "source_detached_check_count": len(detached_checks),
+ },
+ "wait_for_detached_verification_evidence_schema",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "signer_and_algorithm_references_only",
+ detached_boundary.get("signer_key_id_reference_only") is True
+ and detached_boundary.get("signature_algorithm_reference_only") is True
+ and "signer_key_id_reference" in detached_verification_evidence_fields
+ and "signature_algorithm_reference" in detached_verification_evidence_fields,
+ {
+ "signer_key_id_reference_only": detached_boundary.get(
+ "signer_key_id_reference_only"
+ ),
+ "signature_algorithm_reference_only": detached_boundary.get(
+ "signature_algorithm_reference_only"
+ ),
+ },
+ "abort_on_plaintext_key_or_algorithm_material",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "no_signed_receipt_signature_secret_or_verification_execution",
+ detached_boundary.get("detached_signature_verification_performed") is False
+ and detached_boundary.get("external_signed_authorization_receipt_included")
+ is False
+ and detached_boundary.get("signed_authorization_receipt_included") is False
+ and detached_boundary.get("signature_material_included") is False
+ and detached_boundary.get("secret_material_included") is False
+ and operator_secret_boundary.get("secret_reference_mode")
+ == "external_runtime_reference_only"
+ and operator_secret_boundary.get("accepts_plaintext_secret") is False,
+ {
+ "detached_signature_verification_performed": detached_boundary.get(
+ "detached_signature_verification_performed"
+ ),
+ "external_signed_authorization_receipt_included": detached_boundary.get(
+ "external_signed_authorization_receipt_included"
+ ),
+ "signature_material_included": detached_boundary.get(
+ "signature_material_included"
+ ),
+ "secret_reference_mode": operator_secret_boundary.get(
+ "secret_reference_mode"
+ ),
+ },
+ "abort_on_signed_receipt_signature_secret_or_verification_execution",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "same_run_production_truth_required",
+ detached_boundary.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": detached_boundary.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "post_apply_verifier_required",
+ detached_boundary.get("requires_post_apply_verifier") is True
+ and bool(detached_boundary.get("post_apply_verifier_endpoint"))
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "requires_post_apply_verifier": detached_boundary.get(
+ "requires_post_apply_verifier"
+ ),
+ "post_apply_verifier_endpoint": detached_boundary.get(
+ "post_apply_verifier_endpoint"
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ },
+ "require_post_apply_verifier",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "migration_file_hash_locked",
+ detached_boundary.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and detached_boundary.get("hash_matches") is True
+ and bool(detached_boundary.get("expected_sha256"))
+ and detached_boundary.get("expected_sha256")
+ == detached_boundary.get("actual_sha256"),
+ {
+ "target_file": detached_boundary.get("target_file"),
+ "hash_matches": detached_boundary.get("hash_matches"),
+ "expected_sha256": detached_boundary.get("expected_sha256"),
+ "actual_sha256": detached_boundary.get("actual_sha256"),
+ },
+ "abort_on_migration_file_hash_mismatch",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "preview_has_no_side_effects_and_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_or_signing",
+ ),
+ _authorization_signed_receipt_evidence_intake_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and future_closeout.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": future_closeout.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ intake_ready = not waiting_checks
+ intake_status = (
+ "DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_EVIDENCE_INTAKE_READY"
+ if intake_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_CLOSEOUT"
+ )
+ future_signed_authorization_receipt_evidence_intake = {
+ "intake_id": intake_id,
+ "source_signed_receipt_closeout_id": future_closeout.get("closeout_id"),
+ "source_detached_receipt_verification_boundary_id": detached_boundary.get(
+ "boundary_id"
+ ),
+ "source_signed_receipt_preflight_id": detached_boundary.get(
+ "source_signed_receipt_preflight_id"
+ ),
+ "source_external_receipt_evidence_boundary_id": detached_boundary.get(
+ "source_external_receipt_evidence_boundary_id"
+ ),
+ "source_final_signable_request_package_id": detached_boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": intake_status,
+ "ready_for_future_signed_authorization_receipt_evidence_intake": intake_ready,
+ "can_enter_future_detached_verification_evidence_validation": intake_ready,
+ "external_signed_authorization_receipt_evidence_schema_ready": intake_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "detached_signature_verification_performed": False,
+ "external_signed_authorization_receipt_included": False,
+ "signed_authorization_receipt_included": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "secret_material_required_in_preview": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ detached_verification_evidence_schema = {
+ "schema_id": intake_id,
+ "authorization_material_type": "detached_verification_evidence_schema",
+ "source_signed_receipt_closeout_id": future_closeout.get("closeout_id"),
+ "source_detached_receipt_verification_boundary_id": detached_boundary.get(
+ "boundary_id"
+ ),
+ "source_signed_receipt_preflight_id": detached_boundary.get(
+ "source_signed_receipt_preflight_id"
+ ),
+ "source_external_receipt_evidence_boundary_id": detached_boundary.get(
+ "source_external_receipt_evidence_boundary_id"
+ ),
+ "source_final_signable_request_package_id": detached_boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": intake_status,
+ "ready_for_future_detached_verification_evidence_schema": intake_ready,
+ "required_external_receipt_evidence": required_external_evidence,
+ "required_external_receipt_evidence_count": len(required_external_evidence),
+ "external_receipt_acceptance_gates": external_acceptance_gates,
+ "external_receipt_acceptance_gate_count": len(external_acceptance_gates),
+ "detached_receipt_verification_checks": detached_checks,
+ "detached_receipt_verification_check_count": len(detached_checks),
+ "detached_verification_evidence_fields": detached_verification_evidence_fields,
+ "detached_verification_evidence_field_count": len(
+ detached_verification_evidence_fields
+ ),
+ "detached_verification_acceptance_gates": detached_verification_acceptance_gates,
+ "detached_verification_acceptance_gate_count": len(
+ detached_verification_acceptance_gates
+ ),
+ "requires_detached_signature_verification": True,
+ "detached_signature_verification_performed": 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_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "operator_held_secret_boundary_contract": operator_secret_boundary,
+ "target_file": detached_boundary.get("target_file"),
+ "expected_sha256": detached_boundary.get("expected_sha256"),
+ "actual_sha256": detached_boundary.get("actual_sha256"),
+ "hash_matches": detached_boundary.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": detached_boundary.get(
+ "post_apply_verifier_endpoint"
+ ),
+ }
+ signed_receipt_evidence_intake_contract = {
+ "mode": "signed_authorization_receipt_evidence_intake_schema_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-evidence-intake"
+ ),
+ "source_signed_receipt_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_detached_verification_evidence_validation": intake_ready,
+ "accepts_plaintext_secret": False,
+ "detached_signature_verification_performed": False,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_EVIDENCE_INTAKE_POLICY,
+ "result": intake_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "authorization_signed_receipt_evidence_intake_ready_count": (
+ 1 if intake_ready else 0
+ ),
+ "signed_receipt_evidence_intake_check_count": len(checks),
+ "signed_receipt_evidence_intake_pass_count": passed_count,
+ "signed_receipt_evidence_intake_waiting_count": len(waiting_checks),
+ "authorization_signed_receipt_closeout_ready_count": summary.get(
+ "authorization_signed_receipt_closeout_ready_count", 0
+ ),
+ "signed_receipt_closeout_check_count": summary.get(
+ "signed_receipt_closeout_check_count", 0
+ ),
+ "authorization_signed_receipt_preflight_ready_count": summary.get(
+ "authorization_signed_receipt_preflight_ready_count", 0
+ ),
+ "signed_receipt_preflight_check_count": summary.get(
+ "signed_receipt_preflight_check_count", 0
+ ),
+ "external_signing_receipt_evidence_boundary_count": summary.get(
+ "external_signing_receipt_evidence_boundary_count", 0
+ ),
+ "detached_receipt_verification_boundary_count": summary.get(
+ "detached_receipt_verification_boundary_count", 0
+ ),
+ "detached_verification_evidence_schema_count": 1,
+ "required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ "detached_receipt_verification_check_count": summary.get(
+ "detached_receipt_verification_check_count", 0
+ ),
+ "detached_verification_evidence_field_count": len(
+ detached_verification_evidence_fields
+ ),
+ "detached_verification_acceptance_gate_count": len(
+ detached_verification_acceptance_gates
+ ),
+ "operator_held_secret_boundary_count": summary.get(
+ "operator_held_secret_boundary_count", 0
+ ),
+ "rollback_boundary_count": summary.get("rollback_boundary_count", 0),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_signed_authorization_receipt_evidence_intake": (
+ future_signed_authorization_receipt_evidence_intake
+ ),
+ "detached_verification_evidence_schema": detached_verification_evidence_schema,
+ "signed_receipt_evidence_intake_contract": signed_receipt_evidence_intake_contract,
+ "signed_receipt_evidence_intake_checks": checks,
+ "source_signed_receipt_closeout_summary": summary,
+ "source_signed_receipt_closeout_contract": closeout_contract,
+ "source_detached_receipt_verification_boundary": detached_boundary,
+ "safety": {
+ "read_only_db_apply_authorization_signed_receipt_evidence_intake": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this intake schema for a future detached verification evidence validation lane.",
+ "Keep signed receipt body, signature material, secret values, shell execution, SQL, and DB writes out of the AI payload.",
+ "Only a later verifier lane may validate detached signature evidence; this intake still does not authorize DB apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_detached_verification_evidence_validation(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Validate the detached verification evidence schema without verifying signatures."""
+ intake = build_pchome_auto_policy_db_apply_authorization_signed_receipt_evidence_intake(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ future_intake = intake.get("future_signed_authorization_receipt_evidence_intake") or {}
+ schema = intake.get("detached_verification_evidence_schema") or {}
+ intake_contract = intake.get("signed_receipt_evidence_intake_contract") or {}
+ summary = intake.get("summary") or {}
+ safety = intake.get("safety") or {}
+ validation_id = _db_apply_authorization_detached_verification_evidence_validation_id(
+ intake
+ )
+ required_external_evidence = list(schema.get("required_external_receipt_evidence") or [])
+ external_acceptance_gates = list(schema.get("external_receipt_acceptance_gates") or [])
+ detached_checks = list(schema.get("detached_receipt_verification_checks") or [])
+ evidence_fields = list(schema.get("detached_verification_evidence_fields") or [])
+ evidence_acceptance_gates = list(
+ schema.get("detached_verification_acceptance_gates") or []
+ )
+ operator_secret_boundary = (
+ schema.get("operator_held_secret_boundary_contract") or {}
+ )
+ verifier_receipt_fields = [
+ "verifier_receipt_id",
+ "source_signed_receipt_evidence_intake_id",
+ "source_detached_verification_evidence_schema_id",
+ "source_signed_receipt_closeout_id",
+ "source_external_receipt_evidence_boundary_id",
+ "external_receipt_id_reference",
+ "payload_sha256",
+ "receipt_sha256",
+ "detached_signature_verification_status",
+ "verifier_receipt_sha256",
+ "verified_at_utc",
+ "post_apply_verifier_endpoint",
+ ]
+ verifier_receipt_acceptance_gates = [
+ "signed_receipt_evidence_intake_ready",
+ "source_detached_verification_evidence_schema_id_matches",
+ "source_external_receipt_evidence_boundary_id_matches",
+ "payload_hash_matches_final_signable_request_package",
+ "receipt_hash_is_present_and_nonempty",
+ "signer_key_id_is_reference_only",
+ "signature_algorithm_is_reference_only",
+ "detached_signature_verification_status_passed",
+ "no_secret_signature_or_signed_receipt_body_in_ai_payload",
+ "post_apply_verifier_still_required",
+ ]
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("performs_detached_signature_verification") is False
+ and schema.get("detached_signature_verification_performed") is False
+ and schema.get("external_signed_authorization_receipt_included") is False
+ and schema.get("signed_authorization_receipt_included") is False
+ and schema.get("signature_material_included") is False
+ and schema.get("secret_material_included") is False
+ and schema.get("secret_material_required_in_preview") is False
+ and schema.get("accepts_plaintext_secret") is False
+ and schema.get("reads_secret_in_preview") is False
+ and schema.get("executes_shell_in_preview") is False
+ and schema.get("executes_sql_in_preview") is False
+ and schema.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_detached_verification_evidence_validation_check(
+ "signed_receipt_evidence_intake_ready",
+ intake.get("result")
+ == "DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_EVIDENCE_INTAKE_READY"
+ and future_intake.get(
+ "ready_for_future_signed_authorization_receipt_evidence_intake"
+ )
+ is True
+ and schema.get("ready_for_future_detached_verification_evidence_schema")
+ is True,
+ {
+ "result": intake.get("result"),
+ "ready_for_future_signed_authorization_receipt_evidence_intake": (
+ future_intake.get(
+ "ready_for_future_signed_authorization_receipt_evidence_intake"
+ )
+ ),
+ "ready_for_future_detached_verification_evidence_schema": schema.get(
+ "ready_for_future_detached_verification_evidence_schema"
+ ),
+ },
+ "wait_for_signed_receipt_evidence_intake",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "detached_verification_evidence_schema_complete",
+ schema.get("authorization_material_type")
+ == "detached_verification_evidence_schema"
+ and bool(schema.get("schema_id"))
+ and bool(schema.get("source_signed_receipt_closeout_id"))
+ and bool(schema.get("source_detached_receipt_verification_boundary_id"))
+ and schema.get("ready_for_database_apply_now") is False
+ and schema.get("issues_database_apply_authorization") is False
+ and schema.get("signs_database_apply_authorization") is False,
+ {
+ "schema_id": schema.get("schema_id"),
+ "authorization_material_type": schema.get("authorization_material_type"),
+ "source_detached_receipt_verification_boundary_id": schema.get(
+ "source_detached_receipt_verification_boundary_id"
+ ),
+ },
+ "wait_for_detached_verification_evidence_schema",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "source_chain_ids_present",
+ bool(future_intake.get("intake_id"))
+ and bool(future_intake.get("source_signed_receipt_closeout_id"))
+ and bool(future_intake.get("source_detached_receipt_verification_boundary_id"))
+ and bool(future_intake.get("source_external_receipt_evidence_boundary_id"))
+ and bool(schema.get("source_final_signable_request_package_id")),
+ {
+ "intake_id": future_intake.get("intake_id"),
+ "source_signed_receipt_closeout_id": future_intake.get(
+ "source_signed_receipt_closeout_id"
+ ),
+ "source_final_signable_request_package_id": schema.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "external_receipt_contract_carried_forward",
+ len(required_external_evidence) == 10
+ and len(external_acceptance_gates) == 8
+ and int(summary.get("required_external_receipt_evidence_count") or 0) == 10
+ and int(summary.get("external_receipt_acceptance_gate_count") or 0) == 8,
+ {
+ "required_external_receipt_evidence_count": len(required_external_evidence),
+ "external_receipt_acceptance_gate_count": len(external_acceptance_gates),
+ "summary_required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "summary_external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ },
+ "wait_for_external_receipt_contract",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "detached_verification_evidence_requirements_carried_forward",
+ len(detached_checks) == 10
+ and len(evidence_fields) == 12
+ and len(evidence_acceptance_gates) == 10
+ and int(summary.get("detached_verification_evidence_field_count") or 0) == 12
+ and int(summary.get("detached_verification_acceptance_gate_count") or 0) == 10,
+ {
+ "detached_receipt_verification_check_count": len(detached_checks),
+ "detached_verification_evidence_field_count": len(evidence_fields),
+ "detached_verification_acceptance_gate_count": len(
+ evidence_acceptance_gates
+ ),
+ },
+ "wait_for_detached_verification_evidence_requirements",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "verifier_receipt_closeout_boundary_contract_complete",
+ len(verifier_receipt_fields) == 12
+ and len(verifier_receipt_acceptance_gates) == 10
+ and "detached_signature_verification_status_passed"
+ in verifier_receipt_acceptance_gates
+ and "verifier_receipt_sha256" in verifier_receipt_fields,
+ {
+ "verifier_receipt_field_count": len(verifier_receipt_fields),
+ "verifier_receipt_acceptance_gate_count": len(
+ verifier_receipt_acceptance_gates
+ ),
+ "requires_status_passed": (
+ "detached_signature_verification_status_passed"
+ in verifier_receipt_acceptance_gates
+ ),
+ },
+ "wait_for_verifier_receipt_closeout_boundary_contract",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "signer_and_algorithm_references_only",
+ schema.get("signer_key_id_reference_only") is True
+ and schema.get("signature_algorithm_reference_only") is True
+ and "signer_key_id_reference" in evidence_fields
+ and "signature_algorithm_reference" in evidence_fields,
+ {
+ "signer_key_id_reference_only": schema.get(
+ "signer_key_id_reference_only"
+ ),
+ "signature_algorithm_reference_only": schema.get(
+ "signature_algorithm_reference_only"
+ ),
+ },
+ "abort_on_plaintext_key_or_algorithm_material",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "secret_and_signed_material_boundary_enforced",
+ schema.get("detached_signature_verification_performed") is False
+ and schema.get("external_signed_authorization_receipt_included") is False
+ and schema.get("signed_authorization_receipt_included") is False
+ and schema.get("signature_material_included") is False
+ and schema.get("secret_material_included") is False
+ and schema.get("accepts_plaintext_secret") is False
+ and operator_secret_boundary.get("secret_reference_mode")
+ == "external_runtime_reference_only"
+ and operator_secret_boundary.get("accepts_plaintext_secret") is False,
+ {
+ "detached_signature_verification_performed": schema.get(
+ "detached_signature_verification_performed"
+ ),
+ "external_signed_authorization_receipt_included": schema.get(
+ "external_signed_authorization_receipt_included"
+ ),
+ "signature_material_included": schema.get("signature_material_included"),
+ "accepts_plaintext_secret": schema.get("accepts_plaintext_secret"),
+ "secret_reference_mode": operator_secret_boundary.get(
+ "secret_reference_mode"
+ ),
+ },
+ "abort_on_signed_material_or_secret_boundary_violation",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "same_run_production_truth_required",
+ schema.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": schema.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "post_apply_verifier_and_hash_lock_required",
+ schema.get("requires_post_apply_verifier") is True
+ and bool(schema.get("post_apply_verifier_endpoint"))
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1
+ and schema.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and schema.get("hash_matches") is True
+ and bool(schema.get("expected_sha256"))
+ and schema.get("expected_sha256") == schema.get("actual_sha256"),
+ {
+ "requires_post_apply_verifier": schema.get("requires_post_apply_verifier"),
+ "post_apply_verifier_endpoint": schema.get("post_apply_verifier_endpoint"),
+ "target_file": schema.get("target_file"),
+ "hash_matches": schema.get("hash_matches"),
+ },
+ "require_post_apply_verifier_and_hash_lock",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "preview_has_no_side_effects_no_verification_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "performs_detached_signature_verification": safety.get(
+ "performs_detached_signature_verification"
+ ),
+ },
+ "abort_on_preview_side_effect_verification_or_signing",
+ ),
+ _authorization_detached_verification_evidence_validation_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and future_intake.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": future_intake.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ validation_ready = not waiting_checks
+ validation_status = (
+ "DB_APPLY_AUTHORIZATION_DETACHED_VERIFICATION_EVIDENCE_VALIDATION_READY"
+ if validation_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_EVIDENCE_INTAKE"
+ )
+ future_detached_verification_evidence_validation = {
+ "validation_id": validation_id,
+ "source_signed_receipt_evidence_intake_id": future_intake.get("intake_id"),
+ "source_detached_verification_evidence_schema_id": schema.get("schema_id"),
+ "source_signed_receipt_closeout_id": schema.get(
+ "source_signed_receipt_closeout_id"
+ ),
+ "source_detached_receipt_verification_boundary_id": schema.get(
+ "source_detached_receipt_verification_boundary_id"
+ ),
+ "source_external_receipt_evidence_boundary_id": schema.get(
+ "source_external_receipt_evidence_boundary_id"
+ ),
+ "source_final_signable_request_package_id": schema.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": validation_status,
+ "ready_for_future_detached_verification_evidence_validation": validation_ready,
+ "can_enter_future_verifier_receipt_closeout": validation_ready,
+ "verifier_receipt_closeout_boundary_ready": validation_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "detached_signature_verification_performed": False,
+ "verifier_receipt_persisted": False,
+ "external_signed_authorization_receipt_included": False,
+ "signed_authorization_receipt_included": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "secret_material_required_in_preview": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ verifier_receipt_closeout_boundary = {
+ "boundary_id": validation_id,
+ "authorization_material_type": "verifier_receipt_closeout_boundary",
+ "source_signed_receipt_evidence_intake_id": future_intake.get("intake_id"),
+ "source_detached_verification_evidence_schema_id": schema.get("schema_id"),
+ "source_signed_receipt_closeout_id": schema.get(
+ "source_signed_receipt_closeout_id"
+ ),
+ "source_detached_receipt_verification_boundary_id": schema.get(
+ "source_detached_receipt_verification_boundary_id"
+ ),
+ "source_external_receipt_evidence_boundary_id": schema.get(
+ "source_external_receipt_evidence_boundary_id"
+ ),
+ "source_final_signable_request_package_id": schema.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": validation_status,
+ "ready_for_future_verifier_receipt_closeout_boundary": validation_ready,
+ "required_external_receipt_evidence": required_external_evidence,
+ "required_external_receipt_evidence_count": len(required_external_evidence),
+ "external_receipt_acceptance_gates": external_acceptance_gates,
+ "external_receipt_acceptance_gate_count": len(external_acceptance_gates),
+ "detached_receipt_verification_checks": detached_checks,
+ "detached_receipt_verification_check_count": len(detached_checks),
+ "detached_verification_evidence_fields": evidence_fields,
+ "detached_verification_evidence_field_count": len(evidence_fields),
+ "detached_verification_acceptance_gates": evidence_acceptance_gates,
+ "detached_verification_acceptance_gate_count": len(evidence_acceptance_gates),
+ "verifier_receipt_fields": verifier_receipt_fields,
+ "verifier_receipt_field_count": len(verifier_receipt_fields),
+ "verifier_receipt_acceptance_gates": verifier_receipt_acceptance_gates,
+ "verifier_receipt_acceptance_gate_count": len(verifier_receipt_acceptance_gates),
+ "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_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "operator_held_secret_boundary_contract": operator_secret_boundary,
+ "target_file": schema.get("target_file"),
+ "expected_sha256": schema.get("expected_sha256"),
+ "actual_sha256": schema.get("actual_sha256"),
+ "hash_matches": schema.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": schema.get("post_apply_verifier_endpoint"),
+ }
+ detached_verification_evidence_validation_contract = {
+ "mode": "detached_verification_evidence_validation_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-detached-verification-evidence-validation"
+ ),
+ "source_signed_receipt_evidence_intake_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-evidence-intake"
+ ),
+ "machine_verifiable": True,
+ "permits_future_verifier_receipt_closeout": validation_ready,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_DETACHED_VERIFICATION_EVIDENCE_VALIDATION_POLICY,
+ "result": validation_status,
+ "success": bool(intake.get("success")),
+ "generated_at": intake.get("generated_at"),
+ "source_policy": intake.get("policy"),
+ "stats": intake.get("stats") or {},
+ "summary": {
+ "authorization_detached_verification_evidence_validation_ready_count": (
+ 1 if validation_ready else 0
+ ),
+ "detached_verification_evidence_validation_check_count": len(checks),
+ "detached_verification_evidence_validation_pass_count": passed_count,
+ "detached_verification_evidence_validation_waiting_count": len(waiting_checks),
+ "authorization_signed_receipt_evidence_intake_ready_count": summary.get(
+ "authorization_signed_receipt_evidence_intake_ready_count", 0
+ ),
+ "signed_receipt_evidence_intake_check_count": summary.get(
+ "signed_receipt_evidence_intake_check_count", 0
+ ),
+ "authorization_signed_receipt_closeout_ready_count": summary.get(
+ "authorization_signed_receipt_closeout_ready_count", 0
+ ),
+ "signed_receipt_closeout_check_count": summary.get(
+ "signed_receipt_closeout_check_count", 0
+ ),
+ "detached_receipt_verification_boundary_count": summary.get(
+ "detached_receipt_verification_boundary_count", 0
+ ),
+ "detached_verification_evidence_schema_count": summary.get(
+ "detached_verification_evidence_schema_count", 0
+ ),
+ "verifier_receipt_closeout_boundary_count": 1,
+ "required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ "detached_receipt_verification_check_count": summary.get(
+ "detached_receipt_verification_check_count", 0
+ ),
+ "detached_verification_evidence_field_count": summary.get(
+ "detached_verification_evidence_field_count", 0
+ ),
+ "detached_verification_acceptance_gate_count": summary.get(
+ "detached_verification_acceptance_gate_count", 0
+ ),
+ "verifier_receipt_field_count": len(verifier_receipt_fields),
+ "verifier_receipt_acceptance_gate_count": len(
+ verifier_receipt_acceptance_gates
+ ),
+ "operator_held_secret_boundary_count": summary.get(
+ "operator_held_secret_boundary_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_detached_verification_evidence_validation": (
+ future_detached_verification_evidence_validation
+ ),
+ "verifier_receipt_closeout_boundary": verifier_receipt_closeout_boundary,
+ "detached_verification_evidence_validation_contract": (
+ detached_verification_evidence_validation_contract
+ ),
+ "detached_verification_evidence_validation_checks": checks,
+ "source_signed_receipt_evidence_intake_summary": summary,
+ "source_signed_receipt_evidence_intake_contract": intake_contract,
+ "source_detached_verification_evidence_schema": schema,
+ "safety": {
+ "read_only_db_apply_authorization_detached_verification_evidence_validation": (
+ True
+ ),
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this validation boundary for a future verifier receipt closeout lane.",
+ "Keep signed receipt body, signature material, secret values, shell execution, SQL, and DB writes out of the AI payload.",
+ "Only a separate verifier receipt closeout may carry validation receipts; this validation lane still does not authorize DB apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_verifier_receipt_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out verifier receipt evidence handoff without persisting receipts."""
+ validation = (
+ build_pchome_auto_policy_db_apply_authorization_detached_verification_evidence_validation(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_validation = validation.get("future_detached_verification_evidence_validation") or {}
+ boundary = validation.get("verifier_receipt_closeout_boundary") or {}
+ validation_contract = (
+ validation.get("detached_verification_evidence_validation_contract") or {}
+ )
+ summary = validation.get("summary") or {}
+ safety = validation.get("safety") or {}
+ closeout_id = _db_apply_authorization_verifier_receipt_closeout_id(validation)
+ required_external_evidence = list(boundary.get("required_external_receipt_evidence") or [])
+ external_acceptance_gates = list(boundary.get("external_receipt_acceptance_gates") or [])
+ verifier_receipt_fields = list(boundary.get("verifier_receipt_fields") or [])
+ verifier_receipt_acceptance_gates = list(
+ boundary.get("verifier_receipt_acceptance_gates") or []
+ )
+ evidence_fields = list(boundary.get("detached_verification_evidence_fields") or [])
+ evidence_acceptance_gates = list(
+ boundary.get("detached_verification_acceptance_gates") or []
+ )
+ operator_secret_boundary = (
+ boundary.get("operator_held_secret_boundary_contract") or {}
+ )
+ verifier_receipt_evidence_handoff_fields = [
+ "handoff_id",
+ "source_verifier_receipt_closeout_boundary_id",
+ "source_detached_verification_evidence_validation_id",
+ "source_signed_receipt_evidence_intake_id",
+ "source_final_signable_request_package_id",
+ "verifier_receipt_id_reference",
+ "external_receipt_id_reference",
+ "payload_sha256",
+ "receipt_sha256",
+ "verifier_receipt_sha256",
+ "detached_signature_verification_status",
+ "post_apply_verifier_endpoint",
+ ]
+ verifier_receipt_handoff_acceptance_gates = [
+ "detached_verification_evidence_validation_ready",
+ "verifier_receipt_closeout_boundary_ready",
+ "source_chain_ids_match",
+ "payload_hash_matches_final_signable_request_package",
+ "receipt_hash_is_present_and_nonempty",
+ "verifier_receipt_hash_is_present_and_nonempty",
+ "detached_signature_verification_status_passed",
+ "no_secret_signature_or_signed_receipt_body_in_ai_payload",
+ "verifier_receipt_not_persisted_by_preview",
+ "post_apply_verifier_still_required",
+ ]
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("performs_detached_signature_verification") is False
+ and safety.get("persists_verifier_receipt") is False
+ and boundary.get("detached_signature_verification_performed") is False
+ and boundary.get("verifier_receipt_persisted") is False
+ and boundary.get("external_signed_authorization_receipt_included") is False
+ and boundary.get("signed_authorization_receipt_included") is False
+ and boundary.get("signature_material_included") is False
+ and boundary.get("secret_material_included") is False
+ and boundary.get("secret_material_required_in_preview") is False
+ and boundary.get("accepts_plaintext_secret") is False
+ and boundary.get("reads_secret_in_preview") is False
+ and boundary.get("executes_shell_in_preview") is False
+ and boundary.get("executes_sql_in_preview") is False
+ and boundary.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_verifier_receipt_closeout_check(
+ "detached_verification_evidence_validation_ready",
+ validation.get("result")
+ == "DB_APPLY_AUTHORIZATION_DETACHED_VERIFICATION_EVIDENCE_VALIDATION_READY"
+ and future_validation.get(
+ "ready_for_future_detached_verification_evidence_validation"
+ )
+ is True
+ and boundary.get("ready_for_future_verifier_receipt_closeout_boundary")
+ is True,
+ {
+ "result": validation.get("result"),
+ "ready_for_future_detached_verification_evidence_validation": (
+ future_validation.get(
+ "ready_for_future_detached_verification_evidence_validation"
+ )
+ ),
+ "ready_for_future_verifier_receipt_closeout_boundary": boundary.get(
+ "ready_for_future_verifier_receipt_closeout_boundary"
+ ),
+ },
+ "wait_for_detached_verification_evidence_validation",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "verifier_receipt_closeout_boundary_complete",
+ boundary.get("authorization_material_type")
+ == "verifier_receipt_closeout_boundary"
+ and bool(boundary.get("boundary_id"))
+ and bool(boundary.get("source_detached_verification_evidence_schema_id"))
+ and bool(boundary.get("source_signed_receipt_evidence_intake_id"))
+ and boundary.get("ready_for_database_apply_now") is False
+ and boundary.get("issues_database_apply_authorization") is False
+ and boundary.get("signs_database_apply_authorization") is False,
+ {
+ "boundary_id": boundary.get("boundary_id"),
+ "authorization_material_type": boundary.get("authorization_material_type"),
+ "source_detached_verification_evidence_schema_id": boundary.get(
+ "source_detached_verification_evidence_schema_id"
+ ),
+ },
+ "wait_for_verifier_receipt_closeout_boundary",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "source_chain_ids_present",
+ bool(future_validation.get("validation_id"))
+ and bool(future_validation.get("source_signed_receipt_evidence_intake_id"))
+ and bool(future_validation.get("source_detached_verification_evidence_schema_id"))
+ and bool(future_validation.get("source_signed_receipt_closeout_id"))
+ and bool(boundary.get("source_final_signable_request_package_id")),
+ {
+ "validation_id": future_validation.get("validation_id"),
+ "source_signed_receipt_evidence_intake_id": future_validation.get(
+ "source_signed_receipt_evidence_intake_id"
+ ),
+ "source_final_signable_request_package_id": boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "verifier_receipt_contract_carried_forward",
+ len(verifier_receipt_fields) == 12
+ and len(verifier_receipt_acceptance_gates) == 10
+ and int(summary.get("verifier_receipt_field_count") or 0) == 12
+ and int(summary.get("verifier_receipt_acceptance_gate_count") or 0) == 10,
+ {
+ "verifier_receipt_field_count": len(verifier_receipt_fields),
+ "verifier_receipt_acceptance_gate_count": len(
+ verifier_receipt_acceptance_gates
+ ),
+ "summary_verifier_receipt_field_count": summary.get(
+ "verifier_receipt_field_count", 0
+ ),
+ "summary_verifier_receipt_acceptance_gate_count": summary.get(
+ "verifier_receipt_acceptance_gate_count", 0
+ ),
+ },
+ "wait_for_verifier_receipt_contract",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "detached_evidence_and_external_receipt_contracts_carried_forward",
+ len(required_external_evidence) == 10
+ and len(external_acceptance_gates) == 8
+ and len(evidence_fields) == 12
+ and len(evidence_acceptance_gates) == 10,
+ {
+ "required_external_receipt_evidence_count": len(required_external_evidence),
+ "external_receipt_acceptance_gate_count": len(external_acceptance_gates),
+ "detached_verification_evidence_field_count": len(evidence_fields),
+ "detached_verification_acceptance_gate_count": len(
+ evidence_acceptance_gates
+ ),
+ },
+ "wait_for_detached_evidence_or_external_receipt_contract",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "verifier_receipt_evidence_handoff_contract_complete",
+ len(verifier_receipt_evidence_handoff_fields) == 12
+ and len(verifier_receipt_handoff_acceptance_gates) == 10
+ and "verifier_receipt_sha256" in verifier_receipt_evidence_handoff_fields
+ and "verifier_receipt_not_persisted_by_preview"
+ in verifier_receipt_handoff_acceptance_gates,
+ {
+ "verifier_receipt_evidence_handoff_field_count": len(
+ verifier_receipt_evidence_handoff_fields
+ ),
+ "verifier_receipt_handoff_acceptance_gate_count": len(
+ verifier_receipt_handoff_acceptance_gates
+ ),
+ },
+ "wait_for_verifier_receipt_evidence_handoff_contract",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "secret_signed_material_and_receipt_persistence_boundary_enforced",
+ boundary.get("detached_signature_verification_performed") is False
+ and boundary.get("verifier_receipt_persisted") is False
+ and boundary.get("external_signed_authorization_receipt_included") is False
+ and boundary.get("signed_authorization_receipt_included") is False
+ and boundary.get("signature_material_included") is False
+ and boundary.get("secret_material_included") is False
+ and boundary.get("accepts_plaintext_secret") is False
+ and operator_secret_boundary.get("secret_reference_mode")
+ == "external_runtime_reference_only"
+ and operator_secret_boundary.get("accepts_plaintext_secret") is False,
+ {
+ "detached_signature_verification_performed": boundary.get(
+ "detached_signature_verification_performed"
+ ),
+ "verifier_receipt_persisted": boundary.get(
+ "verifier_receipt_persisted"
+ ),
+ "external_signed_authorization_receipt_included": boundary.get(
+ "external_signed_authorization_receipt_included"
+ ),
+ "signature_material_included": boundary.get("signature_material_included"),
+ "accepts_plaintext_secret": boundary.get("accepts_plaintext_secret"),
+ },
+ "abort_on_signed_material_secret_or_receipt_persistence",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "same_run_production_truth_required",
+ boundary.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": boundary.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "post_apply_verifier_and_hash_lock_required",
+ boundary.get("requires_post_apply_verifier") is True
+ and bool(boundary.get("post_apply_verifier_endpoint"))
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1
+ and boundary.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and boundary.get("hash_matches") is True
+ and bool(boundary.get("expected_sha256"))
+ and boundary.get("expected_sha256") == boundary.get("actual_sha256"),
+ {
+ "requires_post_apply_verifier": boundary.get("requires_post_apply_verifier"),
+ "post_apply_verifier_endpoint": boundary.get(
+ "post_apply_verifier_endpoint"
+ ),
+ "target_file": boundary.get("target_file"),
+ "hash_matches": boundary.get("hash_matches"),
+ },
+ "require_post_apply_verifier_and_hash_lock",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "closeout_contract_blocks_database_apply",
+ validation_contract.get("permits_future_verifier_receipt_closeout") is True
+ and validation_contract.get("issues_database_apply_authorization") is False
+ and validation_contract.get("ready_for_database_apply_now") is False
+ and validation_contract.get("signs_database_apply_authorization") is False
+ and validation_contract.get("persists_verifier_receipt") is False
+ and validation_contract.get("performs_detached_signature_verification") is False,
+ {
+ "permits_future_verifier_receipt_closeout": validation_contract.get(
+ "permits_future_verifier_receipt_closeout"
+ ),
+ "ready_for_database_apply_now": validation_contract.get(
+ "ready_for_database_apply_now"
+ ),
+ "persists_verifier_receipt": validation_contract.get(
+ "persists_verifier_receipt"
+ ),
+ },
+ "abort_if_validation_contract_authorizes_database_apply",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "preview_has_no_side_effects_no_verification_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "performs_detached_signature_verification": safety.get(
+ "performs_detached_signature_verification"
+ ),
+ "persists_verifier_receipt": safety.get("persists_verifier_receipt"),
+ },
+ "abort_on_preview_side_effect_verification_signing_or_persistence",
+ ),
+ _authorization_verifier_receipt_closeout_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and future_validation.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": future_validation.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_AUTHORIZATION_VERIFIER_RECEIPT_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_DETACHED_VERIFICATION_EVIDENCE_VALIDATION"
+ )
+ future_verifier_receipt_closeout = {
+ "closeout_id": closeout_id,
+ "source_detached_verification_evidence_validation_id": future_validation.get(
+ "validation_id"
+ ),
+ "source_verifier_receipt_closeout_boundary_id": boundary.get("boundary_id"),
+ "source_signed_receipt_evidence_intake_id": boundary.get(
+ "source_signed_receipt_evidence_intake_id"
+ ),
+ "source_detached_verification_evidence_schema_id": boundary.get(
+ "source_detached_verification_evidence_schema_id"
+ ),
+ "source_final_signable_request_package_id": boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_verifier_receipt_closeout": closeout_ready,
+ "can_enter_future_database_apply_authorization_verifier_handoff": closeout_ready,
+ "verifier_receipt_evidence_handoff_ready": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "detached_signature_verification_performed": False,
+ "verifier_receipt_persisted": False,
+ "external_signed_authorization_receipt_included": False,
+ "signed_authorization_receipt_included": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "secret_material_required_in_preview": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ verifier_receipt_evidence_handoff = {
+ "handoff_id": closeout_id,
+ "authorization_material_type": "verifier_receipt_evidence_handoff",
+ "source_detached_verification_evidence_validation_id": future_validation.get(
+ "validation_id"
+ ),
+ "source_verifier_receipt_closeout_boundary_id": boundary.get("boundary_id"),
+ "source_signed_receipt_evidence_intake_id": boundary.get(
+ "source_signed_receipt_evidence_intake_id"
+ ),
+ "source_detached_verification_evidence_schema_id": boundary.get(
+ "source_detached_verification_evidence_schema_id"
+ ),
+ "source_signed_receipt_closeout_id": boundary.get(
+ "source_signed_receipt_closeout_id"
+ ),
+ "source_external_receipt_evidence_boundary_id": boundary.get(
+ "source_external_receipt_evidence_boundary_id"
+ ),
+ "source_final_signable_request_package_id": boundary.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_verifier_receipt_evidence_handoff": closeout_ready,
+ "required_external_receipt_evidence": required_external_evidence,
+ "required_external_receipt_evidence_count": len(required_external_evidence),
+ "external_receipt_acceptance_gates": external_acceptance_gates,
+ "external_receipt_acceptance_gate_count": len(external_acceptance_gates),
+ "verifier_receipt_fields": verifier_receipt_fields,
+ "verifier_receipt_field_count": len(verifier_receipt_fields),
+ "verifier_receipt_acceptance_gates": verifier_receipt_acceptance_gates,
+ "verifier_receipt_acceptance_gate_count": len(verifier_receipt_acceptance_gates),
+ "verifier_receipt_evidence_handoff_fields": (
+ verifier_receipt_evidence_handoff_fields
+ ),
+ "verifier_receipt_evidence_handoff_field_count": len(
+ verifier_receipt_evidence_handoff_fields
+ ),
+ "verifier_receipt_handoff_acceptance_gates": (
+ verifier_receipt_handoff_acceptance_gates
+ ),
+ "verifier_receipt_handoff_acceptance_gate_count": len(
+ verifier_receipt_handoff_acceptance_gates
+ ),
+ "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_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "operator_held_secret_boundary_contract": operator_secret_boundary,
+ "target_file": boundary.get("target_file"),
+ "expected_sha256": boundary.get("expected_sha256"),
+ "actual_sha256": boundary.get("actual_sha256"),
+ "hash_matches": boundary.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": boundary.get("post_apply_verifier_endpoint"),
+ }
+ verifier_receipt_closeout_contract = {
+ "mode": "verifier_receipt_closeout_handoff_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-verifier-receipt-closeout"
+ ),
+ "source_detached_verification_evidence_validation_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-detached-verification-evidence-validation"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_authorization_verifier_handoff": closeout_ready,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_VERIFIER_RECEIPT_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(validation.get("success")),
+ "generated_at": validation.get("generated_at"),
+ "source_policy": validation.get("policy"),
+ "stats": validation.get("stats") or {},
+ "summary": {
+ "authorization_verifier_receipt_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "verifier_receipt_closeout_check_count": len(checks),
+ "verifier_receipt_closeout_pass_count": passed_count,
+ "verifier_receipt_closeout_waiting_count": len(waiting_checks),
+ "authorization_detached_verification_evidence_validation_ready_count": (
+ summary.get(
+ "authorization_detached_verification_evidence_validation_ready_count",
+ 0,
+ )
+ ),
+ "detached_verification_evidence_validation_check_count": summary.get(
+ "detached_verification_evidence_validation_check_count", 0
+ ),
+ "authorization_signed_receipt_evidence_intake_ready_count": summary.get(
+ "authorization_signed_receipt_evidence_intake_ready_count", 0
+ ),
+ "signed_receipt_evidence_intake_check_count": summary.get(
+ "signed_receipt_evidence_intake_check_count", 0
+ ),
+ "verifier_receipt_closeout_boundary_count": summary.get(
+ "verifier_receipt_closeout_boundary_count", 0
+ ),
+ "verifier_receipt_evidence_handoff_count": 1,
+ "required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ "verifier_receipt_field_count": summary.get(
+ "verifier_receipt_field_count", 0
+ ),
+ "verifier_receipt_acceptance_gate_count": summary.get(
+ "verifier_receipt_acceptance_gate_count", 0
+ ),
+ "verifier_receipt_evidence_handoff_field_count": len(
+ verifier_receipt_evidence_handoff_fields
+ ),
+ "verifier_receipt_handoff_acceptance_gate_count": len(
+ verifier_receipt_handoff_acceptance_gates
+ ),
+ "detached_verification_evidence_field_count": summary.get(
+ "detached_verification_evidence_field_count", 0
+ ),
+ "detached_verification_acceptance_gate_count": summary.get(
+ "detached_verification_acceptance_gate_count", 0
+ ),
+ "operator_held_secret_boundary_count": summary.get(
+ "operator_held_secret_boundary_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_verifier_receipt_closeout": future_verifier_receipt_closeout,
+ "verifier_receipt_evidence_handoff": verifier_receipt_evidence_handoff,
+ "verifier_receipt_closeout_contract": verifier_receipt_closeout_contract,
+ "verifier_receipt_closeout_checks": checks,
+ "source_detached_verification_evidence_validation_summary": summary,
+ "source_detached_verification_evidence_validation_contract": validation_contract,
+ "source_verifier_receipt_closeout_boundary": boundary,
+ "safety": {
+ "read_only_db_apply_authorization_verifier_receipt_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this verifier receipt closeout as the evidence handoff for a future database apply authorization verifier lane.",
+ "Keep signed receipt body, signature material, secret values, shell execution, SQL, and DB writes out of this handoff.",
+ "A later verifier handoff may accept external verifier receipts; this closeout still does not authorize DB apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_evidence_execution_preflight(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Preflight future authorization evidence execution without executing it."""
+ closeout = build_pchome_auto_policy_db_apply_authorization_verifier_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ future_closeout = closeout.get("future_verifier_receipt_closeout") or {}
+ handoff = closeout.get("verifier_receipt_evidence_handoff") or {}
+ closeout_contract = closeout.get("verifier_receipt_closeout_contract") or {}
+ summary = closeout.get("summary") or {}
+ safety = closeout.get("safety") or {}
+ preflight_id = _db_apply_authorization_evidence_execution_preflight_id(closeout)
+ verifier_receipt_fields = list(handoff.get("verifier_receipt_fields") or [])
+ verifier_receipt_acceptance_gates = list(
+ handoff.get("verifier_receipt_acceptance_gates") or []
+ )
+ handoff_fields = list(
+ handoff.get("verifier_receipt_evidence_handoff_fields") or []
+ )
+ handoff_acceptance_gates = list(
+ handoff.get("verifier_receipt_handoff_acceptance_gates") or []
+ )
+ required_external_evidence = list(handoff.get("required_external_receipt_evidence") or [])
+ external_acceptance_gates = list(handoff.get("external_receipt_acceptance_gates") or [])
+ operator_secret_boundary = (
+ handoff.get("operator_held_secret_boundary_contract") or {}
+ )
+ authorization_evidence_execution_fields = [
+ "execution_preflight_id",
+ "source_verifier_receipt_closeout_id",
+ "source_verifier_receipt_evidence_handoff_id",
+ "source_detached_verification_evidence_validation_id",
+ "source_final_signable_request_package_id",
+ "verifier_receipt_id_reference",
+ "external_receipt_id_reference",
+ "payload_sha256",
+ "receipt_sha256",
+ "verifier_receipt_sha256",
+ "detached_signature_verification_status",
+ "post_apply_verifier_endpoint",
+ ]
+ authorization_evidence_execution_acceptance_gates = [
+ "verifier_receipt_closeout_ready",
+ "verifier_receipt_evidence_handoff_ready",
+ "source_chain_ids_match",
+ "production_truth_matches_preflight_run",
+ "payload_hash_matches_final_signable_request_package",
+ "receipt_hash_is_present_and_nonempty",
+ "verifier_receipt_hash_is_present_and_nonempty",
+ "detached_signature_verification_status_passed",
+ "post_apply_verifier_still_required",
+ "no_secret_signature_or_database_write_in_preflight",
+ ]
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("performs_detached_signature_verification") is False
+ and safety.get("persists_verifier_receipt") is False
+ and handoff.get("detached_signature_verification_performed") is False
+ and handoff.get("verifier_receipt_persisted") is False
+ and handoff.get("external_signed_authorization_receipt_included") is False
+ and handoff.get("signed_authorization_receipt_included") is False
+ and handoff.get("signature_material_included") is False
+ and handoff.get("secret_material_included") is False
+ and handoff.get("secret_material_required_in_preview") is False
+ and handoff.get("accepts_plaintext_secret") is False
+ and handoff.get("reads_secret_in_preview") is False
+ and handoff.get("executes_shell_in_preview") is False
+ and handoff.get("executes_sql_in_preview") is False
+ and handoff.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_evidence_execution_preflight_check(
+ "verifier_receipt_closeout_ready",
+ closeout.get("result") == "DB_APPLY_AUTHORIZATION_VERIFIER_RECEIPT_CLOSEOUT_READY"
+ and future_closeout.get("ready_for_future_verifier_receipt_closeout") is True
+ and handoff.get("ready_for_future_verifier_receipt_evidence_handoff") is True,
+ {
+ "result": closeout.get("result"),
+ "ready_for_future_verifier_receipt_closeout": future_closeout.get(
+ "ready_for_future_verifier_receipt_closeout"
+ ),
+ "ready_for_future_verifier_receipt_evidence_handoff": handoff.get(
+ "ready_for_future_verifier_receipt_evidence_handoff"
+ ),
+ },
+ "wait_for_verifier_receipt_closeout",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "verifier_receipt_evidence_handoff_complete",
+ handoff.get("authorization_material_type") == "verifier_receipt_evidence_handoff"
+ and bool(handoff.get("handoff_id"))
+ and bool(handoff.get("source_detached_verification_evidence_validation_id"))
+ and bool(handoff.get("source_final_signable_request_package_id"))
+ and handoff.get("ready_for_database_apply_now") is False
+ and handoff.get("issues_database_apply_authorization") is False
+ and handoff.get("signs_database_apply_authorization") is False,
+ {
+ "handoff_id": handoff.get("handoff_id"),
+ "authorization_material_type": handoff.get("authorization_material_type"),
+ "source_final_signable_request_package_id": handoff.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_verifier_receipt_evidence_handoff",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "source_chain_ids_present",
+ bool(future_closeout.get("closeout_id"))
+ and bool(future_closeout.get("source_detached_verification_evidence_validation_id"))
+ and bool(future_closeout.get("source_verifier_receipt_closeout_boundary_id"))
+ and bool(future_closeout.get("source_signed_receipt_evidence_intake_id"))
+ and bool(handoff.get("source_final_signable_request_package_id")),
+ {
+ "closeout_id": future_closeout.get("closeout_id"),
+ "source_detached_verification_evidence_validation_id": future_closeout.get(
+ "source_detached_verification_evidence_validation_id"
+ ),
+ "source_final_signable_request_package_id": handoff.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "verifier_receipt_handoff_contract_carried_forward",
+ len(verifier_receipt_fields) == 12
+ and len(verifier_receipt_acceptance_gates) == 10
+ and len(handoff_fields) == 12
+ and len(handoff_acceptance_gates) == 10
+ and int(summary.get("verifier_receipt_field_count") or 0) == 12
+ and int(summary.get("verifier_receipt_handoff_acceptance_gate_count") or 0)
+ == 10,
+ {
+ "verifier_receipt_field_count": len(verifier_receipt_fields),
+ "verifier_receipt_acceptance_gate_count": len(
+ verifier_receipt_acceptance_gates
+ ),
+ "verifier_receipt_evidence_handoff_field_count": len(handoff_fields),
+ "verifier_receipt_handoff_acceptance_gate_count": len(
+ handoff_acceptance_gates
+ ),
+ },
+ "wait_for_verifier_receipt_handoff_contract",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "authorization_evidence_execution_preflight_contract_complete",
+ len(authorization_evidence_execution_fields) == 12
+ and len(authorization_evidence_execution_acceptance_gates) == 10
+ and "verifier_receipt_sha256" in authorization_evidence_execution_fields
+ and "no_secret_signature_or_database_write_in_preflight"
+ in authorization_evidence_execution_acceptance_gates,
+ {
+ "authorization_evidence_execution_field_count": len(
+ authorization_evidence_execution_fields
+ ),
+ "authorization_evidence_execution_acceptance_gate_count": len(
+ authorization_evidence_execution_acceptance_gates
+ ),
+ },
+ "wait_for_authorization_evidence_execution_preflight_contract",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "external_receipt_contract_carried_forward",
+ len(required_external_evidence) == 10
+ and len(external_acceptance_gates) == 8
+ and int(summary.get("required_external_receipt_evidence_count") or 0) == 10
+ and int(summary.get("external_receipt_acceptance_gate_count") or 0) == 8,
+ {
+ "required_external_receipt_evidence_count": len(required_external_evidence),
+ "external_receipt_acceptance_gate_count": len(external_acceptance_gates),
+ "summary_required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "summary_external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ },
+ "wait_for_external_receipt_contract",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "secret_signed_material_and_execution_boundary_enforced",
+ handoff.get("detached_signature_verification_performed") is False
+ and handoff.get("verifier_receipt_persisted") is False
+ and handoff.get("external_signed_authorization_receipt_included") is False
+ and handoff.get("signed_authorization_receipt_included") is False
+ and handoff.get("signature_material_included") is False
+ and handoff.get("secret_material_included") is False
+ and handoff.get("accepts_plaintext_secret") is False
+ and operator_secret_boundary.get("secret_reference_mode")
+ == "external_runtime_reference_only"
+ and operator_secret_boundary.get("accepts_plaintext_secret") is False,
+ {
+ "detached_signature_verification_performed": handoff.get(
+ "detached_signature_verification_performed"
+ ),
+ "verifier_receipt_persisted": handoff.get("verifier_receipt_persisted"),
+ "signature_material_included": handoff.get("signature_material_included"),
+ "secret_material_included": handoff.get("secret_material_included"),
+ "accepts_plaintext_secret": handoff.get("accepts_plaintext_secret"),
+ },
+ "abort_on_secret_signed_material_or_execution_boundary_violation",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "same_run_production_truth_required",
+ handoff.get("requires_fresh_production_truth_in_same_run") is True
+ and int(summary.get("same_run_truth_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": handoff.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ },
+ "require_same_run_production_truth",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "post_apply_verifier_and_hash_lock_required",
+ handoff.get("requires_post_apply_verifier") is True
+ and bool(handoff.get("post_apply_verifier_endpoint"))
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1
+ and handoff.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and handoff.get("hash_matches") is True
+ and bool(handoff.get("expected_sha256"))
+ and handoff.get("expected_sha256") == handoff.get("actual_sha256"),
+ {
+ "requires_post_apply_verifier": handoff.get("requires_post_apply_verifier"),
+ "post_apply_verifier_endpoint": handoff.get("post_apply_verifier_endpoint"),
+ "target_file": handoff.get("target_file"),
+ "hash_matches": handoff.get("hash_matches"),
+ },
+ "require_post_apply_verifier_and_hash_lock",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "closeout_contract_blocks_database_apply",
+ closeout_contract.get(
+ "permits_future_database_apply_authorization_verifier_handoff"
+ )
+ is True
+ and closeout_contract.get("issues_database_apply_authorization") is False
+ and closeout_contract.get("ready_for_database_apply_now") is False
+ and closeout_contract.get("signs_database_apply_authorization") is False
+ and closeout_contract.get("persists_verifier_receipt") is False
+ and closeout_contract.get("performs_detached_signature_verification") is False,
+ {
+ "permits_future_database_apply_authorization_verifier_handoff": (
+ closeout_contract.get(
+ "permits_future_database_apply_authorization_verifier_handoff"
+ )
+ ),
+ "ready_for_database_apply_now": closeout_contract.get(
+ "ready_for_database_apply_now"
+ ),
+ "persists_verifier_receipt": closeout_contract.get(
+ "persists_verifier_receipt"
+ ),
+ },
+ "abort_if_closeout_contract_authorizes_database_apply",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "performs_detached_signature_verification": safety.get(
+ "performs_detached_signature_verification"
+ ),
+ "persists_verifier_receipt": safety.get("persists_verifier_receipt"),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _authorization_evidence_execution_preflight_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and future_closeout.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ "manual_review_mode": future_closeout.get("manual_review_mode"),
+ },
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ preflight_ready = not waiting_checks
+ preflight_status = (
+ "DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_PREFLIGHT_READY"
+ if preflight_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_VERIFIER_RECEIPT_CLOSEOUT"
+ )
+ future_database_apply_authorization_verifier_handoff = {
+ "preflight_id": preflight_id,
+ "source_verifier_receipt_closeout_id": future_closeout.get("closeout_id"),
+ "source_verifier_receipt_evidence_handoff_id": handoff.get("handoff_id"),
+ "source_detached_verification_evidence_validation_id": handoff.get(
+ "source_detached_verification_evidence_validation_id"
+ ),
+ "source_final_signable_request_package_id": handoff.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": preflight_status,
+ "ready_for_future_database_apply_authorization_verifier_handoff": (
+ preflight_ready
+ ),
+ "can_enter_future_authorization_evidence_execution_closeout": preflight_ready,
+ "authorization_evidence_execution_preflight_ready": preflight_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "detached_signature_verification_performed": False,
+ "verifier_receipt_persisted": False,
+ "external_signed_authorization_receipt_included": False,
+ "signed_authorization_receipt_included": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "secret_material_required_in_preview": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ authorization_evidence_execution_preflight = {
+ "preflight_id": preflight_id,
+ "authorization_material_type": "authorization_evidence_execution_preflight",
+ "source_verifier_receipt_closeout_id": future_closeout.get("closeout_id"),
+ "source_verifier_receipt_evidence_handoff_id": handoff.get("handoff_id"),
+ "source_detached_verification_evidence_validation_id": handoff.get(
+ "source_detached_verification_evidence_validation_id"
+ ),
+ "source_signed_receipt_evidence_intake_id": handoff.get(
+ "source_signed_receipt_evidence_intake_id"
+ ),
+ "source_final_signable_request_package_id": handoff.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": preflight_status,
+ "ready_for_future_authorization_evidence_execution_preflight": (
+ preflight_ready
+ ),
+ "authorization_evidence_execution_fields": authorization_evidence_execution_fields,
+ "authorization_evidence_execution_field_count": len(
+ authorization_evidence_execution_fields
+ ),
+ "authorization_evidence_execution_acceptance_gates": (
+ authorization_evidence_execution_acceptance_gates
+ ),
+ "authorization_evidence_execution_acceptance_gate_count": len(
+ authorization_evidence_execution_acceptance_gates
+ ),
+ "verifier_receipt_fields": verifier_receipt_fields,
+ "verifier_receipt_field_count": len(verifier_receipt_fields),
+ "verifier_receipt_acceptance_gates": verifier_receipt_acceptance_gates,
+ "verifier_receipt_acceptance_gate_count": len(verifier_receipt_acceptance_gates),
+ "verifier_receipt_evidence_handoff_fields": handoff_fields,
+ "verifier_receipt_evidence_handoff_field_count": len(handoff_fields),
+ "verifier_receipt_handoff_acceptance_gates": handoff_acceptance_gates,
+ "verifier_receipt_handoff_acceptance_gate_count": len(handoff_acceptance_gates),
+ "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_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "executes_authorization_evidence": False,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "operator_held_secret_boundary_contract": operator_secret_boundary,
+ "target_file": handoff.get("target_file"),
+ "expected_sha256": handoff.get("expected_sha256"),
+ "actual_sha256": handoff.get("actual_sha256"),
+ "hash_matches": handoff.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": handoff.get("post_apply_verifier_endpoint"),
+ }
+ authorization_evidence_execution_preflight_contract = {
+ "mode": "authorization_evidence_execution_preflight_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-preflight"
+ ),
+ "source_verifier_receipt_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-verifier-receipt-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_authorization_evidence_execution_closeout": preflight_ready,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_PREFLIGHT_POLICY,
+ "result": preflight_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "authorization_evidence_execution_preflight_ready_count": (
+ 1 if preflight_ready else 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": len(checks),
+ "authorization_evidence_execution_preflight_pass_count": passed_count,
+ "authorization_evidence_execution_preflight_waiting_count": len(
+ waiting_checks
+ ),
+ "authorization_verifier_receipt_closeout_ready_count": summary.get(
+ "authorization_verifier_receipt_closeout_ready_count", 0
+ ),
+ "verifier_receipt_closeout_check_count": summary.get(
+ "verifier_receipt_closeout_check_count", 0
+ ),
+ "authorization_detached_verification_evidence_validation_ready_count": (
+ summary.get(
+ "authorization_detached_verification_evidence_validation_ready_count",
+ 0,
+ )
+ ),
+ "detached_verification_evidence_validation_check_count": summary.get(
+ "detached_verification_evidence_validation_check_count", 0
+ ),
+ "verifier_receipt_evidence_handoff_count": summary.get(
+ "verifier_receipt_evidence_handoff_count", 0
+ ),
+ "authorization_evidence_execution_preflight_count": 1,
+ "authorization_evidence_execution_field_count": len(
+ authorization_evidence_execution_fields
+ ),
+ "authorization_evidence_execution_acceptance_gate_count": len(
+ authorization_evidence_execution_acceptance_gates
+ ),
+ "verifier_receipt_field_count": summary.get(
+ "verifier_receipt_field_count", 0
+ ),
+ "verifier_receipt_acceptance_gate_count": summary.get(
+ "verifier_receipt_acceptance_gate_count", 0
+ ),
+ "verifier_receipt_evidence_handoff_field_count": summary.get(
+ "verifier_receipt_evidence_handoff_field_count", 0
+ ),
+ "verifier_receipt_handoff_acceptance_gate_count": summary.get(
+ "verifier_receipt_handoff_acceptance_gate_count", 0
+ ),
+ "required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ "operator_held_secret_boundary_count": summary.get(
+ "operator_held_secret_boundary_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_database_apply_authorization_verifier_handoff": (
+ future_database_apply_authorization_verifier_handoff
+ ),
+ "authorization_evidence_execution_preflight": (
+ authorization_evidence_execution_preflight
+ ),
+ "authorization_evidence_execution_preflight_contract": (
+ authorization_evidence_execution_preflight_contract
+ ),
+ "authorization_evidence_execution_preflight_checks": checks,
+ "source_verifier_receipt_closeout_summary": summary,
+ "source_verifier_receipt_closeout_contract": closeout_contract,
+ "source_verifier_receipt_evidence_handoff": handoff,
+ "safety": {
+ "read_only_db_apply_authorization_evidence_execution_preflight": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this preflight for a future authorization evidence execution closeout lane.",
+ "Keep signed receipt body, signature material, secret values, shell execution, SQL, and DB writes out of this preflight.",
+ "A later execution closeout may validate external evidence receipt readiness; this preflight still does not authorize DB apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_evidence_execution_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out authorization evidence execution readiness without executing it."""
+ preflight = build_pchome_auto_policy_db_apply_authorization_evidence_execution_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ verifier_handoff = (
+ preflight.get("future_database_apply_authorization_verifier_handoff") or {}
+ )
+ execution_preflight = (
+ preflight.get("authorization_evidence_execution_preflight") or {}
+ )
+ preflight_contract = (
+ preflight.get("authorization_evidence_execution_preflight_contract") or {}
+ )
+ summary = preflight.get("summary") or {}
+ safety = preflight.get("safety") or {}
+ closeout_id = _db_apply_authorization_evidence_execution_closeout_id(preflight)
+ execution_fields = list(
+ execution_preflight.get("authorization_evidence_execution_fields") or []
+ )
+ execution_acceptance_gates = list(
+ execution_preflight.get("authorization_evidence_execution_acceptance_gates") or []
+ )
+ verifier_receipt_fields = list(
+ execution_preflight.get("verifier_receipt_fields") or []
+ )
+ verifier_receipt_acceptance_gates = list(
+ execution_preflight.get("verifier_receipt_acceptance_gates") or []
+ )
+ handoff_fields = list(
+ execution_preflight.get("verifier_receipt_evidence_handoff_fields") or []
+ )
+ handoff_acceptance_gates = list(
+ execution_preflight.get("verifier_receipt_handoff_acceptance_gates") or []
+ )
+ closeout_fields = [
+ "closeout_id",
+ "source_authorization_evidence_execution_preflight_id",
+ "source_verifier_receipt_closeout_id",
+ "source_verifier_receipt_evidence_handoff_id",
+ "source_final_signable_request_package_id",
+ "verifier_receipt_id_reference",
+ "external_receipt_id_reference",
+ "payload_sha256",
+ "receipt_sha256",
+ "verifier_receipt_sha256",
+ "final_verifier_gate_endpoint",
+ "post_apply_verifier_endpoint",
+ ]
+ closeout_acceptance_gates = [
+ "authorization_evidence_execution_preflight_ready",
+ "final_verifier_handoff_ready",
+ "source_chain_ids_match",
+ "production_truth_same_run_required",
+ "payload_receipt_verifier_hashes_present",
+ "final_signable_request_package_hash_locked",
+ "post_apply_verifier_required",
+ "no_secret_signature_or_execution_closeout",
+ "no_database_apply_authorized_by_closeout",
+ "exception_only_failure_routing",
+ ]
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("performs_detached_signature_verification") is False
+ and safety.get("persists_verifier_receipt") is False
+ and safety.get("executes_authorization_evidence") is False
+ and execution_preflight.get("detached_signature_verification_performed") is False
+ and execution_preflight.get("verifier_receipt_persisted") is False
+ and execution_preflight.get("external_signed_authorization_receipt_included") is False
+ and execution_preflight.get("signed_authorization_receipt_included") is False
+ and execution_preflight.get("signature_material_included") is False
+ and execution_preflight.get("secret_material_included") is False
+ and execution_preflight.get("secret_material_required_in_preview") is False
+ and execution_preflight.get("accepts_plaintext_secret") is False
+ and execution_preflight.get("reads_secret_in_preview") is False
+ and execution_preflight.get("executes_shell_in_preview") is False
+ and execution_preflight.get("executes_sql_in_preview") is False
+ and execution_preflight.get("writes_database_in_preview") is False
+ and execution_preflight.get("executes_authorization_evidence") is False
+ )
+ hashes_present = (
+ bool(execution_preflight.get("expected_sha256"))
+ and bool(execution_preflight.get("actual_sha256"))
+ and execution_preflight.get("hash_matches") is True
+ and "payload_sha256" in execution_fields
+ and "receipt_sha256" in execution_fields
+ and "verifier_receipt_sha256" in execution_fields
+ )
+ checks = [
+ _authorization_evidence_execution_closeout_check(
+ "authorization_evidence_execution_preflight_ready",
+ preflight.get("result")
+ == "DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_PREFLIGHT_READY"
+ and execution_preflight.get(
+ "ready_for_future_authorization_evidence_execution_preflight"
+ )
+ is True,
+ {
+ "result": preflight.get("result"),
+ "ready_for_future_authorization_evidence_execution_preflight": (
+ execution_preflight.get(
+ "ready_for_future_authorization_evidence_execution_preflight"
+ )
+ ),
+ },
+ "wait_for_authorization_evidence_execution_preflight",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "final_verifier_handoff_ready",
+ verifier_handoff.get(
+ "ready_for_future_database_apply_authorization_verifier_handoff"
+ )
+ is True
+ and verifier_handoff.get(
+ "can_enter_future_authorization_evidence_execution_closeout"
+ )
+ is True
+ and verifier_handoff.get("authorization_evidence_execution_preflight_ready")
+ is True,
+ {
+ "ready_for_future_database_apply_authorization_verifier_handoff": (
+ verifier_handoff.get(
+ "ready_for_future_database_apply_authorization_verifier_handoff"
+ )
+ ),
+ "can_enter_future_authorization_evidence_execution_closeout": (
+ verifier_handoff.get(
+ "can_enter_future_authorization_evidence_execution_closeout"
+ )
+ ),
+ },
+ "wait_for_database_apply_authorization_verifier_handoff",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "authorization_evidence_execution_preflight_package_complete",
+ execution_preflight.get("authorization_material_type")
+ == "authorization_evidence_execution_preflight"
+ and bool(execution_preflight.get("preflight_id"))
+ and len(execution_fields) == 12
+ and len(execution_acceptance_gates) == 10
+ and execution_preflight.get("ready_for_database_apply_now") is False
+ and execution_preflight.get("issues_database_apply_authorization") is False
+ and execution_preflight.get("signs_database_apply_authorization") is False,
+ {
+ "preflight_id": execution_preflight.get("preflight_id"),
+ "authorization_evidence_execution_field_count": len(execution_fields),
+ "authorization_evidence_execution_acceptance_gate_count": len(
+ execution_acceptance_gates
+ ),
+ },
+ "wait_for_authorization_evidence_execution_preflight_package",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "source_chain_ids_present",
+ bool(execution_preflight.get("source_verifier_receipt_closeout_id"))
+ and bool(execution_preflight.get("source_verifier_receipt_evidence_handoff_id"))
+ and bool(
+ execution_preflight.get(
+ "source_detached_verification_evidence_validation_id"
+ )
+ )
+ and bool(execution_preflight.get("source_signed_receipt_evidence_intake_id"))
+ and bool(execution_preflight.get("source_final_signable_request_package_id")),
+ {
+ "source_verifier_receipt_closeout_id": execution_preflight.get(
+ "source_verifier_receipt_closeout_id"
+ ),
+ "source_verifier_receipt_evidence_handoff_id": (
+ execution_preflight.get("source_verifier_receipt_evidence_handoff_id")
+ ),
+ "source_final_signable_request_package_id": execution_preflight.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "authorization_evidence_execution_closeout_contract_complete",
+ len(closeout_fields) == 12
+ and len(closeout_acceptance_gates) == 10
+ and "final_verifier_gate_endpoint" in closeout_fields
+ and "no_database_apply_authorized_by_closeout"
+ in closeout_acceptance_gates,
+ {
+ "authorization_evidence_execution_closeout_field_count": len(
+ closeout_fields
+ ),
+ "authorization_evidence_execution_closeout_acceptance_gate_count": len(
+ closeout_acceptance_gates
+ ),
+ },
+ "wait_for_authorization_evidence_execution_closeout_contract",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "verifier_receipt_handoff_contract_carried_forward",
+ len(verifier_receipt_fields) == 12
+ and len(verifier_receipt_acceptance_gates) == 10
+ and len(handoff_fields) == 12
+ and len(handoff_acceptance_gates) == 10
+ and int(summary.get("verifier_receipt_field_count") or 0) == 12
+ and int(summary.get("verifier_receipt_handoff_acceptance_gate_count") or 0)
+ == 10,
+ {
+ "verifier_receipt_field_count": len(verifier_receipt_fields),
+ "verifier_receipt_acceptance_gate_count": len(
+ verifier_receipt_acceptance_gates
+ ),
+ "verifier_receipt_evidence_handoff_field_count": len(handoff_fields),
+ "verifier_receipt_handoff_acceptance_gate_count": len(
+ handoff_acceptance_gates
+ ),
+ },
+ "wait_for_verifier_receipt_handoff_contract",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "verifier_hash_and_receipt_chain_locked",
+ hashes_present,
+ {
+ "hash_matches": execution_preflight.get("hash_matches"),
+ "expected_sha256_present": bool(execution_preflight.get("expected_sha256")),
+ "actual_sha256_present": bool(execution_preflight.get("actual_sha256")),
+ "payload_sha256_field_present": "payload_sha256" in execution_fields,
+ "receipt_sha256_field_present": "receipt_sha256" in execution_fields,
+ "verifier_receipt_sha256_field_present": (
+ "verifier_receipt_sha256" in execution_fields
+ ),
+ },
+ "require_payload_receipt_and_verifier_hash_lock",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "same_run_production_truth_and_post_apply_verifier_required",
+ execution_preflight.get("requires_fresh_production_truth_in_same_run")
+ is True
+ and execution_preflight.get("requires_post_apply_verifier") is True
+ and bool(execution_preflight.get("post_apply_verifier_endpoint"))
+ and int(summary.get("same_run_truth_required_count") or 0) == 1
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": (
+ execution_preflight.get("requires_fresh_production_truth_in_same_run")
+ ),
+ "requires_post_apply_verifier": execution_preflight.get(
+ "requires_post_apply_verifier"
+ ),
+ "post_apply_verifier_endpoint": execution_preflight.get(
+ "post_apply_verifier_endpoint"
+ ),
+ },
+ "require_same_run_production_truth_and_post_apply_verifier",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "secret_signed_material_and_execution_boundary_enforced",
+ execution_preflight.get("detached_signature_verification_performed") is False
+ and execution_preflight.get("verifier_receipt_persisted") is False
+ and execution_preflight.get("external_signed_authorization_receipt_included")
+ is False
+ and execution_preflight.get("signed_authorization_receipt_included") is False
+ and execution_preflight.get("signature_material_included") is False
+ and execution_preflight.get("secret_material_included") is False
+ and execution_preflight.get("accepts_plaintext_secret") is False
+ and execution_preflight.get("executes_authorization_evidence") is False
+ and verifier_handoff.get("executes_authorization_evidence") is False,
+ {
+ "detached_signature_verification_performed": execution_preflight.get(
+ "detached_signature_verification_performed"
+ ),
+ "verifier_receipt_persisted": execution_preflight.get(
+ "verifier_receipt_persisted"
+ ),
+ "signature_material_included": execution_preflight.get(
+ "signature_material_included"
+ ),
+ "secret_material_included": execution_preflight.get(
+ "secret_material_included"
+ ),
+ "executes_authorization_evidence": execution_preflight.get(
+ "executes_authorization_evidence"
+ ),
+ },
+ "abort_on_secret_signed_material_execution_or_persistence",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "preflight_contract_blocks_database_apply",
+ preflight_contract.get(
+ "permits_future_authorization_evidence_execution_closeout"
+ )
+ is True
+ and preflight_contract.get("issues_database_apply_authorization") is False
+ and preflight_contract.get("ready_for_database_apply_now") is False
+ and preflight_contract.get("signs_database_apply_authorization") is False
+ and preflight_contract.get("writes_database") is False
+ and preflight_contract.get("executes_authorization_evidence") is False,
+ {
+ "permits_future_authorization_evidence_execution_closeout": (
+ preflight_contract.get(
+ "permits_future_authorization_evidence_execution_closeout"
+ )
+ ),
+ "ready_for_database_apply_now": preflight_contract.get(
+ "ready_for_database_apply_now"
+ ),
+ "executes_authorization_evidence": preflight_contract.get(
+ "executes_authorization_evidence"
+ ),
+ },
+ "abort_if_preflight_contract_authorizes_database_apply",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "executes_authorization_evidence": safety.get(
+ "executes_authorization_evidence"
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _authorization_evidence_execution_closeout_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and verifier_handoff.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0
+ ),
+ "manual_review_mode": verifier_handoff.get("manual_review_mode"),
+ },
+ "route_failed_final_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_PREFLIGHT"
+ )
+ final_verifier_gate_endpoint = (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-authorization-evidence-execution-closeout"
+ )
+ future_database_apply_authorization_final_verifier_gate = {
+ "final_verifier_gate_id": closeout_id,
+ "source_authorization_evidence_execution_preflight_id": (
+ execution_preflight.get("preflight_id")
+ ),
+ "source_verifier_receipt_closeout_id": execution_preflight.get(
+ "source_verifier_receipt_closeout_id"
+ ),
+ "source_verifier_receipt_evidence_handoff_id": execution_preflight.get(
+ "source_verifier_receipt_evidence_handoff_id"
+ ),
+ "source_final_signable_request_package_id": execution_preflight.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_authorization_final_verifier_gate": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_apply_final_preflight": (
+ closeout_ready
+ ),
+ "authorization_evidence_execution_closeout_ready": closeout_ready,
+ "final_verifier_gate_ready": closeout_ready,
+ "final_verifier_gate_executed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "detached_signature_verification_performed": False,
+ "verifier_receipt_persisted": False,
+ "external_signed_authorization_receipt_included": False,
+ "signed_authorization_receipt_included": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "secret_material_required_in_preview": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ authorization_evidence_execution_closeout = {
+ "closeout_id": closeout_id,
+ "authorization_material_type": "authorization_evidence_execution_closeout",
+ "source_authorization_evidence_execution_preflight_id": (
+ execution_preflight.get("preflight_id")
+ ),
+ "source_verifier_receipt_closeout_id": execution_preflight.get(
+ "source_verifier_receipt_closeout_id"
+ ),
+ "source_verifier_receipt_evidence_handoff_id": execution_preflight.get(
+ "source_verifier_receipt_evidence_handoff_id"
+ ),
+ "source_detached_verification_evidence_validation_id": (
+ execution_preflight.get(
+ "source_detached_verification_evidence_validation_id"
+ )
+ ),
+ "source_signed_receipt_evidence_intake_id": execution_preflight.get(
+ "source_signed_receipt_evidence_intake_id"
+ ),
+ "source_final_signable_request_package_id": execution_preflight.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_authorization_evidence_execution_closeout": (
+ closeout_ready
+ ),
+ "final_verifier_gate_endpoint": final_verifier_gate_endpoint,
+ "authorization_evidence_execution_closeout_fields": closeout_fields,
+ "authorization_evidence_execution_closeout_field_count": len(closeout_fields),
+ "authorization_evidence_execution_closeout_acceptance_gates": (
+ closeout_acceptance_gates
+ ),
+ "authorization_evidence_execution_closeout_acceptance_gate_count": len(
+ closeout_acceptance_gates
+ ),
+ "authorization_evidence_execution_fields": execution_fields,
+ "authorization_evidence_execution_field_count": len(execution_fields),
+ "authorization_evidence_execution_acceptance_gates": (
+ execution_acceptance_gates
+ ),
+ "authorization_evidence_execution_acceptance_gate_count": len(
+ execution_acceptance_gates
+ ),
+ "verifier_receipt_fields": verifier_receipt_fields,
+ "verifier_receipt_field_count": len(verifier_receipt_fields),
+ "verifier_receipt_acceptance_gates": verifier_receipt_acceptance_gates,
+ "verifier_receipt_acceptance_gate_count": len(
+ verifier_receipt_acceptance_gates
+ ),
+ "verifier_receipt_evidence_handoff_fields": handoff_fields,
+ "verifier_receipt_evidence_handoff_field_count": len(handoff_fields),
+ "verifier_receipt_handoff_acceptance_gates": handoff_acceptance_gates,
+ "verifier_receipt_handoff_acceptance_gate_count": len(
+ handoff_acceptance_gates
+ ),
+ "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_shell_in_preview": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_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,
+ "target_file": execution_preflight.get("target_file"),
+ "expected_sha256": execution_preflight.get("expected_sha256"),
+ "actual_sha256": execution_preflight.get("actual_sha256"),
+ "hash_matches": execution_preflight.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": execution_preflight.get(
+ "post_apply_verifier_endpoint"
+ ),
+ }
+ authorization_evidence_execution_closeout_contract = {
+ "mode": "authorization_evidence_execution_closeout_final_verifier_gate_only",
+ "source_endpoint": final_verifier_gate_endpoint,
+ "source_authorization_evidence_execution_preflight_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-authorization-evidence-execution-preflight"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_authorization_final_verifier_gate": closeout_ready,
+ "permits_future_database_apply_controlled_apply_final_preflight": (
+ closeout_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(preflight.get("success")),
+ "generated_at": preflight.get("generated_at"),
+ "source_policy": preflight.get("policy"),
+ "stats": preflight.get("stats") or {},
+ "summary": {
+ "authorization_evidence_execution_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": len(checks),
+ "authorization_evidence_execution_closeout_pass_count": passed_count,
+ "authorization_evidence_execution_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "authorization_verifier_receipt_closeout_ready_count": summary.get(
+ "authorization_verifier_receipt_closeout_ready_count", 0
+ ),
+ "verifier_receipt_closeout_check_count": summary.get(
+ "verifier_receipt_closeout_check_count", 0
+ ),
+ "authorization_detached_verification_evidence_validation_ready_count": (
+ summary.get(
+ "authorization_detached_verification_evidence_validation_ready_count",
+ 0,
+ )
+ ),
+ "detached_verification_evidence_validation_check_count": summary.get(
+ "detached_verification_evidence_validation_check_count", 0
+ ),
+ "verifier_receipt_evidence_handoff_count": summary.get(
+ "verifier_receipt_evidence_handoff_count", 0
+ ),
+ "authorization_evidence_execution_preflight_count": summary.get(
+ "authorization_evidence_execution_preflight_count", 0
+ ),
+ "authorization_evidence_execution_closeout_count": 1,
+ "database_apply_final_verifier_gate_count": 1,
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "authorization_evidence_execution_closeout_field_count": len(
+ closeout_fields
+ ),
+ "authorization_evidence_execution_closeout_acceptance_gate_count": len(
+ closeout_acceptance_gates
+ ),
+ "authorization_evidence_execution_field_count": summary.get(
+ "authorization_evidence_execution_field_count", 0
+ ),
+ "authorization_evidence_execution_acceptance_gate_count": summary.get(
+ "authorization_evidence_execution_acceptance_gate_count", 0
+ ),
+ "verifier_receipt_field_count": summary.get(
+ "verifier_receipt_field_count", 0
+ ),
+ "verifier_receipt_acceptance_gate_count": summary.get(
+ "verifier_receipt_acceptance_gate_count", 0
+ ),
+ "verifier_receipt_evidence_handoff_field_count": summary.get(
+ "verifier_receipt_evidence_handoff_field_count", 0
+ ),
+ "verifier_receipt_handoff_acceptance_gate_count": summary.get(
+ "verifier_receipt_handoff_acceptance_gate_count", 0
+ ),
+ "required_external_receipt_evidence_count": summary.get(
+ "required_external_receipt_evidence_count", 0
+ ),
+ "external_receipt_acceptance_gate_count": summary.get(
+ "external_receipt_acceptance_gate_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_authorization_final_verifier_gate": (
+ future_database_apply_authorization_final_verifier_gate
+ ),
+ "authorization_evidence_execution_closeout": (
+ authorization_evidence_execution_closeout
+ ),
+ "authorization_evidence_execution_closeout_contract": (
+ authorization_evidence_execution_closeout_contract
+ ),
+ "authorization_evidence_execution_closeout_checks": checks,
+ "source_authorization_evidence_execution_preflight_summary": summary,
+ "source_authorization_evidence_execution_preflight_contract": (
+ preflight_contract
+ ),
+ "source_authorization_evidence_execution_preflight": execution_preflight,
+ "source_database_apply_authorization_verifier_handoff": verifier_handoff,
+ "safety": {
+ "read_only_db_apply_authorization_evidence_execution_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout as the final verifier gate input for a future controlled-apply final preflight.",
+ "Keep signed receipt body, signature material, secret values, endpoint execution, SQL, and DB writes out of this closeout.",
+ "A later controlled-apply final preflight may bind rollback and post-apply verification; this closeout still does not authorize DB apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_apply_final_preflight(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Bind rollback and verifier requirements before any controlled apply."""
+ closeout = build_pchome_auto_policy_db_apply_authorization_evidence_execution_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ final_gate = (
+ closeout.get("future_database_apply_authorization_final_verifier_gate") or {}
+ )
+ evidence_closeout = closeout.get("authorization_evidence_execution_closeout") or {}
+ closeout_contract = (
+ closeout.get("authorization_evidence_execution_closeout_contract") or {}
+ )
+ summary = closeout.get("summary") or {}
+ safety = closeout.get("safety") or {}
+ preflight_id = _db_apply_controlled_apply_final_preflight_id(closeout)
+ controlled_apply_fields = [
+ "controlled_apply_preflight_id",
+ "source_final_verifier_gate_id",
+ "source_authorization_evidence_execution_closeout_id",
+ "source_final_signable_request_package_id",
+ "target_migration_file",
+ "target_migration_sha256",
+ "rollback_binding_id",
+ "rollback_verifier_endpoint",
+ "post_apply_verifier_endpoint",
+ "same_run_production_truth_reference",
+ "dry_run_command_shape",
+ "abort_conditions",
+ ]
+ controlled_apply_acceptance_gates = [
+ "final_verifier_gate_ready",
+ "source_chain_ids_match",
+ "target_migration_hash_locked",
+ "rollback_plan_bound",
+ "post_apply_verifier_bound",
+ "same_run_production_truth_required",
+ "dry_run_only_no_execution",
+ "no_secret_signature_or_database_apply",
+ "database_write_gate_remains_closed",
+ "exception_only_failure_routing",
+ ]
+ rollback_binding_fields = [
+ "rollback_binding_id",
+ "target_migration_file",
+ "target_migration_sha256",
+ "rollback_strategy",
+ "rollback_verifier_endpoint",
+ "post_apply_verifier_endpoint",
+ "rollback_requires_same_run_truth",
+ "rollback_execution_authorized",
+ ]
+ post_apply_verifier_binding_fields = [
+ "post_apply_verifier_binding_id",
+ "source_final_verifier_gate_id",
+ "post_apply_verifier_endpoint",
+ "expected_migration_sha256",
+ "same_run_production_truth_required",
+ "verifier_must_run_after_apply",
+ "verifier_execution_authorized_in_preview",
+ "database_apply_authorized",
+ ]
+ target_file = evidence_closeout.get("target_file")
+ expected_sha256 = evidence_closeout.get("expected_sha256")
+ actual_sha256 = evidence_closeout.get("actual_sha256")
+ target_hash_locked = (
+ target_file == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(expected_sha256)
+ and bool(actual_sha256)
+ and expected_sha256 == actual_sha256
+ and evidence_closeout.get("hash_matches") is True
+ )
+ rollback_binding_id = f"{preflight_id}-rollback"
+ post_apply_verifier_binding_id = f"{preflight_id}-post-apply-verifier"
+ rollback_binding = {
+ "rollback_binding_id": rollback_binding_id,
+ "target_migration_file": target_file,
+ "target_migration_sha256": expected_sha256,
+ "rollback_strategy": "fail_closed_no_apply_without_post_apply_verifier",
+ "rollback_verifier_endpoint": evidence_closeout.get("post_apply_verifier_endpoint"),
+ "post_apply_verifier_endpoint": evidence_closeout.get("post_apply_verifier_endpoint"),
+ "rollback_requires_same_run_truth": True,
+ "rollback_execution_authorized": False,
+ "rollback_executes_sql": False,
+ "rollback_writes_database": False,
+ "rollback_reads_secret": False,
+ "rollback_binding_field_count": len(rollback_binding_fields),
+ "rollback_binding_fields": rollback_binding_fields,
+ }
+ post_apply_verifier_binding = {
+ "post_apply_verifier_binding_id": post_apply_verifier_binding_id,
+ "source_final_verifier_gate_id": final_gate.get("final_verifier_gate_id"),
+ "post_apply_verifier_endpoint": evidence_closeout.get("post_apply_verifier_endpoint"),
+ "expected_migration_sha256": expected_sha256,
+ "same_run_production_truth_required": True,
+ "verifier_must_run_after_apply": True,
+ "verifier_execution_authorized_in_preview": False,
+ "database_apply_authorized": False,
+ "post_apply_verifier_binding_field_count": len(
+ post_apply_verifier_binding_fields
+ ),
+ "post_apply_verifier_binding_fields": post_apply_verifier_binding_fields,
+ }
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("performs_detached_signature_verification") is False
+ and safety.get("persists_verifier_receipt") is False
+ and safety.get("executes_authorization_evidence") is False
+ and safety.get("executes_database_apply") is False
+ 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_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("secret_material_included") is False
+ and evidence_closeout.get("secret_material_required_in_preview") is False
+ and evidence_closeout.get("accepts_plaintext_secret") is False
+ and evidence_closeout.get("reads_secret_in_preview") is False
+ and evidence_closeout.get("executes_shell_in_preview") is False
+ and evidence_closeout.get("executes_endpoint_in_preview") is False
+ and evidence_closeout.get("executes_sql_in_preview") is False
+ and evidence_closeout.get("writes_database_in_preview") is False
+ and evidence_closeout.get("executes_authorization_evidence") is False
+ and evidence_closeout.get("executes_database_apply") is False
+ )
+ checks = [
+ _controlled_apply_final_preflight_check(
+ "final_verifier_gate_ready",
+ closeout.get("result")
+ == "DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT_READY"
+ 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 evidence_closeout.get(
+ "ready_for_future_authorization_evidence_execution_closeout"
+ )
+ is True,
+ {
+ "result": closeout.get("result"),
+ "ready_for_future_database_apply_authorization_final_verifier_gate": (
+ final_gate.get(
+ "ready_for_future_database_apply_authorization_final_verifier_gate"
+ )
+ ),
+ "can_enter_future_database_apply_controlled_apply_final_preflight": (
+ final_gate.get(
+ "can_enter_future_database_apply_controlled_apply_final_preflight"
+ )
+ ),
+ },
+ "wait_for_database_apply_authorization_final_verifier_gate",
+ ),
+ _controlled_apply_final_preflight_check(
+ "authorization_evidence_execution_closeout_package_complete",
+ evidence_closeout.get("authorization_material_type")
+ == "authorization_evidence_execution_closeout"
+ and bool(evidence_closeout.get("closeout_id"))
+ and int(
+ evidence_closeout.get(
+ "authorization_evidence_execution_closeout_field_count"
+ )
+ or 0
+ )
+ == 12
+ and int(
+ evidence_closeout.get(
+ "authorization_evidence_execution_closeout_acceptance_gate_count"
+ )
+ or 0
+ )
+ == 10
+ and evidence_closeout.get("ready_for_database_apply_now") is False
+ and evidence_closeout.get("database_apply_authorized") is False,
+ {
+ "closeout_id": evidence_closeout.get("closeout_id"),
+ "authorization_evidence_execution_closeout_field_count": (
+ evidence_closeout.get(
+ "authorization_evidence_execution_closeout_field_count"
+ )
+ ),
+ "authorization_evidence_execution_closeout_acceptance_gate_count": (
+ evidence_closeout.get(
+ "authorization_evidence_execution_closeout_acceptance_gate_count"
+ )
+ ),
+ },
+ "wait_for_authorization_evidence_execution_closeout_package",
+ ),
+ _controlled_apply_final_preflight_check(
+ "source_chain_ids_present",
+ bool(final_gate.get("final_verifier_gate_id"))
+ and bool(evidence_closeout.get("closeout_id"))
+ and bool(evidence_closeout.get("source_authorization_evidence_execution_preflight_id"))
+ and bool(evidence_closeout.get("source_verifier_receipt_closeout_id"))
+ and bool(evidence_closeout.get("source_verifier_receipt_evidence_handoff_id"))
+ and bool(evidence_closeout.get("source_final_signable_request_package_id")),
+ {
+ "final_verifier_gate_id": final_gate.get("final_verifier_gate_id"),
+ "closeout_id": evidence_closeout.get("closeout_id"),
+ "source_final_signable_request_package_id": evidence_closeout.get(
+ "source_final_signable_request_package_id"
+ ),
+ },
+ "wait_for_source_authorization_chain_ids",
+ ),
+ _controlled_apply_final_preflight_check(
+ "controlled_apply_final_preflight_contract_complete",
+ len(controlled_apply_fields) == 12
+ and len(controlled_apply_acceptance_gates) == 10
+ and "rollback_binding_id" in controlled_apply_fields
+ and "post_apply_verifier_bound" in controlled_apply_acceptance_gates,
+ {
+ "controlled_apply_final_preflight_field_count": len(
+ controlled_apply_fields
+ ),
+ "controlled_apply_final_preflight_acceptance_gate_count": len(
+ controlled_apply_acceptance_gates
+ ),
+ },
+ "wait_for_controlled_apply_final_preflight_contract",
+ ),
+ _controlled_apply_final_preflight_check(
+ "rollback_binding_complete",
+ len(rollback_binding_fields) == 8
+ and bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_requires_same_run_truth") is True
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "rollback_binding_field_count": len(rollback_binding_fields),
+ "rollback_execution_authorized": rollback_binding.get(
+ "rollback_execution_authorized"
+ ),
+ },
+ "wait_for_rollback_binding",
+ ),
+ _controlled_apply_final_preflight_check(
+ "post_apply_verifier_binding_complete",
+ len(post_apply_verifier_binding_fields) == 8
+ and bool(post_apply_verifier_binding.get("post_apply_verifier_binding_id"))
+ and bool(post_apply_verifier_binding.get("post_apply_verifier_endpoint"))
+ and post_apply_verifier_binding.get("same_run_production_truth_required")
+ is True
+ and post_apply_verifier_binding.get("verifier_must_run_after_apply")
+ is True
+ and post_apply_verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and post_apply_verifier_binding.get("database_apply_authorized") is False,
+ {
+ "post_apply_verifier_binding_id": post_apply_verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "post_apply_verifier_endpoint": post_apply_verifier_binding.get(
+ "post_apply_verifier_endpoint"
+ ),
+ "post_apply_verifier_binding_field_count": len(
+ post_apply_verifier_binding_fields
+ ),
+ },
+ "wait_for_post_apply_verifier_binding",
+ ),
+ _controlled_apply_final_preflight_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": target_file,
+ "expected_sha256_present": bool(expected_sha256),
+ "actual_sha256_present": bool(actual_sha256),
+ "hash_matches": evidence_closeout.get("hash_matches"),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_apply_final_preflight_check(
+ "same_run_production_truth_and_post_apply_verifier_required",
+ evidence_closeout.get("requires_fresh_production_truth_in_same_run") is True
+ and evidence_closeout.get("requires_post_apply_verifier") is True
+ and bool(evidence_closeout.get("post_apply_verifier_endpoint"))
+ and int(summary.get("same_run_truth_required_count") or 0) == 1
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": evidence_closeout.get(
+ "requires_fresh_production_truth_in_same_run"
+ ),
+ "requires_post_apply_verifier": evidence_closeout.get(
+ "requires_post_apply_verifier"
+ ),
+ "post_apply_verifier_endpoint": evidence_closeout.get(
+ "post_apply_verifier_endpoint"
+ ),
+ },
+ "require_same_run_production_truth_and_post_apply_verifier",
+ ),
+ _controlled_apply_final_preflight_check(
+ "secret_signed_material_execution_and_write_boundary_enforced",
+ 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_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("secret_material_included") is False
+ and evidence_closeout.get("accepts_plaintext_secret") is False
+ and evidence_closeout.get("executes_authorization_evidence") is False
+ and evidence_closeout.get("executes_database_apply") is False
+ and final_gate.get("database_apply_authorized") is False,
+ {
+ "signature_material_included": evidence_closeout.get(
+ "signature_material_included"
+ ),
+ "secret_material_included": evidence_closeout.get(
+ "secret_material_included"
+ ),
+ "executes_database_apply": evidence_closeout.get(
+ "executes_database_apply"
+ ),
+ "database_apply_authorized": final_gate.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_on_secret_signed_material_execution_or_write_boundary_violation",
+ ),
+ _controlled_apply_final_preflight_check(
+ "final_verifier_contract_blocks_database_apply",
+ closeout_contract.get(
+ "permits_future_database_apply_controlled_apply_final_preflight"
+ )
+ is True
+ and closeout_contract.get("executes_database_apply") is False
+ and closeout_contract.get("database_apply_authorized") is False
+ and closeout_contract.get("ready_for_database_apply_now") is False
+ and closeout_contract.get("signs_database_apply_authorization") is False
+ and closeout_contract.get("writes_database") is False,
+ {
+ "permits_future_database_apply_controlled_apply_final_preflight": (
+ closeout_contract.get(
+ "permits_future_database_apply_controlled_apply_final_preflight"
+ )
+ ),
+ "database_apply_authorized": closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": closeout_contract.get("writes_database"),
+ },
+ "abort_if_final_verifier_contract_authorizes_database_apply",
+ ),
+ _controlled_apply_final_preflight_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "executes_database_apply": safety.get("executes_database_apply"),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_apply_final_preflight_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and final_gate.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0
+ ),
+ "manual_review_mode": final_gate.get("manual_review_mode"),
+ },
+ "route_failed_controlled_apply_preflight_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ final_preflight_ready = not waiting_checks
+ final_preflight_status = (
+ "DB_APPLY_CONTROLLED_APPLY_FINAL_PREFLIGHT_READY"
+ if final_preflight_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT"
+ )
+ future_database_apply_controlled_apply_final_preflight = {
+ "controlled_apply_preflight_id": preflight_id,
+ "source_final_verifier_gate_id": final_gate.get("final_verifier_gate_id"),
+ "source_authorization_evidence_execution_closeout_id": (
+ evidence_closeout.get("closeout_id")
+ ),
+ "source_authorization_evidence_execution_preflight_id": (
+ evidence_closeout.get("source_authorization_evidence_execution_preflight_id")
+ ),
+ "source_final_signable_request_package_id": evidence_closeout.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": final_preflight_status,
+ "ready_for_future_database_apply_controlled_apply_final_preflight": (
+ final_preflight_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_package": (
+ final_preflight_ready
+ ),
+ "controlled_apply_final_preflight_ready": final_preflight_ready,
+ "rollback_binding_ready": final_preflight_ready,
+ "post_apply_verifier_binding_ready": final_preflight_ready,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_apply_final_preflight = {
+ "controlled_apply_preflight_id": preflight_id,
+ "authorization_material_type": "controlled_apply_final_preflight",
+ "source_final_verifier_gate_id": final_gate.get("final_verifier_gate_id"),
+ "source_authorization_evidence_execution_closeout_id": (
+ evidence_closeout.get("closeout_id")
+ ),
+ "source_authorization_evidence_execution_preflight_id": (
+ evidence_closeout.get("source_authorization_evidence_execution_preflight_id")
+ ),
+ "source_verifier_receipt_closeout_id": evidence_closeout.get(
+ "source_verifier_receipt_closeout_id"
+ ),
+ "source_verifier_receipt_evidence_handoff_id": evidence_closeout.get(
+ "source_verifier_receipt_evidence_handoff_id"
+ ),
+ "source_final_signable_request_package_id": evidence_closeout.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": final_preflight_status,
+ "ready_for_future_database_apply_controlled_apply_final_preflight": (
+ final_preflight_ready
+ ),
+ "controlled_apply_final_preflight_fields": controlled_apply_fields,
+ "controlled_apply_final_preflight_field_count": len(controlled_apply_fields),
+ "controlled_apply_final_preflight_acceptance_gates": (
+ controlled_apply_acceptance_gates
+ ),
+ "controlled_apply_final_preflight_acceptance_gate_count": len(
+ controlled_apply_acceptance_gates
+ ),
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "rollback_binding_field_count": len(rollback_binding_fields),
+ "post_apply_verifier_binding": post_apply_verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "post_apply_verifier_binding_field_count": len(
+ post_apply_verifier_binding_fields
+ ),
+ "target_file": target_file,
+ "expected_sha256": expected_sha256,
+ "actual_sha256": actual_sha256,
+ "hash_matches": evidence_closeout.get("hash_matches"),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "rollback_bound": final_preflight_ready,
+ "post_apply_verifier_bound": final_preflight_ready,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "post_apply_verifier_endpoint": evidence_closeout.get(
+ "post_apply_verifier_endpoint"
+ ),
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "secret_material_required_in_preview": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ }
+ controlled_apply_final_preflight_contract = {
+ "mode": "controlled_apply_final_preflight_rollback_and_verifier_binding_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-apply-final-preflight"
+ ),
+ "source_authorization_evidence_execution_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-authorization-evidence-execution-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_package": (
+ final_preflight_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_APPLY_FINAL_PREFLIGHT_POLICY,
+ "result": final_preflight_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "controlled_apply_final_preflight_ready_count": (
+ 1 if final_preflight_ready else 0
+ ),
+ "controlled_apply_final_preflight_check_count": len(checks),
+ "controlled_apply_final_preflight_pass_count": passed_count,
+ "controlled_apply_final_preflight_waiting_count": len(waiting_checks),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "authorization_verifier_receipt_closeout_ready_count": summary.get(
+ "authorization_verifier_receipt_closeout_ready_count", 0
+ ),
+ "verifier_receipt_closeout_check_count": summary.get(
+ "verifier_receipt_closeout_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_count": 1,
+ "rollback_binding_count": 1,
+ "rollback_binding_field_count": len(rollback_binding_fields),
+ "post_apply_verifier_binding_count": 1,
+ "post_apply_verifier_binding_field_count": len(
+ post_apply_verifier_binding_fields
+ ),
+ "controlled_apply_final_preflight_field_count": len(
+ controlled_apply_fields
+ ),
+ "controlled_apply_final_preflight_acceptance_gate_count": len(
+ controlled_apply_acceptance_gates
+ ),
+ "authorization_evidence_execution_closeout_field_count": summary.get(
+ "authorization_evidence_execution_closeout_field_count", 0
+ ),
+ "authorization_evidence_execution_closeout_acceptance_gate_count": (
+ summary.get(
+ "authorization_evidence_execution_closeout_acceptance_gate_count",
+ 0,
+ )
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get("same_run_truth_required_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_apply_final_preflight": (
+ future_database_apply_controlled_apply_final_preflight
+ ),
+ "controlled_apply_final_preflight": controlled_apply_final_preflight,
+ "controlled_apply_final_preflight_contract": (
+ controlled_apply_final_preflight_contract
+ ),
+ "controlled_apply_final_preflight_checks": checks,
+ "source_authorization_evidence_execution_closeout_summary": summary,
+ "source_authorization_evidence_execution_closeout_contract": closeout_contract,
+ "source_authorization_evidence_execution_closeout": evidence_closeout,
+ "source_database_apply_authorization_final_verifier_gate": final_gate,
+ "safety": {
+ "read_only_db_apply_controlled_apply_final_preflight": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this final preflight to build a future controlled dry-run package.",
+ "Keep rollback and post-apply verifier binding machine-verifiable before any apply execution.",
+ "This preflight still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_package(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Package a future controlled dry-run receipt without executing it."""
+ final_preflight = build_pchome_auto_policy_db_apply_controlled_apply_final_preflight(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ future_preflight = (
+ final_preflight.get("future_database_apply_controlled_apply_final_preflight")
+ or {}
+ )
+ controlled_preflight = final_preflight.get("controlled_apply_final_preflight") or {}
+ final_preflight_contract = (
+ final_preflight.get("controlled_apply_final_preflight_contract") or {}
+ )
+ summary = final_preflight.get("summary") or {}
+ safety = final_preflight.get("safety") or {}
+ package_id = _db_apply_controlled_dry_run_package_id(final_preflight)
+ receipt_id = f"{package_id}-dry-run-receipt-preview"
+ rollback_binding = controlled_preflight.get("rollback_binding") or {}
+ verifier_binding = controlled_preflight.get("post_apply_verifier_binding") or {}
+ target_file = controlled_preflight.get("target_file")
+ expected_sha256 = controlled_preflight.get("expected_sha256")
+ actual_sha256 = controlled_preflight.get("actual_sha256")
+ target_hash_locked = (
+ target_file == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(expected_sha256)
+ and bool(actual_sha256)
+ and expected_sha256 == actual_sha256
+ and controlled_preflight.get("hash_matches") is True
+ )
+ dry_run_package_fields = [
+ "dry_run_package_id",
+ "source_controlled_apply_preflight_id",
+ "source_final_verifier_gate_id",
+ "source_authorization_evidence_execution_closeout_id",
+ "target_migration_file",
+ "target_migration_sha256",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_command_shape",
+ "dry_run_result_parser",
+ "dry_run_execution_receipt_id",
+ "abort_conditions",
+ ]
+ dry_run_acceptance_gates = [
+ "controlled_apply_final_preflight_ready",
+ "source_chain_ids_match",
+ "rollback_binding_present",
+ "post_apply_verifier_binding_present",
+ "target_migration_hash_locked",
+ "dry_run_command_shape_is_non_executable",
+ "dry_run_receipt_preview_only",
+ "no_secret_signature_or_database_apply",
+ "no_endpoint_sql_or_db_write",
+ "exception_only_failure_routing",
+ ]
+ receipt_fields = [
+ "receipt_id",
+ "source_dry_run_package_id",
+ "dry_run_status",
+ "dry_run_command_shape_hash",
+ "execution_performed",
+ "stdout_included",
+ "stderr_included",
+ "database_apply_authorized",
+ ]
+ result_parser_fields = [
+ "parser_id",
+ "source_dry_run_package_id",
+ "expected_receipt_status",
+ "required_receipt_fields",
+ "required_command_shape_hash",
+ "execution_required",
+ "stdout_allowed",
+ "stderr_allowed",
+ "database_apply_authorized",
+ "parser_verification_status",
+ ]
+ command_shape = {
+ "command_family": "pchome_db_apply_controlled_dry_run",
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "execution_allowed": False,
+ "shell_command_included": False,
+ "sql_included": False,
+ "endpoint_execution_included": False,
+ "database_write_included": False,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_rollback_binding": True,
+ "requires_post_apply_verifier_binding": True,
+ "target_file": target_file,
+ "target_sha256": expected_sha256,
+ "args_preview": [
+ "--dry-run",
+ "--check",
+ "--no-execute",
+ "--require-post-apply-verifier",
+ ],
+ }
+ command_shape_hash = hashlib.sha256(
+ json.dumps(command_shape, sort_keys=True).encode("utf-8")
+ ).hexdigest()
+ dry_run_execution_receipt_preview = {
+ "receipt_id": receipt_id,
+ "source_dry_run_package_id": package_id,
+ "dry_run_status": "preview_only_not_executed",
+ "dry_run_command_shape_hash": command_shape_hash,
+ "execution_performed": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "database_apply_authorized": False,
+ "executes_shell": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret": False,
+ "receipt_field_count": len(receipt_fields),
+ "receipt_fields": receipt_fields,
+ }
+ dry_run_result_parser = {
+ "parser_id": f"{package_id}-result-parser",
+ "source_dry_run_package_id": package_id,
+ "expected_receipt_status": "preview_only_not_executed",
+ "required_receipt_fields": receipt_fields,
+ "required_command_shape_hash": command_shape_hash,
+ "execution_required": False,
+ "stdout_allowed": False,
+ "stderr_allowed": False,
+ "database_apply_authorized": False,
+ "parser_verification_status": "schema_preview_ready",
+ "parser_field_count": len(result_parser_fields),
+ "parser_fields": result_parser_fields,
+ }
+ command_shape_preview_only = (
+ command_shape.get("dry_run_only") is True
+ and command_shape.get("check_mode_only") is True
+ and command_shape.get("execution_allowed") is False
+ and command_shape.get("shell_command_included") is False
+ and command_shape.get("sql_included") is False
+ and command_shape.get("endpoint_execution_included") is False
+ and command_shape.get("database_write_included") is False
+ )
+ receipt_preview_only = (
+ dry_run_execution_receipt_preview.get("execution_performed") is False
+ and dry_run_execution_receipt_preview.get("stdout_included") is False
+ and dry_run_execution_receipt_preview.get("stderr_included") is False
+ and dry_run_execution_receipt_preview.get("database_apply_authorized") is False
+ and dry_run_execution_receipt_preview.get("executes_shell") is False
+ and dry_run_execution_receipt_preview.get("executes_endpoint") is False
+ and dry_run_execution_receipt_preview.get("executes_sql") is False
+ and dry_run_execution_receipt_preview.get("writes_database") is False
+ and dry_run_execution_receipt_preview.get("reads_secret") is False
+ )
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("executes_authorization_evidence") is False
+ and safety.get("executes_database_apply") is False
+ and controlled_preflight.get("accepts_plaintext_secret") is False
+ and controlled_preflight.get("reads_secret_in_preview") is False
+ and controlled_preflight.get("signature_material_included") is False
+ and controlled_preflight.get("secret_material_included") is False
+ and controlled_preflight.get("secret_material_required_in_preview") is False
+ and controlled_preflight.get("signs_database_apply_authorization") is False
+ and controlled_preflight.get("executes_authorization_evidence") is False
+ and controlled_preflight.get("executes_database_apply") is False
+ and controlled_preflight.get("executes_endpoint_in_preview") is False
+ and controlled_preflight.get("executes_sql_in_preview") is False
+ and controlled_preflight.get("writes_database_in_preview") is False
+ and command_shape_preview_only
+ and receipt_preview_only
+ )
+ checks = [
+ _controlled_dry_run_package_check(
+ "controlled_apply_final_preflight_ready",
+ final_preflight.get("result")
+ == "DB_APPLY_CONTROLLED_APPLY_FINAL_PREFLIGHT_READY"
+ and future_preflight.get(
+ "ready_for_future_database_apply_controlled_apply_final_preflight"
+ )
+ is True
+ and future_preflight.get(
+ "can_enter_future_database_apply_controlled_dry_run_package"
+ )
+ is True
+ and controlled_preflight.get(
+ "ready_for_future_database_apply_controlled_apply_final_preflight"
+ )
+ is True,
+ {
+ "result": final_preflight.get("result"),
+ "ready_for_future_database_apply_controlled_apply_final_preflight": (
+ future_preflight.get(
+ "ready_for_future_database_apply_controlled_apply_final_preflight"
+ )
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_package": (
+ future_preflight.get(
+ "can_enter_future_database_apply_controlled_dry_run_package"
+ )
+ ),
+ },
+ "wait_for_controlled_apply_final_preflight",
+ ),
+ _controlled_dry_run_package_check(
+ "source_chain_ids_present",
+ bool(future_preflight.get("controlled_apply_preflight_id"))
+ and bool(controlled_preflight.get("source_final_verifier_gate_id"))
+ and bool(
+ controlled_preflight.get(
+ "source_authorization_evidence_execution_closeout_id"
+ )
+ )
+ and bool(
+ controlled_preflight.get("source_final_signable_request_package_id")
+ ),
+ {
+ "controlled_apply_preflight_id": future_preflight.get(
+ "controlled_apply_preflight_id"
+ ),
+ "source_final_verifier_gate_id": controlled_preflight.get(
+ "source_final_verifier_gate_id"
+ ),
+ "source_authorization_evidence_execution_closeout_id": (
+ controlled_preflight.get(
+ "source_authorization_evidence_execution_closeout_id"
+ )
+ ),
+ },
+ "wait_for_source_controlled_apply_chain_ids",
+ ),
+ _controlled_dry_run_package_check(
+ "controlled_dry_run_package_contract_complete",
+ len(dry_run_package_fields) == 12
+ and len(dry_run_acceptance_gates) == 10
+ and len(receipt_fields) == 8
+ and "dry_run_execution_receipt_id" in dry_run_package_fields
+ and "dry_run_receipt_preview_only" in dry_run_acceptance_gates,
+ {
+ "controlled_dry_run_package_field_count": len(
+ dry_run_package_fields
+ ),
+ "controlled_dry_run_acceptance_gate_count": len(
+ dry_run_acceptance_gates
+ ),
+ "dry_run_execution_receipt_field_count": len(receipt_fields),
+ },
+ "wait_for_controlled_dry_run_package_contract",
+ ),
+ _controlled_dry_run_package_check(
+ "rollback_binding_carried_forward",
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_requires_same_run_truth") is True
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "rollback_execution_authorized": rollback_binding.get(
+ "rollback_execution_authorized"
+ ),
+ "rollback_executes_sql": rollback_binding.get("rollback_executes_sql"),
+ },
+ "wait_for_rollback_binding",
+ ),
+ _controlled_dry_run_package_check(
+ "post_apply_verifier_binding_carried_forward",
+ bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and bool(verifier_binding.get("post_apply_verifier_endpoint"))
+ and verifier_binding.get("same_run_production_truth_required") is True
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False,
+ {
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "post_apply_verifier_endpoint": verifier_binding.get(
+ "post_apply_verifier_endpoint"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_post_apply_verifier_binding",
+ ),
+ _controlled_dry_run_package_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": target_file,
+ "expected_sha256_present": bool(expected_sha256),
+ "actual_sha256_present": bool(actual_sha256),
+ "hash_matches": controlled_preflight.get("hash_matches"),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_package_check(
+ "dry_run_command_shape_preview_only",
+ command_shape_preview_only
+ and command_shape.get("requires_fresh_production_truth_in_same_run")
+ is True
+ and command_shape.get("requires_rollback_binding") is True
+ and command_shape.get("requires_post_apply_verifier_binding") is True
+ and bool(command_shape_hash),
+ {
+ "dry_run_only": command_shape.get("dry_run_only"),
+ "check_mode_only": command_shape.get("check_mode_only"),
+ "execution_allowed": command_shape.get("execution_allowed"),
+ "dry_run_command_shape_hash_present": bool(command_shape_hash),
+ },
+ "abort_if_dry_run_command_shape_is_executable",
+ ),
+ _controlled_dry_run_package_check(
+ "dry_run_execution_receipt_preview_only",
+ receipt_preview_only
+ and dry_run_execution_receipt_preview.get("receipt_field_count") == 8
+ and bool(dry_run_execution_receipt_preview.get("receipt_id"))
+ and bool(
+ dry_run_execution_receipt_preview.get(
+ "dry_run_command_shape_hash"
+ )
+ ),
+ {
+ "receipt_id": dry_run_execution_receipt_preview.get("receipt_id"),
+ "dry_run_status": dry_run_execution_receipt_preview.get(
+ "dry_run_status"
+ ),
+ "execution_performed": dry_run_execution_receipt_preview.get(
+ "execution_performed"
+ ),
+ },
+ "abort_if_dry_run_receipt_indicates_execution",
+ ),
+ _controlled_dry_run_package_check(
+ "same_run_production_truth_and_post_apply_verifier_required",
+ controlled_preflight.get("requires_fresh_production_truth_in_same_run")
+ is True
+ and controlled_preflight.get("requires_post_apply_verifier") is True
+ and bool(controlled_preflight.get("post_apply_verifier_endpoint"))
+ and int(summary.get("same_run_truth_required_count") or 0) == 1
+ and int(summary.get("post_apply_verifier_required_count") or 0) == 1,
+ {
+ "requires_fresh_production_truth_in_same_run": (
+ controlled_preflight.get(
+ "requires_fresh_production_truth_in_same_run"
+ )
+ ),
+ "requires_post_apply_verifier": controlled_preflight.get(
+ "requires_post_apply_verifier"
+ ),
+ "post_apply_verifier_endpoint": controlled_preflight.get(
+ "post_apply_verifier_endpoint"
+ ),
+ },
+ "require_same_run_production_truth_and_post_apply_verifier",
+ ),
+ _controlled_dry_run_package_check(
+ "final_preflight_contract_blocks_database_apply",
+ final_preflight_contract.get(
+ "permits_future_database_apply_controlled_dry_run_package"
+ )
+ is True
+ and final_preflight_contract.get("executes_database_apply") is False
+ and final_preflight_contract.get("database_apply_authorized") is False
+ and final_preflight_contract.get("ready_for_database_apply_now") is False
+ and final_preflight_contract.get("signs_database_apply_authorization")
+ is False
+ and final_preflight_contract.get("writes_database") is False,
+ {
+ "permits_future_database_apply_controlled_dry_run_package": (
+ final_preflight_contract.get(
+ "permits_future_database_apply_controlled_dry_run_package"
+ )
+ ),
+ "database_apply_authorized": final_preflight_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": final_preflight_contract.get("writes_database"),
+ },
+ "abort_if_final_preflight_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_package_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "command_shape_preview_only": command_shape_preview_only,
+ "receipt_preview_only": receipt_preview_only,
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_package_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and future_preflight.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0
+ ),
+ "manual_review_mode": future_preflight.get("manual_review_mode"),
+ },
+ "route_failed_dry_run_package_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ package_ready = not waiting_checks
+ package_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_PACKAGE_READY"
+ if package_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_APPLY_FINAL_PREFLIGHT"
+ )
+ future_database_apply_controlled_dry_run_execution_receipt = {
+ "dry_run_package_id": package_id,
+ "dry_run_execution_receipt_id": receipt_id,
+ "source_controlled_apply_preflight_id": future_preflight.get(
+ "controlled_apply_preflight_id"
+ ),
+ "source_final_verifier_gate_id": controlled_preflight.get(
+ "source_final_verifier_gate_id"
+ ),
+ "status": package_status,
+ "ready_for_future_database_apply_controlled_dry_run_execution_receipt": (
+ package_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_receipt_closeout": (
+ package_ready
+ ),
+ "controlled_dry_run_package_ready": package_ready,
+ "dry_run_execution_performed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_package = {
+ "dry_run_package_id": package_id,
+ "authorization_material_type": "controlled_dry_run_package",
+ "source_controlled_apply_preflight_id": future_preflight.get(
+ "controlled_apply_preflight_id"
+ ),
+ "source_final_verifier_gate_id": controlled_preflight.get(
+ "source_final_verifier_gate_id"
+ ),
+ "source_authorization_evidence_execution_closeout_id": (
+ controlled_preflight.get(
+ "source_authorization_evidence_execution_closeout_id"
+ )
+ ),
+ "source_final_signable_request_package_id": controlled_preflight.get(
+ "source_final_signable_request_package_id"
+ ),
+ "status": package_status,
+ "ready_for_future_database_apply_controlled_dry_run_package": (
+ package_ready
+ ),
+ "controlled_dry_run_package_fields": dry_run_package_fields,
+ "controlled_dry_run_package_field_count": len(dry_run_package_fields),
+ "controlled_dry_run_acceptance_gates": dry_run_acceptance_gates,
+ "controlled_dry_run_acceptance_gate_count": len(
+ dry_run_acceptance_gates
+ ),
+ "dry_run_command_shape": command_shape,
+ "dry_run_command_shape_hash": command_shape_hash,
+ "dry_run_result_parser": dry_run_result_parser,
+ "dry_run_result_parser_count": 1,
+ "dry_run_result_parser_field_count": len(result_parser_fields),
+ "dry_run_execution_receipt_preview": dry_run_execution_receipt_preview,
+ "dry_run_execution_receipt_preview_count": 1,
+ "dry_run_execution_receipt_field_count": len(receipt_fields),
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "target_file": target_file,
+ "expected_sha256": expected_sha256,
+ "actual_sha256": actual_sha256,
+ "hash_matches": controlled_preflight.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_package_contract = {
+ "mode": "controlled_dry_run_package_and_receipt_preview_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-package"
+ ),
+ "source_final_preflight_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-apply-final-preflight"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_execution_receipt": (
+ package_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_PACKAGE_POLICY,
+ "result": package_status,
+ "success": bool(final_preflight.get("success")),
+ "generated_at": final_preflight.get("generated_at"),
+ "source_policy": final_preflight.get("policy"),
+ "stats": final_preflight.get("stats") or {},
+ "summary": {
+ "controlled_dry_run_package_ready_count": 1 if package_ready else 0,
+ "controlled_dry_run_package_check_count": len(checks),
+ "controlled_dry_run_package_pass_count": passed_count,
+ "controlled_dry_run_package_waiting_count": len(waiting_checks),
+ "controlled_apply_final_preflight_ready_count": summary.get(
+ "controlled_apply_final_preflight_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_check_count": summary.get(
+ "controlled_apply_final_preflight_check_count", 0
+ ),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "authorization_verifier_receipt_closeout_ready_count": summary.get(
+ "authorization_verifier_receipt_closeout_ready_count", 0
+ ),
+ "verifier_receipt_closeout_check_count": summary.get(
+ "verifier_receipt_closeout_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_package_count": 1,
+ "controlled_dry_run_package_field_count": len(dry_run_package_fields),
+ "controlled_dry_run_acceptance_gate_count": len(
+ dry_run_acceptance_gates
+ ),
+ "dry_run_execution_receipt_preview_count": 1,
+ "dry_run_execution_receipt_field_count": len(receipt_fields),
+ "dry_run_result_parser_count": 1,
+ "dry_run_result_parser_field_count": len(result_parser_fields),
+ "controlled_apply_final_preflight_count": summary.get(
+ "controlled_apply_final_preflight_count", 0
+ ),
+ "controlled_apply_final_preflight_field_count": summary.get(
+ "controlled_apply_final_preflight_field_count", 0
+ ),
+ "controlled_apply_final_preflight_acceptance_gate_count": summary.get(
+ "controlled_apply_final_preflight_acceptance_gate_count", 0
+ ),
+ "rollback_binding_count": summary.get("rollback_binding_count", 0),
+ "rollback_binding_field_count": summary.get(
+ "rollback_binding_field_count", 0
+ ),
+ "post_apply_verifier_binding_count": summary.get(
+ "post_apply_verifier_binding_count", 0
+ ),
+ "post_apply_verifier_binding_field_count": summary.get(
+ "post_apply_verifier_binding_field_count", 0
+ ),
+ "authorization_evidence_execution_closeout_field_count": summary.get(
+ "authorization_evidence_execution_closeout_field_count", 0
+ ),
+ "authorization_evidence_execution_closeout_acceptance_gate_count": (
+ summary.get(
+ "authorization_evidence_execution_closeout_acceptance_gate_count",
+ 0,
+ )
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get(
+ "same_run_truth_required_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_dry_run_execution_receipt": (
+ future_database_apply_controlled_dry_run_execution_receipt
+ ),
+ "controlled_dry_run_package": controlled_dry_run_package,
+ "controlled_dry_run_package_contract": controlled_dry_run_package_contract,
+ "controlled_dry_run_package_checks": checks,
+ "source_controlled_apply_final_preflight_summary": summary,
+ "source_controlled_apply_final_preflight_contract": final_preflight_contract,
+ "source_controlled_apply_final_preflight": controlled_preflight,
+ "source_database_apply_controlled_apply_final_preflight": future_preflight,
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_package": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this package to build a future controlled dry-run execution receipt closeout.",
+ "Keep the dry-run command shape non-executable until a dedicated execution receipt lane is explicit.",
+ "This package still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the dry-run receipt preview and parser without execution."""
+ package_result = build_pchome_auto_policy_db_apply_controlled_dry_run_package(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ future_receipt = (
+ package_result.get("future_database_apply_controlled_dry_run_execution_receipt")
+ or {}
+ )
+ package = package_result.get("controlled_dry_run_package") or {}
+ package_contract = package_result.get("controlled_dry_run_package_contract") or {}
+ summary = package_result.get("summary") or {}
+ safety = package_result.get("safety") or {}
+ receipt_preview = package.get("dry_run_execution_receipt_preview") or {}
+ command_shape = package.get("dry_run_command_shape") or {}
+ result_parser = package.get("dry_run_result_parser") or {}
+ rollback_binding = package.get("rollback_binding") or {}
+ verifier_binding = package.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_receipt_closeout_id(package_result)
+ receipt_closeout_fields = [
+ "receipt_closeout_id",
+ "source_dry_run_package_id",
+ "source_controlled_apply_preflight_id",
+ "source_dry_run_execution_receipt_id",
+ "dry_run_command_shape_hash",
+ "dry_run_result_parser_id",
+ "receipt_validation_status",
+ "target_migration_file",
+ "target_migration_sha256",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "abort_conditions",
+ ]
+ receipt_closeout_acceptance_gates = [
+ "controlled_dry_run_package_ready",
+ "source_chain_ids_match",
+ "result_parser_schema_complete",
+ "receipt_preview_schema_match",
+ "command_shape_hash_match",
+ "receipt_preview_not_executed",
+ "target_migration_hash_locked",
+ "no_secret_signature_or_database_apply",
+ "no_endpoint_sql_or_db_write",
+ "exception_only_failure_routing",
+ ]
+ receipt_validation_fields = [
+ "receipt_id",
+ "source_dry_run_package_id",
+ "dry_run_status",
+ "dry_run_command_shape_hash",
+ "execution_performed",
+ "stdout_included",
+ "stderr_included",
+ "database_apply_authorized",
+ ]
+ target_hash_locked = (
+ package.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(package.get("expected_sha256"))
+ and bool(package.get("actual_sha256"))
+ and package.get("expected_sha256") == package.get("actual_sha256")
+ and package.get("hash_matches") is True
+ and package.get("target_migration_hash_locked") is True
+ )
+ parser_schema_complete = (
+ bool(result_parser.get("parser_id"))
+ and result_parser.get("source_dry_run_package_id")
+ == package.get("dry_run_package_id")
+ and result_parser.get("expected_receipt_status")
+ == "preview_only_not_executed"
+ and result_parser.get("required_command_shape_hash")
+ == package.get("dry_run_command_shape_hash")
+ and result_parser.get("execution_required") is False
+ and result_parser.get("stdout_allowed") is False
+ and result_parser.get("stderr_allowed") is False
+ and result_parser.get("database_apply_authorized") is False
+ and int(result_parser.get("parser_field_count") or 0) == 10
+ )
+ required_receipt_fields = result_parser.get("required_receipt_fields") or []
+ receipt_schema_matches_parser = (
+ parser_schema_complete
+ and receipt_preview.get("source_dry_run_package_id")
+ == package.get("dry_run_package_id")
+ and receipt_preview.get("dry_run_status")
+ == result_parser.get("expected_receipt_status")
+ and receipt_preview.get("dry_run_command_shape_hash")
+ == result_parser.get("required_command_shape_hash")
+ and int(receipt_preview.get("receipt_field_count") or 0) == 8
+ and all(field in receipt_preview for field in required_receipt_fields)
+ )
+ command_hash_matches_receipt = (
+ bool(package.get("dry_run_command_shape_hash"))
+ and package.get("dry_run_command_shape_hash")
+ == receipt_preview.get("dry_run_command_shape_hash")
+ == result_parser.get("required_command_shape_hash")
+ )
+ command_shape_preview_only = (
+ command_shape.get("dry_run_only") is True
+ and command_shape.get("check_mode_only") is True
+ and command_shape.get("execution_allowed") is False
+ and command_shape.get("shell_command_included") is False
+ and command_shape.get("sql_included") is False
+ and command_shape.get("endpoint_execution_included") is False
+ and command_shape.get("database_write_included") is False
+ )
+ receipt_preview_only = (
+ receipt_preview.get("execution_performed") is False
+ and receipt_preview.get("stdout_included") is False
+ and receipt_preview.get("stderr_included") is False
+ and receipt_preview.get("database_apply_authorized") is False
+ and receipt_preview.get("executes_shell") is False
+ and receipt_preview.get("executes_endpoint") is False
+ and receipt_preview.get("executes_sql") is False
+ and receipt_preview.get("writes_database") is False
+ and receipt_preview.get("reads_secret") is False
+ )
+ receipt_validation_report = {
+ "receipt_id": receipt_preview.get("receipt_id"),
+ "source_dry_run_package_id": package.get("dry_run_package_id"),
+ "dry_run_status": receipt_preview.get("dry_run_status"),
+ "dry_run_command_shape_hash": receipt_preview.get(
+ "dry_run_command_shape_hash"
+ ),
+ "execution_performed": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "database_apply_authorized": False,
+ "receipt_validation_status": (
+ "preview_validated_not_executed"
+ if receipt_schema_matches_parser and command_hash_matches_receipt
+ else "waiting_for_receipt_parser_schema_match"
+ ),
+ "receipt_validation_field_count": len(receipt_validation_fields),
+ "receipt_validation_fields": receipt_validation_fields,
+ "executes_shell": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret": False,
+ }
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("executes_authorization_evidence") is False
+ and safety.get("executes_database_apply") is False
+ and package.get("accepts_plaintext_secret") is False
+ and package.get("reads_secret_in_preview") is False
+ and package.get("signature_material_included") is False
+ and package.get("secret_material_included") is False
+ and package.get("signs_database_apply_authorization") is False
+ and package.get("executes_authorization_evidence") is False
+ and package.get("executes_database_apply") is False
+ and package.get("executes_endpoint_in_preview") is False
+ and package.get("executes_sql_in_preview") is False
+ and package.get("writes_database_in_preview") is False
+ and command_shape_preview_only
+ and receipt_preview_only
+ )
+ checks = [
+ _controlled_dry_run_receipt_closeout_check(
+ "controlled_dry_run_package_ready",
+ package_result.get("result") == "DB_APPLY_CONTROLLED_DRY_RUN_PACKAGE_READY"
+ and future_receipt.get(
+ "ready_for_future_database_apply_controlled_dry_run_execution_receipt"
+ )
+ is True
+ and future_receipt.get(
+ "can_enter_future_database_apply_controlled_dry_run_receipt_closeout"
+ )
+ is True
+ and package.get("ready_for_future_database_apply_controlled_dry_run_package")
+ is True,
+ {
+ "result": package_result.get("result"),
+ "ready_for_future_database_apply_controlled_dry_run_execution_receipt": (
+ future_receipt.get(
+ "ready_for_future_database_apply_controlled_dry_run_execution_receipt"
+ )
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_receipt_closeout": (
+ future_receipt.get(
+ "can_enter_future_database_apply_controlled_dry_run_receipt_closeout"
+ )
+ ),
+ },
+ "wait_for_controlled_dry_run_package",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "source_chain_ids_present",
+ bool(package.get("dry_run_package_id"))
+ and bool(future_receipt.get("dry_run_execution_receipt_id"))
+ and bool(package.get("source_controlled_apply_preflight_id"))
+ and bool(package.get("source_final_verifier_gate_id"))
+ and bool(package.get("source_authorization_evidence_execution_closeout_id")),
+ {
+ "dry_run_package_id": package.get("dry_run_package_id"),
+ "dry_run_execution_receipt_id": future_receipt.get(
+ "dry_run_execution_receipt_id"
+ ),
+ "source_controlled_apply_preflight_id": package.get(
+ "source_controlled_apply_preflight_id"
+ ),
+ },
+ "wait_for_controlled_dry_run_source_chain_ids",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "controlled_dry_run_receipt_closeout_contract_complete",
+ len(receipt_closeout_fields) == 12
+ and len(receipt_closeout_acceptance_gates) == 10
+ and len(receipt_validation_fields) == 8
+ and "dry_run_result_parser_id" in receipt_closeout_fields
+ and "receipt_preview_schema_match" in receipt_closeout_acceptance_gates,
+ {
+ "receipt_closeout_field_count": len(receipt_closeout_fields),
+ "receipt_closeout_acceptance_gate_count": len(
+ receipt_closeout_acceptance_gates
+ ),
+ "receipt_validation_field_count": len(receipt_validation_fields),
+ },
+ "wait_for_receipt_closeout_contract",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "dry_run_result_parser_schema_complete",
+ parser_schema_complete,
+ {
+ "parser_id": result_parser.get("parser_id"),
+ "parser_field_count": result_parser.get("parser_field_count"),
+ "execution_required": result_parser.get("execution_required"),
+ "database_apply_authorized": result_parser.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_dry_run_result_parser_schema",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "receipt_preview_schema_matches_parser",
+ receipt_schema_matches_parser,
+ {
+ "receipt_id": receipt_preview.get("receipt_id"),
+ "dry_run_status": receipt_preview.get("dry_run_status"),
+ "required_receipt_field_count": len(required_receipt_fields),
+ "receipt_field_count": receipt_preview.get("receipt_field_count"),
+ },
+ "wait_for_receipt_preview_parser_match",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "command_shape_hash_matches_receipt",
+ command_hash_matches_receipt,
+ {
+ "dry_run_command_shape_hash_present": bool(
+ package.get("dry_run_command_shape_hash")
+ ),
+ "receipt_hash_matches": (
+ package.get("dry_run_command_shape_hash")
+ == receipt_preview.get("dry_run_command_shape_hash")
+ ),
+ },
+ "abort_on_dry_run_command_shape_hash_mismatch",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "receipt_preview_only_not_executed",
+ receipt_preview_only and command_shape_preview_only,
+ {
+ "execution_performed": receipt_preview.get("execution_performed"),
+ "stdout_included": receipt_preview.get("stdout_included"),
+ "stderr_included": receipt_preview.get("stderr_included"),
+ "execution_allowed": command_shape.get("execution_allowed"),
+ },
+ "abort_if_receipt_or_command_shape_indicates_execution",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "rollback_and_post_apply_verifier_bindings_carried_forward",
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": package.get("target_file"),
+ "expected_sha256_present": bool(package.get("expected_sha256")),
+ "actual_sha256_present": bool(package.get("actual_sha256")),
+ "hash_matches": package.get("hash_matches"),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "package_contract_blocks_database_apply",
+ package_contract.get(
+ "permits_future_database_apply_controlled_dry_run_execution_receipt"
+ )
+ is True
+ and package_contract.get("executes_database_apply") is False
+ and package_contract.get("database_apply_authorized") is False
+ and package_contract.get("ready_for_database_apply_now") is False
+ and package_contract.get("signs_database_apply_authorization") is False
+ and package_contract.get("writes_database") is False,
+ {
+ "permits_future_database_apply_controlled_dry_run_execution_receipt": (
+ package_contract.get(
+ "permits_future_database_apply_controlled_dry_run_execution_receipt"
+ )
+ ),
+ "database_apply_authorized": package_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": package_contract.get("writes_database"),
+ },
+ "abort_if_dry_run_package_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "command_shape_preview_only": command_shape_preview_only,
+ "receipt_preview_only": receipt_preview_only,
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_receipt_closeout_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0
+ and future_receipt.get("manual_review_mode") == "exception_only",
+ {
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0
+ ),
+ "manual_review_mode": future_receipt.get("manual_review_mode"),
+ },
+ "route_failed_receipt_closeout_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_PACKAGE"
+ )
+ future_database_apply_controlled_dry_run_result_parser_verification = {
+ "receipt_closeout_id": closeout_id,
+ "source_dry_run_package_id": package.get("dry_run_package_id"),
+ "source_dry_run_execution_receipt_id": future_receipt.get(
+ "dry_run_execution_receipt_id"
+ ),
+ "dry_run_result_parser_id": result_parser.get("parser_id"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_result_parser_verification": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_runner_readiness": (
+ closeout_ready
+ ),
+ "controlled_dry_run_receipt_closeout_ready": closeout_ready,
+ "receipt_validation_status": receipt_validation_report.get(
+ "receipt_validation_status"
+ ),
+ "dry_run_execution_performed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_receipt_closeout = {
+ "receipt_closeout_id": closeout_id,
+ "authorization_material_type": "controlled_dry_run_receipt_closeout",
+ "source_dry_run_package_id": package.get("dry_run_package_id"),
+ "source_controlled_apply_preflight_id": package.get(
+ "source_controlled_apply_preflight_id"
+ ),
+ "source_dry_run_execution_receipt_id": future_receipt.get(
+ "dry_run_execution_receipt_id"
+ ),
+ "dry_run_result_parser_id": result_parser.get("parser_id"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_receipt_closeout": (
+ closeout_ready
+ ),
+ "controlled_dry_run_receipt_closeout_fields": receipt_closeout_fields,
+ "controlled_dry_run_receipt_closeout_field_count": len(
+ receipt_closeout_fields
+ ),
+ "controlled_dry_run_receipt_closeout_acceptance_gates": (
+ receipt_closeout_acceptance_gates
+ ),
+ "controlled_dry_run_receipt_closeout_acceptance_gate_count": len(
+ receipt_closeout_acceptance_gates
+ ),
+ "dry_run_result_parser": result_parser,
+ "dry_run_result_parser_count": 1,
+ "dry_run_result_parser_field_count": int(
+ result_parser.get("parser_field_count") or 0
+ ),
+ "receipt_validation_report": receipt_validation_report,
+ "receipt_validation_report_count": 1,
+ "receipt_validation_field_count": len(receipt_validation_fields),
+ "dry_run_command_shape_hash": package.get("dry_run_command_shape_hash"),
+ "target_file": package.get("target_file"),
+ "expected_sha256": package.get("expected_sha256"),
+ "actual_sha256": package.get("actual_sha256"),
+ "hash_matches": package.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "receipt_preview_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_receipt_closeout_contract = {
+ "mode": "controlled_dry_run_receipt_closeout_and_result_parser_verification_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-receipt-closeout"
+ ),
+ "source_dry_run_package_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-package"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_runner_readiness": (
+ closeout_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(package_result.get("success")),
+ "generated_at": package_result.get("generated_at"),
+ "source_policy": package_result.get("policy"),
+ "stats": package_result.get("stats") or {},
+ "summary": {
+ "controlled_dry_run_receipt_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_receipt_closeout_check_count": len(checks),
+ "controlled_dry_run_receipt_closeout_pass_count": passed_count,
+ "controlled_dry_run_receipt_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_package_ready_count": summary.get(
+ "controlled_dry_run_package_ready_count", 0
+ ),
+ "controlled_dry_run_package_check_count": summary.get(
+ "controlled_dry_run_package_check_count", 0
+ ),
+ "controlled_apply_final_preflight_ready_count": summary.get(
+ "controlled_apply_final_preflight_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_check_count": summary.get(
+ "controlled_apply_final_preflight_check_count", 0
+ ),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "authorization_verifier_receipt_closeout_ready_count": summary.get(
+ "authorization_verifier_receipt_closeout_ready_count", 0
+ ),
+ "verifier_receipt_closeout_check_count": summary.get(
+ "verifier_receipt_closeout_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_receipt_closeout_count": 1,
+ "controlled_dry_run_receipt_closeout_field_count": len(
+ receipt_closeout_fields
+ ),
+ "controlled_dry_run_receipt_closeout_acceptance_gate_count": len(
+ receipt_closeout_acceptance_gates
+ ),
+ "dry_run_result_parser_count": 1,
+ "dry_run_result_parser_field_count": int(
+ result_parser.get("parser_field_count") or 0
+ ),
+ "receipt_validation_report_count": 1,
+ "receipt_validation_field_count": len(receipt_validation_fields),
+ "dry_run_execution_receipt_preview_count": summary.get(
+ "dry_run_execution_receipt_preview_count", 0
+ ),
+ "dry_run_execution_receipt_field_count": summary.get(
+ "dry_run_execution_receipt_field_count", 0
+ ),
+ "controlled_dry_run_package_count": summary.get(
+ "controlled_dry_run_package_count", 0
+ ),
+ "controlled_dry_run_package_field_count": summary.get(
+ "controlled_dry_run_package_field_count", 0
+ ),
+ "controlled_dry_run_acceptance_gate_count": summary.get(
+ "controlled_dry_run_acceptance_gate_count", 0
+ ),
+ "rollback_binding_count": summary.get("rollback_binding_count", 0),
+ "post_apply_verifier_binding_count": summary.get(
+ "post_apply_verifier_binding_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get(
+ "same_run_truth_required_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_dry_run_result_parser_verification": (
+ future_database_apply_controlled_dry_run_result_parser_verification
+ ),
+ "controlled_dry_run_receipt_closeout": controlled_dry_run_receipt_closeout,
+ "controlled_dry_run_receipt_closeout_contract": (
+ controlled_dry_run_receipt_closeout_contract
+ ),
+ "controlled_dry_run_receipt_closeout_checks": checks,
+ "source_controlled_dry_run_package_summary": summary,
+ "source_controlled_dry_run_package_contract": package_contract,
+ "source_controlled_dry_run_package": package,
+ "source_database_apply_controlled_dry_run_execution_receipt": (
+ future_receipt
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_receipt_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run runner readiness package.",
+ "Keep result parsing bound to receipt preview fields and command-shape hash before any execution lane.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_runner_readiness(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Bind a future dry-run runner execution plan without authorizing it."""
+ receipt_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_verification = (
+ receipt_closeout_result.get(
+ "future_database_apply_controlled_dry_run_result_parser_verification"
+ )
+ or {}
+ )
+ receipt_closeout = (
+ receipt_closeout_result.get("controlled_dry_run_receipt_closeout") or {}
+ )
+ closeout_contract = (
+ receipt_closeout_result.get("controlled_dry_run_receipt_closeout_contract")
+ or {}
+ )
+ summary = receipt_closeout_result.get("summary") or {}
+ safety = receipt_closeout_result.get("safety") or {}
+ result_parser = receipt_closeout.get("dry_run_result_parser") or {}
+ validation = receipt_closeout.get("receipt_validation_report") or {}
+ rollback_binding = receipt_closeout.get("rollback_binding") or {}
+ verifier_binding = receipt_closeout.get("post_apply_verifier_binding") or {}
+ runner_readiness_id = _db_apply_controlled_dry_run_runner_readiness_id(
+ receipt_closeout_result
+ )
+ execution_plan_binding_id = f"{runner_readiness_id}-execution-plan-binding"
+ runner_readiness_fields = [
+ "runner_readiness_id",
+ "source_receipt_closeout_id",
+ "source_dry_run_package_id",
+ "source_controlled_apply_preflight_id",
+ "source_dry_run_execution_receipt_id",
+ "dry_run_result_parser_id",
+ "receipt_validation_status",
+ "dry_run_command_shape_hash",
+ "execution_plan_binding_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ ]
+ runner_readiness_acceptance_gates = [
+ "receipt_closeout_ready",
+ "source_chain_ids_match",
+ "result_parser_verified",
+ "receipt_validation_report_ready",
+ "command_shape_hash_bound",
+ "execution_plan_binding_preview_only",
+ "runner_execution_gate_closed",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "closeout_contract_blocks_database_apply",
+ ]
+ execution_plan_binding_fields = [
+ "execution_plan_binding_id",
+ "source_receipt_closeout_id",
+ "source_dry_run_package_id",
+ "dry_run_command_shape_hash",
+ "runner_mode",
+ "plan_status",
+ "dry_run_only",
+ "check_mode_only",
+ "shell_execution_included",
+ "endpoint_execution_included",
+ "sql_execution_included",
+ "database_write_included",
+ ]
+ target_hash_locked = (
+ receipt_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(receipt_closeout.get("expected_sha256"))
+ and bool(receipt_closeout.get("actual_sha256"))
+ and receipt_closeout.get("expected_sha256")
+ == receipt_closeout.get("actual_sha256")
+ and receipt_closeout.get("hash_matches") is True
+ and receipt_closeout.get("target_migration_hash_locked") is True
+ )
+ result_parser_verified = (
+ bool(result_parser.get("parser_id"))
+ and result_parser.get("parser_id")
+ == receipt_closeout.get("dry_run_result_parser_id")
+ and result_parser.get("expected_receipt_status")
+ == "preview_only_not_executed"
+ and result_parser.get("required_command_shape_hash")
+ == receipt_closeout.get("dry_run_command_shape_hash")
+ and result_parser.get("execution_required") is False
+ and result_parser.get("stdout_allowed") is False
+ and result_parser.get("stderr_allowed") is False
+ and result_parser.get("database_apply_authorized") is False
+ and int(result_parser.get("parser_field_count") or 0) == 10
+ )
+ receipt_validation_ready = (
+ validation.get("receipt_validation_status")
+ == "preview_validated_not_executed"
+ and validation.get("dry_run_command_shape_hash")
+ == receipt_closeout.get("dry_run_command_shape_hash")
+ and validation.get("execution_performed") is False
+ and validation.get("stdout_included") is False
+ and validation.get("stderr_included") is False
+ and validation.get("database_apply_authorized") is False
+ and validation.get("executes_shell") is False
+ and validation.get("executes_endpoint") is False
+ and validation.get("executes_sql") is False
+ and validation.get("writes_database") is False
+ and validation.get("reads_secret") is False
+ and int(validation.get("receipt_validation_field_count") or 0) == 8
+ )
+ command_shape_hash_bound = (
+ bool(receipt_closeout.get("dry_run_command_shape_hash"))
+ and receipt_closeout.get("dry_run_command_shape_hash")
+ == validation.get("dry_run_command_shape_hash")
+ == result_parser.get("required_command_shape_hash")
+ )
+ execution_plan_binding = {
+ "execution_plan_binding_id": execution_plan_binding_id,
+ "source_runner_readiness_id": runner_readiness_id,
+ "source_receipt_closeout_id": receipt_closeout.get("receipt_closeout_id"),
+ "source_dry_run_package_id": receipt_closeout.get("source_dry_run_package_id"),
+ "dry_run_command_shape_hash": receipt_closeout.get(
+ "dry_run_command_shape_hash"
+ ),
+ "runner_mode": "future_controlled_dry_run_runner_readiness_only",
+ "plan_status": "plan_binding_preview_not_executable",
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "runner_execution_authorized": False,
+ "shell_execution_included": False,
+ "endpoint_execution_included": False,
+ "sql_execution_included": False,
+ "database_write_included": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "database_apply_authorized": False,
+ "ready_for_database_apply_now": False,
+ "execution_plan_binding_field_count": len(execution_plan_binding_fields),
+ "execution_plan_binding_fields": execution_plan_binding_fields,
+ }
+ execution_plan_preview_only = (
+ execution_plan_binding.get("dry_run_only") is True
+ and execution_plan_binding.get("check_mode_only") is True
+ and execution_plan_binding.get("execution_authorized") is False
+ and execution_plan_binding.get("dry_run_execution_authorized") is False
+ and execution_plan_binding.get("runner_execution_authorized") is False
+ and execution_plan_binding.get("shell_execution_included") is False
+ and execution_plan_binding.get("endpoint_execution_included") is False
+ and execution_plan_binding.get("sql_execution_included") is False
+ and execution_plan_binding.get("database_write_included") is False
+ and execution_plan_binding.get("stdout_capture_allowed") is False
+ and execution_plan_binding.get("stderr_capture_allowed") is False
+ and execution_plan_binding.get("database_apply_authorized") is False
+ )
+ runner_execution_gate_closed = (
+ future_verification.get(
+ "ready_for_future_database_apply_controlled_dry_run_result_parser_verification"
+ )
+ is True
+ and future_verification.get(
+ "can_enter_future_database_apply_controlled_dry_run_runner_readiness"
+ )
+ is True
+ and future_verification.get("dry_run_execution_performed") is False
+ and future_verification.get("database_apply_authorized") is False
+ and future_verification.get("executes_database_apply") is False
+ and future_verification.get("executes_endpoint") is False
+ and future_verification.get("executes_sql") is False
+ and future_verification.get("writes_database") is False
+ and execution_plan_preview_only
+ )
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("executes_authorization_evidence") is False
+ and safety.get("executes_database_apply") is False
+ and receipt_closeout.get("accepts_plaintext_secret") is False
+ and receipt_closeout.get("reads_secret_in_preview") is False
+ and receipt_closeout.get("signature_material_included") is False
+ and receipt_closeout.get("secret_material_included") is False
+ and receipt_closeout.get("signs_database_apply_authorization") is False
+ and receipt_closeout.get("executes_authorization_evidence") is False
+ and receipt_closeout.get("executes_database_apply") is False
+ and receipt_closeout.get("executes_endpoint_in_preview") is False
+ and receipt_closeout.get("executes_sql_in_preview") is False
+ and receipt_closeout.get("writes_database_in_preview") is False
+ and receipt_validation_ready
+ and execution_plan_preview_only
+ )
+ checks = [
+ _controlled_dry_run_runner_readiness_check(
+ "receipt_closeout_ready",
+ receipt_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_CLOSEOUT_READY"
+ and future_verification.get(
+ "ready_for_future_database_apply_controlled_dry_run_result_parser_verification"
+ )
+ is True
+ and future_verification.get(
+ "can_enter_future_database_apply_controlled_dry_run_runner_readiness"
+ )
+ is True
+ and receipt_closeout.get(
+ "ready_for_future_database_apply_controlled_dry_run_receipt_closeout"
+ )
+ is True,
+ {
+ "result": receipt_closeout_result.get("result"),
+ "ready_for_future_database_apply_controlled_dry_run_result_parser_verification": (
+ future_verification.get(
+ "ready_for_future_database_apply_controlled_dry_run_result_parser_verification"
+ )
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_runner_readiness": (
+ future_verification.get(
+ "can_enter_future_database_apply_controlled_dry_run_runner_readiness"
+ )
+ ),
+ },
+ "wait_for_controlled_dry_run_receipt_closeout",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "source_chain_ids_present",
+ bool(receipt_closeout.get("receipt_closeout_id"))
+ and bool(receipt_closeout.get("source_dry_run_package_id"))
+ and bool(receipt_closeout.get("source_controlled_apply_preflight_id"))
+ and bool(receipt_closeout.get("source_dry_run_execution_receipt_id"))
+ and bool(receipt_closeout.get("dry_run_result_parser_id")),
+ {
+ "receipt_closeout_id": receipt_closeout.get("receipt_closeout_id"),
+ "source_dry_run_package_id": receipt_closeout.get(
+ "source_dry_run_package_id"
+ ),
+ "dry_run_result_parser_id": receipt_closeout.get(
+ "dry_run_result_parser_id"
+ ),
+ },
+ "wait_for_runner_readiness_source_chain_ids",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "controlled_dry_run_runner_readiness_contract_complete",
+ len(runner_readiness_fields) == 12
+ and len(runner_readiness_acceptance_gates) == 10
+ and len(execution_plan_binding_fields) == 12
+ and "execution_plan_binding_id" in runner_readiness_fields
+ and "execution_plan_binding_preview_only"
+ in runner_readiness_acceptance_gates,
+ {
+ "runner_readiness_field_count": len(runner_readiness_fields),
+ "runner_readiness_acceptance_gate_count": len(
+ runner_readiness_acceptance_gates
+ ),
+ "execution_plan_binding_field_count": len(
+ execution_plan_binding_fields
+ ),
+ },
+ "wait_for_runner_readiness_contract",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "dry_run_result_parser_verified",
+ result_parser_verified,
+ {
+ "parser_id": result_parser.get("parser_id"),
+ "parser_field_count": result_parser.get("parser_field_count"),
+ "execution_required": result_parser.get("execution_required"),
+ "database_apply_authorized": result_parser.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_dry_run_result_parser_verification",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "receipt_validation_report_ready",
+ receipt_validation_ready,
+ {
+ "receipt_validation_status": validation.get(
+ "receipt_validation_status"
+ ),
+ "receipt_validation_field_count": validation.get(
+ "receipt_validation_field_count"
+ ),
+ "execution_performed": validation.get("execution_performed"),
+ },
+ "wait_for_receipt_validation_report",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "command_shape_hash_bound",
+ command_shape_hash_bound,
+ {
+ "dry_run_command_shape_hash_present": bool(
+ receipt_closeout.get("dry_run_command_shape_hash")
+ ),
+ "parser_hash_matches": (
+ receipt_closeout.get("dry_run_command_shape_hash")
+ == result_parser.get("required_command_shape_hash")
+ ),
+ "validation_hash_matches": (
+ receipt_closeout.get("dry_run_command_shape_hash")
+ == validation.get("dry_run_command_shape_hash")
+ ),
+ },
+ "abort_on_runner_command_shape_hash_mismatch",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "execution_plan_binding_preview_only",
+ execution_plan_preview_only,
+ {
+ "execution_authorized": execution_plan_binding.get(
+ "execution_authorized"
+ ),
+ "runner_execution_authorized": execution_plan_binding.get(
+ "runner_execution_authorized"
+ ),
+ "database_write_included": execution_plan_binding.get(
+ "database_write_included"
+ ),
+ },
+ "abort_if_execution_plan_binding_is_executable",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "runner_execution_gate_closed",
+ runner_execution_gate_closed,
+ {
+ "dry_run_execution_performed": future_verification.get(
+ "dry_run_execution_performed"
+ ),
+ "database_apply_authorized": future_verification.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": future_verification.get("writes_database"),
+ },
+ "abort_if_runner_execution_gate_opens",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": receipt_closeout.get("target_file"),
+ "expected_sha256_present": bool(receipt_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(receipt_closeout.get("actual_sha256")),
+ "hash_matches": receipt_closeout.get("hash_matches"),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "rollback_and_post_apply_verifier_bindings_carried_forward",
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "closeout_contract_blocks_database_apply",
+ closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_runner_readiness"
+ )
+ is True
+ and closeout_contract.get("executes_database_apply") is False
+ and closeout_contract.get("database_apply_authorized") is False
+ and closeout_contract.get("ready_for_database_apply_now") is False
+ and closeout_contract.get("signs_database_apply_authorization") is False
+ and closeout_contract.get("writes_database") is False,
+ {
+ "permits_future_database_apply_controlled_dry_run_runner_readiness": (
+ closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_runner_readiness"
+ )
+ ),
+ "database_apply_authorized": closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": closeout_contract.get("writes_database"),
+ },
+ "abort_if_receipt_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_runner_readiness_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "execution_plan_preview_only": execution_plan_preview_only,
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ runner_ready = not waiting_checks
+ runner_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_READINESS_READY"
+ if runner_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_execution_plan_binding = {
+ "runner_readiness_id": runner_readiness_id,
+ "execution_plan_binding_id": execution_plan_binding_id,
+ "source_receipt_closeout_id": receipt_closeout.get("receipt_closeout_id"),
+ "source_dry_run_package_id": receipt_closeout.get("source_dry_run_package_id"),
+ "source_dry_run_execution_receipt_id": receipt_closeout.get(
+ "source_dry_run_execution_receipt_id"
+ ),
+ "status": runner_status,
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_binding": (
+ runner_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_execution_plan_closeout": (
+ runner_ready
+ ),
+ "controlled_dry_run_runner_readiness_ready": runner_ready,
+ "execution_plan_bound": runner_ready,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_runner_readiness = {
+ "runner_readiness_id": runner_readiness_id,
+ "authorization_material_type": "controlled_dry_run_runner_readiness",
+ "source_receipt_closeout_id": receipt_closeout.get("receipt_closeout_id"),
+ "source_dry_run_package_id": receipt_closeout.get("source_dry_run_package_id"),
+ "source_controlled_apply_preflight_id": receipt_closeout.get(
+ "source_controlled_apply_preflight_id"
+ ),
+ "source_dry_run_execution_receipt_id": receipt_closeout.get(
+ "source_dry_run_execution_receipt_id"
+ ),
+ "dry_run_result_parser_id": receipt_closeout.get(
+ "dry_run_result_parser_id"
+ ),
+ "status": runner_status,
+ "ready_for_future_database_apply_controlled_dry_run_runner_readiness": (
+ runner_ready
+ ),
+ "controlled_dry_run_runner_readiness_fields": runner_readiness_fields,
+ "controlled_dry_run_runner_readiness_field_count": len(
+ runner_readiness_fields
+ ),
+ "controlled_dry_run_runner_readiness_acceptance_gates": (
+ runner_readiness_acceptance_gates
+ ),
+ "controlled_dry_run_runner_readiness_acceptance_gate_count": len(
+ runner_readiness_acceptance_gates
+ ),
+ "execution_plan_binding": execution_plan_binding,
+ "execution_plan_binding_count": 1,
+ "execution_plan_binding_field_count": len(execution_plan_binding_fields),
+ "dry_run_result_parser": result_parser,
+ "dry_run_result_parser_count": 1,
+ "receipt_validation_report": validation,
+ "receipt_validation_report_count": 1,
+ "dry_run_command_shape_hash": receipt_closeout.get(
+ "dry_run_command_shape_hash"
+ ),
+ "target_file": receipt_closeout.get("target_file"),
+ "expected_sha256": receipt_closeout.get("expected_sha256"),
+ "actual_sha256": receipt_closeout.get("actual_sha256"),
+ "hash_matches": receipt_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "runner_readiness_only": True,
+ "execution_plan_preview_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_runner_readiness_contract = {
+ "mode": "controlled_dry_run_runner_readiness_and_execution_plan_binding_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-runner-readiness"
+ ),
+ "source_receipt_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-receipt-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_execution_plan_binding": (
+ runner_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_READINESS_POLICY,
+ "result": runner_status,
+ "success": bool(receipt_closeout_result.get("success")),
+ "generated_at": receipt_closeout_result.get("generated_at"),
+ "source_policy": receipt_closeout_result.get("policy"),
+ "stats": receipt_closeout_result.get("stats") or {},
+ "summary": {
+ "controlled_dry_run_runner_readiness_ready_count": (
+ 1 if runner_ready else 0
+ ),
+ "controlled_dry_run_runner_readiness_check_count": len(checks),
+ "controlled_dry_run_runner_readiness_pass_count": passed_count,
+ "controlled_dry_run_runner_readiness_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_receipt_closeout_ready_count": summary.get(
+ "controlled_dry_run_receipt_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_check_count": summary.get(
+ "controlled_dry_run_receipt_closeout_check_count", 0
+ ),
+ "controlled_dry_run_package_ready_count": summary.get(
+ "controlled_dry_run_package_ready_count", 0
+ ),
+ "controlled_dry_run_package_check_count": summary.get(
+ "controlled_dry_run_package_check_count", 0
+ ),
+ "controlled_apply_final_preflight_ready_count": summary.get(
+ "controlled_apply_final_preflight_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_check_count": summary.get(
+ "controlled_apply_final_preflight_check_count", 0
+ ),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_runner_readiness_count": 1,
+ "controlled_dry_run_runner_readiness_field_count": len(
+ runner_readiness_fields
+ ),
+ "controlled_dry_run_runner_readiness_acceptance_gate_count": len(
+ runner_readiness_acceptance_gates
+ ),
+ "execution_plan_binding_count": 1,
+ "execution_plan_binding_field_count": len(execution_plan_binding_fields),
+ "dry_run_result_parser_count": summary.get(
+ "dry_run_result_parser_count", 0
+ ),
+ "dry_run_result_parser_field_count": summary.get(
+ "dry_run_result_parser_field_count", 0
+ ),
+ "receipt_validation_report_count": summary.get(
+ "receipt_validation_report_count", 0
+ ),
+ "receipt_validation_field_count": summary.get(
+ "receipt_validation_field_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_count": summary.get(
+ "controlled_dry_run_receipt_closeout_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_field_count": summary.get(
+ "controlled_dry_run_receipt_closeout_field_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_acceptance_gate_count": summary.get(
+ "controlled_dry_run_receipt_closeout_acceptance_gate_count", 0
+ ),
+ "rollback_binding_count": summary.get("rollback_binding_count", 0),
+ "post_apply_verifier_binding_count": summary.get(
+ "post_apply_verifier_binding_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get(
+ "same_run_truth_required_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_dry_run_execution_plan_binding": (
+ future_database_apply_controlled_dry_run_execution_plan_binding
+ ),
+ "controlled_dry_run_runner_readiness": controlled_dry_run_runner_readiness,
+ "controlled_dry_run_runner_readiness_contract": (
+ controlled_dry_run_runner_readiness_contract
+ ),
+ "controlled_dry_run_runner_readiness_checks": checks,
+ "source_controlled_dry_run_receipt_closeout_summary": summary,
+ "source_controlled_dry_run_receipt_closeout_contract": closeout_contract,
+ "source_controlled_dry_run_receipt_closeout": receipt_closeout,
+ "source_database_apply_controlled_dry_run_result_parser_verification": (
+ future_verification
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_runner_readiness": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this readiness package to build a future controlled dry-run execution plan closeout.",
+ "Keep runner execution authorization closed until a dedicated execution receipt lane is explicit.",
+ "This readiness package still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out a future dry-run execution plan as a non-executable artifact."""
+ runner_readiness_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_readiness(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_plan = (
+ runner_readiness_result.get(
+ "future_database_apply_controlled_dry_run_execution_plan_binding"
+ )
+ or {}
+ )
+ runner = (
+ runner_readiness_result.get("controlled_dry_run_runner_readiness") or {}
+ )
+ runner_contract = (
+ runner_readiness_result.get("controlled_dry_run_runner_readiness_contract")
+ or {}
+ )
+ summary = runner_readiness_result.get("summary") or {}
+ safety = runner_readiness_result.get("safety") or {}
+ execution_plan_binding = runner.get("execution_plan_binding") or {}
+ validation = runner.get("receipt_validation_report") or {}
+ result_parser = runner.get("dry_run_result_parser") or {}
+ rollback_binding = runner.get("rollback_binding") or {}
+ verifier_binding = runner.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_execution_plan_closeout_id(
+ runner_readiness_result
+ )
+ artifact_id = f"{closeout_id}-non-executable-command-artifact"
+ execution_plan_closeout_fields = [
+ "execution_plan_closeout_id",
+ "source_runner_readiness_id",
+ "source_execution_plan_binding_id",
+ "source_receipt_closeout_id",
+ "source_dry_run_package_id",
+ "dry_run_command_shape_hash",
+ "non_executable_command_artifact_id",
+ "non_executable_command_artifact_sha256",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "abort_conditions",
+ ]
+ execution_plan_closeout_acceptance_gates = [
+ "runner_readiness_ready",
+ "source_chain_ids_match",
+ "execution_plan_binding_preview_only",
+ "non_executable_command_artifact_bound",
+ "command_artifact_hash_locked",
+ "runner_execution_gate_closed",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "runner_contract_blocks_database_apply",
+ "no_secret_signature_or_database_apply",
+ ]
+ non_executable_command_artifact_fields = [
+ "artifact_id",
+ "source_execution_plan_binding_id",
+ "dry_run_command_shape_hash",
+ "artifact_type",
+ "shell_command_included",
+ "endpoint_execution_included",
+ "sql_execution_included",
+ "database_write_included",
+ "execution_authorized",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_runner_readiness_not_ready",
+ "abort_if_execution_plan_becomes_executable",
+ "abort_if_command_artifact_contains_shell_command_or_argv",
+ "abort_if_command_artifact_hash_missing",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_runner_contract_authorizes_database_apply",
+ "abort_if_any_endpoint_sql_database_write_or_signature_is_requested",
+ ]
+ non_executable_command_artifact = {
+ "artifact_id": artifact_id,
+ "source_runner_readiness_id": runner.get("runner_readiness_id"),
+ "source_execution_plan_binding_id": execution_plan_binding.get(
+ "execution_plan_binding_id"
+ ),
+ "source_receipt_closeout_id": runner.get("source_receipt_closeout_id"),
+ "source_dry_run_package_id": runner.get("source_dry_run_package_id"),
+ "dry_run_command_shape_hash": runner.get("dry_run_command_shape_hash"),
+ "artifact_type": "non_executable_command_artifact_reference",
+ "command_text_included": False,
+ "argv_included": False,
+ "shell_command_included": False,
+ "endpoint_execution_included": False,
+ "sql_execution_included": False,
+ "database_write_included": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "runner_execution_authorized": False,
+ "database_apply_authorized": False,
+ "ready_for_database_apply_now": False,
+ "non_executable_command_artifact_field_count": len(
+ non_executable_command_artifact_fields
+ ),
+ "non_executable_command_artifact_fields": (
+ non_executable_command_artifact_fields
+ ),
+ }
+ artifact_hash_payload = {
+ key: value
+ for key, value in non_executable_command_artifact.items()
+ if key != "non_executable_command_artifact_sha256"
+ }
+ artifact_sha256 = hashlib.sha256(
+ json.dumps(artifact_hash_payload, sort_keys=True).encode("utf-8")
+ ).hexdigest()
+ non_executable_command_artifact[
+ "non_executable_command_artifact_sha256"
+ ] = artifact_sha256
+ execution_plan_preview_only = (
+ execution_plan_binding.get("dry_run_only") is True
+ and execution_plan_binding.get("check_mode_only") is True
+ and execution_plan_binding.get("execution_authorized") is False
+ and execution_plan_binding.get("dry_run_execution_authorized") is False
+ and execution_plan_binding.get("runner_execution_authorized") is False
+ and execution_plan_binding.get("shell_execution_included") is False
+ and execution_plan_binding.get("endpoint_execution_included") is False
+ and execution_plan_binding.get("sql_execution_included") is False
+ and execution_plan_binding.get("database_write_included") is False
+ and execution_plan_binding.get("stdout_capture_allowed") is False
+ and execution_plan_binding.get("stderr_capture_allowed") is False
+ and execution_plan_binding.get("database_apply_authorized") is False
+ and execution_plan_binding.get("ready_for_database_apply_now") is False
+ )
+ non_executable_command_artifact_bound = (
+ non_executable_command_artifact.get("artifact_type")
+ == "non_executable_command_artifact_reference"
+ and bool(non_executable_command_artifact.get("artifact_id"))
+ and bool(
+ non_executable_command_artifact.get("source_execution_plan_binding_id")
+ )
+ and bool(non_executable_command_artifact.get("dry_run_command_shape_hash"))
+ and non_executable_command_artifact.get("command_text_included") is False
+ and non_executable_command_artifact.get("argv_included") is False
+ and non_executable_command_artifact.get("shell_command_included") is False
+ and non_executable_command_artifact.get("endpoint_execution_included")
+ is False
+ and non_executable_command_artifact.get("sql_execution_included") is False
+ and non_executable_command_artifact.get("database_write_included") is False
+ and non_executable_command_artifact.get("execution_authorized") is False
+ and non_executable_command_artifact.get("database_apply_authorized")
+ is False
+ and int(
+ non_executable_command_artifact.get(
+ "non_executable_command_artifact_field_count"
+ )
+ or 0
+ )
+ == 10
+ )
+ command_artifact_hash_locked = (
+ bool(artifact_sha256)
+ and len(artifact_sha256) == 64
+ and non_executable_command_artifact.get(
+ "non_executable_command_artifact_sha256"
+ )
+ == artifact_sha256
+ )
+ source_chain_ids_match = (
+ bool(runner.get("runner_readiness_id"))
+ and runner.get("runner_readiness_id") == future_plan.get("runner_readiness_id")
+ and runner.get("runner_readiness_id")
+ == execution_plan_binding.get("source_runner_readiness_id")
+ and execution_plan_binding.get("execution_plan_binding_id")
+ == future_plan.get("execution_plan_binding_id")
+ and execution_plan_binding.get("execution_plan_binding_id")
+ == non_executable_command_artifact.get("source_execution_plan_binding_id")
+ and runner.get("source_receipt_closeout_id")
+ == execution_plan_binding.get("source_receipt_closeout_id")
+ and runner.get("source_dry_run_package_id")
+ == execution_plan_binding.get("source_dry_run_package_id")
+ )
+ receipt_validation_and_parser_carried_forward = (
+ result_parser.get("required_command_shape_hash")
+ == runner.get("dry_run_command_shape_hash")
+ and result_parser.get("execution_required") is False
+ and result_parser.get("stdout_allowed") is False
+ and result_parser.get("stderr_allowed") is False
+ and result_parser.get("database_apply_authorized") is False
+ and validation.get("dry_run_command_shape_hash")
+ == runner.get("dry_run_command_shape_hash")
+ and validation.get("execution_performed") is False
+ and validation.get("stdout_included") is False
+ and validation.get("stderr_included") is False
+ and validation.get("database_apply_authorized") is False
+ and validation.get("executes_endpoint") is False
+ and validation.get("executes_sql") is False
+ and validation.get("writes_database") is False
+ )
+ runner_execution_gate_closed = (
+ future_plan.get("dry_run_execution_performed") is False
+ and future_plan.get("runner_execution_authorized") is False
+ and future_plan.get("dry_run_execution_authorized") is False
+ and future_plan.get("database_apply_authorized") is False
+ and future_plan.get("executes_database_apply") is False
+ and future_plan.get("executes_endpoint") is False
+ and future_plan.get("executes_sql") is False
+ and future_plan.get("writes_database") is False
+ and runner.get("runner_execution_authorized") is False
+ and runner.get("dry_run_execution_authorized") is False
+ and runner.get("database_apply_authorized") is False
+ and execution_plan_preview_only
+ and non_executable_command_artifact_bound
+ )
+ target_hash_locked = (
+ runner.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(runner.get("expected_sha256"))
+ and bool(runner.get("actual_sha256"))
+ and runner.get("expected_sha256") == runner.get("actual_sha256")
+ and runner.get("hash_matches") is True
+ and runner.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ runner_contract_blocks_database_apply = (
+ runner_contract.get(
+ "permits_future_database_apply_controlled_dry_run_execution_plan_binding"
+ )
+ is True
+ and runner_contract.get("executes_database_apply") is False
+ and runner_contract.get("database_apply_authorized") is False
+ and runner_contract.get("ready_for_database_apply_now") is False
+ and runner_contract.get("signs_database_apply_authorization") is False
+ and runner_contract.get("writes_database") is False
+ )
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("executes_authorization_evidence") is False
+ and safety.get("executes_database_apply") is False
+ and runner.get("accepts_plaintext_secret") is False
+ and runner.get("reads_secret_in_preview") is False
+ and runner.get("signature_material_included") is False
+ and runner.get("secret_material_included") is False
+ and runner.get("signs_database_apply_authorization") is False
+ and runner.get("executes_authorization_evidence") is False
+ and runner.get("executes_database_apply") is False
+ and runner.get("executes_endpoint_in_preview") is False
+ and runner.get("executes_sql_in_preview") is False
+ and runner.get("writes_database_in_preview") is False
+ and receipt_validation_and_parser_carried_forward
+ and execution_plan_preview_only
+ and non_executable_command_artifact_bound
+ )
+ checks = [
+ _controlled_dry_run_execution_plan_closeout_check(
+ "runner_readiness_ready",
+ runner_readiness_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_READINESS_READY"
+ and future_plan.get(
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_binding"
+ )
+ is True
+ and future_plan.get(
+ "can_enter_future_database_apply_controlled_dry_run_execution_plan_closeout"
+ )
+ is True
+ and runner.get(
+ "ready_for_future_database_apply_controlled_dry_run_runner_readiness"
+ )
+ is True,
+ {
+ "result": runner_readiness_result.get("result"),
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_binding": (
+ future_plan.get(
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_binding"
+ )
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_execution_plan_closeout": (
+ future_plan.get(
+ "can_enter_future_database_apply_controlled_dry_run_execution_plan_closeout"
+ )
+ ),
+ },
+ "wait_for_controlled_dry_run_runner_readiness",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "runner_readiness_id": runner.get("runner_readiness_id"),
+ "execution_plan_binding_id": execution_plan_binding.get(
+ "execution_plan_binding_id"
+ ),
+ "artifact_source_execution_plan_binding_id": (
+ non_executable_command_artifact.get(
+ "source_execution_plan_binding_id"
+ )
+ ),
+ },
+ "wait_for_execution_plan_source_chain_ids",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "execution_plan_closeout_contract_complete",
+ len(execution_plan_closeout_fields) == 12
+ and len(execution_plan_closeout_acceptance_gates) == 10
+ and len(non_executable_command_artifact_fields) == 10
+ and "non_executable_command_artifact_id"
+ in execution_plan_closeout_fields
+ and "non_executable_command_artifact_bound"
+ in execution_plan_closeout_acceptance_gates,
+ {
+ "execution_plan_closeout_field_count": len(
+ execution_plan_closeout_fields
+ ),
+ "execution_plan_closeout_acceptance_gate_count": len(
+ execution_plan_closeout_acceptance_gates
+ ),
+ "non_executable_command_artifact_field_count": len(
+ non_executable_command_artifact_fields
+ ),
+ },
+ "wait_for_execution_plan_closeout_contract",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "execution_plan_binding_preview_only",
+ execution_plan_preview_only,
+ {
+ "execution_authorized": execution_plan_binding.get(
+ "execution_authorized"
+ ),
+ "runner_execution_authorized": execution_plan_binding.get(
+ "runner_execution_authorized"
+ ),
+ "database_write_included": execution_plan_binding.get(
+ "database_write_included"
+ ),
+ },
+ "abort_if_execution_plan_binding_is_executable",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "non_executable_command_artifact_bound",
+ non_executable_command_artifact_bound,
+ {
+ "artifact_id": non_executable_command_artifact.get("artifact_id"),
+ "command_text_included": non_executable_command_artifact.get(
+ "command_text_included"
+ ),
+ "argv_included": non_executable_command_artifact.get(
+ "argv_included"
+ ),
+ "database_write_included": non_executable_command_artifact.get(
+ "database_write_included"
+ ),
+ },
+ "abort_if_command_artifact_contains_executable_material",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "command_artifact_hash_locked",
+ command_artifact_hash_locked,
+ {
+ "artifact_sha256_present": bool(artifact_sha256),
+ "artifact_sha256_length": len(artifact_sha256),
+ "artifact_id": artifact_id,
+ },
+ "abort_if_command_artifact_hash_is_not_locked",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "receipt_validation_and_parser_carried_forward",
+ receipt_validation_and_parser_carried_forward,
+ {
+ "parser_id": result_parser.get("parser_id"),
+ "receipt_validation_status": validation.get(
+ "receipt_validation_status"
+ ),
+ "dry_run_command_shape_hash": runner.get(
+ "dry_run_command_shape_hash"
+ ),
+ },
+ "wait_for_receipt_validation_and_parser_carry_forward",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "runner_execution_gate_closed",
+ runner_execution_gate_closed,
+ {
+ "dry_run_execution_performed": future_plan.get(
+ "dry_run_execution_performed"
+ ),
+ "runner_execution_authorized": future_plan.get(
+ "runner_execution_authorized"
+ ),
+ "writes_database": future_plan.get("writes_database"),
+ },
+ "abort_if_runner_execution_gate_opens",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": runner.get("target_file"),
+ "expected_sha256_present": bool(runner.get("expected_sha256")),
+ "actual_sha256_present": bool(runner.get("actual_sha256")),
+ "hash_matches": runner.get("hash_matches"),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "rollback_and_post_apply_verifier_bindings_carried_forward",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "runner_contract_blocks_database_apply",
+ runner_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_execution_plan_binding": (
+ runner_contract.get(
+ "permits_future_database_apply_controlled_dry_run_execution_plan_binding"
+ )
+ ),
+ "database_apply_authorized": runner_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": runner_contract.get("writes_database"),
+ },
+ "abort_if_runner_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_execution_plan_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "non_executable_command_artifact_bound": (
+ non_executable_command_artifact_bound
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PLAN_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_READINESS"
+ )
+ future_database_apply_controlled_dry_run_command_artifact_verification = {
+ "execution_plan_closeout_id": closeout_id,
+ "non_executable_command_artifact_id": artifact_id,
+ "non_executable_command_artifact_sha256": artifact_sha256,
+ "source_runner_readiness_id": runner.get("runner_readiness_id"),
+ "source_execution_plan_binding_id": execution_plan_binding.get(
+ "execution_plan_binding_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_verification": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_command_artifact_closeout": (
+ closeout_ready
+ ),
+ "execution_plan_closeout_ready": closeout_ready,
+ "non_executable_command_artifact_verified": closeout_ready,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_execution_plan_closeout = {
+ "execution_plan_closeout_id": closeout_id,
+ "authorization_material_type": "controlled_dry_run_execution_plan_closeout",
+ "source_runner_readiness_id": runner.get("runner_readiness_id"),
+ "source_execution_plan_binding_id": execution_plan_binding.get(
+ "execution_plan_binding_id"
+ ),
+ "source_receipt_closeout_id": runner.get("source_receipt_closeout_id"),
+ "source_dry_run_package_id": runner.get("source_dry_run_package_id"),
+ "source_controlled_apply_preflight_id": runner.get(
+ "source_controlled_apply_preflight_id"
+ ),
+ "source_dry_run_execution_receipt_id": runner.get(
+ "source_dry_run_execution_receipt_id"
+ ),
+ "dry_run_result_parser_id": runner.get("dry_run_result_parser_id"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_closeout": (
+ closeout_ready
+ ),
+ "execution_plan_closeout_fields": execution_plan_closeout_fields,
+ "execution_plan_closeout_field_count": len(
+ execution_plan_closeout_fields
+ ),
+ "execution_plan_closeout_acceptance_gates": (
+ execution_plan_closeout_acceptance_gates
+ ),
+ "execution_plan_closeout_acceptance_gate_count": len(
+ execution_plan_closeout_acceptance_gates
+ ),
+ "non_executable_command_artifact": non_executable_command_artifact,
+ "non_executable_command_artifact_count": 1,
+ "non_executable_command_artifact_field_count": len(
+ non_executable_command_artifact_fields
+ ),
+ "non_executable_command_artifact_sha256": artifact_sha256,
+ "execution_plan_binding": execution_plan_binding,
+ "execution_plan_binding_count": 1,
+ "execution_plan_binding_field_count": runner.get(
+ "execution_plan_binding_field_count", 0
+ ),
+ "dry_run_result_parser": result_parser,
+ "dry_run_result_parser_count": 1,
+ "receipt_validation_report": validation,
+ "receipt_validation_report_count": 1,
+ "dry_run_command_shape_hash": runner.get("dry_run_command_shape_hash"),
+ "target_file": runner.get("target_file"),
+ "expected_sha256": runner.get("expected_sha256"),
+ "actual_sha256": runner.get("actual_sha256"),
+ "hash_matches": runner.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "execution_plan_closeout_only": True,
+ "non_executable_command_artifact_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_execution_plan_closeout_contract = {
+ "mode": "controlled_dry_run_execution_plan_closeout_and_non_executable_command_artifact_verification_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-execution-plan-closeout"
+ ),
+ "source_runner_readiness_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-runner-readiness"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_command_artifact_verification": (
+ closeout_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PLAN_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(runner_readiness_result.get("success")),
+ "generated_at": runner_readiness_result.get("generated_at"),
+ "source_policy": runner_readiness_result.get("policy"),
+ "stats": runner_readiness_result.get("stats") or {},
+ "summary": {
+ "controlled_dry_run_execution_plan_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_check_count": len(checks),
+ "controlled_dry_run_execution_plan_closeout_pass_count": passed_count,
+ "controlled_dry_run_execution_plan_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_runner_readiness_ready_count": summary.get(
+ "controlled_dry_run_runner_readiness_ready_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_check_count": summary.get(
+ "controlled_dry_run_runner_readiness_check_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_ready_count": summary.get(
+ "controlled_dry_run_receipt_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_check_count": summary.get(
+ "controlled_dry_run_receipt_closeout_check_count", 0
+ ),
+ "controlled_dry_run_package_ready_count": summary.get(
+ "controlled_dry_run_package_ready_count", 0
+ ),
+ "controlled_dry_run_package_check_count": summary.get(
+ "controlled_dry_run_package_check_count", 0
+ ),
+ "controlled_apply_final_preflight_ready_count": summary.get(
+ "controlled_apply_final_preflight_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_check_count": summary.get(
+ "controlled_apply_final_preflight_check_count", 0
+ ),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_execution_plan_closeout_count": 1,
+ "controlled_dry_run_execution_plan_closeout_field_count": len(
+ execution_plan_closeout_fields
+ ),
+ "controlled_dry_run_execution_plan_closeout_acceptance_gate_count": len(
+ execution_plan_closeout_acceptance_gates
+ ),
+ "non_executable_command_artifact_count": 1,
+ "non_executable_command_artifact_field_count": len(
+ non_executable_command_artifact_fields
+ ),
+ "execution_plan_binding_count": summary.get(
+ "execution_plan_binding_count", 0
+ ),
+ "execution_plan_binding_field_count": summary.get(
+ "execution_plan_binding_field_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_count": summary.get(
+ "controlled_dry_run_runner_readiness_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_field_count": summary.get(
+ "controlled_dry_run_runner_readiness_field_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_acceptance_gate_count": (
+ summary.get(
+ "controlled_dry_run_runner_readiness_acceptance_gate_count", 0
+ )
+ ),
+ "dry_run_result_parser_count": summary.get(
+ "dry_run_result_parser_count", 0
+ ),
+ "dry_run_result_parser_field_count": summary.get(
+ "dry_run_result_parser_field_count", 0
+ ),
+ "receipt_validation_report_count": summary.get(
+ "receipt_validation_report_count", 0
+ ),
+ "receipt_validation_field_count": summary.get(
+ "receipt_validation_field_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_count": summary.get(
+ "controlled_dry_run_receipt_closeout_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_field_count": summary.get(
+ "controlled_dry_run_receipt_closeout_field_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_acceptance_gate_count": summary.get(
+ "controlled_dry_run_receipt_closeout_acceptance_gate_count", 0
+ ),
+ "rollback_binding_count": summary.get("rollback_binding_count", 0),
+ "post_apply_verifier_binding_count": summary.get(
+ "post_apply_verifier_binding_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get(
+ "same_run_truth_required_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_dry_run_command_artifact_verification": (
+ future_database_apply_controlled_dry_run_command_artifact_verification
+ ),
+ "controlled_dry_run_execution_plan_closeout": (
+ controlled_dry_run_execution_plan_closeout
+ ),
+ "controlled_dry_run_execution_plan_closeout_contract": (
+ controlled_dry_run_execution_plan_closeout_contract
+ ),
+ "controlled_dry_run_execution_plan_closeout_checks": checks,
+ "source_controlled_dry_run_runner_readiness_summary": summary,
+ "source_controlled_dry_run_runner_readiness_contract": runner_contract,
+ "source_controlled_dry_run_runner_readiness": runner,
+ "source_database_apply_controlled_dry_run_execution_plan_binding": future_plan,
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_execution_plan_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run command artifact closeout.",
+ "Keep command artifacts hash-locked and non-executable before any runner execution receipt lane.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out a non-executable command artifact into a receipt preflight."""
+ execution_plan_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_artifact = (
+ execution_plan_closeout_result.get(
+ "future_database_apply_controlled_dry_run_command_artifact_verification"
+ )
+ or {}
+ )
+ plan_closeout = (
+ execution_plan_closeout_result.get(
+ "controlled_dry_run_execution_plan_closeout"
+ )
+ or {}
+ )
+ plan_closeout_contract = (
+ execution_plan_closeout_result.get(
+ "controlled_dry_run_execution_plan_closeout_contract"
+ )
+ or {}
+ )
+ summary = execution_plan_closeout_result.get("summary") or {}
+ safety = execution_plan_closeout_result.get("safety") or {}
+ artifact = plan_closeout.get("non_executable_command_artifact") or {}
+ execution_plan_binding = plan_closeout.get("execution_plan_binding") or {}
+ validation = plan_closeout.get("receipt_validation_report") or {}
+ result_parser = plan_closeout.get("dry_run_result_parser") or {}
+ rollback_binding = plan_closeout.get("rollback_binding") or {}
+ verifier_binding = plan_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_command_artifact_closeout_id(
+ execution_plan_closeout_result
+ )
+ receipt_preflight_id = f"{closeout_id}-runner-execution-receipt-preflight"
+ command_artifact_closeout_fields = [
+ "command_artifact_closeout_id",
+ "source_execution_plan_closeout_id",
+ "source_non_executable_command_artifact_id",
+ "source_execution_plan_binding_id",
+ "source_runner_readiness_id",
+ "source_receipt_closeout_id",
+ "dry_run_command_shape_hash",
+ "non_executable_command_artifact_sha256",
+ "runner_execution_receipt_preflight_id",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "abort_conditions",
+ ]
+ command_artifact_closeout_acceptance_gates = [
+ "execution_plan_closeout_ready",
+ "source_chain_ids_match",
+ "non_executable_command_artifact_hash_verified",
+ "non_executable_artifact_has_no_command_text_or_argv",
+ "runner_execution_receipt_preflight_bound",
+ "runner_execution_receipt_preflight_no_execute",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "execution_plan_closeout_contract_blocks_database_apply",
+ "no_secret_signature_or_database_apply",
+ ]
+ runner_execution_receipt_preflight_fields = [
+ "preflight_id",
+ "source_command_artifact_closeout_id",
+ "source_non_executable_command_artifact_id",
+ "dry_run_command_shape_hash",
+ "preflight_status",
+ "execution_required",
+ "stdout_capture_allowed",
+ "stderr_capture_allowed",
+ "database_apply_authorized",
+ "writes_database",
+ ]
+ abort_conditions = [
+ "abort_if_execution_plan_closeout_not_ready",
+ "abort_if_non_executable_command_artifact_hash_changes",
+ "abort_if_command_artifact_contains_command_text_or_argv",
+ "abort_if_runner_execution_receipt_preflight_requests_execution",
+ "abort_if_stdout_or_stderr_capture_is_requested_before_execution_lane",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_endpoint_sql_database_write_or_signature_is_requested",
+ ]
+ command_artifact_hash_verified = (
+ bool(artifact.get("non_executable_command_artifact_sha256"))
+ and artifact.get("non_executable_command_artifact_sha256")
+ == plan_closeout.get("non_executable_command_artifact_sha256")
+ == future_artifact.get("non_executable_command_artifact_sha256")
+ and len(str(artifact.get("non_executable_command_artifact_sha256") or ""))
+ == 64
+ )
+ non_executable_artifact_has_no_command_text_or_argv = (
+ artifact.get("artifact_type") == "non_executable_command_artifact_reference"
+ and artifact.get("command_text_included") is False
+ and artifact.get("argv_included") is False
+ and artifact.get("command_text") is None
+ and artifact.get("argv") is None
+ and artifact.get("shell_command_included") is False
+ and artifact.get("endpoint_execution_included") is False
+ and artifact.get("sql_execution_included") is False
+ and artifact.get("database_write_included") is False
+ and artifact.get("stdout_capture_allowed") is False
+ and artifact.get("stderr_capture_allowed") is False
+ and artifact.get("execution_authorized") is False
+ and artifact.get("dry_run_execution_authorized") is False
+ and artifact.get("runner_execution_authorized") is False
+ and artifact.get("database_apply_authorized") is False
+ )
+ runner_execution_receipt_preflight = {
+ "preflight_id": receipt_preflight_id,
+ "source_command_artifact_closeout_id": closeout_id,
+ "source_execution_plan_closeout_id": plan_closeout.get(
+ "execution_plan_closeout_id"
+ ),
+ "source_non_executable_command_artifact_id": artifact.get("artifact_id"),
+ "source_execution_plan_binding_id": artifact.get(
+ "source_execution_plan_binding_id"
+ ),
+ "source_runner_readiness_id": artifact.get("source_runner_readiness_id"),
+ "source_receipt_closeout_id": artifact.get("source_receipt_closeout_id"),
+ "source_dry_run_package_id": artifact.get("source_dry_run_package_id"),
+ "dry_run_command_shape_hash": artifact.get("dry_run_command_shape_hash"),
+ "non_executable_command_artifact_sha256": artifact.get(
+ "non_executable_command_artifact_sha256"
+ ),
+ "preflight_status": "preflight_only_not_executed",
+ "execution_required": False,
+ "execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "runner_execution_authorized": False,
+ "shell_execution_included": False,
+ "endpoint_execution_included": False,
+ "sql_execution_included": False,
+ "database_write_included": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "execution_performed": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "database_apply_authorized": False,
+ "ready_for_database_apply_now": False,
+ "writes_database": False,
+ "runner_execution_receipt_preflight_field_count": len(
+ runner_execution_receipt_preflight_fields
+ ),
+ "runner_execution_receipt_preflight_fields": (
+ runner_execution_receipt_preflight_fields
+ ),
+ }
+ runner_execution_receipt_preflight_bound = (
+ bool(runner_execution_receipt_preflight.get("preflight_id"))
+ and runner_execution_receipt_preflight.get(
+ "source_command_artifact_closeout_id"
+ )
+ == closeout_id
+ and runner_execution_receipt_preflight.get(
+ "source_non_executable_command_artifact_id"
+ )
+ == artifact.get("artifact_id")
+ and runner_execution_receipt_preflight.get(
+ "source_execution_plan_binding_id"
+ )
+ == execution_plan_binding.get("execution_plan_binding_id")
+ and runner_execution_receipt_preflight.get("dry_run_command_shape_hash")
+ == plan_closeout.get("dry_run_command_shape_hash")
+ and int(
+ runner_execution_receipt_preflight.get(
+ "runner_execution_receipt_preflight_field_count"
+ )
+ or 0
+ )
+ == 10
+ )
+ runner_execution_receipt_preflight_no_execute = (
+ runner_execution_receipt_preflight.get("preflight_status")
+ == "preflight_only_not_executed"
+ and runner_execution_receipt_preflight.get("execution_required") is False
+ and runner_execution_receipt_preflight.get("execution_authorized") is False
+ and runner_execution_receipt_preflight.get("dry_run_execution_authorized")
+ is False
+ and runner_execution_receipt_preflight.get("runner_execution_authorized")
+ is False
+ and runner_execution_receipt_preflight.get("shell_execution_included")
+ is False
+ and runner_execution_receipt_preflight.get("endpoint_execution_included")
+ is False
+ and runner_execution_receipt_preflight.get("sql_execution_included") is False
+ and runner_execution_receipt_preflight.get("database_write_included")
+ is False
+ and runner_execution_receipt_preflight.get("stdout_capture_allowed") is False
+ and runner_execution_receipt_preflight.get("stderr_capture_allowed") is False
+ and runner_execution_receipt_preflight.get("execution_performed") is False
+ and runner_execution_receipt_preflight.get("stdout_included") is False
+ and runner_execution_receipt_preflight.get("stderr_included") is False
+ and runner_execution_receipt_preflight.get("database_apply_authorized")
+ is False
+ and runner_execution_receipt_preflight.get("writes_database") is False
+ )
+ source_chain_ids_match = (
+ bool(plan_closeout.get("execution_plan_closeout_id"))
+ and plan_closeout.get("execution_plan_closeout_id")
+ == future_artifact.get("execution_plan_closeout_id")
+ and plan_closeout.get("execution_plan_closeout_id")
+ == runner_execution_receipt_preflight.get(
+ "source_execution_plan_closeout_id"
+ )
+ and artifact.get("artifact_id")
+ == future_artifact.get("non_executable_command_artifact_id")
+ == runner_execution_receipt_preflight.get(
+ "source_non_executable_command_artifact_id"
+ )
+ and artifact.get("source_execution_plan_binding_id")
+ == plan_closeout.get("source_execution_plan_binding_id")
+ == execution_plan_binding.get("execution_plan_binding_id")
+ and artifact.get("source_runner_readiness_id")
+ == plan_closeout.get("source_runner_readiness_id")
+ )
+ result_parser_and_validation_carried_forward = (
+ result_parser.get("required_command_shape_hash")
+ == plan_closeout.get("dry_run_command_shape_hash")
+ and result_parser.get("execution_required") is False
+ and result_parser.get("stdout_allowed") is False
+ and result_parser.get("stderr_allowed") is False
+ and result_parser.get("database_apply_authorized") is False
+ and validation.get("dry_run_command_shape_hash")
+ == plan_closeout.get("dry_run_command_shape_hash")
+ and validation.get("execution_performed") is False
+ and validation.get("stdout_included") is False
+ and validation.get("stderr_included") is False
+ and validation.get("database_apply_authorized") is False
+ and validation.get("executes_endpoint") is False
+ and validation.get("executes_sql") is False
+ and validation.get("writes_database") is False
+ )
+ target_hash_locked = (
+ plan_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(plan_closeout.get("expected_sha256"))
+ and bool(plan_closeout.get("actual_sha256"))
+ and plan_closeout.get("expected_sha256")
+ == plan_closeout.get("actual_sha256")
+ and plan_closeout.get("hash_matches") is True
+ and plan_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ plan_closeout_contract_blocks_database_apply = (
+ plan_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_command_artifact_verification"
+ )
+ is True
+ and plan_closeout_contract.get("executes_database_apply") is False
+ and plan_closeout_contract.get("database_apply_authorized") is False
+ and plan_closeout_contract.get("ready_for_database_apply_now") is False
+ and plan_closeout_contract.get("signs_database_apply_authorization") is False
+ and plan_closeout_contract.get("writes_database") is False
+ )
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("executes_authorization_evidence") is False
+ and safety.get("executes_database_apply") is False
+ and plan_closeout.get("accepts_plaintext_secret") is False
+ and plan_closeout.get("reads_secret_in_preview") is False
+ and plan_closeout.get("signature_material_included") is False
+ and plan_closeout.get("secret_material_included") is False
+ and plan_closeout.get("signs_database_apply_authorization") is False
+ and plan_closeout.get("executes_authorization_evidence") is False
+ and plan_closeout.get("executes_database_apply") is False
+ and plan_closeout.get("executes_endpoint_in_preview") is False
+ and plan_closeout.get("executes_sql_in_preview") is False
+ and plan_closeout.get("writes_database_in_preview") is False
+ and non_executable_artifact_has_no_command_text_or_argv
+ and runner_execution_receipt_preflight_no_execute
+ and result_parser_and_validation_carried_forward
+ )
+ checks = [
+ _controlled_dry_run_command_artifact_closeout_check(
+ "execution_plan_closeout_ready",
+ execution_plan_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PLAN_CLOSEOUT_READY"
+ and future_artifact.get(
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_verification"
+ )
+ is True
+ and future_artifact.get(
+ "can_enter_future_database_apply_controlled_dry_run_command_artifact_closeout"
+ )
+ is True
+ and plan_closeout.get(
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_closeout"
+ )
+ is True,
+ {
+ "result": execution_plan_closeout_result.get("result"),
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_verification": (
+ future_artifact.get(
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_verification"
+ )
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_command_artifact_closeout": (
+ future_artifact.get(
+ "can_enter_future_database_apply_controlled_dry_run_command_artifact_closeout"
+ )
+ ),
+ },
+ "wait_for_controlled_dry_run_execution_plan_closeout",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "execution_plan_closeout_id": plan_closeout.get(
+ "execution_plan_closeout_id"
+ ),
+ "artifact_id": artifact.get("artifact_id"),
+ "runner_execution_receipt_preflight_id": receipt_preflight_id,
+ },
+ "wait_for_command_artifact_source_chain_ids",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "command_artifact_closeout_contract_complete",
+ len(command_artifact_closeout_fields) == 12
+ and len(command_artifact_closeout_acceptance_gates) == 10
+ and len(runner_execution_receipt_preflight_fields) == 10
+ and "runner_execution_receipt_preflight_id"
+ in command_artifact_closeout_fields
+ and "runner_execution_receipt_preflight_no_execute"
+ in command_artifact_closeout_acceptance_gates,
+ {
+ "command_artifact_closeout_field_count": len(
+ command_artifact_closeout_fields
+ ),
+ "command_artifact_closeout_acceptance_gate_count": len(
+ command_artifact_closeout_acceptance_gates
+ ),
+ "runner_execution_receipt_preflight_field_count": len(
+ runner_execution_receipt_preflight_fields
+ ),
+ },
+ "wait_for_command_artifact_closeout_contract",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "non_executable_command_artifact_hash_verified",
+ command_artifact_hash_verified,
+ {
+ "artifact_sha256_present": bool(
+ artifact.get("non_executable_command_artifact_sha256")
+ ),
+ "artifact_sha256_length": len(
+ str(artifact.get("non_executable_command_artifact_sha256") or "")
+ ),
+ },
+ "abort_if_command_artifact_hash_changes",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "non_executable_artifact_has_no_command_text_or_argv",
+ non_executable_artifact_has_no_command_text_or_argv,
+ {
+ "command_text_included": artifact.get("command_text_included"),
+ "argv_included": artifact.get("argv_included"),
+ "shell_command_included": artifact.get("shell_command_included"),
+ "database_write_included": artifact.get("database_write_included"),
+ },
+ "abort_if_command_artifact_contains_executable_material",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "runner_execution_receipt_preflight_bound",
+ runner_execution_receipt_preflight_bound,
+ {
+ "preflight_id": runner_execution_receipt_preflight.get(
+ "preflight_id"
+ ),
+ "source_non_executable_command_artifact_id": (
+ runner_execution_receipt_preflight.get(
+ "source_non_executable_command_artifact_id"
+ )
+ ),
+ "runner_execution_receipt_preflight_field_count": (
+ runner_execution_receipt_preflight.get(
+ "runner_execution_receipt_preflight_field_count"
+ )
+ ),
+ },
+ "wait_for_runner_execution_receipt_preflight_binding",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "runner_execution_receipt_preflight_no_execute",
+ runner_execution_receipt_preflight_no_execute,
+ {
+ "execution_required": runner_execution_receipt_preflight.get(
+ "execution_required"
+ ),
+ "execution_authorized": runner_execution_receipt_preflight.get(
+ "execution_authorized"
+ ),
+ "stdout_capture_allowed": runner_execution_receipt_preflight.get(
+ "stdout_capture_allowed"
+ ),
+ "writes_database": runner_execution_receipt_preflight.get(
+ "writes_database"
+ ),
+ },
+ "abort_if_runner_execution_receipt_preflight_executes",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "result_parser_and_receipt_validation_carried_forward",
+ result_parser_and_validation_carried_forward,
+ {
+ "parser_id": result_parser.get("parser_id"),
+ "receipt_validation_status": validation.get(
+ "receipt_validation_status"
+ ),
+ "dry_run_command_shape_hash": plan_closeout.get(
+ "dry_run_command_shape_hash"
+ ),
+ },
+ "wait_for_result_parser_and_receipt_validation_carry_forward",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": plan_closeout.get("target_file"),
+ "expected_sha256_present": bool(plan_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(plan_closeout.get("actual_sha256")),
+ "hash_matches": plan_closeout.get("hash_matches"),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "rollback_and_post_apply_verifier_bindings_carried_forward",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "execution_plan_closeout_contract_blocks_database_apply",
+ plan_closeout_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_command_artifact_verification": (
+ plan_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_command_artifact_verification"
+ )
+ ),
+ "database_apply_authorized": plan_closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": plan_closeout_contract.get("writes_database"),
+ },
+ "abort_if_execution_plan_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_command_artifact_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "runner_execution_receipt_preflight_no_execute": (
+ runner_execution_receipt_preflight_no_execute
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_COMMAND_ARTIFACT_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PLAN_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_runner_execution_receipt_preflight = {
+ "command_artifact_closeout_id": closeout_id,
+ "runner_execution_receipt_preflight_id": receipt_preflight_id,
+ "source_execution_plan_closeout_id": plan_closeout.get(
+ "execution_plan_closeout_id"
+ ),
+ "source_non_executable_command_artifact_id": artifact.get("artifact_id"),
+ "source_execution_plan_binding_id": artifact.get(
+ "source_execution_plan_binding_id"
+ ),
+ "non_executable_command_artifact_sha256": artifact.get(
+ "non_executable_command_artifact_sha256"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout": (
+ closeout_ready
+ ),
+ "command_artifact_closeout_ready": closeout_ready,
+ "runner_execution_receipt_preflight_bound": closeout_ready,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_command_artifact_closeout = {
+ "command_artifact_closeout_id": closeout_id,
+ "authorization_material_type": "controlled_dry_run_command_artifact_closeout",
+ "source_execution_plan_closeout_id": plan_closeout.get(
+ "execution_plan_closeout_id"
+ ),
+ "source_non_executable_command_artifact_id": artifact.get("artifact_id"),
+ "source_execution_plan_binding_id": artifact.get(
+ "source_execution_plan_binding_id"
+ ),
+ "source_runner_readiness_id": artifact.get("source_runner_readiness_id"),
+ "source_receipt_closeout_id": artifact.get("source_receipt_closeout_id"),
+ "source_dry_run_package_id": artifact.get("source_dry_run_package_id"),
+ "dry_run_command_shape_hash": artifact.get("dry_run_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_closeout": (
+ closeout_ready
+ ),
+ "command_artifact_closeout_fields": command_artifact_closeout_fields,
+ "command_artifact_closeout_field_count": len(
+ command_artifact_closeout_fields
+ ),
+ "command_artifact_closeout_acceptance_gates": (
+ command_artifact_closeout_acceptance_gates
+ ),
+ "command_artifact_closeout_acceptance_gate_count": len(
+ command_artifact_closeout_acceptance_gates
+ ),
+ "runner_execution_receipt_preflight": (
+ runner_execution_receipt_preflight
+ ),
+ "runner_execution_receipt_preflight_count": 1,
+ "runner_execution_receipt_preflight_field_count": len(
+ runner_execution_receipt_preflight_fields
+ ),
+ "non_executable_command_artifact": artifact,
+ "non_executable_command_artifact_count": 1,
+ "non_executable_command_artifact_field_count": plan_closeout.get(
+ "non_executable_command_artifact_field_count", 0
+ ),
+ "non_executable_command_artifact_sha256": artifact.get(
+ "non_executable_command_artifact_sha256"
+ ),
+ "execution_plan_binding": execution_plan_binding,
+ "execution_plan_binding_count": 1,
+ "execution_plan_binding_field_count": plan_closeout.get(
+ "execution_plan_binding_field_count", 0
+ ),
+ "dry_run_result_parser": result_parser,
+ "dry_run_result_parser_count": 1,
+ "receipt_validation_report": validation,
+ "receipt_validation_report_count": 1,
+ "target_file": plan_closeout.get("target_file"),
+ "expected_sha256": plan_closeout.get("expected_sha256"),
+ "actual_sha256": plan_closeout.get("actual_sha256"),
+ "hash_matches": plan_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "command_artifact_closeout_only": True,
+ "runner_execution_receipt_preflight_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_command_artifact_closeout_contract = {
+ "mode": "controlled_dry_run_command_artifact_closeout_and_runner_execution_receipt_preflight_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-command-artifact-closeout"
+ ),
+ "source_execution_plan_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-execution-plan-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight": (
+ closeout_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_COMMAND_ARTIFACT_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(execution_plan_closeout_result.get("success")),
+ "generated_at": execution_plan_closeout_result.get("generated_at"),
+ "source_policy": execution_plan_closeout_result.get("policy"),
+ "stats": execution_plan_closeout_result.get("stats") or {},
+ "summary": {
+ "controlled_dry_run_command_artifact_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_command_artifact_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_command_artifact_closeout_pass_count": passed_count,
+ "controlled_dry_run_command_artifact_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_execution_plan_closeout_ready_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_check_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_check_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_ready_count": summary.get(
+ "controlled_dry_run_runner_readiness_ready_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_check_count": summary.get(
+ "controlled_dry_run_runner_readiness_check_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_ready_count": summary.get(
+ "controlled_dry_run_receipt_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_check_count": summary.get(
+ "controlled_dry_run_receipt_closeout_check_count", 0
+ ),
+ "controlled_dry_run_package_ready_count": summary.get(
+ "controlled_dry_run_package_ready_count", 0
+ ),
+ "controlled_dry_run_package_check_count": summary.get(
+ "controlled_dry_run_package_check_count", 0
+ ),
+ "controlled_apply_final_preflight_ready_count": summary.get(
+ "controlled_apply_final_preflight_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_check_count": summary.get(
+ "controlled_apply_final_preflight_check_count", 0
+ ),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_command_artifact_closeout_count": 1,
+ "controlled_dry_run_command_artifact_closeout_field_count": len(
+ command_artifact_closeout_fields
+ ),
+ "controlled_dry_run_command_artifact_closeout_acceptance_gate_count": len(
+ command_artifact_closeout_acceptance_gates
+ ),
+ "runner_execution_receipt_preflight_count": 1,
+ "runner_execution_receipt_preflight_field_count": len(
+ runner_execution_receipt_preflight_fields
+ ),
+ "controlled_dry_run_execution_plan_closeout_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_field_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_field_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_acceptance_gate_count": (
+ summary.get(
+ "controlled_dry_run_execution_plan_closeout_acceptance_gate_count",
+ 0,
+ )
+ ),
+ "non_executable_command_artifact_count": summary.get(
+ "non_executable_command_artifact_count", 0
+ ),
+ "non_executable_command_artifact_field_count": summary.get(
+ "non_executable_command_artifact_field_count", 0
+ ),
+ "execution_plan_binding_count": summary.get(
+ "execution_plan_binding_count", 0
+ ),
+ "execution_plan_binding_field_count": summary.get(
+ "execution_plan_binding_field_count", 0
+ ),
+ "dry_run_result_parser_count": summary.get(
+ "dry_run_result_parser_count", 0
+ ),
+ "dry_run_result_parser_field_count": summary.get(
+ "dry_run_result_parser_field_count", 0
+ ),
+ "receipt_validation_report_count": summary.get(
+ "receipt_validation_report_count", 0
+ ),
+ "receipt_validation_field_count": summary.get(
+ "receipt_validation_field_count", 0
+ ),
+ "rollback_binding_count": summary.get("rollback_binding_count", 0),
+ "post_apply_verifier_binding_count": summary.get(
+ "post_apply_verifier_binding_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get(
+ "same_run_truth_required_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_dry_run_runner_execution_receipt_preflight": (
+ future_database_apply_controlled_dry_run_runner_execution_receipt_preflight
+ ),
+ "controlled_dry_run_command_artifact_closeout": (
+ controlled_dry_run_command_artifact_closeout
+ ),
+ "controlled_dry_run_command_artifact_closeout_contract": (
+ controlled_dry_run_command_artifact_closeout_contract
+ ),
+ "controlled_dry_run_command_artifact_closeout_checks": checks,
+ "source_controlled_dry_run_execution_plan_closeout_summary": summary,
+ "source_controlled_dry_run_execution_plan_closeout_contract": (
+ plan_closeout_contract
+ ),
+ "source_controlled_dry_run_execution_plan_closeout": plan_closeout,
+ "source_database_apply_controlled_dry_run_command_artifact_verification": (
+ future_artifact
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_command_artifact_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run runner execution receipt closeout.",
+ "Keep runner execution receipt preflight non-executing until the dedicated receipt lane is explicit.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the runner receipt preflight without running the runner."""
+ command_artifact_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_preflight = (
+ command_artifact_closeout_result.get(
+ "future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ )
+ or {}
+ )
+ command_closeout = (
+ command_artifact_closeout_result.get(
+ "controlled_dry_run_command_artifact_closeout"
+ )
+ or {}
+ )
+ command_closeout_contract = (
+ command_artifact_closeout_result.get(
+ "controlled_dry_run_command_artifact_closeout_contract"
+ )
+ or {}
+ )
+ summary = command_artifact_closeout_result.get("summary") or {}
+ safety = command_artifact_closeout_result.get("safety") or {}
+ receipt_preflight = command_closeout.get("runner_execution_receipt_preflight") or {}
+ artifact = command_closeout.get("non_executable_command_artifact") or {}
+ execution_plan_binding = command_closeout.get("execution_plan_binding") or {}
+ validation = command_closeout.get("receipt_validation_report") or {}
+ result_parser = command_closeout.get("dry_run_result_parser") or {}
+ rollback_binding = command_closeout.get("rollback_binding") or {}
+ verifier_binding = command_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_runner_execution_receipt_closeout_id(
+ command_artifact_closeout_result
+ )
+ parser_verification_id = f"{closeout_id}-post-receipt-parser-verification"
+ runner_execution_receipt_closeout_fields = [
+ "runner_execution_receipt_closeout_id",
+ "source_command_artifact_closeout_id",
+ "source_runner_execution_receipt_preflight_id",
+ "source_non_executable_command_artifact_id",
+ "source_execution_plan_closeout_id",
+ "dry_run_command_shape_hash",
+ "non_executable_command_artifact_sha256",
+ "post_receipt_parser_verification_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "abort_conditions",
+ ]
+ runner_execution_receipt_closeout_acceptance_gates = [
+ "command_artifact_closeout_ready",
+ "source_chain_ids_match",
+ "runner_execution_receipt_preflight_no_execute",
+ "post_receipt_parser_verification_bound",
+ "post_receipt_parser_blocks_execution",
+ "receipt_closeout_preview_only",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "command_artifact_closeout_contract_blocks_database_apply",
+ "no_secret_signature_or_database_apply",
+ ]
+ post_receipt_parser_verification_fields = [
+ "verification_id",
+ "source_runner_execution_receipt_preflight_id",
+ "source_command_artifact_closeout_id",
+ "expected_preflight_status",
+ "expected_execution_performed",
+ "expected_stdout_included",
+ "expected_stderr_included",
+ "required_command_shape_hash",
+ "execution_required",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_command_artifact_closeout_not_ready",
+ "abort_if_runner_execution_receipt_preflight_requests_execution",
+ "abort_if_receipt_closeout_includes_stdout_or_stderr",
+ "abort_if_post_receipt_parser_requires_execution",
+ "abort_if_command_shape_hash_mismatch",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_endpoint_sql_database_write_or_signature_is_requested",
+ ]
+ receipt_closeout_preview = {
+ "runner_execution_receipt_closeout_id": closeout_id,
+ "source_command_artifact_closeout_id": command_closeout.get(
+ "command_artifact_closeout_id"
+ ),
+ "source_runner_execution_receipt_preflight_id": receipt_preflight.get(
+ "preflight_id"
+ ),
+ "source_non_executable_command_artifact_id": artifact.get("artifact_id"),
+ "source_execution_plan_closeout_id": command_closeout.get(
+ "source_execution_plan_closeout_id"
+ ),
+ "dry_run_command_shape_hash": command_closeout.get(
+ "dry_run_command_shape_hash"
+ ),
+ "non_executable_command_artifact_sha256": artifact.get(
+ "non_executable_command_artifact_sha256"
+ ),
+ "receipt_status": "receipt_closeout_preview_not_executed",
+ "execution_required": False,
+ "execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "runner_execution_authorized": False,
+ "execution_performed": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "database_apply_authorized": False,
+ "ready_for_database_apply_now": False,
+ "writes_database": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "receipt_closeout_preview_field_count": 10,
+ }
+ post_receipt_parser_verification = {
+ "verification_id": parser_verification_id,
+ "source_runner_execution_receipt_closeout_id": closeout_id,
+ "source_runner_execution_receipt_preflight_id": receipt_preflight.get(
+ "preflight_id"
+ ),
+ "source_command_artifact_closeout_id": command_closeout.get(
+ "command_artifact_closeout_id"
+ ),
+ "source_non_executable_command_artifact_id": artifact.get("artifact_id"),
+ "expected_preflight_status": "preflight_only_not_executed",
+ "expected_receipt_status": "receipt_closeout_preview_not_executed",
+ "expected_execution_performed": False,
+ "expected_stdout_included": False,
+ "expected_stderr_included": False,
+ "required_command_shape_hash": command_closeout.get(
+ "dry_run_command_shape_hash"
+ ),
+ "execution_required": False,
+ "stdout_allowed": False,
+ "stderr_allowed": False,
+ "database_apply_authorized": False,
+ "parser_verification_status": "post_receipt_parser_preview_ready",
+ "post_receipt_parser_verification_field_count": len(
+ post_receipt_parser_verification_fields
+ ),
+ "post_receipt_parser_verification_fields": (
+ post_receipt_parser_verification_fields
+ ),
+ }
+ runner_execution_receipt_preflight_no_execute = (
+ receipt_preflight.get("preflight_status") == "preflight_only_not_executed"
+ and receipt_preflight.get("execution_required") is False
+ and receipt_preflight.get("execution_authorized") is False
+ and receipt_preflight.get("dry_run_execution_authorized") is False
+ and receipt_preflight.get("runner_execution_authorized") is False
+ and receipt_preflight.get("shell_execution_included") is False
+ and receipt_preflight.get("endpoint_execution_included") is False
+ and receipt_preflight.get("sql_execution_included") is False
+ and receipt_preflight.get("database_write_included") is False
+ and receipt_preflight.get("stdout_capture_allowed") is False
+ and receipt_preflight.get("stderr_capture_allowed") is False
+ and receipt_preflight.get("execution_performed") is False
+ and receipt_preflight.get("stdout_included") is False
+ and receipt_preflight.get("stderr_included") is False
+ and receipt_preflight.get("database_apply_authorized") is False
+ and receipt_preflight.get("writes_database") is False
+ )
+ post_receipt_parser_verification_bound = (
+ bool(post_receipt_parser_verification.get("verification_id"))
+ and post_receipt_parser_verification.get(
+ "source_runner_execution_receipt_preflight_id"
+ )
+ == receipt_preflight.get("preflight_id")
+ and post_receipt_parser_verification.get(
+ "source_command_artifact_closeout_id"
+ )
+ == command_closeout.get("command_artifact_closeout_id")
+ and post_receipt_parser_verification.get("required_command_shape_hash")
+ == command_closeout.get("dry_run_command_shape_hash")
+ and int(
+ post_receipt_parser_verification.get(
+ "post_receipt_parser_verification_field_count"
+ )
+ or 0
+ )
+ == 10
+ )
+ post_receipt_parser_blocks_execution = (
+ post_receipt_parser_verification.get("expected_preflight_status")
+ == "preflight_only_not_executed"
+ and post_receipt_parser_verification.get("expected_receipt_status")
+ == "receipt_closeout_preview_not_executed"
+ and post_receipt_parser_verification.get("expected_execution_performed")
+ is False
+ and post_receipt_parser_verification.get("expected_stdout_included")
+ is False
+ and post_receipt_parser_verification.get("expected_stderr_included")
+ is False
+ and post_receipt_parser_verification.get("execution_required") is False
+ and post_receipt_parser_verification.get("stdout_allowed") is False
+ and post_receipt_parser_verification.get("stderr_allowed") is False
+ and post_receipt_parser_verification.get("database_apply_authorized")
+ is False
+ )
+ receipt_closeout_preview_only = (
+ receipt_closeout_preview.get("receipt_status")
+ == "receipt_closeout_preview_not_executed"
+ and receipt_closeout_preview.get("execution_required") is False
+ and receipt_closeout_preview.get("execution_authorized") is False
+ and receipt_closeout_preview.get("dry_run_execution_authorized") is False
+ and receipt_closeout_preview.get("runner_execution_authorized") is False
+ and receipt_closeout_preview.get("execution_performed") is False
+ and receipt_closeout_preview.get("stdout_included") is False
+ and receipt_closeout_preview.get("stderr_included") is False
+ and receipt_closeout_preview.get("stdout_capture_allowed") is False
+ and receipt_closeout_preview.get("stderr_capture_allowed") is False
+ and receipt_closeout_preview.get("database_apply_authorized") is False
+ and receipt_closeout_preview.get("writes_database") is False
+ and receipt_closeout_preview.get("executes_endpoint") is False
+ and receipt_closeout_preview.get("executes_sql") is False
+ )
+ source_chain_ids_match = (
+ bool(command_closeout.get("command_artifact_closeout_id"))
+ and command_closeout.get("command_artifact_closeout_id")
+ == future_preflight.get("command_artifact_closeout_id")
+ == receipt_preflight.get("source_command_artifact_closeout_id")
+ == receipt_closeout_preview.get("source_command_artifact_closeout_id")
+ and receipt_preflight.get("preflight_id")
+ == future_preflight.get("runner_execution_receipt_preflight_id")
+ == receipt_closeout_preview.get(
+ "source_runner_execution_receipt_preflight_id"
+ )
+ == post_receipt_parser_verification.get(
+ "source_runner_execution_receipt_preflight_id"
+ )
+ and artifact.get("artifact_id")
+ == command_closeout.get("source_non_executable_command_artifact_id")
+ == receipt_closeout_preview.get("source_non_executable_command_artifact_id")
+ )
+ result_parser_and_validation_carried_forward = (
+ result_parser.get("required_command_shape_hash")
+ == command_closeout.get("dry_run_command_shape_hash")
+ and result_parser.get("execution_required") is False
+ and result_parser.get("stdout_allowed") is False
+ and result_parser.get("stderr_allowed") is False
+ and result_parser.get("database_apply_authorized") is False
+ and validation.get("dry_run_command_shape_hash")
+ == command_closeout.get("dry_run_command_shape_hash")
+ and validation.get("execution_performed") is False
+ and validation.get("stdout_included") is False
+ and validation.get("stderr_included") is False
+ and validation.get("database_apply_authorized") is False
+ and validation.get("executes_endpoint") is False
+ and validation.get("executes_sql") is False
+ and validation.get("writes_database") is False
+ )
+ target_hash_locked = (
+ command_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(command_closeout.get("expected_sha256"))
+ and bool(command_closeout.get("actual_sha256"))
+ and command_closeout.get("expected_sha256")
+ == command_closeout.get("actual_sha256")
+ and command_closeout.get("hash_matches") is True
+ and command_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ command_closeout_contract_blocks_database_apply = (
+ command_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ )
+ is True
+ and command_closeout_contract.get("executes_database_apply") is False
+ and command_closeout_contract.get("database_apply_authorized") is False
+ and command_closeout_contract.get("ready_for_database_apply_now") is False
+ and command_closeout_contract.get("signs_database_apply_authorization")
+ is False
+ and command_closeout_contract.get("writes_database") is False
+ )
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("writes_artifact_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_migration_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_script") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("signs_database_apply_authorization") is False
+ and safety.get("executes_authorization_evidence") is False
+ and safety.get("executes_database_apply") is False
+ and command_closeout.get("accepts_plaintext_secret") is False
+ and command_closeout.get("reads_secret_in_preview") is False
+ and command_closeout.get("signature_material_included") is False
+ and command_closeout.get("secret_material_included") is False
+ and command_closeout.get("signs_database_apply_authorization") is False
+ and command_closeout.get("executes_authorization_evidence") is False
+ and command_closeout.get("executes_database_apply") is False
+ and command_closeout.get("executes_endpoint_in_preview") is False
+ and command_closeout.get("executes_sql_in_preview") is False
+ and command_closeout.get("writes_database_in_preview") is False
+ and runner_execution_receipt_preflight_no_execute
+ and post_receipt_parser_blocks_execution
+ and receipt_closeout_preview_only
+ and result_parser_and_validation_carried_forward
+ )
+ checks = [
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "command_artifact_closeout_ready",
+ command_artifact_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_COMMAND_ARTIFACT_CLOSEOUT_READY"
+ and future_preflight.get(
+ "ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ )
+ is True
+ and future_preflight.get(
+ "can_enter_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout"
+ )
+ is True
+ and command_closeout.get(
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_closeout"
+ )
+ is True,
+ {
+ "result": command_artifact_closeout_result.get("result"),
+ "ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight": (
+ future_preflight.get(
+ "ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ )
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout": (
+ future_preflight.get(
+ "can_enter_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout"
+ )
+ ),
+ },
+ "wait_for_controlled_dry_run_command_artifact_closeout",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "command_artifact_closeout_id": command_closeout.get(
+ "command_artifact_closeout_id"
+ ),
+ "runner_execution_receipt_preflight_id": receipt_preflight.get(
+ "preflight_id"
+ ),
+ "artifact_id": artifact.get("artifact_id"),
+ },
+ "wait_for_runner_execution_receipt_source_chain_ids",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "runner_execution_receipt_closeout_contract_complete",
+ len(runner_execution_receipt_closeout_fields) == 12
+ and len(runner_execution_receipt_closeout_acceptance_gates) == 10
+ and len(post_receipt_parser_verification_fields) == 10
+ and "post_receipt_parser_verification_id"
+ in runner_execution_receipt_closeout_fields
+ and "post_receipt_parser_verification_bound"
+ in runner_execution_receipt_closeout_acceptance_gates,
+ {
+ "runner_execution_receipt_closeout_field_count": len(
+ runner_execution_receipt_closeout_fields
+ ),
+ "runner_execution_receipt_closeout_acceptance_gate_count": len(
+ runner_execution_receipt_closeout_acceptance_gates
+ ),
+ "post_receipt_parser_verification_field_count": len(
+ post_receipt_parser_verification_fields
+ ),
+ },
+ "wait_for_runner_execution_receipt_closeout_contract",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "runner_execution_receipt_preflight_no_execute",
+ runner_execution_receipt_preflight_no_execute,
+ {
+ "execution_required": receipt_preflight.get("execution_required"),
+ "execution_authorized": receipt_preflight.get(
+ "execution_authorized"
+ ),
+ "stdout_capture_allowed": receipt_preflight.get(
+ "stdout_capture_allowed"
+ ),
+ "writes_database": receipt_preflight.get("writes_database"),
+ },
+ "abort_if_runner_execution_receipt_preflight_executes",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "post_receipt_parser_verification_bound",
+ post_receipt_parser_verification_bound,
+ {
+ "verification_id": post_receipt_parser_verification.get(
+ "verification_id"
+ ),
+ "source_runner_execution_receipt_preflight_id": (
+ post_receipt_parser_verification.get(
+ "source_runner_execution_receipt_preflight_id"
+ )
+ ),
+ "post_receipt_parser_verification_field_count": (
+ post_receipt_parser_verification.get(
+ "post_receipt_parser_verification_field_count"
+ )
+ ),
+ },
+ "wait_for_post_receipt_parser_verification_binding",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "post_receipt_parser_blocks_execution",
+ post_receipt_parser_blocks_execution,
+ {
+ "expected_execution_performed": (
+ post_receipt_parser_verification.get(
+ "expected_execution_performed"
+ )
+ ),
+ "execution_required": post_receipt_parser_verification.get(
+ "execution_required"
+ ),
+ "database_apply_authorized": post_receipt_parser_verification.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_post_receipt_parser_requires_execution",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "receipt_closeout_preview_only",
+ receipt_closeout_preview_only,
+ {
+ "receipt_status": receipt_closeout_preview.get("receipt_status"),
+ "execution_performed": receipt_closeout_preview.get(
+ "execution_performed"
+ ),
+ "stdout_included": receipt_closeout_preview.get("stdout_included"),
+ "writes_database": receipt_closeout_preview.get("writes_database"),
+ },
+ "abort_if_receipt_closeout_preview_contains_execution",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "result_parser_and_receipt_validation_carried_forward",
+ result_parser_and_validation_carried_forward,
+ {
+ "parser_id": result_parser.get("parser_id"),
+ "receipt_validation_status": validation.get(
+ "receipt_validation_status"
+ ),
+ "dry_run_command_shape_hash": command_closeout.get(
+ "dry_run_command_shape_hash"
+ ),
+ },
+ "wait_for_result_parser_and_receipt_validation_carry_forward",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": command_closeout.get("target_file"),
+ "expected_sha256_present": bool(command_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(command_closeout.get("actual_sha256")),
+ "hash_matches": command_closeout.get("hash_matches"),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "rollback_and_post_apply_verifier_bindings_carried_forward",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "command_artifact_closeout_contract_blocks_database_apply",
+ command_closeout_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight": (
+ command_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ )
+ ),
+ "database_apply_authorized": command_closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": command_closeout_contract.get("writes_database"),
+ },
+ "abort_if_command_artifact_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_runner_execution_receipt_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "receipt_closeout_preview_only": receipt_closeout_preview_only,
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_EXECUTION_RECEIPT_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_COMMAND_ARTIFACT_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_post_receipt_parser_verification = {
+ "runner_execution_receipt_closeout_id": closeout_id,
+ "post_receipt_parser_verification_id": parser_verification_id,
+ "source_command_artifact_closeout_id": command_closeout.get(
+ "command_artifact_closeout_id"
+ ),
+ "source_runner_execution_receipt_preflight_id": receipt_preflight.get(
+ "preflight_id"
+ ),
+ "source_non_executable_command_artifact_id": artifact.get("artifact_id"),
+ "non_executable_command_artifact_sha256": artifact.get(
+ "non_executable_command_artifact_sha256"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_post_receipt_parser_verification": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_post_receipt_parser_closeout": (
+ closeout_ready
+ ),
+ "runner_execution_receipt_closeout_ready": closeout_ready,
+ "post_receipt_parser_verification_bound": closeout_ready,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_runner_execution_receipt_closeout = {
+ "runner_execution_receipt_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_runner_execution_receipt_closeout"
+ ),
+ "source_command_artifact_closeout_id": command_closeout.get(
+ "command_artifact_closeout_id"
+ ),
+ "source_runner_execution_receipt_preflight_id": receipt_preflight.get(
+ "preflight_id"
+ ),
+ "source_non_executable_command_artifact_id": artifact.get("artifact_id"),
+ "source_execution_plan_closeout_id": command_closeout.get(
+ "source_execution_plan_closeout_id"
+ ),
+ "source_execution_plan_binding_id": command_closeout.get(
+ "source_execution_plan_binding_id"
+ ),
+ "source_runner_readiness_id": command_closeout.get(
+ "source_runner_readiness_id"
+ ),
+ "source_receipt_closeout_id": command_closeout.get(
+ "source_receipt_closeout_id"
+ ),
+ "source_dry_run_package_id": command_closeout.get("source_dry_run_package_id"),
+ "dry_run_command_shape_hash": command_closeout.get(
+ "dry_run_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout": (
+ closeout_ready
+ ),
+ "runner_execution_receipt_closeout_fields": (
+ runner_execution_receipt_closeout_fields
+ ),
+ "runner_execution_receipt_closeout_field_count": len(
+ runner_execution_receipt_closeout_fields
+ ),
+ "runner_execution_receipt_closeout_acceptance_gates": (
+ runner_execution_receipt_closeout_acceptance_gates
+ ),
+ "runner_execution_receipt_closeout_acceptance_gate_count": len(
+ runner_execution_receipt_closeout_acceptance_gates
+ ),
+ "receipt_closeout_preview": receipt_closeout_preview,
+ "receipt_closeout_preview_count": 1,
+ "post_receipt_parser_verification": post_receipt_parser_verification,
+ "post_receipt_parser_verification_count": 1,
+ "post_receipt_parser_verification_field_count": len(
+ post_receipt_parser_verification_fields
+ ),
+ "runner_execution_receipt_preflight": receipt_preflight,
+ "runner_execution_receipt_preflight_count": 1,
+ "non_executable_command_artifact": artifact,
+ "non_executable_command_artifact_count": 1,
+ "non_executable_command_artifact_sha256": artifact.get(
+ "non_executable_command_artifact_sha256"
+ ),
+ "execution_plan_binding": execution_plan_binding,
+ "execution_plan_binding_count": 1,
+ "dry_run_result_parser": result_parser,
+ "dry_run_result_parser_count": 1,
+ "receipt_validation_report": validation,
+ "receipt_validation_report_count": 1,
+ "target_file": command_closeout.get("target_file"),
+ "expected_sha256": command_closeout.get("expected_sha256"),
+ "actual_sha256": command_closeout.get("actual_sha256"),
+ "hash_matches": command_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "runner_execution_receipt_closeout_only": True,
+ "post_receipt_parser_verification_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_runner_execution_receipt_closeout_contract = {
+ "mode": "controlled_dry_run_runner_execution_receipt_closeout_and_post_receipt_parser_verification_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-runner-execution-receipt-closeout"
+ ),
+ "source_command_artifact_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-command-artifact-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_post_receipt_parser_verification": (
+ closeout_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_EXECUTION_RECEIPT_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(command_artifact_closeout_result.get("success")),
+ "generated_at": command_artifact_closeout_result.get("generated_at"),
+ "source_policy": command_artifact_closeout_result.get("policy"),
+ "stats": command_artifact_closeout_result.get("stats") or {},
+ "summary": {
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_command_artifact_closeout_ready_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_command_artifact_closeout_check_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_check_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_ready_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_check_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_check_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_ready_count": summary.get(
+ "controlled_dry_run_runner_readiness_ready_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_check_count": summary.get(
+ "controlled_dry_run_runner_readiness_check_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_ready_count": summary.get(
+ "controlled_dry_run_receipt_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_check_count": summary.get(
+ "controlled_dry_run_receipt_closeout_check_count", 0
+ ),
+ "controlled_dry_run_package_ready_count": summary.get(
+ "controlled_dry_run_package_ready_count", 0
+ ),
+ "controlled_dry_run_package_check_count": summary.get(
+ "controlled_dry_run_package_check_count", 0
+ ),
+ "controlled_apply_final_preflight_ready_count": summary.get(
+ "controlled_apply_final_preflight_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_check_count": summary.get(
+ "controlled_apply_final_preflight_check_count", 0
+ ),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_count": 1,
+ "controlled_dry_run_runner_execution_receipt_closeout_field_count": len(
+ runner_execution_receipt_closeout_fields
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_acceptance_gate_count": len(
+ runner_execution_receipt_closeout_acceptance_gates
+ ),
+ "post_receipt_parser_verification_count": 1,
+ "post_receipt_parser_verification_field_count": len(
+ post_receipt_parser_verification_fields
+ ),
+ "receipt_closeout_preview_count": 1,
+ "controlled_dry_run_command_artifact_closeout_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_count", 0
+ ),
+ "controlled_dry_run_command_artifact_closeout_field_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_field_count", 0
+ ),
+ "controlled_dry_run_command_artifact_closeout_acceptance_gate_count": (
+ summary.get(
+ "controlled_dry_run_command_artifact_closeout_acceptance_gate_count",
+ 0,
+ )
+ ),
+ "runner_execution_receipt_preflight_count": summary.get(
+ "runner_execution_receipt_preflight_count", 0
+ ),
+ "runner_execution_receipt_preflight_field_count": summary.get(
+ "runner_execution_receipt_preflight_field_count", 0
+ ),
+ "non_executable_command_artifact_count": summary.get(
+ "non_executable_command_artifact_count", 0
+ ),
+ "non_executable_command_artifact_field_count": summary.get(
+ "non_executable_command_artifact_field_count", 0
+ ),
+ "execution_plan_binding_count": summary.get(
+ "execution_plan_binding_count", 0
+ ),
+ "execution_plan_binding_field_count": summary.get(
+ "execution_plan_binding_field_count", 0
+ ),
+ "dry_run_result_parser_count": summary.get(
+ "dry_run_result_parser_count", 0
+ ),
+ "dry_run_result_parser_field_count": summary.get(
+ "dry_run_result_parser_field_count", 0
+ ),
+ "receipt_validation_report_count": summary.get(
+ "receipt_validation_report_count", 0
+ ),
+ "receipt_validation_field_count": summary.get(
+ "receipt_validation_field_count", 0
+ ),
+ "rollback_binding_count": summary.get("rollback_binding_count", 0),
+ "post_apply_verifier_binding_count": summary.get(
+ "post_apply_verifier_binding_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get(
+ "same_run_truth_required_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_dry_run_post_receipt_parser_verification": (
+ future_database_apply_controlled_dry_run_post_receipt_parser_verification
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout": (
+ controlled_dry_run_runner_execution_receipt_closeout
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_contract": (
+ controlled_dry_run_runner_execution_receipt_closeout_contract
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_checks": checks,
+ "source_controlled_dry_run_command_artifact_closeout_summary": summary,
+ "source_controlled_dry_run_command_artifact_closeout_contract": (
+ command_closeout_contract
+ ),
+ "source_controlled_dry_run_command_artifact_closeout": command_closeout,
+ "source_database_apply_controlled_dry_run_runner_execution_receipt_preflight": (
+ future_preflight
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_runner_execution_receipt_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run post-receipt parser closeout.",
+ "Keep parser verification preview-only until a dedicated execution lane is explicit.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the post-receipt parser and bind no-apply enforcement."""
+ runner_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_parser = (
+ runner_closeout_result.get(
+ "future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ )
+ or {}
+ )
+ runner_closeout = (
+ runner_closeout_result.get(
+ "controlled_dry_run_runner_execution_receipt_closeout"
+ )
+ or {}
+ )
+ runner_contract = (
+ runner_closeout_result.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_contract"
+ )
+ or {}
+ )
+ summary = runner_closeout_result.get("summary") or {}
+ safety = runner_closeout_result.get("safety") or {}
+ parser = runner_closeout.get("post_receipt_parser_verification") or {}
+ preview = runner_closeout.get("receipt_closeout_preview") or {}
+ preflight = runner_closeout.get("runner_execution_receipt_preflight") or {}
+ artifact = runner_closeout.get("non_executable_command_artifact") or {}
+ result_parser = runner_closeout.get("dry_run_result_parser") or {}
+ validation = runner_closeout.get("receipt_validation_report") or {}
+ rollback_binding = runner_closeout.get("rollback_binding") or {}
+ verifier_binding = runner_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_post_receipt_parser_closeout_id(
+ runner_closeout_result
+ )
+ no_apply_enforcement_verification_id = (
+ f"{closeout_id}-no-apply-enforcement-verification"
+ )
+ post_receipt_parser_closeout_fields = [
+ "post_receipt_parser_closeout_id",
+ "source_runner_execution_receipt_closeout_id",
+ "source_post_receipt_parser_verification_id",
+ "source_command_artifact_closeout_id",
+ "source_non_executable_command_artifact_id",
+ "source_receipt_closeout_preview_status",
+ "dry_run_command_shape_hash",
+ "no_apply_enforcement_verification_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "abort_conditions",
+ ]
+ post_receipt_parser_closeout_acceptance_gates = [
+ "runner_execution_receipt_closeout_ready",
+ "source_chain_ids_match",
+ "post_receipt_parser_verification_ready",
+ "post_receipt_parser_blocks_execution",
+ "receipt_closeout_preview_not_executed",
+ "no_apply_enforcement_verification_bound",
+ "no_apply_enforcement_blocks_endpoint_sql_db_write",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ no_apply_enforcement_verification_fields = [
+ "verification_id",
+ "source_post_receipt_parser_verification_id",
+ "source_runner_execution_receipt_closeout_id",
+ "required_parser_status",
+ "required_receipt_status",
+ "required_command_shape_hash",
+ "expected_execution_performed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_write_allowed",
+ "database_apply_authorized",
+ "enforcement_status",
+ ]
+ abort_conditions = [
+ "abort_if_runner_execution_receipt_closeout_not_ready",
+ "abort_if_post_receipt_parser_verification_missing",
+ "abort_if_parser_verification_requires_execution",
+ "abort_if_receipt_closeout_was_executed",
+ "abort_if_receipt_closeout_includes_stdout_or_stderr",
+ "abort_if_no_apply_enforcement_allows_endpoint_or_sql",
+ "abort_if_no_apply_enforcement_allows_database_write_or_apply",
+ "abort_if_command_shape_hash_mismatch",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ no_apply_enforcement_verification = {
+ "verification_id": no_apply_enforcement_verification_id,
+ "source_post_receipt_parser_closeout_id": closeout_id,
+ "source_post_receipt_parser_verification_id": parser.get("verification_id"),
+ "source_runner_execution_receipt_closeout_id": runner_closeout.get(
+ "runner_execution_receipt_closeout_id"
+ ),
+ "source_command_artifact_closeout_id": runner_closeout.get(
+ "source_command_artifact_closeout_id"
+ ),
+ "required_parser_status": "post_receipt_parser_preview_ready",
+ "required_receipt_status": "receipt_closeout_preview_not_executed",
+ "required_command_shape_hash": parser.get("required_command_shape_hash"),
+ "expected_execution_performed": False,
+ "expected_stdout_included": False,
+ "expected_stderr_included": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "enforcement_status": "no_apply_enforcement_preview_ready",
+ "no_apply_enforcement_verification_field_count": len(
+ no_apply_enforcement_verification_fields
+ ),
+ "no_apply_enforcement_verification_fields": (
+ no_apply_enforcement_verification_fields
+ ),
+ }
+ runner_closeout_ready = (
+ runner_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_EXECUTION_RECEIPT_CLOSEOUT_READY"
+ and summary.get("controlled_dry_run_runner_execution_receipt_closeout_ready_count")
+ == 1
+ and summary.get("controlled_dry_run_runner_execution_receipt_closeout_pass_count")
+ == summary.get("controlled_dry_run_runner_execution_receipt_closeout_check_count")
+ )
+ source_chain_ids_match = (
+ bool(runner_closeout.get("runner_execution_receipt_closeout_id"))
+ and runner_closeout.get("runner_execution_receipt_closeout_id")
+ == future_parser.get("runner_execution_receipt_closeout_id")
+ == no_apply_enforcement_verification.get(
+ "source_runner_execution_receipt_closeout_id"
+ )
+ and parser.get("verification_id")
+ == future_parser.get("post_receipt_parser_verification_id")
+ == no_apply_enforcement_verification.get(
+ "source_post_receipt_parser_verification_id"
+ )
+ and artifact.get("artifact_id")
+ == runner_closeout.get("source_non_executable_command_artifact_id")
+ )
+ post_receipt_parser_verification_ready = (
+ parser.get("parser_verification_status") == "post_receipt_parser_preview_ready"
+ and parser.get("expected_preflight_status") == "preflight_only_not_executed"
+ and parser.get("expected_receipt_status")
+ == "receipt_closeout_preview_not_executed"
+ and parser.get("verification_id")
+ == future_parser.get("post_receipt_parser_verification_id")
+ and int(parser.get("post_receipt_parser_verification_field_count") or 0)
+ == 10
+ )
+ post_receipt_parser_blocks_execution = (
+ parser.get("expected_execution_performed") is False
+ and parser.get("expected_stdout_included") is False
+ and parser.get("expected_stderr_included") is False
+ and parser.get("execution_required") is False
+ and parser.get("stdout_allowed") is False
+ and parser.get("stderr_allowed") is False
+ and parser.get("database_apply_authorized") is False
+ and no_apply_enforcement_verification.get("expected_execution_performed")
+ is False
+ )
+ receipt_closeout_preview_not_executed = (
+ preview.get("receipt_status") == "receipt_closeout_preview_not_executed"
+ and preview.get("execution_required") is False
+ and preview.get("execution_performed") is False
+ and preview.get("stdout_included") is False
+ and preview.get("stderr_included") is False
+ and preview.get("stdout_capture_allowed") is False
+ and preview.get("stderr_capture_allowed") is False
+ and preview.get("database_apply_authorized") is False
+ and preview.get("writes_database") is False
+ and preview.get("executes_endpoint") is False
+ and preview.get("executes_sql") is False
+ )
+ no_apply_enforcement_verification_bound = (
+ bool(no_apply_enforcement_verification.get("verification_id"))
+ and no_apply_enforcement_verification.get(
+ "source_post_receipt_parser_closeout_id"
+ )
+ == closeout_id
+ and no_apply_enforcement_verification.get(
+ "source_post_receipt_parser_verification_id"
+ )
+ == parser.get("verification_id")
+ and no_apply_enforcement_verification.get("required_command_shape_hash")
+ == parser.get("required_command_shape_hash")
+ and int(
+ no_apply_enforcement_verification.get(
+ "no_apply_enforcement_verification_field_count"
+ )
+ or 0
+ )
+ == len(no_apply_enforcement_verification_fields)
+ )
+ no_apply_enforcement_blocks_endpoint_sql_db_write = (
+ no_apply_enforcement_verification.get("endpoint_execution_allowed") is False
+ and no_apply_enforcement_verification.get("sql_execution_allowed") is False
+ and no_apply_enforcement_verification.get("database_write_allowed") is False
+ and no_apply_enforcement_verification.get("database_apply_authorized")
+ is False
+ and no_apply_enforcement_verification.get("executes_endpoint") is False
+ and no_apply_enforcement_verification.get("executes_sql") is False
+ and no_apply_enforcement_verification.get("writes_database") is False
+ and no_apply_enforcement_verification.get("executes_database_apply")
+ is False
+ )
+ result_parser_and_validation_carried_forward = (
+ result_parser.get("required_command_shape_hash")
+ == parser.get("required_command_shape_hash")
+ and result_parser.get("execution_required") is False
+ and result_parser.get("stdout_allowed") is False
+ and result_parser.get("stderr_allowed") is False
+ and result_parser.get("database_apply_authorized") is False
+ and validation.get("receipt_validation_status")
+ == "preview_validated_not_executed"
+ and validation.get("execution_performed") is False
+ and validation.get("stdout_included") is False
+ and validation.get("stderr_included") is False
+ and validation.get("database_apply_authorized") is False
+ and validation.get("executes_endpoint") is False
+ and validation.get("executes_sql") is False
+ and validation.get("writes_database") is False
+ )
+ target_hash_locked = (
+ runner_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(runner_closeout.get("expected_sha256"))
+ and bool(runner_closeout.get("actual_sha256"))
+ and runner_closeout.get("expected_sha256")
+ == runner_closeout.get("actual_sha256")
+ and runner_closeout.get("hash_matches") is True
+ and runner_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ runner_contract_blocks_database_apply = (
+ runner_contract.get("executes_database_apply") is False
+ and runner_contract.get("executes_endpoint") is False
+ and runner_contract.get("executes_sql") is False
+ and runner_contract.get("database_apply_authorized") is False
+ and runner_contract.get("ready_for_database_apply_now") is False
+ and runner_contract.get("signs_database_apply_authorization") is False
+ and runner_contract.get("writes_database") is False
+ and runner_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and no_apply_enforcement_blocks_endpoint_sql_db_write
+ )
+ checks = [
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "runner_execution_receipt_closeout_ready",
+ runner_closeout_ready,
+ {
+ "result": runner_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count"
+ ),
+ },
+ "wait_for_runner_execution_receipt_closeout_ready",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "runner_execution_receipt_closeout_id": runner_closeout.get(
+ "runner_execution_receipt_closeout_id"
+ ),
+ "post_receipt_parser_verification_id": parser.get(
+ "verification_id"
+ ),
+ "artifact_id": artifact.get("artifact_id"),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "post_receipt_parser_verification_ready",
+ post_receipt_parser_verification_ready,
+ {
+ "parser_verification_status": parser.get(
+ "parser_verification_status"
+ ),
+ "verification_id": parser.get("verification_id"),
+ "field_count": parser.get(
+ "post_receipt_parser_verification_field_count"
+ ),
+ },
+ "wait_for_post_receipt_parser_verification_ready",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "post_receipt_parser_blocks_execution",
+ post_receipt_parser_blocks_execution,
+ {
+ "expected_execution_performed": parser.get(
+ "expected_execution_performed"
+ ),
+ "execution_required": parser.get("execution_required"),
+ "database_apply_authorized": parser.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_post_receipt_parser_allows_execution",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "receipt_closeout_preview_not_executed",
+ receipt_closeout_preview_not_executed,
+ {
+ "receipt_status": preview.get("receipt_status"),
+ "execution_performed": preview.get("execution_performed"),
+ "stdout_included": preview.get("stdout_included"),
+ "stderr_included": preview.get("stderr_included"),
+ },
+ "abort_if_receipt_closeout_was_executed",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "no_apply_enforcement_verification_bound",
+ no_apply_enforcement_verification_bound,
+ {
+ "verification_id": no_apply_enforcement_verification.get(
+ "verification_id"
+ ),
+ "source_post_receipt_parser_verification_id": (
+ no_apply_enforcement_verification.get(
+ "source_post_receipt_parser_verification_id"
+ )
+ ),
+ "field_count": no_apply_enforcement_verification.get(
+ "no_apply_enforcement_verification_field_count"
+ ),
+ },
+ "wait_for_no_apply_enforcement_verification_binding",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "no_apply_enforcement_blocks_endpoint_sql_db_write",
+ no_apply_enforcement_blocks_endpoint_sql_db_write,
+ {
+ "endpoint_execution_allowed": no_apply_enforcement_verification.get(
+ "endpoint_execution_allowed"
+ ),
+ "sql_execution_allowed": no_apply_enforcement_verification.get(
+ "sql_execution_allowed"
+ ),
+ "database_write_allowed": no_apply_enforcement_verification.get(
+ "database_write_allowed"
+ ),
+ "database_apply_authorized": no_apply_enforcement_verification.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_no_apply_enforcement_allows_endpoint_sql_or_db_write",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "result_parser_and_receipt_validation_carried_forward",
+ result_parser_and_validation_carried_forward,
+ {
+ "parser_id": result_parser.get("parser_id"),
+ "receipt_validation_status": validation.get(
+ "receipt_validation_status"
+ ),
+ "dry_run_command_shape_hash": parser.get(
+ "required_command_shape_hash"
+ ),
+ },
+ "wait_for_result_parser_and_receipt_validation_carry_forward",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": runner_closeout.get("target_file"),
+ "hash_matches": runner_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(runner_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(runner_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "rollback_and_post_apply_verifier_bindings_carried_forward",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "runner_execution_receipt_contract_blocks_database_apply",
+ runner_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_post_receipt_parser_verification": (
+ runner_contract.get(
+ "permits_future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ )
+ ),
+ "database_apply_authorized": runner_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": runner_contract.get("writes_database"),
+ },
+ "abort_if_runner_execution_receipt_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_post_receipt_parser_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_POST_RECEIPT_PARSER_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_EXECUTION_RECEIPT_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_no_apply_enforcement_verification = {
+ "post_receipt_parser_closeout_id": closeout_id,
+ "no_apply_enforcement_verification_id": no_apply_enforcement_verification_id,
+ "source_runner_execution_receipt_closeout_id": runner_closeout.get(
+ "runner_execution_receipt_closeout_id"
+ ),
+ "source_post_receipt_parser_verification_id": parser.get("verification_id"),
+ "source_non_executable_command_artifact_id": artifact.get("artifact_id"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_no_apply_enforcement_verification": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_no_apply_enforcement_closeout": (
+ closeout_ready
+ ),
+ "post_receipt_parser_closeout_ready": closeout_ready,
+ "no_apply_enforcement_verification_bound": closeout_ready,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_post_receipt_parser_closeout = {
+ "post_receipt_parser_closeout_id": closeout_id,
+ "authorization_material_type": "controlled_dry_run_post_receipt_parser_closeout",
+ "source_runner_execution_receipt_closeout_id": runner_closeout.get(
+ "runner_execution_receipt_closeout_id"
+ ),
+ "source_post_receipt_parser_verification_id": parser.get("verification_id"),
+ "source_command_artifact_closeout_id": runner_closeout.get(
+ "source_command_artifact_closeout_id"
+ ),
+ "source_non_executable_command_artifact_id": artifact.get("artifact_id"),
+ "source_receipt_closeout_preview_status": preview.get("receipt_status"),
+ "dry_run_command_shape_hash": parser.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_post_receipt_parser_closeout": (
+ closeout_ready
+ ),
+ "post_receipt_parser_closeout_fields": post_receipt_parser_closeout_fields,
+ "post_receipt_parser_closeout_field_count": len(
+ post_receipt_parser_closeout_fields
+ ),
+ "post_receipt_parser_closeout_acceptance_gates": (
+ post_receipt_parser_closeout_acceptance_gates
+ ),
+ "post_receipt_parser_closeout_acceptance_gate_count": len(
+ post_receipt_parser_closeout_acceptance_gates
+ ),
+ "post_receipt_parser_verification": parser,
+ "post_receipt_parser_verification_count": 1,
+ "receipt_closeout_preview": preview,
+ "receipt_closeout_preview_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement_verification,
+ "no_apply_enforcement_verification_count": 1,
+ "no_apply_enforcement_verification_field_count": len(
+ no_apply_enforcement_verification_fields
+ ),
+ "runner_execution_receipt_preflight": preflight,
+ "runner_execution_receipt_preflight_count": 1,
+ "runner_execution_receipt_closeout": runner_closeout,
+ "runner_execution_receipt_closeout_count": 1,
+ "non_executable_command_artifact": artifact,
+ "non_executable_command_artifact_count": 1,
+ "dry_run_result_parser": result_parser,
+ "dry_run_result_parser_count": 1,
+ "receipt_validation_report": validation,
+ "receipt_validation_report_count": 1,
+ "target_file": runner_closeout.get("target_file"),
+ "expected_sha256": runner_closeout.get("expected_sha256"),
+ "actual_sha256": runner_closeout.get("actual_sha256"),
+ "hash_matches": runner_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "post_receipt_parser_closeout_only": True,
+ "no_apply_enforcement_verification_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_post_receipt_parser_closeout_contract = {
+ "mode": "controlled_dry_run_post_receipt_parser_closeout_and_no_apply_enforcement_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-post-receipt-parser-closeout"
+ ),
+ "source_runner_execution_receipt_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-runner-execution-receipt-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_no_apply_enforcement_verification": (
+ closeout_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_POST_RECEIPT_PARSER_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(runner_closeout_result.get("success")),
+ "generated_at": runner_closeout_result.get("generated_at"),
+ "source_policy": runner_closeout_result.get("policy"),
+ "stats": runner_closeout_result.get("stats") or {},
+ "summary": {
+ "controlled_dry_run_post_receipt_parser_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count": (
+ summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count": (
+ summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_command_artifact_closeout_ready_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_command_artifact_closeout_check_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_check_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_ready_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_check_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_check_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_ready_count": summary.get(
+ "controlled_dry_run_runner_readiness_ready_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_check_count": summary.get(
+ "controlled_dry_run_runner_readiness_check_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_ready_count": summary.get(
+ "controlled_dry_run_receipt_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_check_count": summary.get(
+ "controlled_dry_run_receipt_closeout_check_count", 0
+ ),
+ "controlled_dry_run_package_ready_count": summary.get(
+ "controlled_dry_run_package_ready_count", 0
+ ),
+ "controlled_dry_run_package_check_count": summary.get(
+ "controlled_dry_run_package_check_count", 0
+ ),
+ "controlled_apply_final_preflight_ready_count": summary.get(
+ "controlled_apply_final_preflight_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_check_count": summary.get(
+ "controlled_apply_final_preflight_check_count", 0
+ ),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_count": 1,
+ "controlled_dry_run_post_receipt_parser_closeout_field_count": len(
+ post_receipt_parser_closeout_fields
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_acceptance_gate_count": len(
+ post_receipt_parser_closeout_acceptance_gates
+ ),
+ "no_apply_enforcement_verification_count": 1,
+ "no_apply_enforcement_verification_field_count": len(
+ no_apply_enforcement_verification_fields
+ ),
+ "post_receipt_parser_verification_count": summary.get(
+ "post_receipt_parser_verification_count", 0
+ ),
+ "post_receipt_parser_verification_field_count": summary.get(
+ "post_receipt_parser_verification_field_count", 0
+ ),
+ "receipt_closeout_preview_count": summary.get(
+ "receipt_closeout_preview_count", 0
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_count": summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_count", 0
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_field_count": (
+ summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_field_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_acceptance_gate_count": (
+ summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_acceptance_gate_count",
+ 0,
+ )
+ ),
+ "runner_execution_receipt_preflight_count": summary.get(
+ "runner_execution_receipt_preflight_count", 0
+ ),
+ "runner_execution_receipt_preflight_field_count": summary.get(
+ "runner_execution_receipt_preflight_field_count", 0
+ ),
+ "non_executable_command_artifact_count": summary.get(
+ "non_executable_command_artifact_count", 0
+ ),
+ "non_executable_command_artifact_field_count": summary.get(
+ "non_executable_command_artifact_field_count", 0
+ ),
+ "dry_run_result_parser_count": summary.get(
+ "dry_run_result_parser_count", 0
+ ),
+ "dry_run_result_parser_field_count": summary.get(
+ "dry_run_result_parser_field_count", 0
+ ),
+ "receipt_validation_report_count": summary.get(
+ "receipt_validation_report_count", 0
+ ),
+ "receipt_validation_field_count": summary.get(
+ "receipt_validation_field_count", 0
+ ),
+ "rollback_binding_count": summary.get("rollback_binding_count", 0),
+ "post_apply_verifier_binding_count": summary.get(
+ "post_apply_verifier_binding_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get(
+ "same_run_truth_required_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_dry_run_no_apply_enforcement_verification": (
+ future_database_apply_controlled_dry_run_no_apply_enforcement_verification
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout": (
+ controlled_dry_run_post_receipt_parser_closeout
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_contract": (
+ controlled_dry_run_post_receipt_parser_closeout_contract
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_checks": checks,
+ "source_controlled_dry_run_runner_execution_receipt_closeout_summary": summary,
+ "source_controlled_dry_run_runner_execution_receipt_closeout_contract": (
+ runner_contract
+ ),
+ "source_controlled_dry_run_runner_execution_receipt_closeout": (
+ runner_closeout
+ ),
+ "source_database_apply_controlled_dry_run_post_receipt_parser_verification": (
+ future_parser
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_post_receipt_parser_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run no-apply enforcement closeout.",
+ "Keep no-apply enforcement machine-verifiable and exception-only; do not introduce manual review as the default path.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out no-apply enforcement and bind the final dry-run executor guard."""
+ parser_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_enforcement = (
+ parser_closeout_result.get(
+ "future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ )
+ or {}
+ )
+ parser_closeout = (
+ parser_closeout_result.get("controlled_dry_run_post_receipt_parser_closeout")
+ or {}
+ )
+ parser_closeout_contract = (
+ parser_closeout_result.get(
+ "controlled_dry_run_post_receipt_parser_closeout_contract"
+ )
+ or {}
+ )
+ summary = parser_closeout_result.get("summary") or {}
+ safety = parser_closeout_result.get("safety") or {}
+ no_apply_enforcement = (
+ parser_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ parser = parser_closeout.get("post_receipt_parser_verification") or {}
+ preview = parser_closeout.get("receipt_closeout_preview") or {}
+ result_parser = parser_closeout.get("dry_run_result_parser") or {}
+ validation = parser_closeout.get("receipt_validation_report") or {}
+ rollback_binding = parser_closeout.get("rollback_binding") or {}
+ verifier_binding = parser_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_no_apply_enforcement_closeout_id(
+ parser_closeout_result
+ )
+ final_guard_id = f"{closeout_id}-final-dry-run-executor-guard"
+ no_apply_enforcement_closeout_fields = [
+ "no_apply_enforcement_closeout_id",
+ "source_post_receipt_parser_closeout_id",
+ "source_no_apply_enforcement_verification_id",
+ "source_runner_execution_receipt_closeout_id",
+ "source_command_artifact_closeout_id",
+ "dry_run_command_shape_hash",
+ "final_dry_run_executor_guard_id",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_write_allowed",
+ "target_migration_file",
+ "abort_conditions",
+ ]
+ no_apply_enforcement_closeout_acceptance_gates = [
+ "post_receipt_parser_closeout_ready",
+ "source_chain_ids_match",
+ "no_apply_enforcement_verification_ready",
+ "no_apply_blocks_endpoint_sql_db_write",
+ "final_dry_run_executor_guard_bound",
+ "final_executor_guard_blocks_execution",
+ "parser_and_receipt_preview_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ final_dry_run_executor_guard_fields = [
+ "guard_id",
+ "source_no_apply_enforcement_closeout_id",
+ "source_no_apply_enforcement_verification_id",
+ "required_enforcement_status",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_write_allowed",
+ "dry_run_executor_invocation_allowed",
+ "stdout_capture_allowed",
+ "stderr_capture_allowed",
+ "database_apply_authorized",
+ "guard_status",
+ ]
+ abort_conditions = [
+ "abort_if_post_receipt_parser_closeout_not_ready",
+ "abort_if_no_apply_enforcement_verification_missing",
+ "abort_if_no_apply_enforcement_allows_endpoint_execution",
+ "abort_if_no_apply_enforcement_allows_sql_execution",
+ "abort_if_no_apply_enforcement_allows_database_write",
+ "abort_if_final_dry_run_executor_guard_allows_invocation",
+ "abort_if_stdout_or_stderr_capture_is_allowed",
+ "abort_if_database_apply_authorization_is_present",
+ "abort_if_command_shape_hash_mismatch",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ final_dry_run_executor_guard = {
+ "guard_id": final_guard_id,
+ "source_no_apply_enforcement_closeout_id": closeout_id,
+ "source_post_receipt_parser_closeout_id": parser_closeout.get(
+ "post_receipt_parser_closeout_id"
+ ),
+ "source_no_apply_enforcement_verification_id": no_apply_enforcement.get(
+ "verification_id"
+ ),
+ "source_runner_execution_receipt_closeout_id": parser_closeout.get(
+ "source_runner_execution_receipt_closeout_id"
+ ),
+ "source_command_artifact_closeout_id": parser_closeout.get(
+ "source_command_artifact_closeout_id"
+ ),
+ "required_enforcement_status": "no_apply_enforcement_preview_ready",
+ "required_command_shape_hash": parser_closeout.get(
+ "dry_run_command_shape_hash"
+ ),
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "database_apply_authorized": False,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "guard_status": "final_dry_run_executor_guard_preview_ready",
+ "final_dry_run_executor_guard_field_count": len(
+ final_dry_run_executor_guard_fields
+ ),
+ "final_dry_run_executor_guard_fields": final_dry_run_executor_guard_fields,
+ }
+ post_receipt_parser_closeout_ready = (
+ parser_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_POST_RECEIPT_PARSER_CLOSEOUT_READY"
+ and summary.get("controlled_dry_run_post_receipt_parser_closeout_ready_count")
+ == 1
+ and summary.get("controlled_dry_run_post_receipt_parser_closeout_pass_count")
+ == summary.get("controlled_dry_run_post_receipt_parser_closeout_check_count")
+ )
+ source_chain_ids_match = (
+ bool(parser_closeout.get("post_receipt_parser_closeout_id"))
+ and parser_closeout.get("post_receipt_parser_closeout_id")
+ == future_enforcement.get("post_receipt_parser_closeout_id")
+ == no_apply_enforcement.get("source_post_receipt_parser_closeout_id")
+ == final_dry_run_executor_guard.get(
+ "source_post_receipt_parser_closeout_id"
+ )
+ and no_apply_enforcement.get("verification_id")
+ == future_enforcement.get("no_apply_enforcement_verification_id")
+ == final_dry_run_executor_guard.get(
+ "source_no_apply_enforcement_verification_id"
+ )
+ and parser_closeout.get("source_runner_execution_receipt_closeout_id")
+ == future_enforcement.get("source_runner_execution_receipt_closeout_id")
+ == final_dry_run_executor_guard.get(
+ "source_runner_execution_receipt_closeout_id"
+ )
+ )
+ no_apply_enforcement_verification_ready = (
+ no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and no_apply_enforcement.get("verification_id")
+ == future_enforcement.get("no_apply_enforcement_verification_id")
+ and int(
+ no_apply_enforcement.get(
+ "no_apply_enforcement_verification_field_count"
+ )
+ or 0
+ )
+ == 12
+ )
+ no_apply_blocks_endpoint_sql_db_write = (
+ no_apply_enforcement.get("endpoint_execution_allowed") is False
+ and no_apply_enforcement.get("sql_execution_allowed") is False
+ and no_apply_enforcement.get("database_write_allowed") is False
+ and no_apply_enforcement.get("database_apply_authorized") is False
+ and no_apply_enforcement.get("executes_endpoint") is False
+ and no_apply_enforcement.get("executes_sql") is False
+ and no_apply_enforcement.get("writes_database") is False
+ and no_apply_enforcement.get("executes_database_apply") is False
+ and future_enforcement.get("endpoint_execution_allowed") is False
+ and future_enforcement.get("sql_execution_allowed") is False
+ and future_enforcement.get("database_write_allowed") is False
+ )
+ final_dry_run_executor_guard_bound = (
+ bool(final_dry_run_executor_guard.get("guard_id"))
+ and final_dry_run_executor_guard.get(
+ "source_no_apply_enforcement_closeout_id"
+ )
+ == closeout_id
+ and final_dry_run_executor_guard.get(
+ "source_no_apply_enforcement_verification_id"
+ )
+ == no_apply_enforcement.get("verification_id")
+ and final_dry_run_executor_guard.get("required_command_shape_hash")
+ == parser_closeout.get("dry_run_command_shape_hash")
+ and int(
+ final_dry_run_executor_guard.get(
+ "final_dry_run_executor_guard_field_count"
+ )
+ or 0
+ )
+ == len(final_dry_run_executor_guard_fields)
+ )
+ final_executor_guard_blocks_execution = (
+ final_dry_run_executor_guard.get("endpoint_execution_allowed") is False
+ and final_dry_run_executor_guard.get("sql_execution_allowed") is False
+ and final_dry_run_executor_guard.get("database_write_allowed") is False
+ and final_dry_run_executor_guard.get("dry_run_executor_invocation_allowed")
+ is False
+ and final_dry_run_executor_guard.get("stdout_capture_allowed") is False
+ and final_dry_run_executor_guard.get("stderr_capture_allowed") is False
+ and final_dry_run_executor_guard.get("database_apply_authorized") is False
+ and final_dry_run_executor_guard.get("executes_database_apply") is False
+ and final_dry_run_executor_guard.get("executes_endpoint") is False
+ and final_dry_run_executor_guard.get("executes_sql") is False
+ and final_dry_run_executor_guard.get("writes_database") is False
+ )
+ parser_and_receipt_preview_carried_forward = (
+ parser.get("parser_verification_status") == "post_receipt_parser_preview_ready"
+ and parser.get("expected_execution_performed") is False
+ and parser.get("expected_stdout_included") is False
+ and parser.get("expected_stderr_included") is False
+ and parser.get("database_apply_authorized") is False
+ and preview.get("receipt_status") == "receipt_closeout_preview_not_executed"
+ and preview.get("execution_performed") is False
+ and preview.get("stdout_included") is False
+ and preview.get("stderr_included") is False
+ and preview.get("writes_database") is False
+ and result_parser.get("required_command_shape_hash")
+ == parser_closeout.get("dry_run_command_shape_hash")
+ and result_parser.get("database_apply_authorized") is False
+ and validation.get("receipt_validation_status")
+ == "preview_validated_not_executed"
+ and validation.get("writes_database") is False
+ )
+ target_hash_locked = (
+ parser_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(parser_closeout.get("expected_sha256"))
+ and bool(parser_closeout.get("actual_sha256"))
+ and parser_closeout.get("expected_sha256")
+ == parser_closeout.get("actual_sha256")
+ and parser_closeout.get("hash_matches") is True
+ and parser_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ parser_closeout_contract_blocks_database_apply = (
+ parser_closeout_contract.get("executes_database_apply") is False
+ and parser_closeout_contract.get("executes_endpoint") is False
+ and parser_closeout_contract.get("executes_sql") is False
+ and parser_closeout_contract.get("database_apply_authorized") is False
+ and parser_closeout_contract.get("ready_for_database_apply_now") is False
+ and parser_closeout_contract.get("signs_database_apply_authorization")
+ is False
+ and parser_closeout_contract.get("writes_database") is False
+ and parser_closeout_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and no_apply_blocks_endpoint_sql_db_write
+ and final_executor_guard_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "post_receipt_parser_closeout_ready",
+ post_receipt_parser_closeout_ready,
+ {
+ "result": parser_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_check_count"
+ ),
+ },
+ "wait_for_post_receipt_parser_closeout_ready",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "post_receipt_parser_closeout_id": parser_closeout.get(
+ "post_receipt_parser_closeout_id"
+ ),
+ "no_apply_enforcement_verification_id": no_apply_enforcement.get(
+ "verification_id"
+ ),
+ "runner_execution_receipt_closeout_id": parser_closeout.get(
+ "source_runner_execution_receipt_closeout_id"
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "no_apply_enforcement_verification_ready",
+ no_apply_enforcement_verification_ready,
+ {
+ "enforcement_status": no_apply_enforcement.get(
+ "enforcement_status"
+ ),
+ "verification_id": no_apply_enforcement.get("verification_id"),
+ "field_count": no_apply_enforcement.get(
+ "no_apply_enforcement_verification_field_count"
+ ),
+ },
+ "wait_for_no_apply_enforcement_verification_ready",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "no_apply_blocks_endpoint_sql_db_write",
+ no_apply_blocks_endpoint_sql_db_write,
+ {
+ "endpoint_execution_allowed": no_apply_enforcement.get(
+ "endpoint_execution_allowed"
+ ),
+ "sql_execution_allowed": no_apply_enforcement.get(
+ "sql_execution_allowed"
+ ),
+ "database_write_allowed": no_apply_enforcement.get(
+ "database_write_allowed"
+ ),
+ "database_apply_authorized": no_apply_enforcement.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_no_apply_enforcement_allows_endpoint_sql_or_db_write",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "final_dry_run_executor_guard_bound",
+ final_dry_run_executor_guard_bound,
+ {
+ "guard_id": final_dry_run_executor_guard.get("guard_id"),
+ "source_no_apply_enforcement_verification_id": (
+ final_dry_run_executor_guard.get(
+ "source_no_apply_enforcement_verification_id"
+ )
+ ),
+ "field_count": final_dry_run_executor_guard.get(
+ "final_dry_run_executor_guard_field_count"
+ ),
+ },
+ "wait_for_final_dry_run_executor_guard_binding",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "final_executor_guard_blocks_execution",
+ final_executor_guard_blocks_execution,
+ {
+ "dry_run_executor_invocation_allowed": (
+ final_dry_run_executor_guard.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ ),
+ "endpoint_execution_allowed": final_dry_run_executor_guard.get(
+ "endpoint_execution_allowed"
+ ),
+ "sql_execution_allowed": final_dry_run_executor_guard.get(
+ "sql_execution_allowed"
+ ),
+ "database_write_allowed": final_dry_run_executor_guard.get(
+ "database_write_allowed"
+ ),
+ },
+ "abort_if_final_executor_guard_allows_execution",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "parser_and_receipt_preview_carried_forward",
+ parser_and_receipt_preview_carried_forward,
+ {
+ "parser_verification_status": parser.get(
+ "parser_verification_status"
+ ),
+ "receipt_status": preview.get("receipt_status"),
+ "receipt_validation_status": validation.get(
+ "receipt_validation_status"
+ ),
+ },
+ "wait_for_parser_and_receipt_preview_carry_forward",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": parser_closeout.get("target_file"),
+ "hash_matches": parser_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(parser_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(parser_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "rollback_and_post_apply_verifier_bindings_carried_forward",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "post_receipt_parser_contract_blocks_database_apply",
+ parser_closeout_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_no_apply_enforcement_verification": (
+ parser_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ )
+ ),
+ "database_apply_authorized": parser_closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": parser_closeout_contract.get("writes_database"),
+ },
+ "abort_if_post_receipt_parser_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_no_apply_enforcement_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ parser_closeout_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": parser_closeout_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_NO_APPLY_ENFORCEMENT_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_POST_RECEIPT_PARSER_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_final_dry_run_executor_guard = {
+ "no_apply_enforcement_closeout_id": closeout_id,
+ "final_dry_run_executor_guard_id": final_guard_id,
+ "source_post_receipt_parser_closeout_id": parser_closeout.get(
+ "post_receipt_parser_closeout_id"
+ ),
+ "source_no_apply_enforcement_verification_id": no_apply_enforcement.get(
+ "verification_id"
+ ),
+ "source_runner_execution_receipt_closeout_id": parser_closeout.get(
+ "source_runner_execution_receipt_closeout_id"
+ ),
+ "source_non_executable_command_artifact_id": parser_closeout.get(
+ "source_non_executable_command_artifact_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_final_dry_run_executor_guard": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_final_executor_guard_closeout": (
+ closeout_ready
+ ),
+ "no_apply_enforcement_closeout_ready": closeout_ready,
+ "final_dry_run_executor_guard_bound": closeout_ready,
+ "dry_run_executor_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_no_apply_enforcement_closeout = {
+ "no_apply_enforcement_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_no_apply_enforcement_closeout"
+ ),
+ "source_post_receipt_parser_closeout_id": parser_closeout.get(
+ "post_receipt_parser_closeout_id"
+ ),
+ "source_no_apply_enforcement_verification_id": no_apply_enforcement.get(
+ "verification_id"
+ ),
+ "source_runner_execution_receipt_closeout_id": parser_closeout.get(
+ "source_runner_execution_receipt_closeout_id"
+ ),
+ "source_command_artifact_closeout_id": parser_closeout.get(
+ "source_command_artifact_closeout_id"
+ ),
+ "source_non_executable_command_artifact_id": parser_closeout.get(
+ "source_non_executable_command_artifact_id"
+ ),
+ "dry_run_command_shape_hash": parser_closeout.get(
+ "dry_run_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_no_apply_enforcement_closeout": (
+ closeout_ready
+ ),
+ "no_apply_enforcement_closeout_fields": no_apply_enforcement_closeout_fields,
+ "no_apply_enforcement_closeout_field_count": len(
+ no_apply_enforcement_closeout_fields
+ ),
+ "no_apply_enforcement_closeout_acceptance_gates": (
+ no_apply_enforcement_closeout_acceptance_gates
+ ),
+ "no_apply_enforcement_closeout_acceptance_gate_count": len(
+ no_apply_enforcement_closeout_acceptance_gates
+ ),
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "final_dry_run_executor_guard": final_dry_run_executor_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "final_dry_run_executor_guard_field_count": len(
+ final_dry_run_executor_guard_fields
+ ),
+ "post_receipt_parser_closeout": parser_closeout,
+ "post_receipt_parser_closeout_count": 1,
+ "post_receipt_parser_verification": parser,
+ "post_receipt_parser_verification_count": 1,
+ "receipt_closeout_preview": preview,
+ "receipt_closeout_preview_count": 1,
+ "dry_run_result_parser": result_parser,
+ "dry_run_result_parser_count": 1,
+ "receipt_validation_report": validation,
+ "receipt_validation_report_count": 1,
+ "target_file": parser_closeout.get("target_file"),
+ "expected_sha256": parser_closeout.get("expected_sha256"),
+ "actual_sha256": parser_closeout.get("actual_sha256"),
+ "hash_matches": parser_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "no_apply_enforcement_closeout_only": True,
+ "final_dry_run_executor_guard_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_no_apply_enforcement_closeout_contract = {
+ "mode": "controlled_dry_run_no_apply_enforcement_closeout_and_final_executor_guard_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-no-apply-enforcement-closeout"
+ ),
+ "source_post_receipt_parser_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-post-receipt-parser-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_final_dry_run_executor_guard": (
+ closeout_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_NO_APPLY_ENFORCEMENT_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(parser_closeout_result.get("success")),
+ "generated_at": parser_closeout_result.get("generated_at"),
+ "source_policy": parser_closeout_result.get("policy"),
+ "stats": parser_closeout_result.get("stats") or {},
+ "summary": {
+ "controlled_dry_run_no_apply_enforcement_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_ready_count": (
+ summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_check_count": (
+ summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_check_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count": (
+ summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count": (
+ summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_command_artifact_closeout_ready_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_command_artifact_closeout_check_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_check_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_ready_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_check_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_check_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_ready_count": summary.get(
+ "controlled_dry_run_runner_readiness_ready_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_check_count": summary.get(
+ "controlled_dry_run_runner_readiness_check_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_ready_count": summary.get(
+ "controlled_dry_run_receipt_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_check_count": summary.get(
+ "controlled_dry_run_receipt_closeout_check_count", 0
+ ),
+ "controlled_dry_run_package_ready_count": summary.get(
+ "controlled_dry_run_package_ready_count", 0
+ ),
+ "controlled_dry_run_package_check_count": summary.get(
+ "controlled_dry_run_package_check_count", 0
+ ),
+ "controlled_apply_final_preflight_ready_count": summary.get(
+ "controlled_apply_final_preflight_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_check_count": summary.get(
+ "controlled_apply_final_preflight_check_count", 0
+ ),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_count": 1,
+ "controlled_dry_run_no_apply_enforcement_closeout_field_count": len(
+ no_apply_enforcement_closeout_fields
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_acceptance_gate_count": len(
+ no_apply_enforcement_closeout_acceptance_gates
+ ),
+ "final_dry_run_executor_guard_count": 1,
+ "final_dry_run_executor_guard_field_count": len(
+ final_dry_run_executor_guard_fields
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_count": summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_count", 0
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_field_count": (
+ summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_field_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_acceptance_gate_count": (
+ summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_acceptance_gate_count",
+ 0,
+ )
+ ),
+ "no_apply_enforcement_verification_count": summary.get(
+ "no_apply_enforcement_verification_count", 0
+ ),
+ "no_apply_enforcement_verification_field_count": summary.get(
+ "no_apply_enforcement_verification_field_count", 0
+ ),
+ "post_receipt_parser_verification_count": summary.get(
+ "post_receipt_parser_verification_count", 0
+ ),
+ "post_receipt_parser_verification_field_count": summary.get(
+ "post_receipt_parser_verification_field_count", 0
+ ),
+ "receipt_closeout_preview_count": summary.get(
+ "receipt_closeout_preview_count", 0
+ ),
+ "runner_execution_receipt_preflight_count": summary.get(
+ "runner_execution_receipt_preflight_count", 0
+ ),
+ "runner_execution_receipt_preflight_field_count": summary.get(
+ "runner_execution_receipt_preflight_field_count", 0
+ ),
+ "non_executable_command_artifact_count": summary.get(
+ "non_executable_command_artifact_count", 0
+ ),
+ "non_executable_command_artifact_field_count": summary.get(
+ "non_executable_command_artifact_field_count", 0
+ ),
+ "dry_run_result_parser_count": summary.get(
+ "dry_run_result_parser_count", 0
+ ),
+ "dry_run_result_parser_field_count": summary.get(
+ "dry_run_result_parser_field_count", 0
+ ),
+ "receipt_validation_report_count": summary.get(
+ "receipt_validation_report_count", 0
+ ),
+ "receipt_validation_field_count": summary.get(
+ "receipt_validation_field_count", 0
+ ),
+ "rollback_binding_count": summary.get("rollback_binding_count", 0),
+ "post_apply_verifier_binding_count": summary.get(
+ "post_apply_verifier_binding_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get(
+ "same_run_truth_required_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_dry_run_final_dry_run_executor_guard": (
+ future_database_apply_controlled_dry_run_final_dry_run_executor_guard
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout": (
+ controlled_dry_run_no_apply_enforcement_closeout
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_contract": (
+ controlled_dry_run_no_apply_enforcement_closeout_contract
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_checks": checks,
+ "source_controlled_dry_run_post_receipt_parser_closeout_summary": summary,
+ "source_controlled_dry_run_post_receipt_parser_closeout_contract": (
+ parser_closeout_contract
+ ),
+ "source_controlled_dry_run_post_receipt_parser_closeout": parser_closeout,
+ "source_database_apply_controlled_dry_run_no_apply_enforcement_verification": (
+ future_enforcement
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_no_apply_enforcement_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run final executor guard closeout.",
+ "Keep the final dry-run executor guard machine-verifiable and non-invoking until an explicit apply lane is built.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the final dry-run executor guard and bind replay verification."""
+ no_apply_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_guard = (
+ no_apply_closeout_result.get(
+ "future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ )
+ or {}
+ )
+ no_apply_closeout = (
+ no_apply_closeout_result.get(
+ "controlled_dry_run_no_apply_enforcement_closeout"
+ )
+ or {}
+ )
+ no_apply_contract = (
+ no_apply_closeout_result.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_contract"
+ )
+ or {}
+ )
+ summary = no_apply_closeout_result.get("summary") or {}
+ safety = no_apply_closeout_result.get("safety") or {}
+ final_guard = no_apply_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ no_apply_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ parser_closeout = no_apply_closeout.get("post_receipt_parser_closeout") or {}
+ parser = no_apply_closeout.get("post_receipt_parser_verification") or {}
+ preview = no_apply_closeout.get("receipt_closeout_preview") or {}
+ validation = no_apply_closeout.get("receipt_validation_report") or {}
+ rollback_binding = no_apply_closeout.get("rollback_binding") or {}
+ verifier_binding = no_apply_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_final_executor_guard_closeout_id(
+ no_apply_closeout_result
+ )
+ replay_verifier_id = f"{closeout_id}-pre-apply-replay-verifier"
+ final_executor_guard_closeout_fields = [
+ "final_executor_guard_closeout_id",
+ "source_no_apply_enforcement_closeout_id",
+ "source_final_dry_run_executor_guard_id",
+ "source_no_apply_enforcement_verification_id",
+ "source_post_receipt_parser_closeout_id",
+ "dry_run_command_shape_hash",
+ "pre_apply_replay_verifier_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ final_executor_guard_closeout_acceptance_gates = [
+ "no_apply_enforcement_closeout_ready",
+ "source_chain_ids_match",
+ "final_dry_run_executor_guard_ready",
+ "final_executor_guard_blocks_invocation",
+ "pre_apply_replay_verifier_bound",
+ "pre_apply_replay_verifier_preview_only",
+ "no_apply_enforcement_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ pre_apply_replay_verifier_fields = [
+ "verification_id",
+ "source_final_executor_guard_closeout_id",
+ "source_final_dry_run_executor_guard_id",
+ "source_no_apply_enforcement_closeout_id",
+ "required_guard_status",
+ "required_enforcement_status",
+ "required_command_shape_hash",
+ "replay_mode",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_write_allowed",
+ ]
+ abort_conditions = [
+ "abort_if_no_apply_enforcement_closeout_not_ready",
+ "abort_if_final_dry_run_executor_guard_missing",
+ "abort_if_final_guard_allows_invocation",
+ "abort_if_replay_verifier_requests_execution",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_stdout_or_stderr_capture_is_allowed",
+ "abort_if_command_shape_hash_mismatch",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ pre_apply_replay_verifier = {
+ "verification_id": replay_verifier_id,
+ "source_final_executor_guard_closeout_id": closeout_id,
+ "source_final_dry_run_executor_guard_id": final_guard.get("guard_id"),
+ "source_no_apply_enforcement_closeout_id": no_apply_closeout.get(
+ "no_apply_enforcement_closeout_id"
+ ),
+ "source_no_apply_enforcement_verification_id": no_apply_enforcement.get(
+ "verification_id"
+ ),
+ "source_post_receipt_parser_closeout_id": parser_closeout.get(
+ "post_receipt_parser_closeout_id"
+ ),
+ "required_guard_status": "final_dry_run_executor_guard_preview_ready",
+ "required_enforcement_status": "no_apply_enforcement_preview_ready",
+ "required_command_shape_hash": final_guard.get("required_command_shape_hash"),
+ "replay_mode": "pre_apply_replay_preview_only",
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "database_apply_authorized": False,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "verifier_status": "pre_apply_replay_verifier_preview_ready",
+ "pre_apply_replay_verifier_field_count": len(
+ pre_apply_replay_verifier_fields
+ ),
+ "pre_apply_replay_verifier_fields": pre_apply_replay_verifier_fields,
+ }
+ no_apply_closeout_ready = (
+ no_apply_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_NO_APPLY_ENFORCEMENT_CLOSEOUT_READY"
+ and summary.get("controlled_dry_run_no_apply_enforcement_closeout_ready_count")
+ == 1
+ and summary.get("controlled_dry_run_no_apply_enforcement_closeout_pass_count")
+ == summary.get("controlled_dry_run_no_apply_enforcement_closeout_check_count")
+ )
+ source_chain_ids_match = (
+ bool(no_apply_closeout.get("no_apply_enforcement_closeout_id"))
+ and no_apply_closeout.get("no_apply_enforcement_closeout_id")
+ == future_guard.get("no_apply_enforcement_closeout_id")
+ == final_guard.get("source_no_apply_enforcement_closeout_id")
+ == pre_apply_replay_verifier.get(
+ "source_no_apply_enforcement_closeout_id"
+ )
+ and final_guard.get("guard_id")
+ == future_guard.get("final_dry_run_executor_guard_id")
+ == pre_apply_replay_verifier.get("source_final_dry_run_executor_guard_id")
+ and no_apply_enforcement.get("verification_id")
+ == no_apply_closeout.get("source_no_apply_enforcement_verification_id")
+ == pre_apply_replay_verifier.get(
+ "source_no_apply_enforcement_verification_id"
+ )
+ )
+ final_dry_run_executor_guard_ready = (
+ final_guard.get("guard_status") == "final_dry_run_executor_guard_preview_ready"
+ and final_guard.get("guard_id")
+ == future_guard.get("final_dry_run_executor_guard_id")
+ and int(final_guard.get("final_dry_run_executor_guard_field_count") or 0)
+ == 12
+ )
+ final_executor_guard_blocks_invocation = (
+ final_guard.get("dry_run_executor_invocation_allowed") is False
+ and final_guard.get("endpoint_execution_allowed") is False
+ and final_guard.get("sql_execution_allowed") is False
+ and final_guard.get("database_write_allowed") is False
+ and final_guard.get("stdout_capture_allowed") is False
+ and final_guard.get("stderr_capture_allowed") is False
+ and final_guard.get("database_apply_authorized") is False
+ and final_guard.get("executes_database_apply") is False
+ and final_guard.get("executes_endpoint") is False
+ and final_guard.get("executes_sql") is False
+ and final_guard.get("writes_database") is False
+ )
+ pre_apply_replay_verifier_bound = (
+ bool(pre_apply_replay_verifier.get("verification_id"))
+ and pre_apply_replay_verifier.get("source_final_executor_guard_closeout_id")
+ == closeout_id
+ and pre_apply_replay_verifier.get("source_final_dry_run_executor_guard_id")
+ == final_guard.get("guard_id")
+ and pre_apply_replay_verifier.get("required_command_shape_hash")
+ == final_guard.get("required_command_shape_hash")
+ and int(
+ pre_apply_replay_verifier.get("pre_apply_replay_verifier_field_count")
+ or 0
+ )
+ == len(pre_apply_replay_verifier_fields)
+ )
+ pre_apply_replay_verifier_preview_only = (
+ pre_apply_replay_verifier.get("replay_mode")
+ == "pre_apply_replay_preview_only"
+ and pre_apply_replay_verifier.get("dry_run_executor_invocation_allowed")
+ is False
+ and pre_apply_replay_verifier.get("endpoint_execution_allowed") is False
+ and pre_apply_replay_verifier.get("sql_execution_allowed") is False
+ and pre_apply_replay_verifier.get("database_write_allowed") is False
+ and pre_apply_replay_verifier.get("stdout_capture_allowed") is False
+ and pre_apply_replay_verifier.get("stderr_capture_allowed") is False
+ and pre_apply_replay_verifier.get("database_apply_authorized") is False
+ and pre_apply_replay_verifier.get("executes_database_apply") is False
+ and pre_apply_replay_verifier.get("executes_endpoint") is False
+ and pre_apply_replay_verifier.get("executes_sql") is False
+ and pre_apply_replay_verifier.get("writes_database") is False
+ )
+ no_apply_enforcement_carried_forward = (
+ no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and no_apply_enforcement.get("endpoint_execution_allowed") is False
+ and no_apply_enforcement.get("sql_execution_allowed") is False
+ and no_apply_enforcement.get("database_write_allowed") is False
+ and no_apply_enforcement.get("database_apply_authorized") is False
+ and parser.get("parser_verification_status")
+ == "post_receipt_parser_preview_ready"
+ and preview.get("receipt_status") == "receipt_closeout_preview_not_executed"
+ and validation.get("receipt_validation_status")
+ == "preview_validated_not_executed"
+ )
+ target_hash_locked = (
+ no_apply_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(no_apply_closeout.get("expected_sha256"))
+ and bool(no_apply_closeout.get("actual_sha256"))
+ and no_apply_closeout.get("expected_sha256")
+ == no_apply_closeout.get("actual_sha256")
+ and no_apply_closeout.get("hash_matches") is True
+ and no_apply_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ no_apply_contract_blocks_database_apply = (
+ no_apply_contract.get("executes_database_apply") is False
+ and no_apply_contract.get("executes_endpoint") is False
+ and no_apply_contract.get("executes_sql") is False
+ and no_apply_contract.get("database_apply_authorized") is False
+ and no_apply_contract.get("ready_for_database_apply_now") is False
+ and no_apply_contract.get("signs_database_apply_authorization") is False
+ and no_apply_contract.get("writes_database") is False
+ and no_apply_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and final_executor_guard_blocks_invocation
+ and pre_apply_replay_verifier_preview_only
+ )
+ checks = [
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "no_apply_enforcement_closeout_ready",
+ no_apply_closeout_ready,
+ {
+ "result": no_apply_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_check_count"
+ ),
+ },
+ "wait_for_no_apply_enforcement_closeout_ready",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "no_apply_enforcement_closeout_id": no_apply_closeout.get(
+ "no_apply_enforcement_closeout_id"
+ ),
+ "final_guard_id": final_guard.get("guard_id"),
+ "no_apply_enforcement_verification_id": no_apply_enforcement.get(
+ "verification_id"
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "final_dry_run_executor_guard_ready",
+ final_dry_run_executor_guard_ready,
+ {
+ "guard_status": final_guard.get("guard_status"),
+ "guard_id": final_guard.get("guard_id"),
+ "field_count": final_guard.get(
+ "final_dry_run_executor_guard_field_count"
+ ),
+ },
+ "wait_for_final_dry_run_executor_guard_ready",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "final_executor_guard_blocks_invocation",
+ final_executor_guard_blocks_invocation,
+ {
+ "dry_run_executor_invocation_allowed": final_guard.get(
+ "dry_run_executor_invocation_allowed"
+ ),
+ "endpoint_execution_allowed": final_guard.get(
+ "endpoint_execution_allowed"
+ ),
+ "sql_execution_allowed": final_guard.get("sql_execution_allowed"),
+ "database_write_allowed": final_guard.get(
+ "database_write_allowed"
+ ),
+ },
+ "abort_if_final_guard_allows_invocation",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "pre_apply_replay_verifier_bound",
+ pre_apply_replay_verifier_bound,
+ {
+ "verification_id": pre_apply_replay_verifier.get(
+ "verification_id"
+ ),
+ "source_final_dry_run_executor_guard_id": (
+ pre_apply_replay_verifier.get(
+ "source_final_dry_run_executor_guard_id"
+ )
+ ),
+ "field_count": pre_apply_replay_verifier.get(
+ "pre_apply_replay_verifier_field_count"
+ ),
+ },
+ "wait_for_pre_apply_replay_verifier_binding",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "pre_apply_replay_verifier_preview_only",
+ pre_apply_replay_verifier_preview_only,
+ {
+ "replay_mode": pre_apply_replay_verifier.get("replay_mode"),
+ "dry_run_executor_invocation_allowed": (
+ pre_apply_replay_verifier.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ ),
+ "database_apply_authorized": pre_apply_replay_verifier.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_pre_apply_replay_verifier_requests_execution",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "no_apply_enforcement_carried_forward",
+ no_apply_enforcement_carried_forward,
+ {
+ "enforcement_status": no_apply_enforcement.get(
+ "enforcement_status"
+ ),
+ "parser_verification_status": parser.get(
+ "parser_verification_status"
+ ),
+ "receipt_validation_status": validation.get(
+ "receipt_validation_status"
+ ),
+ },
+ "wait_for_no_apply_enforcement_carry_forward",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": no_apply_closeout.get("target_file"),
+ "hash_matches": no_apply_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(no_apply_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(no_apply_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "rollback_and_post_apply_verifier_bindings_carried_forward",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "no_apply_enforcement_contract_blocks_database_apply",
+ no_apply_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_final_dry_run_executor_guard": (
+ no_apply_contract.get(
+ "permits_future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ )
+ ),
+ "database_apply_authorized": no_apply_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": no_apply_contract.get("writes_database"),
+ },
+ "abort_if_no_apply_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_final_executor_guard_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ no_apply_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": no_apply_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_FINAL_EXECUTOR_GUARD_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_NO_APPLY_ENFORCEMENT_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_pre_apply_replay_verifier = {
+ "final_executor_guard_closeout_id": closeout_id,
+ "pre_apply_replay_verifier_id": replay_verifier_id,
+ "source_no_apply_enforcement_closeout_id": no_apply_closeout.get(
+ "no_apply_enforcement_closeout_id"
+ ),
+ "source_final_dry_run_executor_guard_id": final_guard.get("guard_id"),
+ "source_no_apply_enforcement_verification_id": no_apply_enforcement.get(
+ "verification_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_pre_apply_replay_verifier": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_pre_apply_replay_closeout": (
+ closeout_ready
+ ),
+ "final_executor_guard_closeout_ready": closeout_ready,
+ "pre_apply_replay_verifier_bound": closeout_ready,
+ "dry_run_executor_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "stdout_capture_allowed": False,
+ "stderr_capture_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_final_executor_guard_closeout = {
+ "final_executor_guard_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_final_executor_guard_closeout"
+ ),
+ "source_no_apply_enforcement_closeout_id": no_apply_closeout.get(
+ "no_apply_enforcement_closeout_id"
+ ),
+ "source_final_dry_run_executor_guard_id": final_guard.get("guard_id"),
+ "source_no_apply_enforcement_verification_id": no_apply_enforcement.get(
+ "verification_id"
+ ),
+ "source_post_receipt_parser_closeout_id": parser_closeout.get(
+ "post_receipt_parser_closeout_id"
+ ),
+ "dry_run_command_shape_hash": final_guard.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_final_executor_guard_closeout": (
+ closeout_ready
+ ),
+ "final_executor_guard_closeout_fields": final_executor_guard_closeout_fields,
+ "final_executor_guard_closeout_field_count": len(
+ final_executor_guard_closeout_fields
+ ),
+ "final_executor_guard_closeout_acceptance_gates": (
+ final_executor_guard_closeout_acceptance_gates
+ ),
+ "final_executor_guard_closeout_acceptance_gate_count": len(
+ final_executor_guard_closeout_acceptance_gates
+ ),
+ "pre_apply_replay_verifier": pre_apply_replay_verifier,
+ "pre_apply_replay_verifier_count": 1,
+ "pre_apply_replay_verifier_field_count": len(
+ pre_apply_replay_verifier_fields
+ ),
+ "final_dry_run_executor_guard": final_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "no_apply_enforcement_closeout": no_apply_closeout,
+ "no_apply_enforcement_closeout_count": 1,
+ "post_receipt_parser_closeout": parser_closeout,
+ "post_receipt_parser_closeout_count": 1,
+ "post_receipt_parser_verification": parser,
+ "post_receipt_parser_verification_count": 1,
+ "receipt_closeout_preview": preview,
+ "receipt_closeout_preview_count": 1,
+ "receipt_validation_report": validation,
+ "receipt_validation_report_count": 1,
+ "target_file": no_apply_closeout.get("target_file"),
+ "expected_sha256": no_apply_closeout.get("expected_sha256"),
+ "actual_sha256": no_apply_closeout.get("actual_sha256"),
+ "hash_matches": no_apply_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "final_executor_guard_closeout_only": True,
+ "pre_apply_replay_verifier_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_final_executor_guard_closeout_contract = {
+ "mode": "controlled_dry_run_final_executor_guard_closeout_and_pre_apply_replay_verifier_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-final-executor-guard-closeout"
+ ),
+ "source_no_apply_enforcement_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-no-apply-enforcement-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_pre_apply_replay_verifier": (
+ closeout_ready
+ ),
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": 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,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_FINAL_EXECUTOR_GUARD_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(no_apply_closeout_result.get("success")),
+ "generated_at": no_apply_closeout_result.get("generated_at"),
+ "source_policy": no_apply_closeout_result.get("policy"),
+ "stats": no_apply_closeout_result.get("stats") or {},
+ "summary": {
+ "controlled_dry_run_final_executor_guard_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_final_executor_guard_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_final_executor_guard_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_final_executor_guard_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_ready_count": (
+ summary.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_check_count": (
+ summary.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_check_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_ready_count": (
+ summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_post_receipt_parser_closeout_check_count": (
+ summary.get(
+ "controlled_dry_run_post_receipt_parser_closeout_check_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count": (
+ summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count": (
+ summary.get(
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_command_artifact_closeout_ready_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_command_artifact_closeout_check_count": summary.get(
+ "controlled_dry_run_command_artifact_closeout_check_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_ready_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_execution_plan_closeout_check_count": summary.get(
+ "controlled_dry_run_execution_plan_closeout_check_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_ready_count": summary.get(
+ "controlled_dry_run_runner_readiness_ready_count", 0
+ ),
+ "controlled_dry_run_runner_readiness_check_count": summary.get(
+ "controlled_dry_run_runner_readiness_check_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_ready_count": summary.get(
+ "controlled_dry_run_receipt_closeout_ready_count", 0
+ ),
+ "controlled_dry_run_receipt_closeout_check_count": summary.get(
+ "controlled_dry_run_receipt_closeout_check_count", 0
+ ),
+ "controlled_dry_run_package_ready_count": summary.get(
+ "controlled_dry_run_package_ready_count", 0
+ ),
+ "controlled_dry_run_package_check_count": summary.get(
+ "controlled_dry_run_package_check_count", 0
+ ),
+ "controlled_apply_final_preflight_ready_count": summary.get(
+ "controlled_apply_final_preflight_ready_count", 0
+ ),
+ "controlled_apply_final_preflight_check_count": summary.get(
+ "controlled_apply_final_preflight_check_count", 0
+ ),
+ "authorization_evidence_execution_closeout_ready_count": summary.get(
+ "authorization_evidence_execution_closeout_ready_count", 0
+ ),
+ "authorization_evidence_execution_closeout_check_count": summary.get(
+ "authorization_evidence_execution_closeout_check_count", 0
+ ),
+ "authorization_evidence_execution_preflight_ready_count": summary.get(
+ "authorization_evidence_execution_preflight_ready_count", 0
+ ),
+ "authorization_evidence_execution_preflight_check_count": summary.get(
+ "authorization_evidence_execution_preflight_check_count", 0
+ ),
+ "database_apply_final_verifier_gate_count": summary.get(
+ "database_apply_final_verifier_gate_count", 0
+ ),
+ "database_apply_authorization_final_verifier_gate_ready_count": (
+ summary.get(
+ "database_apply_authorization_final_verifier_gate_ready_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_final_executor_guard_closeout_count": 1,
+ "controlled_dry_run_final_executor_guard_closeout_field_count": len(
+ final_executor_guard_closeout_fields
+ ),
+ "controlled_dry_run_final_executor_guard_closeout_acceptance_gate_count": len(
+ final_executor_guard_closeout_acceptance_gates
+ ),
+ "pre_apply_replay_verifier_count": 1,
+ "pre_apply_replay_verifier_field_count": len(
+ pre_apply_replay_verifier_fields
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_count": summary.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_count", 0
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_field_count": (
+ summary.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_field_count",
+ 0,
+ )
+ ),
+ "controlled_dry_run_no_apply_enforcement_closeout_acceptance_gate_count": (
+ summary.get(
+ "controlled_dry_run_no_apply_enforcement_closeout_acceptance_gate_count",
+ 0,
+ )
+ ),
+ "final_dry_run_executor_guard_count": summary.get(
+ "final_dry_run_executor_guard_count", 0
+ ),
+ "final_dry_run_executor_guard_field_count": summary.get(
+ "final_dry_run_executor_guard_field_count", 0
+ ),
+ "no_apply_enforcement_verification_count": summary.get(
+ "no_apply_enforcement_verification_count", 0
+ ),
+ "no_apply_enforcement_verification_field_count": summary.get(
+ "no_apply_enforcement_verification_field_count", 0
+ ),
+ "post_receipt_parser_verification_count": summary.get(
+ "post_receipt_parser_verification_count", 0
+ ),
+ "post_receipt_parser_verification_field_count": summary.get(
+ "post_receipt_parser_verification_field_count", 0
+ ),
+ "receipt_closeout_preview_count": summary.get(
+ "receipt_closeout_preview_count", 0
+ ),
+ "receipt_validation_report_count": summary.get(
+ "receipt_validation_report_count", 0
+ ),
+ "receipt_validation_field_count": summary.get(
+ "receipt_validation_field_count", 0
+ ),
+ "rollback_binding_count": summary.get("rollback_binding_count", 0),
+ "post_apply_verifier_binding_count": summary.get(
+ "post_apply_verifier_binding_count", 0
+ ),
+ "post_apply_verifier_required_count": summary.get(
+ "post_apply_verifier_required_count", 0
+ ),
+ "same_run_truth_required_count": summary.get(
+ "same_run_truth_required_count", 0
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ },
+ "future_database_apply_controlled_dry_run_pre_apply_replay_verifier": (
+ future_database_apply_controlled_dry_run_pre_apply_replay_verifier
+ ),
+ "controlled_dry_run_final_executor_guard_closeout": (
+ controlled_dry_run_final_executor_guard_closeout
+ ),
+ "controlled_dry_run_final_executor_guard_closeout_contract": (
+ controlled_dry_run_final_executor_guard_closeout_contract
+ ),
+ "controlled_dry_run_final_executor_guard_closeout_checks": checks,
+ "source_controlled_dry_run_no_apply_enforcement_closeout_summary": summary,
+ "source_controlled_dry_run_no_apply_enforcement_closeout_contract": (
+ no_apply_contract
+ ),
+ "source_controlled_dry_run_no_apply_enforcement_closeout": (
+ no_apply_closeout
+ ),
+ "source_database_apply_controlled_dry_run_final_dry_run_executor_guard": (
+ future_guard
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_final_executor_guard_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run pre-apply replay closeout.",
+ "Keep replay verification preview-only until a dedicated execution lane is explicit.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out replay verification and bind an apply executor readiness contract."""
+ final_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_replay_verifier = (
+ final_closeout_result.get(
+ "future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ )
+ or {}
+ )
+ final_closeout = (
+ final_closeout_result.get(
+ "controlled_dry_run_final_executor_guard_closeout"
+ )
+ or {}
+ )
+ final_closeout_contract = (
+ final_closeout_result.get(
+ "controlled_dry_run_final_executor_guard_closeout_contract"
+ )
+ or {}
+ )
+ summary = final_closeout_result.get("summary") or {}
+ safety = final_closeout_result.get("safety") or {}
+ replay_verifier = final_closeout.get("pre_apply_replay_verifier") or {}
+ final_guard = final_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ final_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ no_apply_closeout = final_closeout.get("no_apply_enforcement_closeout") or {}
+ rollback_binding = final_closeout.get("rollback_binding") or {}
+ verifier_binding = final_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_pre_apply_replay_closeout_id(
+ final_closeout_result
+ )
+ apply_executor_readiness_contract_id = (
+ f"{closeout_id}-apply-executor-readiness-contract"
+ )
+ pre_apply_replay_closeout_fields = [
+ "pre_apply_replay_closeout_id",
+ "source_final_executor_guard_closeout_id",
+ "source_pre_apply_replay_verifier_id",
+ "source_final_dry_run_executor_guard_id",
+ "source_no_apply_enforcement_closeout_id",
+ "dry_run_command_shape_hash",
+ "apply_executor_readiness_contract_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ pre_apply_replay_closeout_acceptance_gates = [
+ "final_executor_guard_closeout_ready",
+ "source_chain_ids_match",
+ "pre_apply_replay_verifier_ready",
+ "pre_apply_replay_preview_only",
+ "apply_executor_readiness_contract_bound",
+ "apply_executor_readiness_contract_blocks_apply",
+ "final_guard_and_no_apply_enforcement_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ apply_executor_readiness_contract_fields = [
+ "contract_id",
+ "source_pre_apply_replay_closeout_id",
+ "source_pre_apply_replay_verifier_id",
+ "source_final_dry_run_executor_guard_id",
+ "required_replay_mode",
+ "required_guard_status",
+ "required_command_shape_hash",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_write_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_final_executor_guard_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_pre_apply_replay_verifier_missing",
+ "abort_if_replay_mode_is_not_preview_only",
+ "abort_if_apply_executor_readiness_contract_missing",
+ "abort_if_contract_allows_apply_executor_invocation",
+ "abort_if_contract_allows_endpoint_or_sql_execution",
+ "abort_if_contract_allows_database_write_or_apply",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ apply_executor_readiness_contract = {
+ "contract_id": apply_executor_readiness_contract_id,
+ "source_pre_apply_replay_closeout_id": closeout_id,
+ "source_pre_apply_replay_verifier_id": replay_verifier.get(
+ "verification_id"
+ ),
+ "source_final_executor_guard_closeout_id": final_closeout.get(
+ "final_executor_guard_closeout_id"
+ ),
+ "source_final_dry_run_executor_guard_id": final_guard.get("guard_id"),
+ "source_no_apply_enforcement_closeout_id": no_apply_closeout.get(
+ "no_apply_enforcement_closeout_id"
+ ),
+ "required_replay_mode": "pre_apply_replay_preview_only",
+ "required_guard_status": "final_dry_run_executor_guard_preview_ready",
+ "required_command_shape_hash": final_guard.get(
+ "required_command_shape_hash"
+ ),
+ "readiness_status": "apply_executor_readiness_contract_preview_ready",
+ "readiness_mode": "apply_executor_readiness_contract_preview_only",
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "apply_executor_readiness_contract_field_count": len(
+ apply_executor_readiness_contract_fields
+ ),
+ "apply_executor_readiness_contract_fields": (
+ apply_executor_readiness_contract_fields
+ ),
+ }
+ final_executor_guard_closeout_ready = (
+ final_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_FINAL_EXECUTOR_GUARD_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_final_executor_guard_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_final_executor_guard_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_final_executor_guard_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(final_closeout.get("final_executor_guard_closeout_id"))
+ and final_closeout.get("final_executor_guard_closeout_id")
+ == future_replay_verifier.get("final_executor_guard_closeout_id")
+ == replay_verifier.get("source_final_executor_guard_closeout_id")
+ == apply_executor_readiness_contract.get(
+ "source_final_executor_guard_closeout_id"
+ )
+ and replay_verifier.get("verification_id")
+ == future_replay_verifier.get("pre_apply_replay_verifier_id")
+ == apply_executor_readiness_contract.get(
+ "source_pre_apply_replay_verifier_id"
+ )
+ and final_guard.get("guard_id")
+ == replay_verifier.get("source_final_dry_run_executor_guard_id")
+ == apply_executor_readiness_contract.get(
+ "source_final_dry_run_executor_guard_id"
+ )
+ )
+ pre_apply_replay_verifier_ready = (
+ replay_verifier.get("verifier_status")
+ == "pre_apply_replay_verifier_preview_ready"
+ and replay_verifier.get("verification_id")
+ == future_replay_verifier.get("pre_apply_replay_verifier_id")
+ and int(replay_verifier.get("pre_apply_replay_verifier_field_count") or 0)
+ == 12
+ )
+ pre_apply_replay_preview_only = (
+ replay_verifier.get("replay_mode") == "pre_apply_replay_preview_only"
+ and replay_verifier.get("dry_run_executor_invocation_allowed") is False
+ and replay_verifier.get("endpoint_execution_allowed") is False
+ and replay_verifier.get("sql_execution_allowed") is False
+ and replay_verifier.get("database_write_allowed") is False
+ and replay_verifier.get("database_apply_authorized") is False
+ and replay_verifier.get("executes_database_apply") is False
+ and replay_verifier.get("executes_endpoint") is False
+ and replay_verifier.get("executes_sql") is False
+ and replay_verifier.get("writes_database") is False
+ )
+ apply_executor_readiness_contract_bound = (
+ bool(apply_executor_readiness_contract.get("contract_id"))
+ and apply_executor_readiness_contract.get(
+ "source_pre_apply_replay_closeout_id"
+ )
+ == closeout_id
+ and apply_executor_readiness_contract.get(
+ "source_pre_apply_replay_verifier_id"
+ )
+ == replay_verifier.get("verification_id")
+ and apply_executor_readiness_contract.get("required_command_shape_hash")
+ == final_guard.get("required_command_shape_hash")
+ and int(
+ apply_executor_readiness_contract.get(
+ "apply_executor_readiness_contract_field_count"
+ )
+ or 0
+ )
+ == len(apply_executor_readiness_contract_fields)
+ )
+ apply_executor_readiness_contract_blocks_apply = (
+ apply_executor_readiness_contract.get("readiness_mode")
+ == "apply_executor_readiness_contract_preview_only"
+ and apply_executor_readiness_contract.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ is False
+ and apply_executor_readiness_contract.get("endpoint_execution_allowed")
+ is False
+ and apply_executor_readiness_contract.get("sql_execution_allowed") is False
+ and apply_executor_readiness_contract.get("database_write_allowed")
+ is False
+ and apply_executor_readiness_contract.get("database_apply_authorized")
+ is False
+ and apply_executor_readiness_contract.get("executes_database_apply")
+ is False
+ and apply_executor_readiness_contract.get("executes_endpoint") is False
+ and apply_executor_readiness_contract.get("executes_sql") is False
+ and apply_executor_readiness_contract.get("writes_database") is False
+ )
+ final_guard_and_no_apply_enforcement_carried_forward = (
+ final_guard.get("guard_status") == "final_dry_run_executor_guard_preview_ready"
+ and final_guard.get("dry_run_executor_invocation_allowed") is False
+ and final_guard.get("database_apply_authorized") is False
+ and no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and no_apply_enforcement.get("database_apply_authorized") is False
+ and bool(no_apply_closeout.get("no_apply_enforcement_closeout_id"))
+ )
+ target_hash_locked = (
+ final_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(final_closeout.get("expected_sha256"))
+ and bool(final_closeout.get("actual_sha256"))
+ and final_closeout.get("expected_sha256")
+ == final_closeout.get("actual_sha256")
+ and final_closeout.get("hash_matches") is True
+ and final_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ final_executor_guard_contract_blocks_database_apply = (
+ final_closeout_contract.get("executes_database_apply") is False
+ and final_closeout_contract.get("executes_endpoint") is False
+ and final_closeout_contract.get("executes_sql") is False
+ and final_closeout_contract.get("database_apply_authorized") is False
+ and final_closeout_contract.get("ready_for_database_apply_now") is False
+ and final_closeout_contract.get("signs_database_apply_authorization")
+ is False
+ and final_closeout_contract.get("writes_database") is False
+ and final_closeout_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and pre_apply_replay_preview_only
+ and apply_executor_readiness_contract_blocks_apply
+ )
+ checks = [
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "final_executor_guard_closeout_ready",
+ final_executor_guard_closeout_ready,
+ {
+ "result": final_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_final_executor_guard_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_final_executor_guard_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_final_executor_guard_closeout_check_count"
+ ),
+ },
+ "wait_for_final_executor_guard_closeout_ready",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "final_executor_guard_closeout_id": final_closeout.get(
+ "final_executor_guard_closeout_id"
+ ),
+ "pre_apply_replay_verifier_id": replay_verifier.get(
+ "verification_id"
+ ),
+ "final_guard_id": final_guard.get("guard_id"),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "pre_apply_replay_verifier_ready",
+ pre_apply_replay_verifier_ready,
+ {
+ "verification_id": replay_verifier.get("verification_id"),
+ "verifier_status": replay_verifier.get("verifier_status"),
+ "field_count": replay_verifier.get(
+ "pre_apply_replay_verifier_field_count"
+ ),
+ },
+ "wait_for_pre_apply_replay_verifier_ready",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "pre_apply_replay_preview_only",
+ pre_apply_replay_preview_only,
+ {
+ "replay_mode": replay_verifier.get("replay_mode"),
+ "dry_run_executor_invocation_allowed": replay_verifier.get(
+ "dry_run_executor_invocation_allowed"
+ ),
+ "database_apply_authorized": replay_verifier.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_pre_apply_replay_requests_execution",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "apply_executor_readiness_contract_bound",
+ apply_executor_readiness_contract_bound,
+ {
+ "contract_id": apply_executor_readiness_contract.get(
+ "contract_id"
+ ),
+ "source_pre_apply_replay_verifier_id": (
+ apply_executor_readiness_contract.get(
+ "source_pre_apply_replay_verifier_id"
+ )
+ ),
+ "field_count": apply_executor_readiness_contract.get(
+ "apply_executor_readiness_contract_field_count"
+ ),
+ },
+ "wait_for_apply_executor_readiness_contract_binding",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "apply_executor_readiness_contract_blocks_apply",
+ apply_executor_readiness_contract_blocks_apply,
+ {
+ "readiness_mode": apply_executor_readiness_contract.get(
+ "readiness_mode"
+ ),
+ "dry_run_executor_invocation_allowed": (
+ apply_executor_readiness_contract.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ ),
+ "database_apply_authorized": apply_executor_readiness_contract.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_apply_executor_readiness_contract_allows_apply",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "final_guard_and_no_apply_enforcement_carried_forward",
+ final_guard_and_no_apply_enforcement_carried_forward,
+ {
+ "guard_status": final_guard.get("guard_status"),
+ "enforcement_status": no_apply_enforcement.get(
+ "enforcement_status"
+ ),
+ "no_apply_enforcement_closeout_id": no_apply_closeout.get(
+ "no_apply_enforcement_closeout_id"
+ ),
+ },
+ "wait_for_final_guard_and_no_apply_enforcement_carry_forward",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": final_closeout.get("target_file"),
+ "hash_matches": final_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ final_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(final_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "final_executor_guard_contract_blocks_database_apply",
+ final_executor_guard_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_pre_apply_replay_verifier": (
+ final_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ )
+ ),
+ "database_apply_authorized": final_closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": final_closeout_contract.get("writes_database"),
+ },
+ "abort_if_final_executor_guard_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_pre_apply_replay_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ final_closeout_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": final_closeout_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_PRE_APPLY_REPLAY_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_FINAL_EXECUTOR_GUARD_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_apply_executor_readiness_contract = {
+ "pre_apply_replay_closeout_id": closeout_id,
+ "apply_executor_readiness_contract_id": (
+ apply_executor_readiness_contract_id
+ ),
+ "source_final_executor_guard_closeout_id": final_closeout.get(
+ "final_executor_guard_closeout_id"
+ ),
+ "source_pre_apply_replay_verifier_id": replay_verifier.get(
+ "verification_id"
+ ),
+ "source_final_dry_run_executor_guard_id": final_guard.get("guard_id"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_apply_executor_readiness_contract": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_apply_executor_readiness_closeout": (
+ closeout_ready
+ ),
+ "pre_apply_replay_closeout_ready": closeout_ready,
+ "apply_executor_readiness_contract_bound": closeout_ready,
+ "dry_run_executor_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_pre_apply_replay_closeout = {
+ "pre_apply_replay_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_pre_apply_replay_closeout"
+ ),
+ "source_final_executor_guard_closeout_id": final_closeout.get(
+ "final_executor_guard_closeout_id"
+ ),
+ "source_pre_apply_replay_verifier_id": replay_verifier.get(
+ "verification_id"
+ ),
+ "source_final_dry_run_executor_guard_id": final_guard.get("guard_id"),
+ "source_no_apply_enforcement_closeout_id": no_apply_closeout.get(
+ "no_apply_enforcement_closeout_id"
+ ),
+ "dry_run_command_shape_hash": final_guard.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_pre_apply_replay_closeout": (
+ closeout_ready
+ ),
+ "pre_apply_replay_closeout_fields": pre_apply_replay_closeout_fields,
+ "pre_apply_replay_closeout_field_count": len(
+ pre_apply_replay_closeout_fields
+ ),
+ "pre_apply_replay_closeout_acceptance_gates": (
+ pre_apply_replay_closeout_acceptance_gates
+ ),
+ "pre_apply_replay_closeout_acceptance_gate_count": len(
+ pre_apply_replay_closeout_acceptance_gates
+ ),
+ "apply_executor_readiness_contract": apply_executor_readiness_contract,
+ "apply_executor_readiness_contract_count": 1,
+ "apply_executor_readiness_contract_field_count": len(
+ apply_executor_readiness_contract_fields
+ ),
+ "pre_apply_replay_verifier": replay_verifier,
+ "pre_apply_replay_verifier_count": 1,
+ "pre_apply_replay_verifier_field_count": len(
+ replay_verifier.get("pre_apply_replay_verifier_fields") or []
+ ),
+ "final_executor_guard_closeout": final_closeout,
+ "final_executor_guard_closeout_count": 1,
+ "final_dry_run_executor_guard": final_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "no_apply_enforcement_closeout": no_apply_closeout,
+ "no_apply_enforcement_closeout_count": 1,
+ "target_file": final_closeout.get("target_file"),
+ "expected_sha256": final_closeout.get("expected_sha256"),
+ "actual_sha256": final_closeout.get("actual_sha256"),
+ "hash_matches": final_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "pre_apply_replay_closeout_only": True,
+ "apply_executor_readiness_contract_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_pre_apply_replay_closeout_contract = {
+ "mode": "controlled_dry_run_pre_apply_replay_closeout_and_apply_executor_readiness_contract_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-pre-apply-replay-closeout"
+ ),
+ "source_final_executor_guard_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-final-executor-guard-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_apply_executor_readiness_contract": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_pre_apply_replay_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout_count": 1,
+ "controlled_dry_run_pre_apply_replay_closeout_field_count": len(
+ pre_apply_replay_closeout_fields
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout_acceptance_gate_count": len(
+ pre_apply_replay_closeout_acceptance_gates
+ ),
+ "apply_executor_readiness_contract_count": 1,
+ "apply_executor_readiness_contract_field_count": len(
+ apply_executor_readiness_contract_fields
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_PRE_APPLY_REPLAY_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(final_closeout_result.get("success")),
+ "generated_at": final_closeout_result.get("generated_at"),
+ "source_policy": final_closeout_result.get("policy"),
+ "stats": final_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_apply_executor_readiness_contract": (
+ future_database_apply_controlled_dry_run_apply_executor_readiness_contract
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout": (
+ controlled_dry_run_pre_apply_replay_closeout
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout_contract": (
+ controlled_dry_run_pre_apply_replay_closeout_contract
+ ),
+ "controlled_dry_run_pre_apply_replay_closeout_checks": checks,
+ "source_controlled_dry_run_final_executor_guard_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_final_executor_guard_closeout_contract": (
+ final_closeout_contract
+ ),
+ "source_controlled_dry_run_final_executor_guard_closeout": (
+ final_closeout
+ ),
+ "source_database_apply_controlled_dry_run_pre_apply_replay_verifier": (
+ future_replay_verifier
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_pre_apply_replay_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run apply executor readiness closeout.",
+ "Keep the dry-run executor invocation disabled until invocation readiness is machine-verifiable.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out apply executor readiness and bind a dry-run invocation receipt."""
+ pre_apply_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_apply_contract = (
+ pre_apply_closeout_result.get(
+ "future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ )
+ or {}
+ )
+ pre_apply_closeout = (
+ pre_apply_closeout_result.get(
+ "controlled_dry_run_pre_apply_replay_closeout"
+ )
+ or {}
+ )
+ pre_apply_contract = (
+ pre_apply_closeout_result.get(
+ "controlled_dry_run_pre_apply_replay_closeout_contract"
+ )
+ or {}
+ )
+ summary = pre_apply_closeout_result.get("summary") or {}
+ safety = pre_apply_closeout_result.get("safety") or {}
+ readiness_contract = pre_apply_closeout.get(
+ "apply_executor_readiness_contract"
+ ) or {}
+ replay_verifier = pre_apply_closeout.get("pre_apply_replay_verifier") or {}
+ final_guard = pre_apply_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ pre_apply_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ rollback_binding = pre_apply_closeout.get("rollback_binding") or {}
+ verifier_binding = pre_apply_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_apply_executor_readiness_closeout_id(
+ pre_apply_closeout_result
+ )
+ receipt_id = f"{closeout_id}-dry-run-invocation-readiness-receipt"
+ readiness_closeout_fields = [
+ "apply_executor_readiness_closeout_id",
+ "source_pre_apply_replay_closeout_id",
+ "source_apply_executor_readiness_contract_id",
+ "source_pre_apply_replay_verifier_id",
+ "source_final_dry_run_executor_guard_id",
+ "dry_run_command_shape_hash",
+ "dry_run_invocation_readiness_receipt_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ readiness_closeout_acceptance_gates = [
+ "pre_apply_replay_closeout_ready",
+ "source_chain_ids_match",
+ "apply_executor_readiness_contract_ready",
+ "apply_executor_readiness_contract_blocks_invocation",
+ "dry_run_invocation_readiness_receipt_bound",
+ "dry_run_invocation_readiness_receipt_no_execute",
+ "pre_apply_replay_and_final_guard_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ dry_run_invocation_readiness_receipt_fields = [
+ "receipt_id",
+ "source_apply_executor_readiness_closeout_id",
+ "source_apply_executor_readiness_contract_id",
+ "source_pre_apply_replay_closeout_id",
+ "source_pre_apply_replay_verifier_id",
+ "required_readiness_mode",
+ "required_guard_status",
+ "required_command_shape_hash",
+ "receipt_mode",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_pre_apply_replay_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_apply_executor_readiness_contract_missing",
+ "abort_if_contract_allows_dry_run_executor_invocation",
+ "abort_if_invocation_readiness_receipt_missing",
+ "abort_if_invocation_readiness_receipt_executes",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ dry_run_invocation_readiness_receipt = {
+ "receipt_id": receipt_id,
+ "source_apply_executor_readiness_closeout_id": closeout_id,
+ "source_apply_executor_readiness_contract_id": readiness_contract.get(
+ "contract_id"
+ ),
+ "source_pre_apply_replay_closeout_id": pre_apply_closeout.get(
+ "pre_apply_replay_closeout_id"
+ ),
+ "source_pre_apply_replay_verifier_id": replay_verifier.get(
+ "verification_id"
+ ),
+ "source_final_dry_run_executor_guard_id": final_guard.get("guard_id"),
+ "required_readiness_mode": "apply_executor_readiness_contract_preview_only",
+ "required_guard_status": "final_dry_run_executor_guard_preview_ready",
+ "required_command_shape_hash": readiness_contract.get(
+ "required_command_shape_hash"
+ ),
+ "receipt_status": "dry_run_invocation_readiness_receipt_preview_ready",
+ "receipt_mode": "dry_run_invocation_readiness_preview_only",
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "dry_run_invocation_readiness_receipt_field_count": len(
+ dry_run_invocation_readiness_receipt_fields
+ ),
+ "dry_run_invocation_readiness_receipt_fields": (
+ dry_run_invocation_readiness_receipt_fields
+ ),
+ }
+ pre_apply_replay_closeout_ready = (
+ pre_apply_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_PRE_APPLY_REPLAY_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_pre_apply_replay_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_pre_apply_replay_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_pre_apply_replay_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(pre_apply_closeout.get("pre_apply_replay_closeout_id"))
+ and pre_apply_closeout.get("pre_apply_replay_closeout_id")
+ == future_apply_contract.get("pre_apply_replay_closeout_id")
+ == readiness_contract.get("source_pre_apply_replay_closeout_id")
+ == dry_run_invocation_readiness_receipt.get(
+ "source_pre_apply_replay_closeout_id"
+ )
+ and readiness_contract.get("contract_id")
+ == future_apply_contract.get("apply_executor_readiness_contract_id")
+ == dry_run_invocation_readiness_receipt.get(
+ "source_apply_executor_readiness_contract_id"
+ )
+ and replay_verifier.get("verification_id")
+ == readiness_contract.get("source_pre_apply_replay_verifier_id")
+ == dry_run_invocation_readiness_receipt.get(
+ "source_pre_apply_replay_verifier_id"
+ )
+ and final_guard.get("guard_id")
+ == readiness_contract.get("source_final_dry_run_executor_guard_id")
+ == dry_run_invocation_readiness_receipt.get(
+ "source_final_dry_run_executor_guard_id"
+ )
+ )
+ apply_executor_readiness_contract_ready = (
+ readiness_contract.get("readiness_status")
+ == "apply_executor_readiness_contract_preview_ready"
+ and readiness_contract.get("contract_id")
+ == future_apply_contract.get("apply_executor_readiness_contract_id")
+ and int(
+ readiness_contract.get(
+ "apply_executor_readiness_contract_field_count"
+ )
+ or 0
+ )
+ == 12
+ )
+ apply_executor_readiness_contract_blocks_invocation = (
+ readiness_contract.get("readiness_mode")
+ == "apply_executor_readiness_contract_preview_only"
+ and readiness_contract.get("dry_run_executor_invocation_allowed")
+ is False
+ and readiness_contract.get("endpoint_execution_allowed") is False
+ and readiness_contract.get("sql_execution_allowed") is False
+ and readiness_contract.get("database_write_allowed") is False
+ and readiness_contract.get("database_apply_authorized") is False
+ and readiness_contract.get("executes_database_apply") is False
+ and readiness_contract.get("executes_endpoint") is False
+ and readiness_contract.get("executes_sql") is False
+ and readiness_contract.get("writes_database") is False
+ )
+ dry_run_invocation_readiness_receipt_bound = (
+ bool(dry_run_invocation_readiness_receipt.get("receipt_id"))
+ and dry_run_invocation_readiness_receipt.get(
+ "source_apply_executor_readiness_closeout_id"
+ )
+ == closeout_id
+ and dry_run_invocation_readiness_receipt.get(
+ "source_apply_executor_readiness_contract_id"
+ )
+ == readiness_contract.get("contract_id")
+ and dry_run_invocation_readiness_receipt.get(
+ "required_command_shape_hash"
+ )
+ == readiness_contract.get("required_command_shape_hash")
+ and int(
+ dry_run_invocation_readiness_receipt.get(
+ "dry_run_invocation_readiness_receipt_field_count"
+ )
+ or 0
+ )
+ == len(dry_run_invocation_readiness_receipt_fields)
+ )
+ dry_run_invocation_readiness_receipt_no_execute = (
+ dry_run_invocation_readiness_receipt.get("receipt_mode")
+ == "dry_run_invocation_readiness_preview_only"
+ and dry_run_invocation_readiness_receipt.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ is False
+ and dry_run_invocation_readiness_receipt.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and dry_run_invocation_readiness_receipt.get("endpoint_execution_allowed")
+ is False
+ and dry_run_invocation_readiness_receipt.get("sql_execution_allowed")
+ is False
+ and dry_run_invocation_readiness_receipt.get("database_write_allowed")
+ is False
+ and dry_run_invocation_readiness_receipt.get("database_apply_authorized")
+ is False
+ and dry_run_invocation_readiness_receipt.get("executes_database_apply")
+ is False
+ and dry_run_invocation_readiness_receipt.get("executes_endpoint") is False
+ and dry_run_invocation_readiness_receipt.get("executes_sql") is False
+ and dry_run_invocation_readiness_receipt.get("writes_database") is False
+ )
+ pre_apply_replay_and_final_guard_carried_forward = (
+ replay_verifier.get("replay_mode") == "pre_apply_replay_preview_only"
+ and replay_verifier.get("dry_run_executor_invocation_allowed") is False
+ and replay_verifier.get("database_apply_authorized") is False
+ and final_guard.get("guard_status")
+ == "final_dry_run_executor_guard_preview_ready"
+ and final_guard.get("dry_run_executor_invocation_allowed") is False
+ and final_guard.get("database_apply_authorized") is False
+ and no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and no_apply_enforcement.get("database_apply_authorized") is False
+ )
+ target_hash_locked = (
+ pre_apply_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(pre_apply_closeout.get("expected_sha256"))
+ and bool(pre_apply_closeout.get("actual_sha256"))
+ and pre_apply_closeout.get("expected_sha256")
+ == pre_apply_closeout.get("actual_sha256")
+ and pre_apply_closeout.get("hash_matches") is True
+ and pre_apply_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ pre_apply_contract_blocks_database_apply = (
+ pre_apply_contract.get("executes_database_apply") is False
+ and pre_apply_contract.get("executes_endpoint") is False
+ and pre_apply_contract.get("executes_sql") is False
+ and pre_apply_contract.get("database_apply_authorized") is False
+ and pre_apply_contract.get("ready_for_database_apply_now") is False
+ and pre_apply_contract.get("ready_for_dry_run_executor_invocation_now")
+ is False
+ and pre_apply_contract.get("signs_database_apply_authorization") is False
+ and pre_apply_contract.get("writes_database") is False
+ and pre_apply_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and apply_executor_readiness_contract_blocks_invocation
+ and dry_run_invocation_readiness_receipt_no_execute
+ )
+ checks = [
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "pre_apply_replay_closeout_ready",
+ pre_apply_replay_closeout_ready,
+ {
+ "result": pre_apply_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_pre_apply_replay_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_pre_apply_replay_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_pre_apply_replay_closeout_check_count"
+ ),
+ },
+ "wait_for_pre_apply_replay_closeout_ready",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "pre_apply_replay_closeout_id": pre_apply_closeout.get(
+ "pre_apply_replay_closeout_id"
+ ),
+ "apply_executor_readiness_contract_id": readiness_contract.get(
+ "contract_id"
+ ),
+ "pre_apply_replay_verifier_id": replay_verifier.get(
+ "verification_id"
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "apply_executor_readiness_contract_ready",
+ apply_executor_readiness_contract_ready,
+ {
+ "contract_id": readiness_contract.get("contract_id"),
+ "readiness_status": readiness_contract.get("readiness_status"),
+ "field_count": readiness_contract.get(
+ "apply_executor_readiness_contract_field_count"
+ ),
+ },
+ "wait_for_apply_executor_readiness_contract_ready",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "apply_executor_readiness_contract_blocks_invocation",
+ apply_executor_readiness_contract_blocks_invocation,
+ {
+ "readiness_mode": readiness_contract.get("readiness_mode"),
+ "dry_run_executor_invocation_allowed": readiness_contract.get(
+ "dry_run_executor_invocation_allowed"
+ ),
+ "database_apply_authorized": readiness_contract.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_apply_executor_readiness_contract_allows_invocation",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "dry_run_invocation_readiness_receipt_bound",
+ dry_run_invocation_readiness_receipt_bound,
+ {
+ "receipt_id": dry_run_invocation_readiness_receipt.get(
+ "receipt_id"
+ ),
+ "source_apply_executor_readiness_contract_id": (
+ dry_run_invocation_readiness_receipt.get(
+ "source_apply_executor_readiness_contract_id"
+ )
+ ),
+ "field_count": dry_run_invocation_readiness_receipt.get(
+ "dry_run_invocation_readiness_receipt_field_count"
+ ),
+ },
+ "wait_for_dry_run_invocation_readiness_receipt_binding",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "dry_run_invocation_readiness_receipt_no_execute",
+ dry_run_invocation_readiness_receipt_no_execute,
+ {
+ "receipt_mode": dry_run_invocation_readiness_receipt.get(
+ "receipt_mode"
+ ),
+ "dry_run_executor_invocation_allowed": (
+ dry_run_invocation_readiness_receipt.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ ),
+ "ready_for_dry_run_executor_invocation_now": (
+ dry_run_invocation_readiness_receipt.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ ),
+ },
+ "abort_if_dry_run_invocation_readiness_receipt_executes",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "pre_apply_replay_and_final_guard_carried_forward",
+ pre_apply_replay_and_final_guard_carried_forward,
+ {
+ "replay_mode": replay_verifier.get("replay_mode"),
+ "guard_status": final_guard.get("guard_status"),
+ "enforcement_status": no_apply_enforcement.get(
+ "enforcement_status"
+ ),
+ },
+ "wait_for_pre_apply_replay_and_final_guard_carry_forward",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": pre_apply_closeout.get("target_file"),
+ "hash_matches": pre_apply_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ pre_apply_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(pre_apply_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "pre_apply_replay_contract_blocks_database_apply",
+ pre_apply_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_apply_executor_readiness_contract": (
+ pre_apply_contract.get(
+ "permits_future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ )
+ ),
+ "database_apply_authorized": pre_apply_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": pre_apply_contract.get("writes_database"),
+ },
+ "abort_if_pre_apply_replay_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_apply_executor_readiness_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ pre_apply_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": pre_apply_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_APPLY_EXECUTOR_READINESS_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_PRE_APPLY_REPLAY_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_invocation_readiness_receipt = {
+ "apply_executor_readiness_closeout_id": closeout_id,
+ "dry_run_invocation_readiness_receipt_id": receipt_id,
+ "source_pre_apply_replay_closeout_id": pre_apply_closeout.get(
+ "pre_apply_replay_closeout_id"
+ ),
+ "source_apply_executor_readiness_contract_id": readiness_contract.get(
+ "contract_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_invocation_readiness_receipt": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_invocation_receipt_closeout": (
+ closeout_ready
+ ),
+ "apply_executor_readiness_closeout_ready": closeout_ready,
+ "dry_run_invocation_readiness_receipt_bound": closeout_ready,
+ "dry_run_executor_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_apply_executor_readiness_closeout = {
+ "apply_executor_readiness_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_apply_executor_readiness_closeout"
+ ),
+ "source_pre_apply_replay_closeout_id": pre_apply_closeout.get(
+ "pre_apply_replay_closeout_id"
+ ),
+ "source_apply_executor_readiness_contract_id": readiness_contract.get(
+ "contract_id"
+ ),
+ "source_pre_apply_replay_verifier_id": replay_verifier.get(
+ "verification_id"
+ ),
+ "source_final_dry_run_executor_guard_id": final_guard.get("guard_id"),
+ "dry_run_command_shape_hash": readiness_contract.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_apply_executor_readiness_closeout": (
+ closeout_ready
+ ),
+ "apply_executor_readiness_closeout_fields": readiness_closeout_fields,
+ "apply_executor_readiness_closeout_field_count": len(
+ readiness_closeout_fields
+ ),
+ "apply_executor_readiness_closeout_acceptance_gates": (
+ readiness_closeout_acceptance_gates
+ ),
+ "apply_executor_readiness_closeout_acceptance_gate_count": len(
+ readiness_closeout_acceptance_gates
+ ),
+ "dry_run_invocation_readiness_receipt": (
+ dry_run_invocation_readiness_receipt
+ ),
+ "dry_run_invocation_readiness_receipt_count": 1,
+ "dry_run_invocation_readiness_receipt_field_count": len(
+ dry_run_invocation_readiness_receipt_fields
+ ),
+ "apply_executor_readiness_contract": readiness_contract,
+ "apply_executor_readiness_contract_count": 1,
+ "pre_apply_replay_closeout": pre_apply_closeout,
+ "pre_apply_replay_closeout_count": 1,
+ "pre_apply_replay_verifier": replay_verifier,
+ "pre_apply_replay_verifier_count": 1,
+ "final_dry_run_executor_guard": final_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "target_file": pre_apply_closeout.get("target_file"),
+ "expected_sha256": pre_apply_closeout.get("expected_sha256"),
+ "actual_sha256": pre_apply_closeout.get("actual_sha256"),
+ "hash_matches": pre_apply_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "apply_executor_readiness_closeout_only": True,
+ "dry_run_invocation_readiness_receipt_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_apply_executor_readiness_closeout_contract = {
+ "mode": "controlled_dry_run_apply_executor_readiness_closeout_and_dry_run_invocation_readiness_receipt_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-apply-executor-readiness-closeout"
+ ),
+ "source_pre_apply_replay_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-pre-apply-replay-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_invocation_readiness_receipt": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_apply_executor_readiness_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout_count": 1,
+ "controlled_dry_run_apply_executor_readiness_closeout_field_count": len(
+ readiness_closeout_fields
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout_acceptance_gate_count": len(
+ readiness_closeout_acceptance_gates
+ ),
+ "dry_run_invocation_readiness_receipt_count": 1,
+ "dry_run_invocation_readiness_receipt_field_count": len(
+ dry_run_invocation_readiness_receipt_fields
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_APPLY_EXECUTOR_READINESS_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(pre_apply_closeout_result.get("success")),
+ "generated_at": pre_apply_closeout_result.get("generated_at"),
+ "source_policy": pre_apply_closeout_result.get("policy"),
+ "stats": pre_apply_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_invocation_readiness_receipt": (
+ future_database_apply_controlled_dry_run_invocation_readiness_receipt
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout": (
+ controlled_dry_run_apply_executor_readiness_closeout
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout_contract": (
+ controlled_dry_run_apply_executor_readiness_closeout_contract
+ ),
+ "controlled_dry_run_apply_executor_readiness_closeout_checks": checks,
+ "source_controlled_dry_run_pre_apply_replay_closeout_summary": summary,
+ "source_controlled_dry_run_pre_apply_replay_closeout_contract": (
+ pre_apply_contract
+ ),
+ "source_controlled_dry_run_pre_apply_replay_closeout": (
+ pre_apply_closeout
+ ),
+ "source_database_apply_controlled_dry_run_apply_executor_readiness_contract": (
+ future_apply_contract
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_apply_executor_readiness_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run invocation receipt closeout.",
+ "Keep actual dry-run executor invocation disabled until a dedicated no-write invocation lane is machine-verifiable.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out invocation readiness receipt and bind a no-write package."""
+ readiness_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_receipt = (
+ readiness_closeout_result.get(
+ "future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ )
+ or {}
+ )
+ readiness_closeout = (
+ readiness_closeout_result.get(
+ "controlled_dry_run_apply_executor_readiness_closeout"
+ )
+ or {}
+ )
+ readiness_closeout_contract = (
+ readiness_closeout_result.get(
+ "controlled_dry_run_apply_executor_readiness_closeout_contract"
+ )
+ or {}
+ )
+ summary = readiness_closeout_result.get("summary") or {}
+ safety = readiness_closeout_result.get("safety") or {}
+ receipt = readiness_closeout.get("dry_run_invocation_readiness_receipt") or {}
+ readiness_contract = (
+ readiness_closeout.get("apply_executor_readiness_contract") or {}
+ )
+ pre_apply_closeout = readiness_closeout.get("pre_apply_replay_closeout") or {}
+ replay_verifier = readiness_closeout.get("pre_apply_replay_verifier") or {}
+ final_guard = readiness_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ readiness_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ rollback_binding = readiness_closeout.get("rollback_binding") or {}
+ verifier_binding = readiness_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_invocation_receipt_closeout_id(
+ readiness_closeout_result
+ )
+ package_id = f"{closeout_id}-no-write-invocation-package"
+ invocation_receipt_closeout_fields = [
+ "invocation_receipt_closeout_id",
+ "source_apply_executor_readiness_closeout_id",
+ "source_dry_run_invocation_readiness_receipt_id",
+ "source_apply_executor_readiness_contract_id",
+ "source_pre_apply_replay_closeout_id",
+ "dry_run_command_shape_hash",
+ "no_write_invocation_package_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ invocation_receipt_closeout_acceptance_gates = [
+ "apply_executor_readiness_closeout_ready",
+ "source_chain_ids_match",
+ "dry_run_invocation_readiness_receipt_ready",
+ "dry_run_invocation_readiness_receipt_no_execute",
+ "no_write_invocation_package_bound",
+ "no_write_invocation_package_blocks_execution",
+ "apply_executor_readiness_and_replay_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ no_write_invocation_package_fields = [
+ "package_id",
+ "source_invocation_receipt_closeout_id",
+ "source_dry_run_invocation_readiness_receipt_id",
+ "source_apply_executor_readiness_closeout_id",
+ "source_apply_executor_readiness_contract_id",
+ "required_receipt_mode",
+ "required_command_shape_hash",
+ "package_mode",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_apply_executor_readiness_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_dry_run_invocation_readiness_receipt_missing",
+ "abort_if_invocation_readiness_receipt_executes",
+ "abort_if_no_write_invocation_package_missing",
+ "abort_if_no_write_invocation_package_executes",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ no_write_invocation_package = {
+ "package_id": package_id,
+ "source_invocation_receipt_closeout_id": closeout_id,
+ "source_dry_run_invocation_readiness_receipt_id": receipt.get(
+ "receipt_id"
+ ),
+ "source_apply_executor_readiness_closeout_id": readiness_closeout.get(
+ "apply_executor_readiness_closeout_id"
+ ),
+ "source_apply_executor_readiness_contract_id": readiness_contract.get(
+ "contract_id"
+ ),
+ "source_pre_apply_replay_closeout_id": pre_apply_closeout.get(
+ "pre_apply_replay_closeout_id"
+ ),
+ "required_receipt_mode": "dry_run_invocation_readiness_preview_only",
+ "required_command_shape_hash": receipt.get("required_command_shape_hash"),
+ "package_status": "no_write_invocation_package_preview_ready",
+ "package_mode": "no_write_invocation_package_preview_only",
+ "dry_run_executor_invocation_allowed": False,
+ "ready_for_no_write_dry_run_invocation_package_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "writes_package_artifact": False,
+ "reads_secret_in_preview": False,
+ "no_write_invocation_package_field_count": len(
+ no_write_invocation_package_fields
+ ),
+ "no_write_invocation_package_fields": no_write_invocation_package_fields,
+ }
+ apply_executor_readiness_closeout_ready = (
+ readiness_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_APPLY_EXECUTOR_READINESS_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_apply_executor_readiness_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_apply_executor_readiness_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_apply_executor_readiness_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(readiness_closeout.get("apply_executor_readiness_closeout_id"))
+ and readiness_closeout.get("apply_executor_readiness_closeout_id")
+ == future_receipt.get("apply_executor_readiness_closeout_id")
+ == receipt.get("source_apply_executor_readiness_closeout_id")
+ == no_write_invocation_package.get(
+ "source_apply_executor_readiness_closeout_id"
+ )
+ and receipt.get("receipt_id")
+ == future_receipt.get("dry_run_invocation_readiness_receipt_id")
+ == no_write_invocation_package.get(
+ "source_dry_run_invocation_readiness_receipt_id"
+ )
+ and readiness_contract.get("contract_id")
+ == receipt.get("source_apply_executor_readiness_contract_id")
+ == no_write_invocation_package.get(
+ "source_apply_executor_readiness_contract_id"
+ )
+ and pre_apply_closeout.get("pre_apply_replay_closeout_id")
+ == receipt.get("source_pre_apply_replay_closeout_id")
+ == no_write_invocation_package.get("source_pre_apply_replay_closeout_id")
+ )
+ dry_run_invocation_readiness_receipt_ready = (
+ receipt.get("receipt_status")
+ == "dry_run_invocation_readiness_receipt_preview_ready"
+ and receipt.get("receipt_id")
+ == future_receipt.get("dry_run_invocation_readiness_receipt_id")
+ and int(receipt.get("dry_run_invocation_readiness_receipt_field_count") or 0)
+ == 12
+ )
+ dry_run_invocation_readiness_receipt_no_execute = (
+ receipt.get("receipt_mode") == "dry_run_invocation_readiness_preview_only"
+ and receipt.get("dry_run_executor_invocation_allowed") is False
+ and receipt.get("ready_for_dry_run_executor_invocation_now") is False
+ and receipt.get("endpoint_execution_allowed") is False
+ and receipt.get("sql_execution_allowed") is False
+ and receipt.get("database_write_allowed") is False
+ and receipt.get("database_apply_authorized") is False
+ and receipt.get("executes_database_apply") is False
+ and receipt.get("executes_endpoint") is False
+ and receipt.get("executes_sql") is False
+ and receipt.get("writes_database") is False
+ )
+ no_write_invocation_package_bound = (
+ bool(no_write_invocation_package.get("package_id"))
+ and no_write_invocation_package.get(
+ "source_invocation_receipt_closeout_id"
+ )
+ == closeout_id
+ and no_write_invocation_package.get(
+ "source_dry_run_invocation_readiness_receipt_id"
+ )
+ == receipt.get("receipt_id")
+ and no_write_invocation_package.get("required_command_shape_hash")
+ == receipt.get("required_command_shape_hash")
+ and int(
+ no_write_invocation_package.get(
+ "no_write_invocation_package_field_count"
+ )
+ or 0
+ )
+ == len(no_write_invocation_package_fields)
+ )
+ no_write_invocation_package_blocks_execution = (
+ no_write_invocation_package.get("package_mode")
+ == "no_write_invocation_package_preview_only"
+ and no_write_invocation_package.get("dry_run_executor_invocation_allowed")
+ is False
+ and no_write_invocation_package.get(
+ "ready_for_no_write_dry_run_invocation_package_now"
+ )
+ is False
+ and no_write_invocation_package.get(
+ "ready_for_actual_dry_run_execution_now"
+ )
+ is False
+ and no_write_invocation_package.get("endpoint_execution_allowed") is False
+ and no_write_invocation_package.get("sql_execution_allowed") is False
+ and no_write_invocation_package.get("database_write_allowed") is False
+ and no_write_invocation_package.get("database_apply_authorized") is False
+ and no_write_invocation_package.get("executes_database_apply") is False
+ and no_write_invocation_package.get("executes_endpoint") is False
+ and no_write_invocation_package.get("executes_sql") is False
+ and no_write_invocation_package.get("writes_database") is False
+ )
+ apply_executor_readiness_and_replay_carried_forward = (
+ readiness_contract.get("readiness_mode")
+ == "apply_executor_readiness_contract_preview_only"
+ and readiness_contract.get("dry_run_executor_invocation_allowed")
+ is False
+ and readiness_contract.get("database_apply_authorized") is False
+ and replay_verifier.get("replay_mode") == "pre_apply_replay_preview_only"
+ and replay_verifier.get("dry_run_executor_invocation_allowed") is False
+ and replay_verifier.get("database_apply_authorized") is False
+ and final_guard.get("guard_status")
+ == "final_dry_run_executor_guard_preview_ready"
+ and final_guard.get("dry_run_executor_invocation_allowed") is False
+ and no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and no_apply_enforcement.get("database_apply_authorized") is False
+ )
+ target_hash_locked = (
+ readiness_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(readiness_closeout.get("expected_sha256"))
+ and bool(readiness_closeout.get("actual_sha256"))
+ and readiness_closeout.get("expected_sha256")
+ == readiness_closeout.get("actual_sha256")
+ and readiness_closeout.get("hash_matches") is True
+ and readiness_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ readiness_closeout_contract_blocks_database_apply = (
+ readiness_closeout_contract.get("executes_database_apply") is False
+ and readiness_closeout_contract.get("executes_endpoint") is False
+ and readiness_closeout_contract.get("executes_sql") is False
+ and readiness_closeout_contract.get("database_apply_authorized") is False
+ and readiness_closeout_contract.get("ready_for_database_apply_now")
+ is False
+ and readiness_closeout_contract.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and readiness_closeout_contract.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and readiness_closeout_contract.get("signs_database_apply_authorization")
+ is False
+ and readiness_closeout_contract.get("writes_database") is False
+ and readiness_closeout_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and dry_run_invocation_readiness_receipt_no_execute
+ and no_write_invocation_package_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "apply_executor_readiness_closeout_ready",
+ apply_executor_readiness_closeout_ready,
+ {
+ "result": readiness_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_apply_executor_readiness_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_apply_executor_readiness_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_apply_executor_readiness_closeout_check_count"
+ ),
+ },
+ "wait_for_apply_executor_readiness_closeout_ready",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "apply_executor_readiness_closeout_id": readiness_closeout.get(
+ "apply_executor_readiness_closeout_id"
+ ),
+ "dry_run_invocation_readiness_receipt_id": receipt.get(
+ "receipt_id"
+ ),
+ "apply_executor_readiness_contract_id": readiness_contract.get(
+ "contract_id"
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "dry_run_invocation_readiness_receipt_ready",
+ dry_run_invocation_readiness_receipt_ready,
+ {
+ "receipt_id": receipt.get("receipt_id"),
+ "receipt_status": receipt.get("receipt_status"),
+ "field_count": receipt.get(
+ "dry_run_invocation_readiness_receipt_field_count"
+ ),
+ },
+ "wait_for_dry_run_invocation_readiness_receipt_ready",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "dry_run_invocation_readiness_receipt_no_execute",
+ dry_run_invocation_readiness_receipt_no_execute,
+ {
+ "receipt_mode": receipt.get("receipt_mode"),
+ "dry_run_executor_invocation_allowed": receipt.get(
+ "dry_run_executor_invocation_allowed"
+ ),
+ "ready_for_dry_run_executor_invocation_now": receipt.get(
+ "ready_for_dry_run_executor_invocation_now"
+ ),
+ },
+ "abort_if_dry_run_invocation_readiness_receipt_executes",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "no_write_invocation_package_bound",
+ no_write_invocation_package_bound,
+ {
+ "package_id": no_write_invocation_package.get("package_id"),
+ "source_dry_run_invocation_readiness_receipt_id": (
+ no_write_invocation_package.get(
+ "source_dry_run_invocation_readiness_receipt_id"
+ )
+ ),
+ "field_count": no_write_invocation_package.get(
+ "no_write_invocation_package_field_count"
+ ),
+ },
+ "wait_for_no_write_invocation_package_binding",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "no_write_invocation_package_blocks_execution",
+ no_write_invocation_package_blocks_execution,
+ {
+ "package_mode": no_write_invocation_package.get("package_mode"),
+ "dry_run_executor_invocation_allowed": (
+ no_write_invocation_package.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ ),
+ "ready_for_actual_dry_run_execution_now": (
+ no_write_invocation_package.get(
+ "ready_for_actual_dry_run_execution_now"
+ )
+ ),
+ },
+ "abort_if_no_write_invocation_package_executes",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "apply_executor_readiness_and_replay_carried_forward",
+ apply_executor_readiness_and_replay_carried_forward,
+ {
+ "readiness_mode": readiness_contract.get("readiness_mode"),
+ "replay_mode": replay_verifier.get("replay_mode"),
+ "guard_status": final_guard.get("guard_status"),
+ },
+ "wait_for_apply_executor_readiness_and_replay_carry_forward",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": readiness_closeout.get("target_file"),
+ "hash_matches": readiness_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ readiness_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(
+ readiness_closeout.get("actual_sha256")
+ ),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "apply_executor_readiness_closeout_contract_blocks_database_apply",
+ readiness_closeout_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_invocation_readiness_receipt": (
+ readiness_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ )
+ ),
+ "database_apply_authorized": readiness_closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": readiness_closeout_contract.get(
+ "writes_database"
+ ),
+ },
+ "abort_if_apply_executor_readiness_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_invocation_receipt_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ readiness_closeout_contract.get("manual_review_mode")
+ == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": readiness_closeout_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_INVOCATION_RECEIPT_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_APPLY_EXECUTOR_READINESS_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_no_write_invocation_package = {
+ "invocation_receipt_closeout_id": closeout_id,
+ "no_write_invocation_package_id": package_id,
+ "source_apply_executor_readiness_closeout_id": readiness_closeout.get(
+ "apply_executor_readiness_closeout_id"
+ ),
+ "source_dry_run_invocation_readiness_receipt_id": receipt.get(
+ "receipt_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_no_write_invocation_package": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_no_write_invocation_package_closeout": (
+ closeout_ready
+ ),
+ "invocation_receipt_closeout_ready": closeout_ready,
+ "no_write_invocation_package_bound": closeout_ready,
+ "dry_run_executor_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_invocation_receipt_closeout = {
+ "invocation_receipt_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_invocation_receipt_closeout"
+ ),
+ "source_apply_executor_readiness_closeout_id": readiness_closeout.get(
+ "apply_executor_readiness_closeout_id"
+ ),
+ "source_dry_run_invocation_readiness_receipt_id": receipt.get(
+ "receipt_id"
+ ),
+ "source_apply_executor_readiness_contract_id": readiness_contract.get(
+ "contract_id"
+ ),
+ "source_pre_apply_replay_closeout_id": pre_apply_closeout.get(
+ "pre_apply_replay_closeout_id"
+ ),
+ "dry_run_command_shape_hash": receipt.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_invocation_receipt_closeout": (
+ closeout_ready
+ ),
+ "invocation_receipt_closeout_fields": invocation_receipt_closeout_fields,
+ "invocation_receipt_closeout_field_count": len(
+ invocation_receipt_closeout_fields
+ ),
+ "invocation_receipt_closeout_acceptance_gates": (
+ invocation_receipt_closeout_acceptance_gates
+ ),
+ "invocation_receipt_closeout_acceptance_gate_count": len(
+ invocation_receipt_closeout_acceptance_gates
+ ),
+ "no_write_invocation_package": no_write_invocation_package,
+ "no_write_invocation_package_count": 1,
+ "no_write_invocation_package_field_count": len(
+ no_write_invocation_package_fields
+ ),
+ "dry_run_invocation_readiness_receipt": receipt,
+ "dry_run_invocation_readiness_receipt_count": 1,
+ "apply_executor_readiness_closeout": readiness_closeout,
+ "apply_executor_readiness_closeout_count": 1,
+ "apply_executor_readiness_contract": readiness_contract,
+ "apply_executor_readiness_contract_count": 1,
+ "pre_apply_replay_closeout": pre_apply_closeout,
+ "pre_apply_replay_closeout_count": 1,
+ "pre_apply_replay_verifier": replay_verifier,
+ "pre_apply_replay_verifier_count": 1,
+ "final_dry_run_executor_guard": final_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "target_file": readiness_closeout.get("target_file"),
+ "expected_sha256": readiness_closeout.get("expected_sha256"),
+ "actual_sha256": readiness_closeout.get("actual_sha256"),
+ "hash_matches": readiness_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "invocation_receipt_closeout_only": True,
+ "no_write_invocation_package_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_invocation_receipt_closeout_contract = {
+ "mode": "controlled_dry_run_invocation_receipt_closeout_and_no_write_invocation_package_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-invocation-receipt-closeout"
+ ),
+ "source_apply_executor_readiness_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-apply-executor-readiness-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_no_write_invocation_package": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_invocation_receipt_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_invocation_receipt_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_invocation_receipt_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_invocation_receipt_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_invocation_receipt_closeout_count": 1,
+ "controlled_dry_run_invocation_receipt_closeout_field_count": len(
+ invocation_receipt_closeout_fields
+ ),
+ "controlled_dry_run_invocation_receipt_closeout_acceptance_gate_count": len(
+ invocation_receipt_closeout_acceptance_gates
+ ),
+ "no_write_invocation_package_count": 1,
+ "no_write_invocation_package_field_count": len(
+ no_write_invocation_package_fields
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_INVOCATION_RECEIPT_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(readiness_closeout_result.get("success")),
+ "generated_at": readiness_closeout_result.get("generated_at"),
+ "source_policy": readiness_closeout_result.get("policy"),
+ "stats": readiness_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_no_write_invocation_package": (
+ future_database_apply_controlled_dry_run_no_write_invocation_package
+ ),
+ "controlled_dry_run_invocation_receipt_closeout": (
+ controlled_dry_run_invocation_receipt_closeout
+ ),
+ "controlled_dry_run_invocation_receipt_closeout_contract": (
+ controlled_dry_run_invocation_receipt_closeout_contract
+ ),
+ "controlled_dry_run_invocation_receipt_closeout_checks": checks,
+ "source_controlled_dry_run_apply_executor_readiness_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_apply_executor_readiness_closeout_contract": (
+ readiness_closeout_contract
+ ),
+ "source_controlled_dry_run_apply_executor_readiness_closeout": (
+ readiness_closeout
+ ),
+ "source_database_apply_controlled_dry_run_invocation_readiness_receipt": (
+ future_receipt
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_invocation_receipt_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run no-write invocation package closeout.",
+ "Keep actual dry-run executor invocation disabled until a dedicated execution-preflight guard is machine-verifiable.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the no-write invocation package and bind execution preflight."""
+ invocation_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_package = (
+ invocation_closeout_result.get(
+ "future_database_apply_controlled_dry_run_no_write_invocation_package"
+ )
+ or {}
+ )
+ invocation_closeout = (
+ invocation_closeout_result.get(
+ "controlled_dry_run_invocation_receipt_closeout"
+ )
+ or {}
+ )
+ invocation_closeout_contract = (
+ invocation_closeout_result.get(
+ "controlled_dry_run_invocation_receipt_closeout_contract"
+ )
+ or {}
+ )
+ summary = invocation_closeout_result.get("summary") or {}
+ safety = invocation_closeout_result.get("safety") or {}
+ package = invocation_closeout.get("no_write_invocation_package") or {}
+ receipt = (
+ invocation_closeout.get("dry_run_invocation_readiness_receipt") or {}
+ )
+ readiness_closeout = (
+ invocation_closeout.get("apply_executor_readiness_closeout") or {}
+ )
+ readiness_contract = (
+ invocation_closeout.get("apply_executor_readiness_contract") or {}
+ )
+ pre_apply_closeout = invocation_closeout.get("pre_apply_replay_closeout") or {}
+ replay_verifier = invocation_closeout.get("pre_apply_replay_verifier") or {}
+ final_guard = invocation_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ invocation_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ rollback_binding = invocation_closeout.get("rollback_binding") or {}
+ verifier_binding = invocation_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = (
+ _db_apply_controlled_dry_run_no_write_invocation_package_closeout_id(
+ invocation_closeout_result
+ )
+ )
+ guard_id = f"{closeout_id}-execution-preflight-guard"
+ package_closeout_fields = [
+ "no_write_invocation_package_closeout_id",
+ "source_invocation_receipt_closeout_id",
+ "source_no_write_invocation_package_id",
+ "source_dry_run_invocation_readiness_receipt_id",
+ "source_apply_executor_readiness_closeout_id",
+ "required_command_shape_hash",
+ "execution_preflight_guard_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ package_closeout_acceptance_gates = [
+ "invocation_receipt_closeout_ready",
+ "source_chain_ids_match",
+ "no_write_invocation_package_ready",
+ "no_write_invocation_package_no_execute",
+ "execution_preflight_guard_bound",
+ "execution_preflight_guard_blocks_execution",
+ "invocation_receipt_and_apply_readiness_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ execution_preflight_guard_fields = [
+ "guard_id",
+ "source_no_write_invocation_package_closeout_id",
+ "source_no_write_invocation_package_id",
+ "source_invocation_receipt_closeout_id",
+ "source_dry_run_invocation_readiness_receipt_id",
+ "required_package_mode",
+ "required_command_shape_hash",
+ "guard_mode",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_invocation_receipt_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_no_write_invocation_package_missing",
+ "abort_if_no_write_invocation_package_executes",
+ "abort_if_execution_preflight_guard_missing",
+ "abort_if_execution_preflight_guard_executes",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ execution_preflight_guard = {
+ "guard_id": guard_id,
+ "source_no_write_invocation_package_closeout_id": closeout_id,
+ "source_no_write_invocation_package_id": package.get("package_id"),
+ "source_invocation_receipt_closeout_id": invocation_closeout.get(
+ "invocation_receipt_closeout_id"
+ ),
+ "source_dry_run_invocation_readiness_receipt_id": receipt.get(
+ "receipt_id"
+ ),
+ "source_apply_executor_readiness_closeout_id": readiness_closeout.get(
+ "apply_executor_readiness_closeout_id"
+ ),
+ "required_package_mode": "no_write_invocation_package_preview_only",
+ "required_command_shape_hash": package.get("required_command_shape_hash"),
+ "guard_status": "execution_preflight_guard_preview_ready",
+ "guard_mode": "execution_preflight_guard_preview_only",
+ "dry_run_executor_invocation_allowed": False,
+ "ready_for_execution_preflight_guard_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "writes_guard_artifact": False,
+ "reads_secret_in_preview": False,
+ "execution_preflight_guard_field_count": len(
+ execution_preflight_guard_fields
+ ),
+ "execution_preflight_guard_fields": execution_preflight_guard_fields,
+ }
+ invocation_receipt_closeout_ready = (
+ invocation_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_INVOCATION_RECEIPT_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_invocation_receipt_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_invocation_receipt_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_invocation_receipt_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(invocation_closeout.get("invocation_receipt_closeout_id"))
+ and invocation_closeout.get("invocation_receipt_closeout_id")
+ == future_package.get("invocation_receipt_closeout_id")
+ == package.get("source_invocation_receipt_closeout_id")
+ == execution_preflight_guard.get("source_invocation_receipt_closeout_id")
+ and package.get("package_id")
+ == future_package.get("no_write_invocation_package_id")
+ == execution_preflight_guard.get("source_no_write_invocation_package_id")
+ and receipt.get("receipt_id")
+ == future_package.get("source_dry_run_invocation_readiness_receipt_id")
+ == package.get("source_dry_run_invocation_readiness_receipt_id")
+ == execution_preflight_guard.get(
+ "source_dry_run_invocation_readiness_receipt_id"
+ )
+ and readiness_closeout.get("apply_executor_readiness_closeout_id")
+ == future_package.get("source_apply_executor_readiness_closeout_id")
+ == package.get("source_apply_executor_readiness_closeout_id")
+ == execution_preflight_guard.get(
+ "source_apply_executor_readiness_closeout_id"
+ )
+ )
+ no_write_invocation_package_ready = (
+ package.get("package_status") == "no_write_invocation_package_preview_ready"
+ and package.get("package_id")
+ == future_package.get("no_write_invocation_package_id")
+ and int(package.get("no_write_invocation_package_field_count") or 0)
+ == 12
+ )
+ no_write_invocation_package_no_execute = (
+ package.get("package_mode") == "no_write_invocation_package_preview_only"
+ and package.get("dry_run_executor_invocation_allowed") is False
+ and package.get("ready_for_no_write_dry_run_invocation_package_now")
+ is False
+ and package.get("ready_for_actual_dry_run_execution_now") is False
+ and package.get("endpoint_execution_allowed") is False
+ and package.get("sql_execution_allowed") is False
+ and package.get("database_write_allowed") is False
+ and package.get("database_apply_authorized") is False
+ and package.get("executes_database_apply") is False
+ and package.get("executes_endpoint") is False
+ and package.get("executes_sql") is False
+ and package.get("writes_database") is False
+ )
+ execution_preflight_guard_bound = (
+ bool(execution_preflight_guard.get("guard_id"))
+ and execution_preflight_guard.get(
+ "source_no_write_invocation_package_closeout_id"
+ )
+ == closeout_id
+ and execution_preflight_guard.get("source_no_write_invocation_package_id")
+ == package.get("package_id")
+ and execution_preflight_guard.get("required_command_shape_hash")
+ == package.get("required_command_shape_hash")
+ and int(
+ execution_preflight_guard.get(
+ "execution_preflight_guard_field_count"
+ )
+ or 0
+ )
+ == len(execution_preflight_guard_fields)
+ )
+ execution_preflight_guard_blocks_execution = (
+ execution_preflight_guard.get("guard_mode")
+ == "execution_preflight_guard_preview_only"
+ and execution_preflight_guard.get("dry_run_executor_invocation_allowed")
+ is False
+ and execution_preflight_guard.get("ready_for_execution_preflight_guard_now")
+ is False
+ and execution_preflight_guard.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and execution_preflight_guard.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and execution_preflight_guard.get("endpoint_execution_allowed") is False
+ and execution_preflight_guard.get("sql_execution_allowed") is False
+ and execution_preflight_guard.get("database_write_allowed") is False
+ and execution_preflight_guard.get("database_apply_authorized") is False
+ and execution_preflight_guard.get("executes_database_apply") is False
+ and execution_preflight_guard.get("executes_endpoint") is False
+ and execution_preflight_guard.get("executes_sql") is False
+ and execution_preflight_guard.get("writes_database") is False
+ )
+ invocation_receipt_and_apply_readiness_carried_forward = (
+ receipt.get("receipt_mode") == "dry_run_invocation_readiness_preview_only"
+ and receipt.get("dry_run_executor_invocation_allowed") is False
+ and readiness_contract.get("readiness_mode")
+ == "apply_executor_readiness_contract_preview_only"
+ and readiness_contract.get("dry_run_executor_invocation_allowed")
+ is False
+ and replay_verifier.get("replay_mode") == "pre_apply_replay_preview_only"
+ and replay_verifier.get("database_apply_authorized") is False
+ and final_guard.get("guard_status")
+ == "final_dry_run_executor_guard_preview_ready"
+ and final_guard.get("dry_run_executor_invocation_allowed") is False
+ and no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and no_apply_enforcement.get("database_apply_authorized") is False
+ )
+ target_hash_locked = (
+ invocation_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(invocation_closeout.get("expected_sha256"))
+ and bool(invocation_closeout.get("actual_sha256"))
+ and invocation_closeout.get("expected_sha256")
+ == invocation_closeout.get("actual_sha256")
+ and invocation_closeout.get("hash_matches") is True
+ and invocation_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ invocation_receipt_closeout_contract_blocks_database_apply = (
+ invocation_closeout_contract.get("executes_database_apply") is False
+ and invocation_closeout_contract.get("executes_endpoint") is False
+ and invocation_closeout_contract.get("executes_sql") is False
+ and invocation_closeout_contract.get("database_apply_authorized") is False
+ and invocation_closeout_contract.get("ready_for_database_apply_now")
+ is False
+ and invocation_closeout_contract.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and invocation_closeout_contract.get(
+ "ready_for_actual_dry_run_execution_now"
+ )
+ is False
+ and invocation_closeout_contract.get("signs_database_apply_authorization")
+ is False
+ and invocation_closeout_contract.get("writes_database") is False
+ and invocation_closeout_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and no_write_invocation_package_no_execute
+ and execution_preflight_guard_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "invocation_receipt_closeout_ready",
+ invocation_receipt_closeout_ready,
+ {
+ "result": invocation_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_invocation_receipt_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_invocation_receipt_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_invocation_receipt_closeout_check_count"
+ ),
+ },
+ "wait_for_invocation_receipt_closeout_ready",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "invocation_receipt_closeout_id": invocation_closeout.get(
+ "invocation_receipt_closeout_id"
+ ),
+ "no_write_invocation_package_id": package.get("package_id"),
+ "dry_run_invocation_readiness_receipt_id": receipt.get(
+ "receipt_id"
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "no_write_invocation_package_ready",
+ no_write_invocation_package_ready,
+ {
+ "package_id": package.get("package_id"),
+ "package_status": package.get("package_status"),
+ "field_count": package.get("no_write_invocation_package_field_count"),
+ },
+ "wait_for_no_write_invocation_package_ready",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "no_write_invocation_package_no_execute",
+ no_write_invocation_package_no_execute,
+ {
+ "package_mode": package.get("package_mode"),
+ "dry_run_executor_invocation_allowed": package.get(
+ "dry_run_executor_invocation_allowed"
+ ),
+ "ready_for_actual_dry_run_execution_now": package.get(
+ "ready_for_actual_dry_run_execution_now"
+ ),
+ },
+ "abort_if_no_write_invocation_package_executes",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "execution_preflight_guard_bound",
+ execution_preflight_guard_bound,
+ {
+ "guard_id": execution_preflight_guard.get("guard_id"),
+ "source_no_write_invocation_package_id": (
+ execution_preflight_guard.get(
+ "source_no_write_invocation_package_id"
+ )
+ ),
+ "field_count": execution_preflight_guard.get(
+ "execution_preflight_guard_field_count"
+ ),
+ },
+ "wait_for_execution_preflight_guard_binding",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "execution_preflight_guard_blocks_execution",
+ execution_preflight_guard_blocks_execution,
+ {
+ "guard_mode": execution_preflight_guard.get("guard_mode"),
+ "dry_run_executor_invocation_allowed": (
+ execution_preflight_guard.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ ),
+ "ready_for_actual_dry_run_execution_now": (
+ execution_preflight_guard.get(
+ "ready_for_actual_dry_run_execution_now"
+ )
+ ),
+ },
+ "abort_if_execution_preflight_guard_executes",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "invocation_receipt_and_apply_readiness_carried_forward",
+ invocation_receipt_and_apply_readiness_carried_forward,
+ {
+ "receipt_mode": receipt.get("receipt_mode"),
+ "readiness_mode": readiness_contract.get("readiness_mode"),
+ "guard_status": final_guard.get("guard_status"),
+ },
+ "wait_for_invocation_receipt_and_apply_readiness_carry_forward",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": invocation_closeout.get("target_file"),
+ "hash_matches": invocation_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ invocation_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(
+ invocation_closeout.get("actual_sha256")
+ ),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "invocation_receipt_closeout_contract_blocks_database_apply",
+ invocation_receipt_closeout_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_no_write_invocation_package": (
+ invocation_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_no_write_invocation_package"
+ )
+ ),
+ "database_apply_authorized": invocation_closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": invocation_closeout_contract.get(
+ "writes_database"
+ ),
+ },
+ "abort_if_invocation_receipt_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_no_write_invocation_package_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ invocation_closeout_contract.get("manual_review_mode")
+ == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": invocation_closeout_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_INVOCATION_PACKAGE_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_INVOCATION_RECEIPT_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_execution_preflight_guard = {
+ "no_write_invocation_package_closeout_id": closeout_id,
+ "execution_preflight_guard_id": guard_id,
+ "source_invocation_receipt_closeout_id": invocation_closeout.get(
+ "invocation_receipt_closeout_id"
+ ),
+ "source_no_write_invocation_package_id": package.get("package_id"),
+ "source_dry_run_invocation_readiness_receipt_id": receipt.get(
+ "receipt_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_execution_preflight_guard": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_execution_preflight_guard_closeout": (
+ closeout_ready
+ ),
+ "no_write_invocation_package_closeout_ready": closeout_ready,
+ "execution_preflight_guard_bound": closeout_ready,
+ "dry_run_executor_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_no_write_invocation_package_closeout = {
+ "no_write_invocation_package_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_no_write_invocation_package_closeout"
+ ),
+ "source_invocation_receipt_closeout_id": invocation_closeout.get(
+ "invocation_receipt_closeout_id"
+ ),
+ "source_no_write_invocation_package_id": package.get("package_id"),
+ "source_dry_run_invocation_readiness_receipt_id": receipt.get(
+ "receipt_id"
+ ),
+ "source_apply_executor_readiness_closeout_id": readiness_closeout.get(
+ "apply_executor_readiness_closeout_id"
+ ),
+ "required_command_shape_hash": package.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_no_write_invocation_package_closeout": (
+ closeout_ready
+ ),
+ "no_write_invocation_package_closeout_fields": package_closeout_fields,
+ "no_write_invocation_package_closeout_field_count": len(
+ package_closeout_fields
+ ),
+ "no_write_invocation_package_closeout_acceptance_gates": (
+ package_closeout_acceptance_gates
+ ),
+ "no_write_invocation_package_closeout_acceptance_gate_count": len(
+ package_closeout_acceptance_gates
+ ),
+ "execution_preflight_guard": execution_preflight_guard,
+ "execution_preflight_guard_count": 1,
+ "execution_preflight_guard_field_count": len(
+ execution_preflight_guard_fields
+ ),
+ "no_write_invocation_package": package,
+ "no_write_invocation_package_count": 1,
+ "invocation_receipt_closeout": invocation_closeout,
+ "invocation_receipt_closeout_count": 1,
+ "dry_run_invocation_readiness_receipt": receipt,
+ "dry_run_invocation_readiness_receipt_count": 1,
+ "apply_executor_readiness_closeout": readiness_closeout,
+ "apply_executor_readiness_closeout_count": 1,
+ "apply_executor_readiness_contract": readiness_contract,
+ "apply_executor_readiness_contract_count": 1,
+ "pre_apply_replay_closeout": pre_apply_closeout,
+ "pre_apply_replay_closeout_count": 1,
+ "pre_apply_replay_verifier": replay_verifier,
+ "pre_apply_replay_verifier_count": 1,
+ "final_dry_run_executor_guard": final_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "target_file": invocation_closeout.get("target_file"),
+ "expected_sha256": invocation_closeout.get("expected_sha256"),
+ "actual_sha256": invocation_closeout.get("actual_sha256"),
+ "hash_matches": invocation_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "no_write_invocation_package_closeout_only": True,
+ "execution_preflight_guard_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_no_write_invocation_package_closeout_contract = {
+ "mode": "controlled_dry_run_no_write_invocation_package_closeout_and_execution_preflight_guard_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-no-write-invocation-package-closeout"
+ ),
+ "source_invocation_receipt_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-invocation-receipt-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_execution_preflight_guard": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_no_write_invocation_package_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout_count": 1,
+ "controlled_dry_run_no_write_invocation_package_closeout_field_count": len(
+ package_closeout_fields
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout_acceptance_gate_count": len(
+ package_closeout_acceptance_gates
+ ),
+ "execution_preflight_guard_count": 1,
+ "execution_preflight_guard_field_count": len(
+ execution_preflight_guard_fields
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_INVOCATION_PACKAGE_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(invocation_closeout_result.get("success")),
+ "generated_at": invocation_closeout_result.get("generated_at"),
+ "source_policy": invocation_closeout_result.get("policy"),
+ "stats": invocation_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_execution_preflight_guard": (
+ future_database_apply_controlled_dry_run_execution_preflight_guard
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout": (
+ controlled_dry_run_no_write_invocation_package_closeout
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout_contract": (
+ controlled_dry_run_no_write_invocation_package_closeout_contract
+ ),
+ "controlled_dry_run_no_write_invocation_package_closeout_checks": checks,
+ "source_controlled_dry_run_invocation_receipt_closeout_summary": summary,
+ "source_controlled_dry_run_invocation_receipt_closeout_contract": (
+ invocation_closeout_contract
+ ),
+ "source_controlled_dry_run_invocation_receipt_closeout": (
+ invocation_closeout
+ ),
+ "source_database_apply_controlled_dry_run_no_write_invocation_package": (
+ future_package
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_no_write_invocation_package_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run execution preflight guard closeout.",
+ "Keep actual dry-run executor invocation disabled until the execution preflight guard is separately closed out.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the execution preflight guard and bind runner invocation boundary."""
+ package_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_guard = (
+ package_closeout_result.get(
+ "future_database_apply_controlled_dry_run_execution_preflight_guard"
+ )
+ or {}
+ )
+ package_closeout = (
+ package_closeout_result.get(
+ "controlled_dry_run_no_write_invocation_package_closeout"
+ )
+ or {}
+ )
+ package_closeout_contract = (
+ package_closeout_result.get(
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ )
+ or {}
+ )
+ summary = package_closeout_result.get("summary") or {}
+ safety = package_closeout_result.get("safety") or {}
+ execution_preflight_guard = package_closeout.get("execution_preflight_guard") or {}
+ package = package_closeout.get("no_write_invocation_package") or {}
+ invocation_closeout = package_closeout.get("invocation_receipt_closeout") or {}
+ receipt = (
+ package_closeout.get("dry_run_invocation_readiness_receipt") or {}
+ )
+ readiness_closeout = (
+ package_closeout.get("apply_executor_readiness_closeout") or {}
+ )
+ readiness_contract = (
+ package_closeout.get("apply_executor_readiness_contract") or {}
+ )
+ pre_apply_closeout = package_closeout.get("pre_apply_replay_closeout") or {}
+ replay_verifier = package_closeout.get("pre_apply_replay_verifier") or {}
+ final_guard = package_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ package_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ rollback_binding = package_closeout.get("rollback_binding") or {}
+ verifier_binding = package_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_execution_preflight_guard_closeout_id(
+ package_closeout_result
+ )
+ boundary_id = f"{closeout_id}-runner-invocation-boundary"
+ guard_closeout_fields = [
+ "execution_preflight_guard_closeout_id",
+ "source_no_write_invocation_package_closeout_id",
+ "source_execution_preflight_guard_id",
+ "source_no_write_invocation_package_id",
+ "source_invocation_receipt_closeout_id",
+ "required_command_shape_hash",
+ "runner_invocation_boundary_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ guard_closeout_acceptance_gates = [
+ "no_write_invocation_package_closeout_ready",
+ "source_chain_ids_match",
+ "execution_preflight_guard_ready",
+ "execution_preflight_guard_no_execute",
+ "runner_invocation_boundary_bound",
+ "runner_invocation_boundary_blocks_execution",
+ "no_write_package_and_invocation_receipt_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ runner_invocation_boundary_fields = [
+ "boundary_id",
+ "source_execution_preflight_guard_closeout_id",
+ "source_execution_preflight_guard_id",
+ "source_no_write_invocation_package_closeout_id",
+ "source_no_write_invocation_package_id",
+ "required_guard_mode",
+ "required_command_shape_hash",
+ "boundary_mode",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_no_write_invocation_package_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_execution_preflight_guard_missing",
+ "abort_if_execution_preflight_guard_executes",
+ "abort_if_runner_invocation_boundary_missing",
+ "abort_if_runner_invocation_boundary_allows_invocation",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ runner_invocation_boundary = {
+ "boundary_id": boundary_id,
+ "source_execution_preflight_guard_closeout_id": closeout_id,
+ "source_execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ "source_no_write_invocation_package_closeout_id": package_closeout.get(
+ "no_write_invocation_package_closeout_id"
+ ),
+ "source_no_write_invocation_package_id": package.get("package_id"),
+ "source_invocation_receipt_closeout_id": invocation_closeout.get(
+ "invocation_receipt_closeout_id"
+ ),
+ "required_guard_mode": "execution_preflight_guard_preview_only",
+ "required_command_shape_hash": execution_preflight_guard.get(
+ "required_command_shape_hash"
+ ),
+ "boundary_status": "runner_invocation_boundary_preview_ready",
+ "boundary_mode": "runner_invocation_boundary_preview_only",
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_runner_invocation_boundary_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "reads_secret_in_preview": False,
+ "runner_invocation_boundary_field_count": len(
+ runner_invocation_boundary_fields
+ ),
+ "runner_invocation_boundary_fields": runner_invocation_boundary_fields,
+ }
+ package_closeout_ready = (
+ package_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_INVOCATION_PACKAGE_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_no_write_invocation_package_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_no_write_invocation_package_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_no_write_invocation_package_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(package_closeout.get("no_write_invocation_package_closeout_id"))
+ and package_closeout.get("no_write_invocation_package_closeout_id")
+ == future_guard.get("no_write_invocation_package_closeout_id")
+ == execution_preflight_guard.get(
+ "source_no_write_invocation_package_closeout_id"
+ )
+ == runner_invocation_boundary.get(
+ "source_no_write_invocation_package_closeout_id"
+ )
+ and execution_preflight_guard.get("guard_id")
+ == future_guard.get("execution_preflight_guard_id")
+ == runner_invocation_boundary.get(
+ "source_execution_preflight_guard_id"
+ )
+ and package.get("package_id")
+ == future_guard.get("source_no_write_invocation_package_id")
+ == execution_preflight_guard.get(
+ "source_no_write_invocation_package_id"
+ )
+ == runner_invocation_boundary.get(
+ "source_no_write_invocation_package_id"
+ )
+ and invocation_closeout.get("invocation_receipt_closeout_id")
+ == future_guard.get("source_invocation_receipt_closeout_id")
+ == execution_preflight_guard.get("source_invocation_receipt_closeout_id")
+ == runner_invocation_boundary.get("source_invocation_receipt_closeout_id")
+ )
+ execution_preflight_guard_ready = (
+ execution_preflight_guard.get("guard_status")
+ == "execution_preflight_guard_preview_ready"
+ and execution_preflight_guard.get("guard_id")
+ == future_guard.get("execution_preflight_guard_id")
+ and int(
+ execution_preflight_guard.get("execution_preflight_guard_field_count")
+ or 0
+ )
+ == 12
+ )
+ execution_preflight_guard_no_execute = (
+ execution_preflight_guard.get("guard_mode")
+ == "execution_preflight_guard_preview_only"
+ and execution_preflight_guard.get("dry_run_executor_invocation_allowed")
+ is False
+ and execution_preflight_guard.get("ready_for_execution_preflight_guard_now")
+ is False
+ and execution_preflight_guard.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and execution_preflight_guard.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and execution_preflight_guard.get("endpoint_execution_allowed") is False
+ and execution_preflight_guard.get("sql_execution_allowed") is False
+ and execution_preflight_guard.get("database_write_allowed") is False
+ and execution_preflight_guard.get("database_apply_authorized") is False
+ and execution_preflight_guard.get("executes_database_apply") is False
+ and execution_preflight_guard.get("executes_endpoint") is False
+ and execution_preflight_guard.get("executes_sql") is False
+ and execution_preflight_guard.get("writes_database") is False
+ )
+ runner_invocation_boundary_bound = (
+ bool(runner_invocation_boundary.get("boundary_id"))
+ and runner_invocation_boundary.get(
+ "source_execution_preflight_guard_closeout_id"
+ )
+ == closeout_id
+ and runner_invocation_boundary.get("source_execution_preflight_guard_id")
+ == execution_preflight_guard.get("guard_id")
+ and runner_invocation_boundary.get("required_command_shape_hash")
+ == execution_preflight_guard.get("required_command_shape_hash")
+ and int(
+ runner_invocation_boundary.get(
+ "runner_invocation_boundary_field_count"
+ )
+ or 0
+ )
+ == len(runner_invocation_boundary_fields)
+ )
+ runner_invocation_boundary_blocks_execution = (
+ runner_invocation_boundary.get("boundary_mode")
+ == "runner_invocation_boundary_preview_only"
+ and runner_invocation_boundary.get("dry_run_executor_invocation_allowed")
+ is False
+ and runner_invocation_boundary.get("runner_invocation_allowed") is False
+ and runner_invocation_boundary.get(
+ "ready_for_runner_invocation_boundary_now"
+ )
+ is False
+ and runner_invocation_boundary.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and runner_invocation_boundary.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and runner_invocation_boundary.get("endpoint_execution_allowed") is False
+ and runner_invocation_boundary.get("sql_execution_allowed") is False
+ and runner_invocation_boundary.get("database_write_allowed") is False
+ and runner_invocation_boundary.get("database_apply_authorized") is False
+ and runner_invocation_boundary.get("executes_database_apply") is False
+ and runner_invocation_boundary.get("executes_endpoint") is False
+ and runner_invocation_boundary.get("executes_sql") is False
+ and runner_invocation_boundary.get("writes_database") is False
+ and runner_invocation_boundary.get("captures_stdout") is False
+ and runner_invocation_boundary.get("captures_stderr") is False
+ )
+ no_write_package_and_invocation_receipt_carried_forward = (
+ package.get("package_mode") == "no_write_invocation_package_preview_only"
+ and package.get("dry_run_executor_invocation_allowed") is False
+ and invocation_closeout.get("invocation_receipt_closeout_only") is True
+ and invocation_closeout.get("database_apply_authorized") is False
+ and receipt.get("receipt_mode") == "dry_run_invocation_readiness_preview_only"
+ and receipt.get("dry_run_executor_invocation_allowed") is False
+ and readiness_contract.get("readiness_mode")
+ == "apply_executor_readiness_contract_preview_only"
+ and readiness_contract.get("dry_run_executor_invocation_allowed")
+ is False
+ and replay_verifier.get("replay_mode") == "pre_apply_replay_preview_only"
+ and replay_verifier.get("database_apply_authorized") is False
+ and final_guard.get("guard_status")
+ == "final_dry_run_executor_guard_preview_ready"
+ and final_guard.get("dry_run_executor_invocation_allowed") is False
+ and no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and no_apply_enforcement.get("database_apply_authorized") is False
+ )
+ target_hash_locked = (
+ package_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(package_closeout.get("expected_sha256"))
+ and bool(package_closeout.get("actual_sha256"))
+ and package_closeout.get("expected_sha256")
+ == package_closeout.get("actual_sha256")
+ and package_closeout.get("hash_matches") is True
+ and package_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ package_closeout_contract_blocks_database_apply = (
+ package_closeout_contract.get("executes_database_apply") is False
+ and package_closeout_contract.get("executes_endpoint") is False
+ and package_closeout_contract.get("executes_sql") is False
+ and package_closeout_contract.get("database_apply_authorized") is False
+ and package_closeout_contract.get("ready_for_database_apply_now")
+ is False
+ and package_closeout_contract.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and package_closeout_contract.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and package_closeout_contract.get("signs_database_apply_authorization")
+ is False
+ and package_closeout_contract.get("writes_database") is False
+ and package_closeout_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and execution_preflight_guard_no_execute
+ and runner_invocation_boundary_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "no_write_invocation_package_closeout_ready",
+ package_closeout_ready,
+ {
+ "result": package_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_no_write_invocation_package_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_no_write_invocation_package_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_no_write_invocation_package_closeout_check_count"
+ ),
+ },
+ "wait_for_no_write_invocation_package_closeout_ready",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "no_write_invocation_package_closeout_id": package_closeout.get(
+ "no_write_invocation_package_closeout_id"
+ ),
+ "execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ "no_write_invocation_package_id": package.get("package_id"),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "execution_preflight_guard_ready",
+ execution_preflight_guard_ready,
+ {
+ "guard_id": execution_preflight_guard.get("guard_id"),
+ "guard_status": execution_preflight_guard.get("guard_status"),
+ "field_count": execution_preflight_guard.get(
+ "execution_preflight_guard_field_count"
+ ),
+ },
+ "wait_for_execution_preflight_guard_ready",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "execution_preflight_guard_no_execute",
+ execution_preflight_guard_no_execute,
+ {
+ "guard_mode": execution_preflight_guard.get("guard_mode"),
+ "dry_run_executor_invocation_allowed": (
+ execution_preflight_guard.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ ),
+ "ready_for_actual_dry_run_execution_now": (
+ execution_preflight_guard.get(
+ "ready_for_actual_dry_run_execution_now"
+ )
+ ),
+ },
+ "abort_if_execution_preflight_guard_executes",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "runner_invocation_boundary_bound",
+ runner_invocation_boundary_bound,
+ {
+ "boundary_id": runner_invocation_boundary.get("boundary_id"),
+ "source_execution_preflight_guard_id": (
+ runner_invocation_boundary.get(
+ "source_execution_preflight_guard_id"
+ )
+ ),
+ "field_count": runner_invocation_boundary.get(
+ "runner_invocation_boundary_field_count"
+ ),
+ },
+ "wait_for_runner_invocation_boundary_binding",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "runner_invocation_boundary_blocks_execution",
+ runner_invocation_boundary_blocks_execution,
+ {
+ "boundary_mode": runner_invocation_boundary.get("boundary_mode"),
+ "dry_run_executor_invocation_allowed": (
+ runner_invocation_boundary.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ ),
+ "runner_invocation_allowed": runner_invocation_boundary.get(
+ "runner_invocation_allowed"
+ ),
+ },
+ "abort_if_runner_invocation_boundary_allows_invocation",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "no_write_package_and_invocation_receipt_carried_forward",
+ no_write_package_and_invocation_receipt_carried_forward,
+ {
+ "package_mode": package.get("package_mode"),
+ "receipt_mode": receipt.get("receipt_mode"),
+ "readiness_mode": readiness_contract.get("readiness_mode"),
+ },
+ "wait_for_no_write_package_and_invocation_receipt_carry_forward",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": package_closeout.get("target_file"),
+ "hash_matches": package_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ package_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(
+ package_closeout.get("actual_sha256")
+ ),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "no_write_invocation_package_closeout_contract_blocks_database_apply",
+ package_closeout_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_execution_preflight_guard": (
+ package_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_execution_preflight_guard"
+ )
+ ),
+ "database_apply_authorized": package_closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": package_closeout_contract.get(
+ "writes_database"
+ ),
+ },
+ "abort_if_no_write_invocation_package_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_execution_preflight_guard_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ package_closeout_contract.get("manual_review_mode")
+ == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": package_closeout_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PREFLIGHT_GUARD_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_INVOCATION_PACKAGE_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_runner_invocation_boundary = {
+ "execution_preflight_guard_closeout_id": closeout_id,
+ "runner_invocation_boundary_id": boundary_id,
+ "source_no_write_invocation_package_closeout_id": package_closeout.get(
+ "no_write_invocation_package_closeout_id"
+ ),
+ "source_execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ "source_no_write_invocation_package_id": package.get("package_id"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_runner_invocation_boundary": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_runner_invocation_boundary_closeout": (
+ closeout_ready
+ ),
+ "execution_preflight_guard_closeout_ready": closeout_ready,
+ "runner_invocation_boundary_bound": closeout_ready,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_execution_preflight_guard_closeout = {
+ "execution_preflight_guard_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_execution_preflight_guard_closeout"
+ ),
+ "source_no_write_invocation_package_closeout_id": package_closeout.get(
+ "no_write_invocation_package_closeout_id"
+ ),
+ "source_execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ "source_no_write_invocation_package_id": package.get("package_id"),
+ "source_invocation_receipt_closeout_id": invocation_closeout.get(
+ "invocation_receipt_closeout_id"
+ ),
+ "required_command_shape_hash": execution_preflight_guard.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_execution_preflight_guard_closeout": (
+ closeout_ready
+ ),
+ "execution_preflight_guard_closeout_fields": guard_closeout_fields,
+ "execution_preflight_guard_closeout_field_count": len(
+ guard_closeout_fields
+ ),
+ "execution_preflight_guard_closeout_acceptance_gates": (
+ guard_closeout_acceptance_gates
+ ),
+ "execution_preflight_guard_closeout_acceptance_gate_count": len(
+ guard_closeout_acceptance_gates
+ ),
+ "runner_invocation_boundary": runner_invocation_boundary,
+ "runner_invocation_boundary_count": 1,
+ "runner_invocation_boundary_field_count": len(
+ runner_invocation_boundary_fields
+ ),
+ "execution_preflight_guard": execution_preflight_guard,
+ "execution_preflight_guard_count": 1,
+ "no_write_invocation_package_closeout": package_closeout,
+ "no_write_invocation_package_closeout_count": 1,
+ "no_write_invocation_package": package,
+ "no_write_invocation_package_count": 1,
+ "invocation_receipt_closeout": invocation_closeout,
+ "invocation_receipt_closeout_count": 1,
+ "dry_run_invocation_readiness_receipt": receipt,
+ "dry_run_invocation_readiness_receipt_count": 1,
+ "apply_executor_readiness_closeout": readiness_closeout,
+ "apply_executor_readiness_closeout_count": 1,
+ "apply_executor_readiness_contract": readiness_contract,
+ "apply_executor_readiness_contract_count": 1,
+ "pre_apply_replay_closeout": pre_apply_closeout,
+ "pre_apply_replay_closeout_count": 1,
+ "pre_apply_replay_verifier": replay_verifier,
+ "pre_apply_replay_verifier_count": 1,
+ "final_dry_run_executor_guard": final_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "target_file": package_closeout.get("target_file"),
+ "expected_sha256": package_closeout.get("expected_sha256"),
+ "actual_sha256": package_closeout.get("actual_sha256"),
+ "hash_matches": package_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "execution_preflight_guard_closeout_only": True,
+ "runner_invocation_boundary_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ }
+ controlled_dry_run_execution_preflight_guard_closeout_contract = {
+ "mode": "controlled_dry_run_execution_preflight_guard_closeout_and_runner_invocation_boundary_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-execution-preflight-guard-closeout"
+ ),
+ "source_no_write_invocation_package_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-no-write-invocation-package-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_runner_invocation_boundary": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_execution_preflight_guard_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout_count": 1,
+ "controlled_dry_run_execution_preflight_guard_closeout_field_count": len(
+ guard_closeout_fields
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout_acceptance_gate_count": len(
+ guard_closeout_acceptance_gates
+ ),
+ "runner_invocation_boundary_count": 1,
+ "runner_invocation_boundary_field_count": len(
+ runner_invocation_boundary_fields
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PREFLIGHT_GUARD_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(package_closeout_result.get("success")),
+ "generated_at": package_closeout_result.get("generated_at"),
+ "source_policy": package_closeout_result.get("policy"),
+ "stats": package_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_runner_invocation_boundary": (
+ future_database_apply_controlled_dry_run_runner_invocation_boundary
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout": (
+ controlled_dry_run_execution_preflight_guard_closeout
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout_contract": (
+ controlled_dry_run_execution_preflight_guard_closeout_contract
+ ),
+ "controlled_dry_run_execution_preflight_guard_closeout_checks": checks,
+ "source_controlled_dry_run_no_write_invocation_package_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_no_write_invocation_package_closeout_contract": (
+ package_closeout_contract
+ ),
+ "source_controlled_dry_run_no_write_invocation_package_closeout": (
+ package_closeout
+ ),
+ "source_database_apply_controlled_dry_run_execution_preflight_guard": (
+ future_guard
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_execution_preflight_guard_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run runner invocation boundary closeout.",
+ "Keep actual runner invocation disabled until the boundary closeout is separately machine-verifiable.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out runner invocation boundary and bind no-execution handoff."""
+ guard_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_boundary = (
+ guard_closeout_result.get(
+ "future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ )
+ or {}
+ )
+ guard_closeout = (
+ guard_closeout_result.get(
+ "controlled_dry_run_execution_preflight_guard_closeout"
+ )
+ or {}
+ )
+ guard_closeout_contract = (
+ guard_closeout_result.get(
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ )
+ or {}
+ )
+ summary = guard_closeout_result.get("summary") or {}
+ safety = guard_closeout_result.get("safety") or {}
+ boundary = guard_closeout.get("runner_invocation_boundary") or {}
+ execution_preflight_guard = guard_closeout.get("execution_preflight_guard") or {}
+ package_closeout = guard_closeout.get("no_write_invocation_package_closeout") or {}
+ package = guard_closeout.get("no_write_invocation_package") or {}
+ invocation_closeout = guard_closeout.get("invocation_receipt_closeout") or {}
+ receipt = guard_closeout.get("dry_run_invocation_readiness_receipt") or {}
+ readiness_closeout = guard_closeout.get("apply_executor_readiness_closeout") or {}
+ readiness_contract = guard_closeout.get("apply_executor_readiness_contract") or {}
+ pre_apply_closeout = guard_closeout.get("pre_apply_replay_closeout") or {}
+ replay_verifier = guard_closeout.get("pre_apply_replay_verifier") or {}
+ final_guard = guard_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ guard_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ rollback_binding = guard_closeout.get("rollback_binding") or {}
+ verifier_binding = guard_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = _db_apply_controlled_dry_run_runner_invocation_boundary_closeout_id(
+ guard_closeout_result
+ )
+ handoff_id = f"{closeout_id}-no-execution-receipt-handoff"
+ boundary_closeout_fields = [
+ "runner_invocation_boundary_closeout_id",
+ "source_execution_preflight_guard_closeout_id",
+ "source_runner_invocation_boundary_id",
+ "source_execution_preflight_guard_id",
+ "source_no_write_invocation_package_id",
+ "required_command_shape_hash",
+ "no_execution_receipt_handoff_id",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ boundary_closeout_acceptance_gates = [
+ "execution_preflight_guard_closeout_ready",
+ "source_chain_ids_match",
+ "runner_invocation_boundary_ready",
+ "runner_invocation_boundary_no_execute",
+ "no_execution_receipt_handoff_bound",
+ "no_execution_receipt_handoff_blocks_execution",
+ "execution_preflight_guard_and_no_write_package_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ no_execution_receipt_handoff_fields = [
+ "handoff_id",
+ "source_runner_invocation_boundary_closeout_id",
+ "source_runner_invocation_boundary_id",
+ "source_execution_preflight_guard_closeout_id",
+ "source_execution_preflight_guard_id",
+ "required_boundary_mode",
+ "required_command_shape_hash",
+ "handoff_mode",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_execution_preflight_guard_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_runner_invocation_boundary_missing",
+ "abort_if_runner_invocation_boundary_allows_invocation",
+ "abort_if_no_execution_receipt_handoff_missing",
+ "abort_if_no_execution_receipt_handoff_executes",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ no_execution_receipt_handoff = {
+ "handoff_id": handoff_id,
+ "source_runner_invocation_boundary_closeout_id": closeout_id,
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "source_execution_preflight_guard_closeout_id": guard_closeout.get(
+ "execution_preflight_guard_closeout_id"
+ ),
+ "source_execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ "source_no_write_invocation_package_id": package.get("package_id"),
+ "required_boundary_mode": "runner_invocation_boundary_preview_only",
+ "required_command_shape_hash": boundary.get("required_command_shape_hash"),
+ "handoff_status": "no_execution_receipt_handoff_preview_ready",
+ "handoff_mode": "no_execution_receipt_handoff_preview_only",
+ "execution_receipt_present": False,
+ "execution_receipt_required": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_no_execution_receipt_handoff_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "reads_secret_in_preview": False,
+ "no_execution_receipt_handoff_field_count": len(
+ no_execution_receipt_handoff_fields
+ ),
+ "no_execution_receipt_handoff_fields": no_execution_receipt_handoff_fields,
+ }
+ guard_closeout_ready = (
+ guard_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PREFLIGHT_GUARD_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_execution_preflight_guard_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_execution_preflight_guard_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_execution_preflight_guard_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(guard_closeout.get("execution_preflight_guard_closeout_id"))
+ and guard_closeout.get("execution_preflight_guard_closeout_id")
+ == future_boundary.get("execution_preflight_guard_closeout_id")
+ == boundary.get("source_execution_preflight_guard_closeout_id")
+ == no_execution_receipt_handoff.get(
+ "source_execution_preflight_guard_closeout_id"
+ )
+ and boundary.get("boundary_id")
+ == future_boundary.get("runner_invocation_boundary_id")
+ == no_execution_receipt_handoff.get(
+ "source_runner_invocation_boundary_id"
+ )
+ and execution_preflight_guard.get("guard_id")
+ == future_boundary.get("source_execution_preflight_guard_id")
+ == boundary.get("source_execution_preflight_guard_id")
+ == no_execution_receipt_handoff.get(
+ "source_execution_preflight_guard_id"
+ )
+ and package.get("package_id")
+ == future_boundary.get("source_no_write_invocation_package_id")
+ == boundary.get("source_no_write_invocation_package_id")
+ == no_execution_receipt_handoff.get(
+ "source_no_write_invocation_package_id"
+ )
+ )
+ runner_invocation_boundary_ready = (
+ boundary.get("boundary_status")
+ == "runner_invocation_boundary_preview_ready"
+ and boundary.get("boundary_id")
+ == future_boundary.get("runner_invocation_boundary_id")
+ and int(boundary.get("runner_invocation_boundary_field_count") or 0)
+ == 12
+ )
+ runner_invocation_boundary_no_execute = (
+ boundary.get("boundary_mode") == "runner_invocation_boundary_preview_only"
+ and boundary.get("dry_run_executor_invocation_allowed") is False
+ and boundary.get("runner_invocation_allowed") is False
+ and boundary.get("ready_for_runner_invocation_boundary_now") is False
+ and boundary.get("ready_for_dry_run_executor_invocation_now") is False
+ and boundary.get("ready_for_actual_dry_run_execution_now") is False
+ and boundary.get("endpoint_execution_allowed") is False
+ and boundary.get("sql_execution_allowed") is False
+ and boundary.get("database_write_allowed") is False
+ and boundary.get("database_apply_authorized") is False
+ and boundary.get("executes_database_apply") is False
+ and boundary.get("executes_endpoint") is False
+ and boundary.get("executes_sql") is False
+ and boundary.get("writes_database") is False
+ and boundary.get("captures_stdout") is False
+ and boundary.get("captures_stderr") is False
+ )
+ no_execution_receipt_handoff_bound = (
+ bool(no_execution_receipt_handoff.get("handoff_id"))
+ and no_execution_receipt_handoff.get(
+ "source_runner_invocation_boundary_closeout_id"
+ )
+ == closeout_id
+ and no_execution_receipt_handoff.get(
+ "source_runner_invocation_boundary_id"
+ )
+ == boundary.get("boundary_id")
+ and no_execution_receipt_handoff.get("required_command_shape_hash")
+ == boundary.get("required_command_shape_hash")
+ and int(
+ no_execution_receipt_handoff.get(
+ "no_execution_receipt_handoff_field_count"
+ )
+ or 0
+ )
+ == len(no_execution_receipt_handoff_fields)
+ )
+ no_execution_receipt_handoff_blocks_execution = (
+ no_execution_receipt_handoff.get("handoff_mode")
+ == "no_execution_receipt_handoff_preview_only"
+ and no_execution_receipt_handoff.get("execution_receipt_present") is False
+ and no_execution_receipt_handoff.get("execution_receipt_required") is False
+ and no_execution_receipt_handoff.get("dry_run_executor_invocation_allowed")
+ is False
+ and no_execution_receipt_handoff.get("runner_invocation_allowed") is False
+ and no_execution_receipt_handoff.get(
+ "ready_for_no_execution_receipt_handoff_now"
+ )
+ is False
+ and no_execution_receipt_handoff.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and no_execution_receipt_handoff.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and no_execution_receipt_handoff.get("endpoint_execution_allowed") is False
+ and no_execution_receipt_handoff.get("sql_execution_allowed") is False
+ and no_execution_receipt_handoff.get("database_write_allowed") is False
+ and no_execution_receipt_handoff.get("database_apply_authorized") is False
+ and no_execution_receipt_handoff.get("executes_database_apply") is False
+ and no_execution_receipt_handoff.get("executes_endpoint") is False
+ and no_execution_receipt_handoff.get("executes_sql") is False
+ and no_execution_receipt_handoff.get("writes_database") is False
+ and no_execution_receipt_handoff.get("captures_stdout") is False
+ and no_execution_receipt_handoff.get("captures_stderr") is False
+ and no_execution_receipt_handoff.get("stdout_included") is False
+ and no_execution_receipt_handoff.get("stderr_included") is False
+ )
+ execution_preflight_guard_and_no_write_package_carried_forward = (
+ execution_preflight_guard.get("guard_mode")
+ == "execution_preflight_guard_preview_only"
+ and execution_preflight_guard.get("dry_run_executor_invocation_allowed")
+ is False
+ and guard_closeout.get("execution_preflight_guard_closeout_only") is True
+ and package_closeout.get("no_write_invocation_package_closeout_only") is True
+ and package_closeout.get("database_apply_authorized") is False
+ and package.get("package_mode") == "no_write_invocation_package_preview_only"
+ and package.get("dry_run_executor_invocation_allowed") is False
+ and invocation_closeout.get("invocation_receipt_closeout_only") is True
+ and invocation_closeout.get("database_apply_authorized") is False
+ and receipt.get("receipt_mode") == "dry_run_invocation_readiness_preview_only"
+ and receipt.get("dry_run_executor_invocation_allowed") is False
+ and readiness_contract.get("readiness_mode")
+ == "apply_executor_readiness_contract_preview_only"
+ and readiness_contract.get("dry_run_executor_invocation_allowed")
+ is False
+ and replay_verifier.get("replay_mode") == "pre_apply_replay_preview_only"
+ and replay_verifier.get("database_apply_authorized") is False
+ and final_guard.get("guard_status")
+ == "final_dry_run_executor_guard_preview_ready"
+ and final_guard.get("dry_run_executor_invocation_allowed") is False
+ and no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and no_apply_enforcement.get("database_apply_authorized") is False
+ )
+ target_hash_locked = (
+ guard_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(guard_closeout.get("expected_sha256"))
+ and bool(guard_closeout.get("actual_sha256"))
+ and guard_closeout.get("expected_sha256")
+ == guard_closeout.get("actual_sha256")
+ and guard_closeout.get("hash_matches") is True
+ and guard_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ guard_closeout_contract_blocks_database_apply = (
+ guard_closeout_contract.get("executes_database_apply") is False
+ and guard_closeout_contract.get("executes_endpoint") is False
+ and guard_closeout_contract.get("executes_sql") is False
+ and guard_closeout_contract.get("database_apply_authorized") is False
+ and guard_closeout_contract.get("ready_for_database_apply_now") is False
+ and guard_closeout_contract.get("ready_for_dry_run_executor_invocation_now")
+ is False
+ and guard_closeout_contract.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and guard_closeout_contract.get("signs_database_apply_authorization")
+ is False
+ and guard_closeout_contract.get("writes_database") is False
+ and guard_closeout_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and runner_invocation_boundary_no_execute
+ and no_execution_receipt_handoff_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "execution_preflight_guard_closeout_ready",
+ guard_closeout_ready,
+ {
+ "result": guard_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_execution_preflight_guard_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_execution_preflight_guard_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_execution_preflight_guard_closeout_check_count"
+ ),
+ },
+ "wait_for_execution_preflight_guard_closeout_ready",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "execution_preflight_guard_closeout_id": guard_closeout.get(
+ "execution_preflight_guard_closeout_id"
+ ),
+ "runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "runner_invocation_boundary_ready",
+ runner_invocation_boundary_ready,
+ {
+ "boundary_id": boundary.get("boundary_id"),
+ "boundary_status": boundary.get("boundary_status"),
+ "field_count": boundary.get(
+ "runner_invocation_boundary_field_count"
+ ),
+ },
+ "wait_for_runner_invocation_boundary_ready",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "runner_invocation_boundary_no_execute",
+ runner_invocation_boundary_no_execute,
+ {
+ "boundary_mode": boundary.get("boundary_mode"),
+ "dry_run_executor_invocation_allowed": boundary.get(
+ "dry_run_executor_invocation_allowed"
+ ),
+ "runner_invocation_allowed": boundary.get(
+ "runner_invocation_allowed"
+ ),
+ },
+ "abort_if_runner_invocation_boundary_allows_invocation",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "no_execution_receipt_handoff_bound",
+ no_execution_receipt_handoff_bound,
+ {
+ "handoff_id": no_execution_receipt_handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_id": (
+ no_execution_receipt_handoff.get(
+ "source_runner_invocation_boundary_id"
+ )
+ ),
+ "field_count": no_execution_receipt_handoff.get(
+ "no_execution_receipt_handoff_field_count"
+ ),
+ },
+ "wait_for_no_execution_receipt_handoff_binding",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "no_execution_receipt_handoff_blocks_execution",
+ no_execution_receipt_handoff_blocks_execution,
+ {
+ "handoff_mode": no_execution_receipt_handoff.get("handoff_mode"),
+ "execution_receipt_present": no_execution_receipt_handoff.get(
+ "execution_receipt_present"
+ ),
+ "runner_invocation_allowed": no_execution_receipt_handoff.get(
+ "runner_invocation_allowed"
+ ),
+ },
+ "abort_if_no_execution_receipt_handoff_executes",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "execution_preflight_guard_and_no_write_package_carried_forward",
+ execution_preflight_guard_and_no_write_package_carried_forward,
+ {
+ "guard_closeout_only": guard_closeout.get(
+ "execution_preflight_guard_closeout_only"
+ ),
+ "package_closeout_only": package_closeout.get(
+ "no_write_invocation_package_closeout_only"
+ ),
+ "guard_mode": execution_preflight_guard.get("guard_mode"),
+ "package_mode": package.get("package_mode"),
+ "receipt_mode": receipt.get("receipt_mode"),
+ },
+ "wait_for_execution_preflight_guard_and_no_write_package_carry_forward",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": guard_closeout.get("target_file"),
+ "hash_matches": guard_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ guard_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(
+ guard_closeout.get("actual_sha256")
+ ),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "execution_preflight_guard_closeout_contract_blocks_database_apply",
+ guard_closeout_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_runner_invocation_boundary": (
+ guard_closeout_contract.get(
+ "permits_future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ )
+ ),
+ "database_apply_authorized": guard_closeout_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": guard_closeout_contract.get("writes_database"),
+ },
+ "abort_if_execution_preflight_guard_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_runner_invocation_boundary_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ guard_closeout_contract.get("manual_review_mode")
+ == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": guard_closeout_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_INVOCATION_BOUNDARY_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PREFLIGHT_GUARD_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_no_execution_receipt_handoff = {
+ "runner_invocation_boundary_closeout_id": closeout_id,
+ "no_execution_receipt_handoff_id": handoff_id,
+ "source_execution_preflight_guard_closeout_id": guard_closeout.get(
+ "execution_preflight_guard_closeout_id"
+ ),
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "source_execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_no_execution_receipt_handoff": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_no_execution_receipt_handoff_closeout": (
+ closeout_ready
+ ),
+ "runner_invocation_boundary_closeout_ready": closeout_ready,
+ "no_execution_receipt_handoff_bound": closeout_ready,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "execution_receipt_present": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_runner_invocation_boundary_closeout = {
+ "runner_invocation_boundary_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_runner_invocation_boundary_closeout"
+ ),
+ "source_execution_preflight_guard_closeout_id": guard_closeout.get(
+ "execution_preflight_guard_closeout_id"
+ ),
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "source_execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ "source_no_write_invocation_package_id": package.get("package_id"),
+ "required_command_shape_hash": boundary.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_runner_invocation_boundary_closeout": (
+ closeout_ready
+ ),
+ "runner_invocation_boundary_closeout_fields": boundary_closeout_fields,
+ "runner_invocation_boundary_closeout_field_count": len(
+ boundary_closeout_fields
+ ),
+ "runner_invocation_boundary_closeout_acceptance_gates": (
+ boundary_closeout_acceptance_gates
+ ),
+ "runner_invocation_boundary_closeout_acceptance_gate_count": len(
+ boundary_closeout_acceptance_gates
+ ),
+ "no_execution_receipt_handoff": no_execution_receipt_handoff,
+ "no_execution_receipt_handoff_count": 1,
+ "no_execution_receipt_handoff_field_count": len(
+ no_execution_receipt_handoff_fields
+ ),
+ "runner_invocation_boundary": boundary,
+ "runner_invocation_boundary_count": 1,
+ "execution_preflight_guard_closeout": guard_closeout,
+ "execution_preflight_guard_closeout_count": 1,
+ "execution_preflight_guard": execution_preflight_guard,
+ "execution_preflight_guard_count": 1,
+ "no_write_invocation_package_closeout": package_closeout,
+ "no_write_invocation_package_closeout_count": 1,
+ "no_write_invocation_package": package,
+ "no_write_invocation_package_count": 1,
+ "invocation_receipt_closeout": invocation_closeout,
+ "invocation_receipt_closeout_count": 1,
+ "dry_run_invocation_readiness_receipt": receipt,
+ "dry_run_invocation_readiness_receipt_count": 1,
+ "apply_executor_readiness_closeout": readiness_closeout,
+ "apply_executor_readiness_closeout_count": 1,
+ "apply_executor_readiness_contract": readiness_contract,
+ "apply_executor_readiness_contract_count": 1,
+ "pre_apply_replay_closeout": pre_apply_closeout,
+ "pre_apply_replay_closeout_count": 1,
+ "pre_apply_replay_verifier": replay_verifier,
+ "pre_apply_replay_verifier_count": 1,
+ "final_dry_run_executor_guard": final_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "target_file": guard_closeout.get("target_file"),
+ "expected_sha256": guard_closeout.get("expected_sha256"),
+ "actual_sha256": guard_closeout.get("actual_sha256"),
+ "hash_matches": guard_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "runner_invocation_boundary_closeout_only": True,
+ "no_execution_receipt_handoff_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ }
+ controlled_dry_run_runner_invocation_boundary_closeout_contract = {
+ "mode": "controlled_dry_run_runner_invocation_boundary_closeout_and_no_execution_receipt_handoff_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-runner-invocation-boundary-closeout"
+ ),
+ "source_execution_preflight_guard_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-execution-preflight-guard-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_no_execution_receipt_handoff": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_runner_invocation_boundary_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout_count": 1,
+ "controlled_dry_run_runner_invocation_boundary_closeout_field_count": len(
+ boundary_closeout_fields
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout_acceptance_gate_count": len(
+ boundary_closeout_acceptance_gates
+ ),
+ "no_execution_receipt_handoff_count": 1,
+ "no_execution_receipt_handoff_field_count": len(
+ no_execution_receipt_handoff_fields
+ ),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_INVOCATION_BOUNDARY_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(guard_closeout_result.get("success")),
+ "generated_at": guard_closeout_result.get("generated_at"),
+ "source_policy": guard_closeout_result.get("policy"),
+ "stats": guard_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_no_execution_receipt_handoff": (
+ future_database_apply_controlled_dry_run_no_execution_receipt_handoff
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout": (
+ controlled_dry_run_runner_invocation_boundary_closeout
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract": (
+ controlled_dry_run_runner_invocation_boundary_closeout_contract
+ ),
+ "controlled_dry_run_runner_invocation_boundary_closeout_checks": checks,
+ "source_controlled_dry_run_execution_preflight_guard_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_execution_preflight_guard_closeout_contract": (
+ guard_closeout_contract
+ ),
+ "source_controlled_dry_run_execution_preflight_guard_closeout": (
+ guard_closeout
+ ),
+ "source_database_apply_controlled_dry_run_runner_invocation_boundary": (
+ future_boundary
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_runner_invocation_boundary_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled dry-run no-execution receipt handoff closeout.",
+ "Keep actual runner invocation disabled until an execution receipt is separately authorized outside this preview lane.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out no-execution handoff and bind final no-runner proof."""
+ boundary_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_handoff = (
+ boundary_closeout_result.get(
+ "future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ )
+ or {}
+ )
+ boundary_closeout = (
+ boundary_closeout_result.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout"
+ )
+ or {}
+ )
+ boundary_contract = (
+ boundary_closeout_result.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ )
+ or {}
+ )
+ summary = boundary_closeout_result.get("summary") or {}
+ safety = boundary_closeout_result.get("safety") or {}
+ handoff = boundary_closeout.get("no_execution_receipt_handoff") or {}
+ boundary = boundary_closeout.get("runner_invocation_boundary") or {}
+ guard_closeout = boundary_closeout.get("execution_preflight_guard_closeout") or {}
+ execution_preflight_guard = boundary_closeout.get("execution_preflight_guard") or {}
+ package_closeout = boundary_closeout.get("no_write_invocation_package_closeout") or {}
+ package = boundary_closeout.get("no_write_invocation_package") or {}
+ invocation_closeout = boundary_closeout.get("invocation_receipt_closeout") or {}
+ receipt = boundary_closeout.get("dry_run_invocation_readiness_receipt") or {}
+ readiness_closeout = boundary_closeout.get("apply_executor_readiness_closeout") or {}
+ readiness_contract = boundary_closeout.get("apply_executor_readiness_contract") or {}
+ pre_apply_closeout = boundary_closeout.get("pre_apply_replay_closeout") or {}
+ replay_verifier = boundary_closeout.get("pre_apply_replay_verifier") or {}
+ final_guard = boundary_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ boundary_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ rollback_binding = boundary_closeout.get("rollback_binding") or {}
+ verifier_binding = boundary_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = (
+ _db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout_id(
+ boundary_closeout_result
+ )
+ )
+ proof_id = f"{closeout_id}-final-no-runner-execution-proof"
+ handoff_closeout_fields = [
+ "no_execution_receipt_handoff_closeout_id",
+ "source_runner_invocation_boundary_closeout_id",
+ "source_no_execution_receipt_handoff_id",
+ "source_runner_invocation_boundary_id",
+ "source_execution_preflight_guard_closeout_id",
+ "final_no_runner_execution_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ handoff_closeout_acceptance_gates = [
+ "runner_invocation_boundary_closeout_ready",
+ "source_chain_ids_match",
+ "no_execution_receipt_handoff_ready",
+ "no_execution_receipt_handoff_no_execute",
+ "final_no_runner_execution_proof_bound",
+ "final_no_runner_execution_proof_blocks_execution",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ final_no_runner_execution_proof_fields = [
+ "proof_id",
+ "source_no_execution_receipt_handoff_closeout_id",
+ "source_no_execution_receipt_handoff_id",
+ "source_runner_invocation_boundary_closeout_id",
+ "source_runner_invocation_boundary_id",
+ "source_execution_preflight_guard_closeout_id",
+ "proof_mode",
+ "dry_run_executor_invocation_allowed",
+ "runner_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_runner_invocation_boundary_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_no_execution_receipt_handoff_missing",
+ "abort_if_no_execution_receipt_handoff_allows_execution",
+ "abort_if_final_no_runner_execution_proof_missing",
+ "abort_if_final_no_runner_execution_proof_reports_execution",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ final_no_runner_execution_proof = {
+ "proof_id": proof_id,
+ "source_no_execution_receipt_handoff_closeout_id": closeout_id,
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "source_execution_preflight_guard_closeout_id": guard_closeout.get(
+ "execution_preflight_guard_closeout_id"
+ ),
+ "source_execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ "required_handoff_mode": "no_execution_receipt_handoff_preview_only",
+ "required_command_shape_hash": handoff.get("required_command_shape_hash"),
+ "proof_status": "final_no_runner_execution_proof_preview_ready",
+ "proof_mode": "final_no_runner_execution_proof_preview_only",
+ "execution_receipt_present": False,
+ "execution_receipt_required": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_final_no_runner_execution_proof_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "reads_secret_in_preview": False,
+ "final_no_runner_execution_proof_field_count": len(
+ final_no_runner_execution_proof_fields
+ ),
+ "final_no_runner_execution_proof_fields": (
+ final_no_runner_execution_proof_fields
+ ),
+ }
+ boundary_closeout_ready = (
+ boundary_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_INVOCATION_BOUNDARY_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(boundary_closeout.get("runner_invocation_boundary_closeout_id"))
+ and boundary_closeout.get("runner_invocation_boundary_closeout_id")
+ == future_handoff.get("runner_invocation_boundary_closeout_id")
+ == handoff.get("source_runner_invocation_boundary_closeout_id")
+ == final_no_runner_execution_proof.get(
+ "source_runner_invocation_boundary_closeout_id"
+ )
+ and handoff.get("handoff_id")
+ == future_handoff.get("no_execution_receipt_handoff_id")
+ == final_no_runner_execution_proof.get(
+ "source_no_execution_receipt_handoff_id"
+ )
+ and boundary.get("boundary_id")
+ == future_handoff.get("source_runner_invocation_boundary_id")
+ == handoff.get("source_runner_invocation_boundary_id")
+ == final_no_runner_execution_proof.get(
+ "source_runner_invocation_boundary_id"
+ )
+ and guard_closeout.get("execution_preflight_guard_closeout_id")
+ == future_handoff.get("source_execution_preflight_guard_closeout_id")
+ == handoff.get("source_execution_preflight_guard_closeout_id")
+ == final_no_runner_execution_proof.get(
+ "source_execution_preflight_guard_closeout_id"
+ )
+ and execution_preflight_guard.get("guard_id")
+ == future_handoff.get("source_execution_preflight_guard_id")
+ == handoff.get("source_execution_preflight_guard_id")
+ == final_no_runner_execution_proof.get("source_execution_preflight_guard_id")
+ )
+ no_execution_receipt_handoff_ready = (
+ handoff.get("handoff_status") == "no_execution_receipt_handoff_preview_ready"
+ and handoff.get("handoff_id")
+ == future_handoff.get("no_execution_receipt_handoff_id")
+ and int(handoff.get("no_execution_receipt_handoff_field_count") or 0)
+ == 12
+ and summary.get("no_execution_receipt_handoff_count") == 1
+ )
+ no_execution_receipt_handoff_no_execute = (
+ handoff.get("handoff_mode") == "no_execution_receipt_handoff_preview_only"
+ and handoff.get("execution_receipt_present") is False
+ and handoff.get("execution_receipt_required") is False
+ and handoff.get("dry_run_executor_invocation_allowed") is False
+ and handoff.get("runner_invocation_allowed") is False
+ and handoff.get("ready_for_no_execution_receipt_handoff_now") is False
+ and handoff.get("ready_for_dry_run_executor_invocation_now") is False
+ and handoff.get("ready_for_actual_dry_run_execution_now") is False
+ and handoff.get("endpoint_execution_allowed") is False
+ and handoff.get("sql_execution_allowed") is False
+ and handoff.get("database_write_allowed") is False
+ and handoff.get("database_apply_authorized") is False
+ and handoff.get("executes_database_apply") is False
+ and handoff.get("executes_endpoint") is False
+ and handoff.get("executes_sql") is False
+ and handoff.get("writes_database") is False
+ and handoff.get("captures_stdout") is False
+ and handoff.get("captures_stderr") is False
+ and handoff.get("stdout_included") is False
+ and handoff.get("stderr_included") is False
+ )
+ final_no_runner_execution_proof_bound = (
+ bool(final_no_runner_execution_proof.get("proof_id"))
+ and final_no_runner_execution_proof.get(
+ "source_no_execution_receipt_handoff_closeout_id"
+ )
+ == closeout_id
+ and final_no_runner_execution_proof.get(
+ "source_no_execution_receipt_handoff_id"
+ )
+ == handoff.get("handoff_id")
+ and final_no_runner_execution_proof.get("required_command_shape_hash")
+ == handoff.get("required_command_shape_hash")
+ and int(
+ final_no_runner_execution_proof.get(
+ "final_no_runner_execution_proof_field_count"
+ )
+ or 0
+ )
+ == len(final_no_runner_execution_proof_fields)
+ )
+ final_no_runner_execution_proof_blocks_execution = (
+ final_no_runner_execution_proof.get("proof_mode")
+ == "final_no_runner_execution_proof_preview_only"
+ and final_no_runner_execution_proof.get("execution_receipt_present")
+ is False
+ and final_no_runner_execution_proof.get("execution_receipt_required")
+ is False
+ and final_no_runner_execution_proof.get("dry_run_executor_invoked")
+ is False
+ and final_no_runner_execution_proof.get("runner_invocation_performed")
+ is False
+ and final_no_runner_execution_proof.get("endpoint_executed") is False
+ and final_no_runner_execution_proof.get("sql_executed") is False
+ and final_no_runner_execution_proof.get("database_written") is False
+ and final_no_runner_execution_proof.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ is False
+ and final_no_runner_execution_proof.get("runner_invocation_allowed")
+ is False
+ and final_no_runner_execution_proof.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and final_no_runner_execution_proof.get(
+ "ready_for_actual_dry_run_execution_now"
+ )
+ is False
+ and final_no_runner_execution_proof.get("endpoint_execution_allowed")
+ is False
+ and final_no_runner_execution_proof.get("sql_execution_allowed") is False
+ and final_no_runner_execution_proof.get("database_write_allowed") is False
+ and final_no_runner_execution_proof.get("database_apply_authorized")
+ is False
+ and final_no_runner_execution_proof.get("executes_database_apply")
+ is False
+ and final_no_runner_execution_proof.get("executes_endpoint") is False
+ and final_no_runner_execution_proof.get("executes_sql") is False
+ and final_no_runner_execution_proof.get("writes_database") is False
+ and final_no_runner_execution_proof.get("captures_stdout") is False
+ and final_no_runner_execution_proof.get("captures_stderr") is False
+ and final_no_runner_execution_proof.get("stdout_included") is False
+ and final_no_runner_execution_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ boundary_closeout.get("runner_invocation_boundary_closeout_only") is True
+ and boundary_closeout.get("no_execution_receipt_handoff_only") is True
+ and boundary_closeout.get("database_apply_authorized") is False
+ and guard_closeout.get("execution_preflight_guard_closeout_only") is True
+ and package_closeout.get("no_write_invocation_package_closeout_only")
+ is True
+ and package.get("package_mode") == "no_write_invocation_package_preview_only"
+ and invocation_closeout.get("invocation_receipt_closeout_only") is True
+ and receipt.get("receipt_mode") == "dry_run_invocation_readiness_preview_only"
+ and readiness_contract.get("readiness_mode")
+ == "apply_executor_readiness_contract_preview_only"
+ and replay_verifier.get("replay_mode") == "pre_apply_replay_preview_only"
+ and final_guard.get("guard_status")
+ == "final_dry_run_executor_guard_preview_ready"
+ and no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and no_execution_receipt_handoff_no_execute
+ )
+ target_hash_locked = (
+ boundary_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(boundary_closeout.get("expected_sha256"))
+ and bool(boundary_closeout.get("actual_sha256"))
+ and boundary_closeout.get("expected_sha256")
+ == boundary_closeout.get("actual_sha256")
+ and boundary_closeout.get("hash_matches") is True
+ and boundary_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ boundary_contract_blocks_database_apply = (
+ boundary_contract.get("executes_database_apply") is False
+ and boundary_contract.get("executes_endpoint") is False
+ and boundary_contract.get("executes_sql") is False
+ and boundary_contract.get("database_apply_authorized") is False
+ and boundary_contract.get("ready_for_database_apply_now") is False
+ and boundary_contract.get("ready_for_dry_run_executor_invocation_now")
+ is False
+ and boundary_contract.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and boundary_contract.get("signs_database_apply_authorization") is False
+ and boundary_contract.get("writes_database") is False
+ and boundary_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and no_execution_receipt_handoff_no_execute
+ and final_no_runner_execution_proof_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "runner_invocation_boundary_closeout_ready",
+ boundary_closeout_ready,
+ {
+ "result": boundary_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_runner_invocation_boundary_closeout_check_count"
+ ),
+ },
+ "wait_for_runner_invocation_boundary_closeout_ready",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "proof_id": final_no_runner_execution_proof.get("proof_id"),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "no_execution_receipt_handoff_ready",
+ no_execution_receipt_handoff_ready,
+ {
+ "handoff_id": handoff.get("handoff_id"),
+ "handoff_status": handoff.get("handoff_status"),
+ "field_count": handoff.get("no_execution_receipt_handoff_field_count"),
+ },
+ "wait_for_no_execution_receipt_handoff_ready",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "no_execution_receipt_handoff_no_execute",
+ no_execution_receipt_handoff_no_execute,
+ {
+ "handoff_mode": handoff.get("handoff_mode"),
+ "execution_receipt_present": handoff.get("execution_receipt_present"),
+ "runner_invocation_allowed": handoff.get("runner_invocation_allowed"),
+ },
+ "abort_if_no_execution_receipt_handoff_executes",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "final_no_runner_execution_proof_bound",
+ final_no_runner_execution_proof_bound,
+ {
+ "proof_id": final_no_runner_execution_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_id": (
+ final_no_runner_execution_proof.get(
+ "source_no_execution_receipt_handoff_id"
+ )
+ ),
+ "field_count": final_no_runner_execution_proof.get(
+ "final_no_runner_execution_proof_field_count"
+ ),
+ },
+ "wait_for_final_no_runner_execution_proof_binding",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "final_no_runner_execution_proof_blocks_execution",
+ final_no_runner_execution_proof_blocks_execution,
+ {
+ "proof_mode": final_no_runner_execution_proof.get("proof_mode"),
+ "dry_run_executor_invoked": final_no_runner_execution_proof.get(
+ "dry_run_executor_invoked"
+ ),
+ "runner_invocation_performed": final_no_runner_execution_proof.get(
+ "runner_invocation_performed"
+ ),
+ },
+ "abort_if_final_no_runner_execution_proof_reports_execution",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "runner_invocation_boundary_closeout_only": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_only"
+ ),
+ "no_execution_receipt_handoff_only": boundary_closeout.get(
+ "no_execution_receipt_handoff_only"
+ ),
+ "handoff_mode": handoff.get("handoff_mode"),
+ },
+ "wait_for_previous_closeouts_carry_forward",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": boundary_closeout.get("target_file"),
+ "hash_matches": boundary_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ boundary_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(boundary_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "runner_invocation_boundary_closeout_contract_blocks_database_apply",
+ boundary_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_no_execution_receipt_handoff": (
+ boundary_contract.get(
+ "permits_future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ )
+ ),
+ "database_apply_authorized": boundary_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": boundary_contract.get("writes_database"),
+ },
+ "abort_if_runner_invocation_boundary_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_no_execution_receipt_handoff_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ boundary_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": boundary_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_NO_EXECUTION_RECEIPT_HANDOFF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_INVOCATION_BOUNDARY_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_final_no_runner_execution_proof = {
+ "no_execution_receipt_handoff_closeout_id": closeout_id,
+ "final_no_runner_execution_proof_id": proof_id,
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "source_execution_preflight_guard_closeout_id": guard_closeout.get(
+ "execution_preflight_guard_closeout_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_final_no_runner_execution_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_final_no_runner_execution_proof_closeout": (
+ closeout_ready
+ ),
+ "no_execution_receipt_handoff_closeout_ready": closeout_ready,
+ "final_no_runner_execution_proof_bound": closeout_ready,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_no_execution_receipt_handoff_closeout = {
+ "no_execution_receipt_handoff_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_no_execution_receipt_handoff_closeout"
+ ),
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "source_execution_preflight_guard_closeout_id": guard_closeout.get(
+ "execution_preflight_guard_closeout_id"
+ ),
+ "source_execution_preflight_guard_id": execution_preflight_guard.get(
+ "guard_id"
+ ),
+ "required_command_shape_hash": handoff.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_no_execution_receipt_handoff_closeout": (
+ closeout_ready
+ ),
+ "no_execution_receipt_handoff_closeout_fields": handoff_closeout_fields,
+ "no_execution_receipt_handoff_closeout_field_count": len(
+ handoff_closeout_fields
+ ),
+ "no_execution_receipt_handoff_closeout_acceptance_gates": (
+ handoff_closeout_acceptance_gates
+ ),
+ "no_execution_receipt_handoff_closeout_acceptance_gate_count": len(
+ handoff_closeout_acceptance_gates
+ ),
+ "final_no_runner_execution_proof": final_no_runner_execution_proof,
+ "final_no_runner_execution_proof_count": 1,
+ "final_no_runner_execution_proof_field_count": len(
+ final_no_runner_execution_proof_fields
+ ),
+ "no_execution_receipt_handoff": handoff,
+ "no_execution_receipt_handoff_count": 1,
+ "runner_invocation_boundary_closeout": boundary_closeout,
+ "runner_invocation_boundary_closeout_count": 1,
+ "runner_invocation_boundary": boundary,
+ "runner_invocation_boundary_count": 1,
+ "execution_preflight_guard_closeout": guard_closeout,
+ "execution_preflight_guard_closeout_count": 1,
+ "execution_preflight_guard": execution_preflight_guard,
+ "execution_preflight_guard_count": 1,
+ "no_write_invocation_package_closeout": package_closeout,
+ "no_write_invocation_package_closeout_count": 1,
+ "no_write_invocation_package": package,
+ "no_write_invocation_package_count": 1,
+ "invocation_receipt_closeout": invocation_closeout,
+ "invocation_receipt_closeout_count": 1,
+ "dry_run_invocation_readiness_receipt": receipt,
+ "dry_run_invocation_readiness_receipt_count": 1,
+ "apply_executor_readiness_closeout": readiness_closeout,
+ "apply_executor_readiness_closeout_count": 1,
+ "apply_executor_readiness_contract": readiness_contract,
+ "apply_executor_readiness_contract_count": 1,
+ "pre_apply_replay_closeout": pre_apply_closeout,
+ "pre_apply_replay_closeout_count": 1,
+ "pre_apply_replay_verifier": replay_verifier,
+ "pre_apply_replay_verifier_count": 1,
+ "final_dry_run_executor_guard": final_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "target_file": boundary_closeout.get("target_file"),
+ "expected_sha256": boundary_closeout.get("expected_sha256"),
+ "actual_sha256": boundary_closeout.get("actual_sha256"),
+ "hash_matches": boundary_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "no_execution_receipt_handoff_closeout_only": True,
+ "final_no_runner_execution_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_no_execution_receipt_handoff_closeout_contract = {
+ "mode": "controlled_dry_run_no_execution_receipt_handoff_closeout_and_final_no_runner_execution_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout"
+ ),
+ "source_runner_invocation_boundary_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-runner-invocation-boundary-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_final_no_runner_execution_proof": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_count": 1,
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_field_count": len(
+ handoff_closeout_fields
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_acceptance_gate_count": len(
+ handoff_closeout_acceptance_gates
+ ),
+ "final_no_runner_execution_proof_count": 1,
+ "final_no_runner_execution_proof_field_count": len(
+ final_no_runner_execution_proof_fields
+ ),
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_NO_EXECUTION_RECEIPT_HANDOFF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(boundary_closeout_result.get("success")),
+ "generated_at": boundary_closeout_result.get("generated_at"),
+ "source_policy": boundary_closeout_result.get("policy"),
+ "stats": boundary_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_final_no_runner_execution_proof": (
+ future_database_apply_controlled_dry_run_final_no_runner_execution_proof
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout": (
+ controlled_dry_run_no_execution_receipt_handoff_closeout
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract": (
+ controlled_dry_run_no_execution_receipt_handoff_closeout_contract
+ ),
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_checks": checks,
+ "source_controlled_dry_run_runner_invocation_boundary_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_runner_invocation_boundary_closeout_contract": (
+ boundary_contract
+ ),
+ "source_controlled_dry_run_runner_invocation_boundary_closeout": (
+ boundary_closeout
+ ),
+ "source_database_apply_controlled_dry_run_no_execution_receipt_handoff": (
+ future_handoff
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future final no-runner-execution proof closeout.",
+ "Keep actual runner invocation disabled; this proof confirms no execution happened in this preview lane.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out final no-runner proof and bind controlled executor quarantine."""
+ proof_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_proof = (
+ proof_closeout_result.get(
+ "future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ )
+ or {}
+ )
+ handoff_closeout = (
+ proof_closeout_result.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout"
+ )
+ or {}
+ )
+ handoff_contract = (
+ proof_closeout_result.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ )
+ or {}
+ )
+ summary = proof_closeout_result.get("summary") or {}
+ safety = proof_closeout_result.get("safety") or {}
+ final_proof = handoff_closeout.get("final_no_runner_execution_proof") or {}
+ handoff = handoff_closeout.get("no_execution_receipt_handoff") or {}
+ boundary_closeout = handoff_closeout.get("runner_invocation_boundary_closeout") or {}
+ boundary = handoff_closeout.get("runner_invocation_boundary") or {}
+ guard_closeout = handoff_closeout.get("execution_preflight_guard_closeout") or {}
+ execution_preflight_guard = handoff_closeout.get("execution_preflight_guard") or {}
+ package_closeout = handoff_closeout.get("no_write_invocation_package_closeout") or {}
+ package = handoff_closeout.get("no_write_invocation_package") or {}
+ invocation_closeout = handoff_closeout.get("invocation_receipt_closeout") or {}
+ receipt = handoff_closeout.get("dry_run_invocation_readiness_receipt") or {}
+ readiness_closeout = handoff_closeout.get("apply_executor_readiness_closeout") or {}
+ readiness_contract = handoff_closeout.get("apply_executor_readiness_contract") or {}
+ pre_apply_closeout = handoff_closeout.get("pre_apply_replay_closeout") or {}
+ replay_verifier = handoff_closeout.get("pre_apply_replay_verifier") or {}
+ final_guard = handoff_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ handoff_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ rollback_binding = handoff_closeout.get("rollback_binding") or {}
+ verifier_binding = handoff_closeout.get("post_apply_verifier_binding") or {}
+ closeout_id = (
+ _db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout_id(
+ proof_closeout_result
+ )
+ )
+ quarantine_id = f"{closeout_id}-controlled-executor-quarantine-proof"
+ proof_closeout_fields = [
+ "final_no_runner_execution_proof_closeout_id",
+ "source_no_execution_receipt_handoff_closeout_id",
+ "source_final_no_runner_execution_proof_id",
+ "source_no_execution_receipt_handoff_id",
+ "source_runner_invocation_boundary_closeout_id",
+ "controlled_executor_quarantine_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ proof_closeout_acceptance_gates = [
+ "no_execution_receipt_handoff_closeout_ready",
+ "source_chain_ids_match",
+ "final_no_runner_execution_proof_ready",
+ "final_no_runner_execution_proof_no_execute",
+ "controlled_executor_quarantine_proof_bound",
+ "controlled_executor_quarantine_proof_blocks_execution",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ controlled_executor_quarantine_proof_fields = [
+ "quarantine_proof_id",
+ "source_final_no_runner_execution_proof_closeout_id",
+ "source_final_no_runner_execution_proof_id",
+ "source_no_execution_receipt_handoff_closeout_id",
+ "source_no_execution_receipt_handoff_id",
+ "source_runner_invocation_boundary_closeout_id",
+ "quarantine_mode",
+ "dry_run_executor_invocation_allowed",
+ "runner_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_no_execution_receipt_handoff_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_final_no_runner_execution_proof_missing",
+ "abort_if_final_no_runner_execution_proof_reports_execution",
+ "abort_if_controlled_executor_quarantine_proof_missing",
+ "abort_if_controlled_executor_quarantine_proof_allows_execution",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ controlled_executor_quarantine_proof = {
+ "quarantine_proof_id": quarantine_id,
+ "source_final_no_runner_execution_proof_closeout_id": closeout_id,
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_id"
+ ),
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "source_execution_preflight_guard_closeout_id": guard_closeout.get(
+ "execution_preflight_guard_closeout_id"
+ ),
+ "required_proof_mode": "final_no_runner_execution_proof_preview_only",
+ "required_command_shape_hash": final_proof.get("required_command_shape_hash"),
+ "quarantine_status": "controlled_executor_quarantine_proof_preview_ready",
+ "quarantine_mode": "controlled_executor_quarantine_proof_preview_only",
+ "controlled_executor_quarantine_bound": True,
+ "executor_quarantine_enforced": True,
+ "execution_receipt_present": False,
+ "execution_receipt_required": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_controlled_executor_quarantine_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "reads_secret_in_preview": False,
+ "controlled_executor_quarantine_proof_field_count": len(
+ controlled_executor_quarantine_proof_fields
+ ),
+ "controlled_executor_quarantine_proof_fields": (
+ controlled_executor_quarantine_proof_fields
+ ),
+ }
+ handoff_closeout_ready = (
+ proof_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_NO_EXECUTION_RECEIPT_HANDOFF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(handoff_closeout.get("no_execution_receipt_handoff_closeout_id"))
+ and handoff_closeout.get("no_execution_receipt_handoff_closeout_id")
+ == future_proof.get("no_execution_receipt_handoff_closeout_id")
+ == final_proof.get("source_no_execution_receipt_handoff_closeout_id")
+ == controlled_executor_quarantine_proof.get(
+ "source_no_execution_receipt_handoff_closeout_id"
+ )
+ and final_proof.get("proof_id")
+ == future_proof.get("final_no_runner_execution_proof_id")
+ == controlled_executor_quarantine_proof.get(
+ "source_final_no_runner_execution_proof_id"
+ )
+ and handoff.get("handoff_id")
+ == future_proof.get("source_no_execution_receipt_handoff_id")
+ == final_proof.get("source_no_execution_receipt_handoff_id")
+ == controlled_executor_quarantine_proof.get(
+ "source_no_execution_receipt_handoff_id"
+ )
+ and boundary_closeout.get("runner_invocation_boundary_closeout_id")
+ == future_proof.get("source_runner_invocation_boundary_closeout_id")
+ == final_proof.get("source_runner_invocation_boundary_closeout_id")
+ == controlled_executor_quarantine_proof.get(
+ "source_runner_invocation_boundary_closeout_id"
+ )
+ and boundary.get("boundary_id")
+ == future_proof.get("source_runner_invocation_boundary_id")
+ == controlled_executor_quarantine_proof.get(
+ "source_runner_invocation_boundary_id"
+ )
+ )
+ final_no_runner_execution_proof_ready = (
+ final_proof.get("proof_status")
+ == "final_no_runner_execution_proof_preview_ready"
+ and final_proof.get("proof_id")
+ == future_proof.get("final_no_runner_execution_proof_id")
+ and int(final_proof.get("final_no_runner_execution_proof_field_count") or 0)
+ == 12
+ and summary.get("final_no_runner_execution_proof_count") == 1
+ )
+ final_no_runner_execution_proof_no_execute = (
+ final_proof.get("proof_mode")
+ == "final_no_runner_execution_proof_preview_only"
+ and final_proof.get("execution_receipt_present") is False
+ and final_proof.get("execution_receipt_required") is False
+ and final_proof.get("dry_run_executor_invoked") is False
+ and final_proof.get("runner_invocation_performed") is False
+ and final_proof.get("endpoint_executed") is False
+ and final_proof.get("sql_executed") is False
+ and final_proof.get("database_written") is False
+ and final_proof.get("dry_run_executor_invocation_allowed") is False
+ and final_proof.get("runner_invocation_allowed") is False
+ and final_proof.get("ready_for_final_no_runner_execution_proof_now")
+ is False
+ and final_proof.get("ready_for_dry_run_executor_invocation_now") is False
+ and final_proof.get("ready_for_actual_dry_run_execution_now") is False
+ and final_proof.get("endpoint_execution_allowed") is False
+ and final_proof.get("sql_execution_allowed") is False
+ and final_proof.get("database_write_allowed") is False
+ and final_proof.get("database_apply_authorized") is False
+ and final_proof.get("executes_database_apply") is False
+ and final_proof.get("executes_endpoint") is False
+ and final_proof.get("executes_sql") is False
+ and final_proof.get("writes_database") is False
+ and final_proof.get("captures_stdout") is False
+ and final_proof.get("captures_stderr") is False
+ and final_proof.get("stdout_included") is False
+ and final_proof.get("stderr_included") is False
+ )
+ controlled_executor_quarantine_proof_bound = (
+ bool(controlled_executor_quarantine_proof.get("quarantine_proof_id"))
+ and controlled_executor_quarantine_proof.get(
+ "source_final_no_runner_execution_proof_closeout_id"
+ )
+ == closeout_id
+ and controlled_executor_quarantine_proof.get(
+ "source_final_no_runner_execution_proof_id"
+ )
+ == final_proof.get("proof_id")
+ and controlled_executor_quarantine_proof.get("required_command_shape_hash")
+ == final_proof.get("required_command_shape_hash")
+ and int(
+ controlled_executor_quarantine_proof.get(
+ "controlled_executor_quarantine_proof_field_count"
+ )
+ or 0
+ )
+ == len(controlled_executor_quarantine_proof_fields)
+ )
+ controlled_executor_quarantine_proof_blocks_execution = (
+ controlled_executor_quarantine_proof.get("quarantine_mode")
+ == "controlled_executor_quarantine_proof_preview_only"
+ and controlled_executor_quarantine_proof.get(
+ "controlled_executor_quarantine_bound"
+ )
+ is True
+ and controlled_executor_quarantine_proof.get("executor_quarantine_enforced")
+ is True
+ and controlled_executor_quarantine_proof.get("execution_receipt_present")
+ is False
+ and controlled_executor_quarantine_proof.get("execution_receipt_required")
+ is False
+ and controlled_executor_quarantine_proof.get("dry_run_executor_invoked")
+ is False
+ and controlled_executor_quarantine_proof.get("runner_invocation_performed")
+ is False
+ and controlled_executor_quarantine_proof.get("endpoint_executed") is False
+ and controlled_executor_quarantine_proof.get("sql_executed") is False
+ and controlled_executor_quarantine_proof.get("database_written") is False
+ and controlled_executor_quarantine_proof.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ is False
+ and controlled_executor_quarantine_proof.get("runner_invocation_allowed")
+ is False
+ and controlled_executor_quarantine_proof.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and controlled_executor_quarantine_proof.get(
+ "ready_for_actual_dry_run_execution_now"
+ )
+ is False
+ and controlled_executor_quarantine_proof.get("endpoint_execution_allowed")
+ is False
+ and controlled_executor_quarantine_proof.get("sql_execution_allowed") is False
+ and controlled_executor_quarantine_proof.get("database_write_allowed") is False
+ and controlled_executor_quarantine_proof.get("database_apply_authorized")
+ is False
+ and controlled_executor_quarantine_proof.get("executes_database_apply")
+ is False
+ and controlled_executor_quarantine_proof.get("executes_endpoint") is False
+ and controlled_executor_quarantine_proof.get("executes_sql") is False
+ and controlled_executor_quarantine_proof.get("writes_database") is False
+ and controlled_executor_quarantine_proof.get("captures_stdout") is False
+ and controlled_executor_quarantine_proof.get("captures_stderr") is False
+ and controlled_executor_quarantine_proof.get("stdout_included") is False
+ and controlled_executor_quarantine_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ handoff_closeout.get("no_execution_receipt_handoff_closeout_only") is True
+ and handoff_closeout.get("final_no_runner_execution_proof_only") is True
+ and handoff_closeout.get("database_apply_authorized") is False
+ and boundary_closeout.get("runner_invocation_boundary_closeout_only") is True
+ and boundary_closeout.get("no_execution_receipt_handoff_only") is True
+ and guard_closeout.get("execution_preflight_guard_closeout_only") is True
+ and package_closeout.get("no_write_invocation_package_closeout_only")
+ is True
+ and package.get("package_mode") == "no_write_invocation_package_preview_only"
+ and invocation_closeout.get("invocation_receipt_closeout_only") is True
+ and receipt.get("receipt_mode") == "dry_run_invocation_readiness_preview_only"
+ and readiness_contract.get("readiness_mode")
+ == "apply_executor_readiness_contract_preview_only"
+ and replay_verifier.get("replay_mode") == "pre_apply_replay_preview_only"
+ and final_guard.get("guard_status")
+ == "final_dry_run_executor_guard_preview_ready"
+ and no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and final_no_runner_execution_proof_no_execute
+ )
+ target_hash_locked = (
+ handoff_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(handoff_closeout.get("expected_sha256"))
+ and bool(handoff_closeout.get("actual_sha256"))
+ and handoff_closeout.get("expected_sha256")
+ == handoff_closeout.get("actual_sha256")
+ and handoff_closeout.get("hash_matches") is True
+ and handoff_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ handoff_contract_blocks_database_apply = (
+ handoff_contract.get("executes_database_apply") is False
+ and handoff_contract.get("executes_endpoint") is False
+ and handoff_contract.get("executes_sql") is False
+ and handoff_contract.get("database_apply_authorized") is False
+ and handoff_contract.get("ready_for_database_apply_now") is False
+ and handoff_contract.get("ready_for_dry_run_executor_invocation_now")
+ is False
+ and handoff_contract.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and handoff_contract.get("signs_database_apply_authorization") is False
+ and handoff_contract.get("writes_database") is False
+ and handoff_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("dry_run_executor_invoked_count", 0) == 0
+ and summary.get("runner_invocation_performed_count", 0) == 0
+ and summary.get("endpoint_executed_count", 0) == 0
+ and summary.get("sql_executed_count", 0) == 0
+ and summary.get("database_written_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and final_no_runner_execution_proof_no_execute
+ and controlled_executor_quarantine_proof_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "no_execution_receipt_handoff_closeout_ready",
+ handoff_closeout_ready,
+ {
+ "result": proof_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_check_count"
+ ),
+ },
+ "wait_for_no_execution_receipt_handoff_closeout_ready",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "no_execution_receipt_handoff_closeout_id": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_id"
+ ),
+ "final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "quarantine_proof_id": controlled_executor_quarantine_proof.get(
+ "quarantine_proof_id"
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "final_no_runner_execution_proof_ready",
+ final_no_runner_execution_proof_ready,
+ {
+ "proof_id": final_proof.get("proof_id"),
+ "proof_status": final_proof.get("proof_status"),
+ "field_count": final_proof.get(
+ "final_no_runner_execution_proof_field_count"
+ ),
+ },
+ "wait_for_final_no_runner_execution_proof_ready",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "final_no_runner_execution_proof_no_execute",
+ final_no_runner_execution_proof_no_execute,
+ {
+ "proof_mode": final_proof.get("proof_mode"),
+ "dry_run_executor_invoked": final_proof.get(
+ "dry_run_executor_invoked"
+ ),
+ "runner_invocation_performed": final_proof.get(
+ "runner_invocation_performed"
+ ),
+ },
+ "abort_if_final_no_runner_execution_proof_reports_execution",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "controlled_executor_quarantine_proof_bound",
+ controlled_executor_quarantine_proof_bound,
+ {
+ "quarantine_proof_id": controlled_executor_quarantine_proof.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_id": (
+ controlled_executor_quarantine_proof.get(
+ "source_final_no_runner_execution_proof_id"
+ )
+ ),
+ "field_count": controlled_executor_quarantine_proof.get(
+ "controlled_executor_quarantine_proof_field_count"
+ ),
+ },
+ "wait_for_controlled_executor_quarantine_proof_binding",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "controlled_executor_quarantine_proof_blocks_execution",
+ controlled_executor_quarantine_proof_blocks_execution,
+ {
+ "quarantine_mode": controlled_executor_quarantine_proof.get(
+ "quarantine_mode"
+ ),
+ "dry_run_executor_invoked": controlled_executor_quarantine_proof.get(
+ "dry_run_executor_invoked"
+ ),
+ "runner_invocation_performed": controlled_executor_quarantine_proof.get(
+ "runner_invocation_performed"
+ ),
+ },
+ "abort_if_controlled_executor_quarantine_proof_allows_execution",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "no_execution_receipt_handoff_closeout_only": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_only"
+ ),
+ "final_no_runner_execution_proof_only": handoff_closeout.get(
+ "final_no_runner_execution_proof_only"
+ ),
+ "proof_mode": final_proof.get("proof_mode"),
+ },
+ "wait_for_previous_closeouts_carry_forward",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": handoff_closeout.get("target_file"),
+ "hash_matches": handoff_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(handoff_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(handoff_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "no_execution_receipt_handoff_closeout_contract_blocks_database_apply",
+ handoff_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_final_no_runner_execution_proof": (
+ handoff_contract.get(
+ "permits_future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ )
+ ),
+ "database_apply_authorized": handoff_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": handoff_contract.get("writes_database"),
+ },
+ "abort_if_no_execution_receipt_handoff_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "dry_run_executor_invoked_count": summary.get(
+ "dry_run_executor_invoked_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_final_no_runner_execution_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ handoff_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": handoff_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_FINAL_NO_RUNNER_EXECUTION_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_NO_EXECUTION_RECEIPT_HANDOFF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof = {
+ "final_no_runner_execution_proof_closeout_id": closeout_id,
+ "controlled_executor_quarantine_proof_id": quarantine_id,
+ "source_no_execution_receipt_handoff_closeout_id": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_id"
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout": (
+ closeout_ready
+ ),
+ "final_no_runner_execution_proof_closeout_ready": closeout_ready,
+ "controlled_executor_quarantine_proof_bound": closeout_ready,
+ "controlled_executor_quarantine_bound": True,
+ "executor_quarantine_enforced": True,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_final_no_runner_execution_proof_closeout = {
+ "final_no_runner_execution_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_final_no_runner_execution_proof_closeout"
+ ),
+ "source_no_execution_receipt_handoff_closeout_id": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_id"
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "source_execution_preflight_guard_closeout_id": guard_closeout.get(
+ "execution_preflight_guard_closeout_id"
+ ),
+ "required_command_shape_hash": final_proof.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_final_no_runner_execution_proof_closeout": (
+ closeout_ready
+ ),
+ "final_no_runner_execution_proof_closeout_fields": proof_closeout_fields,
+ "final_no_runner_execution_proof_closeout_field_count": len(
+ proof_closeout_fields
+ ),
+ "final_no_runner_execution_proof_closeout_acceptance_gates": (
+ proof_closeout_acceptance_gates
+ ),
+ "final_no_runner_execution_proof_closeout_acceptance_gate_count": len(
+ proof_closeout_acceptance_gates
+ ),
+ "controlled_executor_quarantine_proof": controlled_executor_quarantine_proof,
+ "controlled_executor_quarantine_proof_count": 1,
+ "controlled_executor_quarantine_proof_field_count": len(
+ controlled_executor_quarantine_proof_fields
+ ),
+ "final_no_runner_execution_proof": final_proof,
+ "final_no_runner_execution_proof_count": 1,
+ "no_execution_receipt_handoff_closeout": handoff_closeout,
+ "no_execution_receipt_handoff_closeout_count": 1,
+ "no_execution_receipt_handoff": handoff,
+ "no_execution_receipt_handoff_count": 1,
+ "runner_invocation_boundary_closeout": boundary_closeout,
+ "runner_invocation_boundary_closeout_count": 1,
+ "runner_invocation_boundary": boundary,
+ "runner_invocation_boundary_count": 1,
+ "execution_preflight_guard_closeout": guard_closeout,
+ "execution_preflight_guard_closeout_count": 1,
+ "execution_preflight_guard": execution_preflight_guard,
+ "execution_preflight_guard_count": 1,
+ "no_write_invocation_package_closeout": package_closeout,
+ "no_write_invocation_package_closeout_count": 1,
+ "no_write_invocation_package": package,
+ "no_write_invocation_package_count": 1,
+ "invocation_receipt_closeout": invocation_closeout,
+ "invocation_receipt_closeout_count": 1,
+ "dry_run_invocation_readiness_receipt": receipt,
+ "dry_run_invocation_readiness_receipt_count": 1,
+ "apply_executor_readiness_closeout": readiness_closeout,
+ "apply_executor_readiness_closeout_count": 1,
+ "apply_executor_readiness_contract": readiness_contract,
+ "apply_executor_readiness_contract_count": 1,
+ "pre_apply_replay_closeout": pre_apply_closeout,
+ "pre_apply_replay_closeout_count": 1,
+ "pre_apply_replay_verifier": replay_verifier,
+ "pre_apply_replay_verifier_count": 1,
+ "final_dry_run_executor_guard": final_guard,
+ "final_dry_run_executor_guard_count": 1,
+ "no_apply_enforcement_verification": no_apply_enforcement,
+ "no_apply_enforcement_verification_count": 1,
+ "target_file": handoff_closeout.get("target_file"),
+ "expected_sha256": handoff_closeout.get("expected_sha256"),
+ "actual_sha256": handoff_closeout.get("actual_sha256"),
+ "hash_matches": handoff_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "final_no_runner_execution_proof_closeout_only": True,
+ "controlled_executor_quarantine_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "controlled_executor_quarantine_bound": True,
+ "executor_quarantine_enforced": True,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_final_no_runner_execution_proof_closeout_contract = {
+ "mode": "controlled_dry_run_final_no_runner_execution_proof_closeout_and_controlled_executor_quarantine_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout"
+ ),
+ "source_no_execution_receipt_handoff_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_count": 1,
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_field_count": len(
+ proof_closeout_fields
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_acceptance_gate_count": len(
+ proof_closeout_acceptance_gates
+ ),
+ "controlled_executor_quarantine_proof_count": 1,
+ "controlled_executor_quarantine_proof_field_count": len(
+ controlled_executor_quarantine_proof_fields
+ ),
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_FINAL_NO_RUNNER_EXECUTION_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(proof_closeout_result.get("success")),
+ "generated_at": proof_closeout_result.get("generated_at"),
+ "source_policy": proof_closeout_result.get("policy"),
+ "stats": proof_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof": (
+ future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout": (
+ controlled_dry_run_final_no_runner_execution_proof_closeout
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract": (
+ controlled_dry_run_final_no_runner_execution_proof_closeout_contract
+ ),
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_checks": checks,
+ "source_controlled_dry_run_no_execution_receipt_handoff_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_no_execution_receipt_handoff_closeout_contract": (
+ handoff_contract
+ ),
+ "source_controlled_dry_run_no_execution_receipt_handoff_closeout": (
+ handoff_closeout
+ ),
+ "source_database_apply_controlled_dry_run_final_no_runner_execution_proof": (
+ future_proof
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future controlled executor quarantine proof closeout.",
+ "Keep actual dry-run executor invocation disabled; this proof confirms executor quarantine in this preview lane.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out executor quarantine and freeze the dry-run execution envelope."""
+ quarantine_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_quarantine = (
+ quarantine_closeout_result.get(
+ "future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ )
+ or {}
+ )
+ quarantine_closeout = (
+ quarantine_closeout_result.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout"
+ )
+ or {}
+ )
+ quarantine_contract = (
+ quarantine_closeout_result.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = quarantine_closeout_result.get("summary") or {}
+ safety = quarantine_closeout_result.get("safety") or {}
+ quarantine = quarantine_closeout.get("controlled_executor_quarantine_proof") or {}
+ final_proof = quarantine_closeout.get("final_no_runner_execution_proof") or {}
+ handoff_closeout = (
+ quarantine_closeout.get("no_execution_receipt_handoff_closeout") or {}
+ )
+ handoff = quarantine_closeout.get("no_execution_receipt_handoff") or {}
+ boundary_closeout = (
+ quarantine_closeout.get("runner_invocation_boundary_closeout") or {}
+ )
+ boundary = quarantine_closeout.get("runner_invocation_boundary") or {}
+ guard_closeout = (
+ quarantine_closeout.get("execution_preflight_guard_closeout") or {}
+ )
+ package_closeout = (
+ quarantine_closeout.get("no_write_invocation_package_closeout") or {}
+ )
+ invocation_closeout = quarantine_closeout.get("invocation_receipt_closeout") or {}
+ readiness_closeout = (
+ quarantine_closeout.get("apply_executor_readiness_closeout") or {}
+ )
+ pre_apply_closeout = quarantine_closeout.get("pre_apply_replay_closeout") or {}
+ final_guard = quarantine_closeout.get("final_dry_run_executor_guard") or {}
+ no_apply_enforcement = (
+ quarantine_closeout.get("no_apply_enforcement_verification") or {}
+ )
+ rollback_binding = quarantine_closeout.get("rollback_binding") or {}
+ verifier_binding = quarantine_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = quarantine_closeout.get(
+ "final_no_runner_execution_proof_closeout_id"
+ )
+ closeout_id = (
+ _db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout_id(
+ quarantine_closeout_result
+ )
+ )
+ freeze_id = f"{closeout_id}-dry-run-execution-envelope-freeze-proof"
+ quarantine_closeout_fields = [
+ "controlled_executor_quarantine_proof_closeout_id",
+ "source_final_no_runner_execution_proof_closeout_id",
+ "source_controlled_executor_quarantine_proof_id",
+ "source_final_no_runner_execution_proof_id",
+ "dry_run_execution_envelope_freeze_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "execution_envelope_frozen",
+ "dry_run_executor_invocation_allowed",
+ "abort_conditions",
+ ]
+ quarantine_closeout_acceptance_gates = [
+ "final_no_runner_execution_proof_closeout_ready",
+ "source_chain_ids_match",
+ "controlled_executor_quarantine_proof_ready",
+ "controlled_executor_quarantine_proof_no_execute",
+ "dry_run_execution_envelope_freeze_proof_bound",
+ "dry_run_execution_envelope_freeze_proof_blocks_execution",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ dry_run_execution_envelope_freeze_proof_fields = [
+ "freeze_proof_id",
+ "source_controlled_executor_quarantine_proof_closeout_id",
+ "source_controlled_executor_quarantine_proof_id",
+ "source_final_no_runner_execution_proof_closeout_id",
+ "source_final_no_runner_execution_proof_id",
+ "source_no_execution_receipt_handoff_closeout_id",
+ "freeze_mode",
+ "execution_envelope_frozen",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_final_no_runner_execution_proof_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_controlled_executor_quarantine_proof_missing",
+ "abort_if_controlled_executor_quarantine_proof_reports_execution",
+ "abort_if_dry_run_execution_envelope_freeze_proof_missing",
+ "abort_if_execution_envelope_is_mutable_or_allows_execution",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ dry_run_execution_envelope_freeze_proof = {
+ "freeze_proof_id": freeze_id,
+ "source_controlled_executor_quarantine_proof_closeout_id": closeout_id,
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_closeout_id": source_closeout_id,
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_id"
+ ),
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "required_command_shape_hash": quarantine.get("required_command_shape_hash"),
+ "freeze_status": "dry_run_execution_envelope_freeze_proof_preview_ready",
+ "freeze_mode": "dry_run_execution_envelope_freeze_proof_preview_only",
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "execution_receipt_required": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_controlled_executor_quarantine_closeout_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "dry_run_execution_envelope_freeze_proof_field_count": len(
+ dry_run_execution_envelope_freeze_proof_fields
+ ),
+ "dry_run_execution_envelope_freeze_proof_fields": (
+ dry_run_execution_envelope_freeze_proof_fields
+ ),
+ }
+ quarantine_closeout_ready = (
+ quarantine_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_FINAL_NO_RUNNER_EXECUTION_PROOF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(source_closeout_id)
+ and source_closeout_id
+ == future_quarantine.get("final_no_runner_execution_proof_closeout_id")
+ == quarantine.get("source_final_no_runner_execution_proof_closeout_id")
+ == dry_run_execution_envelope_freeze_proof.get(
+ "source_final_no_runner_execution_proof_closeout_id"
+ )
+ and quarantine.get("quarantine_proof_id")
+ == future_quarantine.get("controlled_executor_quarantine_proof_id")
+ == dry_run_execution_envelope_freeze_proof.get(
+ "source_controlled_executor_quarantine_proof_id"
+ )
+ and final_proof.get("proof_id")
+ == future_quarantine.get("source_final_no_runner_execution_proof_id")
+ == quarantine.get("source_final_no_runner_execution_proof_id")
+ == dry_run_execution_envelope_freeze_proof.get(
+ "source_final_no_runner_execution_proof_id"
+ )
+ and handoff_closeout.get("no_execution_receipt_handoff_closeout_id")
+ == future_quarantine.get("source_no_execution_receipt_handoff_closeout_id")
+ == quarantine.get("source_no_execution_receipt_handoff_closeout_id")
+ == dry_run_execution_envelope_freeze_proof.get(
+ "source_no_execution_receipt_handoff_closeout_id"
+ )
+ and handoff.get("handoff_id")
+ == future_quarantine.get("source_no_execution_receipt_handoff_id")
+ == quarantine.get("source_no_execution_receipt_handoff_id")
+ == dry_run_execution_envelope_freeze_proof.get(
+ "source_no_execution_receipt_handoff_id"
+ )
+ and boundary_closeout.get("runner_invocation_boundary_closeout_id")
+ == future_quarantine.get("source_runner_invocation_boundary_closeout_id")
+ == quarantine.get("source_runner_invocation_boundary_closeout_id")
+ == dry_run_execution_envelope_freeze_proof.get(
+ "source_runner_invocation_boundary_closeout_id"
+ )
+ )
+ controlled_executor_quarantine_proof_ready = (
+ quarantine_closeout_ready
+ and quarantine.get("quarantine_status")
+ == "controlled_executor_quarantine_proof_preview_ready"
+ and quarantine.get("quarantine_proof_id")
+ == future_quarantine.get("controlled_executor_quarantine_proof_id")
+ and int(quarantine.get("controlled_executor_quarantine_proof_field_count") or 0)
+ == 12
+ and summary.get("controlled_executor_quarantine_proof_count") == 1
+ )
+ controlled_executor_quarantine_proof_no_execute = (
+ quarantine.get("quarantine_mode")
+ == "controlled_executor_quarantine_proof_preview_only"
+ and quarantine.get("controlled_executor_quarantine_bound") is True
+ and quarantine.get("executor_quarantine_enforced") is True
+ and quarantine.get("execution_receipt_present") is False
+ and quarantine.get("execution_receipt_required") is False
+ and quarantine.get("dry_run_executor_invoked") is False
+ and quarantine.get("runner_invocation_performed") is False
+ and quarantine.get("endpoint_executed") is False
+ and quarantine.get("sql_executed") is False
+ and quarantine.get("database_written") is False
+ and quarantine.get("dry_run_executor_invocation_allowed") is False
+ and quarantine.get("runner_invocation_allowed") is False
+ and quarantine.get("ready_for_dry_run_executor_invocation_now") is False
+ and quarantine.get("ready_for_actual_dry_run_execution_now") is False
+ and quarantine.get("endpoint_execution_allowed") is False
+ and quarantine.get("sql_execution_allowed") is False
+ and quarantine.get("database_write_allowed") is False
+ and quarantine.get("database_apply_authorized") is False
+ and quarantine.get("executes_database_apply") is False
+ and quarantine.get("executes_endpoint") is False
+ and quarantine.get("executes_sql") is False
+ and quarantine.get("writes_database") is False
+ and quarantine.get("captures_stdout") is False
+ and quarantine.get("captures_stderr") is False
+ and quarantine.get("stdout_included") is False
+ and quarantine.get("stderr_included") is False
+ )
+ dry_run_execution_envelope_freeze_proof_bound = (
+ controlled_executor_quarantine_proof_ready
+ and bool(dry_run_execution_envelope_freeze_proof.get("freeze_proof_id"))
+ and dry_run_execution_envelope_freeze_proof.get(
+ "source_controlled_executor_quarantine_proof_closeout_id"
+ )
+ == closeout_id
+ and dry_run_execution_envelope_freeze_proof.get(
+ "source_controlled_executor_quarantine_proof_id"
+ )
+ == quarantine.get("quarantine_proof_id")
+ and dry_run_execution_envelope_freeze_proof.get("required_command_shape_hash")
+ == quarantine.get("required_command_shape_hash")
+ and int(
+ dry_run_execution_envelope_freeze_proof.get(
+ "dry_run_execution_envelope_freeze_proof_field_count"
+ )
+ or 0
+ )
+ == len(dry_run_execution_envelope_freeze_proof_fields)
+ )
+ dry_run_execution_envelope_freeze_proof_blocks_execution = (
+ dry_run_execution_envelope_freeze_proof.get("freeze_mode")
+ == "dry_run_execution_envelope_freeze_proof_preview_only"
+ and dry_run_execution_envelope_freeze_proof.get("execution_envelope_frozen")
+ is True
+ and dry_run_execution_envelope_freeze_proof.get(
+ "execution_envelope_mutation_allowed"
+ )
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("dry_run_executor_invoked")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("runner_invocation_performed")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("endpoint_executed") is False
+ and dry_run_execution_envelope_freeze_proof.get("sql_executed") is False
+ and dry_run_execution_envelope_freeze_proof.get("database_written") is False
+ and dry_run_execution_envelope_freeze_proof.get("execution_receipt_present")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("execution_receipt_required")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("runner_invocation_allowed")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and dry_run_execution_envelope_freeze_proof.get(
+ "ready_for_actual_dry_run_execution_now"
+ )
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("endpoint_execution_allowed")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("sql_execution_allowed")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("database_write_allowed")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("database_apply_authorized")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("executes_database_apply")
+ is False
+ and dry_run_execution_envelope_freeze_proof.get("executes_endpoint") is False
+ and dry_run_execution_envelope_freeze_proof.get("executes_sql") is False
+ and dry_run_execution_envelope_freeze_proof.get("writes_database") is False
+ and dry_run_execution_envelope_freeze_proof.get("captures_stdout") is False
+ and dry_run_execution_envelope_freeze_proof.get("captures_stderr") is False
+ and dry_run_execution_envelope_freeze_proof.get("stdout_included") is False
+ and dry_run_execution_envelope_freeze_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ quarantine_closeout.get("final_no_runner_execution_proof_closeout_only")
+ is True
+ and quarantine_closeout.get("controlled_executor_quarantine_proof_only")
+ is True
+ and quarantine_closeout.get("database_apply_authorized") is False
+ and handoff_closeout.get("no_execution_receipt_handoff_closeout_only") is True
+ and boundary_closeout.get("runner_invocation_boundary_closeout_only") is True
+ and guard_closeout.get("execution_preflight_guard_closeout_only") is True
+ and package_closeout.get("no_write_invocation_package_closeout_only")
+ is True
+ and invocation_closeout.get("invocation_receipt_closeout_only") is True
+ and readiness_closeout.get("apply_executor_readiness_closeout_only") is True
+ and pre_apply_closeout.get("pre_apply_replay_closeout_only") is True
+ and final_guard.get("guard_status")
+ == "final_dry_run_executor_guard_preview_ready"
+ and no_apply_enforcement.get("enforcement_status")
+ == "no_apply_enforcement_preview_ready"
+ and controlled_executor_quarantine_proof_no_execute
+ )
+ target_hash_locked = (
+ quarantine_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(quarantine_closeout.get("expected_sha256"))
+ and bool(quarantine_closeout.get("actual_sha256"))
+ and quarantine_closeout.get("expected_sha256")
+ == quarantine_closeout.get("actual_sha256")
+ and quarantine_closeout.get("hash_matches") is True
+ and quarantine_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ quarantine_contract_blocks_database_apply = (
+ quarantine_contract.get("executes_database_apply") is False
+ and quarantine_contract.get("executes_endpoint") is False
+ and quarantine_contract.get("executes_sql") is False
+ and quarantine_contract.get("database_apply_authorized") is False
+ and quarantine_contract.get("ready_for_database_apply_now") is False
+ and quarantine_contract.get("ready_for_dry_run_executor_invocation_now")
+ is False
+ and quarantine_contract.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and quarantine_contract.get("signs_database_apply_authorization") is False
+ and quarantine_contract.get("writes_database") is False
+ and quarantine_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("dry_run_executor_invoked_count", 0) == 0
+ and summary.get("runner_invocation_performed_count", 0) == 0
+ and summary.get("endpoint_executed_count", 0) == 0
+ and summary.get("sql_executed_count", 0) == 0
+ and summary.get("database_written_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and controlled_executor_quarantine_proof_no_execute
+ and dry_run_execution_envelope_freeze_proof_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "final_no_runner_execution_proof_closeout_ready",
+ quarantine_closeout_ready,
+ {
+ "result": quarantine_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_check_count"
+ ),
+ },
+ "wait_for_final_no_runner_execution_proof_closeout_ready",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "final_no_runner_execution_proof_closeout_id": source_closeout_id,
+ "controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "freeze_proof_id": dry_run_execution_envelope_freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "controlled_executor_quarantine_proof_ready",
+ controlled_executor_quarantine_proof_ready,
+ {
+ "quarantine_proof_id": quarantine.get("quarantine_proof_id"),
+ "quarantine_status": quarantine.get("quarantine_status"),
+ "field_count": quarantine.get(
+ "controlled_executor_quarantine_proof_field_count"
+ ),
+ },
+ "wait_for_controlled_executor_quarantine_proof_ready",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "controlled_executor_quarantine_proof_no_execute",
+ controlled_executor_quarantine_proof_no_execute,
+ {
+ "quarantine_mode": quarantine.get("quarantine_mode"),
+ "dry_run_executor_invoked": quarantine.get("dry_run_executor_invoked"),
+ "runner_invocation_performed": quarantine.get(
+ "runner_invocation_performed"
+ ),
+ },
+ "abort_if_controlled_executor_quarantine_proof_reports_execution",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "dry_run_execution_envelope_freeze_proof_bound",
+ dry_run_execution_envelope_freeze_proof_bound,
+ {
+ "freeze_proof_id": dry_run_execution_envelope_freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ "source_controlled_executor_quarantine_proof_id": (
+ dry_run_execution_envelope_freeze_proof.get(
+ "source_controlled_executor_quarantine_proof_id"
+ )
+ ),
+ "field_count": dry_run_execution_envelope_freeze_proof.get(
+ "dry_run_execution_envelope_freeze_proof_field_count"
+ ),
+ },
+ "wait_for_dry_run_execution_envelope_freeze_proof_binding",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "dry_run_execution_envelope_freeze_proof_blocks_execution",
+ dry_run_execution_envelope_freeze_proof_blocks_execution,
+ {
+ "freeze_mode": dry_run_execution_envelope_freeze_proof.get(
+ "freeze_mode"
+ ),
+ "execution_envelope_frozen": (
+ dry_run_execution_envelope_freeze_proof.get(
+ "execution_envelope_frozen"
+ )
+ ),
+ "execution_envelope_mutation_allowed": (
+ dry_run_execution_envelope_freeze_proof.get(
+ "execution_envelope_mutation_allowed"
+ )
+ ),
+ },
+ "abort_if_execution_envelope_allows_execution",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "final_no_runner_execution_proof_closeout_only": (
+ quarantine_closeout.get(
+ "final_no_runner_execution_proof_closeout_only"
+ )
+ ),
+ "controlled_executor_quarantine_proof_only": (
+ quarantine_closeout.get("controlled_executor_quarantine_proof_only")
+ ),
+ "quarantine_mode": quarantine.get("quarantine_mode"),
+ },
+ "wait_for_previous_closeouts_carry_forward",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": quarantine_closeout.get("target_file"),
+ "hash_matches": quarantine_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ quarantine_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(
+ quarantine_closeout.get("actual_sha256")
+ ),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "final_no_runner_execution_proof_closeout_contract_blocks_database_apply",
+ quarantine_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof": (
+ quarantine_contract.get(
+ "permits_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ )
+ ),
+ "database_apply_authorized": quarantine_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": quarantine_contract.get("writes_database"),
+ },
+ "abort_if_final_no_runner_execution_proof_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "dry_run_executor_invoked_count": summary.get(
+ "dry_run_executor_invoked_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_controlled_executor_quarantine_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ quarantine_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": quarantine_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_CONTROLLED_EXECUTOR_QUARANTINE_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_FINAL_NO_RUNNER_EXECUTION_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_execution_envelope_freeze_proof = {
+ "controlled_executor_quarantine_proof_closeout_id": closeout_id,
+ "dry_run_execution_envelope_freeze_proof_id": freeze_id,
+ "source_final_no_runner_execution_proof_closeout_id": source_closeout_id,
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout": (
+ closeout_ready
+ ),
+ "controlled_executor_quarantine_proof_closeout_ready": closeout_ready,
+ "dry_run_execution_envelope_freeze_proof_bound": closeout_ready,
+ "controlled_executor_quarantine_bound": True,
+ "executor_quarantine_enforced": True,
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_controlled_executor_quarantine_proof_closeout = {
+ "controlled_executor_quarantine_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ ),
+ "source_final_no_runner_execution_proof_closeout_id": source_closeout_id,
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_id"
+ ),
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "source_runner_invocation_boundary_id": boundary.get("boundary_id"),
+ "required_command_shape_hash": quarantine.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout": (
+ closeout_ready
+ ),
+ "controlled_executor_quarantine_proof_closeout_fields": (
+ quarantine_closeout_fields
+ ),
+ "controlled_executor_quarantine_proof_closeout_field_count": len(
+ quarantine_closeout_fields
+ ),
+ "controlled_executor_quarantine_proof_closeout_acceptance_gates": (
+ quarantine_closeout_acceptance_gates
+ ),
+ "controlled_executor_quarantine_proof_closeout_acceptance_gate_count": len(
+ quarantine_closeout_acceptance_gates
+ ),
+ "dry_run_execution_envelope_freeze_proof": (
+ dry_run_execution_envelope_freeze_proof
+ ),
+ "dry_run_execution_envelope_freeze_proof_count": 1,
+ "dry_run_execution_envelope_freeze_proof_field_count": len(
+ dry_run_execution_envelope_freeze_proof_fields
+ ),
+ "controlled_executor_quarantine_proof": quarantine,
+ "controlled_executor_quarantine_proof_count": 1,
+ "final_no_runner_execution_proof_closeout": quarantine_closeout,
+ "final_no_runner_execution_proof_closeout_count": 1,
+ "final_no_runner_execution_proof": final_proof,
+ "final_no_runner_execution_proof_count": 1,
+ "no_execution_receipt_handoff_closeout": handoff_closeout,
+ "no_execution_receipt_handoff_closeout_count": 1,
+ "no_execution_receipt_handoff": handoff,
+ "no_execution_receipt_handoff_count": 1,
+ "runner_invocation_boundary_closeout": boundary_closeout,
+ "runner_invocation_boundary_closeout_count": 1,
+ "runner_invocation_boundary": boundary,
+ "runner_invocation_boundary_count": 1,
+ "target_file": quarantine_closeout.get("target_file"),
+ "expected_sha256": quarantine_closeout.get("expected_sha256"),
+ "actual_sha256": quarantine_closeout.get("actual_sha256"),
+ "hash_matches": quarantine_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "controlled_executor_quarantine_proof_closeout_only": True,
+ "dry_run_execution_envelope_freeze_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "controlled_executor_quarantine_bound": True,
+ "executor_quarantine_enforced": True,
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract = {
+ "mode": "controlled_dry_run_controlled_executor_quarantine_proof_closeout_and_execution_envelope_freeze_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout"
+ ),
+ "source_final_no_runner_execution_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_count": 1,
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_field_count": len(
+ quarantine_closeout_fields
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_acceptance_gate_count": len(
+ quarantine_closeout_acceptance_gates
+ ),
+ "dry_run_execution_envelope_freeze_proof_count": 1,
+ "dry_run_execution_envelope_freeze_proof_field_count": len(
+ dry_run_execution_envelope_freeze_proof_fields
+ ),
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_CONTROLLED_EXECUTOR_QUARANTINE_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(quarantine_closeout_result.get("success")),
+ "generated_at": quarantine_closeout_result.get("generated_at"),
+ "source_policy": quarantine_closeout_result.get("policy"),
+ "stats": quarantine_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_execution_envelope_freeze_proof": (
+ future_database_apply_controlled_dry_run_execution_envelope_freeze_proof
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout": (
+ controlled_dry_run_controlled_executor_quarantine_proof_closeout
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract": (
+ controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract
+ ),
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_checks": (
+ checks
+ ),
+ "source_controlled_dry_run_final_no_runner_execution_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_final_no_runner_execution_proof_closeout_contract": (
+ quarantine_contract
+ ),
+ "source_controlled_dry_run_final_no_runner_execution_proof_closeout": (
+ quarantine_closeout
+ ),
+ "source_database_apply_controlled_dry_run_controlled_executor_quarantine_proof": (
+ future_quarantine
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future execution envelope freeze proof closeout.",
+ "Keep actual dry-run executor invocation disabled; this proof freezes the preview envelope before any executor lane can run.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the frozen execution envelope and hand it to verifier preview."""
+ freeze_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_freeze = (
+ freeze_closeout_result.get(
+ "future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ )
+ or {}
+ )
+ freeze_closeout = (
+ freeze_closeout_result.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ )
+ or {}
+ )
+ freeze_contract = (
+ freeze_closeout_result.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = freeze_closeout_result.get("summary") or {}
+ safety = freeze_closeout_result.get("safety") or {}
+ freeze_proof = freeze_closeout.get("dry_run_execution_envelope_freeze_proof") or {}
+ quarantine = freeze_closeout.get("controlled_executor_quarantine_proof") or {}
+ source_quarantine_closeout = (
+ freeze_closeout.get("final_no_runner_execution_proof_closeout") or {}
+ )
+ final_proof = freeze_closeout.get("final_no_runner_execution_proof") or {}
+ handoff_closeout = (
+ freeze_closeout.get("no_execution_receipt_handoff_closeout") or {}
+ )
+ handoff = freeze_closeout.get("no_execution_receipt_handoff") or {}
+ boundary_closeout = (
+ freeze_closeout.get("runner_invocation_boundary_closeout") or {}
+ )
+ rollback_binding = freeze_closeout.get("rollback_binding") or {}
+ verifier_binding = freeze_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = freeze_closeout.get(
+ "controlled_executor_quarantine_proof_closeout_id"
+ )
+ closeout_id = (
+ _db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout_id(
+ freeze_closeout_result
+ )
+ )
+ handoff_id = f"{closeout_id}-frozen-envelope-verifier-handoff"
+ freeze_closeout_fields = [
+ "execution_envelope_freeze_proof_closeout_id",
+ "source_controlled_executor_quarantine_proof_closeout_id",
+ "source_dry_run_execution_envelope_freeze_proof_id",
+ "source_controlled_executor_quarantine_proof_id",
+ "frozen_envelope_verifier_handoff_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "execution_envelope_frozen",
+ "verifier_invocation_allowed",
+ "abort_conditions",
+ ]
+ freeze_closeout_acceptance_gates = [
+ "controlled_executor_quarantine_proof_closeout_ready",
+ "source_chain_ids_match",
+ "dry_run_execution_envelope_freeze_proof_ready",
+ "dry_run_execution_envelope_freeze_proof_no_execute",
+ "frozen_envelope_verifier_handoff_bound",
+ "frozen_envelope_verifier_handoff_blocks_execution",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ frozen_envelope_verifier_handoff_fields = [
+ "handoff_id",
+ "source_execution_envelope_freeze_proof_closeout_id",
+ "source_dry_run_execution_envelope_freeze_proof_id",
+ "source_controlled_executor_quarantine_proof_closeout_id",
+ "source_controlled_executor_quarantine_proof_id",
+ "verifier_handoff_mode",
+ "execution_envelope_frozen",
+ "verifier_invocation_allowed",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_controlled_executor_quarantine_proof_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_dry_run_execution_envelope_freeze_proof_missing",
+ "abort_if_dry_run_execution_envelope_freeze_proof_reports_execution",
+ "abort_if_frozen_envelope_verifier_handoff_missing",
+ "abort_if_verifier_handoff_allows_execution",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ frozen_envelope_verifier_handoff = {
+ "handoff_id": handoff_id,
+ "source_execution_envelope_freeze_proof_closeout_id": closeout_id,
+ "source_dry_run_execution_envelope_freeze_proof_id": freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ "source_controlled_executor_quarantine_proof_closeout_id": source_closeout_id,
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_closeout_id": (
+ source_quarantine_closeout.get("final_no_runner_execution_proof_closeout_id")
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_id"
+ ),
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "required_command_shape_hash": freeze_proof.get("required_command_shape_hash"),
+ "handoff_status": "frozen_envelope_verifier_handoff_preview_ready",
+ "verifier_handoff_mode": "frozen_envelope_verifier_handoff_preview_only",
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "verifier_handoff_bound": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "verifier_receipt_required": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "execution_receipt_required": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_frozen_envelope_verifier_handoff_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "frozen_envelope_verifier_handoff_field_count": len(
+ frozen_envelope_verifier_handoff_fields
+ ),
+ "frozen_envelope_verifier_handoff_fields": (
+ frozen_envelope_verifier_handoff_fields
+ ),
+ }
+ freeze_closeout_ready = (
+ freeze_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_CONTROLLED_EXECUTOR_QUARANTINE_PROOF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(source_closeout_id)
+ and source_closeout_id
+ == future_freeze.get("controlled_executor_quarantine_proof_closeout_id")
+ == freeze_proof.get("source_controlled_executor_quarantine_proof_closeout_id")
+ == frozen_envelope_verifier_handoff.get(
+ "source_controlled_executor_quarantine_proof_closeout_id"
+ )
+ and freeze_proof.get("freeze_proof_id")
+ == future_freeze.get("dry_run_execution_envelope_freeze_proof_id")
+ == frozen_envelope_verifier_handoff.get(
+ "source_dry_run_execution_envelope_freeze_proof_id"
+ )
+ and quarantine.get("quarantine_proof_id")
+ == future_freeze.get("source_controlled_executor_quarantine_proof_id")
+ == freeze_proof.get("source_controlled_executor_quarantine_proof_id")
+ == frozen_envelope_verifier_handoff.get(
+ "source_controlled_executor_quarantine_proof_id"
+ )
+ and final_proof.get("proof_id")
+ == future_freeze.get("source_final_no_runner_execution_proof_id")
+ == freeze_proof.get("source_final_no_runner_execution_proof_id")
+ == frozen_envelope_verifier_handoff.get(
+ "source_final_no_runner_execution_proof_id"
+ )
+ and handoff_closeout.get("no_execution_receipt_handoff_closeout_id")
+ == future_freeze.get("source_no_execution_receipt_handoff_closeout_id")
+ == freeze_proof.get("source_no_execution_receipt_handoff_closeout_id")
+ == frozen_envelope_verifier_handoff.get(
+ "source_no_execution_receipt_handoff_closeout_id"
+ )
+ )
+ dry_run_execution_envelope_freeze_proof_ready = (
+ freeze_closeout_ready
+ and freeze_proof.get("freeze_status")
+ == "dry_run_execution_envelope_freeze_proof_preview_ready"
+ and freeze_proof.get("freeze_proof_id")
+ == future_freeze.get("dry_run_execution_envelope_freeze_proof_id")
+ and int(freeze_proof.get("dry_run_execution_envelope_freeze_proof_field_count") or 0)
+ == 12
+ and summary.get("dry_run_execution_envelope_freeze_proof_count") == 1
+ )
+ dry_run_execution_envelope_freeze_proof_no_execute = (
+ freeze_proof.get("freeze_mode")
+ == "dry_run_execution_envelope_freeze_proof_preview_only"
+ and freeze_proof.get("execution_envelope_frozen") is True
+ and freeze_proof.get("execution_envelope_mutation_allowed") is False
+ and freeze_proof.get("dry_run_executor_invoked") is False
+ and freeze_proof.get("runner_invocation_performed") is False
+ and freeze_proof.get("endpoint_executed") is False
+ and freeze_proof.get("sql_executed") is False
+ and freeze_proof.get("database_written") is False
+ and freeze_proof.get("execution_receipt_present") is False
+ and freeze_proof.get("execution_receipt_required") is False
+ and freeze_proof.get("dry_run_executor_invocation_allowed") is False
+ and freeze_proof.get("runner_invocation_allowed") is False
+ and freeze_proof.get("ready_for_dry_run_executor_invocation_now") is False
+ and freeze_proof.get("ready_for_actual_dry_run_execution_now") is False
+ and freeze_proof.get("endpoint_execution_allowed") is False
+ and freeze_proof.get("sql_execution_allowed") is False
+ and freeze_proof.get("database_write_allowed") is False
+ and freeze_proof.get("database_apply_authorized") is False
+ and freeze_proof.get("executes_database_apply") is False
+ and freeze_proof.get("executes_endpoint") is False
+ and freeze_proof.get("executes_sql") is False
+ and freeze_proof.get("writes_database") is False
+ and freeze_proof.get("captures_stdout") is False
+ and freeze_proof.get("captures_stderr") is False
+ and freeze_proof.get("stdout_included") is False
+ and freeze_proof.get("stderr_included") is False
+ )
+ frozen_envelope_verifier_handoff_bound = (
+ dry_run_execution_envelope_freeze_proof_ready
+ and bool(frozen_envelope_verifier_handoff.get("handoff_id"))
+ and frozen_envelope_verifier_handoff.get(
+ "source_execution_envelope_freeze_proof_closeout_id"
+ )
+ == closeout_id
+ and frozen_envelope_verifier_handoff.get(
+ "source_dry_run_execution_envelope_freeze_proof_id"
+ )
+ == freeze_proof.get("freeze_proof_id")
+ and frozen_envelope_verifier_handoff.get("required_command_shape_hash")
+ == freeze_proof.get("required_command_shape_hash")
+ and int(
+ frozen_envelope_verifier_handoff.get(
+ "frozen_envelope_verifier_handoff_field_count"
+ )
+ or 0
+ )
+ == len(frozen_envelope_verifier_handoff_fields)
+ )
+ frozen_envelope_verifier_handoff_blocks_execution = (
+ frozen_envelope_verifier_handoff.get("verifier_handoff_mode")
+ == "frozen_envelope_verifier_handoff_preview_only"
+ and frozen_envelope_verifier_handoff.get("execution_envelope_frozen") is True
+ and frozen_envelope_verifier_handoff.get(
+ "execution_envelope_mutation_allowed"
+ )
+ is False
+ and frozen_envelope_verifier_handoff.get("verifier_handoff_bound") is True
+ and frozen_envelope_verifier_handoff.get("verifier_invocation_allowed")
+ is False
+ and frozen_envelope_verifier_handoff.get("verifier_invoked") is False
+ and frozen_envelope_verifier_handoff.get("verifier_receipt_present") is False
+ and frozen_envelope_verifier_handoff.get("verifier_receipt_required") is False
+ and frozen_envelope_verifier_handoff.get("dry_run_executor_invoked") is False
+ and frozen_envelope_verifier_handoff.get("runner_invocation_performed")
+ is False
+ and frozen_envelope_verifier_handoff.get("endpoint_executed") is False
+ and frozen_envelope_verifier_handoff.get("sql_executed") is False
+ and frozen_envelope_verifier_handoff.get("database_written") is False
+ and frozen_envelope_verifier_handoff.get("execution_receipt_present") is False
+ and frozen_envelope_verifier_handoff.get("execution_receipt_required") is False
+ and frozen_envelope_verifier_handoff.get("dry_run_executor_invocation_allowed")
+ is False
+ and frozen_envelope_verifier_handoff.get("runner_invocation_allowed") is False
+ and frozen_envelope_verifier_handoff.get("ready_for_verifier_invocation_now")
+ is False
+ and frozen_envelope_verifier_handoff.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and frozen_envelope_verifier_handoff.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and frozen_envelope_verifier_handoff.get("endpoint_execution_allowed") is False
+ and frozen_envelope_verifier_handoff.get("sql_execution_allowed") is False
+ and frozen_envelope_verifier_handoff.get("database_write_allowed") is False
+ and frozen_envelope_verifier_handoff.get("database_apply_authorized") is False
+ and frozen_envelope_verifier_handoff.get("executes_database_apply") is False
+ and frozen_envelope_verifier_handoff.get("executes_endpoint") is False
+ and frozen_envelope_verifier_handoff.get("executes_sql") is False
+ and frozen_envelope_verifier_handoff.get("writes_database") is False
+ and frozen_envelope_verifier_handoff.get("captures_stdout") is False
+ and frozen_envelope_verifier_handoff.get("captures_stderr") is False
+ and frozen_envelope_verifier_handoff.get("stdout_included") is False
+ and frozen_envelope_verifier_handoff.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ freeze_closeout.get("controlled_executor_quarantine_proof_closeout_only")
+ is True
+ and freeze_closeout.get("dry_run_execution_envelope_freeze_proof_only")
+ is True
+ and freeze_closeout.get("database_apply_authorized") is False
+ and source_quarantine_closeout.get("final_no_runner_execution_proof_closeout_only")
+ is True
+ and source_quarantine_closeout.get("controlled_executor_quarantine_proof_only")
+ is True
+ and handoff_closeout.get("no_execution_receipt_handoff_closeout_only") is True
+ and boundary_closeout.get("runner_invocation_boundary_closeout_only") is True
+ and dry_run_execution_envelope_freeze_proof_no_execute
+ )
+ target_hash_locked = (
+ freeze_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(freeze_closeout.get("expected_sha256"))
+ and bool(freeze_closeout.get("actual_sha256"))
+ and freeze_closeout.get("expected_sha256")
+ == freeze_closeout.get("actual_sha256")
+ and freeze_closeout.get("hash_matches") is True
+ and freeze_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ freeze_contract_blocks_database_apply = (
+ freeze_contract.get("executes_database_apply") is False
+ and freeze_contract.get("executes_endpoint") is False
+ and freeze_contract.get("executes_sql") is False
+ and freeze_contract.get("database_apply_authorized") is False
+ and freeze_contract.get("ready_for_database_apply_now") is False
+ and freeze_contract.get("ready_for_dry_run_executor_invocation_now") is False
+ and freeze_contract.get("ready_for_actual_dry_run_execution_now") is False
+ and freeze_contract.get("signs_database_apply_authorization") is False
+ and freeze_contract.get("writes_database") is False
+ and freeze_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("dry_run_executor_invoked_count", 0) == 0
+ and summary.get("runner_invocation_performed_count", 0) == 0
+ and summary.get("endpoint_executed_count", 0) == 0
+ and summary.get("sql_executed_count", 0) == 0
+ and summary.get("database_written_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and dry_run_execution_envelope_freeze_proof_no_execute
+ and frozen_envelope_verifier_handoff_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "controlled_executor_quarantine_proof_closeout_ready",
+ freeze_closeout_ready,
+ {
+ "result": freeze_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_check_count"
+ ),
+ },
+ "wait_for_controlled_executor_quarantine_proof_closeout_ready",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "controlled_executor_quarantine_proof_closeout_id": source_closeout_id,
+ "freeze_proof_id": freeze_proof.get("freeze_proof_id"),
+ "handoff_id": frozen_envelope_verifier_handoff.get("handoff_id"),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "dry_run_execution_envelope_freeze_proof_ready",
+ dry_run_execution_envelope_freeze_proof_ready,
+ {
+ "freeze_proof_id": freeze_proof.get("freeze_proof_id"),
+ "freeze_status": freeze_proof.get("freeze_status"),
+ "field_count": freeze_proof.get(
+ "dry_run_execution_envelope_freeze_proof_field_count"
+ ),
+ },
+ "wait_for_dry_run_execution_envelope_freeze_proof_ready",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "dry_run_execution_envelope_freeze_proof_no_execute",
+ dry_run_execution_envelope_freeze_proof_no_execute,
+ {
+ "freeze_mode": freeze_proof.get("freeze_mode"),
+ "execution_envelope_frozen": freeze_proof.get(
+ "execution_envelope_frozen"
+ ),
+ "execution_envelope_mutation_allowed": freeze_proof.get(
+ "execution_envelope_mutation_allowed"
+ ),
+ },
+ "abort_if_dry_run_execution_envelope_freeze_proof_reports_execution",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "frozen_envelope_verifier_handoff_bound",
+ frozen_envelope_verifier_handoff_bound,
+ {
+ "handoff_id": frozen_envelope_verifier_handoff.get("handoff_id"),
+ "source_dry_run_execution_envelope_freeze_proof_id": (
+ frozen_envelope_verifier_handoff.get(
+ "source_dry_run_execution_envelope_freeze_proof_id"
+ )
+ ),
+ "field_count": frozen_envelope_verifier_handoff.get(
+ "frozen_envelope_verifier_handoff_field_count"
+ ),
+ },
+ "wait_for_frozen_envelope_verifier_handoff_binding",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "frozen_envelope_verifier_handoff_blocks_execution",
+ frozen_envelope_verifier_handoff_blocks_execution,
+ {
+ "verifier_handoff_mode": frozen_envelope_verifier_handoff.get(
+ "verifier_handoff_mode"
+ ),
+ "verifier_invocation_allowed": frozen_envelope_verifier_handoff.get(
+ "verifier_invocation_allowed"
+ ),
+ "verifier_invoked": frozen_envelope_verifier_handoff.get(
+ "verifier_invoked"
+ ),
+ },
+ "abort_if_frozen_envelope_verifier_handoff_allows_execution",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "controlled_executor_quarantine_proof_closeout_only": (
+ freeze_closeout.get(
+ "controlled_executor_quarantine_proof_closeout_only"
+ )
+ ),
+ "dry_run_execution_envelope_freeze_proof_only": (
+ freeze_closeout.get("dry_run_execution_envelope_freeze_proof_only")
+ ),
+ "freeze_mode": freeze_proof.get("freeze_mode"),
+ },
+ "wait_for_previous_closeouts_carry_forward",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": freeze_closeout.get("target_file"),
+ "hash_matches": freeze_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(freeze_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(freeze_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "controlled_executor_quarantine_proof_closeout_contract_blocks_database_apply",
+ freeze_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof": (
+ freeze_contract.get(
+ "permits_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ )
+ ),
+ "database_apply_authorized": freeze_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": freeze_contract.get("writes_database"),
+ },
+ "abort_if_controlled_executor_quarantine_proof_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ "dry_run_executor_invoked_count": summary.get(
+ "dry_run_executor_invoked_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_execution_envelope_freeze_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ freeze_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": freeze_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_ENVELOPE_FREEZE_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_CONTROLLED_EXECUTOR_QUARANTINE_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff = {
+ "execution_envelope_freeze_proof_closeout_id": closeout_id,
+ "frozen_envelope_verifier_handoff_id": handoff_id,
+ "source_controlled_executor_quarantine_proof_closeout_id": source_closeout_id,
+ "source_dry_run_execution_envelope_freeze_proof_id": freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout": (
+ closeout_ready
+ ),
+ "execution_envelope_freeze_proof_closeout_ready": closeout_ready,
+ "frozen_envelope_verifier_handoff_bound": closeout_ready,
+ "controlled_executor_quarantine_bound": True,
+ "executor_quarantine_enforced": True,
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "dry_run_execution_performed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "reads_secret_in_preview": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_execution_envelope_freeze_proof_closeout = {
+ "execution_envelope_freeze_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ ),
+ "source_controlled_executor_quarantine_proof_closeout_id": source_closeout_id,
+ "source_dry_run_execution_envelope_freeze_proof_id": freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_closeout_id": (
+ source_quarantine_closeout.get("final_no_runner_execution_proof_closeout_id")
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": handoff_closeout.get(
+ "no_execution_receipt_handoff_closeout_id"
+ ),
+ "source_no_execution_receipt_handoff_id": handoff.get("handoff_id"),
+ "source_runner_invocation_boundary_closeout_id": boundary_closeout.get(
+ "runner_invocation_boundary_closeout_id"
+ ),
+ "required_command_shape_hash": freeze_proof.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout": (
+ closeout_ready
+ ),
+ "execution_envelope_freeze_proof_closeout_fields": freeze_closeout_fields,
+ "execution_envelope_freeze_proof_closeout_field_count": len(
+ freeze_closeout_fields
+ ),
+ "execution_envelope_freeze_proof_closeout_acceptance_gates": (
+ freeze_closeout_acceptance_gates
+ ),
+ "execution_envelope_freeze_proof_closeout_acceptance_gate_count": len(
+ freeze_closeout_acceptance_gates
+ ),
+ "frozen_envelope_verifier_handoff": frozen_envelope_verifier_handoff,
+ "frozen_envelope_verifier_handoff_count": 1,
+ "frozen_envelope_verifier_handoff_field_count": len(
+ frozen_envelope_verifier_handoff_fields
+ ),
+ "dry_run_execution_envelope_freeze_proof": freeze_proof,
+ "dry_run_execution_envelope_freeze_proof_count": 1,
+ "controlled_executor_quarantine_proof_closeout": freeze_closeout,
+ "controlled_executor_quarantine_proof_closeout_count": 1,
+ "controlled_executor_quarantine_proof": quarantine,
+ "controlled_executor_quarantine_proof_count": 1,
+ "final_no_runner_execution_proof_closeout": source_quarantine_closeout,
+ "final_no_runner_execution_proof_closeout_count": 1,
+ "final_no_runner_execution_proof": final_proof,
+ "final_no_runner_execution_proof_count": 1,
+ "no_execution_receipt_handoff_closeout": handoff_closeout,
+ "no_execution_receipt_handoff_closeout_count": 1,
+ "target_file": freeze_closeout.get("target_file"),
+ "expected_sha256": freeze_closeout.get("expected_sha256"),
+ "actual_sha256": freeze_closeout.get("actual_sha256"),
+ "hash_matches": freeze_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "execution_envelope_freeze_proof_closeout_only": True,
+ "frozen_envelope_verifier_handoff_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "controlled_executor_quarantine_bound": True,
+ "executor_quarantine_enforced": True,
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_execution_envelope_freeze_proof_closeout_contract = {
+ "mode": "controlled_dry_run_execution_envelope_freeze_proof_closeout_and_frozen_envelope_verifier_handoff_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout"
+ ),
+ "source_controlled_executor_quarantine_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff": (
+ closeout_ready
+ ),
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_count": 1,
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_field_count": len(
+ freeze_closeout_fields
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_acceptance_gate_count": len(
+ freeze_closeout_acceptance_gates
+ ),
+ "frozen_envelope_verifier_handoff_count": 1,
+ "frozen_envelope_verifier_handoff_field_count": len(
+ frozen_envelope_verifier_handoff_fields
+ ),
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_ENVELOPE_FREEZE_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(freeze_closeout_result.get("success")),
+ "generated_at": freeze_closeout_result.get("generated_at"),
+ "source_policy": freeze_closeout_result.get("policy"),
+ "stats": freeze_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff": (
+ future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout": (
+ controlled_dry_run_execution_envelope_freeze_proof_closeout
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract": (
+ controlled_dry_run_execution_envelope_freeze_proof_closeout_contract
+ ),
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_checks": (
+ checks
+ ),
+ "source_controlled_dry_run_controlled_executor_quarantine_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract": (
+ freeze_contract
+ ),
+ "source_controlled_dry_run_controlled_executor_quarantine_proof_closeout": (
+ freeze_closeout
+ ),
+ "source_database_apply_controlled_dry_run_execution_envelope_freeze_proof": (
+ future_freeze
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future frozen envelope verifier handoff closeout.",
+ "Keep verifier invocation disabled until a later lane explicitly proves the verifier boundary.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the verifier handoff while keeping verifier invocation locked."""
+ handoff_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_handoff = (
+ handoff_closeout_result.get(
+ "future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ )
+ or {}
+ )
+ handoff_closeout = (
+ handoff_closeout_result.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ )
+ or {}
+ )
+ handoff_contract = (
+ handoff_closeout_result.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = handoff_closeout_result.get("summary") or {}
+ safety = handoff_closeout_result.get("safety") or {}
+ frozen_handoff = handoff_closeout.get("frozen_envelope_verifier_handoff") or {}
+ freeze_proof = (
+ handoff_closeout.get("dry_run_execution_envelope_freeze_proof") or {}
+ )
+ source_freeze_closeout = (
+ handoff_closeout.get("controlled_executor_quarantine_proof_closeout") or {}
+ )
+ quarantine = handoff_closeout.get("controlled_executor_quarantine_proof") or {}
+ source_quarantine_closeout = (
+ handoff_closeout.get("final_no_runner_execution_proof_closeout") or {}
+ )
+ final_proof = handoff_closeout.get("final_no_runner_execution_proof") or {}
+ handoff_receipt_closeout = (
+ handoff_closeout.get("no_execution_receipt_handoff_closeout") or {}
+ )
+ rollback_binding = handoff_closeout.get("rollback_binding") or {}
+ verifier_binding = handoff_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = handoff_closeout.get(
+ "execution_envelope_freeze_proof_closeout_id"
+ )
+ closeout_id = (
+ _db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout_id(
+ handoff_closeout_result
+ )
+ )
+ lock_id = f"{closeout_id}-verifier-invocation-lock-proof"
+ handoff_closeout_fields = [
+ "frozen_envelope_verifier_handoff_closeout_id",
+ "source_execution_envelope_freeze_proof_closeout_id",
+ "source_frozen_envelope_verifier_handoff_id",
+ "source_dry_run_execution_envelope_freeze_proof_id",
+ "verifier_invocation_lock_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "verifier_invocation_locked",
+ "verifier_invocation_allowed",
+ "abort_conditions",
+ ]
+ handoff_closeout_acceptance_gates = [
+ "execution_envelope_freeze_proof_closeout_ready",
+ "source_chain_ids_match",
+ "frozen_envelope_verifier_handoff_ready",
+ "frozen_envelope_verifier_handoff_no_execute",
+ "verifier_invocation_lock_proof_bound",
+ "verifier_invocation_lock_proof_blocks_execution",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ verifier_invocation_lock_proof_fields = [
+ "lock_proof_id",
+ "source_frozen_envelope_verifier_handoff_closeout_id",
+ "source_frozen_envelope_verifier_handoff_id",
+ "source_execution_envelope_freeze_proof_closeout_id",
+ "source_dry_run_execution_envelope_freeze_proof_id",
+ "lock_mode",
+ "verifier_invocation_locked",
+ "verifier_invocation_allowed",
+ "dry_run_executor_invocation_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_execution_envelope_freeze_proof_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_frozen_envelope_verifier_handoff_missing",
+ "abort_if_frozen_envelope_verifier_handoff_reports_execution",
+ "abort_if_verifier_invocation_lock_proof_missing",
+ "abort_if_verifier_invocation_is_allowed",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ verifier_invocation_lock_proof = {
+ "lock_proof_id": lock_id,
+ "source_frozen_envelope_verifier_handoff_closeout_id": closeout_id,
+ "source_frozen_envelope_verifier_handoff_id": frozen_handoff.get(
+ "handoff_id"
+ ),
+ "source_execution_envelope_freeze_proof_closeout_id": source_closeout_id,
+ "source_dry_run_execution_envelope_freeze_proof_id": freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ "source_controlled_executor_quarantine_proof_closeout_id": (
+ source_freeze_closeout.get("controlled_executor_quarantine_proof_closeout_id")
+ ),
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_closeout_id": (
+ source_quarantine_closeout.get("final_no_runner_execution_proof_closeout_id")
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": (
+ handoff_receipt_closeout.get("no_execution_receipt_handoff_closeout_id")
+ ),
+ "required_command_shape_hash": freeze_proof.get("required_command_shape_hash"),
+ "lock_status": "verifier_invocation_lock_proof_preview_ready",
+ "lock_mode": "verifier_invocation_lock_proof_preview_only",
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "verifier_receipt_required": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "execution_receipt_required": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "ready_for_database_apply_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "verifier_invocation_lock_proof_field_count": len(
+ verifier_invocation_lock_proof_fields
+ ),
+ "verifier_invocation_lock_proof_fields": verifier_invocation_lock_proof_fields,
+ }
+ handoff_closeout_ready = (
+ handoff_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_ENVELOPE_FREEZE_PROOF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(source_closeout_id)
+ and source_closeout_id
+ == future_handoff.get("execution_envelope_freeze_proof_closeout_id")
+ == frozen_handoff.get("source_execution_envelope_freeze_proof_closeout_id")
+ == verifier_invocation_lock_proof.get(
+ "source_execution_envelope_freeze_proof_closeout_id"
+ )
+ and frozen_handoff.get("handoff_id")
+ == future_handoff.get("frozen_envelope_verifier_handoff_id")
+ == verifier_invocation_lock_proof.get(
+ "source_frozen_envelope_verifier_handoff_id"
+ )
+ and freeze_proof.get("freeze_proof_id")
+ == future_handoff.get("source_dry_run_execution_envelope_freeze_proof_id")
+ == frozen_handoff.get("source_dry_run_execution_envelope_freeze_proof_id")
+ == verifier_invocation_lock_proof.get(
+ "source_dry_run_execution_envelope_freeze_proof_id"
+ )
+ and quarantine.get("quarantine_proof_id")
+ == future_handoff.get("source_controlled_executor_quarantine_proof_id")
+ == frozen_handoff.get("source_controlled_executor_quarantine_proof_id")
+ == verifier_invocation_lock_proof.get(
+ "source_controlled_executor_quarantine_proof_id"
+ )
+ and final_proof.get("proof_id")
+ == frozen_handoff.get("source_final_no_runner_execution_proof_id")
+ == verifier_invocation_lock_proof.get(
+ "source_final_no_runner_execution_proof_id"
+ )
+ and handoff_receipt_closeout.get("no_execution_receipt_handoff_closeout_id")
+ == frozen_handoff.get("source_no_execution_receipt_handoff_closeout_id")
+ == verifier_invocation_lock_proof.get(
+ "source_no_execution_receipt_handoff_closeout_id"
+ )
+ )
+ frozen_envelope_verifier_handoff_ready = (
+ handoff_closeout_ready
+ and frozen_handoff.get("handoff_status")
+ == "frozen_envelope_verifier_handoff_preview_ready"
+ and frozen_handoff.get("handoff_id")
+ == future_handoff.get("frozen_envelope_verifier_handoff_id")
+ and int(
+ frozen_handoff.get("frozen_envelope_verifier_handoff_field_count")
+ or 0
+ )
+ == 12
+ and summary.get("frozen_envelope_verifier_handoff_count") == 1
+ )
+ frozen_envelope_verifier_handoff_no_execute = (
+ frozen_handoff.get("verifier_handoff_mode")
+ == "frozen_envelope_verifier_handoff_preview_only"
+ and frozen_handoff.get("execution_envelope_frozen") is True
+ and frozen_handoff.get("execution_envelope_mutation_allowed") is False
+ and frozen_handoff.get("verifier_invocation_allowed") is False
+ and frozen_handoff.get("verifier_invoked") is False
+ and frozen_handoff.get("verifier_receipt_present") is False
+ and frozen_handoff.get("verifier_receipt_required") is False
+ and frozen_handoff.get("dry_run_executor_invoked") is False
+ and frozen_handoff.get("runner_invocation_performed") is False
+ and frozen_handoff.get("endpoint_executed") is False
+ and frozen_handoff.get("sql_executed") is False
+ and frozen_handoff.get("database_written") is False
+ and frozen_handoff.get("dry_run_executor_invocation_allowed") is False
+ and frozen_handoff.get("runner_invocation_allowed") is False
+ and frozen_handoff.get("ready_for_verifier_invocation_now") is False
+ and frozen_handoff.get("ready_for_dry_run_executor_invocation_now") is False
+ and frozen_handoff.get("ready_for_actual_dry_run_execution_now") is False
+ and frozen_handoff.get("endpoint_execution_allowed") is False
+ and frozen_handoff.get("sql_execution_allowed") is False
+ and frozen_handoff.get("database_write_allowed") is False
+ and frozen_handoff.get("database_apply_authorized") is False
+ and frozen_handoff.get("executes_database_apply") is False
+ and frozen_handoff.get("executes_endpoint") is False
+ and frozen_handoff.get("executes_sql") is False
+ and frozen_handoff.get("writes_database") is False
+ and frozen_handoff.get("captures_stdout") is False
+ and frozen_handoff.get("captures_stderr") is False
+ and frozen_handoff.get("stdout_included") is False
+ and frozen_handoff.get("stderr_included") is False
+ )
+ verifier_invocation_lock_proof_bound = (
+ frozen_envelope_verifier_handoff_ready
+ and bool(verifier_invocation_lock_proof.get("lock_proof_id"))
+ and verifier_invocation_lock_proof.get(
+ "source_frozen_envelope_verifier_handoff_closeout_id"
+ )
+ == closeout_id
+ and verifier_invocation_lock_proof.get(
+ "source_frozen_envelope_verifier_handoff_id"
+ )
+ == frozen_handoff.get("handoff_id")
+ and verifier_invocation_lock_proof.get("required_command_shape_hash")
+ == freeze_proof.get("required_command_shape_hash")
+ and int(
+ verifier_invocation_lock_proof.get(
+ "verifier_invocation_lock_proof_field_count"
+ )
+ or 0
+ )
+ == len(verifier_invocation_lock_proof_fields)
+ )
+ verifier_invocation_lock_proof_blocks_execution = (
+ verifier_invocation_lock_proof.get("lock_mode")
+ == "verifier_invocation_lock_proof_preview_only"
+ and verifier_invocation_lock_proof.get("verifier_invocation_locked") is True
+ and verifier_invocation_lock_proof.get("verifier_invocation_allowed")
+ is False
+ and verifier_invocation_lock_proof.get("verifier_invoked") is False
+ and verifier_invocation_lock_proof.get("verifier_receipt_present") is False
+ and verifier_invocation_lock_proof.get("verifier_receipt_required") is False
+ and verifier_invocation_lock_proof.get("dry_run_executor_invoked") is False
+ and verifier_invocation_lock_proof.get("runner_invocation_performed")
+ is False
+ and verifier_invocation_lock_proof.get("endpoint_executed") is False
+ and verifier_invocation_lock_proof.get("sql_executed") is False
+ and verifier_invocation_lock_proof.get("database_written") is False
+ and verifier_invocation_lock_proof.get("execution_receipt_present") is False
+ and verifier_invocation_lock_proof.get("execution_receipt_required") is False
+ and verifier_invocation_lock_proof.get("dry_run_executor_invocation_allowed")
+ is False
+ and verifier_invocation_lock_proof.get("runner_invocation_allowed") is False
+ and verifier_invocation_lock_proof.get("ready_for_verifier_invocation_now")
+ is False
+ and verifier_invocation_lock_proof.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and verifier_invocation_lock_proof.get("ready_for_actual_dry_run_execution_now")
+ is False
+ and verifier_invocation_lock_proof.get("endpoint_execution_allowed") is False
+ and verifier_invocation_lock_proof.get("sql_execution_allowed") is False
+ and verifier_invocation_lock_proof.get("database_write_allowed") is False
+ and verifier_invocation_lock_proof.get("database_apply_authorized") is False
+ and verifier_invocation_lock_proof.get("executes_database_apply") is False
+ and verifier_invocation_lock_proof.get("executes_endpoint") is False
+ and verifier_invocation_lock_proof.get("executes_sql") is False
+ and verifier_invocation_lock_proof.get("writes_database") is False
+ and verifier_invocation_lock_proof.get("captures_stdout") is False
+ and verifier_invocation_lock_proof.get("captures_stderr") is False
+ and verifier_invocation_lock_proof.get("stdout_included") is False
+ and verifier_invocation_lock_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ handoff_closeout.get("execution_envelope_freeze_proof_closeout_only") is True
+ and handoff_closeout.get("frozen_envelope_verifier_handoff_only") is True
+ and handoff_closeout.get("database_apply_authorized") is False
+ and source_freeze_closeout.get("controlled_executor_quarantine_proof_closeout_only")
+ is True
+ and source_freeze_closeout.get("dry_run_execution_envelope_freeze_proof_only")
+ is True
+ and source_quarantine_closeout.get(
+ "final_no_runner_execution_proof_closeout_only"
+ )
+ is True
+ and handoff_receipt_closeout.get("no_execution_receipt_handoff_closeout_only")
+ is True
+ and frozen_envelope_verifier_handoff_no_execute
+ )
+ target_hash_locked = (
+ handoff_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(handoff_closeout.get("expected_sha256"))
+ and bool(handoff_closeout.get("actual_sha256"))
+ and handoff_closeout.get("expected_sha256")
+ == handoff_closeout.get("actual_sha256")
+ and handoff_closeout.get("hash_matches") is True
+ and handoff_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ handoff_contract_blocks_database_apply = (
+ handoff_contract.get("executes_database_apply") is False
+ and handoff_contract.get("executes_endpoint") is False
+ and handoff_contract.get("executes_sql") is False
+ and handoff_contract.get("database_apply_authorized") is False
+ and handoff_contract.get("ready_for_database_apply_now") is False
+ and handoff_contract.get("ready_for_verifier_invocation_now") is False
+ and handoff_contract.get("ready_for_dry_run_executor_invocation_now") is False
+ and handoff_contract.get("ready_for_actual_dry_run_execution_now") is False
+ and handoff_contract.get("signs_database_apply_authorization") is False
+ and handoff_contract.get("writes_database") is False
+ and handoff_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("verifier_invoked_count", 0) == 0
+ and summary.get("verifier_receipt_present_count", 0) == 0
+ and summary.get("dry_run_executor_invoked_count", 0) == 0
+ and summary.get("runner_invocation_performed_count", 0) == 0
+ and summary.get("endpoint_executed_count", 0) == 0
+ and summary.get("sql_executed_count", 0) == 0
+ and summary.get("database_written_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and frozen_envelope_verifier_handoff_no_execute
+ and verifier_invocation_lock_proof_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "execution_envelope_freeze_proof_closeout_ready",
+ handoff_closeout_ready,
+ {
+ "result": handoff_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_check_count"
+ ),
+ },
+ "wait_for_execution_envelope_freeze_proof_closeout_ready",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "execution_envelope_freeze_proof_closeout_id": source_closeout_id,
+ "frozen_envelope_verifier_handoff_id": frozen_handoff.get(
+ "handoff_id"
+ ),
+ "verifier_invocation_lock_proof_id": (
+ verifier_invocation_lock_proof.get("lock_proof_id")
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "frozen_envelope_verifier_handoff_ready",
+ frozen_envelope_verifier_handoff_ready,
+ {
+ "handoff_id": frozen_handoff.get("handoff_id"),
+ "handoff_status": frozen_handoff.get("handoff_status"),
+ "field_count": frozen_handoff.get(
+ "frozen_envelope_verifier_handoff_field_count"
+ ),
+ },
+ "wait_for_frozen_envelope_verifier_handoff_ready",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "frozen_envelope_verifier_handoff_no_execute",
+ frozen_envelope_verifier_handoff_no_execute,
+ {
+ "verifier_handoff_mode": frozen_handoff.get(
+ "verifier_handoff_mode"
+ ),
+ "verifier_invocation_allowed": frozen_handoff.get(
+ "verifier_invocation_allowed"
+ ),
+ "verifier_invoked": frozen_handoff.get("verifier_invoked"),
+ },
+ "abort_if_frozen_envelope_verifier_handoff_reports_execution",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "verifier_invocation_lock_proof_bound",
+ verifier_invocation_lock_proof_bound,
+ {
+ "lock_proof_id": verifier_invocation_lock_proof.get(
+ "lock_proof_id"
+ ),
+ "source_frozen_envelope_verifier_handoff_id": (
+ verifier_invocation_lock_proof.get(
+ "source_frozen_envelope_verifier_handoff_id"
+ )
+ ),
+ "field_count": verifier_invocation_lock_proof.get(
+ "verifier_invocation_lock_proof_field_count"
+ ),
+ },
+ "wait_for_verifier_invocation_lock_proof_binding",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "verifier_invocation_lock_proof_blocks_execution",
+ verifier_invocation_lock_proof_blocks_execution,
+ {
+ "lock_mode": verifier_invocation_lock_proof.get("lock_mode"),
+ "verifier_invocation_locked": verifier_invocation_lock_proof.get(
+ "verifier_invocation_locked"
+ ),
+ "verifier_invocation_allowed": verifier_invocation_lock_proof.get(
+ "verifier_invocation_allowed"
+ ),
+ },
+ "abort_if_verifier_invocation_lock_proof_allows_execution",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "execution_envelope_freeze_proof_closeout_only": (
+ handoff_closeout.get(
+ "execution_envelope_freeze_proof_closeout_only"
+ )
+ ),
+ "frozen_envelope_verifier_handoff_only": (
+ handoff_closeout.get("frozen_envelope_verifier_handoff_only")
+ ),
+ "lock_mode": verifier_invocation_lock_proof.get("lock_mode"),
+ },
+ "wait_for_previous_closeouts_carry_forward",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": handoff_closeout.get("target_file"),
+ "hash_matches": handoff_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(handoff_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(handoff_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "execution_envelope_freeze_proof_closeout_contract_blocks_database_apply",
+ handoff_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff": (
+ handoff_contract.get(
+ "permits_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ )
+ ),
+ "database_apply_authorized": handoff_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": handoff_contract.get("writes_database"),
+ },
+ "abort_if_execution_envelope_freeze_proof_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "verifier_invoked_count": summary.get("verifier_invoked_count", 0),
+ "signs_database_apply_authorization_count": summary.get(
+ "signs_database_apply_authorization_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ handoff_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": handoff_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_FROZEN_ENVELOPE_VERIFIER_HANDOFF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_ENVELOPE_FREEZE_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_verifier_invocation_lock_proof = {
+ "frozen_envelope_verifier_handoff_closeout_id": closeout_id,
+ "verifier_invocation_lock_proof_id": lock_id,
+ "source_execution_envelope_freeze_proof_closeout_id": source_closeout_id,
+ "source_frozen_envelope_verifier_handoff_id": frozen_handoff.get(
+ "handoff_id"
+ ),
+ "source_dry_run_execution_envelope_freeze_proof_id": freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout": (
+ closeout_ready
+ ),
+ "frozen_envelope_verifier_handoff_closeout_ready": closeout_ready,
+ "execution_envelope_freeze_proof_closeout_ready": handoff_closeout_ready,
+ "frozen_envelope_verifier_handoff_ready": (
+ frozen_envelope_verifier_handoff_ready
+ ),
+ "verifier_invocation_lock_proof_bound": closeout_ready,
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_frozen_envelope_verifier_handoff_closeout = {
+ "frozen_envelope_verifier_handoff_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ ),
+ "source_execution_envelope_freeze_proof_closeout_id": source_closeout_id,
+ "source_frozen_envelope_verifier_handoff_id": frozen_handoff.get(
+ "handoff_id"
+ ),
+ "source_dry_run_execution_envelope_freeze_proof_id": freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ "source_controlled_executor_quarantine_proof_closeout_id": (
+ source_freeze_closeout.get("controlled_executor_quarantine_proof_closeout_id")
+ ),
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_closeout_id": (
+ source_quarantine_closeout.get("final_no_runner_execution_proof_closeout_id")
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": (
+ handoff_receipt_closeout.get("no_execution_receipt_handoff_closeout_id")
+ ),
+ "required_command_shape_hash": freeze_proof.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof": (
+ closeout_ready
+ ),
+ "frozen_envelope_verifier_handoff_closeout_fields": (
+ handoff_closeout_fields
+ ),
+ "frozen_envelope_verifier_handoff_closeout_field_count": len(
+ handoff_closeout_fields
+ ),
+ "frozen_envelope_verifier_handoff_closeout_acceptance_gates": (
+ handoff_closeout_acceptance_gates
+ ),
+ "frozen_envelope_verifier_handoff_closeout_acceptance_gate_count": len(
+ handoff_closeout_acceptance_gates
+ ),
+ "verifier_invocation_lock_proof": verifier_invocation_lock_proof,
+ "verifier_invocation_lock_proof_count": 1,
+ "verifier_invocation_lock_proof_field_count": len(
+ verifier_invocation_lock_proof_fields
+ ),
+ "frozen_envelope_verifier_handoff": frozen_handoff,
+ "frozen_envelope_verifier_handoff_count": 1,
+ "frozen_envelope_verifier_handoff_field_count": int(
+ frozen_handoff.get("frozen_envelope_verifier_handoff_field_count") or 0
+ ),
+ "dry_run_execution_envelope_freeze_proof": freeze_proof,
+ "dry_run_execution_envelope_freeze_proof_count": 1,
+ "execution_envelope_freeze_proof_closeout": handoff_closeout,
+ "execution_envelope_freeze_proof_closeout_count": 1,
+ "controlled_executor_quarantine_proof_closeout": source_freeze_closeout,
+ "controlled_executor_quarantine_proof_closeout_count": 1,
+ "controlled_executor_quarantine_proof": quarantine,
+ "controlled_executor_quarantine_proof_count": 1,
+ "final_no_runner_execution_proof_closeout": source_quarantine_closeout,
+ "final_no_runner_execution_proof_closeout_count": 1,
+ "final_no_runner_execution_proof": final_proof,
+ "final_no_runner_execution_proof_count": 1,
+ "no_execution_receipt_handoff_closeout": handoff_receipt_closeout,
+ "no_execution_receipt_handoff_closeout_count": 1,
+ "target_file": handoff_closeout.get("target_file"),
+ "expected_sha256": handoff_closeout.get("expected_sha256"),
+ "actual_sha256": handoff_closeout.get("actual_sha256"),
+ "hash_matches": handoff_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "frozen_envelope_verifier_handoff_closeout_only": True,
+ "verifier_invocation_lock_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "controlled_executor_quarantine_bound": True,
+ "executor_quarantine_enforced": True,
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract = {
+ "mode": "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_and_verifier_invocation_lock_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout"
+ ),
+ "source_execution_envelope_freeze_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof": (
+ closeout_ready
+ ),
+ "verifier_invocation_locked": True,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_count": 1,
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_field_count": len(
+ handoff_closeout_fields
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_acceptance_gate_count": len(
+ handoff_closeout_acceptance_gates
+ ),
+ "verifier_invocation_lock_proof_count": 1,
+ "verifier_invocation_lock_proof_field_count": len(
+ verifier_invocation_lock_proof_fields
+ ),
+ "verifier_invocation_locked_count": 1,
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_FROZEN_ENVELOPE_VERIFIER_HANDOFF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(handoff_closeout_result.get("success")),
+ "generated_at": handoff_closeout_result.get("generated_at"),
+ "source_policy": handoff_closeout_result.get("policy"),
+ "stats": handoff_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_verifier_invocation_lock_proof": (
+ future_database_apply_controlled_dry_run_verifier_invocation_lock_proof
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout": (
+ controlled_dry_run_frozen_envelope_verifier_handoff_closeout
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract": (
+ controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract
+ ),
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_checks": checks,
+ "source_controlled_dry_run_execution_envelope_freeze_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_execution_envelope_freeze_proof_closeout_contract": (
+ handoff_contract
+ ),
+ "source_controlled_dry_run_execution_envelope_freeze_proof_closeout": (
+ handoff_closeout
+ ),
+ "source_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff": (
+ future_handoff
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future verifier invocation lock proof closeout.",
+ "Keep verifier invocation disabled until a later no-execution receipt proof closes this lock boundary.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, verifier invocation, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the verifier invocation lock and prove no verifier receipt exists."""
+ lock_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_lock = (
+ lock_closeout_result.get(
+ "future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ )
+ or {}
+ )
+ lock_closeout = (
+ lock_closeout_result.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ )
+ or {}
+ )
+ lock_contract = (
+ lock_closeout_result.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract"
+ )
+ or {}
+ )
+ summary = lock_closeout_result.get("summary") or {}
+ safety = lock_closeout_result.get("safety") or {}
+ lock_proof = lock_closeout.get("verifier_invocation_lock_proof") or {}
+ frozen_handoff = lock_closeout.get("frozen_envelope_verifier_handoff") or {}
+ source_handoff_closeout = (
+ lock_closeout.get("execution_envelope_freeze_proof_closeout") or {}
+ )
+ freeze_proof = (
+ lock_closeout.get("dry_run_execution_envelope_freeze_proof") or {}
+ )
+ source_freeze_closeout = (
+ lock_closeout.get("controlled_executor_quarantine_proof_closeout") or {}
+ )
+ quarantine = lock_closeout.get("controlled_executor_quarantine_proof") or {}
+ source_quarantine_closeout = (
+ lock_closeout.get("final_no_runner_execution_proof_closeout") or {}
+ )
+ final_proof = lock_closeout.get("final_no_runner_execution_proof") or {}
+ handoff_receipt_closeout = (
+ lock_closeout.get("no_execution_receipt_handoff_closeout") or {}
+ )
+ rollback_binding = lock_closeout.get("rollback_binding") or {}
+ verifier_binding = lock_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = lock_closeout.get(
+ "frozen_envelope_verifier_handoff_closeout_id"
+ )
+ closeout_id = (
+ _db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout_id(
+ lock_closeout_result
+ )
+ )
+ receipt_id = f"{closeout_id}-verifier-no-execution-receipt-proof"
+ lock_closeout_fields = [
+ "verifier_invocation_lock_proof_closeout_id",
+ "source_frozen_envelope_verifier_handoff_closeout_id",
+ "source_verifier_invocation_lock_proof_id",
+ "source_frozen_envelope_verifier_handoff_id",
+ "verifier_no_execution_receipt_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "verifier_invocation_locked",
+ "verifier_invoked",
+ "abort_conditions",
+ ]
+ lock_closeout_acceptance_gates = [
+ "frozen_envelope_verifier_handoff_closeout_ready",
+ "source_chain_ids_match",
+ "verifier_invocation_lock_proof_ready",
+ "verifier_invocation_lock_proof_no_execute",
+ "verifier_no_execution_receipt_proof_bound",
+ "verifier_no_execution_receipt_proof_blocks_execution",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_or_database_apply",
+ ]
+ verifier_no_execution_receipt_proof_fields = [
+ "receipt_proof_id",
+ "source_verifier_invocation_lock_proof_closeout_id",
+ "source_verifier_invocation_lock_proof_id",
+ "source_frozen_envelope_verifier_handoff_closeout_id",
+ "source_frozen_envelope_verifier_handoff_id",
+ "receipt_mode",
+ "verifier_invocation_locked",
+ "verifier_invoked",
+ "verifier_receipt_present",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_frozen_envelope_verifier_handoff_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_verifier_invocation_lock_proof_missing",
+ "abort_if_verifier_invocation_lock_proof_reports_execution",
+ "abort_if_verifier_no_execution_receipt_proof_missing",
+ "abort_if_verifier_receipt_is_present",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_or_apply_material_is_present",
+ ]
+ verifier_no_execution_receipt_proof = {
+ "receipt_proof_id": receipt_id,
+ "source_verifier_invocation_lock_proof_closeout_id": closeout_id,
+ "source_verifier_invocation_lock_proof_id": lock_proof.get("lock_proof_id"),
+ "source_frozen_envelope_verifier_handoff_closeout_id": source_closeout_id,
+ "source_frozen_envelope_verifier_handoff_id": frozen_handoff.get(
+ "handoff_id"
+ ),
+ "source_execution_envelope_freeze_proof_closeout_id": (
+ source_handoff_closeout.get("execution_envelope_freeze_proof_closeout_id")
+ ),
+ "source_dry_run_execution_envelope_freeze_proof_id": freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ "source_controlled_executor_quarantine_proof_closeout_id": (
+ source_freeze_closeout.get("controlled_executor_quarantine_proof_closeout_id")
+ ),
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_closeout_id": (
+ source_quarantine_closeout.get("final_no_runner_execution_proof_closeout_id")
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": (
+ handoff_receipt_closeout.get("no_execution_receipt_handoff_closeout_id")
+ ),
+ "required_command_shape_hash": freeze_proof.get("required_command_shape_hash"),
+ "receipt_status": "verifier_no_execution_receipt_proof_preview_ready",
+ "receipt_mode": "verifier_no_execution_receipt_proof_preview_only",
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "verifier_receipt_required": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "execution_receipt_required": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "ready_for_database_apply_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "verifier_no_execution_receipt_proof_field_count": len(
+ verifier_no_execution_receipt_proof_fields
+ ),
+ "verifier_no_execution_receipt_proof_fields": (
+ verifier_no_execution_receipt_proof_fields
+ ),
+ }
+ lock_closeout_ready = (
+ lock_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_FROZEN_ENVELOPE_VERIFIER_HANDOFF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(source_closeout_id)
+ and source_closeout_id
+ == future_lock.get("frozen_envelope_verifier_handoff_closeout_id")
+ == lock_proof.get("source_frozen_envelope_verifier_handoff_closeout_id")
+ == verifier_no_execution_receipt_proof.get(
+ "source_frozen_envelope_verifier_handoff_closeout_id"
+ )
+ and lock_proof.get("lock_proof_id")
+ == future_lock.get("verifier_invocation_lock_proof_id")
+ == verifier_no_execution_receipt_proof.get(
+ "source_verifier_invocation_lock_proof_id"
+ )
+ and frozen_handoff.get("handoff_id")
+ == future_lock.get("source_frozen_envelope_verifier_handoff_id")
+ == lock_proof.get("source_frozen_envelope_verifier_handoff_id")
+ == verifier_no_execution_receipt_proof.get(
+ "source_frozen_envelope_verifier_handoff_id"
+ )
+ and source_handoff_closeout.get("execution_envelope_freeze_proof_closeout_id")
+ == future_lock.get("source_execution_envelope_freeze_proof_closeout_id")
+ == lock_proof.get("source_execution_envelope_freeze_proof_closeout_id")
+ == verifier_no_execution_receipt_proof.get(
+ "source_execution_envelope_freeze_proof_closeout_id"
+ )
+ and freeze_proof.get("freeze_proof_id")
+ == future_lock.get("source_dry_run_execution_envelope_freeze_proof_id")
+ == lock_proof.get("source_dry_run_execution_envelope_freeze_proof_id")
+ == verifier_no_execution_receipt_proof.get(
+ "source_dry_run_execution_envelope_freeze_proof_id"
+ )
+ and quarantine.get("quarantine_proof_id")
+ == lock_proof.get("source_controlled_executor_quarantine_proof_id")
+ == verifier_no_execution_receipt_proof.get(
+ "source_controlled_executor_quarantine_proof_id"
+ )
+ and final_proof.get("proof_id")
+ == lock_proof.get("source_final_no_runner_execution_proof_id")
+ == verifier_no_execution_receipt_proof.get(
+ "source_final_no_runner_execution_proof_id"
+ )
+ and handoff_receipt_closeout.get("no_execution_receipt_handoff_closeout_id")
+ == lock_proof.get("source_no_execution_receipt_handoff_closeout_id")
+ == verifier_no_execution_receipt_proof.get(
+ "source_no_execution_receipt_handoff_closeout_id"
+ )
+ )
+ verifier_invocation_lock_proof_ready = (
+ lock_closeout_ready
+ and lock_proof.get("lock_status")
+ == "verifier_invocation_lock_proof_preview_ready"
+ and lock_proof.get("lock_proof_id")
+ == future_lock.get("verifier_invocation_lock_proof_id")
+ and int(lock_proof.get("verifier_invocation_lock_proof_field_count") or 0)
+ == 12
+ and summary.get("verifier_invocation_lock_proof_count") == 1
+ )
+ verifier_invocation_lock_proof_no_execute = (
+ lock_proof.get("lock_mode") == "verifier_invocation_lock_proof_preview_only"
+ and lock_proof.get("verifier_invocation_locked") is True
+ and lock_proof.get("verifier_invocation_allowed") is False
+ and lock_proof.get("verifier_invoked") is False
+ and lock_proof.get("verifier_receipt_present") is False
+ and lock_proof.get("verifier_receipt_required") is False
+ and lock_proof.get("dry_run_executor_invoked") is False
+ and lock_proof.get("runner_invocation_performed") is False
+ and lock_proof.get("endpoint_executed") is False
+ and lock_proof.get("sql_executed") is False
+ and lock_proof.get("database_written") is False
+ and lock_proof.get("execution_receipt_present") is False
+ and lock_proof.get("execution_receipt_required") is False
+ and lock_proof.get("dry_run_executor_invocation_allowed") is False
+ and lock_proof.get("runner_invocation_allowed") is False
+ and lock_proof.get("ready_for_verifier_invocation_now") is False
+ and lock_proof.get("ready_for_dry_run_executor_invocation_now") is False
+ and lock_proof.get("ready_for_actual_dry_run_execution_now") is False
+ and lock_proof.get("endpoint_execution_allowed") is False
+ and lock_proof.get("sql_execution_allowed") is False
+ and lock_proof.get("database_write_allowed") is False
+ and lock_proof.get("database_apply_authorized") is False
+ and lock_proof.get("executes_database_apply") is False
+ and lock_proof.get("executes_endpoint") is False
+ and lock_proof.get("executes_sql") is False
+ and lock_proof.get("writes_database") is False
+ and lock_proof.get("captures_stdout") is False
+ and lock_proof.get("captures_stderr") is False
+ and lock_proof.get("stdout_included") is False
+ and lock_proof.get("stderr_included") is False
+ )
+ verifier_no_execution_receipt_proof_bound = (
+ verifier_invocation_lock_proof_ready
+ and bool(verifier_no_execution_receipt_proof.get("receipt_proof_id"))
+ and verifier_no_execution_receipt_proof.get(
+ "source_verifier_invocation_lock_proof_closeout_id"
+ )
+ == closeout_id
+ and verifier_no_execution_receipt_proof.get(
+ "source_verifier_invocation_lock_proof_id"
+ )
+ == lock_proof.get("lock_proof_id")
+ and verifier_no_execution_receipt_proof.get("required_command_shape_hash")
+ == freeze_proof.get("required_command_shape_hash")
+ and int(
+ verifier_no_execution_receipt_proof.get(
+ "verifier_no_execution_receipt_proof_field_count"
+ )
+ or 0
+ )
+ == len(verifier_no_execution_receipt_proof_fields)
+ )
+ verifier_no_execution_receipt_proof_blocks_execution = (
+ verifier_no_execution_receipt_proof.get("receipt_mode")
+ == "verifier_no_execution_receipt_proof_preview_only"
+ and verifier_no_execution_receipt_proof.get("verifier_invocation_locked")
+ is True
+ and verifier_no_execution_receipt_proof.get("verifier_invocation_allowed")
+ is False
+ and verifier_no_execution_receipt_proof.get("verifier_invoked") is False
+ and verifier_no_execution_receipt_proof.get("verifier_receipt_present")
+ is False
+ and verifier_no_execution_receipt_proof.get("verifier_receipt_required")
+ is False
+ and verifier_no_execution_receipt_proof.get("dry_run_executor_invoked")
+ is False
+ and verifier_no_execution_receipt_proof.get("runner_invocation_performed")
+ is False
+ and verifier_no_execution_receipt_proof.get("endpoint_executed") is False
+ and verifier_no_execution_receipt_proof.get("sql_executed") is False
+ and verifier_no_execution_receipt_proof.get("database_written") is False
+ and verifier_no_execution_receipt_proof.get("execution_receipt_present")
+ is False
+ and verifier_no_execution_receipt_proof.get("execution_receipt_required")
+ is False
+ and verifier_no_execution_receipt_proof.get(
+ "dry_run_executor_invocation_allowed"
+ )
+ is False
+ and verifier_no_execution_receipt_proof.get("runner_invocation_allowed")
+ is False
+ and verifier_no_execution_receipt_proof.get(
+ "ready_for_verifier_invocation_now"
+ )
+ is False
+ and verifier_no_execution_receipt_proof.get(
+ "ready_for_dry_run_executor_invocation_now"
+ )
+ is False
+ and verifier_no_execution_receipt_proof.get(
+ "ready_for_actual_dry_run_execution_now"
+ )
+ is False
+ and verifier_no_execution_receipt_proof.get("endpoint_execution_allowed")
+ is False
+ and verifier_no_execution_receipt_proof.get("sql_execution_allowed") is False
+ and verifier_no_execution_receipt_proof.get("database_write_allowed") is False
+ and verifier_no_execution_receipt_proof.get("database_apply_authorized")
+ is False
+ and verifier_no_execution_receipt_proof.get("executes_database_apply")
+ is False
+ and verifier_no_execution_receipt_proof.get("executes_endpoint") is False
+ and verifier_no_execution_receipt_proof.get("executes_sql") is False
+ and verifier_no_execution_receipt_proof.get("writes_database") is False
+ and verifier_no_execution_receipt_proof.get("captures_stdout") is False
+ and verifier_no_execution_receipt_proof.get("captures_stderr") is False
+ and verifier_no_execution_receipt_proof.get("stdout_included") is False
+ and verifier_no_execution_receipt_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ lock_closeout.get("frozen_envelope_verifier_handoff_closeout_only") is True
+ and lock_closeout.get("verifier_invocation_lock_proof_only") is True
+ and lock_closeout.get("database_apply_authorized") is False
+ and source_handoff_closeout.get("execution_envelope_freeze_proof_closeout_only")
+ is True
+ and source_handoff_closeout.get("frozen_envelope_verifier_handoff_only")
+ is True
+ and source_freeze_closeout.get("controlled_executor_quarantine_proof_closeout_only")
+ is True
+ and handoff_receipt_closeout.get("no_execution_receipt_handoff_closeout_only")
+ is True
+ and verifier_invocation_lock_proof_no_execute
+ )
+ target_hash_locked = (
+ lock_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(lock_closeout.get("expected_sha256"))
+ and bool(lock_closeout.get("actual_sha256"))
+ and lock_closeout.get("expected_sha256") == lock_closeout.get("actual_sha256")
+ and lock_closeout.get("hash_matches") is True
+ and lock_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ lock_contract_blocks_database_apply = (
+ lock_contract.get("executes_database_apply") is False
+ and lock_contract.get("executes_endpoint") is False
+ and lock_contract.get("executes_sql") is False
+ and lock_contract.get("database_apply_authorized") is False
+ and lock_contract.get("ready_for_database_apply_now") is False
+ and lock_contract.get("ready_for_verifier_invocation_now") is False
+ and lock_contract.get("ready_for_dry_run_executor_invocation_now") is False
+ and lock_contract.get("ready_for_actual_dry_run_execution_now") is False
+ and lock_contract.get("signs_database_apply_authorization") is False
+ and lock_contract.get("writes_database") is False
+ and lock_contract.get("executes_in_preview") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_script_count", 0) == 0
+ and summary.get("executes_migration_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("verifier_invoked_count", 0) == 0
+ and summary.get("verifier_receipt_present_count", 0) == 0
+ and summary.get("dry_run_executor_invoked_count", 0) == 0
+ and summary.get("runner_invocation_performed_count", 0) == 0
+ and summary.get("endpoint_executed_count", 0) == 0
+ and summary.get("sql_executed_count", 0) == 0
+ and summary.get("database_written_count", 0) == 0
+ and safety.get("reads_secret_in_preview") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ and verifier_invocation_lock_proof_no_execute
+ and verifier_no_execution_receipt_proof_blocks_execution
+ )
+ checks = [
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "frozen_envelope_verifier_handoff_closeout_ready",
+ lock_closeout_ready,
+ {
+ "result": lock_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_count"
+ ),
+ "pass_count": summary.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_pass_count"
+ ),
+ "check_count": summary.get(
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check_count"
+ ),
+ },
+ "wait_for_frozen_envelope_verifier_handoff_closeout_ready",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "frozen_envelope_verifier_handoff_closeout_id": source_closeout_id,
+ "verifier_invocation_lock_proof_id": lock_proof.get(
+ "lock_proof_id"
+ ),
+ "verifier_no_execution_receipt_proof_id": (
+ verifier_no_execution_receipt_proof.get("receipt_proof_id")
+ ),
+ },
+ "wait_for_source_chain_ids_match",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "verifier_invocation_lock_proof_ready",
+ verifier_invocation_lock_proof_ready,
+ {
+ "lock_proof_id": lock_proof.get("lock_proof_id"),
+ "lock_status": lock_proof.get("lock_status"),
+ "field_count": lock_proof.get(
+ "verifier_invocation_lock_proof_field_count"
+ ),
+ },
+ "wait_for_verifier_invocation_lock_proof_ready",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "verifier_invocation_lock_proof_no_execute",
+ verifier_invocation_lock_proof_no_execute,
+ {
+ "lock_mode": lock_proof.get("lock_mode"),
+ "verifier_invocation_locked": lock_proof.get(
+ "verifier_invocation_locked"
+ ),
+ "verifier_invoked": lock_proof.get("verifier_invoked"),
+ },
+ "abort_if_verifier_invocation_lock_proof_reports_execution",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "verifier_no_execution_receipt_proof_bound",
+ verifier_no_execution_receipt_proof_bound,
+ {
+ "receipt_proof_id": verifier_no_execution_receipt_proof.get(
+ "receipt_proof_id"
+ ),
+ "source_verifier_invocation_lock_proof_id": (
+ verifier_no_execution_receipt_proof.get(
+ "source_verifier_invocation_lock_proof_id"
+ )
+ ),
+ "field_count": verifier_no_execution_receipt_proof.get(
+ "verifier_no_execution_receipt_proof_field_count"
+ ),
+ },
+ "wait_for_verifier_no_execution_receipt_proof_binding",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "verifier_no_execution_receipt_proof_blocks_execution",
+ verifier_no_execution_receipt_proof_blocks_execution,
+ {
+ "receipt_mode": verifier_no_execution_receipt_proof.get(
+ "receipt_mode"
+ ),
+ "verifier_invoked": verifier_no_execution_receipt_proof.get(
+ "verifier_invoked"
+ ),
+ "verifier_receipt_present": verifier_no_execution_receipt_proof.get(
+ "verifier_receipt_present"
+ ),
+ },
+ "abort_if_verifier_no_execution_receipt_proof_allows_execution",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "frozen_envelope_verifier_handoff_closeout_only": (
+ lock_closeout.get("frozen_envelope_verifier_handoff_closeout_only")
+ ),
+ "verifier_invocation_lock_proof_only": (
+ lock_closeout.get("verifier_invocation_lock_proof_only")
+ ),
+ "receipt_mode": verifier_no_execution_receipt_proof.get(
+ "receipt_mode"
+ ),
+ },
+ "wait_for_previous_closeouts_carry_forward",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": lock_closeout.get("target_file"),
+ "hash_matches": lock_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(lock_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(lock_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "database_apply_authorized": verifier_binding.get(
+ "database_apply_authorized"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "frozen_envelope_verifier_handoff_closeout_contract_blocks_database_apply",
+ lock_contract_blocks_database_apply,
+ {
+ "permits_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof": (
+ lock_contract.get(
+ "permits_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ )
+ ),
+ "database_apply_authorized": lock_contract.get(
+ "database_apply_authorized"
+ ),
+ "writes_database": lock_contract.get("writes_database"),
+ },
+ "abort_if_frozen_envelope_verifier_handoff_closeout_contract_authorizes_database_apply",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "preview_has_no_side_effects_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "verifier_invoked_count": summary.get("verifier_invoked_count", 0),
+ "verifier_receipt_present_count": summary.get(
+ "verifier_receipt_present_count", 0
+ ),
+ },
+ "abort_on_preview_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_verifier_invocation_lock_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ lock_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": lock_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_INVOCATION_LOCK_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_FROZEN_ENVELOPE_VERIFIER_HANDOFF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof = {
+ "verifier_invocation_lock_proof_closeout_id": closeout_id,
+ "verifier_no_execution_receipt_proof_id": receipt_id,
+ "source_frozen_envelope_verifier_handoff_closeout_id": source_closeout_id,
+ "source_verifier_invocation_lock_proof_id": lock_proof.get("lock_proof_id"),
+ "source_frozen_envelope_verifier_handoff_id": frozen_handoff.get(
+ "handoff_id"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout": (
+ closeout_ready
+ ),
+ "verifier_invocation_lock_proof_closeout_ready": closeout_ready,
+ "frozen_envelope_verifier_handoff_closeout_ready": lock_closeout_ready,
+ "verifier_invocation_lock_proof_ready": verifier_invocation_lock_proof_ready,
+ "verifier_no_execution_receipt_proof_bound": closeout_ready,
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_verifier_invocation_lock_proof_closeout = {
+ "verifier_invocation_lock_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ ),
+ "source_frozen_envelope_verifier_handoff_closeout_id": source_closeout_id,
+ "source_verifier_invocation_lock_proof_id": lock_proof.get("lock_proof_id"),
+ "source_frozen_envelope_verifier_handoff_id": frozen_handoff.get(
+ "handoff_id"
+ ),
+ "source_execution_envelope_freeze_proof_closeout_id": (
+ source_handoff_closeout.get("execution_envelope_freeze_proof_closeout_id")
+ ),
+ "source_dry_run_execution_envelope_freeze_proof_id": freeze_proof.get(
+ "freeze_proof_id"
+ ),
+ "source_controlled_executor_quarantine_proof_closeout_id": (
+ source_freeze_closeout.get("controlled_executor_quarantine_proof_closeout_id")
+ ),
+ "source_controlled_executor_quarantine_proof_id": quarantine.get(
+ "quarantine_proof_id"
+ ),
+ "source_final_no_runner_execution_proof_closeout_id": (
+ source_quarantine_closeout.get("final_no_runner_execution_proof_closeout_id")
+ ),
+ "source_final_no_runner_execution_proof_id": final_proof.get("proof_id"),
+ "source_no_execution_receipt_handoff_closeout_id": (
+ handoff_receipt_closeout.get("no_execution_receipt_handoff_closeout_id")
+ ),
+ "required_command_shape_hash": freeze_proof.get("required_command_shape_hash"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof": (
+ closeout_ready
+ ),
+ "verifier_invocation_lock_proof_closeout_fields": lock_closeout_fields,
+ "verifier_invocation_lock_proof_closeout_field_count": len(
+ lock_closeout_fields
+ ),
+ "verifier_invocation_lock_proof_closeout_acceptance_gates": (
+ lock_closeout_acceptance_gates
+ ),
+ "verifier_invocation_lock_proof_closeout_acceptance_gate_count": len(
+ lock_closeout_acceptance_gates
+ ),
+ "verifier_no_execution_receipt_proof": verifier_no_execution_receipt_proof,
+ "verifier_no_execution_receipt_proof_count": 1,
+ "verifier_no_execution_receipt_proof_field_count": len(
+ verifier_no_execution_receipt_proof_fields
+ ),
+ "verifier_invocation_lock_proof": lock_proof,
+ "verifier_invocation_lock_proof_count": 1,
+ "verifier_invocation_lock_proof_field_count": int(
+ lock_proof.get("verifier_invocation_lock_proof_field_count") or 0
+ ),
+ "frozen_envelope_verifier_handoff_closeout": lock_closeout,
+ "frozen_envelope_verifier_handoff_closeout_count": 1,
+ "frozen_envelope_verifier_handoff": frozen_handoff,
+ "frozen_envelope_verifier_handoff_count": 1,
+ "dry_run_execution_envelope_freeze_proof": freeze_proof,
+ "dry_run_execution_envelope_freeze_proof_count": 1,
+ "execution_envelope_freeze_proof_closeout": source_handoff_closeout,
+ "execution_envelope_freeze_proof_closeout_count": 1,
+ "controlled_executor_quarantine_proof_closeout": source_freeze_closeout,
+ "controlled_executor_quarantine_proof_closeout_count": 1,
+ "controlled_executor_quarantine_proof": quarantine,
+ "controlled_executor_quarantine_proof_count": 1,
+ "final_no_runner_execution_proof_closeout": source_quarantine_closeout,
+ "final_no_runner_execution_proof_closeout_count": 1,
+ "final_no_runner_execution_proof": final_proof,
+ "final_no_runner_execution_proof_count": 1,
+ "no_execution_receipt_handoff_closeout": handoff_receipt_closeout,
+ "no_execution_receipt_handoff_closeout_count": 1,
+ "target_file": lock_closeout.get("target_file"),
+ "expected_sha256": lock_closeout.get("expected_sha256"),
+ "actual_sha256": lock_closeout.get("actual_sha256"),
+ "hash_matches": lock_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "verifier_invocation_lock_proof_closeout_only": True,
+ "verifier_no_execution_receipt_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "controlled_executor_quarantine_bound": True,
+ "executor_quarantine_enforced": True,
+ "execution_envelope_frozen": True,
+ "execution_envelope_mutation_allowed": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_verifier_invocation_lock_proof_closeout_contract = {
+ "mode": "controlled_dry_run_verifier_invocation_lock_proof_closeout_and_verifier_no_execution_receipt_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout"
+ ),
+ "source_frozen_envelope_verifier_handoff_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof": (
+ closeout_ready
+ ),
+ "verifier_invocation_locked": True,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_count": 1,
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_field_count": len(
+ lock_closeout_fields
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_acceptance_gate_count": len(
+ lock_closeout_acceptance_gates
+ ),
+ "verifier_no_execution_receipt_proof_count": 1,
+ "verifier_no_execution_receipt_proof_field_count": len(
+ verifier_no_execution_receipt_proof_fields
+ ),
+ "verifier_invocation_locked_count": 1,
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_INVOCATION_LOCK_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(lock_closeout_result.get("success")),
+ "generated_at": lock_closeout_result.get("generated_at"),
+ "source_policy": lock_closeout_result.get("policy"),
+ "stats": lock_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof": (
+ future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout": (
+ controlled_dry_run_verifier_invocation_lock_proof_closeout
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_contract": (
+ controlled_dry_run_verifier_invocation_lock_proof_closeout_contract
+ ),
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_checks": checks,
+ "source_controlled_dry_run_frozen_envelope_verifier_handoff_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract": (
+ lock_contract
+ ),
+ "source_controlled_dry_run_frozen_envelope_verifier_handoff_closeout": (
+ lock_closeout
+ ),
+ "source_database_apply_controlled_dry_run_verifier_invocation_lock_proof": (
+ future_lock
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future verifier no-execution receipt proof closeout.",
+ "Keep verifier invocation and verifier receipt persistence disabled until a later lane explicitly proves that boundary.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, verifier invocation, verifier receipt persistence, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the no-execution receipt proof and keep receipt persistence locked."""
+ receipt_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_receipt = (
+ receipt_closeout_result.get(
+ "future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ )
+ or {}
+ )
+ receipt_closeout = (
+ receipt_closeout_result.get(
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ )
+ or {}
+ )
+ receipt_contract = (
+ receipt_closeout_result.get(
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = receipt_closeout_result.get("summary") or {}
+ safety = receipt_closeout_result.get("safety") or {}
+ receipt_proof = receipt_closeout.get("verifier_no_execution_receipt_proof") or {}
+ lock_proof = receipt_closeout.get("verifier_invocation_lock_proof") or {}
+ previous_lock_closeout = (
+ receipt_closeout.get("frozen_envelope_verifier_handoff_closeout") or {}
+ )
+ rollback_binding = receipt_closeout.get("rollback_binding") or {}
+ verifier_binding = receipt_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = receipt_closeout.get(
+ "verifier_invocation_lock_proof_closeout_id"
+ )
+ source_receipt_id = receipt_proof.get("receipt_proof_id")
+ closeout_id = (
+ _db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout_id(
+ receipt_closeout_result
+ )
+ )
+ guard_id = f"{closeout_id}-verifier-receipt-persistence-guard-proof"
+ closeout_fields = [
+ "verifier_no_execution_receipt_proof_closeout_id",
+ "source_verifier_invocation_lock_proof_closeout_id",
+ "source_verifier_no_execution_receipt_proof_id",
+ "source_verifier_invocation_lock_proof_id",
+ "verifier_receipt_persistence_guard_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "verifier_receipt_persistence_locked",
+ "verifier_receipt_persisted",
+ "abort_conditions",
+ ]
+ acceptance_gates = [
+ "verifier_invocation_lock_proof_closeout_ready",
+ "source_chain_ids_match",
+ "verifier_no_execution_receipt_proof_ready",
+ "verifier_no_execution_receipt_proof_no_execute",
+ "verifier_receipt_persistence_guard_proof_bound",
+ "verifier_receipt_persistence_guard_proof_blocks_persistence",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_persistence_or_database_apply",
+ ]
+ persistence_guard_fields = [
+ "guard_proof_id",
+ "source_verifier_no_execution_receipt_proof_closeout_id",
+ "source_verifier_invocation_lock_proof_closeout_id",
+ "source_verifier_no_execution_receipt_proof_id",
+ "source_verifier_invocation_lock_proof_id",
+ "guard_mode",
+ "verifier_receipt_persistence_locked",
+ "verifier_receipt_persisted",
+ "verifier_receipt_persistence_allowed",
+ "endpoint_execution_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_verifier_invocation_lock_proof_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_verifier_no_execution_receipt_proof_missing",
+ "abort_if_verifier_no_execution_receipt_proof_reports_execution",
+ "abort_if_verifier_receipt_persistence_guard_missing",
+ "abort_if_verifier_receipt_persistence_is_allowed",
+ "abort_if_verifier_receipt_is_persisted",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_persistence_or_apply_material_is_present",
+ ]
+ persistence_guard_proof = {
+ "guard_proof_id": guard_id,
+ "source_verifier_no_execution_receipt_proof_closeout_id": closeout_id,
+ "source_verifier_invocation_lock_proof_closeout_id": source_closeout_id,
+ "source_verifier_no_execution_receipt_proof_id": source_receipt_id,
+ "source_verifier_invocation_lock_proof_id": lock_proof.get("lock_proof_id"),
+ "source_frozen_envelope_verifier_handoff_closeout_id": (
+ receipt_proof.get("source_frozen_envelope_verifier_handoff_closeout_id")
+ ),
+ "source_frozen_envelope_verifier_handoff_id": (
+ receipt_proof.get("source_frozen_envelope_verifier_handoff_id")
+ ),
+ "required_command_shape_hash": receipt_proof.get(
+ "required_command_shape_hash"
+ ),
+ "guard_status": "verifier_receipt_persistence_guard_proof_preview_ready",
+ "guard_mode": "verifier_receipt_persistence_guard_proof_preview_only",
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "verifier_receipt_present": False,
+ "verifier_receipt_required": False,
+ "persists_verifier_receipt": False,
+ "persistence_receipt_present": False,
+ "persistence_receipt_required": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "verifier_receipt_persistence_guard_proof_field_count": len(
+ persistence_guard_fields
+ ),
+ "verifier_receipt_persistence_guard_proof_fields": (
+ persistence_guard_fields
+ ),
+ }
+ receipt_closeout_ready = (
+ receipt_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_INVOCATION_LOCK_PROOF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(source_closeout_id)
+ and source_closeout_id
+ == future_receipt.get("verifier_invocation_lock_proof_closeout_id")
+ == receipt_proof.get("source_verifier_invocation_lock_proof_closeout_id")
+ == persistence_guard_proof.get(
+ "source_verifier_invocation_lock_proof_closeout_id"
+ )
+ and source_receipt_id
+ == future_receipt.get("verifier_no_execution_receipt_proof_id")
+ == persistence_guard_proof.get(
+ "source_verifier_no_execution_receipt_proof_id"
+ )
+ and lock_proof.get("lock_proof_id")
+ == receipt_proof.get("source_verifier_invocation_lock_proof_id")
+ == persistence_guard_proof.get(
+ "source_verifier_invocation_lock_proof_id"
+ )
+ and receipt_proof.get("required_command_shape_hash")
+ == lock_proof.get("required_command_shape_hash")
+ == persistence_guard_proof.get("required_command_shape_hash")
+ )
+ receipt_proof_ready = (
+ receipt_closeout_ready
+ and receipt_proof.get("receipt_status")
+ == "verifier_no_execution_receipt_proof_preview_ready"
+ and receipt_proof.get("receipt_proof_id")
+ == future_receipt.get("verifier_no_execution_receipt_proof_id")
+ and int(
+ receipt_proof.get("verifier_no_execution_receipt_proof_field_count")
+ or 0
+ )
+ == 12
+ and summary.get("verifier_no_execution_receipt_proof_count") == 1
+ )
+ receipt_proof_no_execute = (
+ receipt_proof.get("receipt_mode")
+ == "verifier_no_execution_receipt_proof_preview_only"
+ and receipt_proof.get("verifier_invocation_locked") is True
+ and receipt_proof.get("verifier_invocation_allowed") is False
+ and receipt_proof.get("verifier_invoked") is False
+ and receipt_proof.get("verifier_receipt_present") is False
+ and receipt_proof.get("verifier_receipt_required") is False
+ and receipt_proof.get("dry_run_executor_invoked") is False
+ and receipt_proof.get("runner_invocation_performed") is False
+ and receipt_proof.get("endpoint_executed") is False
+ and receipt_proof.get("sql_executed") is False
+ and receipt_proof.get("database_written") is False
+ and receipt_proof.get("endpoint_execution_allowed") is False
+ and receipt_proof.get("sql_execution_allowed") is False
+ and receipt_proof.get("database_write_allowed") is False
+ and receipt_proof.get("database_apply_authorized") is False
+ and receipt_proof.get("executes_database_apply") is False
+ and receipt_proof.get("executes_endpoint") is False
+ and receipt_proof.get("executes_sql") is False
+ and receipt_proof.get("writes_database") is False
+ and receipt_proof.get("stdout_included") is False
+ and receipt_proof.get("stderr_included") is False
+ )
+ persistence_guard_bound = (
+ receipt_proof_ready
+ and bool(persistence_guard_proof.get("guard_proof_id"))
+ and persistence_guard_proof.get(
+ "source_verifier_no_execution_receipt_proof_closeout_id"
+ )
+ == closeout_id
+ and persistence_guard_proof.get(
+ "source_verifier_invocation_lock_proof_closeout_id"
+ )
+ == source_closeout_id
+ and persistence_guard_proof.get(
+ "source_verifier_no_execution_receipt_proof_id"
+ )
+ == source_receipt_id
+ and persistence_guard_proof.get("required_command_shape_hash")
+ == receipt_proof.get("required_command_shape_hash")
+ and int(
+ persistence_guard_proof.get(
+ "verifier_receipt_persistence_guard_proof_field_count"
+ )
+ or 0
+ )
+ == len(persistence_guard_fields)
+ )
+ persistence_guard_blocks_persistence = (
+ persistence_guard_proof.get("guard_mode")
+ == "verifier_receipt_persistence_guard_proof_preview_only"
+ and persistence_guard_proof.get("verifier_receipt_persistence_locked")
+ is True
+ and persistence_guard_proof.get("verifier_receipt_persistence_allowed")
+ is False
+ and persistence_guard_proof.get("verifier_receipt_persisted") is False
+ and persistence_guard_proof.get("persists_verifier_receipt") is False
+ and persistence_guard_proof.get("persistence_receipt_present") is False
+ and persistence_guard_proof.get("verifier_invocation_allowed") is False
+ and persistence_guard_proof.get("verifier_invoked") is False
+ and persistence_guard_proof.get("dry_run_executor_invoked") is False
+ and persistence_guard_proof.get("runner_invocation_performed") is False
+ and persistence_guard_proof.get("endpoint_executed") is False
+ and persistence_guard_proof.get("sql_executed") is False
+ and persistence_guard_proof.get("database_written") is False
+ and persistence_guard_proof.get("endpoint_execution_allowed") is False
+ and persistence_guard_proof.get("sql_execution_allowed") is False
+ and persistence_guard_proof.get("database_write_allowed") is False
+ and persistence_guard_proof.get("database_apply_authorized") is False
+ and persistence_guard_proof.get("executes_database_apply") is False
+ and persistence_guard_proof.get("executes_endpoint") is False
+ and persistence_guard_proof.get("executes_sql") is False
+ and persistence_guard_proof.get("writes_database") is False
+ and persistence_guard_proof.get("stdout_included") is False
+ and persistence_guard_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ receipt_closeout.get("verifier_invocation_lock_proof_closeout_only") is True
+ and receipt_closeout.get("verifier_no_execution_receipt_proof_only")
+ is True
+ and previous_lock_closeout.get("frozen_envelope_verifier_handoff_closeout_only")
+ is True
+ and receipt_proof_no_execute
+ )
+ target_hash_locked = (
+ receipt_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(receipt_closeout.get("expected_sha256"))
+ and bool(receipt_closeout.get("actual_sha256"))
+ and receipt_closeout.get("expected_sha256")
+ == receipt_closeout.get("actual_sha256")
+ and receipt_closeout.get("hash_matches") is True
+ and receipt_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ contract_blocks_persistence_and_apply = (
+ receipt_contract.get(
+ "permits_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ )
+ is True
+ and receipt_contract.get("persists_verifier_receipt") is False
+ and receipt_contract.get("executes_database_apply") is False
+ and receipt_contract.get("database_apply_authorized") is False
+ and receipt_contract.get("writes_database") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("verifier_invoked_count", 0) == 0
+ and summary.get("verifier_receipt_present_count", 0) == 0
+ and safety.get("persists_verifier_receipt") is False
+ )
+ checks = [
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "verifier_invocation_lock_proof_closeout_ready",
+ receipt_closeout_ready,
+ {
+ "result": receipt_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_count"
+ ),
+ },
+ "wait_for_verifier_invocation_lock_proof_closeout",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "source_closeout_id": source_closeout_id,
+ "source_receipt_id": source_receipt_id,
+ "lock_proof_id": lock_proof.get("lock_proof_id"),
+ },
+ "wait_for_source_chain_alignment",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "verifier_no_execution_receipt_proof_ready",
+ receipt_proof_ready,
+ {
+ "receipt_proof_id": source_receipt_id,
+ "receipt_status": receipt_proof.get("receipt_status"),
+ "field_count": receipt_proof.get(
+ "verifier_no_execution_receipt_proof_field_count"
+ ),
+ },
+ "wait_for_verifier_no_execution_receipt_proof",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "verifier_no_execution_receipt_proof_no_execute",
+ receipt_proof_no_execute,
+ {
+ "receipt_mode": receipt_proof.get("receipt_mode"),
+ "verifier_invoked": receipt_proof.get("verifier_invoked"),
+ "database_written": receipt_proof.get("database_written"),
+ },
+ "abort_if_verifier_no_execution_receipt_proof_reports_execution",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "verifier_receipt_persistence_guard_proof_bound",
+ persistence_guard_bound,
+ {
+ "guard_proof_id": persistence_guard_proof.get("guard_proof_id"),
+ "source_receipt_id": persistence_guard_proof.get(
+ "source_verifier_no_execution_receipt_proof_id"
+ ),
+ "field_count": persistence_guard_proof.get(
+ "verifier_receipt_persistence_guard_proof_field_count"
+ ),
+ },
+ "wait_for_verifier_receipt_persistence_guard_proof",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "verifier_receipt_persistence_guard_proof_blocks_persistence",
+ persistence_guard_blocks_persistence,
+ {
+ "guard_mode": persistence_guard_proof.get("guard_mode"),
+ "persistence_allowed": persistence_guard_proof.get(
+ "verifier_receipt_persistence_allowed"
+ ),
+ "persisted": persistence_guard_proof.get(
+ "verifier_receipt_persisted"
+ ),
+ },
+ "abort_if_verifier_receipt_persistence_guard_allows_persistence",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "verifier_invocation_lock_proof_closeout_only": (
+ receipt_closeout.get(
+ "verifier_invocation_lock_proof_closeout_only"
+ )
+ ),
+ "verifier_no_execution_receipt_proof_only": (
+ receipt_closeout.get("verifier_no_execution_receipt_proof_only")
+ ),
+ },
+ "wait_for_previous_closeouts_carry_forward",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": receipt_closeout.get("target_file"),
+ "hash_matches": receipt_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ receipt_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(receipt_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "verifier_invocation_lock_proof_closeout_contract_blocks_persistence_and_database_apply",
+ contract_blocks_persistence_and_apply,
+ {
+ "permits_future_no_execution_receipt_proof": receipt_contract.get(
+ "permits_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ ),
+ "persists_verifier_receipt": receipt_contract.get(
+ "persists_verifier_receipt"
+ ),
+ "database_apply_authorized": receipt_contract.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_source_contract_allows_persistence_or_database_apply",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "preview_has_no_side_effects_no_persistence_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "verifier_invoked_count": summary.get("verifier_invoked_count", 0),
+ "verifier_receipt_present_count": summary.get(
+ "verifier_receipt_present_count", 0
+ ),
+ },
+ "abort_on_preview_persistence_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ receipt_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": receipt_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_NO_EXECUTION_RECEIPT_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_INVOCATION_LOCK_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof = {
+ "verifier_no_execution_receipt_proof_closeout_id": closeout_id,
+ "verifier_receipt_persistence_guard_proof_id": guard_id,
+ "source_verifier_invocation_lock_proof_closeout_id": source_closeout_id,
+ "source_verifier_no_execution_receipt_proof_id": source_receipt_id,
+ "source_verifier_invocation_lock_proof_id": lock_proof.get("lock_proof_id"),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout": (
+ closeout_ready
+ ),
+ "verifier_no_execution_receipt_proof_closeout_ready": closeout_ready,
+ "verifier_invocation_lock_proof_closeout_ready": receipt_closeout_ready,
+ "verifier_no_execution_receipt_proof_ready": receipt_proof_ready,
+ "verifier_receipt_persistence_guard_proof_bound": closeout_ready,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_verifier_no_execution_receipt_proof_closeout = {
+ "verifier_no_execution_receipt_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ ),
+ "source_verifier_invocation_lock_proof_closeout_id": source_closeout_id,
+ "source_verifier_no_execution_receipt_proof_id": source_receipt_id,
+ "source_verifier_invocation_lock_proof_id": lock_proof.get("lock_proof_id"),
+ "required_command_shape_hash": receipt_proof.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof": (
+ closeout_ready
+ ),
+ "verifier_no_execution_receipt_proof_closeout_fields": closeout_fields,
+ "verifier_no_execution_receipt_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "verifier_no_execution_receipt_proof_closeout_acceptance_gates": (
+ acceptance_gates
+ ),
+ "verifier_no_execution_receipt_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "verifier_receipt_persistence_guard_proof": persistence_guard_proof,
+ "verifier_receipt_persistence_guard_proof_count": 1,
+ "verifier_receipt_persistence_guard_proof_field_count": len(
+ persistence_guard_fields
+ ),
+ "verifier_no_execution_receipt_proof": receipt_proof,
+ "verifier_no_execution_receipt_proof_count": 1,
+ "verifier_invocation_lock_proof_closeout": receipt_closeout,
+ "verifier_invocation_lock_proof_closeout_count": 1,
+ "verifier_invocation_lock_proof": lock_proof,
+ "verifier_invocation_lock_proof_count": 1,
+ "target_file": receipt_closeout.get("target_file"),
+ "expected_sha256": receipt_closeout.get("expected_sha256"),
+ "actual_sha256": receipt_closeout.get("actual_sha256"),
+ "hash_matches": receipt_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "verifier_no_execution_receipt_proof_closeout_only": True,
+ "verifier_receipt_persistence_guard_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract = {
+ "mode": "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_and_verifier_receipt_persistence_guard_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout"
+ ),
+ "source_verifier_invocation_lock_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof": (
+ closeout_ready
+ ),
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_count": 1,
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "verifier_receipt_persistence_guard_proof_count": 1,
+ "verifier_receipt_persistence_guard_proof_field_count": len(
+ persistence_guard_fields
+ ),
+ "verifier_receipt_persistence_locked_count": 1,
+ "verifier_receipt_persistence_allowed_count": 0,
+ "verifier_receipt_persisted_count": 0,
+ "persists_verifier_receipt_count": 0,
+ "verifier_invocation_locked_count": 1,
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_NO_EXECUTION_RECEIPT_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(receipt_closeout_result.get("success")),
+ "generated_at": receipt_closeout_result.get("generated_at"),
+ "source_policy": receipt_closeout_result.get("policy"),
+ "stats": receipt_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof": (
+ future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout": (
+ controlled_dry_run_verifier_no_execution_receipt_proof_closeout
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract": (
+ controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract
+ ),
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_checks": (
+ checks
+ ),
+ "source_controlled_dry_run_verifier_invocation_lock_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_verifier_invocation_lock_proof_closeout_contract": (
+ receipt_contract
+ ),
+ "source_controlled_dry_run_verifier_invocation_lock_proof_closeout": (
+ receipt_closeout
+ ),
+ "source_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof": (
+ future_receipt
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future verifier receipt persistence guard proof closeout.",
+ "Keep verifier receipt persistence disabled until a later persistence guard closeout proves the storage boundary.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, verifier invocation, verifier receipt persistence, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the persistence guard proof and prove storage remains locked."""
+ storage_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_guard = (
+ storage_closeout_result.get(
+ "future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ )
+ or {}
+ )
+ guard_closeout = (
+ storage_closeout_result.get(
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ )
+ or {}
+ )
+ guard_contract = (
+ storage_closeout_result.get(
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = storage_closeout_result.get("summary") or {}
+ safety = storage_closeout_result.get("safety") or {}
+ guard_proof = guard_closeout.get("verifier_receipt_persistence_guard_proof") or {}
+ receipt_proof = guard_closeout.get("verifier_no_execution_receipt_proof") or {}
+ previous_receipt_closeout = (
+ guard_closeout.get("verifier_invocation_lock_proof_closeout") or {}
+ )
+ rollback_binding = guard_closeout.get("rollback_binding") or {}
+ verifier_binding = guard_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = guard_closeout.get(
+ "verifier_no_execution_receipt_proof_closeout_id"
+ )
+ source_guard_id = guard_proof.get("guard_proof_id")
+ source_receipt_id = guard_proof.get("source_verifier_no_execution_receipt_proof_id")
+ closeout_id = (
+ _db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_id(
+ storage_closeout_result
+ )
+ )
+ storage_boundary_id = f"{closeout_id}-receipt-persistence-storage-boundary-proof"
+ closeout_fields = [
+ "verifier_receipt_persistence_guard_proof_closeout_id",
+ "source_verifier_no_execution_receipt_proof_closeout_id",
+ "source_verifier_receipt_persistence_guard_proof_id",
+ "source_verifier_no_execution_receipt_proof_id",
+ "receipt_persistence_storage_boundary_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "receipt_persistence_storage_boundary_locked",
+ "receipt_persistence_storage_written",
+ "abort_conditions",
+ ]
+ acceptance_gates = [
+ "verifier_no_execution_receipt_proof_closeout_ready",
+ "source_chain_ids_match",
+ "verifier_receipt_persistence_guard_proof_ready",
+ "verifier_receipt_persistence_guard_proof_no_persistence",
+ "receipt_persistence_storage_boundary_proof_bound",
+ "receipt_persistence_storage_boundary_proof_blocks_storage",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_storage_persistence_or_database_apply",
+ ]
+ storage_boundary_fields = [
+ "storage_boundary_proof_id",
+ "source_verifier_receipt_persistence_guard_proof_closeout_id",
+ "source_verifier_no_execution_receipt_proof_closeout_id",
+ "source_verifier_receipt_persistence_guard_proof_id",
+ "source_verifier_no_execution_receipt_proof_id",
+ "storage_boundary_mode",
+ "receipt_persistence_storage_boundary_locked",
+ "receipt_persistence_storage_write_allowed",
+ "receipt_persistence_storage_written",
+ "verifier_receipt_persistence_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_verifier_no_execution_receipt_proof_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_verifier_receipt_persistence_guard_proof_missing",
+ "abort_if_verifier_receipt_persistence_guard_allows_persistence",
+ "abort_if_receipt_persistence_storage_boundary_missing",
+ "abort_if_receipt_persistence_storage_write_is_allowed",
+ "abort_if_receipt_persistence_storage_written",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_storage_persistence_or_apply_material_is_present",
+ ]
+ storage_boundary_proof = {
+ "storage_boundary_proof_id": storage_boundary_id,
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": closeout_id,
+ "source_verifier_no_execution_receipt_proof_closeout_id": source_closeout_id,
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "source_verifier_no_execution_receipt_proof_id": source_receipt_id,
+ "source_verifier_invocation_lock_proof_closeout_id": (
+ guard_proof.get("source_verifier_invocation_lock_proof_closeout_id")
+ ),
+ "source_verifier_invocation_lock_proof_id": (
+ guard_proof.get("source_verifier_invocation_lock_proof_id")
+ ),
+ "required_command_shape_hash": guard_proof.get(
+ "required_command_shape_hash"
+ ),
+ "storage_boundary_status": "receipt_persistence_storage_boundary_proof_preview_ready",
+ "storage_boundary_mode": "receipt_persistence_storage_boundary_proof_preview_only",
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "receipt_persistence_storage_receipt_present": False,
+ "receipt_persistence_storage_receipt_required": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_receipt_present": False,
+ "verifier_receipt_required": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "receipt_persistence_storage_boundary_proof_field_count": len(
+ storage_boundary_fields
+ ),
+ "receipt_persistence_storage_boundary_proof_fields": (
+ storage_boundary_fields
+ ),
+ }
+ guard_closeout_ready = (
+ storage_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_NO_EXECUTION_RECEIPT_PROOF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(source_closeout_id)
+ and source_closeout_id
+ == future_guard.get("verifier_no_execution_receipt_proof_closeout_id")
+ == guard_proof.get("source_verifier_no_execution_receipt_proof_closeout_id")
+ == storage_boundary_proof.get(
+ "source_verifier_no_execution_receipt_proof_closeout_id"
+ )
+ and source_guard_id
+ == future_guard.get("verifier_receipt_persistence_guard_proof_id")
+ == storage_boundary_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_id"
+ )
+ and source_receipt_id
+ == guard_proof.get("source_verifier_no_execution_receipt_proof_id")
+ == receipt_proof.get("receipt_proof_id")
+ == storage_boundary_proof.get(
+ "source_verifier_no_execution_receipt_proof_id"
+ )
+ and guard_proof.get("required_command_shape_hash")
+ == receipt_proof.get("required_command_shape_hash")
+ == storage_boundary_proof.get("required_command_shape_hash")
+ )
+ guard_proof_ready = (
+ guard_closeout_ready
+ and guard_proof.get("guard_status")
+ == "verifier_receipt_persistence_guard_proof_preview_ready"
+ and guard_proof.get("guard_proof_id")
+ == future_guard.get("verifier_receipt_persistence_guard_proof_id")
+ and int(
+ guard_proof.get(
+ "verifier_receipt_persistence_guard_proof_field_count"
+ )
+ or 0
+ )
+ == 12
+ and summary.get("verifier_receipt_persistence_guard_proof_count") == 1
+ )
+ guard_proof_no_persistence = (
+ guard_proof.get("guard_mode")
+ == "verifier_receipt_persistence_guard_proof_preview_only"
+ and guard_proof.get("verifier_receipt_persistence_locked") is True
+ and guard_proof.get("verifier_receipt_persistence_allowed") is False
+ and guard_proof.get("verifier_receipt_persisted") is False
+ and guard_proof.get("persists_verifier_receipt") is False
+ and guard_proof.get("persistence_receipt_present") is False
+ and guard_proof.get("verifier_invocation_allowed") is False
+ and guard_proof.get("verifier_invoked") is False
+ and guard_proof.get("dry_run_executor_invoked") is False
+ and guard_proof.get("runner_invocation_performed") is False
+ and guard_proof.get("endpoint_executed") is False
+ and guard_proof.get("sql_executed") is False
+ and guard_proof.get("database_written") is False
+ and guard_proof.get("endpoint_execution_allowed") is False
+ and guard_proof.get("sql_execution_allowed") is False
+ and guard_proof.get("database_write_allowed") is False
+ and guard_proof.get("database_apply_authorized") is False
+ and guard_proof.get("executes_database_apply") is False
+ and guard_proof.get("executes_endpoint") is False
+ and guard_proof.get("executes_sql") is False
+ and guard_proof.get("writes_database") is False
+ and guard_proof.get("stdout_included") is False
+ and guard_proof.get("stderr_included") is False
+ )
+ storage_boundary_bound = (
+ guard_proof_ready
+ and bool(storage_boundary_proof.get("storage_boundary_proof_id"))
+ and storage_boundary_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ == closeout_id
+ and storage_boundary_proof.get(
+ "source_verifier_no_execution_receipt_proof_closeout_id"
+ )
+ == source_closeout_id
+ and storage_boundary_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_id"
+ )
+ == source_guard_id
+ and storage_boundary_proof.get("required_command_shape_hash")
+ == guard_proof.get("required_command_shape_hash")
+ and int(
+ storage_boundary_proof.get(
+ "receipt_persistence_storage_boundary_proof_field_count"
+ )
+ or 0
+ )
+ == len(storage_boundary_fields)
+ )
+ storage_boundary_blocks_storage = (
+ storage_boundary_proof.get("storage_boundary_mode")
+ == "receipt_persistence_storage_boundary_proof_preview_only"
+ and storage_boundary_proof.get("receipt_persistence_storage_boundary_locked")
+ is True
+ and storage_boundary_proof.get("receipt_persistence_storage_write_allowed")
+ is False
+ and storage_boundary_proof.get("receipt_persistence_storage_written")
+ is False
+ and storage_boundary_proof.get("verifier_receipt_persistence_allowed")
+ is False
+ and storage_boundary_proof.get("verifier_receipt_persisted") is False
+ and storage_boundary_proof.get("persists_verifier_receipt") is False
+ and storage_boundary_proof.get("verifier_invoked") is False
+ and storage_boundary_proof.get("dry_run_executor_invoked") is False
+ and storage_boundary_proof.get("runner_invocation_performed") is False
+ and storage_boundary_proof.get("endpoint_executed") is False
+ and storage_boundary_proof.get("sql_executed") is False
+ and storage_boundary_proof.get("database_written") is False
+ and storage_boundary_proof.get("endpoint_execution_allowed") is False
+ and storage_boundary_proof.get("sql_execution_allowed") is False
+ and storage_boundary_proof.get("database_write_allowed") is False
+ and storage_boundary_proof.get("database_apply_authorized") is False
+ and storage_boundary_proof.get("executes_database_apply") is False
+ and storage_boundary_proof.get("executes_endpoint") is False
+ and storage_boundary_proof.get("executes_sql") is False
+ and storage_boundary_proof.get("writes_database") is False
+ and storage_boundary_proof.get("stdout_included") is False
+ and storage_boundary_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ guard_closeout.get("verifier_no_execution_receipt_proof_closeout_only")
+ is True
+ and guard_closeout.get("verifier_receipt_persistence_guard_proof_only")
+ is True
+ and previous_receipt_closeout.get(
+ "verifier_invocation_lock_proof_closeout_only"
+ )
+ is True
+ and guard_proof_no_persistence
+ )
+ target_hash_locked = (
+ guard_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(guard_closeout.get("expected_sha256"))
+ and bool(guard_closeout.get("actual_sha256"))
+ and guard_closeout.get("expected_sha256")
+ == guard_closeout.get("actual_sha256")
+ and guard_closeout.get("hash_matches") is True
+ and guard_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ contract_blocks_storage_persistence_and_apply = (
+ guard_contract.get(
+ "permits_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ )
+ is True
+ and guard_contract.get("persists_verifier_receipt") is False
+ and guard_contract.get("executes_database_apply") is False
+ and guard_contract.get("database_apply_authorized") is False
+ and guard_contract.get("writes_database") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("persists_verifier_receipt_count", 0) == 0
+ and summary.get("verifier_receipt_persisted_count", 0) == 0
+ and safety.get("persists_verifier_receipt") is False
+ )
+ checks = [
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "verifier_no_execution_receipt_proof_closeout_ready",
+ guard_closeout_ready,
+ {
+ "result": storage_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_count"
+ ),
+ },
+ "wait_for_verifier_no_execution_receipt_proof_closeout",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "source_closeout_id": source_closeout_id,
+ "source_guard_id": source_guard_id,
+ "source_receipt_id": source_receipt_id,
+ },
+ "wait_for_source_chain_alignment",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "verifier_receipt_persistence_guard_proof_ready",
+ guard_proof_ready,
+ {
+ "guard_proof_id": source_guard_id,
+ "guard_status": guard_proof.get("guard_status"),
+ "field_count": guard_proof.get(
+ "verifier_receipt_persistence_guard_proof_field_count"
+ ),
+ },
+ "wait_for_verifier_receipt_persistence_guard_proof",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "verifier_receipt_persistence_guard_proof_no_persistence",
+ guard_proof_no_persistence,
+ {
+ "guard_mode": guard_proof.get("guard_mode"),
+ "persistence_allowed": guard_proof.get(
+ "verifier_receipt_persistence_allowed"
+ ),
+ "persisted": guard_proof.get("verifier_receipt_persisted"),
+ },
+ "abort_if_verifier_receipt_persistence_guard_reports_persistence",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "receipt_persistence_storage_boundary_proof_bound",
+ storage_boundary_bound,
+ {
+ "storage_boundary_proof_id": storage_boundary_proof.get(
+ "storage_boundary_proof_id"
+ ),
+ "source_guard_id": storage_boundary_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_id"
+ ),
+ "field_count": storage_boundary_proof.get(
+ "receipt_persistence_storage_boundary_proof_field_count"
+ ),
+ },
+ "wait_for_receipt_persistence_storage_boundary_proof",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "receipt_persistence_storage_boundary_proof_blocks_storage",
+ storage_boundary_blocks_storage,
+ {
+ "storage_boundary_mode": storage_boundary_proof.get(
+ "storage_boundary_mode"
+ ),
+ "storage_write_allowed": storage_boundary_proof.get(
+ "receipt_persistence_storage_write_allowed"
+ ),
+ "storage_written": storage_boundary_proof.get(
+ "receipt_persistence_storage_written"
+ ),
+ },
+ "abort_if_receipt_persistence_storage_boundary_allows_storage",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "verifier_no_execution_receipt_proof_closeout_only": (
+ guard_closeout.get(
+ "verifier_no_execution_receipt_proof_closeout_only"
+ )
+ ),
+ "verifier_receipt_persistence_guard_proof_only": (
+ guard_closeout.get(
+ "verifier_receipt_persistence_guard_proof_only"
+ )
+ ),
+ },
+ "wait_for_previous_closeouts_carry_forward",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": guard_closeout.get("target_file"),
+ "hash_matches": guard_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(guard_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(guard_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "verifier_no_execution_receipt_proof_closeout_contract_blocks_storage_persistence_and_database_apply",
+ contract_blocks_storage_persistence_and_apply,
+ {
+ "permits_future_guard_proof": guard_contract.get(
+ "permits_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ ),
+ "persists_verifier_receipt": guard_contract.get(
+ "persists_verifier_receipt"
+ ),
+ "database_apply_authorized": guard_contract.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_source_contract_allows_storage_persistence_or_database_apply",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "preview_has_no_side_effects_no_storage_no_persistence_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "persists_verifier_receipt_count": summary.get(
+ "persists_verifier_receipt_count", 0
+ ),
+ },
+ "abort_on_preview_storage_persistence_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ guard_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": guard_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_RECEIPT_PERSISTENCE_GUARD_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_NO_EXECUTION_RECEIPT_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof = {
+ "verifier_receipt_persistence_guard_proof_closeout_id": closeout_id,
+ "receipt_persistence_storage_boundary_proof_id": storage_boundary_id,
+ "source_verifier_no_execution_receipt_proof_closeout_id": source_closeout_id,
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "source_verifier_no_execution_receipt_proof_id": source_receipt_id,
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout": (
+ closeout_ready
+ ),
+ "verifier_receipt_persistence_guard_proof_closeout_ready": closeout_ready,
+ "verifier_no_execution_receipt_proof_closeout_ready": guard_closeout_ready,
+ "verifier_receipt_persistence_guard_proof_ready": guard_proof_ready,
+ "receipt_persistence_storage_boundary_proof_bound": closeout_ready,
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout = {
+ "verifier_receipt_persistence_guard_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ ),
+ "source_verifier_no_execution_receipt_proof_closeout_id": source_closeout_id,
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "source_verifier_no_execution_receipt_proof_id": source_receipt_id,
+ "required_command_shape_hash": guard_proof.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof": (
+ closeout_ready
+ ),
+ "verifier_receipt_persistence_guard_proof_closeout_fields": closeout_fields,
+ "verifier_receipt_persistence_guard_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "verifier_receipt_persistence_guard_proof_closeout_acceptance_gates": (
+ acceptance_gates
+ ),
+ "verifier_receipt_persistence_guard_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "receipt_persistence_storage_boundary_proof": storage_boundary_proof,
+ "receipt_persistence_storage_boundary_proof_count": 1,
+ "receipt_persistence_storage_boundary_proof_field_count": len(
+ storage_boundary_fields
+ ),
+ "verifier_receipt_persistence_guard_proof": guard_proof,
+ "verifier_receipt_persistence_guard_proof_count": 1,
+ "verifier_no_execution_receipt_proof_closeout": guard_closeout,
+ "verifier_no_execution_receipt_proof_closeout_count": 1,
+ "verifier_no_execution_receipt_proof": receipt_proof,
+ "verifier_no_execution_receipt_proof_count": 1,
+ "target_file": guard_closeout.get("target_file"),
+ "expected_sha256": guard_closeout.get("expected_sha256"),
+ "actual_sha256": guard_closeout.get("actual_sha256"),
+ "hash_matches": guard_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "verifier_receipt_persistence_guard_proof_closeout_only": True,
+ "receipt_persistence_storage_boundary_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract = {
+ "mode": "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_and_receipt_persistence_storage_boundary_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout"
+ ),
+ "source_verifier_no_execution_receipt_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof": (
+ closeout_ready
+ ),
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_count": 1,
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "receipt_persistence_storage_boundary_proof_count": 1,
+ "receipt_persistence_storage_boundary_proof_field_count": len(
+ storage_boundary_fields
+ ),
+ "receipt_persistence_storage_boundary_locked_count": 1,
+ "receipt_persistence_storage_write_allowed_count": 0,
+ "receipt_persistence_storage_written_count": 0,
+ "verifier_receipt_persistence_allowed_count": 0,
+ "verifier_receipt_persisted_count": 0,
+ "persists_verifier_receipt_count": 0,
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_RECEIPT_PERSISTENCE_GUARD_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(storage_closeout_result.get("success")),
+ "generated_at": storage_closeout_result.get("generated_at"),
+ "source_policy": storage_closeout_result.get("policy"),
+ "stats": storage_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof": (
+ future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout": (
+ controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract": (
+ controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract
+ ),
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_checks": (
+ checks
+ ),
+ "source_controlled_dry_run_verifier_no_execution_receipt_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract": (
+ guard_contract
+ ),
+ "source_controlled_dry_run_verifier_no_execution_receipt_proof_closeout": (
+ guard_closeout
+ ),
+ "source_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof": (
+ future_guard
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future receipt persistence storage boundary proof closeout.",
+ "Keep receipt persistence storage disabled until a later storage boundary closeout proves the write boundary.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, verifier invocation, verifier receipt persistence, receipt storage, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the storage boundary proof and add a no-write ledger proof."""
+ ledger_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_storage = (
+ ledger_closeout_result.get(
+ "future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ )
+ or {}
+ )
+ storage_closeout = (
+ ledger_closeout_result.get(
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ )
+ or {}
+ )
+ storage_contract = (
+ ledger_closeout_result.get(
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = ledger_closeout_result.get("summary") or {}
+ safety = ledger_closeout_result.get("safety") or {}
+ storage_proof = (
+ storage_closeout.get("receipt_persistence_storage_boundary_proof") or {}
+ )
+ guard_proof = (
+ storage_closeout.get("verifier_receipt_persistence_guard_proof") or {}
+ )
+ previous_guard_closeout = (
+ storage_closeout.get("verifier_no_execution_receipt_proof_closeout") or {}
+ )
+ rollback_binding = storage_closeout.get("rollback_binding") or {}
+ verifier_binding = storage_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = storage_closeout.get(
+ "verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ source_storage_id = storage_proof.get("storage_boundary_proof_id")
+ source_guard_id = storage_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_id"
+ )
+ closeout_id = (
+ _db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_id(
+ ledger_closeout_result
+ )
+ )
+ ledger_id = f"{closeout_id}-storage-boundary-no-write-ledger-proof"
+ closeout_fields = [
+ "receipt_persistence_storage_boundary_proof_closeout_id",
+ "source_verifier_receipt_persistence_guard_proof_closeout_id",
+ "source_receipt_persistence_storage_boundary_proof_id",
+ "source_verifier_receipt_persistence_guard_proof_id",
+ "storage_boundary_no_write_ledger_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "storage_boundary_write_locked",
+ "storage_boundary_written",
+ "abort_conditions",
+ ]
+ acceptance_gates = [
+ "verifier_receipt_persistence_guard_proof_closeout_ready",
+ "source_chain_ids_match",
+ "receipt_persistence_storage_boundary_proof_ready",
+ "receipt_persistence_storage_boundary_proof_no_write",
+ "storage_boundary_no_write_ledger_proof_bound",
+ "storage_boundary_no_write_ledger_proof_blocks_write",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_ledger_storage_persistence_or_database_apply",
+ ]
+ no_write_ledger_fields = [
+ "ledger_proof_id",
+ "source_receipt_persistence_storage_boundary_proof_closeout_id",
+ "source_verifier_receipt_persistence_guard_proof_closeout_id",
+ "source_receipt_persistence_storage_boundary_proof_id",
+ "source_verifier_receipt_persistence_guard_proof_id",
+ "ledger_mode",
+ "storage_boundary_write_locked",
+ "storage_boundary_write_allowed",
+ "storage_boundary_written",
+ "ledger_write_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_verifier_receipt_persistence_guard_proof_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_receipt_persistence_storage_boundary_proof_missing",
+ "abort_if_receipt_persistence_storage_boundary_allows_write",
+ "abort_if_storage_boundary_no_write_ledger_missing",
+ "abort_if_storage_boundary_no_write_ledger_allows_write",
+ "abort_if_storage_boundary_or_ledger_written",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_ledger_storage_persistence_or_apply_material_is_present",
+ ]
+ no_write_ledger_proof = {
+ "ledger_proof_id": ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": closeout_id,
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": source_storage_id,
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "source_verifier_no_execution_receipt_proof_closeout_id": (
+ storage_proof.get("source_verifier_no_execution_receipt_proof_closeout_id")
+ ),
+ "source_verifier_no_execution_receipt_proof_id": (
+ storage_proof.get("source_verifier_no_execution_receipt_proof_id")
+ ),
+ "required_command_shape_hash": storage_proof.get(
+ "required_command_shape_hash"
+ ),
+ "ledger_status": "storage_boundary_no_write_ledger_proof_preview_ready",
+ "ledger_mode": "storage_boundary_no_write_ledger_proof_preview_only",
+ "storage_boundary_write_locked": True,
+ "storage_boundary_write_allowed": False,
+ "storage_boundary_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "ledger_receipt_present": False,
+ "ledger_receipt_required": False,
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_receipt_present": False,
+ "verifier_receipt_required": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "storage_boundary_no_write_ledger_proof_field_count": len(
+ no_write_ledger_fields
+ ),
+ "storage_boundary_no_write_ledger_proof_fields": no_write_ledger_fields,
+ }
+ storage_closeout_ready = (
+ ledger_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_RECEIPT_PERSISTENCE_GUARD_PROOF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(source_closeout_id)
+ and source_closeout_id
+ == future_storage.get("verifier_receipt_persistence_guard_proof_closeout_id")
+ == storage_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ == no_write_ledger_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ and source_storage_id
+ == future_storage.get("receipt_persistence_storage_boundary_proof_id")
+ == no_write_ledger_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_id"
+ )
+ and source_guard_id
+ == storage_proof.get("source_verifier_receipt_persistence_guard_proof_id")
+ == guard_proof.get("guard_proof_id")
+ == no_write_ledger_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_id"
+ )
+ and storage_proof.get("required_command_shape_hash")
+ == guard_proof.get("required_command_shape_hash")
+ == no_write_ledger_proof.get("required_command_shape_hash")
+ )
+ storage_boundary_proof_ready = (
+ storage_closeout_ready
+ and storage_proof.get("storage_boundary_status")
+ == "receipt_persistence_storage_boundary_proof_preview_ready"
+ and storage_proof.get("storage_boundary_proof_id")
+ == future_storage.get("receipt_persistence_storage_boundary_proof_id")
+ and int(
+ storage_proof.get(
+ "receipt_persistence_storage_boundary_proof_field_count"
+ )
+ or 0
+ )
+ == 12
+ and summary.get("receipt_persistence_storage_boundary_proof_count") == 1
+ )
+ storage_boundary_proof_no_write = (
+ storage_proof.get("storage_boundary_mode")
+ == "receipt_persistence_storage_boundary_proof_preview_only"
+ and storage_proof.get("receipt_persistence_storage_boundary_locked")
+ is True
+ and storage_proof.get("receipt_persistence_storage_write_allowed") is False
+ and storage_proof.get("receipt_persistence_storage_written") is False
+ and storage_proof.get("verifier_receipt_persistence_allowed") is False
+ and storage_proof.get("verifier_receipt_persisted") is False
+ and storage_proof.get("persists_verifier_receipt") is False
+ and storage_proof.get("verifier_invoked") is False
+ and storage_proof.get("dry_run_executor_invoked") is False
+ and storage_proof.get("runner_invocation_performed") is False
+ and storage_proof.get("endpoint_executed") is False
+ and storage_proof.get("sql_executed") is False
+ and storage_proof.get("database_written") is False
+ and storage_proof.get("endpoint_execution_allowed") is False
+ and storage_proof.get("sql_execution_allowed") is False
+ and storage_proof.get("database_write_allowed") is False
+ and storage_proof.get("database_apply_authorized") is False
+ and storage_proof.get("executes_database_apply") is False
+ and storage_proof.get("executes_endpoint") is False
+ and storage_proof.get("executes_sql") is False
+ and storage_proof.get("writes_database") is False
+ and storage_proof.get("stdout_included") is False
+ and storage_proof.get("stderr_included") is False
+ )
+ no_write_ledger_bound = (
+ storage_boundary_proof_ready
+ and bool(no_write_ledger_proof.get("ledger_proof_id"))
+ and no_write_ledger_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ == closeout_id
+ and no_write_ledger_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ == source_closeout_id
+ and no_write_ledger_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_id"
+ )
+ == source_storage_id
+ and no_write_ledger_proof.get("required_command_shape_hash")
+ == storage_proof.get("required_command_shape_hash")
+ and int(
+ no_write_ledger_proof.get(
+ "storage_boundary_no_write_ledger_proof_field_count"
+ )
+ or 0
+ )
+ == len(no_write_ledger_fields)
+ )
+ no_write_ledger_blocks_write = (
+ no_write_ledger_proof.get("ledger_mode")
+ == "storage_boundary_no_write_ledger_proof_preview_only"
+ and no_write_ledger_proof.get("storage_boundary_write_locked") is True
+ and no_write_ledger_proof.get("storage_boundary_write_allowed") is False
+ and no_write_ledger_proof.get("storage_boundary_written") is False
+ and no_write_ledger_proof.get("ledger_write_allowed") is False
+ and no_write_ledger_proof.get("ledger_written") is False
+ and no_write_ledger_proof.get("receipt_persistence_storage_write_allowed")
+ is False
+ and no_write_ledger_proof.get("receipt_persistence_storage_written")
+ is False
+ and no_write_ledger_proof.get("verifier_receipt_persistence_allowed")
+ is False
+ and no_write_ledger_proof.get("verifier_receipt_persisted") is False
+ and no_write_ledger_proof.get("persists_verifier_receipt") is False
+ and no_write_ledger_proof.get("verifier_invoked") is False
+ and no_write_ledger_proof.get("dry_run_executor_invoked") is False
+ and no_write_ledger_proof.get("runner_invocation_performed") is False
+ and no_write_ledger_proof.get("endpoint_executed") is False
+ and no_write_ledger_proof.get("sql_executed") is False
+ and no_write_ledger_proof.get("database_written") is False
+ and no_write_ledger_proof.get("endpoint_execution_allowed") is False
+ and no_write_ledger_proof.get("sql_execution_allowed") is False
+ and no_write_ledger_proof.get("database_write_allowed") is False
+ and no_write_ledger_proof.get("database_apply_authorized") is False
+ and no_write_ledger_proof.get("executes_database_apply") is False
+ and no_write_ledger_proof.get("executes_endpoint") is False
+ and no_write_ledger_proof.get("executes_sql") is False
+ and no_write_ledger_proof.get("writes_database") is False
+ and no_write_ledger_proof.get("stdout_included") is False
+ and no_write_ledger_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ storage_closeout.get("verifier_receipt_persistence_guard_proof_closeout_only")
+ is True
+ and storage_closeout.get("receipt_persistence_storage_boundary_proof_only")
+ is True
+ and previous_guard_closeout.get(
+ "verifier_no_execution_receipt_proof_closeout_only"
+ )
+ is True
+ and storage_boundary_proof_no_write
+ )
+ target_hash_locked = (
+ storage_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(storage_closeout.get("expected_sha256"))
+ and bool(storage_closeout.get("actual_sha256"))
+ and storage_closeout.get("expected_sha256")
+ == storage_closeout.get("actual_sha256")
+ and storage_closeout.get("hash_matches") is True
+ and storage_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ contract_blocks_ledger_storage_persistence_and_apply = (
+ storage_contract.get(
+ "permits_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ )
+ is True
+ and storage_contract.get("receipt_persistence_storage_write_allowed")
+ is False
+ and storage_contract.get("receipt_persistence_storage_written") is False
+ and storage_contract.get("persists_verifier_receipt") is False
+ and storage_contract.get("executes_database_apply") is False
+ and storage_contract.get("database_apply_authorized") is False
+ and storage_contract.get("writes_database") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("receipt_persistence_storage_write_allowed_count", 0) == 0
+ and summary.get("receipt_persistence_storage_written_count", 0) == 0
+ and summary.get("persists_verifier_receipt_count", 0) == 0
+ and safety.get("persists_verifier_receipt") is False
+ )
+ checks = [
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "verifier_receipt_persistence_guard_proof_closeout_ready",
+ storage_closeout_ready,
+ {
+ "result": ledger_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_count"
+ ),
+ },
+ "wait_for_verifier_receipt_persistence_guard_proof_closeout",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "source_closeout_id": source_closeout_id,
+ "source_storage_id": source_storage_id,
+ "source_guard_id": source_guard_id,
+ },
+ "wait_for_source_chain_alignment",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "receipt_persistence_storage_boundary_proof_ready",
+ storage_boundary_proof_ready,
+ {
+ "storage_boundary_proof_id": source_storage_id,
+ "storage_boundary_status": storage_proof.get(
+ "storage_boundary_status"
+ ),
+ "field_count": storage_proof.get(
+ "receipt_persistence_storage_boundary_proof_field_count"
+ ),
+ },
+ "wait_for_receipt_persistence_storage_boundary_proof",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "receipt_persistence_storage_boundary_proof_no_write",
+ storage_boundary_proof_no_write,
+ {
+ "storage_boundary_mode": storage_proof.get(
+ "storage_boundary_mode"
+ ),
+ "storage_write_allowed": storage_proof.get(
+ "receipt_persistence_storage_write_allowed"
+ ),
+ "storage_written": storage_proof.get(
+ "receipt_persistence_storage_written"
+ ),
+ },
+ "abort_if_receipt_persistence_storage_boundary_reports_write",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "storage_boundary_no_write_ledger_proof_bound",
+ no_write_ledger_bound,
+ {
+ "ledger_proof_id": no_write_ledger_proof.get("ledger_proof_id"),
+ "source_storage_id": no_write_ledger_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_id"
+ ),
+ "field_count": no_write_ledger_proof.get(
+ "storage_boundary_no_write_ledger_proof_field_count"
+ ),
+ },
+ "wait_for_storage_boundary_no_write_ledger_proof",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "storage_boundary_no_write_ledger_proof_blocks_write",
+ no_write_ledger_blocks_write,
+ {
+ "ledger_mode": no_write_ledger_proof.get("ledger_mode"),
+ "ledger_write_allowed": no_write_ledger_proof.get(
+ "ledger_write_allowed"
+ ),
+ "ledger_written": no_write_ledger_proof.get("ledger_written"),
+ },
+ "abort_if_storage_boundary_no_write_ledger_allows_write",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "verifier_receipt_persistence_guard_proof_closeout_only": (
+ storage_closeout.get(
+ "verifier_receipt_persistence_guard_proof_closeout_only"
+ )
+ ),
+ "receipt_persistence_storage_boundary_proof_only": (
+ storage_closeout.get(
+ "receipt_persistence_storage_boundary_proof_only"
+ )
+ ),
+ },
+ "wait_for_previous_closeouts_carry_forward",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": storage_closeout.get("target_file"),
+ "hash_matches": storage_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ storage_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(storage_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "verifier_receipt_persistence_guard_proof_closeout_contract_blocks_ledger_storage_persistence_and_database_apply",
+ contract_blocks_ledger_storage_persistence_and_apply,
+ {
+ "permits_future_storage_boundary_proof": storage_contract.get(
+ "permits_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ ),
+ "receipt_persistence_storage_write_allowed": storage_contract.get(
+ "receipt_persistence_storage_write_allowed"
+ ),
+ "database_apply_authorized": storage_contract.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_source_contract_allows_ledger_storage_persistence_or_database_apply",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "preview_has_no_side_effects_no_ledger_no_storage_no_persistence_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "receipt_persistence_storage_written_count": summary.get(
+ "receipt_persistence_storage_written_count", 0
+ ),
+ },
+ "abort_on_preview_ledger_storage_persistence_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ storage_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": storage_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_PERSISTENCE_STORAGE_BOUNDARY_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_RECEIPT_PERSISTENCE_GUARD_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof = {
+ "receipt_persistence_storage_boundary_proof_closeout_id": closeout_id,
+ "storage_boundary_no_write_ledger_proof_id": ledger_id,
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": source_storage_id,
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout": (
+ closeout_ready
+ ),
+ "receipt_persistence_storage_boundary_proof_closeout_ready": closeout_ready,
+ "verifier_receipt_persistence_guard_proof_closeout_ready": (
+ storage_closeout_ready
+ ),
+ "receipt_persistence_storage_boundary_proof_ready": (
+ storage_boundary_proof_ready
+ ),
+ "storage_boundary_no_write_ledger_proof_bound": closeout_ready,
+ "storage_boundary_write_locked": True,
+ "storage_boundary_write_allowed": False,
+ "storage_boundary_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout = {
+ "receipt_persistence_storage_boundary_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ ),
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": source_storage_id,
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "required_command_shape_hash": storage_proof.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof": (
+ closeout_ready
+ ),
+ "receipt_persistence_storage_boundary_proof_closeout_fields": (
+ closeout_fields
+ ),
+ "receipt_persistence_storage_boundary_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "receipt_persistence_storage_boundary_proof_closeout_acceptance_gates": (
+ acceptance_gates
+ ),
+ "receipt_persistence_storage_boundary_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "storage_boundary_no_write_ledger_proof": no_write_ledger_proof,
+ "storage_boundary_no_write_ledger_proof_count": 1,
+ "storage_boundary_no_write_ledger_proof_field_count": len(
+ no_write_ledger_fields
+ ),
+ "receipt_persistence_storage_boundary_proof": storage_proof,
+ "receipt_persistence_storage_boundary_proof_count": 1,
+ "verifier_receipt_persistence_guard_proof_closeout": storage_closeout,
+ "verifier_receipt_persistence_guard_proof_closeout_count": 1,
+ "verifier_receipt_persistence_guard_proof": guard_proof,
+ "verifier_receipt_persistence_guard_proof_count": 1,
+ "target_file": storage_closeout.get("target_file"),
+ "expected_sha256": storage_closeout.get("expected_sha256"),
+ "actual_sha256": storage_closeout.get("actual_sha256"),
+ "hash_matches": storage_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "receipt_persistence_storage_boundary_proof_closeout_only": True,
+ "storage_boundary_no_write_ledger_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "storage_boundary_write_locked": True,
+ "storage_boundary_write_allowed": False,
+ "storage_boundary_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "dry_run_executor_invocation_allowed": False,
+ "runner_invocation_allowed": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract = {
+ "mode": "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_and_storage_boundary_no_write_ledger_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout"
+ ),
+ "source_verifier_receipt_persistence_guard_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof": (
+ closeout_ready
+ ),
+ "storage_boundary_write_locked": True,
+ "storage_boundary_write_allowed": False,
+ "storage_boundary_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_count": 1,
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "storage_boundary_no_write_ledger_proof_count": 1,
+ "storage_boundary_no_write_ledger_proof_field_count": len(
+ no_write_ledger_fields
+ ),
+ "storage_boundary_write_locked_count": 1,
+ "storage_boundary_write_allowed_count": 0,
+ "storage_boundary_written_count": 0,
+ "ledger_write_allowed_count": 0,
+ "ledger_written_count": 0,
+ "receipt_persistence_storage_write_allowed_count": 0,
+ "receipt_persistence_storage_written_count": 0,
+ "verifier_receipt_persistence_allowed_count": 0,
+ "verifier_receipt_persisted_count": 0,
+ "persists_verifier_receipt_count": 0,
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_PERSISTENCE_STORAGE_BOUNDARY_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(ledger_closeout_result.get("success")),
+ "generated_at": ledger_closeout_result.get("generated_at"),
+ "source_policy": ledger_closeout_result.get("policy"),
+ "stats": ledger_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof": (
+ future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout": (
+ controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract": (
+ controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract
+ ),
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_checks": (
+ checks
+ ),
+ "source_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract": (
+ storage_contract
+ ),
+ "source_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout": (
+ storage_closeout
+ ),
+ "source_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof": (
+ future_storage
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future storage boundary no-write ledger proof closeout.",
+ "Keep storage-boundary ledger writes disabled until a later no-write ledger closeout proves the ledger boundary.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, ledger writes, verifier invocation, verifier receipt persistence, receipt storage, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the no-write ledger proof and add a retention proof."""
+ retention_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_ledger = (
+ retention_closeout_result.get(
+ "future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ )
+ or {}
+ )
+ ledger_closeout = (
+ retention_closeout_result.get(
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ )
+ or {}
+ )
+ ledger_contract = (
+ retention_closeout_result.get(
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = retention_closeout_result.get("summary") or {}
+ safety = retention_closeout_result.get("safety") or {}
+ no_write_ledger_proof = (
+ ledger_closeout.get("storage_boundary_no_write_ledger_proof") or {}
+ )
+ storage_proof = (
+ ledger_closeout.get("receipt_persistence_storage_boundary_proof") or {}
+ )
+ previous_storage_closeout = (
+ ledger_closeout.get("verifier_receipt_persistence_guard_proof_closeout")
+ or {}
+ )
+ rollback_binding = ledger_closeout.get("rollback_binding") or {}
+ verifier_binding = ledger_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = ledger_closeout.get(
+ "receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ source_ledger_id = no_write_ledger_proof.get("ledger_proof_id")
+ source_storage_id = no_write_ledger_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_id"
+ )
+ source_guard_closeout_id = no_write_ledger_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ source_guard_id = no_write_ledger_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_id"
+ )
+ closeout_id = (
+ _db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_id(
+ retention_closeout_result
+ )
+ )
+ retention_id = f"{closeout_id}-no-write-ledger-retention-proof"
+ closeout_fields = [
+ "storage_boundary_no_write_ledger_proof_closeout_id",
+ "source_receipt_persistence_storage_boundary_proof_closeout_id",
+ "source_storage_boundary_no_write_ledger_proof_id",
+ "source_receipt_persistence_storage_boundary_proof_id",
+ "source_verifier_receipt_persistence_guard_proof_closeout_id",
+ "no_write_ledger_retention_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "ledger_retention_write_locked",
+ "abort_conditions",
+ ]
+ acceptance_gates = [
+ "receipt_persistence_storage_boundary_proof_closeout_ready",
+ "source_chain_ids_match",
+ "storage_boundary_no_write_ledger_proof_ready",
+ "storage_boundary_no_write_ledger_proof_no_write",
+ "no_write_ledger_retention_proof_bound",
+ "no_write_ledger_retention_proof_blocks_persistence",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_ledger_storage_retention_persistence_or_database_apply",
+ ]
+ retention_fields = [
+ "retention_proof_id",
+ "source_storage_boundary_no_write_ledger_proof_closeout_id",
+ "source_storage_boundary_no_write_ledger_proof_id",
+ "source_receipt_persistence_storage_boundary_proof_closeout_id",
+ "source_receipt_persistence_storage_boundary_proof_id",
+ "retention_mode",
+ "ledger_retention_write_locked",
+ "ledger_retention_write_allowed",
+ "ledger_retention_written",
+ "ledger_write_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_receipt_persistence_storage_boundary_proof_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_storage_boundary_no_write_ledger_proof_missing",
+ "abort_if_storage_boundary_no_write_ledger_allows_write",
+ "abort_if_no_write_ledger_retention_proof_missing",
+ "abort_if_no_write_ledger_retention_allows_write",
+ "abort_if_ledger_or_retention_written",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_ledger_retention_storage_persistence_or_apply_material_is_present",
+ ]
+ retention_proof = {
+ "retention_proof_id": retention_id,
+ "source_storage_boundary_no_write_ledger_proof_closeout_id": closeout_id,
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": source_storage_id,
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_guard_closeout_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "required_command_shape_hash": no_write_ledger_proof.get(
+ "required_command_shape_hash"
+ ),
+ "retention_status": "no_write_ledger_retention_proof_preview_ready",
+ "retention_mode": "no_write_ledger_retention_proof_preview_only",
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "retention_receipt_present": False,
+ "retention_receipt_required": False,
+ "storage_boundary_write_locked": True,
+ "storage_boundary_write_allowed": False,
+ "storage_boundary_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "ledger_receipt_present": False,
+ "ledger_receipt_required": False,
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_receipt_present": False,
+ "verifier_receipt_required": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "no_write_ledger_retention_proof_field_count": len(retention_fields),
+ "no_write_ledger_retention_proof_fields": retention_fields,
+ }
+ ledger_closeout_ready = (
+ retention_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_PERSISTENCE_STORAGE_BOUNDARY_PROOF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(source_closeout_id)
+ and source_closeout_id
+ == future_ledger.get(
+ "receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ == no_write_ledger_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ == retention_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ and source_ledger_id
+ == future_ledger.get("storage_boundary_no_write_ledger_proof_id")
+ == retention_proof.get("source_storage_boundary_no_write_ledger_proof_id")
+ and source_storage_id
+ == future_ledger.get("source_receipt_persistence_storage_boundary_proof_id")
+ == storage_proof.get("storage_boundary_proof_id")
+ == retention_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_id"
+ )
+ and source_guard_closeout_id
+ == future_ledger.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ == retention_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ and source_guard_id
+ == future_ledger.get("source_verifier_receipt_persistence_guard_proof_id")
+ == retention_proof.get("source_verifier_receipt_persistence_guard_proof_id")
+ and no_write_ledger_proof.get("required_command_shape_hash")
+ == storage_proof.get("required_command_shape_hash")
+ == retention_proof.get("required_command_shape_hash")
+ )
+ no_write_ledger_ready = (
+ ledger_closeout_ready
+ and no_write_ledger_proof.get("ledger_status")
+ == "storage_boundary_no_write_ledger_proof_preview_ready"
+ and no_write_ledger_proof.get("ledger_proof_id")
+ == future_ledger.get("storage_boundary_no_write_ledger_proof_id")
+ and int(
+ no_write_ledger_proof.get(
+ "storage_boundary_no_write_ledger_proof_field_count"
+ )
+ or 0
+ )
+ == 12
+ and summary.get("storage_boundary_no_write_ledger_proof_count") == 1
+ )
+ no_write_ledger_no_write = (
+ no_write_ledger_proof.get("ledger_mode")
+ == "storage_boundary_no_write_ledger_proof_preview_only"
+ and no_write_ledger_proof.get("storage_boundary_write_locked") is True
+ and no_write_ledger_proof.get("storage_boundary_write_allowed") is False
+ and no_write_ledger_proof.get("storage_boundary_written") is False
+ and no_write_ledger_proof.get("ledger_write_allowed") is False
+ and no_write_ledger_proof.get("ledger_written") is False
+ and no_write_ledger_proof.get("receipt_persistence_storage_write_allowed")
+ is False
+ and no_write_ledger_proof.get("receipt_persistence_storage_written")
+ is False
+ and no_write_ledger_proof.get("verifier_receipt_persistence_allowed")
+ is False
+ and no_write_ledger_proof.get("verifier_receipt_persisted") is False
+ and no_write_ledger_proof.get("persists_verifier_receipt") is False
+ and no_write_ledger_proof.get("verifier_invoked") is False
+ and no_write_ledger_proof.get("dry_run_executor_invoked") is False
+ and no_write_ledger_proof.get("runner_invocation_performed") is False
+ and no_write_ledger_proof.get("endpoint_executed") is False
+ and no_write_ledger_proof.get("sql_executed") is False
+ and no_write_ledger_proof.get("database_written") is False
+ and no_write_ledger_proof.get("endpoint_execution_allowed") is False
+ and no_write_ledger_proof.get("sql_execution_allowed") is False
+ and no_write_ledger_proof.get("database_write_allowed") is False
+ and no_write_ledger_proof.get("database_apply_authorized") is False
+ and no_write_ledger_proof.get("executes_database_apply") is False
+ and no_write_ledger_proof.get("executes_endpoint") is False
+ and no_write_ledger_proof.get("executes_sql") is False
+ and no_write_ledger_proof.get("writes_database") is False
+ and no_write_ledger_proof.get("stdout_included") is False
+ and no_write_ledger_proof.get("stderr_included") is False
+ )
+ retention_bound = (
+ no_write_ledger_ready
+ and bool(retention_proof.get("retention_proof_id"))
+ and retention_proof.get(
+ "source_storage_boundary_no_write_ledger_proof_closeout_id"
+ )
+ == closeout_id
+ and retention_proof.get("source_storage_boundary_no_write_ledger_proof_id")
+ == source_ledger_id
+ and retention_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ == source_closeout_id
+ and retention_proof.get("source_receipt_persistence_storage_boundary_proof_id")
+ == source_storage_id
+ and retention_proof.get("required_command_shape_hash")
+ == no_write_ledger_proof.get("required_command_shape_hash")
+ and int(
+ retention_proof.get("no_write_ledger_retention_proof_field_count")
+ or 0
+ )
+ == len(retention_fields)
+ )
+ retention_blocks_persistence = (
+ retention_proof.get("retention_mode")
+ == "no_write_ledger_retention_proof_preview_only"
+ and retention_proof.get("ledger_retention_write_locked") is True
+ and retention_proof.get("ledger_retention_write_allowed") is False
+ and retention_proof.get("ledger_retention_written") is False
+ and retention_proof.get("ledger_write_allowed") is False
+ and retention_proof.get("ledger_written") is False
+ and retention_proof.get("receipt_persistence_storage_write_allowed")
+ is False
+ and retention_proof.get("receipt_persistence_storage_written") is False
+ and retention_proof.get("verifier_receipt_persistence_allowed") is False
+ and retention_proof.get("verifier_receipt_persisted") is False
+ and retention_proof.get("persists_verifier_receipt") is False
+ and retention_proof.get("verifier_invoked") is False
+ and retention_proof.get("dry_run_executor_invoked") is False
+ and retention_proof.get("runner_invocation_performed") is False
+ and retention_proof.get("endpoint_executed") is False
+ and retention_proof.get("sql_executed") is False
+ and retention_proof.get("database_written") is False
+ and retention_proof.get("endpoint_execution_allowed") is False
+ and retention_proof.get("sql_execution_allowed") is False
+ and retention_proof.get("database_write_allowed") is False
+ and retention_proof.get("database_apply_authorized") is False
+ and retention_proof.get("executes_database_apply") is False
+ and retention_proof.get("executes_endpoint") is False
+ and retention_proof.get("executes_sql") is False
+ and retention_proof.get("writes_database") is False
+ and retention_proof.get("stdout_included") is False
+ and retention_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ ledger_closeout.get("receipt_persistence_storage_boundary_proof_closeout_only")
+ is True
+ and ledger_closeout.get("storage_boundary_no_write_ledger_proof_only")
+ is True
+ and previous_storage_closeout.get(
+ "verifier_receipt_persistence_guard_proof_closeout_only"
+ )
+ is True
+ and no_write_ledger_no_write
+ )
+ target_hash_locked = (
+ ledger_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(ledger_closeout.get("expected_sha256"))
+ and bool(ledger_closeout.get("actual_sha256"))
+ and ledger_closeout.get("expected_sha256")
+ == ledger_closeout.get("actual_sha256")
+ and ledger_closeout.get("hash_matches") is True
+ and ledger_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ contract_blocks_retention_storage_persistence_and_apply = (
+ ledger_contract.get(
+ "permits_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ )
+ is True
+ and ledger_contract.get("storage_boundary_write_allowed") is False
+ and ledger_contract.get("storage_boundary_written") is False
+ and ledger_contract.get("ledger_write_allowed") is False
+ and ledger_contract.get("ledger_written") is False
+ and ledger_contract.get("receipt_persistence_storage_write_allowed")
+ is False
+ and ledger_contract.get("receipt_persistence_storage_written") is False
+ and ledger_contract.get("persists_verifier_receipt") is False
+ and ledger_contract.get("executes_database_apply") is False
+ and ledger_contract.get("database_apply_authorized") is False
+ and ledger_contract.get("writes_database") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("ledger_write_allowed_count", 0) == 0
+ and summary.get("ledger_written_count", 0) == 0
+ and summary.get("receipt_persistence_storage_write_allowed_count", 0) == 0
+ and summary.get("receipt_persistence_storage_written_count", 0) == 0
+ and summary.get("persists_verifier_receipt_count", 0) == 0
+ and safety.get("persists_verifier_receipt") is False
+ )
+ checks = [
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "receipt_persistence_storage_boundary_proof_closeout_ready",
+ ledger_closeout_ready,
+ {
+ "result": retention_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_count"
+ ),
+ },
+ "wait_for_receipt_persistence_storage_boundary_proof_closeout",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "source_closeout_id": source_closeout_id,
+ "source_ledger_id": source_ledger_id,
+ "source_storage_id": source_storage_id,
+ "source_guard_closeout_id": source_guard_closeout_id,
+ },
+ "wait_for_source_chain_alignment",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "storage_boundary_no_write_ledger_proof_ready",
+ no_write_ledger_ready,
+ {
+ "ledger_proof_id": source_ledger_id,
+ "ledger_status": no_write_ledger_proof.get("ledger_status"),
+ "field_count": no_write_ledger_proof.get(
+ "storage_boundary_no_write_ledger_proof_field_count"
+ ),
+ },
+ "wait_for_storage_boundary_no_write_ledger_proof",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "storage_boundary_no_write_ledger_proof_no_write",
+ no_write_ledger_no_write,
+ {
+ "ledger_mode": no_write_ledger_proof.get("ledger_mode"),
+ "ledger_write_allowed": no_write_ledger_proof.get(
+ "ledger_write_allowed"
+ ),
+ "database_apply_authorized": no_write_ledger_proof.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_storage_boundary_no_write_ledger_allows_write",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "no_write_ledger_retention_proof_bound",
+ retention_bound,
+ {
+ "retention_proof_id": retention_id,
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "field_count": retention_proof.get(
+ "no_write_ledger_retention_proof_field_count"
+ ),
+ },
+ "wait_for_no_write_ledger_retention_proof",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "no_write_ledger_retention_proof_blocks_persistence",
+ retention_blocks_persistence,
+ {
+ "retention_mode": retention_proof.get("retention_mode"),
+ "ledger_retention_write_allowed": retention_proof.get(
+ "ledger_retention_write_allowed"
+ ),
+ "ledger_retention_written": retention_proof.get(
+ "ledger_retention_written"
+ ),
+ },
+ "abort_if_no_write_ledger_retention_allows_persistence",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "source_closeout_only": ledger_closeout.get(
+ "receipt_persistence_storage_boundary_proof_closeout_only"
+ ),
+ "source_ledger_only": ledger_closeout.get(
+ "storage_boundary_no_write_ledger_proof_only"
+ ),
+ },
+ "wait_for_previous_closeouts",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": ledger_closeout.get("target_file"),
+ "hash_matches": ledger_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(ledger_closeout.get("expected_sha256")),
+ "actual_sha256_present": bool(ledger_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "receipt_persistence_storage_boundary_proof_closeout_contract_blocks_retention_storage_persistence_and_database_apply",
+ contract_blocks_retention_storage_persistence_and_apply,
+ {
+ "permits_future_storage_boundary_no_write_ledger_proof": ledger_contract.get(
+ "permits_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ ),
+ "ledger_write_allowed": ledger_contract.get("ledger_write_allowed"),
+ "database_apply_authorized": ledger_contract.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_source_contract_allows_retention_storage_persistence_or_database_apply",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "preview_has_no_side_effects_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "ledger_written_count": summary.get("ledger_written_count", 0),
+ },
+ "abort_on_preview_retention_ledger_storage_persistence_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ ledger_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": ledger_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_STORAGE_BOUNDARY_NO_WRITE_LEDGER_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_PERSISTENCE_STORAGE_BOUNDARY_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_no_write_ledger_retention_proof = {
+ "storage_boundary_no_write_ledger_proof_closeout_id": closeout_id,
+ "no_write_ledger_retention_proof_id": retention_id,
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_id": source_storage_id,
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_guard_closeout_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout": (
+ closeout_ready
+ ),
+ "storage_boundary_no_write_ledger_proof_closeout_ready": closeout_ready,
+ "receipt_persistence_storage_boundary_proof_closeout_ready": (
+ ledger_closeout_ready
+ ),
+ "storage_boundary_no_write_ledger_proof_ready": no_write_ledger_ready,
+ "no_write_ledger_retention_proof_bound": closeout_ready,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "storage_boundary_write_locked": True,
+ "storage_boundary_write_allowed": False,
+ "storage_boundary_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout = {
+ "storage_boundary_no_write_ledger_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+ ),
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_id": source_storage_id,
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_guard_closeout_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "required_command_shape_hash": no_write_ledger_proof.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof": (
+ closeout_ready
+ ),
+ "storage_boundary_no_write_ledger_proof_closeout_fields": closeout_fields,
+ "storage_boundary_no_write_ledger_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "storage_boundary_no_write_ledger_proof_closeout_acceptance_gates": (
+ acceptance_gates
+ ),
+ "storage_boundary_no_write_ledger_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "no_write_ledger_retention_proof": retention_proof,
+ "no_write_ledger_retention_proof_count": 1,
+ "no_write_ledger_retention_proof_field_count": len(retention_fields),
+ "storage_boundary_no_write_ledger_proof": no_write_ledger_proof,
+ "storage_boundary_no_write_ledger_proof_count": 1,
+ "receipt_persistence_storage_boundary_proof_closeout": ledger_closeout,
+ "receipt_persistence_storage_boundary_proof_closeout_count": 1,
+ "receipt_persistence_storage_boundary_proof": storage_proof,
+ "receipt_persistence_storage_boundary_proof_count": 1,
+ "target_file": ledger_closeout.get("target_file"),
+ "expected_sha256": ledger_closeout.get("expected_sha256"),
+ "actual_sha256": ledger_closeout.get("actual_sha256"),
+ "hash_matches": ledger_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "storage_boundary_no_write_ledger_proof_closeout_only": True,
+ "no_write_ledger_retention_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "storage_boundary_write_locked": True,
+ "storage_boundary_write_allowed": False,
+ "storage_boundary_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract = {
+ "mode": "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_and_no_write_ledger_retention_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout"
+ ),
+ "source_receipt_persistence_storage_boundary_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof": (
+ closeout_ready
+ ),
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "storage_boundary_write_locked": True,
+ "storage_boundary_write_allowed": False,
+ "storage_boundary_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_count": 1,
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "no_write_ledger_retention_proof_count": 1,
+ "no_write_ledger_retention_proof_field_count": len(retention_fields),
+ "ledger_retention_write_locked_count": 1,
+ "ledger_retention_write_allowed_count": 0,
+ "ledger_retention_written_count": 0,
+ "storage_boundary_write_allowed_count": 0,
+ "storage_boundary_written_count": 0,
+ "ledger_write_allowed_count": 0,
+ "ledger_written_count": 0,
+ "receipt_persistence_storage_write_allowed_count": 0,
+ "receipt_persistence_storage_written_count": 0,
+ "verifier_receipt_persistence_allowed_count": 0,
+ "verifier_receipt_persisted_count": 0,
+ "persists_verifier_receipt_count": 0,
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_STORAGE_BOUNDARY_NO_WRITE_LEDGER_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(retention_closeout_result.get("success")),
+ "generated_at": retention_closeout_result.get("generated_at"),
+ "source_policy": retention_closeout_result.get("policy"),
+ "stats": retention_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_no_write_ledger_retention_proof": (
+ future_database_apply_controlled_dry_run_no_write_ledger_retention_proof
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout": (
+ controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract": (
+ controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract
+ ),
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_checks": (
+ checks
+ ),
+ "source_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract": (
+ ledger_contract
+ ),
+ "source_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout": (
+ ledger_closeout
+ ),
+ "source_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof": (
+ future_ledger
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future no-write ledger retention proof closeout.",
+ "Keep ledger retention writes disabled until a later retention boundary closeout proves the retention path.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, ledger writes, ledger retention writes, verifier invocation, verifier receipt persistence, receipt storage, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the retention proof and add a no-write archive proof."""
+ archive_closeout_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_retention = (
+ archive_closeout_result.get(
+ "future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ )
+ or {}
+ )
+ retention_closeout = (
+ archive_closeout_result.get(
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+ )
+ or {}
+ )
+ retention_contract = (
+ archive_closeout_result.get(
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = archive_closeout_result.get("summary") or {}
+ safety = archive_closeout_result.get("safety") or {}
+ retention_proof = retention_closeout.get("no_write_ledger_retention_proof") or {}
+ no_write_ledger_proof = (
+ retention_closeout.get("storage_boundary_no_write_ledger_proof") or {}
+ )
+ previous_ledger_closeout = (
+ retention_closeout.get(
+ "receipt_persistence_storage_boundary_proof_closeout"
+ )
+ or {}
+ )
+ rollback_binding = retention_closeout.get("rollback_binding") or {}
+ verifier_binding = retention_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = retention_closeout.get(
+ "storage_boundary_no_write_ledger_proof_closeout_id"
+ )
+ source_retention_id = retention_proof.get("retention_proof_id")
+ source_ledger_id = retention_proof.get(
+ "source_storage_boundary_no_write_ledger_proof_id"
+ )
+ source_storage_closeout_id = retention_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ source_storage_id = retention_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_id"
+ )
+ source_guard_closeout_id = retention_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ source_guard_id = retention_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_id"
+ )
+ closeout_id = (
+ _db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout_id(
+ archive_closeout_result
+ )
+ )
+ archive_id = f"{closeout_id}-retention-boundary-no-write-archive-proof"
+ closeout_fields = [
+ "no_write_ledger_retention_proof_closeout_id",
+ "source_storage_boundary_no_write_ledger_proof_closeout_id",
+ "source_no_write_ledger_retention_proof_id",
+ "source_storage_boundary_no_write_ledger_proof_id",
+ "source_receipt_persistence_storage_boundary_proof_closeout_id",
+ "retention_boundary_no_write_archive_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "retention_archive_write_locked",
+ "abort_conditions",
+ ]
+ acceptance_gates = [
+ "storage_boundary_no_write_ledger_proof_closeout_ready",
+ "source_chain_ids_match",
+ "no_write_ledger_retention_proof_ready",
+ "no_write_ledger_retention_proof_no_write",
+ "retention_boundary_no_write_archive_proof_bound",
+ "retention_boundary_no_write_archive_proof_blocks_archive",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_secret_signature_archive_retention_ledger_storage_persistence_or_database_apply",
+ ]
+ archive_fields = [
+ "archive_proof_id",
+ "source_no_write_ledger_retention_proof_closeout_id",
+ "source_no_write_ledger_retention_proof_id",
+ "source_storage_boundary_no_write_ledger_proof_closeout_id",
+ "source_storage_boundary_no_write_ledger_proof_id",
+ "archive_mode",
+ "retention_archive_write_locked",
+ "retention_archive_write_allowed",
+ "retention_archive_written",
+ "ledger_retention_write_allowed",
+ "sql_execution_allowed",
+ "database_apply_authorized",
+ ]
+ abort_conditions = [
+ "abort_if_storage_boundary_no_write_ledger_proof_closeout_not_ready",
+ "abort_if_source_chain_ids_do_not_match",
+ "abort_if_no_write_ledger_retention_proof_missing",
+ "abort_if_no_write_ledger_retention_allows_write",
+ "abort_if_retention_boundary_no_write_archive_proof_missing",
+ "abort_if_retention_boundary_archive_allows_write",
+ "abort_if_retention_archive_or_ledger_written",
+ "abort_if_endpoint_or_sql_execution_is_allowed",
+ "abort_if_database_write_or_apply_is_allowed",
+ "abort_if_target_migration_hash_changes",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_signature_archive_retention_ledger_storage_persistence_or_apply_material_is_present",
+ ]
+ archive_proof = {
+ "archive_proof_id": archive_id,
+ "source_no_write_ledger_retention_proof_closeout_id": closeout_id,
+ "source_no_write_ledger_retention_proof_id": source_retention_id,
+ "source_storage_boundary_no_write_ledger_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": (
+ source_storage_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": source_storage_id,
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_guard_closeout_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "required_command_shape_hash": retention_proof.get(
+ "required_command_shape_hash"
+ ),
+ "archive_status": "retention_boundary_no_write_archive_proof_preview_ready",
+ "archive_mode": "retention_boundary_no_write_archive_proof_preview_only",
+ "retention_archive_write_locked": True,
+ "retention_archive_write_allowed": False,
+ "retention_archive_written": False,
+ "archive_receipt_present": False,
+ "archive_receipt_required": False,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "retention_receipt_present": False,
+ "retention_receipt_required": False,
+ "storage_boundary_write_locked": True,
+ "storage_boundary_write_allowed": False,
+ "storage_boundary_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "ledger_receipt_present": False,
+ "ledger_receipt_required": False,
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_receipt_present": False,
+ "verifier_receipt_required": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "ready_for_retention_boundary_archive_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "retention_boundary_no_write_archive_proof_field_count": len(
+ archive_fields
+ ),
+ "retention_boundary_no_write_archive_proof_fields": archive_fields,
+ }
+ retention_closeout_ready = (
+ archive_closeout_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_STORAGE_BOUNDARY_NO_WRITE_LEDGER_PROOF_CLOSEOUT_READY"
+ and summary.get(
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_count"
+ )
+ == 1
+ and summary.get(
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_pass_count"
+ )
+ == summary.get(
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check_count"
+ )
+ )
+ source_chain_ids_match = (
+ bool(source_closeout_id)
+ and source_closeout_id
+ == future_retention.get(
+ "storage_boundary_no_write_ledger_proof_closeout_id"
+ )
+ == retention_proof.get(
+ "source_storage_boundary_no_write_ledger_proof_closeout_id"
+ )
+ == archive_proof.get(
+ "source_storage_boundary_no_write_ledger_proof_closeout_id"
+ )
+ and source_retention_id
+ == future_retention.get("no_write_ledger_retention_proof_id")
+ == archive_proof.get("source_no_write_ledger_retention_proof_id")
+ and source_ledger_id
+ == future_retention.get("source_storage_boundary_no_write_ledger_proof_id")
+ == no_write_ledger_proof.get("ledger_proof_id")
+ == archive_proof.get("source_storage_boundary_no_write_ledger_proof_id")
+ and source_storage_closeout_id
+ == future_retention.get(
+ "source_receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ == archive_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ and source_storage_id
+ == future_retention.get("source_receipt_persistence_storage_boundary_proof_id")
+ == archive_proof.get("source_receipt_persistence_storage_boundary_proof_id")
+ and source_guard_closeout_id
+ == future_retention.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ == archive_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ and source_guard_id
+ == future_retention.get("source_verifier_receipt_persistence_guard_proof_id")
+ == archive_proof.get("source_verifier_receipt_persistence_guard_proof_id")
+ and retention_proof.get("required_command_shape_hash")
+ == no_write_ledger_proof.get("required_command_shape_hash")
+ == archive_proof.get("required_command_shape_hash")
+ )
+ retention_proof_ready = (
+ retention_closeout_ready
+ and retention_proof.get("retention_status")
+ == "no_write_ledger_retention_proof_preview_ready"
+ and retention_proof.get("retention_proof_id")
+ == future_retention.get("no_write_ledger_retention_proof_id")
+ and int(
+ retention_proof.get("no_write_ledger_retention_proof_field_count")
+ or 0
+ )
+ == 12
+ and summary.get("no_write_ledger_retention_proof_count") == 1
+ )
+ retention_proof_no_write = (
+ retention_proof.get("retention_mode")
+ == "no_write_ledger_retention_proof_preview_only"
+ and retention_proof.get("ledger_retention_write_locked") is True
+ and retention_proof.get("ledger_retention_write_allowed") is False
+ and retention_proof.get("ledger_retention_written") is False
+ and retention_proof.get("ledger_write_allowed") is False
+ and retention_proof.get("ledger_written") is False
+ and retention_proof.get("receipt_persistence_storage_write_allowed")
+ is False
+ and retention_proof.get("receipt_persistence_storage_written") is False
+ and retention_proof.get("verifier_receipt_persistence_allowed") is False
+ and retention_proof.get("verifier_receipt_persisted") is False
+ and retention_proof.get("persists_verifier_receipt") is False
+ and retention_proof.get("verifier_invoked") is False
+ and retention_proof.get("dry_run_executor_invoked") is False
+ and retention_proof.get("runner_invocation_performed") is False
+ and retention_proof.get("endpoint_executed") is False
+ and retention_proof.get("sql_executed") is False
+ and retention_proof.get("database_written") is False
+ and retention_proof.get("endpoint_execution_allowed") is False
+ and retention_proof.get("sql_execution_allowed") is False
+ and retention_proof.get("database_write_allowed") is False
+ and retention_proof.get("database_apply_authorized") is False
+ and retention_proof.get("executes_database_apply") is False
+ and retention_proof.get("executes_endpoint") is False
+ and retention_proof.get("executes_sql") is False
+ and retention_proof.get("writes_database") is False
+ and retention_proof.get("stdout_included") is False
+ and retention_proof.get("stderr_included") is False
+ )
+ archive_bound = (
+ retention_proof_ready
+ and bool(archive_proof.get("archive_proof_id"))
+ and archive_proof.get(
+ "source_no_write_ledger_retention_proof_closeout_id"
+ )
+ == closeout_id
+ and archive_proof.get("source_no_write_ledger_retention_proof_id")
+ == source_retention_id
+ and archive_proof.get(
+ "source_storage_boundary_no_write_ledger_proof_closeout_id"
+ )
+ == source_closeout_id
+ and archive_proof.get("source_storage_boundary_no_write_ledger_proof_id")
+ == source_ledger_id
+ and archive_proof.get("required_command_shape_hash")
+ == retention_proof.get("required_command_shape_hash")
+ and int(
+ archive_proof.get(
+ "retention_boundary_no_write_archive_proof_field_count"
+ )
+ or 0
+ )
+ == len(archive_fields)
+ )
+ archive_blocks_write = (
+ archive_proof.get("archive_mode")
+ == "retention_boundary_no_write_archive_proof_preview_only"
+ and archive_proof.get("retention_archive_write_locked") is True
+ and archive_proof.get("retention_archive_write_allowed") is False
+ and archive_proof.get("retention_archive_written") is False
+ and archive_proof.get("ledger_retention_write_allowed") is False
+ and archive_proof.get("ledger_retention_written") is False
+ and archive_proof.get("ledger_write_allowed") is False
+ and archive_proof.get("ledger_written") is False
+ and archive_proof.get("receipt_persistence_storage_write_allowed") is False
+ and archive_proof.get("receipt_persistence_storage_written") is False
+ and archive_proof.get("verifier_receipt_persistence_allowed") is False
+ and archive_proof.get("verifier_receipt_persisted") is False
+ and archive_proof.get("persists_verifier_receipt") is False
+ and archive_proof.get("verifier_invoked") is False
+ and archive_proof.get("dry_run_executor_invoked") is False
+ and archive_proof.get("runner_invocation_performed") is False
+ and archive_proof.get("endpoint_executed") is False
+ and archive_proof.get("sql_executed") is False
+ and archive_proof.get("database_written") is False
+ and archive_proof.get("endpoint_execution_allowed") is False
+ and archive_proof.get("sql_execution_allowed") is False
+ and archive_proof.get("database_write_allowed") is False
+ and archive_proof.get("database_apply_authorized") is False
+ and archive_proof.get("executes_database_apply") is False
+ and archive_proof.get("executes_endpoint") is False
+ and archive_proof.get("executes_sql") is False
+ and archive_proof.get("writes_database") is False
+ and archive_proof.get("stdout_included") is False
+ and archive_proof.get("stderr_included") is False
+ )
+ previous_closeouts_carried_forward = (
+ retention_closeout.get("storage_boundary_no_write_ledger_proof_closeout_only")
+ is True
+ and retention_closeout.get("no_write_ledger_retention_proof_only")
+ is True
+ and previous_ledger_closeout.get(
+ "receipt_persistence_storage_boundary_proof_closeout_only"
+ )
+ is True
+ and retention_proof_no_write
+ )
+ target_hash_locked = (
+ retention_closeout.get("target_file")
+ == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ and bool(retention_closeout.get("expected_sha256"))
+ and bool(retention_closeout.get("actual_sha256"))
+ and retention_closeout.get("expected_sha256")
+ == retention_closeout.get("actual_sha256")
+ and retention_closeout.get("hash_matches") is True
+ and retention_closeout.get("target_migration_hash_locked") is True
+ )
+ rollback_and_verifier_bound = (
+ bool(rollback_binding.get("rollback_binding_id"))
+ and rollback_binding.get("rollback_execution_authorized") is False
+ and rollback_binding.get("rollback_executes_sql") is False
+ and rollback_binding.get("rollback_writes_database") is False
+ and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ and verifier_binding.get("verifier_must_run_after_apply") is True
+ and verifier_binding.get("verifier_execution_authorized_in_preview")
+ is False
+ and verifier_binding.get("database_apply_authorized") is False
+ )
+ contract_blocks_archive_retention_storage_persistence_and_apply = (
+ retention_contract.get(
+ "permits_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ )
+ is True
+ and retention_contract.get("ledger_retention_write_allowed") is False
+ and retention_contract.get("ledger_retention_written") is False
+ and retention_contract.get("ledger_write_allowed") is False
+ and retention_contract.get("ledger_written") is False
+ and retention_contract.get("receipt_persistence_storage_write_allowed")
+ is False
+ and retention_contract.get("receipt_persistence_storage_written") is False
+ and retention_contract.get("persists_verifier_receipt") is False
+ and retention_contract.get("executes_database_apply") is False
+ and retention_contract.get("database_apply_authorized") is False
+ and retention_contract.get("writes_database") is False
+ )
+ side_effect_free = (
+ summary.get("reads_secret_count", 0) == 0
+ and summary.get("executes_endpoint_count", 0) == 0
+ and summary.get("executes_sql_count", 0) == 0
+ and summary.get("writes_database_count", 0) == 0
+ and summary.get("signs_database_apply_authorization_count", 0) == 0
+ and summary.get("ledger_retention_write_allowed_count", 0) == 0
+ and summary.get("ledger_retention_written_count", 0) == 0
+ and summary.get("ledger_write_allowed_count", 0) == 0
+ and summary.get("ledger_written_count", 0) == 0
+ and summary.get("receipt_persistence_storage_write_allowed_count", 0) == 0
+ and summary.get("receipt_persistence_storage_written_count", 0) == 0
+ and summary.get("persists_verifier_receipt_count", 0) == 0
+ and safety.get("persists_verifier_receipt") is False
+ )
+ checks = [
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "storage_boundary_no_write_ledger_proof_closeout_ready",
+ retention_closeout_ready,
+ {
+ "result": archive_closeout_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_count"
+ ),
+ },
+ "wait_for_storage_boundary_no_write_ledger_proof_closeout",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "source_chain_ids_match",
+ source_chain_ids_match,
+ {
+ "source_closeout_id": source_closeout_id,
+ "source_retention_id": source_retention_id,
+ "source_ledger_id": source_ledger_id,
+ "source_storage_closeout_id": source_storage_closeout_id,
+ },
+ "wait_for_source_chain_alignment",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "no_write_ledger_retention_proof_ready",
+ retention_proof_ready,
+ {
+ "retention_proof_id": source_retention_id,
+ "retention_status": retention_proof.get("retention_status"),
+ "field_count": retention_proof.get(
+ "no_write_ledger_retention_proof_field_count"
+ ),
+ },
+ "wait_for_no_write_ledger_retention_proof",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "no_write_ledger_retention_proof_no_write",
+ retention_proof_no_write,
+ {
+ "retention_mode": retention_proof.get("retention_mode"),
+ "ledger_retention_write_allowed": retention_proof.get(
+ "ledger_retention_write_allowed"
+ ),
+ "database_apply_authorized": retention_proof.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_no_write_ledger_retention_allows_write",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "retention_boundary_no_write_archive_proof_bound",
+ archive_bound,
+ {
+ "archive_proof_id": archive_id,
+ "source_no_write_ledger_retention_proof_id": source_retention_id,
+ "field_count": archive_proof.get(
+ "retention_boundary_no_write_archive_proof_field_count"
+ ),
+ },
+ "wait_for_retention_boundary_no_write_archive_proof",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "retention_boundary_no_write_archive_proof_blocks_archive",
+ archive_blocks_write,
+ {
+ "archive_mode": archive_proof.get("archive_mode"),
+ "retention_archive_write_allowed": archive_proof.get(
+ "retention_archive_write_allowed"
+ ),
+ "retention_archive_written": archive_proof.get(
+ "retention_archive_written"
+ ),
+ },
+ "abort_if_retention_boundary_archive_allows_write",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "source_closeout_only": retention_closeout.get(
+ "storage_boundary_no_write_ledger_proof_closeout_only"
+ ),
+ "source_retention_only": retention_closeout.get(
+ "no_write_ledger_retention_proof_only"
+ ),
+ },
+ "wait_for_previous_closeouts",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": retention_closeout.get("target_file"),
+ "hash_matches": retention_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ retention_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(retention_closeout.get("actual_sha256")),
+ },
+ "require_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "storage_boundary_no_write_ledger_proof_closeout_contract_blocks_archive_retention_storage_persistence_and_database_apply",
+ contract_blocks_archive_retention_storage_persistence_and_apply,
+ {
+ "permits_future_no_write_ledger_retention_proof": retention_contract.get(
+ "permits_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ ),
+ "ledger_retention_write_allowed": retention_contract.get(
+ "ledger_retention_write_allowed"
+ ),
+ "database_apply_authorized": retention_contract.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_source_contract_allows_archive_retention_storage_persistence_or_database_apply",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "preview_has_no_side_effects_no_archive_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "ledger_retention_written_count": summary.get(
+ "ledger_retention_written_count", 0
+ ),
+ },
+ "abort_on_preview_archive_retention_ledger_storage_persistence_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_no_write_ledger_retention_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ retention_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": retention_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_LEDGER_RETENTION_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_STORAGE_BOUNDARY_NO_WRITE_LEDGER_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof = {
+ "no_write_ledger_retention_proof_closeout_id": closeout_id,
+ "retention_boundary_no_write_archive_proof_id": archive_id,
+ "source_storage_boundary_no_write_ledger_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_no_write_ledger_retention_proof_id": source_retention_id,
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": (
+ source_storage_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": source_storage_id,
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_guard_closeout_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout": (
+ closeout_ready
+ ),
+ "no_write_ledger_retention_proof_closeout_ready": closeout_ready,
+ "storage_boundary_no_write_ledger_proof_closeout_ready": (
+ retention_closeout_ready
+ ),
+ "no_write_ledger_retention_proof_ready": retention_proof_ready,
+ "retention_boundary_no_write_archive_proof_bound": closeout_ready,
+ "retention_archive_write_locked": True,
+ "retention_archive_write_allowed": False,
+ "retention_archive_written": False,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_retention_boundary_archive_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_no_write_ledger_retention_proof_closeout = {
+ "no_write_ledger_retention_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout"
+ ),
+ "source_storage_boundary_no_write_ledger_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_no_write_ledger_retention_proof_id": source_retention_id,
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": (
+ source_storage_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": source_storage_id,
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_guard_closeout_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "required_command_shape_hash": retention_proof.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof": (
+ closeout_ready
+ ),
+ "no_write_ledger_retention_proof_closeout_fields": closeout_fields,
+ "no_write_ledger_retention_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "no_write_ledger_retention_proof_closeout_acceptance_gates": (
+ acceptance_gates
+ ),
+ "no_write_ledger_retention_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "retention_boundary_no_write_archive_proof": archive_proof,
+ "retention_boundary_no_write_archive_proof_count": 1,
+ "retention_boundary_no_write_archive_proof_field_count": len(
+ archive_fields
+ ),
+ "no_write_ledger_retention_proof": retention_proof,
+ "no_write_ledger_retention_proof_count": 1,
+ "storage_boundary_no_write_ledger_proof_closeout": retention_closeout,
+ "storage_boundary_no_write_ledger_proof_closeout_count": 1,
+ "storage_boundary_no_write_ledger_proof": no_write_ledger_proof,
+ "storage_boundary_no_write_ledger_proof_count": 1,
+ "target_file": retention_closeout.get("target_file"),
+ "expected_sha256": retention_closeout.get("expected_sha256"),
+ "actual_sha256": retention_closeout.get("actual_sha256"),
+ "hash_matches": retention_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "no_write_ledger_retention_proof_closeout_only": True,
+ "retention_boundary_no_write_archive_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "retention_archive_write_locked": True,
+ "retention_archive_write_allowed": False,
+ "retention_archive_written": False,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_retention_boundary_archive_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_no_write_ledger_retention_proof_closeout_contract = {
+ "mode": "controlled_dry_run_no_write_ledger_retention_proof_closeout_and_retention_boundary_no_write_archive_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout"
+ ),
+ "source_storage_boundary_no_write_ledger_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof": (
+ closeout_ready
+ ),
+ "retention_archive_write_locked": True,
+ "retention_archive_write_allowed": False,
+ "retention_archive_written": False,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_retention_boundary_archive_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_count": 1,
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "retention_boundary_no_write_archive_proof_count": 1,
+ "retention_boundary_no_write_archive_proof_field_count": len(
+ archive_fields
+ ),
+ "retention_archive_write_locked_count": 1,
+ "retention_archive_write_allowed_count": 0,
+ "retention_archive_written_count": 0,
+ "ledger_retention_write_allowed_count": 0,
+ "ledger_retention_written_count": 0,
+ "storage_boundary_write_allowed_count": 0,
+ "storage_boundary_written_count": 0,
+ "ledger_write_allowed_count": 0,
+ "ledger_written_count": 0,
+ "receipt_persistence_storage_write_allowed_count": 0,
+ "receipt_persistence_storage_written_count": 0,
+ "verifier_receipt_persistence_allowed_count": 0,
+ "verifier_receipt_persisted_count": 0,
+ "persists_verifier_receipt_count": 0,
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_LEDGER_RETENTION_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(archive_closeout_result.get("success")),
+ "generated_at": archive_closeout_result.get("generated_at"),
+ "source_policy": archive_closeout_result.get("policy"),
+ "stats": archive_closeout_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof": (
+ future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout": (
+ controlled_dry_run_no_write_ledger_retention_proof_closeout
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_contract": (
+ controlled_dry_run_no_write_ledger_retention_proof_closeout_contract
+ ),
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_checks": (
+ checks
+ ),
+ "source_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract": (
+ retention_contract
+ ),
+ "source_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout": (
+ retention_closeout
+ ),
+ "source_database_apply_controlled_dry_run_no_write_ledger_retention_proof": (
+ future_retention
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future retention boundary no-write archive proof closeout.",
+ "Keep retention archive writes disabled until a later archive boundary closeout proves the archive path.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, archive writes, ledger writes, ledger retention writes, verifier invocation, verifier receipt persistence, receipt storage, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the archive proof into a sealed no-write handoff proof."""
+ sealed_handoff_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_archive = (
+ sealed_handoff_result.get(
+ "future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ )
+ or {}
+ )
+ archive_closeout = (
+ sealed_handoff_result.get(
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout"
+ )
+ or {}
+ )
+ archive_contract = (
+ sealed_handoff_result.get(
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = sealed_handoff_result.get("summary") or {}
+ safety = sealed_handoff_result.get("safety") or {}
+ archive_proof = (
+ archive_closeout.get("retention_boundary_no_write_archive_proof") or {}
+ )
+ retention_proof = archive_closeout.get("no_write_ledger_retention_proof") or {}
+ previous_retention_closeout = (
+ archive_closeout.get("storage_boundary_no_write_ledger_proof_closeout")
+ or {}
+ )
+ rollback_binding = archive_closeout.get("rollback_binding") or {}
+ verifier_binding = archive_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = archive_closeout.get(
+ "no_write_ledger_retention_proof_closeout_id"
+ )
+ source_archive_id = archive_proof.get("archive_proof_id")
+ source_retention_id = archive_proof.get("source_no_write_ledger_retention_proof_id")
+ source_storage_closeout_id = archive_proof.get(
+ "source_storage_boundary_no_write_ledger_proof_closeout_id"
+ )
+ source_ledger_id = archive_proof.get(
+ "source_storage_boundary_no_write_ledger_proof_id"
+ )
+ source_receipt_storage_closeout_id = archive_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_closeout_id"
+ )
+ source_receipt_storage_id = archive_proof.get(
+ "source_receipt_persistence_storage_boundary_proof_id"
+ )
+ source_guard_closeout_id = archive_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_closeout_id"
+ )
+ source_guard_id = archive_proof.get(
+ "source_verifier_receipt_persistence_guard_proof_id"
+ )
+ closeout_id = (
+ _db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_id(
+ sealed_handoff_result
+ )
+ )
+ handoff_id = f"{closeout_id}-archive-retention-sealed-handoff-proof"
+ closeout_fields = [
+ "retention_boundary_no_write_archive_proof_closeout_id",
+ "source_no_write_ledger_retention_proof_closeout_id",
+ "source_retention_boundary_no_write_archive_proof_id",
+ "source_no_write_ledger_retention_proof_id",
+ "source_storage_boundary_no_write_ledger_proof_closeout_id",
+ "archive_retention_sealed_handoff_proof_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "expected_sha256",
+ "actual_sha256",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ ]
+ handoff_fields = [
+ "archive_retention_sealed_handoff_proof_id",
+ "source_no_write_ledger_retention_proof_closeout_id",
+ "source_retention_boundary_no_write_archive_proof_id",
+ "source_no_write_ledger_retention_proof_id",
+ "source_storage_boundary_no_write_ledger_proof_closeout_id",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "expected_sha256",
+ "actual_sha256",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "sealed_handoff_manifest_hash",
+ ]
+ acceptance_gates = [
+ "no_write_ledger_retention_proof_closeout_ready",
+ "retention_boundary_no_write_archive_proof_ready",
+ "retention_boundary_no_write_archive_proof_no_write",
+ "archive_retention_sealed_handoff_proof_bound",
+ "archive_retention_sealed_handoff_proof_blocks_handoff_write",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "no_write_ledger_retention_proof_closeout_contract_blocks_handoff_archive_retention_storage_persistence_and_database_apply",
+ "preview_has_no_side_effects_no_handoff_no_archive_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing",
+ ]
+ abort_conditions = [
+ "abort_if_no_write_ledger_retention_proof_closeout_not_ready",
+ "abort_if_retention_boundary_archive_proof_missing",
+ "abort_if_retention_boundary_archive_write_allowed_or_written",
+ "abort_if_sealed_handoff_write_allowed_or_written",
+ "abort_if_previous_closeout_ids_do_not_match",
+ "abort_if_target_migration_hash_is_not_locked",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_or_signature_material_is_included",
+ "abort_if_any_endpoint_sql_database_runner_verifier_or_executor_action_is_allowed",
+ "abort_if_manual_review_mode_is_not_exception_only",
+ ]
+ archive_closeout_ready = (
+ sealed_handoff_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_LEDGER_RETENTION_PROOF_CLOSEOUT_READY"
+ and archive_closeout.get(
+ "ready_for_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ )
+ is True
+ )
+ archive_proof_ready = (
+ archive_proof.get("archive_status")
+ == "retention_boundary_no_write_archive_proof_preview_ready"
+ and archive_proof.get("archive_mode")
+ == "retention_boundary_no_write_archive_proof_preview_only"
+ and archive_proof.get("retention_boundary_no_write_archive_proof_field_count")
+ == 12
+ )
+ archive_no_write = (
+ archive_proof.get("retention_archive_write_locked") is True
+ and archive_proof.get("retention_archive_write_allowed") is False
+ and archive_proof.get("retention_archive_written") is False
+ and archive_proof.get("ledger_retention_write_allowed") is False
+ and archive_proof.get("ledger_retention_written") is False
+ and archive_proof.get("ledger_write_allowed") is False
+ and archive_proof.get("ledger_written") is False
+ and archive_proof.get("receipt_persistence_storage_write_allowed") is False
+ and archive_proof.get("receipt_persistence_storage_written") is False
+ and archive_proof.get("persists_verifier_receipt") is False
+ and archive_proof.get("endpoint_executed") is False
+ and archive_proof.get("sql_executed") is False
+ and archive_proof.get("database_written") is False
+ and archive_proof.get("database_apply_authorized") is False
+ )
+ target_hash_locked = (
+ bool(archive_closeout.get("target_file"))
+ and bool(archive_closeout.get("expected_sha256"))
+ and bool(archive_closeout.get("actual_sha256"))
+ and archive_closeout.get("expected_sha256")
+ == archive_closeout.get("actual_sha256")
+ and archive_closeout.get("hash_matches") is True
+ )
+ rollback_and_verifier_bound = bool(
+ rollback_binding.get("rollback_binding_id")
+ ) and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ previous_closeouts_carried_forward = (
+ archive_closeout.get("no_write_ledger_retention_proof_closeout_only")
+ is True
+ and archive_closeout.get("retention_boundary_no_write_archive_proof_only")
+ is True
+ and archive_proof.get("source_no_write_ledger_retention_proof_closeout_id")
+ == source_closeout_id
+ and archive_proof.get("source_no_write_ledger_retention_proof_id")
+ == source_retention_id
+ and archive_proof.get("source_storage_boundary_no_write_ledger_proof_closeout_id")
+ == source_storage_closeout_id
+ )
+ contract_blocks_handoff_archive_retention_storage_persistence_and_apply = (
+ archive_contract.get(
+ "permits_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ )
+ is True
+ and archive_contract.get("retention_archive_write_allowed") is False
+ and archive_contract.get("ledger_retention_write_allowed") is False
+ and archive_contract.get("ledger_write_allowed") is False
+ and archive_contract.get("receipt_persistence_storage_write_allowed") is False
+ and archive_contract.get("persists_verifier_receipt") is False
+ and archive_contract.get("executes_database_apply") is False
+ and archive_contract.get("database_apply_authorized") is False
+ and archive_contract.get("writes_database") is False
+ )
+ handoff_manifest = {
+ "source_no_write_ledger_retention_proof_closeout_id": source_closeout_id,
+ "source_retention_boundary_no_write_archive_proof_id": source_archive_id,
+ "source_no_write_ledger_retention_proof_id": source_retention_id,
+ "source_storage_boundary_no_write_ledger_proof_closeout_id": (
+ source_storage_closeout_id
+ ),
+ "target_file": archive_closeout.get("target_file"),
+ "expected_sha256": archive_closeout.get("expected_sha256"),
+ "actual_sha256": archive_closeout.get("actual_sha256"),
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "manual_review_mode": "exception_only",
+ }
+ handoff_manifest_hash = hashlib.sha256(
+ json.dumps(handoff_manifest, sort_keys=True).encode("utf-8")
+ ).hexdigest()
+ handoff_bound = (
+ archive_closeout_ready
+ and archive_proof_ready
+ and bool(source_closeout_id)
+ and bool(source_archive_id)
+ and bool(source_retention_id)
+ and len(handoff_fields) == 12
+ and bool(handoff_manifest_hash)
+ )
+ handoff_write_blocked = True
+ nonsecret_machine_readable_handoff = (
+ handoff_bound
+ and bool(handoff_manifest_hash)
+ and handoff_manifest.get("manual_review_mode") == "exception_only"
+ )
+ side_effect_free = (
+ int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("retention_archive_written_count") or 0) == 0
+ and int(summary.get("ledger_retention_written_count") or 0) == 0
+ and int(summary.get("ledger_written_count") or 0) == 0
+ and int(summary.get("receipt_persistence_storage_written_count") or 0) == 0
+ and int(summary.get("persists_verifier_receipt_count") or 0) == 0
+ and int(summary.get("verifier_invoked_count") or 0) == 0
+ and int(summary.get("dry_run_executor_invoked_count") or 0) == 0
+ and int(summary.get("runner_invocation_performed_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and archive_proof.get("retention_archive_written") is False
+ and archive_proof.get("database_written") is False
+ and archive_proof.get("database_apply_authorized") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ )
+ archive_retention_sealed_handoff_proof = {
+ "archive_retention_sealed_handoff_proof_id": handoff_id,
+ "authorization_material_type": (
+ "controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ),
+ "source_no_write_ledger_retention_proof_closeout_id": source_closeout_id,
+ "source_retention_boundary_no_write_archive_proof_id": source_archive_id,
+ "source_no_write_ledger_retention_proof_id": source_retention_id,
+ "source_storage_boundary_no_write_ledger_proof_closeout_id": (
+ source_storage_closeout_id
+ ),
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": (
+ source_receipt_storage_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": (
+ source_receipt_storage_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_guard_closeout_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "required_command_shape_hash": archive_proof.get(
+ "required_command_shape_hash"
+ ),
+ "target_file": archive_closeout.get("target_file"),
+ "expected_sha256": archive_closeout.get("expected_sha256"),
+ "actual_sha256": archive_closeout.get("actual_sha256"),
+ "hash_matches": archive_closeout.get("hash_matches"),
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "sealed_handoff_manifest": handoff_manifest,
+ "sealed_handoff_manifest_hash": handoff_manifest_hash,
+ "handoff_status": "archive_retention_sealed_handoff_proof_preview_ready",
+ "handoff_mode": "archive_retention_sealed_handoff_proof_preview_only",
+ "archive_retention_sealed_handoff_proof_fields": handoff_fields,
+ "archive_retention_sealed_handoff_proof_field_count": len(handoff_fields),
+ "sealed_handoff_write_locked": True,
+ "sealed_handoff_write_allowed": False,
+ "sealed_handoff_written": False,
+ "retention_archive_write_locked": True,
+ "retention_archive_write_allowed": False,
+ "retention_archive_written": False,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_archive_retention_sealed_handoff_write_now": False,
+ "ready_for_retention_boundary_archive_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ checks = [
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "no_write_ledger_retention_proof_closeout_ready",
+ archive_closeout_ready,
+ {
+ "result": sealed_handoff_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_count"
+ ),
+ },
+ "wait_for_no_write_ledger_retention_proof_closeout",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "retention_boundary_no_write_archive_proof_ready",
+ archive_proof_ready,
+ {
+ "archive_status": archive_proof.get("archive_status"),
+ "archive_mode": archive_proof.get("archive_mode"),
+ "field_count": archive_proof.get(
+ "retention_boundary_no_write_archive_proof_field_count"
+ ),
+ },
+ "wait_for_retention_boundary_no_write_archive_proof",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "retention_boundary_no_write_archive_proof_no_write",
+ archive_no_write,
+ {
+ "retention_archive_write_allowed": archive_proof.get(
+ "retention_archive_write_allowed"
+ ),
+ "retention_archive_written": archive_proof.get(
+ "retention_archive_written"
+ ),
+ "database_written": archive_proof.get("database_written"),
+ "database_apply_authorized": archive_proof.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_archive_retention_storage_persistence_or_database_apply_allowed",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "archive_retention_sealed_handoff_proof_bound",
+ handoff_bound,
+ {
+ "handoff_id": handoff_id,
+ "source_archive_id": source_archive_id,
+ "source_closeout_id": source_closeout_id,
+ "field_count": len(handoff_fields),
+ },
+ "wait_for_archive_retention_sealed_handoff_proof",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "archive_retention_sealed_handoff_proof_blocks_handoff_write",
+ handoff_write_blocked,
+ {
+ "handoff_mode": archive_retention_sealed_handoff_proof.get(
+ "handoff_mode"
+ ),
+ "sealed_handoff_write_allowed": False,
+ "sealed_handoff_written": False,
+ },
+ "abort_if_sealed_handoff_write_allowed_or_written",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "source_closeout_only": archive_closeout.get(
+ "no_write_ledger_retention_proof_closeout_only"
+ ),
+ "source_archive_only": archive_closeout.get(
+ "retention_boundary_no_write_archive_proof_only"
+ ),
+ "source_previous_closeout_id": previous_retention_closeout.get(
+ "storage_boundary_no_write_ledger_proof_closeout_id"
+ ),
+ },
+ "wait_for_previous_closeout_chain",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": archive_closeout.get("target_file"),
+ "hash_matches": archive_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ archive_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(
+ archive_closeout.get("actual_sha256")
+ ),
+ },
+ "wait_for_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "no_write_ledger_retention_proof_closeout_contract_blocks_handoff_archive_retention_storage_persistence_and_database_apply",
+ contract_blocks_handoff_archive_retention_storage_persistence_and_apply,
+ {
+ "permits_future_archive_proof": archive_contract.get(
+ "permits_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ ),
+ "retention_archive_write_allowed": archive_contract.get(
+ "retention_archive_write_allowed"
+ ),
+ "database_apply_authorized": archive_contract.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_source_contract_allows_handoff_archive_retention_storage_persistence_or_database_apply",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "sealed_handoff_has_nonsecret_machine_readable_manifest",
+ nonsecret_machine_readable_handoff,
+ {
+ "manifest_hash_present": bool(handoff_manifest_hash),
+ "accepts_plaintext_secret": archive_retention_sealed_handoff_proof.get(
+ "accepts_plaintext_secret"
+ ),
+ "secret_material_included": archive_retention_sealed_handoff_proof.get(
+ "secret_material_included"
+ ),
+ "signature_material_included": archive_retention_sealed_handoff_proof.get(
+ "signature_material_included"
+ ),
+ },
+ "abort_if_sealed_handoff_manifest_contains_secret_signature_or_is_not_machine_readable",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "preview_has_no_side_effects_no_handoff_no_archive_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "retention_archive_written_count": summary.get(
+ "retention_archive_written_count", 0
+ ),
+ },
+ "abort_on_preview_handoff_archive_retention_ledger_storage_persistence_side_effect_execution_or_signing",
+ ),
+ _controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ archive_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": archive_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_RETENTION_BOUNDARY_NO_WRITE_ARCHIVE_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_LEDGER_RETENTION_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof = {
+ "retention_boundary_no_write_archive_proof_closeout_id": closeout_id,
+ "archive_retention_sealed_handoff_proof_id": handoff_id,
+ "source_no_write_ledger_retention_proof_closeout_id": source_closeout_id,
+ "source_retention_boundary_no_write_archive_proof_id": source_archive_id,
+ "source_no_write_ledger_retention_proof_id": source_retention_id,
+ "source_storage_boundary_no_write_ledger_proof_closeout_id": (
+ source_storage_closeout_id
+ ),
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": (
+ source_receipt_storage_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": (
+ source_receipt_storage_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_guard_closeout_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout": (
+ closeout_ready
+ ),
+ "retention_boundary_no_write_archive_proof_closeout_ready": closeout_ready,
+ "no_write_ledger_retention_proof_closeout_ready": archive_closeout_ready,
+ "retention_boundary_no_write_archive_proof_ready": archive_proof_ready,
+ "archive_retention_sealed_handoff_proof_bound": closeout_ready,
+ "sealed_handoff_write_locked": True,
+ "sealed_handoff_write_allowed": False,
+ "sealed_handoff_written": False,
+ "retention_archive_write_locked": True,
+ "retention_archive_write_allowed": False,
+ "retention_archive_written": False,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_archive_retention_sealed_handoff_write_now": False,
+ "ready_for_retention_boundary_archive_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ controlled_dry_run_retention_boundary_no_write_archive_proof_closeout = {
+ "retention_boundary_no_write_archive_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ ),
+ "source_no_write_ledger_retention_proof_closeout_id": source_closeout_id,
+ "source_retention_boundary_no_write_archive_proof_id": source_archive_id,
+ "source_no_write_ledger_retention_proof_id": source_retention_id,
+ "source_storage_boundary_no_write_ledger_proof_closeout_id": (
+ source_storage_closeout_id
+ ),
+ "source_storage_boundary_no_write_ledger_proof_id": source_ledger_id,
+ "source_receipt_persistence_storage_boundary_proof_closeout_id": (
+ source_receipt_storage_closeout_id
+ ),
+ "source_receipt_persistence_storage_boundary_proof_id": (
+ source_receipt_storage_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_closeout_id": (
+ source_guard_closeout_id
+ ),
+ "source_verifier_receipt_persistence_guard_proof_id": source_guard_id,
+ "required_command_shape_hash": archive_proof.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof": (
+ closeout_ready
+ ),
+ "retention_boundary_no_write_archive_proof_closeout_fields": (
+ closeout_fields
+ ),
+ "retention_boundary_no_write_archive_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "retention_boundary_no_write_archive_proof_closeout_acceptance_gates": (
+ acceptance_gates
+ ),
+ "retention_boundary_no_write_archive_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "archive_retention_sealed_handoff_proof": (
+ archive_retention_sealed_handoff_proof
+ ),
+ "archive_retention_sealed_handoff_proof_count": 1,
+ "archive_retention_sealed_handoff_proof_field_count": len(handoff_fields),
+ "retention_boundary_no_write_archive_proof": archive_proof,
+ "retention_boundary_no_write_archive_proof_count": 1,
+ "no_write_ledger_retention_proof_closeout": archive_closeout,
+ "no_write_ledger_retention_proof_closeout_count": 1,
+ "no_write_ledger_retention_proof": retention_proof,
+ "no_write_ledger_retention_proof_count": 1,
+ "storage_boundary_no_write_ledger_proof_closeout": (
+ previous_retention_closeout
+ ),
+ "storage_boundary_no_write_ledger_proof_closeout_count": 1,
+ "target_file": archive_closeout.get("target_file"),
+ "expected_sha256": archive_closeout.get("expected_sha256"),
+ "actual_sha256": archive_closeout.get("actual_sha256"),
+ "hash_matches": archive_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "retention_boundary_no_write_archive_proof_closeout_only": True,
+ "archive_retention_sealed_handoff_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "sealed_handoff_write_locked": True,
+ "sealed_handoff_write_allowed": False,
+ "sealed_handoff_written": False,
+ "retention_archive_write_locked": True,
+ "retention_archive_write_allowed": False,
+ "retention_archive_written": False,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_boundary_locked": True,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_archive_retention_sealed_handoff_write_now": False,
+ "ready_for_retention_boundary_archive_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract = {
+ "mode": "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_and_archive_retention_sealed_handoff_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout"
+ ),
+ "source_no_write_ledger_retention_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof": (
+ closeout_ready
+ ),
+ "sealed_handoff_write_locked": True,
+ "sealed_handoff_write_allowed": False,
+ "sealed_handoff_written": False,
+ "retention_archive_write_locked": True,
+ "retention_archive_write_allowed": False,
+ "retention_archive_written": False,
+ "ledger_retention_write_locked": True,
+ "ledger_retention_write_allowed": False,
+ "ledger_retention_written": False,
+ "ledger_write_allowed": False,
+ "ledger_written": False,
+ "receipt_persistence_storage_write_allowed": False,
+ "receipt_persistence_storage_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_archive_retention_sealed_handoff_write_now": False,
+ "ready_for_retention_boundary_archive_now": False,
+ "ready_for_no_write_ledger_retention_now": False,
+ "ready_for_storage_boundary_ledger_write_now": False,
+ "ready_for_receipt_persistence_storage_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_count": 1,
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "archive_retention_sealed_handoff_proof_count": 1,
+ "archive_retention_sealed_handoff_proof_field_count": len(
+ handoff_fields
+ ),
+ "sealed_handoff_write_locked_count": 1,
+ "sealed_handoff_write_allowed_count": 0,
+ "sealed_handoff_written_count": 0,
+ "retention_archive_write_allowed_count": 0,
+ "retention_archive_written_count": 0,
+ "ledger_retention_write_allowed_count": 0,
+ "ledger_retention_written_count": 0,
+ "storage_boundary_write_allowed_count": 0,
+ "storage_boundary_written_count": 0,
+ "ledger_write_allowed_count": 0,
+ "ledger_written_count": 0,
+ "receipt_persistence_storage_write_allowed_count": 0,
+ "receipt_persistence_storage_written_count": 0,
+ "verifier_receipt_persistence_allowed_count": 0,
+ "verifier_receipt_persisted_count": 0,
+ "persists_verifier_receipt_count": 0,
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_RETENTION_BOUNDARY_NO_WRITE_ARCHIVE_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(sealed_handoff_result.get("success")),
+ "generated_at": sealed_handoff_result.get("generated_at"),
+ "source_policy": sealed_handoff_result.get("policy"),
+ "stats": sealed_handoff_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof": (
+ future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout": (
+ controlled_dry_run_retention_boundary_no_write_archive_proof_closeout
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract": (
+ controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract
+ ),
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_checks": (
+ checks
+ ),
+ "source_controlled_dry_run_no_write_ledger_retention_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_no_write_ledger_retention_proof_closeout_contract": (
+ archive_contract
+ ),
+ "source_controlled_dry_run_no_write_ledger_retention_proof_closeout": (
+ archive_closeout
+ ),
+ "source_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof": (
+ future_archive
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future archive retention sealed handoff proof closeout.",
+ "Keep sealed handoff writes disabled until a later handoff closeout proves the handoff path.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, archive writes, handoff writes, ledger writes, ledger retention writes, verifier invocation, verifier receipt persistence, receipt storage, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out the sealed handoff proof into a verifier transfer proof."""
+ transfer_result = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ )
+ future_handoff = (
+ transfer_result.get(
+ "future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ )
+ or {}
+ )
+ handoff_closeout = (
+ transfer_result.get(
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ )
+ or {}
+ )
+ handoff_contract = (
+ transfer_result.get(
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract"
+ )
+ or {}
+ )
+ summary = transfer_result.get("summary") or {}
+ safety = transfer_result.get("safety") or {}
+ handoff_proof = (
+ handoff_closeout.get("archive_retention_sealed_handoff_proof") or {}
+ )
+ archive_proof = (
+ handoff_closeout.get("retention_boundary_no_write_archive_proof") or {}
+ )
+ source_archive_closeout = (
+ handoff_closeout.get("no_write_ledger_retention_proof_closeout") or {}
+ )
+ rollback_binding = handoff_closeout.get("rollback_binding") or {}
+ verifier_binding = handoff_closeout.get("post_apply_verifier_binding") or {}
+ source_closeout_id = handoff_closeout.get(
+ "retention_boundary_no_write_archive_proof_closeout_id"
+ )
+ source_handoff_id = handoff_proof.get("archive_retention_sealed_handoff_proof_id")
+ source_archive_id = handoff_proof.get(
+ "source_retention_boundary_no_write_archive_proof_id"
+ )
+ source_retention_closeout_id = handoff_proof.get(
+ "source_no_write_ledger_retention_proof_closeout_id"
+ )
+ closeout_id = (
+ _db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_id(
+ transfer_result
+ )
+ )
+ transfer_id = f"{closeout_id}-sealed-handoff-verifier-transfer-proof"
+ closeout_fields = [
+ "archive_retention_sealed_handoff_proof_closeout_id",
+ "source_retention_boundary_no_write_archive_proof_closeout_id",
+ "source_archive_retention_sealed_handoff_proof_id",
+ "source_retention_boundary_no_write_archive_proof_id",
+ "source_no_write_ledger_retention_proof_closeout_id",
+ "sealed_handoff_verifier_transfer_proof_id",
+ "sealed_handoff_manifest_hash",
+ "required_command_shape_hash",
+ "target_migration_file",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "verifier_transfer_manifest_hash",
+ ]
+ transfer_fields = [
+ "sealed_handoff_verifier_transfer_proof_id",
+ "source_archive_retention_sealed_handoff_proof_closeout_id",
+ "source_archive_retention_sealed_handoff_proof_id",
+ "source_retention_boundary_no_write_archive_proof_closeout_id",
+ "sealed_handoff_manifest_hash",
+ "verifier_transfer_manifest_hash",
+ "target_migration_file",
+ "expected_sha256",
+ "actual_sha256",
+ "rollback_binding_id",
+ "post_apply_verifier_binding_id",
+ "verifier_transfer_status",
+ ]
+ acceptance_gates = [
+ "retention_boundary_no_write_archive_proof_closeout_ready",
+ "archive_retention_sealed_handoff_proof_ready",
+ "sealed_handoff_manifest_hash_locked",
+ "sealed_handoff_verifier_transfer_proof_bound",
+ "sealed_handoff_verifier_transfer_blocks_verifier_invocation",
+ "previous_closeouts_carried_forward",
+ "target_migration_hash_locked",
+ "rollback_and_post_apply_verifier_bound",
+ "archive_retention_sealed_handoff_contract_blocks_handoff_verifier_execution_and_database_apply",
+ "preview_has_no_side_effects_no_handoff_no_verifier_no_receipt_no_execution_no_signing",
+ ]
+ abort_conditions = [
+ "abort_if_archive_retention_sealed_handoff_proof_not_ready",
+ "abort_if_sealed_handoff_proof_missing",
+ "abort_if_sealed_handoff_manifest_hash_missing_or_mismatched",
+ "abort_if_verifier_transfer_allows_invocation_or_receipt_persistence",
+ "abort_if_previous_closeout_ids_do_not_match",
+ "abort_if_target_migration_hash_is_not_locked",
+ "abort_if_rollback_or_post_apply_verifier_binding_missing",
+ "abort_if_any_secret_or_signature_material_is_included",
+ "abort_if_any_endpoint_sql_database_runner_verifier_or_executor_action_is_allowed",
+ "abort_if_manual_review_mode_is_not_exception_only",
+ ]
+ previous_ready = (
+ transfer_result.get("result")
+ == "DB_APPLY_CONTROLLED_DRY_RUN_RETENTION_BOUNDARY_NO_WRITE_ARCHIVE_PROOF_CLOSEOUT_READY"
+ and future_handoff.get(
+ "ready_for_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ )
+ is True
+ )
+ handoff_ready = (
+ handoff_proof.get("handoff_status")
+ == "archive_retention_sealed_handoff_proof_preview_ready"
+ and handoff_proof.get("handoff_mode")
+ == "archive_retention_sealed_handoff_proof_preview_only"
+ and handoff_proof.get("archive_retention_sealed_handoff_proof_field_count")
+ == 12
+ )
+ handoff_manifest = handoff_proof.get("sealed_handoff_manifest") or {}
+ expected_handoff_manifest_hash = hashlib.sha256(
+ json.dumps(handoff_manifest, sort_keys=True).encode("utf-8")
+ ).hexdigest()
+ sealed_handoff_manifest_hash_locked = (
+ bool(handoff_manifest)
+ and len(handoff_proof.get("sealed_handoff_manifest_hash") or "") == 64
+ and handoff_proof.get("sealed_handoff_manifest_hash")
+ == expected_handoff_manifest_hash
+ )
+ target_hash_locked = (
+ bool(handoff_closeout.get("target_file"))
+ and bool(handoff_closeout.get("expected_sha256"))
+ and bool(handoff_closeout.get("actual_sha256"))
+ and handoff_closeout.get("expected_sha256")
+ == handoff_closeout.get("actual_sha256")
+ and handoff_closeout.get("hash_matches") is True
+ )
+ rollback_and_verifier_bound = bool(
+ rollback_binding.get("rollback_binding_id")
+ ) and bool(verifier_binding.get("post_apply_verifier_binding_id"))
+ previous_closeouts_carried_forward = (
+ handoff_closeout.get("retention_boundary_no_write_archive_proof_closeout_only")
+ is True
+ and handoff_closeout.get("archive_retention_sealed_handoff_proof_only")
+ is True
+ and handoff_proof.get("source_no_write_ledger_retention_proof_closeout_id")
+ == source_retention_closeout_id
+ and handoff_proof.get("source_retention_boundary_no_write_archive_proof_id")
+ == source_archive_id
+ and archive_proof.get("archive_proof_id") == source_archive_id
+ )
+ source_contract_blocks_handoff_verifier_execution_and_apply = (
+ handoff_contract.get(
+ "permits_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ )
+ is True
+ and handoff_contract.get("sealed_handoff_write_allowed") is False
+ and handoff_contract.get("persists_verifier_receipt") is False
+ and handoff_contract.get("verifier_invoked") is False
+ and handoff_contract.get("executes_database_apply") is False
+ and handoff_contract.get("database_apply_authorized") is False
+ and handoff_contract.get("writes_database") is False
+ )
+ transfer_manifest = {
+ "source_retention_boundary_no_write_archive_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_archive_retention_sealed_handoff_proof_id": source_handoff_id,
+ "source_retention_boundary_no_write_archive_proof_id": source_archive_id,
+ "sealed_handoff_manifest_hash": handoff_proof.get(
+ "sealed_handoff_manifest_hash"
+ ),
+ "target_file": handoff_closeout.get("target_file"),
+ "expected_sha256": handoff_closeout.get("expected_sha256"),
+ "actual_sha256": handoff_closeout.get("actual_sha256"),
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "verifier_transfer_mode": "sealed_handoff_verifier_transfer_proof_preview_only",
+ "manual_review_mode": "exception_only",
+ }
+ verifier_transfer_manifest_hash = hashlib.sha256(
+ json.dumps(transfer_manifest, sort_keys=True).encode("utf-8")
+ ).hexdigest()
+ transfer_bound = (
+ previous_ready
+ and handoff_ready
+ and sealed_handoff_manifest_hash_locked
+ and bool(source_closeout_id)
+ and bool(source_handoff_id)
+ and bool(source_archive_id)
+ and len(transfer_fields) == 12
+ and bool(verifier_transfer_manifest_hash)
+ )
+ verifier_transfer_blocks_invocation = True
+ nonsecret_machine_readable_transfer = (
+ transfer_bound
+ and bool(verifier_transfer_manifest_hash)
+ and transfer_manifest.get("manual_review_mode") == "exception_only"
+ )
+ side_effect_free = (
+ int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_endpoint_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and int(summary.get("sealed_handoff_written_count") or 0) == 0
+ and int(summary.get("retention_archive_written_count") or 0) == 0
+ and int(summary.get("ledger_retention_written_count") or 0) == 0
+ and int(summary.get("ledger_written_count") or 0) == 0
+ and int(summary.get("receipt_persistence_storage_written_count") or 0) == 0
+ and int(summary.get("persists_verifier_receipt_count") or 0) == 0
+ and int(summary.get("verifier_invoked_count") or 0) == 0
+ and int(summary.get("dry_run_executor_invoked_count") or 0) == 0
+ and int(summary.get("runner_invocation_performed_count") or 0) == 0
+ and int(summary.get("signs_database_apply_authorization_count") or 0) == 0
+ and handoff_proof.get("sealed_handoff_written") is False
+ and handoff_proof.get("verifier_invoked") is False
+ and handoff_proof.get("database_written") is False
+ and handoff_proof.get("database_apply_authorized") is False
+ and safety.get("executes_endpoint") is False
+ and safety.get("executes_sql") is False
+ and safety.get("writes_database") is False
+ and safety.get("executes_database_apply") is False
+ )
+ sealed_handoff_verifier_transfer_proof = {
+ "sealed_handoff_verifier_transfer_proof_id": transfer_id,
+ "authorization_material_type": (
+ "controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ ),
+ "source_archive_retention_sealed_handoff_proof_closeout_id": closeout_id,
+ "source_retention_boundary_no_write_archive_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_archive_retention_sealed_handoff_proof_id": source_handoff_id,
+ "source_retention_boundary_no_write_archive_proof_id": source_archive_id,
+ "source_no_write_ledger_retention_proof_closeout_id": (
+ source_retention_closeout_id
+ ),
+ "required_command_shape_hash": handoff_proof.get(
+ "required_command_shape_hash"
+ ),
+ "target_file": handoff_closeout.get("target_file"),
+ "expected_sha256": handoff_closeout.get("expected_sha256"),
+ "actual_sha256": handoff_closeout.get("actual_sha256"),
+ "hash_matches": handoff_closeout.get("hash_matches"),
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ "sealed_handoff_manifest_hash": handoff_proof.get(
+ "sealed_handoff_manifest_hash"
+ ),
+ "verifier_transfer_manifest": transfer_manifest,
+ "verifier_transfer_manifest_hash": verifier_transfer_manifest_hash,
+ "verifier_transfer_status": (
+ "sealed_handoff_verifier_transfer_proof_preview_ready"
+ ),
+ "verifier_transfer_mode": (
+ "sealed_handoff_verifier_transfer_proof_preview_only"
+ ),
+ "sealed_handoff_verifier_transfer_proof_fields": transfer_fields,
+ "sealed_handoff_verifier_transfer_proof_field_count": len(
+ transfer_fields
+ ),
+ "verifier_transfer_write_locked": True,
+ "verifier_transfer_write_allowed": False,
+ "verifier_transfer_written": False,
+ "sealed_handoff_write_locked": True,
+ "sealed_handoff_write_allowed": False,
+ "sealed_handoff_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_archive_retention_sealed_handoff_write_now": False,
+ "ready_for_verifier_transfer_write_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ checks = [
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "retention_boundary_no_write_archive_proof_closeout_ready",
+ previous_ready,
+ {
+ "result": transfer_result.get("result"),
+ "ready_count": summary.get(
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_ready_count"
+ ),
+ },
+ "wait_for_retention_boundary_no_write_archive_proof_closeout",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "archive_retention_sealed_handoff_proof_ready",
+ handoff_ready,
+ {
+ "handoff_status": handoff_proof.get("handoff_status"),
+ "handoff_mode": handoff_proof.get("handoff_mode"),
+ "field_count": handoff_proof.get(
+ "archive_retention_sealed_handoff_proof_field_count"
+ ),
+ },
+ "wait_for_archive_retention_sealed_handoff_proof",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "sealed_handoff_manifest_hash_locked",
+ sealed_handoff_manifest_hash_locked,
+ {
+ "manifest_hash_present": bool(
+ handoff_proof.get("sealed_handoff_manifest_hash")
+ ),
+ "expected_hash_matches": sealed_handoff_manifest_hash_locked,
+ },
+ "wait_for_sealed_handoff_manifest_hash_lock",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "sealed_handoff_verifier_transfer_proof_bound",
+ transfer_bound,
+ {
+ "transfer_id": transfer_id,
+ "source_handoff_id": source_handoff_id,
+ "source_closeout_id": source_closeout_id,
+ "field_count": len(transfer_fields),
+ },
+ "wait_for_sealed_handoff_verifier_transfer_proof",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "sealed_handoff_verifier_transfer_blocks_verifier_invocation",
+ verifier_transfer_blocks_invocation,
+ {
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "persists_verifier_receipt": False,
+ },
+ "abort_if_verifier_transfer_allows_invocation_or_receipt_persistence",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "previous_closeouts_carried_forward",
+ previous_closeouts_carried_forward,
+ {
+ "source_handoff_only": handoff_closeout.get(
+ "archive_retention_sealed_handoff_proof_only"
+ ),
+ "source_closeout_only": handoff_closeout.get(
+ "retention_boundary_no_write_archive_proof_closeout_only"
+ ),
+ "source_archive_closeout_id": source_archive_closeout.get(
+ "no_write_ledger_retention_proof_closeout_id"
+ ),
+ },
+ "wait_for_previous_closeout_chain",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "target_migration_hash_locked",
+ target_hash_locked,
+ {
+ "target_file": handoff_closeout.get("target_file"),
+ "hash_matches": handoff_closeout.get("hash_matches"),
+ "expected_sha256_present": bool(
+ handoff_closeout.get("expected_sha256")
+ ),
+ "actual_sha256_present": bool(handoff_closeout.get("actual_sha256")),
+ },
+ "wait_for_target_migration_hash_lock",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "rollback_and_post_apply_verifier_bound",
+ rollback_and_verifier_bound,
+ {
+ "rollback_binding_id": rollback_binding.get("rollback_binding_id"),
+ "post_apply_verifier_binding_id": verifier_binding.get(
+ "post_apply_verifier_binding_id"
+ ),
+ },
+ "wait_for_rollback_and_post_apply_verifier_bindings",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "archive_retention_sealed_handoff_contract_blocks_handoff_verifier_execution_and_database_apply",
+ source_contract_blocks_handoff_verifier_execution_and_apply,
+ {
+ "permits_future_handoff_proof": handoff_contract.get(
+ "permits_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ),
+ "sealed_handoff_write_allowed": handoff_contract.get(
+ "sealed_handoff_write_allowed"
+ ),
+ "verifier_invoked": handoff_contract.get("verifier_invoked"),
+ "database_apply_authorized": handoff_contract.get(
+ "database_apply_authorized"
+ ),
+ },
+ "abort_if_source_contract_allows_handoff_verifier_execution_or_database_apply",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "sealed_handoff_verifier_transfer_has_nonsecret_machine_readable_manifest",
+ nonsecret_machine_readable_transfer,
+ {
+ "manifest_hash_present": bool(verifier_transfer_manifest_hash),
+ "accepts_plaintext_secret": sealed_handoff_verifier_transfer_proof.get(
+ "accepts_plaintext_secret"
+ ),
+ "secret_material_included": sealed_handoff_verifier_transfer_proof.get(
+ "secret_material_included"
+ ),
+ "signature_material_included": sealed_handoff_verifier_transfer_proof.get(
+ "signature_material_included"
+ ),
+ },
+ "abort_if_verifier_transfer_manifest_contains_secret_signature_or_is_not_machine_readable",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "preview_has_no_side_effects_no_handoff_no_verifier_no_receipt_no_execution_no_signing",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_endpoint_count": summary.get("executes_endpoint_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ "verifier_invoked_count": summary.get("verifier_invoked_count", 0),
+ },
+ "abort_on_preview_handoff_verifier_receipt_execution_or_signing_side_effect",
+ ),
+ _controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check(
+ "manual_review_not_required_for_safe_preview",
+ handoff_contract.get("manual_review_mode") == "exception_only"
+ and safety.get("manual_review_mode") == "exception_only",
+ {
+ "contract_manual_review_mode": handoff_contract.get(
+ "manual_review_mode"
+ ),
+ "safety_manual_review_mode": safety.get("manual_review_mode"),
+ },
+ "keep_manual_review_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_CONTROLLED_DRY_RUN_ARCHIVE_RETENTION_SEALED_HANDOFF_PROOF_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RETENTION_BOUNDARY_NO_WRITE_ARCHIVE_PROOF_CLOSEOUT"
+ )
+ future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof = {
+ "archive_retention_sealed_handoff_proof_closeout_id": closeout_id,
+ "sealed_handoff_verifier_transfer_proof_id": transfer_id,
+ "source_retention_boundary_no_write_archive_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_archive_retention_sealed_handoff_proof_id": source_handoff_id,
+ "source_retention_boundary_no_write_archive_proof_id": source_archive_id,
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof": (
+ closeout_ready
+ ),
+ "can_enter_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof_closeout": (
+ closeout_ready
+ ),
+ "archive_retention_sealed_handoff_proof_closeout_ready": closeout_ready,
+ "retention_boundary_no_write_archive_proof_closeout_ready": previous_ready,
+ "archive_retention_sealed_handoff_proof_ready": handoff_ready,
+ "sealed_handoff_manifest_hash_locked": sealed_handoff_manifest_hash_locked,
+ "sealed_handoff_verifier_transfer_proof_bound": closeout_ready,
+ "verifier_transfer_write_locked": True,
+ "verifier_transfer_write_allowed": False,
+ "verifier_transfer_written": False,
+ "sealed_handoff_write_locked": True,
+ "sealed_handoff_write_allowed": False,
+ "sealed_handoff_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_archive_retention_sealed_handoff_write_now": False,
+ "ready_for_verifier_transfer_write_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "writes_database": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ }
+ archive_retention_sealed_handoff_proof_closeout = {
+ "archive_retention_sealed_handoff_proof_closeout_id": closeout_id,
+ "authorization_material_type": (
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+ ),
+ "source_retention_boundary_no_write_archive_proof_closeout_id": (
+ source_closeout_id
+ ),
+ "source_archive_retention_sealed_handoff_proof_id": source_handoff_id,
+ "source_retention_boundary_no_write_archive_proof_id": source_archive_id,
+ "source_no_write_ledger_retention_proof_closeout_id": (
+ source_retention_closeout_id
+ ),
+ "required_command_shape_hash": handoff_proof.get(
+ "required_command_shape_hash"
+ ),
+ "status": closeout_status,
+ "ready_for_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof": (
+ closeout_ready
+ ),
+ "archive_retention_sealed_handoff_proof_closeout_fields": (
+ closeout_fields
+ ),
+ "archive_retention_sealed_handoff_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "archive_retention_sealed_handoff_proof_closeout_acceptance_gates": (
+ acceptance_gates
+ ),
+ "archive_retention_sealed_handoff_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "sealed_handoff_verifier_transfer_proof": (
+ sealed_handoff_verifier_transfer_proof
+ ),
+ "sealed_handoff_verifier_transfer_proof_count": 1,
+ "sealed_handoff_verifier_transfer_proof_field_count": len(
+ transfer_fields
+ ),
+ "archive_retention_sealed_handoff_proof": handoff_proof,
+ "archive_retention_sealed_handoff_proof_count": 1,
+ "retention_boundary_no_write_archive_proof_closeout": handoff_closeout,
+ "retention_boundary_no_write_archive_proof_closeout_count": 1,
+ "target_file": handoff_closeout.get("target_file"),
+ "expected_sha256": handoff_closeout.get("expected_sha256"),
+ "actual_sha256": handoff_closeout.get("actual_sha256"),
+ "hash_matches": handoff_closeout.get("hash_matches"),
+ "target_migration_hash_locked": target_hash_locked,
+ "rollback_binding": rollback_binding,
+ "rollback_binding_count": 1,
+ "post_apply_verifier_binding": verifier_binding,
+ "post_apply_verifier_binding_count": 1,
+ "abort_conditions": abort_conditions,
+ "abort_condition_count": len(abort_conditions),
+ "dry_run_only": True,
+ "check_mode_only": True,
+ "archive_retention_sealed_handoff_proof_closeout_only": True,
+ "sealed_handoff_verifier_transfer_proof_only": True,
+ "requires_fresh_production_truth_in_same_run": True,
+ "requires_post_apply_verifier": True,
+ "verifier_transfer_write_locked": True,
+ "verifier_transfer_write_allowed": False,
+ "verifier_transfer_written": False,
+ "sealed_handoff_write_locked": True,
+ "sealed_handoff_write_allowed": False,
+ "sealed_handoff_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "dry_run_executor_invoked": False,
+ "runner_invocation_performed": False,
+ "endpoint_executed": False,
+ "sql_executed": False,
+ "database_written": False,
+ "execution_receipt_present": False,
+ "runner_execution_authorized": False,
+ "dry_run_execution_authorized": False,
+ "execution_authorized": False,
+ "endpoint_execution_allowed": False,
+ "sql_execution_allowed": False,
+ "database_write_allowed": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_archive_retention_sealed_handoff_write_now": False,
+ "ready_for_verifier_transfer_write_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "database_apply_authorized": False,
+ "issues_database_apply_authorization": False,
+ "signs_database_apply_authorization": False,
+ "accepts_plaintext_secret": False,
+ "reads_secret_in_preview": False,
+ "signature_material_included": False,
+ "secret_material_included": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "captures_stdout": False,
+ "captures_stderr": False,
+ "stdout_included": False,
+ "stderr_included": False,
+ }
+ archive_retention_sealed_handoff_proof_closeout_contract = {
+ "mode": "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_and_sealed_handoff_verifier_transfer_proof_only",
+ "source_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-archive-retention-sealed-handoff-proof-closeout"
+ ),
+ "source_retention_boundary_no_write_archive_proof_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/"
+ "auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout"
+ ),
+ "machine_verifiable": True,
+ "permits_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof": (
+ closeout_ready
+ ),
+ "verifier_transfer_write_locked": True,
+ "verifier_transfer_write_allowed": False,
+ "verifier_transfer_written": False,
+ "sealed_handoff_write_locked": True,
+ "sealed_handoff_write_allowed": False,
+ "sealed_handoff_written": False,
+ "verifier_receipt_persistence_locked": True,
+ "verifier_receipt_persistence_allowed": False,
+ "verifier_receipt_persisted": False,
+ "persists_verifier_receipt": False,
+ "verifier_invocation_locked": True,
+ "verifier_invocation_allowed": False,
+ "verifier_invoked": False,
+ "verifier_receipt_present": False,
+ "ready_for_database_apply_now": False,
+ "ready_for_archive_retention_sealed_handoff_write_now": False,
+ "ready_for_verifier_transfer_write_now": False,
+ "ready_for_verifier_receipt_persistence_now": False,
+ "ready_for_verifier_invocation_now": False,
+ "ready_for_dry_run_executor_invocation_now": False,
+ "ready_for_actual_dry_run_execution_now": False,
+ "accepts_plaintext_secret": False,
+ "performs_detached_signature_verification": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "executes_endpoint": False,
+ "executes_sql": False,
+ "issues_database_apply_authorization": False,
+ "database_apply_authorized": False,
+ "signs_database_apply_authorization": False,
+ "writes_database": False,
+ "executes_in_preview": False,
+ "secret_material_required_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ output_summary = dict(summary)
+ output_summary.update(
+ {
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_ready_count": (
+ 1 if closeout_ready else 0
+ ),
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check_count": len(
+ checks
+ ),
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_pass_count": (
+ passed_count
+ ),
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_waiting_count": len(
+ waiting_checks
+ ),
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_count": 1,
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_field_count": len(
+ closeout_fields
+ ),
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_acceptance_gate_count": len(
+ acceptance_gates
+ ),
+ "sealed_handoff_verifier_transfer_proof_count": 1,
+ "sealed_handoff_verifier_transfer_proof_field_count": len(
+ transfer_fields
+ ),
+ "sealed_handoff_manifest_hash_locked_count": (
+ 1 if sealed_handoff_manifest_hash_locked else 0
+ ),
+ "verifier_transfer_write_locked_count": 1,
+ "verifier_transfer_write_allowed_count": 0,
+ "verifier_transfer_written_count": 0,
+ "sealed_handoff_write_allowed_count": 0,
+ "sealed_handoff_written_count": 0,
+ "persists_verifier_receipt_count": 0,
+ "verifier_invoked_count": 0,
+ "verifier_receipt_present_count": 0,
+ "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,
+ "dry_run_executor_invoked_count": 0,
+ "runner_invocation_performed_count": 0,
+ "endpoint_executed_count": 0,
+ "sql_executed_count": 0,
+ "database_written_count": 0,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: (
+ summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0) or 0
+ ),
+ }
+ )
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_CONTROLLED_DRY_RUN_ARCHIVE_RETENTION_SEALED_HANDOFF_PROOF_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(transfer_result.get("success")),
+ "generated_at": transfer_result.get("generated_at"),
+ "source_policy": transfer_result.get("policy"),
+ "stats": transfer_result.get("stats") or {},
+ "summary": output_summary,
+ "future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof": (
+ future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof
+ ),
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout": (
+ archive_retention_sealed_handoff_proof_closeout
+ ),
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_contract": (
+ archive_retention_sealed_handoff_proof_closeout_contract
+ ),
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_checks": (
+ checks
+ ),
+ "source_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_summary": (
+ summary
+ ),
+ "source_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract": (
+ handoff_contract
+ ),
+ "source_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout": (
+ handoff_closeout
+ ),
+ "source_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof": (
+ future_handoff
+ ),
+ "safety": {
+ "read_only_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout": True,
+ "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,
+ "performs_detached_signature_verification": False,
+ "persists_verifier_receipt": False,
+ "executes_authorization_evidence": False,
+ "executes_database_apply": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout to build a future sealed handoff verifier transfer proof closeout.",
+ "Keep verifier invocation and verifier receipt persistence disabled until a later verifier transfer closeout proves the transfer boundary.",
+ "This closeout still does not authorize endpoint execution, SQL, DB writes, handoff writes, verifier invocation, verifier receipt persistence, receipt storage, or database apply.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_lane_guard(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Guard the future DB apply authorization lane without issuing authorization."""
+ closeout = build_pchome_auto_policy_db_apply_authorization_request_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ package = closeout.get("final_exact_request_package") or {}
+ manifest = closeout.get("machine_request_manifest") or {}
+ summary = closeout.get("summary") or {}
+ safety = closeout.get("safety") or {}
+ template = package.get("exact_request_payload_template") or {}
+ manifest_steps = manifest.get("manifest_steps") or []
+ side_effect_free = (
+ int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and package.get("reads_secret_in_preview") is False
+ and package.get("executes_shell_in_preview") is False
+ and package.get("executes_sql_in_preview") is False
+ and package.get("writes_database_in_preview") is False
+ and safety.get("writes_database") is False
+ )
+ template_has_truth_gate = (
+ isinstance(template.get("fresh_production_truth"), dict)
+ and template["fresh_production_truth"].get("same_run_only") is True
+ and template["fresh_production_truth"].get("required") is True
+ )
+ manifest_step_names = {step.get("name") for step in manifest_steps}
+ checks = [
+ _authorization_lane_guard_check(
+ "request_closeout_ready",
+ closeout.get("result") == "DB_APPLY_AUTHORIZATION_REQUEST_CLOSEOUT_READY"
+ and package.get("ready_for_exact_authorization_request_package") is True,
+ {
+ "result": closeout.get("result"),
+ "ready_for_exact_authorization_request_package": package.get(
+ "ready_for_exact_authorization_request_package"
+ ),
+ },
+ "wait_for_authorization_request_closeout",
+ ),
+ _authorization_lane_guard_check(
+ "package_does_not_issue_apply_authorization",
+ package.get("issues_database_apply_authorization") is False
+ and package.get("ready_for_database_apply_now") is False,
+ {
+ "issues_database_apply_authorization": package.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": package.get("ready_for_database_apply_now"),
+ },
+ "block_if_package_issues_apply_authorization",
+ ),
+ _authorization_lane_guard_check(
+ "manifest_does_not_issue_apply_authorization",
+ manifest.get("issues_database_apply_authorization") is False
+ and manifest.get("writes_database") is False
+ and manifest.get("executes_in_preview") is False,
+ {
+ "issues_database_apply_authorization": manifest.get(
+ "issues_database_apply_authorization"
+ ),
+ "writes_database": manifest.get("writes_database"),
+ "executes_in_preview": manifest.get("executes_in_preview"),
+ },
+ "block_if_manifest_executes_or_authorizes",
+ ),
+ _authorization_lane_guard_check(
+ "fresh_production_truth_same_run_required",
+ template_has_truth_gate,
+ {"fresh_production_truth": template.get("fresh_production_truth")},
+ "require_same_run_production_truth",
+ ),
+ _authorization_lane_guard_check(
+ "exact_request_payload_complete",
+ int(summary.get("exact_request_payload_field_count") or 0) == 10
+ and package.get("payload_template_field_count") == 10,
+ {
+ "exact_request_payload_field_count": summary.get(
+ "exact_request_payload_field_count", 0
+ ),
+ "payload_template_field_count": package.get("payload_template_field_count"),
+ },
+ "wait_for_exact_request_payload",
+ ),
+ _authorization_lane_guard_check(
+ "machine_manifest_complete",
+ int(summary.get("machine_request_manifest_step_count") or 0) == 6
+ and manifest.get("manifest_step_count") == 6,
+ {
+ "machine_request_manifest_step_count": summary.get(
+ "machine_request_manifest_step_count", 0
+ ),
+ "manifest_step_count": manifest.get("manifest_step_count"),
+ },
+ "wait_for_machine_request_manifest",
+ ),
+ _authorization_lane_guard_check(
+ "secret_rejection_step_present",
+ "reject_secret_material" in manifest_step_names
+ and template.get("operator_acknowledges_secret_boundary") is True,
+ {
+ "manifest_step_names": sorted(name for name in manifest_step_names if name),
+ "operator_acknowledges_secret_boundary": template.get(
+ "operator_acknowledges_secret_boundary"
+ ),
+ },
+ "block_until_secret_rejection_step_exists",
+ ),
+ _authorization_lane_guard_check(
+ "rollback_acknowledgement_present",
+ template.get("operator_acknowledges_rollback_boundary") is True,
+ {
+ "operator_acknowledges_rollback_boundary": template.get(
+ "operator_acknowledges_rollback_boundary"
+ ),
+ },
+ "block_until_rollback_boundary_acknowledged",
+ ),
+ _authorization_lane_guard_check(
+ "migration_hash_locked",
+ bool(package.get("target_file"))
+ and bool(package.get("expected_sha256"))
+ and package.get("hash_matches") is True,
+ {
+ "target_file": package.get("target_file"),
+ "hash_matches": package.get("hash_matches"),
+ },
+ "abort_on_migration_hash_gap",
+ ),
+ _authorization_lane_guard_check(
+ "preview_has_no_side_effects",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ },
+ "abort_on_preview_side_effect",
+ ),
+ _authorization_lane_guard_check(
+ "source_intake_and_closeout_ids_present",
+ bool(package.get("source_intake_id"))
+ and bool(package.get("source_closeout_boundary_id"))
+ and bool(package.get("source_dry_run_shell_preview_id")),
+ {
+ "source_intake_id": package.get("source_intake_id"),
+ "source_closeout_boundary_id": package.get("source_closeout_boundary_id"),
+ "source_dry_run_shell_preview_id": package.get("source_dry_run_shell_preview_id"),
+ },
+ "wait_for_source_proof_ids",
+ ),
+ _authorization_lane_guard_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0,
+ {LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0)},
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ lane_ready = not waiting_checks
+ lane_status = (
+ "DB_APPLY_AUTHORIZATION_LANE_GUARD_READY"
+ if lane_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_REQUEST_CLOSEOUT"
+ )
+ lane_entry_requirements = [
+ {
+ "key": "production_truth_refreshed_in_same_run",
+ "required": True,
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ },
+ {
+ "key": "exact_request_payload_matches_template",
+ "required": True,
+ "field_count": len(template),
+ },
+ {
+ "key": "migration_file_hash_matches_package",
+ "required": True,
+ "target_file": package.get("target_file"),
+ "expected_sha256": package.get("expected_sha256"),
+ },
+ {
+ "key": "secret_material_absent_from_request",
+ "required": True,
+ "rejects_database_url": True,
+ "rejects_authorization_header": True,
+ "rejects_cookie": True,
+ },
+ {
+ "key": "rollback_boundary_acknowledged",
+ "required": True,
+ },
+ {
+ "key": "direct_apply_rejected_until_next_lane",
+ "required": True,
+ "issues_database_apply_authorization": False,
+ },
+ ]
+ lane_transfer_contract = {
+ "mode": "future_authorization_lane_entry_guard_only",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-lane-guard",
+ "source_request_closeout_endpoint": (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-closeout"
+ ),
+ "entry_requirement_count": len(lane_entry_requirements),
+ "machine_verifiable": True,
+ "issues_database_apply_authorization": False,
+ "ready_for_database_apply_now": False,
+ "executes_in_preview": False,
+ "writes_database": False,
+ }
+ future_authorization_lane_guard = {
+ "guard_id": _db_apply_authorization_lane_guard_id(closeout),
+ "source_closeout_package_id": package.get("package_id"),
+ "source_intake_id": package.get("source_intake_id"),
+ "source_closeout_boundary_id": package.get("source_closeout_boundary_id"),
+ "source_dry_run_shell_preview_id": package.get("source_dry_run_shell_preview_id"),
+ "status": lane_status,
+ "ready_for_future_authorization_lane_entry": lane_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "request_scope": "future_explicit_db_apply_authorization_only",
+ "target_file": package.get("target_file"),
+ "expected_sha256": package.get("expected_sha256"),
+ "actual_sha256": package.get("actual_sha256"),
+ "hash_matches": package.get("hash_matches"),
+ "requires_fresh_production_truth_in_same_run": True,
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "executes_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_LANE_GUARD_POLICY,
+ "result": lane_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "authorization_lane_guard_ready_count": 1 if lane_ready else 0,
+ "lane_guard_check_count": len(checks),
+ "lane_guard_pass_count": passed_count,
+ "lane_guard_waiting_count": len(waiting_checks),
+ "authorization_request_closeout_ready_count": summary.get(
+ "authorization_request_closeout_ready_count", 0
+ ),
+ "exact_request_payload_field_count": summary.get(
+ "exact_request_payload_field_count", 0
+ ),
+ "machine_request_manifest_step_count": summary.get(
+ "machine_request_manifest_step_count", 0
+ ),
+ "lane_entry_requirement_count": len(lane_entry_requirements),
+ "required_request_evidence_count": summary.get("required_request_evidence_count", 0),
+ "authorization_acceptance_gate_count": summary.get(
+ "authorization_acceptance_gate_count", 0
+ ),
+ "rejection_reason_count": summary.get("rejection_reason_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "future_authorization_lane_guard": future_authorization_lane_guard,
+ "lane_transfer_contract": lane_transfer_contract,
+ "lane_entry_requirements": lane_entry_requirements,
+ "lane_guard_checks": checks,
+ "source_request_closeout_summary": summary,
+ "source_final_exact_request_package": package,
+ "source_machine_request_manifest": manifest,
+ "safety": {
+ "read_only_db_apply_authorization_lane_guard": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this guard to decide whether the exact request package can enter a future authorization lane.",
+ "Keep DB apply authorization, shell execution, SQL execution, and database writes blocked in this guard.",
+ "Require fresh production truth in the future authorization lane before any apply decision is issued.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_request_closeout(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Close out authorization request intake into a final exact request package."""
+ intake = build_pchome_auto_policy_db_apply_authorization_request_intake(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ request_intake = intake.get("authorization_request_intake") or {}
+ envelope = intake.get("authorization_envelope") or {}
+ summary = intake.get("summary") or {}
+ schema = intake.get("request_payload_schema") or {}
+ acceptance_gates = intake.get("authorization_acceptance_gates") or []
+ evidence = intake.get("required_request_evidence") or []
+ rejection_reasons = intake.get("rejection_reasons") or []
+ required_fields = schema.get("required_fields") or []
+ side_effect_free = (
+ int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and request_intake.get("reads_secret_in_preview") is False
+ and request_intake.get("executes_shell_in_preview") is False
+ and request_intake.get("executes_sql_in_preview") is False
+ and request_intake.get("writes_database_in_preview") is False
+ )
+ checks = [
+ _authorization_request_closeout_check(
+ "authorization_request_intake_ready",
+ intake.get("result") == "DB_APPLY_AUTHORIZATION_REQUEST_INTAKE_READY"
+ and request_intake.get("ready_for_authorization_request_intake") is True,
+ {
+ "result": intake.get("result"),
+ "ready_for_authorization_request_intake": request_intake.get(
+ "ready_for_authorization_request_intake"
+ ),
+ },
+ "wait_for_authorization_request_intake",
+ ),
+ _authorization_request_closeout_check(
+ "source_closeout_ready",
+ int(summary.get("closeout_ready_count") or 0) == 1,
+ {"closeout_ready_count": summary.get("closeout_ready_count", 0)},
+ "wait_for_controlled_dry_run_shell_closeout",
+ ),
+ _authorization_request_closeout_check(
+ "required_request_evidence_complete",
+ len(evidence) == 7 and int(summary.get("required_request_evidence_count") or 0) == 7,
+ {"required_request_evidence_count": summary.get("required_request_evidence_count", 0)},
+ "wait_for_required_request_evidence",
+ ),
+ _authorization_request_closeout_check(
+ "request_schema_complete",
+ len(required_fields) == 10
+ and int(summary.get("request_payload_required_field_count") or 0) == 10,
+ {"required_fields": required_fields},
+ "wait_for_request_payload_schema",
+ ),
+ _authorization_request_closeout_check(
+ "acceptance_gates_all_passed",
+ len(acceptance_gates) == 11
+ and int(summary.get("authorization_acceptance_waiting_count", 0) or 0) == 0,
+ {
+ "authorization_acceptance_gate_count": summary.get(
+ "authorization_acceptance_gate_count", 0
+ ),
+ "authorization_acceptance_waiting_count": summary.get(
+ "authorization_acceptance_waiting_count", 0
+ ),
+ },
+ "route_failed_acceptance_gate_to_exception_review",
+ ),
+ _authorization_request_closeout_check(
+ "rejection_policy_complete",
+ len(rejection_reasons) == 10 and "direct_database_apply_requested_from_intake" in rejection_reasons,
+ {"rejection_reason_count": len(rejection_reasons)},
+ "wait_for_rejection_policy",
+ ),
+ _authorization_request_closeout_check(
+ "envelope_accepts_request_but_not_apply",
+ envelope.get("accepts_authorization_request") is True
+ and envelope.get("issues_database_apply_authorization") is False
+ and envelope.get("ready_for_database_apply_now") is False,
+ {
+ "accepts_authorization_request": envelope.get("accepts_authorization_request"),
+ "issues_database_apply_authorization": envelope.get(
+ "issues_database_apply_authorization"
+ ),
+ "ready_for_database_apply_now": envelope.get("ready_for_database_apply_now"),
+ },
+ "block_if_intake_issues_apply_authorization",
+ ),
+ _authorization_request_closeout_check(
+ "migration_target_and_hash_locked",
+ bool(request_intake.get("target_file"))
+ and bool(request_intake.get("expected_sha256"))
+ and request_intake.get("hash_matches") is True,
+ {
+ "target_file": request_intake.get("target_file"),
+ "hash_matches": request_intake.get("hash_matches"),
+ },
+ "abort_on_target_or_hash_gap",
+ ),
+ _authorization_request_closeout_check(
+ "secret_boundary_future_shell_only",
+ request_intake.get("operator_secret_boundary") == "future_shell_only"
+ and request_intake.get("reads_secret_in_preview") is False,
+ {
+ "operator_secret_boundary": request_intake.get("operator_secret_boundary"),
+ "reads_secret_in_preview": request_intake.get("reads_secret_in_preview"),
+ },
+ "abort_on_secret_boundary_violation",
+ ),
+ _authorization_request_closeout_check(
+ "preview_has_no_shell_sql_or_db_side_effect",
+ side_effect_free,
+ {
+ "reads_secret_count": summary.get("reads_secret_count", 0),
+ "executes_script_count": summary.get("executes_script_count", 0),
+ "executes_sql_count": summary.get("executes_sql_count", 0),
+ "writes_database_count": summary.get("writes_database_count", 0),
+ },
+ "abort_on_preview_side_effect",
+ ),
+ _authorization_request_closeout_check(
+ "direct_apply_rejected",
+ envelope.get("rejects_direct_database_apply") is True,
+ {"rejects_direct_database_apply": envelope.get("rejects_direct_database_apply")},
+ "reject_direct_database_apply_request",
+ ),
+ _authorization_request_closeout_check(
+ "manual_review_regression_absent",
+ int(summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY) or 0) == 0,
+ {LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0)},
+ "route_failed_verifier_to_exception_only",
+ ),
+ ]
+ passed_count = sum(1 for check in checks if check.get("passed"))
+ waiting_checks = [check for check in checks if not check.get("passed")]
+ closeout_ready = not waiting_checks
+ closeout_status = (
+ "DB_APPLY_AUTHORIZATION_REQUEST_CLOSEOUT_READY"
+ if closeout_ready
+ else "WAITING_FOR_DB_APPLY_AUTHORIZATION_REQUEST_INTAKE"
+ )
+ exact_request_payload_template = {
+ "requester": "{future_automation_or_operator_identity}",
+ "requested_at": "{utc_iso8601}",
+ "reason": "apply additive PChome auto-policy evidence receipts migration",
+ "target_file": request_intake.get("target_file"),
+ "expected_sha256": request_intake.get("expected_sha256"),
+ "closeout_boundary_id": request_intake.get("source_closeout_boundary_id"),
+ "dry_run_shell_preview_id": request_intake.get("source_dry_run_shell_preview_id"),
+ "fresh_production_truth": {
+ "required": True,
+ "same_run_only": True,
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ },
+ "operator_acknowledges_secret_boundary": True,
+ "operator_acknowledges_rollback_boundary": True,
+ }
+ machine_request_manifest_steps = [
+ {
+ "name": "refresh_production_truth_in_same_run",
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ "executes_in_preview": False,
+ },
+ {
+ "name": "refresh_authorization_request_closeout",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-closeout",
+ "executes_in_preview": False,
+ },
+ {
+ "name": "fill_exact_request_payload",
+ "required_fields": required_fields,
+ "writes_artifact_in_preview": False,
+ },
+ {
+ "name": "reject_secret_material",
+ "rejects_database_url": True,
+ "rejects_authorization_header": True,
+ "rejects_cookie": True,
+ },
+ {
+ "name": "run_acceptance_gates",
+ "gate_count": len(acceptance_gates),
+ "executes_sql_in_preview": False,
+ },
+ {
+ "name": "emit_request_to_future_apply_authorization_lane",
+ "issues_database_apply_authorization": False,
+ "writes_database": False,
+ },
+ ]
+ final_exact_request_package = {
+ "package_id": _db_apply_authorization_request_closeout_id(intake),
+ "source_intake_id": request_intake.get("intake_id"),
+ "source_closeout_boundary_id": request_intake.get("source_closeout_boundary_id"),
+ "source_dry_run_shell_preview_id": request_intake.get("source_dry_run_shell_preview_id"),
+ "status": closeout_status,
+ "ready_for_exact_authorization_request_package": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "issues_database_apply_authorization": False,
+ "request_scope": "future_explicit_db_apply_authorization_only",
+ "target_file": request_intake.get("target_file"),
+ "expected_sha256": request_intake.get("expected_sha256"),
+ "actual_sha256": request_intake.get("actual_sha256"),
+ "hash_matches": request_intake.get("hash_matches"),
+ "exact_request_payload_template": exact_request_payload_template,
+ "payload_template_field_count": len(exact_request_payload_template),
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "executes_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_REQUEST_CLOSEOUT_POLICY,
+ "result": closeout_status,
+ "success": bool(intake.get("success")),
+ "generated_at": intake.get("generated_at"),
+ "source_policy": intake.get("policy"),
+ "stats": intake.get("stats") or {},
+ "summary": {
+ "authorization_request_closeout_ready_count": 1 if closeout_ready else 0,
+ "closeout_check_count": len(checks),
+ "closeout_pass_count": passed_count,
+ "closeout_waiting_count": len(waiting_checks),
+ "authorization_request_intake_ready_count": summary.get(
+ "authorization_request_intake_ready_count", 0
+ ),
+ "required_request_evidence_count": len(evidence),
+ "request_payload_required_field_count": len(required_fields),
+ "authorization_acceptance_gate_count": len(acceptance_gates),
+ "rejection_reason_count": len(rejection_reasons),
+ "exact_request_payload_field_count": len(exact_request_payload_template),
+ "machine_request_manifest_step_count": len(machine_request_manifest_steps),
+ "closeout_ready_count": summary.get("closeout_ready_count", 0),
+ "future_apply_boundary_count": summary.get("future_apply_boundary_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "authorization_request_closeout": {
+ "status": closeout_status,
+ "ready_for_exact_authorization_request_package": closeout_ready,
+ "ready_for_database_apply_now": False,
+ "waiting_checks": waiting_checks,
+ "manual_review_mode": "exception_only",
+ },
+ "final_exact_request_package": final_exact_request_package,
+ "machine_request_manifest": {
+ "mode": "future_apply_authorization_lane_only",
+ "source_endpoint": "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-closeout",
+ "manifest_steps": machine_request_manifest_steps,
+ "manifest_step_count": len(machine_request_manifest_steps),
+ "issues_database_apply_authorization": False,
+ "executes_in_preview": False,
+ "writes_database": False,
+ },
+ "closeout_checks": checks,
+ "source_intake_summary": summary,
+ "source_request_payload_schema": schema,
+ "source_rejection_reasons": rejection_reasons,
+ "safety": {
+ "read_only_db_apply_authorization_request_closeout": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this closeout as the exact request payload template for the future apply authorization lane.",
+ "Keep the future lane from issuing DB apply authorization unless fresh production truth is present in the same run.",
+ "Reject direct apply, secrets, shell execution, SQL execution, or DB writes from this closeout.",
+ ],
+ }
+
+
+def build_pchome_auto_policy_db_apply_authorization_request_intake(
+ payload: dict[str, Any],
+ batch_size: int = 12,
+ *,
+ execute_fetch: bool = False,
+ timeout_seconds: int = PCHOME_FETCH_DEFAULT_TIMEOUT_SECONDS,
+ http_get: Any = None,
+) -> dict[str, Any]:
+ """Build a no-write intake envelope for a future explicit DB apply authorization request."""
+ closeout = build_pchome_auto_policy_db_apply_controlled_dry_run_shell_closeout(
+ payload,
+ batch_size=batch_size,
+ execute_fetch=execute_fetch,
+ timeout_seconds=timeout_seconds,
+ http_get=http_get,
+ )
+ boundary = closeout.get("explicit_authorization_boundary") or {}
+ summary = closeout.get("summary") or {}
+ safety = closeout.get("safety") or {}
+ closeout_ready = (
+ closeout.get("result") == "DB_APPLY_CONTROLLED_DRY_RUN_SHELL_CLOSEOUT_READY"
+ and boundary.get("ready_for_explicit_apply_authorization_boundary") is True
+ )
+ side_effect_free = (
+ int(summary.get("writes_script_count") or 0) == 0
+ and int(summary.get("reads_secret_count") or 0) == 0
+ and int(summary.get("executes_script_count") or 0) == 0
+ and int(summary.get("executes_sql_count") or 0) == 0
+ and int(summary.get("writes_database_count") or 0) == 0
+ and boundary.get("reads_secret_in_preview") is False
+ and boundary.get("executes_shell_in_preview") is False
+ and boundary.get("executes_sql_in_preview") is False
+ and boundary.get("writes_database_in_preview") is False
+ and safety.get("writes_database") is False
+ )
+ target_file = boundary.get("target_file")
+ expected_sha256 = boundary.get("expected_sha256")
+ actual_sha256 = boundary.get("actual_sha256")
+ hash_matches = boundary.get("hash_matches")
+ intake_ready = closeout_ready and side_effect_free and bool(target_file) and hash_matches is True
+ intake_status = (
+ "DB_APPLY_AUTHORIZATION_REQUEST_INTAKE_READY"
+ if intake_ready
+ else "WAITING_FOR_CONTROLLED_DRY_RUN_SHELL_CLOSEOUT"
+ )
+ required_request_evidence = [
+ {
+ "key": "fresh_production_truth_same_run",
+ "required": True,
+ "source_command": "python scripts/ops/check_production_version_truth.py",
+ },
+ {
+ "key": "controlled_dry_run_shell_closeout_id",
+ "required": True,
+ "source_id": boundary.get("boundary_id"),
+ },
+ {
+ "key": "final_handoff_package_id",
+ "required": True,
+ "source_id": boundary.get("source_final_handoff_package_id"),
+ },
+ {
+ "key": "dry_run_shell_preview_id",
+ "required": True,
+ "source_id": boundary.get("source_dry_run_shell_preview_id"),
+ },
+ {
+ "key": "migration_file_hash",
+ "required": True,
+ "target_file": target_file,
+ "expected_sha256": expected_sha256,
+ "actual_sha256": actual_sha256,
+ "hash_matches": hash_matches,
+ },
+ {
+ "key": "future_apply_boundaries",
+ "required": True,
+ "boundary_count": summary.get("future_apply_boundary_count", 0),
+ },
+ {
+ "key": "no_secret_no_shell_no_sql_no_db_preview_counters",
+ "required": True,
+ "reads_secret_count": 0,
+ "executes_script_count": 0,
+ "executes_sql_count": 0,
+ "writes_database_count": 0,
+ },
+ ]
+ request_payload_schema = {
+ "required_fields": [
+ "requester",
+ "requested_at",
+ "reason",
+ "target_file",
+ "expected_sha256",
+ "closeout_boundary_id",
+ "dry_run_shell_preview_id",
+ "fresh_production_truth",
+ "operator_acknowledges_secret_boundary",
+ "operator_acknowledges_rollback_boundary",
+ ],
+ "optional_fields": [
+ "change_window_utc",
+ "rollback_contact",
+ "ticket_url",
+ "evidence_bundle_url",
+ ],
+ "rejects_extra_secret_material": True,
+ "accepts_database_url": False,
+ "accepts_authorization_header": False,
+ "accepts_cookie": False,
+ }
+ authorization_acceptance_gates = [
+ {
+ "key": "closeout_result_ready",
+ "passed": closeout.get("result") == "DB_APPLY_CONTROLLED_DRY_RUN_SHELL_CLOSEOUT_READY",
+ "failure_route": "refresh_controlled_dry_run_shell_closeout",
+ },
+ {
+ "key": "explicit_boundary_ready",
+ "passed": boundary.get("ready_for_explicit_apply_authorization_boundary") is True,
+ "failure_route": "wait_for_explicit_authorization_boundary",
+ },
+ {
+ "key": "closeout_checks_all_passed",
+ "passed": int(summary.get("closeout_waiting_count") or 0) == 0
+ and int(summary.get("closeout_pass_count") or 0) == int(summary.get("closeout_check_count") or -1),
+ "failure_route": "route_failed_closeout_checks_to_exception_review",
+ },
+ {
+ "key": "future_apply_boundaries_complete",
+ "passed": int(summary.get("future_apply_boundary_count") or 0) >= 6,
+ "failure_route": "wait_for_future_apply_boundaries",
+ },
+ {
+ "key": "production_truth_refresh_required",
+ "passed": boundary.get("requires_fresh_production_truth_in_future_run") is True,
+ "failure_route": "abort_without_fresh_production_truth",
+ },
+ {
+ "key": "target_file_present",
+ "passed": bool(target_file),
+ "failure_route": "wait_for_migration_file_target",
+ },
+ {
+ "key": "migration_hash_present_and_matches",
+ "passed": bool(expected_sha256) and bool(actual_sha256) and hash_matches is True,
+ "failure_route": "abort_on_migration_hash_mismatch",
+ },
+ {
+ "key": "final_handoff_source_present",
+ "passed": bool(boundary.get("source_final_handoff_package_id")),
+ "failure_route": "wait_for_final_handoff_package",
+ },
+ {
+ "key": "shell_secret_boundary_clean",
+ "passed": boundary.get("operator_secret_boundary") == "future_shell_only"
+ and boundary.get("reads_secret_in_preview") is False,
+ "failure_route": "abort_on_secret_boundary_violation",
+ },
+ {
+ "key": "preview_has_no_side_effects",
+ "passed": side_effect_free,
+ "failure_route": "abort_on_preview_side_effect",
+ },
+ {
+ "key": "explicit_request_payload_schema_defined",
+ "passed": len(request_payload_schema["required_fields"]) == 10,
+ "failure_route": "wait_for_request_schema",
+ },
+ ]
+ rejection_reasons = [
+ "production_truth_missing_or_stale",
+ "controlled_dry_run_shell_closeout_not_ready",
+ "closeout_boundary_id_missing",
+ "migration_file_hash_missing_or_mismatch",
+ "requester_missing",
+ "reason_missing",
+ "secret_material_in_request_payload",
+ "preview_attempted_shell_sql_or_database_side_effect",
+ "rollback_boundary_missing",
+ "direct_database_apply_requested_from_intake",
+ ]
+ authorization_request_intake = {
+ "intake_id": _db_apply_authorization_request_intake_id(closeout),
+ "source_closeout_boundary_id": boundary.get("boundary_id"),
+ "source_dry_run_shell_preview_id": boundary.get("source_dry_run_shell_preview_id"),
+ "source_final_handoff_package_id": boundary.get("source_final_handoff_package_id"),
+ "source_artifact_preview_id": boundary.get("source_artifact_preview_id"),
+ "source_authorization_package_id": boundary.get("source_authorization_package_id"),
+ "source_preflight_id": boundary.get("source_preflight_id"),
+ "source_request_id": boundary.get("source_request_id"),
+ "status": intake_status,
+ "ready_for_authorization_request_intake": intake_ready,
+ "ready_for_database_apply_now": False,
+ "request_scope": "future_explicit_db_apply_authorization_only",
+ "target_file": target_file,
+ "expected_sha256": expected_sha256,
+ "actual_sha256": actual_sha256,
+ "hash_matches": hash_matches,
+ "requires_new_explicit_db_apply_authorization": True,
+ "requires_fresh_production_truth_in_future_run": True,
+ "operator_secret_boundary": "future_shell_only",
+ "reads_secret_in_preview": False,
+ "writes_script_in_preview": False,
+ "executes_shell_in_preview": False,
+ "executes_sql_in_preview": False,
+ "writes_database_in_preview": False,
+ "manual_review_mode": "exception_only",
+ }
+ authorization_envelope = {
+ "mode": "request_intake_only",
+ "accepts_authorization_request": intake_ready,
+ "issues_database_apply_authorization": False,
+ "ready_for_database_apply_now": False,
+ "manual_review_mode": "exception_only",
+ "failed_gate_route": "exception_review_only",
+ "rejects_direct_database_apply": True,
+ }
+ passed_gate_count = sum(1 for gate in authorization_acceptance_gates if gate.get("passed"))
+
+ return {
+ "policy": AUTO_POLICY_DB_APPLY_AUTHORIZATION_REQUEST_INTAKE_POLICY,
+ "result": intake_status,
+ "success": bool(closeout.get("success")),
+ "generated_at": closeout.get("generated_at"),
+ "source_policy": closeout.get("policy"),
+ "stats": closeout.get("stats") or {},
+ "summary": {
+ "authorization_request_intake_ready_count": 1 if intake_ready else 0,
+ "required_request_evidence_count": len(required_request_evidence),
+ "request_payload_required_field_count": len(request_payload_schema["required_fields"]),
+ "authorization_acceptance_gate_count": len(authorization_acceptance_gates),
+ "authorization_acceptance_pass_count": passed_gate_count,
+ "authorization_acceptance_waiting_count": len(authorization_acceptance_gates) - passed_gate_count,
+ "rejection_reason_count": len(rejection_reasons),
+ "closeout_ready_count": summary.get("closeout_ready_count", 0),
+ "closeout_check_count": summary.get("closeout_check_count", 0),
+ "future_apply_boundary_count": summary.get("future_apply_boundary_count", 0),
+ "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,
+ LEGACY_REVIEW_REQUIRED_COUNT_KEY: summary.get(LEGACY_REVIEW_REQUIRED_COUNT_KEY, 0),
+ },
+ "authorization_request_intake": authorization_request_intake,
+ "authorization_envelope": authorization_envelope,
+ "required_request_evidence": required_request_evidence,
+ "request_payload_schema": request_payload_schema,
+ "authorization_acceptance_gates": authorization_acceptance_gates,
+ "rejection_reasons": rejection_reasons,
+ "source_closeout_summary": summary,
+ "safety": {
+ "read_only_db_apply_authorization_request_intake": True,
+ "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,
+ "persists_receipt": False,
+ "updates_mapping": False,
+ "dispatches_telegram": False,
+ "llm_calls_in_gate": False,
+ "manual_review_mode": "exception_only",
+ },
+ "next_actions": [
+ "Use this intake envelope to accept or reject a separate explicit DB apply authorization request.",
+ "Keep the intake from issuing DB apply authorization; it only verifies request evidence completeness.",
+ "Reject any payload that includes secrets, asks for direct DB apply, or skips fresh production truth.",
+ ],
+ }
diff --git a/tests/test_ai_automation_debt_service.py b/tests/test_ai_automation_debt_service.py
new file mode 100644
index 0000000..145c2f9
--- /dev/null
+++ b/tests/test_ai_automation_debt_service.py
@@ -0,0 +1,49 @@
+from pathlib import Path
+
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+def test_ai_automation_debt_scan_is_read_only_and_prioritized():
+ from services.ai_automation_debt_service import build_ai_automation_debt_report
+
+ report = build_ai_automation_debt_report(max_findings=40, per_file_limit=6)
+
+ assert report["policy"] == "read_only_ai_automation_debt_scan"
+ assert report["success"] is True
+ assert report["result"] == "PRODUCT_SURFACE_CLEAR"
+ assert report["summary"]["product_surface_blocker_count"] == 0
+ assert report["summary"]["primary_human_gate_count"] == 0
+ assert report["summary"]["ai_controlled_apply_ready"] is True
+ assert report["summary"]["market_intel_ai_controlled_alias_count"] >= 10
+ assert report["summary"]["controlled_apply_candidate_count"] == 0
+ assert report["summary"]["category_counts"].get("market_intel_ai_controlled_apply_candidate", 0) == 0
+ assert report["summary"]["category_counts"].get("automation_debt", 0) == 0
+ assert report["summary"]["category_counts"].get("governance_doc_debt", 0) == 0
+ assert report["summary"]["category_counts"].get("ea_legacy_callback_debt", 0) == 0
+ assert report["safety"] == {
+ "read_only": True,
+ "writes_database": False,
+ "executes_network": False,
+ "uses_llm": False,
+ "scans_raw_sessions": False,
+ "github_used": False,
+ }
+ assert all(item["priority"] != "P0" for item in report["findings"])
+ assert report["next_work_order"][0]["lane"] == "product_surface"
+ assert report["next_work_order"][0]["status"] == "clear"
+ assert report["next_work_order"][1]["lane"] == "market_intel_controlled_apply"
+ assert report["next_work_order"][1]["status"] == "review_report_alias_layer_complete"
+ assert report["next_work_order"][1]["alias_count"] >= 90
+ assert report["next_work_order"][1]["target_count"] == 0
+ assert report["next_work_order"][2]["target_count"] == 0
+ assert report["next_work_order"][3]["lane"] == "legacy_compatibility_aliases"
+ assert report["next_work_order"][3]["target_count"] == report["summary"]["legacy_compatibility_field_count"]
+
+
+def test_ai_automation_debt_api_route_is_registered():
+ route_source = (ROOT / "routes" / "ai_routes.py").read_text(encoding="utf-8")
+
+ assert "@ai_bp.route('/api/ai/automation-debt')" in route_source
+ assert "build_ai_automation_debt_report" in route_source
+ assert '"source_endpoint"] = "/api/ai/automation-debt"' in route_source
diff --git a/tests/test_pchome_mapping_backlog_report.py b/tests/test_pchome_mapping_backlog_report.py
new file mode 100644
index 0000000..8780eb8
--- /dev/null
+++ b/tests/test_pchome_mapping_backlog_report.py
@@ -0,0 +1,20616 @@
+import hashlib
+import json
+from pathlib import Path
+
+from scripts.ops import report_pchome_mapping_backlog as report
+from services.pchome_mapping_backlog_service import (
+ build_pchome_auto_policy_apply_readiness_closeout,
+ build_pchome_auto_policy_db_apply_controlled_apply_final_preflight,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_package,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_readiness,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout,
+ build_pchome_auto_policy_db_apply_authorization_decision_closeout,
+ build_pchome_auto_policy_db_apply_authorization_decision_preflight,
+ build_pchome_auto_policy_db_apply_authorization_evidence_execution_closeout,
+ build_pchome_auto_policy_db_apply_authorization_evidence_execution_preflight,
+ build_pchome_auto_policy_db_apply_authorization_issuer_gate,
+ build_pchome_auto_policy_db_apply_authorization_lane_guard,
+ build_pchome_auto_policy_db_apply_authorization_request_closeout,
+ build_pchome_auto_policy_db_apply_authorization_request_intake,
+ build_pchome_auto_policy_db_apply_authorization_signing_decision_closeout,
+ build_pchome_auto_policy_db_apply_authorization_signing_decision_preflight,
+ build_pchome_auto_policy_db_apply_authorization_signing_execution_closeout,
+ build_pchome_auto_policy_db_apply_authorization_signing_execution_preflight,
+ build_pchome_auto_policy_db_apply_authorization_signing_issuer_closeout,
+ build_pchome_auto_policy_db_apply_authorization_signing_issuer_guard,
+ build_pchome_auto_policy_db_apply_authorization_detached_verification_evidence_validation,
+ build_pchome_auto_policy_db_apply_authorization_signed_receipt_closeout,
+ build_pchome_auto_policy_db_apply_authorization_signed_receipt_evidence_intake,
+ build_pchome_auto_policy_db_apply_authorization_signed_receipt_preflight,
+ build_pchome_auto_policy_db_apply_authorization_verifier_receipt_closeout,
+ build_pchome_auto_policy_db_apply_authorization_package,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_shell_closeout,
+ build_pchome_auto_policy_db_apply_controlled_dry_run_shell_preview,
+ build_pchome_auto_policy_db_apply_execution_preflight,
+ build_pchome_auto_policy_db_apply_final_handoff_package,
+ build_pchome_auto_policy_db_apply_request_gate_preview,
+ build_pchome_auto_policy_db_apply_verifier_artifact_preview,
+ build_pchome_auto_policy_migration_apply_gate_preview,
+ build_pchome_auto_policy_migration_file_generation_request,
+ build_pchome_auto_policy_migration_file_preview,
+ build_pchome_auto_policy_persistence_gate,
+ build_pchome_auto_policy_receipt_gate,
+ build_pchome_auto_policy_schema_migration_preview,
+ build_pchome_evidence_enrichment_preview,
+ build_pchome_evidence_fetch_gate,
+ build_pchome_evidence_merge_preview,
+ build_pchome_evidence_source_preview,
+ build_pchome_direct_mapping_auto_search_package,
+ build_pchome_direct_mapping_candidate_decision_package,
+ build_pchome_growth_ai_automation_readiness,
+ build_pchome_mapping_operator_preview,
+ parse_pchome_product_page_evidence_html,
+ parse_unit_package_basis,
+ summarize_pchome_mapping_backlog,
+)
+
+
+ROOT = Path(__file__).resolve().parents[1]
+
+
+def _payload():
+ return {
+ "success": True,
+ "system_name": "MOMO Pro",
+ "generated_at": "2026-06-28T01:16:02",
+ "cache_state": "fresh",
+ "stats": {
+ "candidate_count": 4,
+ "mapped_count": 1,
+ "mapping_rate": 25.0,
+ "needs_mapping_count": 3,
+ "review_candidate_count": 1,
+ "overall_latest_sales_date": "2026-06-24",
+ "overall_sales_7d": 2020234.0,
+ "action_counts": {
+ "先補商品對應": 2,
+ "確認候選": 1,
+ "放大價格優勢": 1,
+ },
+ "action_code_counts": {
+ "map_external_product": 2,
+ "review_external_candidate": 1,
+ "amplify_price_advantage": 1,
+ },
+ },
+ "opportunities": [
+ {
+ "pchome_product_id": "PCH-1",
+ "product_name": "Mapped product",
+ "sales_7d": 0,
+ "external_price": {"momo_sku": "M-1", "price_basis": "unit_price", "gap_pct": 12.5},
+ "recommended_action": {"code": "amplify_price_advantage", "label": "放大價格優勢"},
+ "priority_score": 75.0,
+ },
+ {
+ "pchome_product_id": "PCH-2",
+ "product_name": "Direct mapping product 40ml x2",
+ "sales_7d": 9800,
+ "pchome_price": 1200,
+ "external_price": None,
+ "recommended_action": {"code": "map_external_product", "label": "先補商品對應"},
+ "priority_score": 88.0,
+ "reason_lines": ["需要補商品對應"],
+ },
+ {
+ "pchome_product_id": "PCH-3",
+ "product_name": "Review candidate product",
+ "sales_7d": 1200,
+ "external_price": None,
+ "review_candidate": {
+ "id": 725,
+ "momo_sku": "5868343",
+ "momo_name": "MOMO candidate",
+ "quality_score": 94.8,
+ },
+ "recommended_action": {"code": "review_external_candidate", "label": "確認候選"},
+ "priority_score": 64.0,
+ },
+ {
+ "pchome_product_id": "PCH-4",
+ "product_name": "Another direct mapping product",
+ "sales_7d": 3100,
+ "external_price": None,
+ "recommended_action": {"code": "map_external_product", "label": "先補商品對應"},
+ "priority_score": 52.0,
+ },
+ ],
+ }
+
+
+def test_with_limit_preserves_existing_query_and_clamps_limit():
+ url = report.with_limit("https://example.test/path?refresh=1", 99)
+
+ assert url == "https://example.test/path?refresh=1&limit=50"
+
+
+def test_summarize_payload_turns_growth_api_into_mapping_backlog():
+ summary = report.summarize_payload(_payload())
+
+ assert summary["policy"] == "read_only_pchome_growth_mapping_backlog"
+ assert summary["result"] == "NEEDS_MAPPING"
+ assert summary["stats"]["mapping_rate"] == 25.0
+ assert summary["stats"]["needs_mapping_count"] == 3
+ assert summary["backlog"]["direct_mapping_count"] == 2
+ assert summary["backlog"]["review_candidate_count"] == 1
+ assert summary["backlog"]["mapped_opportunity_count"] == 1
+ assert summary["backlog"]["top_needs_mapping"][0]["pchome_product_id"] == "PCH-2"
+ assert summary["backlog"]["top_needs_mapping"][0]["product_url"] == "https://24h.pchome.com.tw/prod/PCH-2"
+ direct_evidence = summary["backlog"]["top_needs_mapping"][0]["evidence_completeness"]
+ assert "stable_product_id" in direct_evidence["present_fields"]
+ assert "unit_price_or_package_basis" in direct_evidence["present_fields"]
+ assert direct_evidence["unit_package_basis"]["package_basis"] == "multi_pack_quantity_candidate"
+ assert direct_evidence["unit_package_basis"]["estimated_total_quantity"] == 80
+ assert "image" in direct_evidence["missing_fields"]
+ assert "availability" in direct_evidence["missing_fields"]
+ assert direct_evidence["auto_accept_ready"] is False
+ assert direct_evidence["human_review_required"] is False
+ assert direct_evidence["legacy_human_review_required"] is True
+ assert direct_evidence["ai_exception_required"] is True
+ assert direct_evidence["primary_human_gate_count"] == 0
+ assert summary["backlog"]["top_review_candidates"][0]["review_candidate"]["momo_sku"] == "5868343"
+
+
+def test_shared_service_is_the_single_mapping_backlog_summary_source():
+ assert report.summarize_payload(_payload()) == summarize_pchome_mapping_backlog(_payload())
+
+
+def test_operator_preview_is_read_only_and_requires_write_gate():
+ preview = build_pchome_mapping_operator_preview(_payload(), batch_size=1)
+
+ assert preview["policy"] == "read_only_pchome_growth_mapping_operator_preview"
+ assert preview["result"] == "READY_FOR_OPERATOR_PREVIEW"
+ assert preview["operator_batch"]["selected_direct_mapping_count"] == 1
+ assert preview["operator_batch"]["direct_mapping_targets"][0]["pchome_product_id"] == "PCH-2"
+ assert preview["operator_batch"]["direct_mapping_targets"][0]["evidence_completeness"]["auto_accept_ready"] is False
+ assert preview["command_preview"]["endpoint"] == "/api/ai/pchome-growth/backfill-momo-candidates"
+ assert preview["command_preview"]["writes_database"] is True
+ assert preview["command_preview"]["write_gate_required"] is True
+ assert preview["external_benchmark_alignment"]["references"][0]["source"] == "Google Merchant Center product data specification"
+ assert any(
+ item["field"] == "image" and item["status"] == "missing_in_current_growth_payload"
+ for item in preview["external_benchmark_alignment"]["required_evidence_fields"]
+ )
+ assert preview["ai_automation_plan"]["policy"] == "ollama_first_read_only_ai_assist"
+ assert preview["ai_automation_plan"]["llm_calls_in_preview"] is False
+ assert preview["ai_automation_plan"]["gemini_allowed"] is False
+ assert preview["ai_automation_plan"]["automation_readiness"]["can_execute_write"] is False
+ assert preview["safety"]["read_only_preview"] is True
+ assert preview["safety"]["writes_database"] is False
+ assert preview["safety"]["executes_search"] is False
+
+
+def test_direct_mapping_auto_search_package_builds_p1_no_write_search_terms():
+ package = build_pchome_direct_mapping_auto_search_package(_payload(), batch_size=1)
+
+ target = package["search_package"]["targets"][0]
+ assert package["policy"] == "read_only_pchome_growth_direct_mapping_auto_search_package"
+ assert package["result"] == "DIRECT_MAPPING_SEARCH_PACKAGE_READY"
+ assert package["source_policy"] == "read_only_pchome_growth_mapping_operator_preview"
+ assert package["summary"]["direct_mapping_count"] == 2
+ assert package["summary"]["selected_direct_mapping_count"] == 1
+ assert package["summary"]["search_ready_target_count"] == 1
+ assert package["summary"]["planned_search_term_count"] >= 1
+ assert package["summary"]["execute_search_count"] == 0
+ assert package["summary"]["candidates_found_count"] == 0
+ assert target["pchome_product_id"] == "PCH-2"
+ assert target["can_execute_read_only_search"] is True
+ assert "40ml" in " ".join(target["search_terms"]).lower()
+ assert target["identity_anchors"]["stable_product_id"] is True
+ assert target["identity_anchors"]["unit_basis_present"] is True
+ assert "no_database_write_from_search_package" in target["candidate_acceptance_gates"]
+ assert package["search_execution"]["executed"] is False
+ assert package["search_execution"]["writes_database"] is False
+ assert package["candidate_acceptance_policy"]["routes_manual_review_to_machine_verifiable_decision"] is True
+ assert package["safety"]["executes_search"] is False
+ assert package["safety"]["writes_database"] is False
+ assert package["safety"]["syncs_external_offers"] is False
+
+
+def test_direct_mapping_auto_search_package_executes_fake_search_without_db_write():
+ def fake_search(targets, limit_per_product, max_products, max_terms_per_product, min_score):
+ assert targets[0]["product_id"] == "PCH-2"
+ assert max_products == 1
+ assert limit_per_product == 3
+ assert max_terms_per_product == 2
+ assert min_score == 0.5
+ return True, "found", [
+ {
+ "product_id": "MOMO-1",
+ "name": "Direct mapping product 40ml x2",
+ "price": 999,
+ "target_pchome_product_id": "PCH-2",
+ "target_match_score": 0.92,
+ "auto_compare_type": "total_price",
+ "target_hard_veto": False,
+ }
+ ]
+
+ package = build_pchome_direct_mapping_auto_search_package(
+ _payload(),
+ batch_size=1,
+ execute_search=True,
+ limit_per_product=3,
+ max_terms_per_product=2,
+ min_score=0.5,
+ search_func=fake_search,
+ )
+
+ target = package["search_package"]["targets"][0]
+ assert package["result"] == "DIRECT_MAPPING_CANDIDATES_FOUND"
+ assert package["summary"]["execute_search_count"] == 1
+ assert package["summary"]["candidates_found_count"] == 1
+ assert package["summary"]["auto_compare_candidate_count"] == 1
+ assert package["summary"]["review_candidate_count"] == 0
+ assert target["candidate_count"] == 1
+ assert target["candidate_ids"] == ["MOMO-1"]
+ assert package["search_execution"]["executed"] is True
+ assert package["search_execution"]["search_success"] is True
+ assert package["search_execution"]["candidate_count"] == 1
+ assert package["search_execution"]["writes_database"] is False
+ assert package["search_execution"]["syncs_external_offers"] is False
+ assert package["candidate_preview"][0]["target_pchome_product_id"] == "PCH-2"
+ assert package["safety"]["executes_search"] is True
+ assert package["safety"]["writes_database"] is False
+ assert package["safety"]["persists_candidate"] is False
+
+
+def test_direct_mapping_candidate_decision_package_waits_for_search_candidates_without_db_write():
+ package = build_pchome_direct_mapping_candidate_decision_package(_payload(), batch_size=1)
+
+ assert package["policy"] == "read_only_pchome_growth_direct_mapping_candidate_decision_package"
+ assert package["result"] == "WAITING_FOR_DIRECT_MAPPING_CANDIDATES"
+ assert package["source_policy"] == "read_only_pchome_growth_direct_mapping_auto_search_package"
+ assert package["summary"]["direct_mapping_count"] == 2
+ assert package["summary"]["selected_direct_mapping_count"] == 1
+ assert package["summary"]["candidate_decision_count"] == 0
+ assert package["summary"]["auto_compare_decision_count"] == 0
+ assert package["summary"]["machine_review_decision_count"] == 0
+ assert package["summary"]["can_auto_persist_now_count"] == 0
+ assert package["decision_package"]["candidate_decisions"] == []
+ assert package["decision_package"]["manual_review_mode"] == "exception_only"
+ assert package["decision_acceptance_policy"]["writes_database"] is False
+ assert package["safety"]["executes_search"] is False
+ assert package["safety"]["writes_database"] is False
+ assert package["safety"]["persists_candidate"] is False
+
+
+def test_direct_mapping_candidate_decision_package_routes_candidates_to_machine_verifiable_actions():
+ def fake_search(targets, limit_per_product, max_products, max_terms_per_product, min_score):
+ return True, "found", [
+ {
+ "product_id": "MOMO-1",
+ "name": "Direct mapping product 40ml x2",
+ "price": 999,
+ "target_pchome_product_id": "PCH-2",
+ "target_pchome_name": "Direct mapping product 40ml x2",
+ "target_match_score": 0.92,
+ "auto_compare_type": "total_price",
+ "target_hard_veto": False,
+ "target_price_basis": "total_price",
+ "target_gap_pct": 16.8,
+ "target_search_term": "direct mapping product 40ml x2",
+ },
+ {
+ "product_id": "MOMO-2",
+ "name": "Variant candidate",
+ "price": 899,
+ "target_pchome_product_id": "PCH-2",
+ "target_pchome_name": "Direct mapping product 40ml x2",
+ "target_match_score": 0.51,
+ "auto_compare_type": "manual_review",
+ "target_hard_veto": False,
+ },
+ ]
+
+ package = build_pchome_direct_mapping_candidate_decision_package(
+ _payload(),
+ batch_size=1,
+ execute_search=True,
+ limit_per_product=3,
+ max_terms_per_product=2,
+ min_score=0.5,
+ search_func=fake_search,
+ )
+
+ decisions = package["decision_package"]["candidate_decisions"]
+ assert package["result"] == "DIRECT_MAPPING_CANDIDATE_DECISION_PACKAGE_READY"
+ assert package["summary"]["candidates_found_count"] == 2
+ assert package["summary"]["candidate_decision_count"] == 2
+ assert package["summary"]["auto_compare_decision_count"] == 1
+ assert package["summary"]["machine_review_decision_count"] == 1
+ assert package["summary"]["can_auto_persist_now_count"] == 0
+ assert decisions[0]["decision_id"].startswith("pchome-direct-mapping-candidate-")
+ assert decisions[0]["decision"] == "route_to_no_write_auto_compare_receipt"
+ assert decisions[0]["data_quality"] == "ready_for_no_write_receipt"
+ assert decisions[0]["guardrails"]["machine_actionable"] is True
+ assert decisions[0]["guardrails"]["can_auto_execute"] is False
+ assert decisions[0]["guardrails"]["writes_database"] is False
+ assert decisions[0]["guardrails"]["persists_candidate"] is False
+ assert decisions[0]["guardrails"]["manual_review_mode"] == "exception_only"
+ assert decisions[1]["decision"] == "route_to_machine_review_decision"
+ assert decisions[1]["failure_reasons"] == ["auto_compare_type_not_receipt_ready"]
+ assert package["safety"]["executes_search"] is True
+ assert package["safety"]["writes_database"] is False
+ assert package["safety"]["persists_candidate"] is False
+
+
+def test_ai_automation_readiness_makes_automation_visible_without_manual_primary_flow():
+ readiness = build_pchome_growth_ai_automation_readiness(_payload(), batch_size=1)
+
+ lanes = {lane["key"]: lane for lane in readiness["automation_lanes"]}
+ assert readiness["policy"] == "read_only_pchome_growth_ai_automation_readiness"
+ assert readiness["result"] == "AI_AUTOMATION_ACTIVE_WAITING_FOR_CANDIDATES"
+ assert readiness["summary"]["direct_mapping_count"] == 2
+ assert readiness["summary"]["selected_search_target_count"] == 1
+ assert readiness["summary"]["planned_search_term_count"] >= 1
+ assert readiness["summary"]["waiting_candidate_count"] == 1
+ assert readiness["summary"]["primary_human_gate_count"] == 0
+ assert readiness["summary"]["ai_exception_count"] == 0
+ assert readiness["summary"]["manual_required_as_primary_flow_count"] == 0
+ assert readiness["automation_policy"]["primary_flow"] == "ai_controlled"
+ assert readiness["automation_policy"]["human_primary_flow"] is False
+ assert readiness["ai_exception_auto_resolution"]["mode"] == "machine_verifiable_auto_resolution"
+ assert readiness["ai_exception_auto_resolution"]["primary_human_gate_count"] == 0
+ assert readiness["manual_policy"]["manual_review_mode"] == "exception_only"
+ assert readiness["manual_policy"]["deprecated_product_surface"] is True
+ assert readiness["manual_policy"]["manual_as_primary_flow"] is False
+ assert lanes["same_item_search_package"]["status"] == "ready"
+ assert lanes["candidate_decision_package"]["status"] == "waiting"
+ assert lanes["candidate_decision_package"]["ai_exception_mode"] == "machine_verifiable_auto_resolution"
+ assert lanes["controlled_apply"]["status"] == "blocked_until_verifier"
+ assert readiness["safety"]["writes_database"] is False
+ assert readiness["safety"]["llm_calls_in_preview"] is False
+
+
+def test_unit_package_basis_parser_extracts_quantity_count_and_risk_signals():
+ single = parse_unit_package_basis("雅詩蘭黛 粉持久完美持妝粉底 30ml")
+ assert single["package_basis"] == "single_unit_quantity_candidate"
+ assert single["quantities"][0] == {"value": 30, "unit": "ml", "raw": "30ml"}
+ assert single["unit_pricing_measure"] == {"value": 30, "unit": "ml"}
+ assert single["unit_pricing_base_measure"] == {"value": 100, "unit": "ml"}
+ assert single["human_review_required"] is False
+ assert single["ai_exception_required"] is False
+ assert single["primary_human_gate_count"] == 0
+ assert single["writes_database"] is False
+ assert single["fetches_external_sites"] is False
+ assert single["llm_calls"] is False
+
+ bundle = parse_unit_package_basis("理膚寶水 B5 修復霜 40ml x2 超值組")
+ assert bundle["package_basis"] == "variant_sensitive_quantity_candidate"
+ assert bundle["multipliers"] == [2]
+ assert bundle["estimated_total_quantity"] == 80
+ assert "bundle_or_promo" in bundle["risk_signals"]
+ assert bundle["human_review_required"] is False
+ assert bundle["legacy_human_review_required"] is True
+ assert bundle["ai_exception_required"] is True
+
+ variant = parse_unit_package_basis("Dior 癮誘唇膏 3.2g 多款任選")
+ assert variant["package_basis"] == "variant_sensitive_quantity_candidate"
+ assert variant["unit_label"] == "g"
+ assert "variant_selection" in variant["risk_signals"]
+ assert variant["human_review_required"] is False
+ assert variant["legacy_human_review_required"] is True
+ assert variant["ai_exception_required"] is True
+
+ count_only = parse_unit_package_basis("濕紙巾 42張")
+ assert count_only["package_basis"] == "count_package_candidate"
+ assert count_only["unit_pricing_measure"] == {"value": 42, "unit": "ct"}
+
+
+def test_evidence_enrichment_preview_builds_missing_field_tasks():
+ preview = build_pchome_evidence_enrichment_preview(_payload(), batch_size=1)
+
+ assert preview["policy"] == "read_only_pchome_growth_evidence_enrichment_preview"
+ assert preview["result"] == "NEEDS_EVIDENCE_ENRICHMENT"
+ assert preview["summary"]["task_count"] == 2
+ assert preview["summary"]["tasks_with_blockers"] == 2
+ assert preview["summary"]["missing_field_counts"]["image"] == 2
+ assert preview["summary"]["missing_field_counts"]["availability"] == 2
+ assert preview["summary"]["missing_field_counts"]["unit_price_or_package_basis"] == 1
+ assert preview["evidence_tasks"][0]["lane"] == "direct_mapping"
+ assert preview["evidence_tasks"][0]["product_url"] == "https://24h.pchome.com.tw/prod/PCH-2"
+ assert "unit_price_or_package_basis" not in preview["evidence_tasks"][0]["missing_fields"]
+ assert preview["evidence_tasks"][0]["unit_package_basis"]["estimated_total_quantity"] == 80
+ assert "image" in preview["evidence_tasks"][0]["blocking_missing_fields"]
+ assert preview["evidence_tasks"][0]["enrichment_steps"][0]["sources"][0]["writes_database"] is False
+ assert preview["ai_automation_plan"]["policy"] == "ollama_first_read_only_evidence_assist"
+ assert preview["ai_automation_plan"]["llm_calls_in_preview"] is False
+ assert preview["safety"]["fetches_external_sites"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_review_candidate_pchome_price_counts_as_price_evidence():
+ payload = json.loads(json.dumps(_payload()))
+ payload["opportunities"][2]["review_candidate"]["pchome_price"] = 880
+
+ preview = build_pchome_evidence_enrichment_preview(payload, batch_size=1)
+ review_task = next(task for task in preview["evidence_tasks"] if task["pchome_product_id"] == "PCH-3")
+
+ assert "price" not in review_task["missing_fields"]
+ assert "price" in review_task["present_fields"]
+
+
+def test_evidence_source_preview_plans_read_only_fetch_gates_without_fetching():
+ preview = build_pchome_evidence_source_preview(_payload(), batch_size=1)
+
+ assert preview["policy"] == "read_only_pchome_growth_evidence_source_preview"
+ assert preview["result"] == "NEEDS_SOURCE_WIRING"
+ assert preview["source_policy"] == "read_only_pchome_growth_evidence_enrichment_preview"
+ assert preview["summary"]["field_counts"]["image"]["missing_count"] == 2
+ assert preview["summary"]["field_counts"]["availability"]["missing_count"] == 2
+ assert preview["summary"]["field_counts"]["price"]["missing_count"] == 1
+ assert preview["source_plans"]["image"]["future_read_only_fetch_gate"]["method"] == "GET"
+ assert (
+ preview["source_plans"]["image"]["future_read_only_fetch_gate"]["check_mode_parser"]
+ == "read_only_pchome_product_page_evidence_parser"
+ )
+ assert preview["source_plans"]["image"]["future_read_only_fetch_gate"]["fetches_external_sites_in_preview"] is False
+ assert preview["source_plans"]["availability"]["future_read_only_fetch_gate"]["writes_database"] is False
+ assert preview["source_plans"]["price"]["payload_mapping_probe"]["writes_database"] is False
+ assert preview["fetch_gate_candidates"][0]["product_url"] == "https://24h.pchome.com.tw/prod/PCH-2"
+ assert preview["fetch_gate_candidates"][0]["executes_fetch_in_preview"] is False
+ assert preview["ai_automation_plan"]["llm_calls_in_preview"] is False
+ assert preview["ai_automation_plan"]["gemini_allowed"] is False
+ assert preview["safety"]["fetches_external_sites"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_product_page_evidence_parser_reads_jsonld_without_fetching():
+ html = """
+
+
+
+ """
+
+ parsed = parse_pchome_product_page_evidence_html(html, product_url="https://24h.pchome.com.tw/prod/PCH-2")
+
+ assert parsed["policy"] == "read_only_pchome_product_page_evidence_parser"
+ assert parsed["source"] == "html_fixture"
+ assert parsed["image_url"] == "https://cdn.example.test/product.jpg"
+ assert parsed["availability"] == "in_stock"
+ assert parsed["jsonld_product_found"] is True
+ assert parsed["jsonld_offer_found"] is True
+ assert parsed["safety"]["fetches_external_sites"] is False
+ assert parsed["safety"]["writes_database"] is False
+ assert parsed["safety"]["llm_calls"] is False
+
+
+def test_product_page_evidence_parser_uses_meta_fallbacks_and_skips_invalid_jsonld():
+ html = """
+
+
+
+
+
+ """
+
+ parsed = parse_pchome_product_page_evidence_html(html)
+
+ assert parsed["image_url"] == "https://cdn.example.test/fallback.jpg"
+ assert parsed["availability"] == "out_of_stock"
+ assert parsed["fallbacks_used"] == ["og:image", "product:availability"]
+ assert parsed["parser_warnings"] == ["invalid_jsonld_skipped"]
+
+
+def test_evidence_fetch_gate_defaults_to_planned_no_fetch_receipts():
+ preview = build_pchome_evidence_fetch_gate(_payload(), batch_size=1)
+
+ assert preview["policy"] == "controlled_read_only_pchome_product_page_evidence_fetch_gate"
+ assert preview["result"] == "FETCH_GATE_PLANNED"
+ assert preview["summary"]["candidate_count"] == 1
+ assert preview["summary"]["executed_fetch_count"] == 0
+ assert preview["fetch_config"]["execute_fetch"] is False
+ assert preview["fetch_receipts"][0]["status"] == "PLANNED"
+ assert preview["fetch_receipts"][0]["executed_fetch"] is False
+ assert preview["fetch_receipts"][0]["writes_database"] is False
+ assert preview["safety"]["read_only_fetch_gate"] is True
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_evidence_fetch_gate_executes_fake_get_and_parses_receipt():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ calls = []
+
+ def fake_get(url, timeout, headers):
+ calls.append({"url": url, "timeout": timeout, "headers": headers})
+ return FakeResponse()
+
+ preview = build_pchome_evidence_fetch_gate(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ timeout_seconds=2,
+ http_get=fake_get,
+ )
+
+ receipt = preview["fetch_receipts"][0]
+ assert preview["result"] == "FETCH_GATE_EXECUTED_WITH_EVIDENCE"
+ assert preview["summary"]["executed_fetch_count"] == 1
+ assert preview["summary"]["parsed_image_count"] == 1
+ assert preview["summary"]["parsed_availability_count"] == 1
+ assert receipt["status"] == "FETCHED_WITH_EVIDENCE"
+ assert receipt["executed_fetch"] is True
+ assert receipt["parsed_evidence"]["image_url"] == "https://cdn.example.test/pchome.jpg"
+ assert receipt["parsed_evidence"]["availability"] == "in_stock"
+ assert receipt["writes_database"] is False
+ assert calls[0]["url"] == "https://24h.pchome.com.tw/prod/PCH-2"
+ assert calls[0]["timeout"] == 2
+
+
+def test_evidence_fetch_gate_blocks_non_allowlisted_product_url():
+ payload = _payload()
+ payload["opportunities"][1]["product_url"] = "https://example.test/prod/PCH-2"
+
+ preview = build_pchome_evidence_fetch_gate(payload, batch_size=1, execute_fetch=True)
+
+ assert preview["result"] == "FETCH_GATE_EXECUTED_WITH_BLOCKERS"
+ assert preview["summary"]["blocked_count"] == 1
+ assert preview["fetch_receipts"][0]["status"] == "BLOCKED_BY_ALLOWLIST"
+ assert preview["fetch_receipts"][0]["executed_fetch"] is False
+
+
+def test_evidence_merge_preview_requires_fetch_before_merge_by_default():
+ preview = build_pchome_evidence_merge_preview(_payload(), batch_size=1)
+
+ assert preview["policy"] == "read_only_pchome_growth_evidence_merge_preview"
+ assert preview["result"] == "FETCH_REQUIRED_FOR_MERGE_PREVIEW"
+ assert preview["summary"]["executed_fetch_count"] == 0
+ assert preview["summary"]["writes_database_count"] == 0
+ assert preview["summary"]["manual_review_required_count"] == 0
+ assert preview["summary"]["manual_review_mode"] == "exception_only"
+ assert preview["merge_items"][0]["merge_status"] == "FETCH_GATE_PLANNED"
+ assert preview["merge_items"][0]["automation_decision"] == "AUTO_RUN_FETCH_GATE"
+ assert preview["merge_items"][0]["manual_review_required"] is False
+ assert preview["merge_items"][0]["writes_database"] is False
+ assert preview["safety"]["writes_database"] is False
+ assert preview["safety"]["updates_mapping"] is False
+ assert preview["safety"]["requires_operator_review_before_write"] is False
+
+
+def test_evidence_merge_preview_merges_fake_fetch_receipt_without_writing():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preview = build_pchome_evidence_merge_preview(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ item = preview["merge_items"][0]
+ assert preview["result"] == "MERGE_PREVIEW_READY"
+ assert preview["summary"]["merge_ready_count"] == 1
+ assert preview["summary"]["auto_merge_ready_count"] == 1
+ assert preview["summary"]["manual_review_required_count"] == 0
+ assert item["merge_status"] == "MERGE_PREVIEW_READY"
+ assert item["automation_decision"] == "AUTO_ACCEPT_EVIDENCE_MERGE"
+ assert item["automation_allowed"] is True
+ assert item["manual_review_required"] is False
+ assert item["evidence_delta"] == {
+ "image_url": "https://cdn.example.test/merged.jpg",
+ "availability": "in_stock",
+ }
+ assert "image" in item["merged_present_fields"]
+ assert "availability" in item["merged_present_fields"]
+ assert "image" not in item["remaining_missing_fields"]
+ assert "availability" not in item["remaining_missing_fields"]
+ assert item["writes_database"] is False
+ assert preview["safety"]["writes_database"] is False
+ assert preview["safety"]["manual_review_mode"] == "exception_only"
+
+
+def test_auto_policy_receipt_gate_builds_planned_receipts_without_persisting():
+ gate = build_pchome_auto_policy_receipt_gate(_payload(), batch_size=1)
+
+ receipt = gate["auto_policy_receipts"][0]
+ assert gate["policy"] == "read_only_pchome_growth_auto_policy_receipt_gate"
+ assert gate["result"] == "AUTO_POLICY_RECEIPTS_PLANNED"
+ assert gate["summary"]["receipt_count"] == 2
+ assert gate["summary"]["ready_for_auto_fetch_count"] == 1
+ assert gate["summary"]["manual_review_required_count"] == 0
+ assert gate["summary"]["writes_database_count"] == 0
+ assert gate["summary"]["persists_receipt_count"] == 0
+ assert receipt["receipt_id"].startswith("pchome-evidence-")
+ assert receipt["receipt_status"] == "READY_FOR_AUTO_FETCH"
+ assert receipt["automation_decision"] == "AUTO_RUN_FETCH_GATE"
+ assert receipt["manual_review_required"] is False
+ assert receipt["writes_database"] is False
+ assert receipt["persists_receipt"] is False
+ assert gate["persistence_gate"]["mode"] == "no_write_receipt_preview"
+ assert gate["safety"]["writes_database"] is False
+ assert gate["safety"]["persists_receipt"] is False
+
+
+def test_auto_policy_receipt_gate_prepares_auto_persistence_receipt_after_fake_fetch():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ gate = build_pchome_auto_policy_receipt_gate(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ receipt = gate["auto_policy_receipts"][0]
+ assert gate["result"] == "AUTO_POLICY_RECEIPTS_READY"
+ assert gate["summary"]["ready_for_auto_persistence_count"] == 1
+ assert gate["summary"]["receipt_status_counts"]["READY_FOR_AUTO_PERSISTENCE"] == 1
+ assert receipt["receipt_status"] == "READY_FOR_AUTO_PERSISTENCE"
+ assert receipt["automation_decision"] == "AUTO_ACCEPT_EVIDENCE_MERGE"
+ assert receipt["evidence_delta"] == {
+ "image_url": "https://cdn.example.test/receipt.jpg",
+ "availability": "in_stock",
+ }
+ assert receipt["persists_receipt"] is False
+ assert receipt["writes_database"] is False
+
+
+def test_auto_policy_persistence_gate_builds_no_write_dry_run_contract_without_fetch():
+ gate = build_pchome_auto_policy_persistence_gate(_payload(), batch_size=1)
+
+ item = gate["persistence_items"][0]
+ assert gate["policy"] == "read_only_pchome_growth_auto_policy_persistence_gate"
+ assert gate["result"] == "PERSISTENCE_WAITING_FOR_RECEIPTS"
+ assert gate["summary"]["persistence_item_count"] == 2
+ assert gate["summary"]["dry_run_ready_count"] == 0
+ assert gate["summary"]["waiting_for_receipt_count"] == 2
+ assert gate["summary"]["writes_database_count"] == 0
+ assert gate["summary"]["persists_receipt_count"] == 0
+ assert gate["schema_contract"]["requires_schema_migration_before_apply"] is True
+ assert gate["apply_gate"]["mode"] == "dry_run_only"
+ assert item["persistence_status"] == "WAITING_FOR_READY_RECEIPT"
+ assert item["planned_operation"] == "NOOP"
+ assert item["writes_database"] is False
+ assert item["persists_receipt"] is False
+ assert gate["safety"]["writes_database"] is False
+ assert gate["safety"]["persists_receipt"] is False
+
+
+def test_auto_policy_persistence_gate_prepares_idempotent_transaction_preview_after_fake_fetch():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ gate = build_pchome_auto_policy_persistence_gate(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ item = gate["persistence_items"][0]
+ assert gate["result"] == "PERSISTENCE_DRY_RUN_READY"
+ assert gate["summary"]["dry_run_ready_count"] == 1
+ assert item["persistence_status"] == "DRY_RUN_READY"
+ assert item["planned_operation"] == "UPSERT_EVIDENCE_RECEIPT_AND_PATCH_EXTERNAL_OFFER_EVIDENCE"
+ assert item["idempotency_key"].startswith("pchome-evidence-")
+ assert item["payload_hash"]
+ assert item["transaction_preview"]["commit"] == "future_apply_gate_only"
+ assert item["parameter_preview"]["image_url_present"] is True
+ assert item["parameter_preview"]["availability"] == "in_stock"
+ assert item["target_tables"] == ["external_offer_evidence_receipts", "external_offers"]
+ assert item["writes_database"] is False
+ assert item["persists_receipt"] is False
+ assert gate["apply_gate"]["writes_database"] is False
+ assert gate["safety"]["writes_database"] is False
+
+
+def test_auto_policy_schema_migration_preview_stays_no_write_without_fetch():
+ preview = build_pchome_auto_policy_schema_migration_preview(_payload(), batch_size=1)
+
+ ddl_preview = "\n".join(preview["schema_migration_preview"]["ddl_preview"])
+ assert preview["policy"] == "read_only_pchome_growth_auto_policy_schema_migration_preview"
+ assert preview["result"] == "SCHEMA_MIGRATION_PREVIEW_READY"
+ assert preview["future_apply_gate"]["status"] == "WAITING_FOR_DRY_RUN_READY_ITEMS"
+ assert preview["summary"]["dry_run_ready_count"] == 0
+ assert preview["summary"]["executes_migration_count"] == 0
+ assert preview["summary"]["writes_database_count"] == 0
+ assert "CREATE TABLE IF NOT EXISTS external_offer_evidence_receipts" in ddl_preview
+ assert "JSONB" in ddl_preview
+ assert preview["schema_migration_preview"]["executes_sql"] is False
+ assert preview["prewrite_snapshot_contract"]["executes_sql"] is False
+ assert preview["future_apply_verifier"]["executes_in_preview"] is False
+ assert preview["future_apply_gate"]["current_preview_apply_allowed"] is False
+ assert preview["safety"]["executes_migration"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_schema_migration_preview_builds_future_apply_verifier_after_fake_fetch():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preview = build_pchome_auto_policy_schema_migration_preview(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ assert preview["future_apply_gate"]["status"] == "APPLY_CONTRACT_READY"
+ assert preview["summary"]["dry_run_ready_count"] == 1
+ assert preview["summary"]["future_verifier_count"] == 5
+ assert preview["prewrite_snapshot_contract"]["target_receipt_ids"][0].startswith("pchome-evidence-")
+ assert preview["prewrite_snapshot_contract"]["target_pchome_product_ids"] == ["PCH-2"]
+ assert preview["future_apply_verifier"]["manual_review_mode"] == "exception_only"
+ assert preview["future_apply_verifier"]["checks"][0]["routes_failure_to"] == "exception_review"
+ assert preview["future_apply_gate"]["requires_prewrite_snapshot"] is True
+ assert preview["future_apply_gate"]["requires_post_write_readback"] is True
+ assert preview["future_apply_gate"]["writes_database"] is False
+ assert preview["safety"]["persists_receipt"] is False
+
+
+def test_auto_policy_migration_file_preview_stays_no_write_without_fetch():
+ preview = build_pchome_auto_policy_migration_file_preview(_payload(), batch_size=1)
+
+ migration = preview["migration_file_preview"]
+ endpoint = preview["future_apply_endpoint_verifier"]
+ forward_sql = "\n".join(migration["forward_sql_preview"])
+ assert preview["policy"] == "read_only_pchome_growth_auto_policy_migration_file_preview"
+ assert preview["result"] == "MIGRATION_FILE_PREVIEW_READY"
+ assert preview["summary"]["apply_endpoint_contract_ready_count"] == 0
+ assert preview["summary"]["writes_file_count"] == 0
+ assert preview["summary"]["executes_endpoint_count"] == 0
+ assert preview["summary"]["writes_database_count"] == 0
+ assert migration["migration_filename"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert migration["file_write_mode"] == "preview_only"
+ assert migration["forbidden_forward_tokens_absent"] is True
+ assert "Migration 045: PChome auto-policy evidence receipts" in forward_sql
+ assert "CREATE TABLE IF NOT EXISTS external_offer_evidence_receipts" in forward_sql
+ assert "GRANT ALL PRIVILEGES ON external_offer_evidence_receipts TO momo" in forward_sql
+ assert migration["writes_file"] is False
+ assert migration["executes_sql"] is False
+ assert endpoint["contract_status"] == "WAITING_FOR_APPLY_INPUTS"
+ assert endpoint["executes_endpoint"] is False
+ assert endpoint["writes_database"] is False
+ assert preview["safety"]["writes_file"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_migration_file_preview_builds_apply_endpoint_contract_after_fake_fetch():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preview = build_pchome_auto_policy_migration_file_preview(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ endpoint = preview["future_apply_endpoint_verifier"]
+ assert preview["summary"]["dry_run_ready_count"] == 1
+ assert preview["summary"]["apply_endpoint_contract_ready_count"] == 1
+ assert preview["migration_file_preview"]["forward_sql_hash"]
+ assert endpoint["contract_status"] == "APPLY_ENDPOINT_CONTRACT_READY"
+ assert endpoint["request_contract"]["receipt_count"] == 1
+ assert endpoint["request_contract"]["product_count"] == 1
+ assert endpoint["request_contract"]["expected_migration_filename"] == (
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ )
+ assert endpoint["request_contract"]["expected_migration_hash"] == (
+ preview["migration_file_preview"]["forward_sql_hash"]
+ )
+ assert endpoint["rollback_contract"]["uses_prewrite_snapshot"] is True
+ assert endpoint["post_write_verifier_contract"]["verifier_count"] == 5
+ assert endpoint["manual_review_mode"] == "exception_only"
+ assert endpoint["executes_endpoint"] is False
+ assert endpoint["executes_sql"] is False
+
+
+def test_auto_policy_apply_readiness_closeout_waits_for_ready_receipts_without_fetch():
+ closeout = build_pchome_auto_policy_apply_readiness_closeout(_payload(), batch_size=1)
+
+ waiting_keys = {check["key"] for check in closeout["closeout"]["waiting_checks"]}
+ assert closeout["policy"] == "read_only_pchome_growth_auto_policy_apply_readiness_closeout"
+ assert closeout["result"] == "APPLY_READINESS_WAITING_FOR_READY_RECEIPTS"
+ assert closeout["summary"]["readiness_check_count"] == 9
+ assert closeout["summary"]["current_preview_ready_count"] == 0
+ assert closeout["summary"]["future_apply_blocker_count"] == 4
+ assert "ready_receipts_present" in waiting_keys
+ assert "future_apply_endpoint_contract_ready" in waiting_keys
+ assert closeout["closeout"]["ready_for_migration_file_generation_request"] is False
+ assert closeout["closeout"]["ready_for_database_apply"] is False
+ assert closeout["summary"]["writes_file_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["safety"]["writes_file"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_apply_readiness_closeout_ready_after_fake_fetch_but_not_db_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_apply_readiness_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ assert closeout["result"] == "APPLY_READINESS_CLOSEOUT_READY"
+ assert closeout["summary"]["readiness_pass_count"] == 9
+ assert closeout["summary"]["readiness_waiting_count"] == 0
+ assert closeout["summary"]["current_preview_ready_count"] == 1
+ assert closeout["summary"]["future_apply_blocker_count"] == 4
+ assert closeout["closeout"]["status"] == "READY_FOR_MIGRATION_FILE_GENERATION_REQUEST"
+ assert closeout["closeout"]["ready_for_migration_file_generation_request"] is True
+ assert closeout["closeout"]["ready_for_database_apply"] is False
+ assert closeout["migration_file_preview_summary"]["migration_filename"] == (
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ )
+ assert closeout["future_apply_endpoint_summary"]["contract_status"] == "APPLY_ENDPOINT_CONTRACT_READY"
+ assert closeout["future_apply_endpoint_summary"]["receipt_count"] == 1
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_migration_file_generation_request_waits_for_closeout_without_fetch():
+ request_package = build_pchome_auto_policy_migration_file_generation_request(_payload(), batch_size=1)
+
+ request = request_package["file_generation_request"]
+ assert request_package["policy"] == "read_only_pchome_growth_auto_policy_migration_file_generation_request"
+ assert request_package["result"] == "WAITING_FOR_APPLY_READINESS_CLOSEOUT"
+ assert request_package["summary"]["request_ready_count"] == 0
+ assert request_package["summary"]["required_artifact_count"] == 4
+ assert request_package["summary"]["file_generation_step_count"] == 3
+ assert request["ready_to_generate_file"] is False
+ assert request["ready_for_database_apply"] is False
+ assert request["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert request["writes_file_in_preview"] is False
+ assert request_package["summary"]["writes_file_count"] == 0
+ assert request_package["summary"]["executes_endpoint_count"] == 0
+ assert request_package["summary"]["writes_database_count"] == 0
+ assert request_package["safety"]["writes_file"] is False
+ assert request_package["safety"]["writes_database"] is False
+
+
+def test_auto_policy_migration_file_generation_request_ready_after_fake_fetch_without_writing_file():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ request_package = build_pchome_auto_policy_migration_file_generation_request(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ request = request_package["file_generation_request"]
+ assert request_package["result"] == "FILE_GENERATION_REQUEST_READY"
+ assert request_package["summary"]["request_ready_count"] == 1
+ assert request["request_id"].startswith("pchome-migration-file-request-")
+ assert request["ready_to_generate_file"] is True
+ assert request["ready_for_database_apply"] is False
+ assert request["expected_sha256"]
+ assert request["expected_line_count"] == 31
+ assert request["file_generation_steps"][0]["content_source"] == "migration_file_preview.forward_sql_preview"
+ assert request_package["future_apply_endpoint_summary"]["contract_status"] == "APPLY_ENDPOINT_CONTRACT_READY"
+ assert request_package["future_apply_blockers"][0]["key"] == "migration_file_not_written"
+ assert request_package["safety"]["writes_file"] is False
+ assert request_package["safety"]["executes_sql"] is False
+
+
+def test_generated_auto_policy_migration_file_matches_generation_request_hash():
+ request_package = build_pchome_auto_policy_migration_file_generation_request(_payload(), batch_size=1)
+ request = request_package["file_generation_request"]
+ migration_path = ROOT / request["target_file"]
+
+ assert migration_path.exists()
+ migration_text = migration_path.read_text(encoding="utf-8")
+ assert request["expected_sha256"] == hashlib.sha256(migration_text.encode("utf-8")).hexdigest()
+ assert migration_text.endswith("\n")
+ assert request_package["safety"]["writes_database"] is False
+
+
+def test_auto_policy_migration_apply_gate_preview_reads_generated_file_without_db_apply():
+ preview = build_pchome_auto_policy_migration_apply_gate_preview(_payload(), batch_size=1)
+
+ assert preview["policy"] == "read_only_pchome_growth_auto_policy_migration_apply_gate_preview"
+ assert preview["result"] == "MIGRATION_APPLY_GATE_WAITING"
+ assert preview["summary"]["generated_file_exists_count"] == 1
+ assert preview["summary"]["generated_file_hash_matches_count"] == 1
+ assert preview["apply_gate"]["ready_for_database_apply_now"] is False
+ assert preview["generated_migration_file"]["exists"] is True
+ assert preview["generated_migration_file"]["target_file"] == (
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ )
+ assert preview["generated_migration_file"]["sha256"] == preview["apply_gate"]["expected_sha256"]
+ assert preview["safety"]["executes_migration"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_migration_apply_gate_preview_ready_after_fake_fetch_but_still_no_db_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preview = build_pchome_auto_policy_migration_apply_gate_preview(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ assert preview["result"] == "MIGRATION_APPLY_GATE_PREVIEW_READY"
+ assert preview["summary"]["apply_gate_pass_count"] == 9
+ assert preview["summary"]["apply_gate_waiting_count"] == 0
+ assert preview["summary"]["apply_preview_ready_count"] == 1
+ assert preview["summary"]["future_apply_blocker_count"] == 3
+ assert preview["apply_gate"]["status"] == "READY_FOR_EXPLICIT_DB_APPLY_REQUEST"
+ assert preview["apply_gate"]["ready_for_explicit_db_apply_request"] is True
+ assert preview["apply_gate"]["ready_for_database_apply_now"] is False
+ assert preview["apply_gate"]["hash_matches"] is True
+ assert preview["future_apply_endpoint_summary"]["contract_status"] == "APPLY_ENDPOINT_CONTRACT_READY"
+ assert preview["future_apply_endpoint_summary"]["receipt_count"] == 1
+ assert preview["safety"]["executes_sql"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_request_gate_preview_waits_without_ready_receipts():
+ preview = build_pchome_auto_policy_db_apply_request_gate_preview(_payload(), batch_size=1)
+
+ gate = preview["db_apply_request_gate"]
+ assert preview["policy"] == "read_only_pchome_growth_auto_policy_db_apply_request_gate_preview"
+ assert preview["result"] == "WAITING_FOR_MIGRATION_APPLY_GATE_PREVIEW"
+ assert preview["summary"]["request_ready_count"] == 0
+ assert preview["summary"]["required_artifact_count"] == 5
+ assert preview["summary"]["apply_sequence_step_count"] == 5
+ assert gate["ready_for_explicit_db_apply_request"] is False
+ assert gate["ready_for_database_apply_now"] is False
+ assert gate["command_preview"]["executes_in_preview"] is False
+ assert gate["command_preview"]["reads_secret_in_preview"] is False
+ assert gate["command_preview"]["writes_database"] is False
+ assert preview["rollback_gate_preview"]["writes_database"] is False
+ assert preview["safety"]["reads_secret_in_preview"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_request_gate_preview_ready_after_fake_fetch_but_still_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preview = build_pchome_auto_policy_db_apply_request_gate_preview(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ gate = preview["db_apply_request_gate"]
+ assert preview["result"] == "DB_APPLY_REQUEST_GATE_READY"
+ assert preview["summary"]["request_ready_count"] == 1
+ assert preview["summary"]["generated_file_hash_matches_count"] == 1
+ assert gate["request_id"].startswith("pchome-db-apply-request-")
+ assert gate["ready_for_explicit_db_apply_request"] is True
+ assert gate["ready_for_database_apply_now"] is False
+ assert gate["hash_matches"] is True
+ assert gate["command_preview"]["command"].startswith('psql "$DATABASE_URL"')
+ assert gate["command_preview"]["uses_secret_placeholder"] is True
+ assert gate["apply_sequence_preview"][3]["name"] == "execute_migration"
+ assert preview["required_runtime_readback"] == [
+ "fresh production /health",
+ "schema catalog readback for external_offer_evidence_receipts",
+ "index catalog readback",
+ "privilege readback",
+ "mapping backlog read-only smoke",
+ ]
+ assert preview["safety"]["executes_migration"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_execution_preflight_waits_without_ready_request_gate():
+ preflight = build_pchome_auto_policy_db_apply_execution_preflight(_payload(), batch_size=1)
+
+ execution = preflight["execution_preflight"]
+ assert preflight["policy"] == "read_only_pchome_growth_auto_policy_db_apply_execution_preflight"
+ assert preflight["result"] == "WAITING_FOR_DB_APPLY_REQUEST_GATE"
+ assert preflight["summary"]["preflight_ready_count"] == 0
+ assert preflight["summary"]["request_ready_count"] == 0
+ assert preflight["summary"]["required_artifact_count"] == 6
+ assert preflight["summary"]["snapshot_plan_count"] == 5
+ assert preflight["summary"]["readback_plan_count"] == 6
+ assert preflight["summary"]["rollback_artifact_count"] == 1
+ assert preflight["summary"]["abort_condition_count"] == 8
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert execution["ready_for_preflight_artifact_generation"] is False
+ assert execution["ready_for_database_apply_now"] is False
+ assert execution["reads_secret_in_preview"] is False
+ assert preflight["prewrite_snapshot_plan"]["required"] is True
+ assert preflight["prewrite_snapshot_plan"]["executes_sql_in_preview"] is False
+ assert preflight["post_apply_readback_plan"]["readback_check_count"] == 6
+ assert preflight["rollback_artifact_plan"]["uses_prewrite_snapshot"] is True
+ assert preflight["rollback_artifact_plan"]["writes_database"] is False
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_execution_preflight_ready_after_fake_fetch_but_still_no_db_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preflight = build_pchome_auto_policy_db_apply_execution_preflight(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ execution = preflight["execution_preflight"]
+ readback_keys = [check["key"] for check in preflight["post_apply_readback_plan"]["readback_checks"]]
+ snapshot_keys = [step["key"] for step in preflight["prewrite_snapshot_plan"]["snapshot_steps"]]
+ assert preflight["result"] == "DB_APPLY_EXECUTION_PREFLIGHT_READY"
+ assert preflight["summary"]["preflight_ready_count"] == 1
+ assert preflight["summary"]["request_ready_count"] == 1
+ assert preflight["summary"]["generated_file_hash_matches_count"] == 1
+ assert preflight["summary"]["executes_migration_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert execution["preflight_id"].startswith("pchome-db-apply-preflight-")
+ assert execution["source_request_id"].startswith("pchome-db-apply-request-")
+ assert execution["ready_for_preflight_artifact_generation"] is True
+ assert execution["ready_for_database_apply_now"] is False
+ assert execution["hash_matches"] is True
+ assert execution["operator_secret_boundary"] == "future_shell_only"
+ assert "schema_catalog_prewrite_snapshot" in snapshot_keys
+ assert "receipt_table_exists" in readback_keys
+ assert "mapping_backlog_read_only_smoke" in readback_keys
+ assert preflight["rollback_artifact_plan"]["artifacts"][0]["rollback_sql_preview"] == [
+ "DROP TABLE IF EXISTS external_offer_evidence_receipts;"
+ ]
+ assert preflight["rollback_artifact_plan"]["artifacts"][0]["executes_sql_in_preview"] is False
+ assert preflight["safety"]["writes_artifact_in_preview"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_package_waits_without_ready_preflight():
+ package = build_pchome_auto_policy_db_apply_authorization_package(_payload(), batch_size=1)
+
+ authorization = package["authorization_package"]
+ assert package["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_package"
+ assert package["result"] == "WAITING_FOR_DB_APPLY_EXECUTION_PREFLIGHT"
+ assert package["summary"]["authorization_check_count"] == 11
+ assert package["summary"]["authorization_package_ready_count"] == 0
+ assert package["summary"]["freshness_requirement_count"] == 5
+ assert package["summary"]["manifest_step_count"] == 6
+ assert package["summary"]["verifier_bundle_count"] == 3
+ assert package["summary"]["reads_secret_count"] == 0
+ assert package["summary"]["executes_sql_count"] == 0
+ assert package["summary"]["writes_database_count"] == 0
+ assert authorization["ready_for_explicit_apply_authorization_request"] is False
+ assert authorization["ready_for_database_apply_now"] is False
+ assert authorization["reads_secret_in_preview"] is False
+ assert package["machine_apply_manifest"]["executes_in_preview"] is False
+ assert package["machine_apply_manifest"]["writes_database"] is False
+ assert package["verifier_bundle"]["executes_in_preview"] is False
+ assert package["verifier_bundle"]["writes_database"] is False
+ assert package["safety"]["executes_sql"] is False
+ assert package["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_package_ready_after_fake_fetch_but_still_no_db_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ package = build_pchome_auto_policy_db_apply_authorization_package(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ authorization = package["authorization_package"]
+ assert package["result"] == "DB_APPLY_AUTHORIZATION_PACKAGE_READY"
+ assert package["summary"]["authorization_check_count"] == 11
+ assert package["summary"]["authorization_pass_count"] == 11
+ assert package["summary"]["authorization_waiting_count"] == 0
+ assert package["summary"]["authorization_package_ready_count"] == 1
+ assert package["summary"]["required_artifact_count"] == 6
+ assert package["summary"]["snapshot_plan_count"] == 5
+ assert package["summary"]["readback_plan_count"] == 6
+ assert package["summary"]["rollback_artifact_count"] == 1
+ assert package["summary"]["executes_migration_count"] == 0
+ assert package["summary"]["writes_database_count"] == 0
+ assert authorization["package_id"].startswith("pchome-db-apply-authorization-")
+ assert authorization["source_preflight_id"].startswith("pchome-db-apply-preflight-")
+ assert authorization["source_request_id"].startswith("pchome-db-apply-request-")
+ assert authorization["ready_for_explicit_apply_authorization_request"] is True
+ assert authorization["ready_for_database_apply_now"] is False
+ assert authorization["freshness_window_seconds"] == 300
+ assert authorization["operator_secret_boundary"] == "future_shell_only"
+ assert authorization["reads_secret_in_preview"] is False
+ assert authorization["executes_sql_in_preview"] is False
+ assert authorization["writes_database_in_preview"] is False
+ assert package["freshness_requirements"][0]["key"] == "production_truth_fresh_within_300_seconds"
+ assert package["machine_apply_manifest"]["manifest_step_count"] == 6
+ assert package["machine_apply_manifest"]["manifest_steps"][3]["executes_in_preview"] is False
+ assert "receipt_table_exists" in package["verifier_bundle"]["post_apply_verifiers"]
+ assert package["safety"]["reads_secret_in_preview"] is False
+ assert package["safety"]["executes_sql"] is False
+ assert package["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_verifier_artifact_preview_waits_without_ready_authorization_package():
+ preview = build_pchome_auto_policy_db_apply_verifier_artifact_preview(_payload(), batch_size=1)
+
+ artifact_preview = preview["artifact_preview"]
+ assert preview["policy"] == "read_only_pchome_growth_auto_policy_db_apply_verifier_artifact_preview"
+ assert preview["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_PACKAGE"
+ assert preview["summary"]["artifact_preview_ready_count"] == 0
+ assert preview["summary"]["artifact_schema_count"] == 3
+ assert preview["summary"]["artifact_generation_step_count"] == 5
+ assert preview["summary"]["verifier_check_count"] == 15
+ assert preview["summary"]["writes_artifact_count"] == 0
+ assert preview["summary"]["executes_sql_count"] == 0
+ assert preview["summary"]["writes_database_count"] == 0
+ assert artifact_preview["ready_for_future_artifact_generation"] is False
+ assert artifact_preview["ready_to_write_artifacts_now"] is False
+ assert artifact_preview["ready_for_database_apply_now"] is False
+ assert artifact_preview["writes_artifact_in_preview"] is False
+ assert preview["artifact_generation_plan"]["writes_artifact_in_preview"] is False
+ assert preview["verifier_manifest"]["executes_in_preview"] is False
+ assert preview["verifier_manifest"]["writes_database"] is False
+ assert preview["safety"]["writes_artifact_in_preview"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_verifier_artifact_preview_ready_after_fake_fetch_but_no_artifact_write():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preview = build_pchome_auto_policy_db_apply_verifier_artifact_preview(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ artifact_preview = preview["artifact_preview"]
+ artifact_keys = [schema["key"] for schema in preview["artifact_schemas"]]
+ assert preview["result"] == "DB_APPLY_VERIFIER_ARTIFACT_PREVIEW_READY"
+ assert preview["summary"]["artifact_preview_ready_count"] == 1
+ assert preview["summary"]["authorization_package_ready_count"] == 1
+ assert preview["summary"]["artifact_schema_count"] == 3
+ assert preview["summary"]["artifact_generation_step_count"] == 5
+ assert preview["summary"]["verifier_check_count"] == 15
+ assert preview["summary"]["writes_artifact_count"] == 0
+ assert preview["summary"]["writes_database_count"] == 0
+ assert artifact_preview["preview_id"].startswith("pchome-db-apply-artifacts-")
+ assert artifact_preview["source_authorization_package_id"].startswith("pchome-db-apply-authorization-")
+ assert artifact_preview["ready_for_future_artifact_generation"] is True
+ assert artifact_preview["ready_to_write_artifacts_now"] is False
+ assert artifact_preview["ready_for_database_apply_now"] is False
+ assert artifact_preview["writes_artifact_in_preview"] is False
+ assert artifact_preview["executes_sql_in_preview"] is False
+ assert "prewrite_snapshot_artifact" in artifact_keys
+ assert "post_apply_readback_artifact" in artifact_keys
+ assert "rollback_artifact" in artifact_keys
+ assert preview["artifact_schemas"][2]["rollback_sql_preview"] == [
+ "DROP TABLE IF EXISTS external_offer_evidence_receipts;"
+ ]
+ assert "receipt_table_exists" in preview["verifier_manifest"]["post_apply_checks"]
+ assert preview["artifact_generation_plan"]["generation_step_count"] == 5
+ assert preview["safety"]["writes_artifact_in_preview"] is False
+ assert preview["safety"]["executes_sql"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_final_handoff_package_waits_without_ready_artifact_preview():
+ package = build_pchome_auto_policy_db_apply_final_handoff_package(_payload(), batch_size=1)
+
+ handoff = package["final_handoff_package"]
+ assert package["policy"] == "read_only_pchome_growth_auto_policy_db_apply_final_handoff_package"
+ assert package["result"] == "WAITING_FOR_DB_APPLY_VERIFIER_ARTIFACT_PREVIEW"
+ assert package["summary"]["final_handoff_ready_count"] == 0
+ assert package["summary"]["handoff_section_count"] == 6
+ assert package["summary"]["final_runbook_step_count"] == 7
+ assert package["summary"]["command_preview_count"] == 3
+ assert package["summary"]["abort_gate_count"] == 10
+ assert package["summary"]["source_endpoint_count"] == 4
+ assert package["summary"]["writes_artifact_count"] == 0
+ assert package["summary"]["executes_sql_count"] == 0
+ assert package["summary"]["writes_database_count"] == 0
+ assert handoff["ready_for_explicit_db_apply_handoff"] is False
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["reads_secret_in_preview"] is False
+ assert handoff["writes_artifact_in_preview"] is False
+ assert package["final_runbook_manifest"]["executes_in_preview"] is False
+ assert package["final_runbook_manifest"]["writes_database"] is False
+ assert package["command_previews"][1]["reads_secret_in_preview"] is False
+ assert package["safety"]["executes_sql"] is False
+ assert package["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_final_handoff_package_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ package = build_pchome_auto_policy_db_apply_final_handoff_package(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ handoff = package["final_handoff_package"]
+ assert package["result"] == "DB_APPLY_FINAL_HANDOFF_PACKAGE_READY"
+ assert package["summary"]["final_handoff_ready_count"] == 1
+ assert package["summary"]["artifact_preview_ready_count"] == 1
+ assert package["summary"]["handoff_section_count"] == 6
+ assert package["summary"]["final_runbook_step_count"] == 7
+ assert package["summary"]["command_preview_count"] == 3
+ assert package["summary"]["abort_gate_count"] == 10
+ assert package["summary"]["source_endpoint_count"] == 4
+ assert package["summary"]["artifact_schema_count"] == 3
+ assert package["summary"]["verifier_check_count"] == 15
+ assert package["summary"]["writes_artifact_count"] == 0
+ assert package["summary"]["executes_sql_count"] == 0
+ assert package["summary"]["writes_database_count"] == 0
+ assert handoff["package_id"].startswith("pchome-db-apply-final-handoff-")
+ assert handoff["source_artifact_preview_id"].startswith("pchome-db-apply-artifacts-")
+ assert handoff["source_authorization_package_id"].startswith("pchome-db-apply-authorization-")
+ assert handoff["ready_for_explicit_db_apply_handoff"] is True
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["requires_separate_explicit_db_apply_authorization"] is True
+ assert handoff["operator_secret_boundary"] == "future_shell_only"
+ assert handoff["reads_secret_in_preview"] is False
+ assert handoff["executes_sql_in_preview"] is False
+ assert handoff["writes_database_in_preview"] is False
+ assert package["source_proof_manifest"]["source_endpoint_chain"][-1].endswith(
+ "auto-policy-db-apply-verifier-artifact-preview"
+ )
+ assert package["command_previews"][1]["command"].startswith('psql "$DATABASE_URL"')
+ assert package["command_previews"][1]["executes_in_preview"] is False
+ assert package["final_runbook_manifest"]["step_count"] == 7
+ assert package["safety"]["reads_secret_in_preview"] is False
+ assert package["safety"]["executes_sql"] is False
+ assert package["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_shell_preview_waits_without_ready_final_handoff():
+ preview = build_pchome_auto_policy_db_apply_controlled_dry_run_shell_preview(_payload(), batch_size=1)
+
+ shell = preview["controlled_dry_run_shell_preview"]
+ assert preview["policy"] == "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_preview"
+ assert preview["result"] == "WAITING_FOR_DB_APPLY_FINAL_HANDOFF_PACKAGE"
+ assert preview["summary"]["dry_run_shell_preview_ready_count"] == 0
+ assert preview["summary"]["shell_phase_count"] == 9
+ assert preview["summary"]["shell_script_line_count"] == 10
+ assert preview["summary"]["check_mode_required_check_count"] == 6
+ assert preview["summary"]["rollback_hook_count"] == 3
+ assert preview["summary"]["writes_script_count"] == 0
+ assert preview["summary"]["executes_script_count"] == 0
+ assert preview["summary"]["executes_sql_count"] == 0
+ assert preview["summary"]["writes_database_count"] == 0
+ assert shell["ready_for_future_shell_script_generation"] is False
+ assert shell["ready_to_write_script_now"] is False
+ assert shell["ready_to_execute_shell_now"] is False
+ assert shell["ready_for_database_apply_now"] is False
+ assert shell["reads_secret_in_preview"] is False
+ assert preview["shell_script_preview"]["writes_file_in_preview"] is False
+ assert preview["shell_script_preview"]["executes_script_in_preview"] is False
+ assert preview["check_mode_contract"]["dry_run_only"] is True
+ assert preview["rollback_hook_preview"]["writes_database"] is False
+ assert preview["safety"]["executes_script"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_shell_preview_ready_after_fake_fetch_but_no_shell_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preview = build_pchome_auto_policy_db_apply_controlled_dry_run_shell_preview(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ shell = preview["controlled_dry_run_shell_preview"]
+ phase_names = [phase["name"] for phase in preview["shell_phases"]]
+ assert preview["result"] == "DB_APPLY_CONTROLLED_DRY_RUN_SHELL_PREVIEW_READY"
+ assert preview["summary"]["dry_run_shell_preview_ready_count"] == 1
+ assert preview["summary"]["final_handoff_ready_count"] == 1
+ assert preview["summary"]["shell_phase_count"] == 9
+ assert preview["summary"]["shell_script_line_count"] == 10
+ assert preview["summary"]["check_mode_required_check_count"] == 6
+ assert preview["summary"]["rollback_hook_count"] == 3
+ assert preview["summary"]["writes_script_count"] == 0
+ assert preview["summary"]["executes_script_count"] == 0
+ assert preview["summary"]["writes_database_count"] == 0
+ assert shell["preview_id"].startswith("pchome-db-apply-dry-run-shell-")
+ assert shell["source_final_handoff_package_id"].startswith("pchome-db-apply-final-handoff-")
+ assert shell["ready_for_future_shell_script_generation"] is True
+ assert shell["ready_to_write_script_now"] is False
+ assert shell["ready_to_execute_shell_now"] is False
+ assert shell["ready_for_database_apply_now"] is False
+ assert shell["dry_run_only"] is True
+ assert shell["reads_secret_in_preview"] is False
+ assert shell["executes_sql_in_preview"] is False
+ assert shell["writes_database_in_preview"] is False
+ assert "render_database_apply_command_preview" in phase_names
+ assert preview["shell_script_preview"]["line_count"] == 10
+ assert preview["shell_script_preview"]["executes_script_in_preview"] is False
+ assert any('psql "$DATABASE_URL"' in line for line in preview["shell_script_preview"]["lines"])
+ assert preview["check_mode_contract"]["required_check_count"] == 6
+ assert preview["rollback_hook_preview"]["hook_count"] == 3
+ assert preview["safety"]["writes_script_in_preview"] is False
+ assert preview["safety"]["executes_script"] is False
+ assert preview["safety"]["executes_sql"] is False
+ assert preview["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_shell_closeout_waits_without_ready_preview():
+ closeout = build_pchome_auto_policy_db_apply_controlled_dry_run_shell_closeout(_payload(), batch_size=1)
+
+ boundary = closeout["explicit_authorization_boundary"]
+ assert closeout["policy"] == "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_closeout"
+ assert closeout["result"] == "WAITING_FOR_CONTROLLED_DRY_RUN_SHELL_PREVIEW"
+ assert closeout["summary"]["closeout_ready_count"] == 0
+ assert closeout["summary"]["closeout_check_count"] == 13
+ assert closeout["summary"]["closeout_waiting_count"] > 0
+ assert closeout["summary"]["future_apply_boundary_count"] == 6
+ assert closeout["summary"]["writes_script_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert boundary["ready_for_explicit_apply_authorization_boundary"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["requires_new_explicit_db_apply_authorization"] is True
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert closeout["controlled_dry_run_shell_closeout"]["ready_for_database_apply_now"] is False
+ assert closeout["safety"]["executes_script"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_shell_closeout_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_controlled_dry_run_shell_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ boundary = closeout["explicit_authorization_boundary"]
+ check_keys = [check["key"] for check in closeout["closeout_checks"]]
+ future_boundary_keys = [item["key"] for item in closeout["future_apply_boundaries"]]
+ assert closeout["result"] == "DB_APPLY_CONTROLLED_DRY_RUN_SHELL_CLOSEOUT_READY"
+ assert closeout["summary"]["closeout_ready_count"] == 1
+ assert closeout["summary"]["closeout_check_count"] == 13
+ assert closeout["summary"]["closeout_pass_count"] == 13
+ assert closeout["summary"]["closeout_waiting_count"] == 0
+ assert closeout["summary"]["dry_run_shell_preview_ready_count"] == 1
+ assert closeout["summary"]["future_apply_boundary_count"] == 6
+ assert closeout["summary"]["writes_script_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert boundary["boundary_id"].startswith("pchome-db-apply-dry-run-closeout-")
+ assert boundary["source_dry_run_shell_preview_id"].startswith("pchome-db-apply-dry-run-shell-")
+ assert boundary["ready_for_explicit_apply_authorization_boundary"] is True
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["requires_new_explicit_db_apply_authorization"] is True
+ assert boundary["operator_secret_boundary"] == "future_shell_only"
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert "preview_executes_no_shell" in check_keys
+ assert "database_url_from_future_shell_only" in future_boundary_keys
+ assert closeout["controlled_dry_run_shell_closeout"]["waiting_checks"] == []
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_script"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_request_intake_waits_without_ready_closeout():
+ intake = build_pchome_auto_policy_db_apply_authorization_request_intake(_payload(), batch_size=1)
+
+ request_intake = intake["authorization_request_intake"]
+ envelope = intake["authorization_envelope"]
+ assert intake["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_request_intake"
+ assert intake["result"] == "WAITING_FOR_CONTROLLED_DRY_RUN_SHELL_CLOSEOUT"
+ assert intake["summary"]["authorization_request_intake_ready_count"] == 0
+ assert intake["summary"]["required_request_evidence_count"] == 7
+ assert intake["summary"]["request_payload_required_field_count"] == 10
+ assert intake["summary"]["authorization_acceptance_gate_count"] == 11
+ assert intake["summary"]["rejection_reason_count"] == 10
+ assert intake["summary"]["writes_script_count"] == 0
+ assert intake["summary"]["reads_secret_count"] == 0
+ assert intake["summary"]["executes_script_count"] == 0
+ assert intake["summary"]["executes_sql_count"] == 0
+ assert intake["summary"]["writes_database_count"] == 0
+ assert request_intake["ready_for_authorization_request_intake"] is False
+ assert request_intake["ready_for_database_apply_now"] is False
+ assert request_intake["requires_new_explicit_db_apply_authorization"] is True
+ assert request_intake["reads_secret_in_preview"] is False
+ assert request_intake["executes_shell_in_preview"] is False
+ assert request_intake["executes_sql_in_preview"] is False
+ assert envelope["accepts_authorization_request"] is False
+ assert envelope["issues_database_apply_authorization"] is False
+ assert envelope["ready_for_database_apply_now"] is False
+ assert "operator_acknowledges_secret_boundary" in intake["request_payload_schema"]["required_fields"]
+ assert intake["request_payload_schema"]["accepts_database_url"] is False
+ assert intake["safety"]["executes_script"] is False
+ assert intake["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_request_intake_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ intake = build_pchome_auto_policy_db_apply_authorization_request_intake(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ request_intake = intake["authorization_request_intake"]
+ envelope = intake["authorization_envelope"]
+ evidence_keys = [item["key"] for item in intake["required_request_evidence"]]
+ gate_keys = [item["key"] for item in intake["authorization_acceptance_gates"]]
+ assert intake["result"] == "DB_APPLY_AUTHORIZATION_REQUEST_INTAKE_READY"
+ assert intake["summary"]["authorization_request_intake_ready_count"] == 1
+ assert intake["summary"]["required_request_evidence_count"] == 7
+ assert intake["summary"]["request_payload_required_field_count"] == 10
+ assert intake["summary"]["authorization_acceptance_gate_count"] == 11
+ assert intake["summary"]["authorization_acceptance_pass_count"] == 11
+ assert intake["summary"]["authorization_acceptance_waiting_count"] == 0
+ assert intake["summary"]["rejection_reason_count"] == 10
+ assert intake["summary"]["closeout_ready_count"] == 1
+ assert intake["summary"]["future_apply_boundary_count"] == 6
+ assert intake["summary"]["writes_script_count"] == 0
+ assert intake["summary"]["reads_secret_count"] == 0
+ assert intake["summary"]["executes_script_count"] == 0
+ assert intake["summary"]["executes_sql_count"] == 0
+ assert intake["summary"]["writes_database_count"] == 0
+ assert request_intake["intake_id"].startswith("pchome-db-apply-authorization-intake-")
+ assert request_intake["source_closeout_boundary_id"].startswith("pchome-db-apply-dry-run-closeout-")
+ assert request_intake["source_dry_run_shell_preview_id"].startswith("pchome-db-apply-dry-run-shell-")
+ assert request_intake["ready_for_authorization_request_intake"] is True
+ assert request_intake["ready_for_database_apply_now"] is False
+ assert request_intake["request_scope"] == "future_explicit_db_apply_authorization_only"
+ assert request_intake["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert request_intake["hash_matches"] is True
+ assert request_intake["operator_secret_boundary"] == "future_shell_only"
+ assert request_intake["reads_secret_in_preview"] is False
+ assert request_intake["executes_shell_in_preview"] is False
+ assert request_intake["executes_sql_in_preview"] is False
+ assert request_intake["writes_database_in_preview"] is False
+ assert envelope["accepts_authorization_request"] is True
+ assert envelope["issues_database_apply_authorization"] is False
+ assert envelope["ready_for_database_apply_now"] is False
+ assert envelope["rejects_direct_database_apply"] is True
+ assert "migration_file_hash" in evidence_keys
+ assert "preview_has_no_side_effects" in gate_keys
+ assert "direct_database_apply_requested_from_intake" in intake["rejection_reasons"]
+ assert intake["request_payload_schema"]["accepts_database_url"] is False
+ assert intake["request_payload_schema"]["accepts_authorization_header"] is False
+ assert intake["safety"]["reads_secret_in_preview"] is False
+ assert intake["safety"]["executes_script"] is False
+ assert intake["safety"]["executes_sql"] is False
+ assert intake["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_request_closeout_waits_without_ready_intake():
+ closeout = build_pchome_auto_policy_db_apply_authorization_request_closeout(_payload(), batch_size=1)
+
+ package = closeout["final_exact_request_package"]
+ assert closeout["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_request_closeout"
+ assert closeout["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_REQUEST_INTAKE"
+ assert closeout["summary"]["authorization_request_closeout_ready_count"] == 0
+ assert closeout["summary"]["closeout_check_count"] == 12
+ assert closeout["summary"]["closeout_waiting_count"] > 0
+ assert closeout["summary"]["exact_request_payload_field_count"] == 10
+ assert closeout["summary"]["machine_request_manifest_step_count"] == 6
+ assert closeout["summary"]["required_request_evidence_count"] == 7
+ assert closeout["summary"]["authorization_acceptance_gate_count"] == 11
+ assert closeout["summary"]["rejection_reason_count"] == 10
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert package["ready_for_exact_authorization_request_package"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["issues_database_apply_authorization"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_shell_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert closeout["machine_request_manifest"]["issues_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_request_closeout_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_request_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ package = closeout["final_exact_request_package"]
+ manifest = closeout["machine_request_manifest"]
+ check_keys = [check["key"] for check in closeout["closeout_checks"]]
+ template = package["exact_request_payload_template"]
+ assert closeout["result"] == "DB_APPLY_AUTHORIZATION_REQUEST_CLOSEOUT_READY"
+ assert closeout["summary"]["authorization_request_closeout_ready_count"] == 1
+ assert closeout["summary"]["closeout_check_count"] == 12
+ assert closeout["summary"]["closeout_pass_count"] == 12
+ assert closeout["summary"]["closeout_waiting_count"] == 0
+ assert closeout["summary"]["authorization_request_intake_ready_count"] == 1
+ assert closeout["summary"]["exact_request_payload_field_count"] == 10
+ assert closeout["summary"]["machine_request_manifest_step_count"] == 6
+ assert closeout["summary"]["required_request_evidence_count"] == 7
+ assert closeout["summary"]["authorization_acceptance_gate_count"] == 11
+ assert closeout["summary"]["rejection_reason_count"] == 10
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert package["package_id"].startswith("pchome-db-apply-authorization-closeout-")
+ assert package["source_intake_id"].startswith("pchome-db-apply-authorization-intake-")
+ assert package["ready_for_exact_authorization_request_package"] is True
+ assert package["ready_for_database_apply_now"] is False
+ assert package["issues_database_apply_authorization"] is False
+ assert package["request_scope"] == "future_explicit_db_apply_authorization_only"
+ assert package["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert package["hash_matches"] is True
+ assert package["payload_template_field_count"] == 10
+ assert template["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert template["operator_acknowledges_secret_boundary"] is True
+ assert template["fresh_production_truth"]["same_run_only"] is True
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_shell_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert manifest["manifest_step_count"] == 6
+ assert manifest["issues_database_apply_authorization"] is False
+ assert manifest["writes_database"] is False
+ assert "direct_apply_rejected" in check_keys
+ assert "preview_has_no_shell_sql_or_db_side_effect" in check_keys
+ assert closeout["authorization_request_closeout"]["waiting_checks"] == []
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_script"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_lane_guard_waits_without_ready_closeout():
+ guard = build_pchome_auto_policy_db_apply_authorization_lane_guard(_payload(), batch_size=1)
+
+ lane = guard["future_authorization_lane_guard"]
+ contract = guard["lane_transfer_contract"]
+ assert guard["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_lane_guard"
+ assert guard["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_REQUEST_CLOSEOUT"
+ assert guard["summary"]["authorization_lane_guard_ready_count"] == 0
+ assert guard["summary"]["lane_guard_check_count"] == 12
+ assert guard["summary"]["lane_guard_waiting_count"] > 0
+ assert guard["summary"]["lane_entry_requirement_count"] == 6
+ assert guard["summary"]["exact_request_payload_field_count"] == 10
+ assert guard["summary"]["machine_request_manifest_step_count"] == 6
+ assert guard["summary"]["reads_secret_count"] == 0
+ assert guard["summary"]["executes_script_count"] == 0
+ assert guard["summary"]["executes_sql_count"] == 0
+ assert guard["summary"]["writes_database_count"] == 0
+ assert lane["ready_for_future_authorization_lane_entry"] is False
+ assert lane["ready_for_database_apply_now"] is False
+ assert lane["issues_database_apply_authorization"] is False
+ assert lane["reads_secret_in_preview"] is False
+ assert lane["executes_shell_in_preview"] is False
+ assert lane["executes_sql_in_preview"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert guard["safety"]["executes_sql"] is False
+ assert guard["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_lane_guard_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ guard = build_pchome_auto_policy_db_apply_authorization_lane_guard(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ lane = guard["future_authorization_lane_guard"]
+ contract = guard["lane_transfer_contract"]
+ check_keys = [check["key"] for check in guard["lane_guard_checks"]]
+ requirement_keys = [item["key"] for item in guard["lane_entry_requirements"]]
+ assert guard["result"] == "DB_APPLY_AUTHORIZATION_LANE_GUARD_READY"
+ assert guard["summary"]["authorization_lane_guard_ready_count"] == 1
+ assert guard["summary"]["lane_guard_check_count"] == 12
+ assert guard["summary"]["lane_guard_pass_count"] == 12
+ assert guard["summary"]["lane_guard_waiting_count"] == 0
+ assert guard["summary"]["authorization_request_closeout_ready_count"] == 1
+ assert guard["summary"]["exact_request_payload_field_count"] == 10
+ assert guard["summary"]["machine_request_manifest_step_count"] == 6
+ assert guard["summary"]["lane_entry_requirement_count"] == 6
+ assert guard["summary"]["required_request_evidence_count"] == 7
+ assert guard["summary"]["authorization_acceptance_gate_count"] == 11
+ assert guard["summary"]["rejection_reason_count"] == 10
+ assert guard["summary"]["reads_secret_count"] == 0
+ assert guard["summary"]["executes_script_count"] == 0
+ assert guard["summary"]["executes_sql_count"] == 0
+ assert guard["summary"]["writes_database_count"] == 0
+ assert lane["guard_id"].startswith("pchome-db-apply-authorization-lane-")
+ assert lane["source_closeout_package_id"].startswith("pchome-db-apply-authorization-closeout-")
+ assert lane["ready_for_future_authorization_lane_entry"] is True
+ assert lane["ready_for_database_apply_now"] is False
+ assert lane["issues_database_apply_authorization"] is False
+ assert lane["request_scope"] == "future_explicit_db_apply_authorization_only"
+ assert lane["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert lane["hash_matches"] is True
+ assert lane["requires_fresh_production_truth_in_same_run"] is True
+ assert lane["operator_secret_boundary"] == "future_shell_only"
+ assert lane["reads_secret_in_preview"] is False
+ assert lane["executes_shell_in_preview"] is False
+ assert lane["executes_sql_in_preview"] is False
+ assert lane["writes_database_in_preview"] is False
+ assert contract["machine_verifiable"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "fresh_production_truth_same_run_required" in check_keys
+ assert "secret_rejection_step_present" in check_keys
+ assert "rollback_acknowledgement_present" in check_keys
+ assert "production_truth_refreshed_in_same_run" in requirement_keys
+ assert "direct_apply_rejected_until_next_lane" in requirement_keys
+ assert guard["safety"]["reads_secret_in_preview"] is False
+ assert guard["safety"]["executes_script"] is False
+ assert guard["safety"]["executes_sql"] is False
+ assert guard["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_decision_preflight_waits_without_ready_lane_guard():
+ preflight = build_pchome_auto_policy_db_apply_authorization_decision_preflight(_payload(), batch_size=1)
+
+ decision = preflight["future_authorization_decision_preflight"]
+ envelope = preflight["decision_preflight_envelope"]
+ assert preflight["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_decision_preflight"
+ assert preflight["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_LANE_GUARD"
+ assert preflight["summary"]["authorization_decision_preflight_ready_count"] == 0
+ assert preflight["summary"]["decision_preflight_check_count"] == 12
+ assert preflight["summary"]["decision_preflight_waiting_count"] > 0
+ assert preflight["summary"]["decision_input_requirement_count"] == 8
+ assert preflight["summary"]["decision_rejection_reason_count"] == 10
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert decision["ready_for_future_authorization_decision"] is False
+ assert decision["can_enter_authorization_decision_lane"] is False
+ assert decision["ready_for_database_apply_now"] is False
+ assert decision["issues_database_apply_authorization"] is False
+ assert decision["reads_secret_in_preview"] is False
+ assert decision["executes_shell_in_preview"] is False
+ assert decision["executes_sql_in_preview"] is False
+ assert envelope["allows_authorization_decision_in_future_lane"] is False
+ assert envelope["issues_database_apply_authorization"] is False
+ assert envelope["ready_for_database_apply_now"] is False
+ assert envelope["requires_post_apply_verifier"] is True
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_decision_preflight_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preflight = build_pchome_auto_policy_db_apply_authorization_decision_preflight(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ decision = preflight["future_authorization_decision_preflight"]
+ envelope = preflight["decision_preflight_envelope"]
+ check_keys = [check["key"] for check in preflight["decision_preflight_checks"]]
+ input_keys = [item["key"] for item in preflight["decision_input_requirements"]]
+ assert preflight["result"] == "DB_APPLY_AUTHORIZATION_DECISION_PREFLIGHT_READY"
+ assert preflight["summary"]["authorization_decision_preflight_ready_count"] == 1
+ assert preflight["summary"]["decision_preflight_check_count"] == 12
+ assert preflight["summary"]["decision_preflight_pass_count"] == 12
+ assert preflight["summary"]["decision_preflight_waiting_count"] == 0
+ assert preflight["summary"]["authorization_lane_guard_ready_count"] == 1
+ assert preflight["summary"]["decision_input_requirement_count"] == 8
+ assert preflight["summary"]["decision_rejection_reason_count"] == 10
+ assert preflight["summary"]["lane_entry_requirement_count"] == 6
+ assert preflight["summary"]["exact_request_payload_field_count"] == 10
+ assert preflight["summary"]["machine_request_manifest_step_count"] == 6
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert decision["preflight_id"].startswith("pchome-db-apply-authorization-decision-")
+ assert decision["source_lane_guard_id"].startswith("pchome-db-apply-authorization-lane-")
+ assert decision["ready_for_future_authorization_decision"] is True
+ assert decision["can_enter_authorization_decision_lane"] is True
+ assert decision["ready_for_database_apply_now"] is False
+ assert decision["issues_database_apply_authorization"] is False
+ assert decision["request_scope"] == "future_explicit_db_apply_authorization_only"
+ assert decision["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert decision["hash_matches"] is True
+ assert decision["requires_fresh_production_truth_in_same_run"] is True
+ assert decision["operator_secret_boundary"] == "future_shell_only"
+ assert decision["reads_secret_in_preview"] is False
+ assert decision["executes_shell_in_preview"] is False
+ assert decision["executes_sql_in_preview"] is False
+ assert decision["writes_database_in_preview"] is False
+ assert envelope["allows_authorization_decision_in_future_lane"] is True
+ assert envelope["issues_database_apply_authorization"] is False
+ assert envelope["ready_for_database_apply_now"] is False
+ assert envelope["rejects_direct_database_apply"] is True
+ assert envelope["requires_post_apply_verifier"] is True
+ assert "same_run_production_truth_required" in check_keys
+ assert "secret_boundary_rejects_secret_material" in check_keys
+ assert "post_apply_verifier_reference" in input_keys
+ assert "direct_database_apply_requested_from_decision_preflight" in preflight["decision_rejection_policy"]
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["executes_script"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_decision_closeout_waits_without_ready_preflight():
+ closeout = build_pchome_auto_policy_db_apply_authorization_decision_closeout(
+ _payload(),
+ batch_size=1,
+ )
+
+ decision = closeout["future_authorization_decision_closeout"]
+ package = closeout["future_authorization_decision_package"]
+ contract = closeout["decision_closeout_contract"]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_decision_closeout"
+ )
+ assert closeout["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_DECISION_PREFLIGHT"
+ assert closeout["summary"]["authorization_decision_closeout_ready_count"] == 0
+ assert closeout["summary"]["decision_closeout_check_count"] == 12
+ assert closeout["summary"]["decision_closeout_waiting_count"] > 0
+ assert closeout["summary"]["authorization_decision_preflight_ready_count"] == 0
+ assert closeout["summary"]["decision_input_requirement_count"] == 8
+ assert closeout["summary"]["decision_rejection_reason_count"] == 10
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert decision["ready_for_future_authorization_decision_closeout"] is False
+ assert decision["ready_for_database_apply_now"] is False
+ assert decision["issues_database_apply_authorization"] is False
+ assert package["ready_for_future_authorization_decision_package"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["issues_database_apply_authorization"] is False
+ assert package["requires_post_apply_verifier"] is True
+ assert package["reads_secret_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert contract["permits_future_authorization_decision_lane"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_decision_closeout_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_decision_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ decision = closeout["future_authorization_decision_closeout"]
+ package = closeout["future_authorization_decision_package"]
+ contract = closeout["decision_closeout_contract"]
+ check_keys = [check["key"] for check in closeout["decision_closeout_checks"]]
+ assert closeout["result"] == "DB_APPLY_AUTHORIZATION_DECISION_CLOSEOUT_READY"
+ assert closeout["summary"]["authorization_decision_closeout_ready_count"] == 1
+ assert closeout["summary"]["decision_closeout_check_count"] == 12
+ assert closeout["summary"]["decision_closeout_pass_count"] == 12
+ assert closeout["summary"]["decision_closeout_waiting_count"] == 0
+ assert closeout["summary"]["authorization_decision_preflight_ready_count"] == 1
+ assert closeout["summary"]["decision_input_requirement_count"] == 8
+ assert closeout["summary"]["decision_rejection_reason_count"] == 10
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert decision["closeout_id"].startswith("pchome-db-apply-authorization-decision-closeout-")
+ assert decision["source_preflight_id"].startswith("pchome-db-apply-authorization-decision-")
+ assert decision["source_lane_guard_id"].startswith("pchome-db-apply-authorization-lane-")
+ assert decision["source_closeout_package_id"].startswith("pchome-db-apply-authorization-closeout-")
+ assert decision["ready_for_future_authorization_decision_closeout"] is True
+ assert decision["ready_for_database_apply_now"] is False
+ assert decision["issues_database_apply_authorization"] is False
+ assert package["package_id"].startswith("pchome-db-apply-authorization-decision-closeout-")
+ assert package["ready_for_future_authorization_decision_package"] is True
+ assert package["ready_for_database_apply_now"] is False
+ assert package["issues_database_apply_authorization"] is False
+ assert package["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert package["hash_matches"] is True
+ assert package["requires_fresh_production_truth_in_same_run"] is True
+ assert package["requires_post_apply_verifier"] is True
+ assert package["operator_secret_boundary"] == "future_shell_only"
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_shell_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert contract["permits_future_authorization_decision_lane"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "post_apply_verifier_required" in check_keys
+ assert "direct_apply_still_rejected" in check_keys
+ assert "manual_review_regression_absent" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_script"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_issuer_gate_waits_without_ready_closeout():
+ gate = build_pchome_auto_policy_db_apply_authorization_issuer_gate(
+ _payload(),
+ batch_size=1,
+ )
+
+ issuer = gate["future_authorization_issuer_gate"]
+ envelope = gate["final_nonsecret_authorization_envelope"]
+ contract = gate["issuer_gate_contract"]
+ assert gate["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_issuer_gate"
+ assert gate["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_DECISION_CLOSEOUT"
+ assert gate["summary"]["authorization_issuer_gate_ready_count"] == 0
+ assert gate["summary"]["issuer_gate_check_count"] == 12
+ assert gate["summary"]["issuer_gate_waiting_count"] > 0
+ assert gate["summary"]["authorization_decision_closeout_ready_count"] == 0
+ assert gate["summary"]["decision_closeout_check_count"] == 12
+ assert gate["summary"]["required_issuer_evidence_count"] == 9
+ assert gate["summary"]["nonsecret_authorization_claim_count"] == 8
+ assert gate["summary"]["post_apply_verifier_required_count"] == 1
+ assert gate["summary"]["same_run_truth_required_count"] == 1
+ assert gate["summary"]["reads_secret_count"] == 0
+ assert gate["summary"]["executes_script_count"] == 0
+ assert gate["summary"]["executes_sql_count"] == 0
+ assert gate["summary"]["writes_database_count"] == 0
+ assert issuer["ready_for_future_authorization_issuer_lane"] is False
+ assert issuer["ready_for_database_apply_now"] is False
+ assert issuer["issues_database_apply_authorization"] is False
+ assert issuer["signs_database_apply_authorization"] is False
+ assert envelope["authorization_material_type"] == "nonsecret_request_envelope"
+ assert envelope["ready_for_future_authorization_issuer_lane"] is False
+ assert envelope["ready_for_database_apply_now"] is False
+ assert envelope["issues_database_apply_authorization"] is False
+ assert envelope["signs_database_apply_authorization"] is False
+ assert envelope["secret_material_included"] is False
+ assert envelope["reads_secret_in_preview"] is False
+ assert envelope["writes_database_in_preview"] is False
+ assert contract["permits_future_authorization_issuer_lane"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert gate["safety"]["executes_sql"] is False
+ assert gate["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_issuer_gate_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ gate = build_pchome_auto_policy_db_apply_authorization_issuer_gate(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ issuer = gate["future_authorization_issuer_gate"]
+ envelope = gate["final_nonsecret_authorization_envelope"]
+ contract = gate["issuer_gate_contract"]
+ check_keys = [check["key"] for check in gate["issuer_gate_checks"]]
+ evidence_keys = [item["key"] for item in envelope["required_issuer_evidence"]]
+ claim_keys = [item["key"] for item in envelope["nonsecret_authorization_claims"]]
+ assert gate["result"] == "DB_APPLY_AUTHORIZATION_ISSUER_GATE_READY"
+ assert gate["summary"]["authorization_issuer_gate_ready_count"] == 1
+ assert gate["summary"]["issuer_gate_check_count"] == 12
+ assert gate["summary"]["issuer_gate_pass_count"] == 12
+ assert gate["summary"]["issuer_gate_waiting_count"] == 0
+ assert gate["summary"]["authorization_decision_closeout_ready_count"] == 1
+ assert gate["summary"]["decision_closeout_check_count"] == 12
+ assert gate["summary"]["required_issuer_evidence_count"] == 9
+ assert gate["summary"]["nonsecret_authorization_claim_count"] == 8
+ assert gate["summary"]["post_apply_verifier_required_count"] == 1
+ assert gate["summary"]["same_run_truth_required_count"] == 1
+ assert gate["summary"]["decision_rejection_reason_count"] == 10
+ assert gate["summary"]["reads_secret_count"] == 0
+ assert gate["summary"]["executes_script_count"] == 0
+ assert gate["summary"]["executes_sql_count"] == 0
+ assert gate["summary"]["writes_database_count"] == 0
+ assert issuer["gate_id"].startswith("pchome-db-apply-authorization-issuer-gate-")
+ assert issuer["source_decision_closeout_id"].startswith(
+ "pchome-db-apply-authorization-decision-closeout-"
+ )
+ assert issuer["source_decision_preflight_id"].startswith("pchome-db-apply-authorization-decision-")
+ assert issuer["source_lane_guard_id"].startswith("pchome-db-apply-authorization-lane-")
+ assert issuer["ready_for_future_authorization_issuer_lane"] is True
+ assert issuer["ready_for_database_apply_now"] is False
+ assert issuer["issues_database_apply_authorization"] is False
+ assert issuer["signs_database_apply_authorization"] is False
+ assert envelope["envelope_id"].startswith("pchome-db-apply-authorization-issuer-gate-")
+ assert envelope["authorization_material_type"] == "nonsecret_request_envelope"
+ assert envelope["decision_scope"] == "future_explicit_db_apply_authorization_issuer_lane_only"
+ assert envelope["ready_for_future_authorization_issuer_lane"] is True
+ assert envelope["ready_for_database_apply_now"] is False
+ assert envelope["issues_database_apply_authorization"] is False
+ assert envelope["signs_database_apply_authorization"] is False
+ assert envelope["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert envelope["hash_matches"] is True
+ assert envelope["requires_fresh_production_truth_in_same_run"] is True
+ assert envelope["requires_post_apply_verifier"] is True
+ assert envelope["operator_secret_boundary"] == "future_shell_only"
+ assert envelope["secret_material_included"] is False
+ assert envelope["reads_secret_in_preview"] is False
+ assert envelope["executes_shell_in_preview"] is False
+ assert envelope["executes_sql_in_preview"] is False
+ assert envelope["writes_database_in_preview"] is False
+ assert contract["permits_future_authorization_issuer_lane"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "issuer_policy_requires_future_explicit_authorization" in check_keys
+ assert "direct_apply_still_rejected" in check_keys
+ assert "secret_boundary_rejection" in evidence_keys
+ assert "no_database_apply_authorization_issued" in claim_keys
+ assert gate["safety"]["reads_secret_in_preview"] is False
+ assert gate["safety"]["executes_script"] is False
+ assert gate["safety"]["executes_sql"] is False
+ assert gate["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_decision_preflight_waits_without_ready_issuer_gate():
+ preflight = build_pchome_auto_policy_db_apply_authorization_signing_decision_preflight(
+ _payload(),
+ batch_size=1,
+ )
+
+ decision = preflight["future_authorization_signing_decision_preflight"]
+ envelope = preflight["signing_decision_preflight_envelope"]
+ assert preflight["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_decision_preflight"
+ )
+ assert preflight["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_ISSUER_GATE"
+ assert preflight["summary"]["authorization_signing_decision_preflight_ready_count"] == 0
+ assert preflight["summary"]["signing_decision_preflight_check_count"] == 12
+ assert preflight["summary"]["signing_decision_preflight_waiting_count"] > 0
+ assert preflight["summary"]["authorization_issuer_gate_ready_count"] == 0
+ assert preflight["summary"]["issuer_gate_check_count"] == 12
+ assert preflight["summary"]["required_issuer_evidence_count"] == 9
+ assert preflight["summary"]["nonsecret_authorization_claim_count"] == 8
+ assert preflight["summary"]["signing_decision_input_requirement_count"] == 10
+ assert preflight["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert decision["ready_for_future_signing_decision_preflight"] is False
+ assert decision["can_enter_authorization_signing_decision_lane"] is False
+ assert decision["ready_for_database_apply_now"] is False
+ assert decision["issues_database_apply_authorization"] is False
+ assert decision["signs_database_apply_authorization"] is False
+ assert envelope["allows_future_authorization_signing_decision_lane"] is False
+ assert envelope["issues_database_apply_authorization"] is False
+ assert envelope["ready_for_database_apply_now"] is False
+ assert envelope["signs_database_apply_authorization"] is False
+ assert envelope["secret_material_required_in_preview"] is False
+ assert preflight["safety"]["signs_database_apply_authorization"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_decision_preflight_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preflight = build_pchome_auto_policy_db_apply_authorization_signing_decision_preflight(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ decision = preflight["future_authorization_signing_decision_preflight"]
+ envelope = preflight["signing_decision_preflight_envelope"]
+ source_envelope = preflight["source_nonsecret_authorization_envelope"]
+ check_keys = [check["key"] for check in preflight["signing_decision_preflight_checks"]]
+ input_keys = [item["key"] for item in preflight["signing_decision_input_requirements"]]
+ assert preflight["result"] == "DB_APPLY_AUTHORIZATION_SIGNING_DECISION_PREFLIGHT_READY"
+ assert preflight["summary"]["authorization_signing_decision_preflight_ready_count"] == 1
+ assert preflight["summary"]["signing_decision_preflight_check_count"] == 12
+ assert preflight["summary"]["signing_decision_preflight_pass_count"] == 12
+ assert preflight["summary"]["signing_decision_preflight_waiting_count"] == 0
+ assert preflight["summary"]["authorization_issuer_gate_ready_count"] == 1
+ assert preflight["summary"]["issuer_gate_check_count"] == 12
+ assert preflight["summary"]["required_issuer_evidence_count"] == 9
+ assert preflight["summary"]["nonsecret_authorization_claim_count"] == 8
+ assert preflight["summary"]["signing_decision_input_requirement_count"] == 10
+ assert preflight["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert preflight["summary"]["post_apply_verifier_required_count"] == 1
+ assert preflight["summary"]["same_run_truth_required_count"] == 1
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert decision["preflight_id"].startswith("pchome-db-apply-authorization-signing-preflight-")
+ assert decision["source_issuer_gate_id"].startswith("pchome-db-apply-authorization-issuer-gate-")
+ assert decision["source_decision_closeout_id"].startswith(
+ "pchome-db-apply-authorization-decision-closeout-"
+ )
+ assert decision["ready_for_future_signing_decision_preflight"] is True
+ assert decision["can_enter_authorization_signing_decision_lane"] is True
+ assert decision["ready_for_database_apply_now"] is False
+ assert decision["issues_database_apply_authorization"] is False
+ assert decision["signs_database_apply_authorization"] is False
+ assert envelope["allows_future_authorization_signing_decision_lane"] is True
+ assert envelope["issues_database_apply_authorization"] is False
+ assert envelope["ready_for_database_apply_now"] is False
+ assert envelope["signs_database_apply_authorization"] is False
+ assert envelope["rejects_direct_database_apply"] is True
+ assert envelope["requires_post_apply_verifier"] is True
+ assert envelope["secret_material_required_in_preview"] is False
+ assert source_envelope["authorization_material_type"] == "nonsecret_request_envelope"
+ assert source_envelope["secret_material_included"] is False
+ assert source_envelope["signs_database_apply_authorization"] is False
+ assert "nonsecret_envelope_complete" in check_keys
+ assert "signing_and_direct_apply_still_rejected" in check_keys
+ assert "no_signing_without_future_explicit_authorization" in input_keys
+ assert "authorization_signing_requested_from_preflight" in preflight["signing_decision_rejection_policy"]
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["signs_database_apply_authorization"] is False
+ assert preflight["safety"]["executes_script"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_decision_closeout_waits_without_ready_preflight():
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_decision_closeout(
+ _payload(),
+ batch_size=1,
+ )
+
+ decision = closeout["future_authorization_signing_decision_closeout"]
+ package = closeout["unsigned_signing_decision_package"]
+ contract = closeout["signing_decision_closeout_contract"]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_decision_closeout"
+ )
+ assert closeout["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_DECISION_PREFLIGHT"
+ assert closeout["summary"]["authorization_signing_decision_closeout_ready_count"] == 0
+ assert closeout["summary"]["signing_decision_closeout_check_count"] == 12
+ assert closeout["summary"]["signing_decision_closeout_waiting_count"] > 0
+ assert closeout["summary"]["authorization_signing_decision_preflight_ready_count"] == 0
+ assert closeout["summary"]["signing_decision_preflight_check_count"] == 12
+ assert closeout["summary"]["signing_decision_input_requirement_count"] == 10
+ assert closeout["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert decision["ready_for_future_signing_decision_closeout"] is False
+ assert decision["can_enter_unsigned_signing_decision_package_lane"] is False
+ assert decision["ready_for_database_apply_now"] is False
+ assert decision["issues_database_apply_authorization"] is False
+ assert decision["signs_database_apply_authorization"] is False
+ assert package["authorization_material_type"] == "unsigned_signing_decision_package"
+ assert package["ready_for_future_unsigned_signing_decision_package"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["issues_database_apply_authorization"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert package["secret_material_included"] is False
+ assert package["secret_material_required_in_preview"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert contract["permits_future_unsigned_signing_decision_package_lane"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_decision_closeout_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_decision_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ decision = closeout["future_authorization_signing_decision_closeout"]
+ package = closeout["unsigned_signing_decision_package"]
+ contract = closeout["signing_decision_closeout_contract"]
+ check_keys = [check["key"] for check in closeout["signing_decision_closeout_checks"]]
+ assert closeout["result"] == "DB_APPLY_AUTHORIZATION_SIGNING_DECISION_CLOSEOUT_READY"
+ assert closeout["summary"]["authorization_signing_decision_closeout_ready_count"] == 1
+ assert closeout["summary"]["signing_decision_closeout_check_count"] == 12
+ assert closeout["summary"]["signing_decision_closeout_pass_count"] == 12
+ assert closeout["summary"]["signing_decision_closeout_waiting_count"] == 0
+ assert closeout["summary"]["authorization_signing_decision_preflight_ready_count"] == 1
+ assert closeout["summary"]["signing_decision_preflight_check_count"] == 12
+ assert closeout["summary"]["signing_decision_input_requirement_count"] == 10
+ assert closeout["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert closeout["summary"]["required_issuer_evidence_count"] == 9
+ assert closeout["summary"]["nonsecret_authorization_claim_count"] == 8
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert decision["closeout_id"].startswith("pchome-db-apply-authorization-signing-closeout-")
+ assert decision["source_signing_decision_preflight_id"].startswith(
+ "pchome-db-apply-authorization-signing-preflight-"
+ )
+ assert decision["source_issuer_gate_id"].startswith("pchome-db-apply-authorization-issuer-gate-")
+ assert decision["ready_for_future_signing_decision_closeout"] is True
+ assert decision["can_enter_unsigned_signing_decision_package_lane"] is True
+ assert decision["ready_for_database_apply_now"] is False
+ assert decision["issues_database_apply_authorization"] is False
+ assert decision["signs_database_apply_authorization"] is False
+ assert package["package_id"].startswith("pchome-db-apply-authorization-signing-closeout-")
+ assert package["authorization_material_type"] == "unsigned_signing_decision_package"
+ assert package["ready_for_future_unsigned_signing_decision_package"] is True
+ assert package["ready_for_database_apply_now"] is False
+ assert package["issues_database_apply_authorization"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert package["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert package["hash_matches"] is True
+ assert package["requires_fresh_production_truth_in_same_run"] is True
+ assert package["requires_post_apply_verifier"] is True
+ assert package["operator_secret_boundary"] == "future_shell_only"
+ assert package["secret_material_included"] is False
+ assert package["secret_material_required_in_preview"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_shell_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert contract["permits_future_unsigned_signing_decision_package_lane"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "unsigned_package_source_envelope_complete" in check_keys
+ assert "preview_has_no_side_effects_and_no_signing" in check_keys
+ assert "manual_review_regression_absent" in check_keys
+ assert "authorization_signing_requested_from_preflight" in package[
+ "signing_decision_rejection_policy"
+ ]
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_script"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_issuer_guard_waits_without_ready_closeout():
+ guard = build_pchome_auto_policy_db_apply_authorization_signing_issuer_guard(
+ _payload(),
+ batch_size=1,
+ )
+
+ issuer_guard = guard["future_authorization_signing_issuer_guard"]
+ boundary = guard["signable_request_boundary"]
+ contract = guard["signing_issuer_guard_contract"]
+ assert guard["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_guard"
+ )
+ assert guard["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_DECISION_CLOSEOUT"
+ assert guard["summary"]["authorization_signing_issuer_guard_ready_count"] == 0
+ assert guard["summary"]["signing_issuer_guard_check_count"] == 12
+ assert guard["summary"]["signing_issuer_guard_waiting_count"] > 0
+ assert guard["summary"]["authorization_signing_decision_closeout_ready_count"] == 0
+ assert guard["summary"]["signing_decision_closeout_check_count"] == 12
+ assert guard["summary"]["signing_decision_input_requirement_count"] == 10
+ assert guard["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert guard["summary"]["signs_database_apply_authorization_count"] == 0
+ assert guard["summary"]["reads_secret_count"] == 0
+ assert guard["summary"]["executes_script_count"] == 0
+ assert guard["summary"]["executes_sql_count"] == 0
+ assert guard["summary"]["writes_database_count"] == 0
+ assert issuer_guard["ready_for_future_signing_issuer_guard"] is False
+ assert issuer_guard["can_enter_future_authorization_signing_issuer_lane"] is False
+ assert issuer_guard["ready_for_database_apply_now"] is False
+ assert issuer_guard["issues_database_apply_authorization"] is False
+ assert issuer_guard["signs_database_apply_authorization"] is False
+ assert boundary["request_boundary_type"] == "future_signable_request_boundary"
+ assert boundary["ready_for_future_signable_request_boundary"] is False
+ assert boundary["can_enter_future_authorization_signing_issuer_lane"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["issues_database_apply_authorization"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert contract["permits_future_authorization_signing_issuer_lane"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert guard["safety"]["signs_database_apply_authorization"] is False
+ assert guard["safety"]["executes_sql"] is False
+ assert guard["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_issuer_guard_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ guard = build_pchome_auto_policy_db_apply_authorization_signing_issuer_guard(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ issuer_guard = guard["future_authorization_signing_issuer_guard"]
+ boundary = guard["signable_request_boundary"]
+ contract = guard["signing_issuer_guard_contract"]
+ check_keys = [check["key"] for check in guard["signing_issuer_guard_checks"]]
+ assert guard["result"] == "DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_GUARD_READY"
+ assert guard["summary"]["authorization_signing_issuer_guard_ready_count"] == 1
+ assert guard["summary"]["signing_issuer_guard_check_count"] == 12
+ assert guard["summary"]["signing_issuer_guard_pass_count"] == 12
+ assert guard["summary"]["signing_issuer_guard_waiting_count"] == 0
+ assert guard["summary"]["authorization_signing_decision_closeout_ready_count"] == 1
+ assert guard["summary"]["signing_decision_closeout_check_count"] == 12
+ assert guard["summary"]["signing_decision_input_requirement_count"] == 10
+ assert guard["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert guard["summary"]["required_issuer_evidence_count"] == 9
+ assert guard["summary"]["nonsecret_authorization_claim_count"] == 8
+ assert guard["summary"]["post_apply_verifier_required_count"] == 1
+ assert guard["summary"]["same_run_truth_required_count"] == 1
+ assert guard["summary"]["signs_database_apply_authorization_count"] == 0
+ assert guard["summary"]["reads_secret_count"] == 0
+ assert guard["summary"]["executes_script_count"] == 0
+ assert guard["summary"]["executes_sql_count"] == 0
+ assert guard["summary"]["writes_database_count"] == 0
+ assert issuer_guard["guard_id"].startswith("pchome-db-apply-authorization-signing-issuer-")
+ assert issuer_guard["source_signing_decision_closeout_id"].startswith(
+ "pchome-db-apply-authorization-signing-closeout-"
+ )
+ assert issuer_guard["source_signing_decision_preflight_id"].startswith(
+ "pchome-db-apply-authorization-signing-preflight-"
+ )
+ assert issuer_guard["ready_for_future_signing_issuer_guard"] is True
+ assert issuer_guard["can_enter_future_authorization_signing_issuer_lane"] is True
+ assert issuer_guard["ready_for_database_apply_now"] is False
+ assert issuer_guard["issues_database_apply_authorization"] is False
+ assert issuer_guard["signs_database_apply_authorization"] is False
+ assert boundary["boundary_id"].startswith("pchome-db-apply-authorization-signing-issuer-")
+ assert boundary["request_boundary_type"] == "future_signable_request_boundary"
+ assert boundary["ready_for_future_signable_request_boundary"] is True
+ assert boundary["can_enter_future_authorization_signing_issuer_lane"] is True
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["issues_database_apply_authorization"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert boundary["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert boundary["hash_matches"] is True
+ assert boundary["requires_fresh_production_truth_in_same_run"] is True
+ assert boundary["requires_post_apply_verifier"] is True
+ assert boundary["operator_secret_boundary"] == "future_shell_only"
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert contract["permits_future_authorization_signing_issuer_lane"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "signable_boundary_is_future_only" in check_keys
+ assert "preview_has_no_side_effects_and_no_signing" in check_keys
+ assert guard["safety"]["reads_secret_in_preview"] is False
+ assert guard["safety"]["signs_database_apply_authorization"] is False
+ assert guard["safety"]["executes_script"] is False
+ assert guard["safety"]["executes_sql"] is False
+ assert guard["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_issuer_closeout_waits_without_ready_guard():
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_issuer_closeout(
+ _payload(),
+ batch_size=1,
+ )
+
+ issuer_closeout = closeout["future_authorization_signing_issuer_closeout"]
+ final_package = closeout["final_signable_request_package"]
+ contract = closeout["signing_issuer_closeout_contract"]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_closeout"
+ )
+ assert closeout["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_GUARD"
+ assert closeout["summary"]["authorization_signing_issuer_closeout_ready_count"] == 0
+ assert closeout["summary"]["signing_issuer_closeout_check_count"] == 12
+ assert closeout["summary"]["signing_issuer_closeout_waiting_count"] > 0
+ assert closeout["summary"]["authorization_signing_issuer_guard_ready_count"] == 0
+ assert closeout["summary"]["signing_issuer_guard_check_count"] == 12
+ assert closeout["summary"]["signing_decision_input_requirement_count"] == 10
+ assert closeout["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert issuer_closeout["ready_for_future_signing_issuer_closeout"] is False
+ assert issuer_closeout["can_enter_future_final_signable_request_package_lane"] is False
+ assert issuer_closeout["ready_for_database_apply_now"] is False
+ assert issuer_closeout["issues_database_apply_authorization"] is False
+ assert issuer_closeout["signs_database_apply_authorization"] is False
+ assert final_package["authorization_material_type"] == "final_signable_request_package"
+ assert final_package["ready_for_future_final_signable_request_package"] is False
+ assert final_package["ready_for_database_apply_now"] is False
+ assert final_package["issues_database_apply_authorization"] is False
+ assert final_package["signs_database_apply_authorization"] is False
+ assert final_package["secret_material_included"] is False
+ assert final_package["secret_material_required_in_preview"] is False
+ assert final_package["reads_secret_in_preview"] is False
+ assert contract["permits_future_final_signable_request_package_lane"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_issuer_closeout_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_issuer_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ issuer_closeout = closeout["future_authorization_signing_issuer_closeout"]
+ final_package = closeout["final_signable_request_package"]
+ contract = closeout["signing_issuer_closeout_contract"]
+ check_keys = [check["key"] for check in closeout["signing_issuer_closeout_checks"]]
+ assert closeout["result"] == "DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_CLOSEOUT_READY"
+ assert closeout["summary"]["authorization_signing_issuer_closeout_ready_count"] == 1
+ assert closeout["summary"]["signing_issuer_closeout_check_count"] == 12
+ assert closeout["summary"]["signing_issuer_closeout_pass_count"] == 12
+ assert closeout["summary"]["signing_issuer_closeout_waiting_count"] == 0
+ assert closeout["summary"]["authorization_signing_issuer_guard_ready_count"] == 1
+ assert closeout["summary"]["signing_issuer_guard_check_count"] == 12
+ assert closeout["summary"]["signing_decision_input_requirement_count"] == 10
+ assert closeout["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert closeout["summary"]["required_issuer_evidence_count"] == 9
+ assert closeout["summary"]["nonsecret_authorization_claim_count"] == 8
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert issuer_closeout["closeout_id"].startswith(
+ "pchome-db-apply-authorization-signing-issuer-closeout-"
+ )
+ assert issuer_closeout["source_signing_issuer_guard_id"].startswith(
+ "pchome-db-apply-authorization-signing-issuer-"
+ )
+ assert issuer_closeout["ready_for_future_signing_issuer_closeout"] is True
+ assert issuer_closeout["can_enter_future_final_signable_request_package_lane"] is True
+ assert issuer_closeout["ready_for_database_apply_now"] is False
+ assert issuer_closeout["issues_database_apply_authorization"] is False
+ assert issuer_closeout["signs_database_apply_authorization"] is False
+ assert final_package["package_id"].startswith(
+ "pchome-db-apply-authorization-signing-issuer-closeout-"
+ )
+ assert final_package["authorization_material_type"] == "final_signable_request_package"
+ assert final_package["ready_for_future_final_signable_request_package"] is True
+ assert final_package["ready_for_database_apply_now"] is False
+ assert final_package["issues_database_apply_authorization"] is False
+ assert final_package["signs_database_apply_authorization"] is False
+ assert final_package["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert final_package["hash_matches"] is True
+ assert final_package["requires_fresh_production_truth_in_same_run"] is True
+ assert final_package["requires_post_apply_verifier"] is True
+ assert final_package["operator_secret_boundary"] == "future_shell_only"
+ assert final_package["secret_material_included"] is False
+ assert final_package["secret_material_required_in_preview"] is False
+ assert final_package["reads_secret_in_preview"] is False
+ assert final_package["executes_shell_in_preview"] is False
+ assert final_package["executes_sql_in_preview"] is False
+ assert final_package["writes_database_in_preview"] is False
+ assert contract["permits_future_final_signable_request_package_lane"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "final_package_does_not_authorize_sign_or_apply" in check_keys
+ assert "preview_has_no_side_effects_and_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_script"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_execution_preflight_waits_without_ready_closeout():
+ preflight = build_pchome_auto_policy_db_apply_authorization_signing_execution_preflight(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_preflight = preflight["future_authorization_signing_execution_preflight"]
+ package = preflight["signing_execution_preflight_package"]
+ boundary = preflight["operator_held_secret_boundary_contract"]
+ contract = preflight["signing_execution_preflight_contract"]
+ assert preflight["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_execution_preflight"
+ )
+ assert preflight["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_ISSUER_CLOSEOUT"
+ assert preflight["summary"]["authorization_signing_execution_preflight_ready_count"] == 0
+ assert preflight["summary"]["signing_execution_preflight_check_count"] == 12
+ assert preflight["summary"]["signing_execution_preflight_waiting_count"] > 0
+ assert preflight["summary"]["authorization_signing_issuer_closeout_ready_count"] == 0
+ assert preflight["summary"]["signing_issuer_closeout_check_count"] == 12
+ assert preflight["summary"]["final_signable_request_package_ready_count"] == 0
+ assert preflight["summary"]["operator_held_secret_boundary_count"] == 1
+ assert preflight["summary"]["signing_execution_input_requirement_count"] == 10
+ assert preflight["summary"]["signing_execution_abort_condition_count"] == 8
+ assert preflight["summary"]["rollback_boundary_count"] == 4
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert future_preflight["ready_for_future_signing_execution_preflight"] is False
+ assert future_preflight["can_enter_future_authorization_signing_execution_lane"] is False
+ assert future_preflight["ready_for_database_apply_now"] is False
+ assert future_preflight["issues_database_apply_authorization"] is False
+ assert future_preflight["signs_database_apply_authorization"] is False
+ assert future_preflight["secret_material_included"] is False
+ assert future_preflight["secret_material_required_in_preview"] is False
+ assert future_preflight["reads_secret_in_preview"] is False
+ assert package["authorization_material_type"] == "signing_execution_preflight_package"
+ assert package["required_nonsecret_input_count"] == 10
+ assert package["ready_for_database_apply_now"] is False
+ assert package["issues_database_apply_authorization"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert package["secret_material_included"] is False
+ assert package["secret_material_required_in_preview"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_shell_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert boundary["secret_reference_mode"] == "external_runtime_reference_only"
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["accepts_plaintext_secret"] is False
+ assert boundary["permits_secret_value_logging"] is False
+ assert contract["permits_future_explicit_authorization_signing_execution_lane"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["signs_database_apply_authorization"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_execution_preflight_ready_after_fake_fetch_but_no_signing():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preflight = build_pchome_auto_policy_db_apply_authorization_signing_execution_preflight(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_preflight = preflight["future_authorization_signing_execution_preflight"]
+ package = preflight["signing_execution_preflight_package"]
+ boundary = preflight["operator_held_secret_boundary_contract"]
+ contract = preflight["signing_execution_preflight_contract"]
+ check_keys = [check["key"] for check in preflight["signing_execution_preflight_checks"]]
+ assert preflight["result"] == "DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT_READY"
+ assert preflight["summary"]["authorization_signing_execution_preflight_ready_count"] == 1
+ assert preflight["summary"]["signing_execution_preflight_check_count"] == 12
+ assert preflight["summary"]["signing_execution_preflight_pass_count"] == 12
+ assert preflight["summary"]["signing_execution_preflight_waiting_count"] == 0
+ assert preflight["summary"]["authorization_signing_issuer_closeout_ready_count"] == 1
+ assert preflight["summary"]["signing_issuer_closeout_check_count"] == 12
+ assert preflight["summary"]["final_signable_request_package_ready_count"] == 1
+ assert preflight["summary"]["operator_held_secret_boundary_count"] == 1
+ assert preflight["summary"]["signing_execution_input_requirement_count"] == 10
+ assert preflight["summary"]["signing_execution_abort_condition_count"] == 8
+ assert preflight["summary"]["rollback_boundary_count"] == 4
+ assert preflight["summary"]["post_apply_verifier_required_count"] == 1
+ assert preflight["summary"]["same_run_truth_required_count"] == 1
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert future_preflight["preflight_id"].startswith(
+ "pchome-db-apply-authorization-signing-execution-preflight-"
+ )
+ assert future_preflight["source_final_signable_request_package_id"].startswith(
+ "pchome-db-apply-authorization-signing-issuer-closeout-"
+ )
+ assert future_preflight["ready_for_future_signing_execution_preflight"] is True
+ assert future_preflight["can_enter_future_authorization_signing_execution_lane"] is True
+ assert future_preflight["ready_for_database_apply_now"] is False
+ assert future_preflight["issues_database_apply_authorization"] is False
+ assert future_preflight["signs_database_apply_authorization"] is False
+ assert package["package_id"].startswith(
+ "pchome-db-apply-authorization-signing-execution-preflight-"
+ )
+ assert package["authorization_material_type"] == "signing_execution_preflight_package"
+ assert package["ready_for_future_signing_execution_preflight"] is True
+ assert package["required_nonsecret_input_count"] == 10
+ assert all(item["secret"] is False for item in package["required_nonsecret_inputs"])
+ assert package["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert package["hash_matches"] is True
+ assert package["requires_fresh_production_truth_in_same_run"] is True
+ assert package["requires_post_apply_verifier"] is True
+ assert package["secret_material_included"] is False
+ assert package["secret_material_required_in_preview"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_shell_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert package["command_preview"]["redacts_secret_values"] is True
+ assert package["command_preview"]["executes_in_preview"] is False
+ assert package["command_preview"]["signs_database_apply_authorization"] is False
+ assert boundary["secret_reference_mode"] == "external_runtime_reference_only"
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["accepts_plaintext_secret"] is False
+ assert boundary["permits_secret_value_logging"] is False
+ assert contract["permits_future_explicit_authorization_signing_execution_lane"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "operator_held_secret_boundary_is_externalized" in check_keys
+ assert "future_command_preview_is_non_executing_and_redacted" in check_keys
+ assert "preview_has_no_side_effects_and_no_signing" in check_keys
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["signs_database_apply_authorization"] is False
+ assert preflight["safety"]["executes_script"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_execution_closeout_waits_without_ready_preflight():
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_execution_closeout(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_closeout = closeout["future_authorization_signing_execution_closeout"]
+ boundary = closeout["unsigned_signed_authorization_receipt_boundary"]
+ contract = closeout["signing_execution_closeout_contract"]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_execution_closeout"
+ )
+ assert closeout["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_PREFLIGHT"
+ assert closeout["summary"]["authorization_signing_execution_closeout_ready_count"] == 0
+ assert closeout["summary"]["signing_execution_closeout_check_count"] == 12
+ assert closeout["summary"]["signing_execution_closeout_waiting_count"] > 0
+ assert closeout["summary"]["authorization_signing_execution_preflight_ready_count"] == 0
+ assert closeout["summary"]["signing_execution_preflight_check_count"] == 12
+ assert closeout["summary"]["unsigned_signed_authorization_receipt_boundary_count"] == 1
+ assert closeout["summary"]["operator_held_secret_boundary_count"] == 1
+ assert closeout["summary"]["signing_execution_input_requirement_count"] == 10
+ assert closeout["summary"]["signing_execution_abort_condition_count"] == 8
+ assert closeout["summary"]["rollback_boundary_count"] == 4
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future_closeout["ready_for_future_signing_execution_closeout"] is False
+ assert (
+ future_closeout["can_enter_future_unsigned_signed_authorization_receipt_boundary"]
+ is False
+ )
+ assert future_closeout["ready_for_database_apply_now"] is False
+ assert future_closeout["issues_database_apply_authorization"] is False
+ assert future_closeout["signs_database_apply_authorization"] is False
+ assert future_closeout["secret_material_included"] is False
+ assert future_closeout["secret_material_required_in_preview"] is False
+ assert future_closeout["reads_secret_in_preview"] is False
+ assert boundary["authorization_material_type"] == (
+ "unsigned_signed_authorization_receipt_boundary"
+ )
+ assert boundary["ready_for_future_unsigned_signed_authorization_receipt_boundary"] is False
+ assert boundary["ready_for_future_signed_authorization_receipt_lane"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["issues_database_apply_authorization"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert boundary["signed_authorization_receipt_included"] is False
+ assert boundary["signature_material_included"] is False
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert contract["permits_future_unsigned_signed_authorization_receipt_boundary"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_execution_closeout_ready_after_fake_fetch_but_no_signing():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_signing_execution_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_closeout = closeout["future_authorization_signing_execution_closeout"]
+ boundary = closeout["unsigned_signed_authorization_receipt_boundary"]
+ contract = closeout["signing_execution_closeout_contract"]
+ check_keys = [check["key"] for check in closeout["signing_execution_closeout_checks"]]
+ assert closeout["result"] == "DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_CLOSEOUT_READY"
+ assert closeout["summary"]["authorization_signing_execution_closeout_ready_count"] == 1
+ assert closeout["summary"]["signing_execution_closeout_check_count"] == 12
+ assert closeout["summary"]["signing_execution_closeout_pass_count"] == 12
+ assert closeout["summary"]["signing_execution_closeout_waiting_count"] == 0
+ assert closeout["summary"]["authorization_signing_execution_preflight_ready_count"] == 1
+ assert closeout["summary"]["signing_execution_preflight_check_count"] == 12
+ assert closeout["summary"]["unsigned_signed_authorization_receipt_boundary_count"] == 1
+ assert closeout["summary"]["operator_held_secret_boundary_count"] == 1
+ assert closeout["summary"]["signing_execution_input_requirement_count"] == 10
+ assert closeout["summary"]["signing_execution_abort_condition_count"] == 8
+ assert closeout["summary"]["rollback_boundary_count"] == 4
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future_closeout["closeout_id"].startswith(
+ "pchome-db-apply-authorization-signing-execution-closeout-"
+ )
+ assert future_closeout["source_signing_execution_preflight_id"].startswith(
+ "pchome-db-apply-authorization-signing-execution-preflight-"
+ )
+ assert future_closeout["source_final_signable_request_package_id"].startswith(
+ "pchome-db-apply-authorization-signing-issuer-closeout-"
+ )
+ assert future_closeout["ready_for_future_signing_execution_closeout"] is True
+ assert (
+ future_closeout["can_enter_future_unsigned_signed_authorization_receipt_boundary"]
+ is True
+ )
+ assert future_closeout["ready_for_database_apply_now"] is False
+ assert future_closeout["issues_database_apply_authorization"] is False
+ assert future_closeout["signs_database_apply_authorization"] is False
+ assert boundary["boundary_id"].startswith(
+ "pchome-db-apply-authorization-signing-execution-closeout-"
+ )
+ assert boundary["authorization_material_type"] == (
+ "unsigned_signed_authorization_receipt_boundary"
+ )
+ assert boundary["ready_for_future_unsigned_signed_authorization_receipt_boundary"] is True
+ assert boundary["ready_for_future_signed_authorization_receipt_lane"] is True
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["issues_database_apply_authorization"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert boundary["signed_authorization_receipt_included"] is False
+ assert boundary["signature_material_included"] is False
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert boundary["operator_held_secret_boundary_contract"]["secret_reference_mode"] == (
+ "external_runtime_reference_only"
+ )
+ assert boundary["command_preview"]["redacts_secret_values"] is True
+ assert boundary["command_preview"]["executes_in_preview"] is False
+ assert boundary["command_preview"]["signs_database_apply_authorization"] is False
+ assert boundary["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert boundary["hash_matches"] is True
+ assert boundary["requires_fresh_production_truth_in_same_run"] is True
+ assert boundary["requires_post_apply_verifier"] is True
+ assert contract["permits_future_unsigned_signed_authorization_receipt_boundary"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "operator_held_secret_boundary_carried_forward" in check_keys
+ assert "closeout_does_not_authorize_sign_or_apply" in check_keys
+ assert "preview_has_no_side_effects_and_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_script"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signed_receipt_preflight_waits_without_ready_closeout():
+ preflight = build_pchome_auto_policy_db_apply_authorization_signed_receipt_preflight(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_preflight = preflight["future_authorization_signed_receipt_preflight"]
+ boundary = preflight["external_signing_receipt_evidence_boundary"]
+ contract = preflight["signed_receipt_preflight_contract"]
+ assert preflight["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_preflight"
+ )
+ assert preflight["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNING_EXECUTION_CLOSEOUT"
+ assert preflight["summary"]["authorization_signed_receipt_preflight_ready_count"] == 0
+ assert preflight["summary"]["signed_receipt_preflight_check_count"] == 12
+ assert preflight["summary"]["signed_receipt_preflight_waiting_count"] > 0
+ assert preflight["summary"]["authorization_signing_execution_closeout_ready_count"] == 0
+ assert preflight["summary"]["signing_execution_closeout_check_count"] == 12
+ assert preflight["summary"]["unsigned_signed_authorization_receipt_boundary_count"] == 1
+ assert preflight["summary"]["external_signing_receipt_evidence_boundary_count"] == 1
+ assert preflight["summary"]["required_external_receipt_evidence_count"] == 10
+ assert preflight["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert preflight["summary"]["operator_held_secret_boundary_count"] == 1
+ assert preflight["summary"]["signing_execution_input_requirement_count"] == 10
+ assert preflight["summary"]["signing_execution_abort_condition_count"] == 8
+ assert preflight["summary"]["rollback_boundary_count"] == 4
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert future_preflight["ready_for_future_signed_authorization_receipt_preflight"] is False
+ assert (
+ future_preflight["can_enter_future_external_signing_receipt_evidence_boundary"]
+ is False
+ )
+ assert future_preflight["ready_for_database_apply_now"] is False
+ assert future_preflight["issues_database_apply_authorization"] is False
+ assert future_preflight["signs_database_apply_authorization"] is False
+ assert future_preflight["signed_authorization_receipt_included"] is False
+ assert future_preflight["signature_material_included"] is False
+ assert future_preflight["secret_material_included"] is False
+ assert future_preflight["reads_secret_in_preview"] is False
+ assert boundary["authorization_material_type"] == "external_signing_receipt_evidence_boundary"
+ assert boundary["ready_for_future_external_signing_receipt_evidence_boundary"] is False
+ assert boundary["ready_for_future_signed_authorization_receipt_lane"] is False
+ assert boundary["required_external_receipt_evidence_count"] == 10
+ assert boundary["external_receipt_acceptance_gate_count"] == 8
+ assert boundary["external_signed_authorization_receipt_required_in_future"] is True
+ assert boundary["external_signed_authorization_receipt_included"] is False
+ assert boundary["signed_authorization_receipt_included"] is False
+ assert boundary["signature_material_included"] is False
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert contract["permits_future_external_signing_receipt_evidence_boundary"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["signs_database_apply_authorization"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signed_receipt_preflight_ready_after_fake_fetch_but_no_signed_receipt():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preflight = build_pchome_auto_policy_db_apply_authorization_signed_receipt_preflight(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_preflight = preflight["future_authorization_signed_receipt_preflight"]
+ boundary = preflight["external_signing_receipt_evidence_boundary"]
+ contract = preflight["signed_receipt_preflight_contract"]
+ check_keys = [check["key"] for check in preflight["signed_receipt_preflight_checks"]]
+ assert preflight["result"] == "DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_PREFLIGHT_READY"
+ assert preflight["summary"]["authorization_signed_receipt_preflight_ready_count"] == 1
+ assert preflight["summary"]["signed_receipt_preflight_check_count"] == 12
+ assert preflight["summary"]["signed_receipt_preflight_pass_count"] == 12
+ assert preflight["summary"]["signed_receipt_preflight_waiting_count"] == 0
+ assert preflight["summary"]["authorization_signing_execution_closeout_ready_count"] == 1
+ assert preflight["summary"]["signing_execution_closeout_check_count"] == 12
+ assert preflight["summary"]["unsigned_signed_authorization_receipt_boundary_count"] == 1
+ assert preflight["summary"]["external_signing_receipt_evidence_boundary_count"] == 1
+ assert preflight["summary"]["required_external_receipt_evidence_count"] == 10
+ assert preflight["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert preflight["summary"]["operator_held_secret_boundary_count"] == 1
+ assert preflight["summary"]["signing_execution_input_requirement_count"] == 10
+ assert preflight["summary"]["signing_execution_abort_condition_count"] == 8
+ assert preflight["summary"]["rollback_boundary_count"] == 4
+ assert preflight["summary"]["post_apply_verifier_required_count"] == 1
+ assert preflight["summary"]["same_run_truth_required_count"] == 1
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert future_preflight["preflight_id"].startswith(
+ "pchome-db-apply-authorization-signed-receipt-preflight-"
+ )
+ assert future_preflight["source_signing_execution_closeout_id"].startswith(
+ "pchome-db-apply-authorization-signing-execution-closeout-"
+ )
+ assert future_preflight["ready_for_future_signed_authorization_receipt_preflight"] is True
+ assert (
+ future_preflight["can_enter_future_external_signing_receipt_evidence_boundary"]
+ is True
+ )
+ assert future_preflight["ready_for_database_apply_now"] is False
+ assert future_preflight["issues_database_apply_authorization"] is False
+ assert future_preflight["signs_database_apply_authorization"] is False
+ assert future_preflight["signed_authorization_receipt_included"] is False
+ assert future_preflight["signature_material_included"] is False
+ assert boundary["boundary_id"].startswith(
+ "pchome-db-apply-authorization-signed-receipt-preflight-"
+ )
+ assert boundary["authorization_material_type"] == "external_signing_receipt_evidence_boundary"
+ assert boundary["ready_for_future_external_signing_receipt_evidence_boundary"] is True
+ assert boundary["ready_for_future_signed_authorization_receipt_lane"] is True
+ assert boundary["required_external_receipt_evidence_count"] == 10
+ assert boundary["external_receipt_acceptance_gate_count"] == 8
+ assert "detached_signature_verification_status" in boundary["required_external_receipt_evidence"]
+ assert boundary["external_signed_authorization_receipt_required_in_future"] is True
+ assert boundary["external_signed_authorization_receipt_included"] is False
+ assert boundary["signed_authorization_receipt_included"] is False
+ assert boundary["signature_material_included"] is False
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert boundary["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert boundary["hash_matches"] is True
+ assert boundary["requires_fresh_production_truth_in_same_run"] is True
+ assert boundary["requires_post_apply_verifier"] is True
+ assert contract["permits_future_external_signing_receipt_evidence_boundary"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "external_receipt_evidence_contract_complete" in check_keys
+ assert "preflight_has_no_signed_receipt_signature_or_authorization" in check_keys
+ assert "preview_has_no_side_effects_and_no_signing" in check_keys
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["signs_database_apply_authorization"] is False
+ assert preflight["safety"]["executes_script"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signed_receipt_closeout_waits_without_ready_preflight():
+ closeout = build_pchome_auto_policy_db_apply_authorization_signed_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_closeout = closeout["future_authorization_signed_receipt_closeout"]
+ boundary = closeout["detached_receipt_verification_boundary"]
+ contract = closeout["signed_receipt_closeout_contract"]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_closeout"
+ )
+ assert closeout["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_PREFLIGHT"
+ assert closeout["summary"]["authorization_signed_receipt_closeout_ready_count"] == 0
+ assert closeout["summary"]["signed_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["signed_receipt_closeout_waiting_count"] > 0
+ assert closeout["summary"]["authorization_signed_receipt_preflight_ready_count"] == 0
+ assert closeout["summary"]["signed_receipt_preflight_check_count"] == 12
+ assert closeout["summary"]["external_signing_receipt_evidence_boundary_count"] == 1
+ assert closeout["summary"]["detached_receipt_verification_boundary_count"] == 1
+ assert closeout["summary"]["required_external_receipt_evidence_count"] == 10
+ assert closeout["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert closeout["summary"]["detached_receipt_verification_check_count"] == 10
+ assert closeout["summary"]["operator_held_secret_boundary_count"] == 1
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future_closeout["ready_for_future_signed_authorization_receipt_closeout"] is False
+ assert future_closeout["can_enter_future_detached_receipt_verification_boundary"] is False
+ assert future_closeout["ready_for_database_apply_now"] is False
+ assert future_closeout["issues_database_apply_authorization"] is False
+ assert future_closeout["signs_database_apply_authorization"] is False
+ assert future_closeout["external_signed_authorization_receipt_included"] is False
+ assert future_closeout["signed_authorization_receipt_included"] is False
+ assert future_closeout["signature_material_included"] is False
+ assert future_closeout["secret_material_included"] is False
+ assert boundary["authorization_material_type"] == "detached_receipt_verification_boundary"
+ assert boundary["ready_for_future_detached_receipt_verification_boundary"] is False
+ assert boundary["ready_for_future_signed_authorization_receipt_verification_lane"] is False
+ assert boundary["detached_receipt_verification_check_count"] == 10
+ assert boundary["requires_detached_signature_verification"] is True
+ assert boundary["detached_signature_verification_performed"] is False
+ assert boundary["external_signed_authorization_receipt_included"] is False
+ assert boundary["signed_authorization_receipt_included"] is False
+ assert boundary["signature_material_included"] is False
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert contract["permits_future_detached_receipt_verification_boundary"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signed_receipt_closeout_ready_after_fake_fetch_but_no_signed_receipt():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_signed_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_closeout = closeout["future_authorization_signed_receipt_closeout"]
+ boundary = closeout["detached_receipt_verification_boundary"]
+ contract = closeout["signed_receipt_closeout_contract"]
+ check_keys = [check["key"] for check in closeout["signed_receipt_closeout_checks"]]
+ assert closeout["result"] == "DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_CLOSEOUT_READY"
+ assert closeout["summary"]["authorization_signed_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["signed_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["signed_receipt_closeout_pass_count"] == 12
+ assert closeout["summary"]["signed_receipt_closeout_waiting_count"] == 0
+ assert closeout["summary"]["authorization_signed_receipt_preflight_ready_count"] == 1
+ assert closeout["summary"]["signed_receipt_preflight_check_count"] == 12
+ assert closeout["summary"]["external_signing_receipt_evidence_boundary_count"] == 1
+ assert closeout["summary"]["detached_receipt_verification_boundary_count"] == 1
+ assert closeout["summary"]["required_external_receipt_evidence_count"] == 10
+ assert closeout["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert closeout["summary"]["detached_receipt_verification_check_count"] == 10
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future_closeout["closeout_id"].startswith(
+ "pchome-db-apply-authorization-signed-receipt-closeout-"
+ )
+ assert future_closeout["source_signed_receipt_preflight_id"].startswith(
+ "pchome-db-apply-authorization-signed-receipt-preflight-"
+ )
+ assert future_closeout["ready_for_future_signed_authorization_receipt_closeout"] is True
+ assert future_closeout["can_enter_future_detached_receipt_verification_boundary"] is True
+ assert future_closeout["ready_for_database_apply_now"] is False
+ assert future_closeout["issues_database_apply_authorization"] is False
+ assert future_closeout["signs_database_apply_authorization"] is False
+ assert future_closeout["external_signed_authorization_receipt_included"] is False
+ assert future_closeout["signed_authorization_receipt_included"] is False
+ assert future_closeout["signature_material_included"] is False
+ assert boundary["boundary_id"].startswith(
+ "pchome-db-apply-authorization-signed-receipt-closeout-"
+ )
+ assert boundary["authorization_material_type"] == "detached_receipt_verification_boundary"
+ assert boundary["ready_for_future_detached_receipt_verification_boundary"] is True
+ assert boundary["ready_for_future_signed_authorization_receipt_verification_lane"] is True
+ assert boundary["required_external_receipt_evidence_count"] == 10
+ assert boundary["external_receipt_acceptance_gate_count"] == 8
+ assert boundary["detached_receipt_verification_check_count"] == 10
+ assert "detached_signature_verification_status_passed" in (
+ boundary["detached_receipt_verification_checks"]
+ )
+ assert boundary["requires_detached_signature_verification"] is True
+ assert boundary["detached_signature_verification_performed"] is False
+ assert boundary["external_signed_authorization_receipt_included"] is False
+ assert boundary["signed_authorization_receipt_included"] is False
+ assert boundary["signature_material_included"] is False
+ assert boundary["secret_material_included"] is False
+ assert boundary["secret_material_required_in_preview"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert boundary["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert boundary["hash_matches"] is True
+ assert boundary["requires_fresh_production_truth_in_same_run"] is True
+ assert boundary["requires_post_apply_verifier"] is True
+ assert contract["permits_future_detached_receipt_verification_boundary"] is True
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "detached_receipt_verification_boundary_contract_complete" in check_keys
+ assert "closeout_has_no_signed_receipt_signature_or_authorization" in check_keys
+ assert "preview_has_no_side_effects_and_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_script"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signed_receipt_evidence_intake_waits_without_ready_closeout():
+ intake = build_pchome_auto_policy_db_apply_authorization_signed_receipt_evidence_intake(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_intake = intake["future_signed_authorization_receipt_evidence_intake"]
+ schema = intake["detached_verification_evidence_schema"]
+ contract = intake["signed_receipt_evidence_intake_contract"]
+ assert intake["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_evidence_intake"
+ )
+ assert intake["result"] == "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_CLOSEOUT"
+ assert intake["summary"]["authorization_signed_receipt_evidence_intake_ready_count"] == 0
+ assert intake["summary"]["signed_receipt_evidence_intake_check_count"] == 12
+ assert intake["summary"]["signed_receipt_evidence_intake_waiting_count"] > 0
+ assert intake["summary"]["authorization_signed_receipt_closeout_ready_count"] == 0
+ assert intake["summary"]["signed_receipt_closeout_check_count"] == 12
+ assert intake["summary"]["detached_receipt_verification_boundary_count"] == 1
+ assert intake["summary"]["detached_verification_evidence_schema_count"] == 1
+ assert intake["summary"]["detached_verification_evidence_field_count"] == 12
+ assert intake["summary"]["detached_verification_acceptance_gate_count"] == 10
+ assert intake["summary"]["signs_database_apply_authorization_count"] == 0
+ assert intake["summary"]["reads_secret_count"] == 0
+ assert intake["summary"]["executes_script_count"] == 0
+ assert intake["summary"]["executes_sql_count"] == 0
+ assert intake["summary"]["writes_database_count"] == 0
+ assert (
+ future_intake["ready_for_future_signed_authorization_receipt_evidence_intake"]
+ is False
+ )
+ assert future_intake["can_enter_future_detached_verification_evidence_validation"] is False
+ assert future_intake["external_signed_authorization_receipt_evidence_schema_ready"] is False
+ assert future_intake["ready_for_database_apply_now"] is False
+ assert future_intake["issues_database_apply_authorization"] is False
+ assert future_intake["signs_database_apply_authorization"] is False
+ assert future_intake["detached_signature_verification_performed"] is False
+ assert future_intake["external_signed_authorization_receipt_included"] is False
+ assert future_intake["signed_authorization_receipt_included"] is False
+ assert future_intake["signature_material_included"] is False
+ assert future_intake["secret_material_included"] is False
+ assert schema["authorization_material_type"] == "detached_verification_evidence_schema"
+ assert schema["ready_for_future_detached_verification_evidence_schema"] is False
+ assert schema["detached_verification_evidence_field_count"] == 12
+ assert schema["detached_verification_acceptance_gate_count"] == 10
+ assert schema["requires_detached_signature_verification"] is True
+ assert schema["detached_signature_verification_performed"] is False
+ assert schema["external_signed_authorization_receipt_required_in_future"] is True
+ assert schema["external_signed_authorization_receipt_included"] is False
+ assert schema["signed_authorization_receipt_included"] is False
+ assert schema["signature_material_included"] is False
+ assert schema["secret_material_included"] is False
+ assert schema["accepts_plaintext_secret"] is False
+ assert schema["reads_secret_in_preview"] is False
+ assert schema["executes_shell_in_preview"] is False
+ assert schema["executes_sql_in_preview"] is False
+ assert schema["writes_database_in_preview"] is False
+ assert schema["ready_for_database_apply_now"] is False
+ assert schema["signs_database_apply_authorization"] is False
+ assert contract["permits_future_detached_verification_evidence_validation"] is False
+ assert contract["accepts_plaintext_secret"] is False
+ assert contract["detached_signature_verification_performed"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert intake["safety"]["reads_secret_in_preview"] is False
+ assert intake["safety"]["performs_detached_signature_verification"] is False
+ assert intake["safety"]["signs_database_apply_authorization"] is False
+ assert intake["safety"]["executes_sql"] is False
+ assert intake["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signed_receipt_evidence_intake_ready_after_fake_fetch_but_no_signed_receipt():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ intake = build_pchome_auto_policy_db_apply_authorization_signed_receipt_evidence_intake(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_intake = intake["future_signed_authorization_receipt_evidence_intake"]
+ schema = intake["detached_verification_evidence_schema"]
+ contract = intake["signed_receipt_evidence_intake_contract"]
+ check_keys = [check["key"] for check in intake["signed_receipt_evidence_intake_checks"]]
+ assert intake["result"] == "DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_EVIDENCE_INTAKE_READY"
+ assert intake["summary"]["authorization_signed_receipt_evidence_intake_ready_count"] == 1
+ assert intake["summary"]["signed_receipt_evidence_intake_check_count"] == 12
+ assert intake["summary"]["signed_receipt_evidence_intake_pass_count"] == 12
+ assert intake["summary"]["signed_receipt_evidence_intake_waiting_count"] == 0
+ assert intake["summary"]["authorization_signed_receipt_closeout_ready_count"] == 1
+ assert intake["summary"]["signed_receipt_closeout_check_count"] == 12
+ assert intake["summary"]["detached_receipt_verification_boundary_count"] == 1
+ assert intake["summary"]["detached_verification_evidence_schema_count"] == 1
+ assert intake["summary"]["required_external_receipt_evidence_count"] == 10
+ assert intake["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert intake["summary"]["detached_receipt_verification_check_count"] == 10
+ assert intake["summary"]["detached_verification_evidence_field_count"] == 12
+ assert intake["summary"]["detached_verification_acceptance_gate_count"] == 10
+ assert intake["summary"]["post_apply_verifier_required_count"] == 1
+ assert intake["summary"]["same_run_truth_required_count"] == 1
+ assert intake["summary"]["signs_database_apply_authorization_count"] == 0
+ assert intake["summary"]["reads_secret_count"] == 0
+ assert intake["summary"]["executes_script_count"] == 0
+ assert intake["summary"]["executes_sql_count"] == 0
+ assert intake["summary"]["writes_database_count"] == 0
+ assert future_intake["intake_id"].startswith(
+ "pchome-db-apply-authorization-signed-receipt-evidence-intake-"
+ )
+ assert future_intake["source_signed_receipt_closeout_id"].startswith(
+ "pchome-db-apply-authorization-signed-receipt-closeout-"
+ )
+ assert (
+ future_intake["ready_for_future_signed_authorization_receipt_evidence_intake"]
+ is True
+ )
+ assert future_intake["can_enter_future_detached_verification_evidence_validation"] is True
+ assert future_intake["external_signed_authorization_receipt_evidence_schema_ready"] is True
+ assert future_intake["ready_for_database_apply_now"] is False
+ assert future_intake["issues_database_apply_authorization"] is False
+ assert future_intake["signs_database_apply_authorization"] is False
+ assert future_intake["detached_signature_verification_performed"] is False
+ assert future_intake["external_signed_authorization_receipt_included"] is False
+ assert future_intake["signed_authorization_receipt_included"] is False
+ assert future_intake["signature_material_included"] is False
+ assert future_intake["secret_material_included"] is False
+ assert schema["schema_id"].startswith(
+ "pchome-db-apply-authorization-signed-receipt-evidence-intake-"
+ )
+ assert schema["authorization_material_type"] == "detached_verification_evidence_schema"
+ assert schema["ready_for_future_detached_verification_evidence_schema"] is True
+ assert schema["required_external_receipt_evidence_count"] == 10
+ assert schema["external_receipt_acceptance_gate_count"] == 8
+ assert schema["detached_receipt_verification_check_count"] == 10
+ assert schema["detached_verification_evidence_field_count"] == 12
+ assert schema["detached_verification_acceptance_gate_count"] == 10
+ assert "verifier_receipt_sha256" in schema["detached_verification_evidence_fields"]
+ assert "detached_signature_verification_status_is_passed" in (
+ schema["detached_verification_acceptance_gates"]
+ )
+ assert schema["requires_detached_signature_verification"] is True
+ assert schema["detached_signature_verification_performed"] is False
+ assert schema["external_signed_authorization_receipt_required_in_future"] is True
+ assert schema["external_signed_authorization_receipt_included"] is False
+ assert schema["signed_authorization_receipt_included"] is False
+ assert schema["signature_material_included"] is False
+ assert schema["secret_material_included"] is False
+ assert schema["accepts_plaintext_secret"] is False
+ assert schema["reads_secret_in_preview"] is False
+ assert schema["executes_shell_in_preview"] is False
+ assert schema["executes_sql_in_preview"] is False
+ assert schema["writes_database_in_preview"] is False
+ assert schema["ready_for_database_apply_now"] is False
+ assert schema["signs_database_apply_authorization"] is False
+ assert schema["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert schema["hash_matches"] is True
+ assert schema["requires_fresh_production_truth_in_same_run"] is True
+ assert schema["requires_post_apply_verifier"] is True
+ assert contract["permits_future_detached_verification_evidence_validation"] is True
+ assert contract["accepts_plaintext_secret"] is False
+ assert contract["detached_signature_verification_performed"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "detached_verification_evidence_schema_complete" in check_keys
+ assert "no_signed_receipt_signature_secret_or_verification_execution" in check_keys
+ assert "preview_has_no_side_effects_and_no_signing" in check_keys
+ assert intake["safety"]["reads_secret_in_preview"] is False
+ assert intake["safety"]["performs_detached_signature_verification"] is False
+ assert intake["safety"]["signs_database_apply_authorization"] is False
+ assert intake["safety"]["executes_script"] is False
+ assert intake["safety"]["executes_sql"] is False
+ assert intake["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_detached_verification_evidence_validation_waits_without_ready_intake():
+ validation = (
+ build_pchome_auto_policy_db_apply_authorization_detached_verification_evidence_validation(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future_validation = validation["future_detached_verification_evidence_validation"]
+ boundary = validation["verifier_receipt_closeout_boundary"]
+ contract = validation["detached_verification_evidence_validation_contract"]
+ assert validation["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_detached_verification_evidence_validation"
+ )
+ assert (
+ validation["result"]
+ == "WAITING_FOR_DB_APPLY_AUTHORIZATION_SIGNED_RECEIPT_EVIDENCE_INTAKE"
+ )
+ assert (
+ validation["summary"][
+ "authorization_detached_verification_evidence_validation_ready_count"
+ ]
+ == 0
+ )
+ assert validation["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert validation["summary"]["detached_verification_evidence_validation_waiting_count"] > 0
+ assert validation["summary"]["authorization_signed_receipt_evidence_intake_ready_count"] == 0
+ assert validation["summary"]["signed_receipt_evidence_intake_check_count"] == 12
+ assert validation["summary"]["detached_verification_evidence_schema_count"] == 1
+ assert validation["summary"]["verifier_receipt_closeout_boundary_count"] == 1
+ assert validation["summary"]["detached_verification_evidence_field_count"] == 12
+ assert validation["summary"]["detached_verification_acceptance_gate_count"] == 10
+ assert validation["summary"]["verifier_receipt_field_count"] == 12
+ assert validation["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert validation["summary"]["signs_database_apply_authorization_count"] == 0
+ assert validation["summary"]["reads_secret_count"] == 0
+ assert validation["summary"]["executes_script_count"] == 0
+ assert validation["summary"]["executes_sql_count"] == 0
+ assert validation["summary"]["writes_database_count"] == 0
+ assert (
+ future_validation["ready_for_future_detached_verification_evidence_validation"]
+ is False
+ )
+ assert future_validation["can_enter_future_verifier_receipt_closeout"] is False
+ assert future_validation["verifier_receipt_closeout_boundary_ready"] is False
+ assert future_validation["ready_for_database_apply_now"] is False
+ assert future_validation["issues_database_apply_authorization"] is False
+ assert future_validation["signs_database_apply_authorization"] is False
+ assert future_validation["detached_signature_verification_performed"] is False
+ assert future_validation["verifier_receipt_persisted"] is False
+ assert future_validation["external_signed_authorization_receipt_included"] is False
+ assert future_validation["signed_authorization_receipt_included"] is False
+ assert future_validation["signature_material_included"] is False
+ assert future_validation["secret_material_included"] is False
+ assert boundary["authorization_material_type"] == "verifier_receipt_closeout_boundary"
+ assert boundary["ready_for_future_verifier_receipt_closeout_boundary"] is False
+ assert boundary["verifier_receipt_field_count"] == 12
+ assert boundary["verifier_receipt_acceptance_gate_count"] == 10
+ assert boundary["requires_detached_signature_verification"] is True
+ assert boundary["detached_signature_verification_performed"] is False
+ assert boundary["verifier_receipt_persisted"] is False
+ assert boundary["external_signed_authorization_receipt_included"] is False
+ assert boundary["signed_authorization_receipt_included"] is False
+ assert boundary["signature_material_included"] is False
+ assert boundary["secret_material_included"] is False
+ assert boundary["accepts_plaintext_secret"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert contract["permits_future_verifier_receipt_closeout"] is False
+ assert contract["accepts_plaintext_secret"] is False
+ assert contract["performs_detached_signature_verification"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert validation["safety"]["reads_secret_in_preview"] is False
+ assert validation["safety"]["performs_detached_signature_verification"] is False
+ assert validation["safety"]["persists_verifier_receipt"] is False
+ assert validation["safety"]["signs_database_apply_authorization"] is False
+ assert validation["safety"]["executes_sql"] is False
+ assert validation["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_detached_verification_evidence_validation_ready_after_fake_fetch_but_no_verification():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ validation = (
+ build_pchome_auto_policy_db_apply_authorization_detached_verification_evidence_validation(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future_validation = validation["future_detached_verification_evidence_validation"]
+ boundary = validation["verifier_receipt_closeout_boundary"]
+ contract = validation["detached_verification_evidence_validation_contract"]
+ check_keys = [
+ check["key"]
+ for check in validation["detached_verification_evidence_validation_checks"]
+ ]
+ assert (
+ validation["result"]
+ == "DB_APPLY_AUTHORIZATION_DETACHED_VERIFICATION_EVIDENCE_VALIDATION_READY"
+ )
+ assert (
+ validation["summary"][
+ "authorization_detached_verification_evidence_validation_ready_count"
+ ]
+ == 1
+ )
+ assert validation["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert validation["summary"]["detached_verification_evidence_validation_pass_count"] == 12
+ assert validation["summary"]["detached_verification_evidence_validation_waiting_count"] == 0
+ assert validation["summary"]["authorization_signed_receipt_evidence_intake_ready_count"] == 1
+ assert validation["summary"]["signed_receipt_evidence_intake_check_count"] == 12
+ assert validation["summary"]["detached_verification_evidence_schema_count"] == 1
+ assert validation["summary"]["verifier_receipt_closeout_boundary_count"] == 1
+ assert validation["summary"]["required_external_receipt_evidence_count"] == 10
+ assert validation["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert validation["summary"]["detached_receipt_verification_check_count"] == 10
+ assert validation["summary"]["detached_verification_evidence_field_count"] == 12
+ assert validation["summary"]["detached_verification_acceptance_gate_count"] == 10
+ assert validation["summary"]["verifier_receipt_field_count"] == 12
+ assert validation["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert validation["summary"]["post_apply_verifier_required_count"] == 1
+ assert validation["summary"]["same_run_truth_required_count"] == 1
+ assert validation["summary"]["signs_database_apply_authorization_count"] == 0
+ assert validation["summary"]["reads_secret_count"] == 0
+ assert validation["summary"]["executes_script_count"] == 0
+ assert validation["summary"]["executes_sql_count"] == 0
+ assert validation["summary"]["writes_database_count"] == 0
+ assert future_validation["validation_id"].startswith(
+ "pchome-db-apply-authorization-detached-verification-evidence-validation-"
+ )
+ assert future_validation["source_signed_receipt_evidence_intake_id"].startswith(
+ "pchome-db-apply-authorization-signed-receipt-evidence-intake-"
+ )
+ assert (
+ future_validation["ready_for_future_detached_verification_evidence_validation"]
+ is True
+ )
+ assert future_validation["can_enter_future_verifier_receipt_closeout"] is True
+ assert future_validation["verifier_receipt_closeout_boundary_ready"] is True
+ assert future_validation["ready_for_database_apply_now"] is False
+ assert future_validation["issues_database_apply_authorization"] is False
+ assert future_validation["signs_database_apply_authorization"] is False
+ assert future_validation["detached_signature_verification_performed"] is False
+ assert future_validation["verifier_receipt_persisted"] is False
+ assert future_validation["external_signed_authorization_receipt_included"] is False
+ assert future_validation["signed_authorization_receipt_included"] is False
+ assert future_validation["signature_material_included"] is False
+ assert future_validation["secret_material_included"] is False
+ assert boundary["boundary_id"].startswith(
+ "pchome-db-apply-authorization-detached-verification-evidence-validation-"
+ )
+ assert boundary["authorization_material_type"] == "verifier_receipt_closeout_boundary"
+ assert boundary["ready_for_future_verifier_receipt_closeout_boundary"] is True
+ assert boundary["required_external_receipt_evidence_count"] == 10
+ assert boundary["external_receipt_acceptance_gate_count"] == 8
+ assert boundary["detached_receipt_verification_check_count"] == 10
+ assert boundary["detached_verification_evidence_field_count"] == 12
+ assert boundary["detached_verification_acceptance_gate_count"] == 10
+ assert boundary["verifier_receipt_field_count"] == 12
+ assert boundary["verifier_receipt_acceptance_gate_count"] == 10
+ assert "verifier_receipt_sha256" in boundary["verifier_receipt_fields"]
+ assert "detached_signature_verification_status_passed" in (
+ boundary["verifier_receipt_acceptance_gates"]
+ )
+ assert boundary["requires_detached_signature_verification"] is True
+ assert boundary["detached_signature_verification_performed"] is False
+ assert boundary["verifier_receipt_persisted"] is False
+ assert boundary["external_signed_authorization_receipt_required_in_future"] is True
+ assert boundary["external_signed_authorization_receipt_included"] is False
+ assert boundary["signed_authorization_receipt_included"] is False
+ assert boundary["signature_material_included"] is False
+ assert boundary["secret_material_included"] is False
+ assert boundary["accepts_plaintext_secret"] is False
+ assert boundary["reads_secret_in_preview"] is False
+ assert boundary["executes_shell_in_preview"] is False
+ assert boundary["executes_sql_in_preview"] is False
+ assert boundary["writes_database_in_preview"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert boundary["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert boundary["hash_matches"] is True
+ assert boundary["requires_fresh_production_truth_in_same_run"] is True
+ assert boundary["requires_post_apply_verifier"] is True
+ assert contract["permits_future_verifier_receipt_closeout"] is True
+ assert contract["accepts_plaintext_secret"] is False
+ assert contract["performs_detached_signature_verification"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "verifier_receipt_closeout_boundary_contract_complete" in check_keys
+ assert "secret_and_signed_material_boundary_enforced" in check_keys
+ assert "preview_has_no_side_effects_no_verification_no_signing" in check_keys
+ assert validation["safety"]["reads_secret_in_preview"] is False
+ assert validation["safety"]["performs_detached_signature_verification"] is False
+ assert validation["safety"]["persists_verifier_receipt"] is False
+ assert validation["safety"]["signs_database_apply_authorization"] is False
+ assert validation["safety"]["executes_script"] is False
+ assert validation["safety"]["executes_sql"] is False
+ assert validation["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_verifier_receipt_closeout_waits_without_ready_validation():
+ closeout = build_pchome_auto_policy_db_apply_authorization_verifier_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_closeout = closeout["future_verifier_receipt_closeout"]
+ handoff = closeout["verifier_receipt_evidence_handoff"]
+ contract = closeout["verifier_receipt_closeout_contract"]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_verifier_receipt_closeout"
+ )
+ assert (
+ closeout["result"]
+ == "WAITING_FOR_DB_APPLY_AUTHORIZATION_DETACHED_VERIFICATION_EVIDENCE_VALIDATION"
+ )
+ assert closeout["summary"]["authorization_verifier_receipt_closeout_ready_count"] == 0
+ assert closeout["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["verifier_receipt_closeout_waiting_count"] > 0
+ assert (
+ closeout["summary"][
+ "authorization_detached_verification_evidence_validation_ready_count"
+ ]
+ == 0
+ )
+ assert closeout["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert closeout["summary"]["verifier_receipt_closeout_boundary_count"] == 1
+ assert closeout["summary"]["verifier_receipt_evidence_handoff_count"] == 1
+ assert closeout["summary"]["verifier_receipt_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert closeout["summary"]["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future_closeout["ready_for_future_verifier_receipt_closeout"] is False
+ assert (
+ future_closeout[
+ "can_enter_future_database_apply_authorization_verifier_handoff"
+ ]
+ is False
+ )
+ assert future_closeout["verifier_receipt_evidence_handoff_ready"] is False
+ assert future_closeout["ready_for_database_apply_now"] is False
+ assert future_closeout["issues_database_apply_authorization"] is False
+ assert future_closeout["signs_database_apply_authorization"] is False
+ assert future_closeout["detached_signature_verification_performed"] is False
+ assert future_closeout["verifier_receipt_persisted"] is False
+ assert future_closeout["external_signed_authorization_receipt_included"] is False
+ assert future_closeout["signed_authorization_receipt_included"] is False
+ assert future_closeout["signature_material_included"] is False
+ assert future_closeout["secret_material_included"] is False
+ assert handoff["authorization_material_type"] == "verifier_receipt_evidence_handoff"
+ assert handoff["ready_for_future_verifier_receipt_evidence_handoff"] is False
+ assert handoff["verifier_receipt_field_count"] == 12
+ assert handoff["verifier_receipt_acceptance_gate_count"] == 10
+ assert handoff["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert handoff["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert handoff["requires_detached_signature_verification"] is True
+ assert handoff["detached_signature_verification_performed"] is False
+ assert handoff["verifier_receipt_persisted"] is False
+ assert handoff["external_signed_authorization_receipt_included"] is False
+ assert handoff["signed_authorization_receipt_included"] is False
+ assert handoff["signature_material_included"] is False
+ assert handoff["secret_material_included"] is False
+ assert handoff["accepts_plaintext_secret"] is False
+ assert handoff["reads_secret_in_preview"] is False
+ assert handoff["executes_shell_in_preview"] is False
+ assert handoff["executes_sql_in_preview"] is False
+ assert handoff["writes_database_in_preview"] is False
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["signs_database_apply_authorization"] is False
+ assert contract["permits_future_database_apply_authorization_verifier_handoff"] is False
+ assert contract["accepts_plaintext_secret"] is False
+ assert contract["performs_detached_signature_verification"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["performs_detached_signature_verification"] is False
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_verifier_receipt_closeout_ready_after_fake_fetch_but_no_receipt_persist():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_verifier_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_closeout = closeout["future_verifier_receipt_closeout"]
+ handoff = closeout["verifier_receipt_evidence_handoff"]
+ contract = closeout["verifier_receipt_closeout_contract"]
+ check_keys = [check["key"] for check in closeout["verifier_receipt_closeout_checks"]]
+ assert closeout["result"] == "DB_APPLY_AUTHORIZATION_VERIFIER_RECEIPT_CLOSEOUT_READY"
+ assert closeout["summary"]["authorization_verifier_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["verifier_receipt_closeout_pass_count"] == 12
+ assert closeout["summary"]["verifier_receipt_closeout_waiting_count"] == 0
+ assert (
+ closeout["summary"][
+ "authorization_detached_verification_evidence_validation_ready_count"
+ ]
+ == 1
+ )
+ assert closeout["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert closeout["summary"]["authorization_signed_receipt_evidence_intake_ready_count"] == 1
+ assert closeout["summary"]["signed_receipt_evidence_intake_check_count"] == 12
+ assert closeout["summary"]["verifier_receipt_closeout_boundary_count"] == 1
+ assert closeout["summary"]["verifier_receipt_evidence_handoff_count"] == 1
+ assert closeout["summary"]["required_external_receipt_evidence_count"] == 10
+ assert closeout["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert closeout["summary"]["verifier_receipt_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert closeout["summary"]["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert closeout["summary"]["detached_verification_evidence_field_count"] == 12
+ assert closeout["summary"]["detached_verification_acceptance_gate_count"] == 10
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future_closeout["closeout_id"].startswith(
+ "pchome-db-apply-authorization-verifier-receipt-closeout-"
+ )
+ assert future_closeout["source_detached_verification_evidence_validation_id"].startswith(
+ "pchome-db-apply-authorization-detached-verification-evidence-validation-"
+ )
+ assert future_closeout["ready_for_future_verifier_receipt_closeout"] is True
+ assert (
+ future_closeout[
+ "can_enter_future_database_apply_authorization_verifier_handoff"
+ ]
+ is True
+ )
+ assert future_closeout["verifier_receipt_evidence_handoff_ready"] is True
+ assert future_closeout["ready_for_database_apply_now"] is False
+ assert future_closeout["issues_database_apply_authorization"] is False
+ assert future_closeout["signs_database_apply_authorization"] is False
+ assert future_closeout["detached_signature_verification_performed"] is False
+ assert future_closeout["verifier_receipt_persisted"] is False
+ assert future_closeout["external_signed_authorization_receipt_included"] is False
+ assert future_closeout["signed_authorization_receipt_included"] is False
+ assert future_closeout["signature_material_included"] is False
+ assert future_closeout["secret_material_included"] is False
+ assert handoff["handoff_id"].startswith(
+ "pchome-db-apply-authorization-verifier-receipt-closeout-"
+ )
+ assert handoff["authorization_material_type"] == "verifier_receipt_evidence_handoff"
+ assert handoff["ready_for_future_verifier_receipt_evidence_handoff"] is True
+ assert handoff["required_external_receipt_evidence_count"] == 10
+ assert handoff["external_receipt_acceptance_gate_count"] == 8
+ assert handoff["verifier_receipt_field_count"] == 12
+ assert handoff["verifier_receipt_acceptance_gate_count"] == 10
+ assert handoff["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert handoff["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert "verifier_receipt_sha256" in handoff["verifier_receipt_evidence_handoff_fields"]
+ assert "verifier_receipt_not_persisted_by_preview" in (
+ handoff["verifier_receipt_handoff_acceptance_gates"]
+ )
+ assert handoff["requires_detached_signature_verification"] is True
+ assert handoff["detached_signature_verification_performed"] is False
+ assert handoff["verifier_receipt_persisted"] is False
+ assert handoff["external_signed_authorization_receipt_required_in_future"] is True
+ assert handoff["external_signed_authorization_receipt_included"] is False
+ assert handoff["signed_authorization_receipt_included"] is False
+ assert handoff["signature_material_included"] is False
+ assert handoff["secret_material_included"] is False
+ assert handoff["accepts_plaintext_secret"] is False
+ assert handoff["reads_secret_in_preview"] is False
+ assert handoff["executes_shell_in_preview"] is False
+ assert handoff["executes_sql_in_preview"] is False
+ assert handoff["writes_database_in_preview"] is False
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["signs_database_apply_authorization"] is False
+ assert handoff["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert handoff["hash_matches"] is True
+ assert handoff["requires_fresh_production_truth_in_same_run"] is True
+ assert handoff["requires_post_apply_verifier"] is True
+ assert contract["permits_future_database_apply_authorization_verifier_handoff"] is True
+ assert contract["accepts_plaintext_secret"] is False
+ assert contract["performs_detached_signature_verification"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["issues_database_apply_authorization"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "verifier_receipt_evidence_handoff_contract_complete" in check_keys
+ assert "secret_signed_material_and_receipt_persistence_boundary_enforced" in check_keys
+ assert "preview_has_no_side_effects_no_verification_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["performs_detached_signature_verification"] is False
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_script"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_evidence_execution_preflight_waits_without_ready_closeout():
+ preflight = build_pchome_auto_policy_db_apply_authorization_evidence_execution_preflight(
+ _payload(),
+ batch_size=1,
+ )
+
+ handoff = preflight["future_database_apply_authorization_verifier_handoff"]
+ package = preflight["authorization_evidence_execution_preflight"]
+ contract = preflight["authorization_evidence_execution_preflight_contract"]
+ check_keys = [
+ check["key"]
+ for check in preflight["authorization_evidence_execution_preflight_checks"]
+ ]
+ assert preflight["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_preflight"
+ )
+ assert (
+ preflight["result"]
+ == "WAITING_FOR_DB_APPLY_AUTHORIZATION_VERIFIER_RECEIPT_CLOSEOUT"
+ )
+ assert preflight["summary"]["authorization_evidence_execution_preflight_ready_count"] == 0
+ assert preflight["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert preflight["summary"]["authorization_evidence_execution_preflight_waiting_count"] > 0
+ assert preflight["summary"]["authorization_verifier_receipt_closeout_ready_count"] == 0
+ assert preflight["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert (
+ preflight["summary"][
+ "authorization_detached_verification_evidence_validation_ready_count"
+ ]
+ == 0
+ )
+ assert preflight["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert preflight["summary"]["verifier_receipt_evidence_handoff_count"] == 1
+ assert preflight["summary"]["authorization_evidence_execution_preflight_count"] == 1
+ assert preflight["summary"]["authorization_evidence_execution_field_count"] == 12
+ assert preflight["summary"]["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert preflight["summary"]["verifier_receipt_field_count"] == 12
+ assert preflight["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert preflight["summary"]["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert preflight["summary"]["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_migration_count"] == 0
+ assert preflight["summary"]["executes_endpoint_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert handoff["preflight_id"].startswith(
+ "pchome-db-apply-authorization-evidence-execution-preflight-"
+ )
+ assert (
+ handoff["ready_for_future_database_apply_authorization_verifier_handoff"]
+ is False
+ )
+ assert (
+ handoff["can_enter_future_authorization_evidence_execution_closeout"]
+ is False
+ )
+ assert handoff["authorization_evidence_execution_preflight_ready"] is False
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["issues_database_apply_authorization"] is False
+ assert handoff["signs_database_apply_authorization"] is False
+ assert handoff["executes_authorization_evidence"] is False
+ assert handoff["detached_signature_verification_performed"] is False
+ assert handoff["verifier_receipt_persisted"] is False
+ assert handoff["external_signed_authorization_receipt_included"] is False
+ assert handoff["signed_authorization_receipt_included"] is False
+ assert handoff["signature_material_included"] is False
+ assert handoff["secret_material_included"] is False
+ assert package["authorization_material_type"] == (
+ "authorization_evidence_execution_preflight"
+ )
+ assert package["ready_for_future_authorization_evidence_execution_preflight"] is False
+ assert package["authorization_evidence_execution_field_count"] == 12
+ assert package["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert package["verifier_receipt_field_count"] == 12
+ assert package["verifier_receipt_acceptance_gate_count"] == 10
+ assert package["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert package["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert package["requires_detached_signature_verification"] is True
+ assert package["detached_signature_verification_performed"] is False
+ assert package["verifier_receipt_persisted"] is False
+ assert package["external_signed_authorization_receipt_included"] is False
+ assert package["signed_authorization_receipt_included"] is False
+ assert package["signature_material_included"] is False
+ assert package["secret_material_included"] is False
+ assert package["accepts_plaintext_secret"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_shell_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert package["executes_authorization_evidence"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert contract["permits_future_authorization_evidence_execution_closeout"] is False
+ assert contract["accepts_plaintext_secret"] is False
+ assert contract["performs_detached_signature_verification"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "verifier_receipt_closeout_ready" in check_keys
+ assert "authorization_evidence_execution_preflight_contract_complete" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["performs_detached_signature_verification"] is False
+ assert preflight["safety"]["persists_verifier_receipt"] is False
+ assert preflight["safety"]["executes_authorization_evidence"] is False
+ assert preflight["safety"]["signs_database_apply_authorization"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_evidence_execution_preflight_ready_after_fake_fetch_but_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preflight = build_pchome_auto_policy_db_apply_authorization_evidence_execution_preflight(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ handoff = preflight["future_database_apply_authorization_verifier_handoff"]
+ package = preflight["authorization_evidence_execution_preflight"]
+ contract = preflight["authorization_evidence_execution_preflight_contract"]
+ check_keys = [
+ check["key"]
+ for check in preflight["authorization_evidence_execution_preflight_checks"]
+ ]
+ assert (
+ preflight["result"]
+ == "DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_PREFLIGHT_READY"
+ )
+ assert preflight["summary"]["authorization_evidence_execution_preflight_ready_count"] == 1
+ assert preflight["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert preflight["summary"]["authorization_evidence_execution_preflight_pass_count"] == 12
+ assert preflight["summary"]["authorization_evidence_execution_preflight_waiting_count"] == 0
+ assert preflight["summary"]["authorization_verifier_receipt_closeout_ready_count"] == 1
+ assert preflight["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert (
+ preflight["summary"][
+ "authorization_detached_verification_evidence_validation_ready_count"
+ ]
+ == 1
+ )
+ assert preflight["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert preflight["summary"]["authorization_evidence_execution_preflight_count"] == 1
+ assert preflight["summary"]["authorization_evidence_execution_field_count"] == 12
+ assert preflight["summary"]["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert preflight["summary"]["verifier_receipt_field_count"] == 12
+ assert preflight["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert preflight["summary"]["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert preflight["summary"]["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert preflight["summary"]["required_external_receipt_evidence_count"] == 10
+ assert preflight["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert preflight["summary"]["post_apply_verifier_required_count"] == 1
+ assert preflight["summary"]["same_run_truth_required_count"] == 1
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_migration_count"] == 0
+ assert preflight["summary"]["executes_endpoint_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert handoff["preflight_id"].startswith(
+ "pchome-db-apply-authorization-evidence-execution-preflight-"
+ )
+ assert handoff["source_verifier_receipt_closeout_id"].startswith(
+ "pchome-db-apply-authorization-verifier-receipt-closeout-"
+ )
+ assert (
+ handoff["source_verifier_receipt_evidence_handoff_id"].startswith(
+ "pchome-db-apply-authorization-verifier-receipt-closeout-"
+ )
+ )
+ assert (
+ handoff["ready_for_future_database_apply_authorization_verifier_handoff"]
+ is True
+ )
+ assert (
+ handoff["can_enter_future_authorization_evidence_execution_closeout"]
+ is True
+ )
+ assert handoff["authorization_evidence_execution_preflight_ready"] is True
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["issues_database_apply_authorization"] is False
+ assert handoff["signs_database_apply_authorization"] is False
+ assert handoff["executes_authorization_evidence"] is False
+ assert handoff["detached_signature_verification_performed"] is False
+ assert handoff["verifier_receipt_persisted"] is False
+ assert handoff["external_signed_authorization_receipt_included"] is False
+ assert handoff["signed_authorization_receipt_included"] is False
+ assert handoff["signature_material_included"] is False
+ assert handoff["secret_material_included"] is False
+ assert package["preflight_id"].startswith(
+ "pchome-db-apply-authorization-evidence-execution-preflight-"
+ )
+ assert package["authorization_material_type"] == (
+ "authorization_evidence_execution_preflight"
+ )
+ assert package["ready_for_future_authorization_evidence_execution_preflight"] is True
+ assert package["authorization_evidence_execution_field_count"] == 12
+ assert package["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert package["verifier_receipt_field_count"] == 12
+ assert package["verifier_receipt_acceptance_gate_count"] == 10
+ assert package["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert package["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert "verifier_receipt_sha256" in package["authorization_evidence_execution_fields"]
+ assert "no_secret_signature_or_database_write_in_preflight" in (
+ package["authorization_evidence_execution_acceptance_gates"]
+ )
+ assert package["requires_detached_signature_verification"] is True
+ assert package["detached_signature_verification_performed"] is False
+ assert package["verifier_receipt_persisted"] is False
+ assert package["external_signed_authorization_receipt_included"] is False
+ assert package["signed_authorization_receipt_included"] is False
+ assert package["signature_material_included"] is False
+ assert package["secret_material_included"] is False
+ assert package["accepts_plaintext_secret"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_shell_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert package["executes_authorization_evidence"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert package["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert package["hash_matches"] is True
+ assert package["requires_fresh_production_truth_in_same_run"] is True
+ assert package["requires_post_apply_verifier"] is True
+ assert contract["permits_future_authorization_evidence_execution_closeout"] is True
+ assert contract["accepts_plaintext_secret"] is False
+ assert contract["performs_detached_signature_verification"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "verifier_receipt_closeout_ready" in check_keys
+ assert "source_chain_ids_present" in check_keys
+ assert "closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["performs_detached_signature_verification"] is False
+ assert preflight["safety"]["persists_verifier_receipt"] is False
+ assert preflight["safety"]["executes_authorization_evidence"] is False
+ assert preflight["safety"]["signs_database_apply_authorization"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_evidence_execution_closeout_waits_without_ready_preflight():
+ closeout = build_pchome_auto_policy_db_apply_authorization_evidence_execution_closeout(
+ _payload(),
+ batch_size=1,
+ )
+
+ final_gate = closeout["future_database_apply_authorization_final_verifier_gate"]
+ package = closeout["authorization_evidence_execution_closeout"]
+ contract = closeout["authorization_evidence_execution_closeout_contract"]
+ check_keys = [
+ check["key"]
+ for check in closeout["authorization_evidence_execution_closeout_checks"]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_closeout"
+ )
+ assert (
+ closeout["result"]
+ == "WAITING_FOR_DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_PREFLIGHT"
+ )
+ assert closeout["summary"]["authorization_evidence_execution_closeout_ready_count"] == 0
+ assert closeout["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_closeout_waiting_count"] > 0
+ assert closeout["summary"]["authorization_evidence_execution_preflight_ready_count"] == 0
+ assert closeout["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert closeout["summary"]["authorization_verifier_receipt_closeout_ready_count"] == 0
+ assert closeout["summary"]["authorization_evidence_execution_closeout_count"] == 1
+ assert closeout["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert closeout["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 0
+ assert closeout["summary"]["authorization_evidence_execution_closeout_field_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["authorization_evidence_execution_field_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert closeout["summary"]["verifier_receipt_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert closeout["summary"]["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_migration_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert final_gate["final_verifier_gate_id"].startswith(
+ "pchome-db-apply-authorization-evidence-execution-closeout-"
+ )
+ assert (
+ final_gate["ready_for_future_database_apply_authorization_final_verifier_gate"]
+ is False
+ )
+ assert (
+ final_gate["can_enter_future_database_apply_controlled_apply_final_preflight"]
+ is False
+ )
+ assert final_gate["authorization_evidence_execution_closeout_ready"] is False
+ assert final_gate["final_verifier_gate_ready"] is False
+ assert final_gate["final_verifier_gate_executed"] is False
+ assert final_gate["ready_for_database_apply_now"] is False
+ assert final_gate["database_apply_authorized"] is False
+ assert final_gate["issues_database_apply_authorization"] is False
+ assert final_gate["signs_database_apply_authorization"] is False
+ assert final_gate["executes_authorization_evidence"] is False
+ assert final_gate["executes_database_apply"] is False
+ assert final_gate["detached_signature_verification_performed"] is False
+ assert final_gate["verifier_receipt_persisted"] is False
+ assert final_gate["external_signed_authorization_receipt_included"] is False
+ assert final_gate["signed_authorization_receipt_included"] is False
+ assert final_gate["signature_material_included"] is False
+ assert final_gate["secret_material_included"] is False
+ assert package["authorization_material_type"] == (
+ "authorization_evidence_execution_closeout"
+ )
+ assert package["ready_for_future_authorization_evidence_execution_closeout"] is False
+ assert package["authorization_evidence_execution_closeout_field_count"] == 12
+ assert package["authorization_evidence_execution_closeout_acceptance_gate_count"] == 10
+ assert package["authorization_evidence_execution_field_count"] == 12
+ assert package["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert "final_verifier_gate_endpoint" in package[
+ "authorization_evidence_execution_closeout_fields"
+ ]
+ assert "no_database_apply_authorized_by_closeout" in package[
+ "authorization_evidence_execution_closeout_acceptance_gates"
+ ]
+ assert package["requires_detached_signature_verification"] is True
+ assert package["detached_signature_verification_performed"] is False
+ assert package["verifier_receipt_persisted"] is False
+ assert package["external_signed_authorization_receipt_included"] is False
+ assert package["signed_authorization_receipt_included"] is False
+ assert package["signature_material_included"] is False
+ assert package["secret_material_included"] is False
+ assert package["accepts_plaintext_secret"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_endpoint_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert package["executes_authorization_evidence"] is False
+ assert package["executes_database_apply"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["database_apply_authorized"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert contract["permits_future_database_apply_authorization_final_verifier_gate"] is False
+ assert contract["permits_future_database_apply_controlled_apply_final_preflight"] is False
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "authorization_evidence_execution_preflight_ready" in check_keys
+ assert "authorization_evidence_execution_closeout_contract_complete" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["performs_detached_signature_verification"] is False
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_authorization_evidence"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_evidence_execution_closeout_ready_after_fake_fetch_but_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_authorization_evidence_execution_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ final_gate = closeout["future_database_apply_authorization_final_verifier_gate"]
+ package = closeout["authorization_evidence_execution_closeout"]
+ contract = closeout["authorization_evidence_execution_closeout_contract"]
+ check_keys = [
+ check["key"]
+ for check in closeout["authorization_evidence_execution_closeout_checks"]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["authorization_evidence_execution_closeout_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_closeout_pass_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_closeout_waiting_count"] == 0
+ assert closeout["summary"]["authorization_evidence_execution_preflight_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert closeout["summary"]["authorization_verifier_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert (
+ closeout["summary"][
+ "authorization_detached_verification_evidence_validation_ready_count"
+ ]
+ == 1
+ )
+ assert closeout["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_preflight_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_closeout_count"] == 1
+ assert closeout["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert closeout["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_closeout_field_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["authorization_evidence_execution_field_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert closeout["summary"]["verifier_receipt_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert closeout["summary"]["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert closeout["summary"]["required_external_receipt_evidence_count"] == 10
+ assert closeout["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_script_count"] == 0
+ assert closeout["summary"]["executes_migration_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert final_gate["final_verifier_gate_id"].startswith(
+ "pchome-db-apply-authorization-evidence-execution-closeout-"
+ )
+ assert (
+ final_gate["source_authorization_evidence_execution_preflight_id"].startswith(
+ "pchome-db-apply-authorization-evidence-execution-preflight-"
+ )
+ )
+ assert final_gate["source_verifier_receipt_closeout_id"].startswith(
+ "pchome-db-apply-authorization-verifier-receipt-closeout-"
+ )
+ assert (
+ final_gate["ready_for_future_database_apply_authorization_final_verifier_gate"]
+ is True
+ )
+ assert (
+ final_gate["can_enter_future_database_apply_controlled_apply_final_preflight"]
+ is True
+ )
+ assert final_gate["authorization_evidence_execution_closeout_ready"] is True
+ assert final_gate["final_verifier_gate_ready"] is True
+ assert final_gate["final_verifier_gate_executed"] is False
+ assert final_gate["ready_for_database_apply_now"] is False
+ assert final_gate["database_apply_authorized"] is False
+ assert final_gate["issues_database_apply_authorization"] is False
+ assert final_gate["signs_database_apply_authorization"] is False
+ assert final_gate["executes_authorization_evidence"] is False
+ assert final_gate["executes_database_apply"] is False
+ assert final_gate["detached_signature_verification_performed"] is False
+ assert final_gate["verifier_receipt_persisted"] is False
+ assert final_gate["external_signed_authorization_receipt_included"] is False
+ assert final_gate["signed_authorization_receipt_included"] is False
+ assert final_gate["signature_material_included"] is False
+ assert final_gate["secret_material_included"] is False
+ assert package["closeout_id"].startswith(
+ "pchome-db-apply-authorization-evidence-execution-closeout-"
+ )
+ assert package["authorization_material_type"] == (
+ "authorization_evidence_execution_closeout"
+ )
+ assert package["ready_for_future_authorization_evidence_execution_closeout"] is True
+ assert package["authorization_evidence_execution_closeout_field_count"] == 12
+ assert package["authorization_evidence_execution_closeout_acceptance_gate_count"] == 10
+ assert package["authorization_evidence_execution_field_count"] == 12
+ assert package["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert "final_verifier_gate_endpoint" in package[
+ "authorization_evidence_execution_closeout_fields"
+ ]
+ assert "no_database_apply_authorized_by_closeout" in package[
+ "authorization_evidence_execution_closeout_acceptance_gates"
+ ]
+ assert package["requires_detached_signature_verification"] is True
+ assert package["detached_signature_verification_performed"] is False
+ assert package["verifier_receipt_persisted"] is False
+ assert package["external_signed_authorization_receipt_included"] is False
+ assert package["signed_authorization_receipt_included"] is False
+ assert package["signature_material_included"] is False
+ assert package["secret_material_included"] is False
+ assert package["accepts_plaintext_secret"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["executes_endpoint_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert package["executes_authorization_evidence"] is False
+ assert package["executes_database_apply"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["database_apply_authorized"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert package["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert package["hash_matches"] is True
+ assert package["requires_fresh_production_truth_in_same_run"] is True
+ assert package["requires_post_apply_verifier"] is True
+ assert contract["permits_future_database_apply_authorization_final_verifier_gate"] is True
+ assert contract["permits_future_database_apply_controlled_apply_final_preflight"] is True
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "authorization_evidence_execution_preflight_ready" in check_keys
+ assert "final_verifier_handoff_ready" in check_keys
+ assert "verifier_hash_and_receipt_chain_locked" in check_keys
+ assert "preflight_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["performs_detached_signature_verification"] is False
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_authorization_evidence"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+ assert closeout["safety"]["signs_database_apply_authorization"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_apply_final_preflight_waits_without_ready_final_gate():
+ preflight = build_pchome_auto_policy_db_apply_controlled_apply_final_preflight(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_preflight = preflight["future_database_apply_controlled_apply_final_preflight"]
+ package = preflight["controlled_apply_final_preflight"]
+ contract = preflight["controlled_apply_final_preflight_contract"]
+ rollback_binding = package["rollback_binding"]
+ verifier_binding = package["post_apply_verifier_binding"]
+ check_keys = [check["key"] for check in preflight["controlled_apply_final_preflight_checks"]]
+ assert preflight["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_apply_final_preflight"
+ )
+ assert (
+ preflight["result"]
+ == "WAITING_FOR_DB_APPLY_AUTHORIZATION_EVIDENCE_EXECUTION_CLOSEOUT"
+ )
+ assert preflight["summary"]["controlled_apply_final_preflight_ready_count"] == 0
+ assert preflight["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert preflight["summary"]["controlled_apply_final_preflight_waiting_count"] > 0
+ assert preflight["summary"]["authorization_evidence_execution_closeout_ready_count"] == 0
+ assert preflight["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert preflight["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert preflight["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 0
+ assert preflight["summary"]["controlled_apply_final_preflight_count"] == 1
+ assert preflight["summary"]["controlled_apply_final_preflight_field_count"] == 12
+ assert preflight["summary"]["controlled_apply_final_preflight_acceptance_gate_count"] == 10
+ assert preflight["summary"]["rollback_binding_count"] == 1
+ assert preflight["summary"]["rollback_binding_field_count"] == 8
+ assert preflight["summary"]["post_apply_verifier_binding_count"] == 1
+ assert preflight["summary"]["post_apply_verifier_binding_field_count"] == 8
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_migration_count"] == 0
+ assert preflight["summary"]["executes_endpoint_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future_preflight["controlled_apply_preflight_id"].startswith(
+ "pchome-db-apply-controlled-apply-final-preflight-"
+ )
+ assert (
+ future_preflight["ready_for_future_database_apply_controlled_apply_final_preflight"]
+ is False
+ )
+ assert (
+ future_preflight["can_enter_future_database_apply_controlled_dry_run_package"]
+ is False
+ )
+ assert future_preflight["controlled_apply_final_preflight_ready"] is False
+ assert future_preflight["rollback_binding_ready"] is False
+ assert future_preflight["post_apply_verifier_binding_ready"] is False
+ assert future_preflight["ready_for_database_apply_now"] is False
+ assert future_preflight["database_apply_authorized"] is False
+ assert future_preflight["issues_database_apply_authorization"] is False
+ assert future_preflight["signs_database_apply_authorization"] is False
+ assert future_preflight["executes_authorization_evidence"] is False
+ assert future_preflight["executes_database_apply"] is False
+ assert future_preflight["executes_endpoint"] is False
+ assert future_preflight["executes_sql"] is False
+ assert future_preflight["writes_database"] is False
+ assert package["authorization_material_type"] == "controlled_apply_final_preflight"
+ assert (
+ package["ready_for_future_database_apply_controlled_apply_final_preflight"]
+ is False
+ )
+ assert package["controlled_apply_final_preflight_field_count"] == 12
+ assert package["controlled_apply_final_preflight_acceptance_gate_count"] == 10
+ assert package["rollback_binding_count"] == 1
+ assert package["rollback_binding_field_count"] == 8
+ assert package["post_apply_verifier_binding_count"] == 1
+ assert package["post_apply_verifier_binding_field_count"] == 8
+ assert rollback_binding["rollback_execution_authorized"] is False
+ assert rollback_binding["rollback_executes_sql"] is False
+ assert rollback_binding["rollback_writes_database"] is False
+ assert rollback_binding["rollback_reads_secret"] is False
+ assert verifier_binding["verifier_must_run_after_apply"] is True
+ assert verifier_binding["verifier_execution_authorized_in_preview"] is False
+ assert verifier_binding["database_apply_authorized"] is False
+ assert package["dry_run_only"] is True
+ assert package["check_mode_only"] is True
+ assert package["rollback_bound"] is False
+ assert package["post_apply_verifier_bound"] is False
+ assert package["accepts_plaintext_secret"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["signature_material_included"] is False
+ assert package["secret_material_included"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert package["executes_authorization_evidence"] is False
+ assert package["executes_database_apply"] is False
+ assert package["executes_endpoint_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["database_apply_authorized"] is False
+ assert contract["permits_future_database_apply_controlled_dry_run_package"] is False
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "final_verifier_gate_ready" in check_keys
+ assert "rollback_binding_complete" in check_keys
+ assert "post_apply_verifier_binding_complete" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["executes_endpoint"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+ assert preflight["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_apply_final_preflight_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ preflight = build_pchome_auto_policy_db_apply_controlled_apply_final_preflight(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_preflight = preflight["future_database_apply_controlled_apply_final_preflight"]
+ package = preflight["controlled_apply_final_preflight"]
+ contract = preflight["controlled_apply_final_preflight_contract"]
+ rollback_binding = package["rollback_binding"]
+ verifier_binding = package["post_apply_verifier_binding"]
+ check_keys = [check["key"] for check in preflight["controlled_apply_final_preflight_checks"]]
+ assert preflight["result"] == "DB_APPLY_CONTROLLED_APPLY_FINAL_PREFLIGHT_READY"
+ assert preflight["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert preflight["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert preflight["summary"]["controlled_apply_final_preflight_pass_count"] == 12
+ assert preflight["summary"]["controlled_apply_final_preflight_waiting_count"] == 0
+ assert preflight["summary"]["authorization_evidence_execution_closeout_ready_count"] == 1
+ assert preflight["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert preflight["summary"]["authorization_evidence_execution_preflight_ready_count"] == 1
+ assert preflight["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert preflight["summary"]["authorization_verifier_receipt_closeout_ready_count"] == 1
+ assert preflight["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert preflight["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert preflight["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 1
+ assert preflight["summary"]["controlled_apply_final_preflight_count"] == 1
+ assert preflight["summary"]["controlled_apply_final_preflight_field_count"] == 12
+ assert preflight["summary"]["controlled_apply_final_preflight_acceptance_gate_count"] == 10
+ assert preflight["summary"]["rollback_binding_count"] == 1
+ assert preflight["summary"]["rollback_binding_field_count"] == 8
+ assert preflight["summary"]["post_apply_verifier_binding_count"] == 1
+ assert preflight["summary"]["post_apply_verifier_binding_field_count"] == 8
+ assert preflight["summary"]["post_apply_verifier_required_count"] == 1
+ assert preflight["summary"]["same_run_truth_required_count"] == 1
+ assert preflight["summary"]["reads_secret_count"] == 0
+ assert preflight["summary"]["executes_script_count"] == 0
+ assert preflight["summary"]["executes_migration_count"] == 0
+ assert preflight["summary"]["executes_endpoint_count"] == 0
+ assert preflight["summary"]["executes_sql_count"] == 0
+ assert preflight["summary"]["writes_database_count"] == 0
+ assert preflight["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future_preflight["controlled_apply_preflight_id"].startswith(
+ "pchome-db-apply-controlled-apply-final-preflight-"
+ )
+ assert future_preflight["source_final_verifier_gate_id"].startswith(
+ "pchome-db-apply-authorization-evidence-execution-closeout-"
+ )
+ assert (
+ future_preflight["ready_for_future_database_apply_controlled_apply_final_preflight"]
+ is True
+ )
+ assert (
+ future_preflight["can_enter_future_database_apply_controlled_dry_run_package"]
+ is True
+ )
+ assert future_preflight["controlled_apply_final_preflight_ready"] is True
+ assert future_preflight["rollback_binding_ready"] is True
+ assert future_preflight["post_apply_verifier_binding_ready"] is True
+ assert future_preflight["ready_for_database_apply_now"] is False
+ assert future_preflight["database_apply_authorized"] is False
+ assert future_preflight["issues_database_apply_authorization"] is False
+ assert future_preflight["signs_database_apply_authorization"] is False
+ assert future_preflight["executes_authorization_evidence"] is False
+ assert future_preflight["executes_database_apply"] is False
+ assert future_preflight["executes_endpoint"] is False
+ assert future_preflight["executes_sql"] is False
+ assert future_preflight["writes_database"] is False
+ assert package["controlled_apply_preflight_id"].startswith(
+ "pchome-db-apply-controlled-apply-final-preflight-"
+ )
+ assert package["authorization_material_type"] == "controlled_apply_final_preflight"
+ assert (
+ package["ready_for_future_database_apply_controlled_apply_final_preflight"]
+ is True
+ )
+ assert package["controlled_apply_final_preflight_field_count"] == 12
+ assert package["controlled_apply_final_preflight_acceptance_gate_count"] == 10
+ assert "rollback_binding_id" in package["controlled_apply_final_preflight_fields"]
+ assert "post_apply_verifier_bound" in (
+ package["controlled_apply_final_preflight_acceptance_gates"]
+ )
+ assert package["rollback_binding_count"] == 1
+ assert package["rollback_binding_field_count"] == 8
+ assert package["post_apply_verifier_binding_count"] == 1
+ assert package["post_apply_verifier_binding_field_count"] == 8
+ assert rollback_binding["rollback_execution_authorized"] is False
+ assert rollback_binding["rollback_executes_sql"] is False
+ assert rollback_binding["rollback_writes_database"] is False
+ assert rollback_binding["rollback_reads_secret"] is False
+ assert verifier_binding["verifier_must_run_after_apply"] is True
+ assert verifier_binding["verifier_execution_authorized_in_preview"] is False
+ assert verifier_binding["database_apply_authorized"] is False
+ assert package["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert package["hash_matches"] is True
+ assert package["dry_run_only"] is True
+ assert package["check_mode_only"] is True
+ assert package["rollback_bound"] is True
+ assert package["post_apply_verifier_bound"] is True
+ assert package["requires_fresh_production_truth_in_same_run"] is True
+ assert package["requires_post_apply_verifier"] is True
+ assert package["accepts_plaintext_secret"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["signature_material_included"] is False
+ assert package["secret_material_included"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert package["executes_authorization_evidence"] is False
+ assert package["executes_database_apply"] is False
+ assert package["executes_endpoint_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["database_apply_authorized"] is False
+ assert contract["permits_future_database_apply_controlled_dry_run_package"] is True
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "final_verifier_gate_ready" in check_keys
+ assert "rollback_binding_complete" in check_keys
+ assert "post_apply_verifier_binding_complete" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "final_verifier_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert preflight["safety"]["reads_secret_in_preview"] is False
+ assert preflight["safety"]["executes_endpoint"] is False
+ assert preflight["safety"]["executes_sql"] is False
+ assert preflight["safety"]["writes_database"] is False
+ assert preflight["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_package_waits_without_ready_final_preflight():
+ package = build_pchome_auto_policy_db_apply_controlled_dry_run_package(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_receipt = package[
+ "future_database_apply_controlled_dry_run_execution_receipt"
+ ]
+ dry_run_package = package["controlled_dry_run_package"]
+ receipt = dry_run_package["dry_run_execution_receipt_preview"]
+ command_shape = dry_run_package["dry_run_command_shape"]
+ contract = package["controlled_dry_run_package_contract"]
+ check_keys = [check["key"] for check in package["controlled_dry_run_package_checks"]]
+ assert package["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_package"
+ )
+ assert package["result"] == "WAITING_FOR_DB_APPLY_CONTROLLED_APPLY_FINAL_PREFLIGHT"
+ assert package["summary"]["controlled_dry_run_package_ready_count"] == 0
+ assert package["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert package["summary"]["controlled_dry_run_package_waiting_count"] > 0
+ assert package["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert package["summary"]["controlled_apply_final_preflight_ready_count"] == 0
+ assert package["summary"]["controlled_dry_run_package_count"] == 1
+ assert package["summary"]["controlled_dry_run_package_field_count"] == 12
+ assert package["summary"]["controlled_dry_run_acceptance_gate_count"] == 10
+ assert package["summary"]["dry_run_execution_receipt_preview_count"] == 1
+ assert package["summary"]["dry_run_execution_receipt_field_count"] == 8
+ assert package["summary"]["rollback_binding_count"] == 1
+ assert package["summary"]["post_apply_verifier_binding_count"] == 1
+ assert package["summary"]["reads_secret_count"] == 0
+ assert package["summary"]["executes_script_count"] == 0
+ assert package["summary"]["executes_migration_count"] == 0
+ assert package["summary"]["executes_endpoint_count"] == 0
+ assert package["summary"]["executes_sql_count"] == 0
+ assert package["summary"]["writes_database_count"] == 0
+ assert package["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future_receipt["dry_run_package_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-package-"
+ )
+ assert (
+ future_receipt[
+ "ready_for_future_database_apply_controlled_dry_run_execution_receipt"
+ ]
+ is False
+ )
+ assert (
+ future_receipt[
+ "can_enter_future_database_apply_controlled_dry_run_receipt_closeout"
+ ]
+ is False
+ )
+ assert future_receipt["controlled_dry_run_package_ready"] is False
+ assert future_receipt["dry_run_execution_performed"] is False
+ assert future_receipt["ready_for_database_apply_now"] is False
+ assert future_receipt["database_apply_authorized"] is False
+ assert future_receipt["issues_database_apply_authorization"] is False
+ assert future_receipt["signs_database_apply_authorization"] is False
+ assert future_receipt["executes_authorization_evidence"] is False
+ assert future_receipt["executes_database_apply"] is False
+ assert future_receipt["executes_endpoint"] is False
+ assert future_receipt["executes_sql"] is False
+ assert future_receipt["writes_database"] is False
+ assert dry_run_package["authorization_material_type"] == "controlled_dry_run_package"
+ assert (
+ dry_run_package["ready_for_future_database_apply_controlled_dry_run_package"]
+ is False
+ )
+ assert dry_run_package["controlled_dry_run_package_field_count"] == 12
+ assert dry_run_package["controlled_dry_run_acceptance_gate_count"] == 10
+ assert dry_run_package["dry_run_execution_receipt_preview_count"] == 1
+ assert dry_run_package["dry_run_execution_receipt_field_count"] == 8
+ assert dry_run_package["rollback_binding_count"] == 1
+ assert dry_run_package["post_apply_verifier_binding_count"] == 1
+ assert command_shape["dry_run_only"] is True
+ assert command_shape["check_mode_only"] is True
+ assert command_shape["execution_allowed"] is False
+ assert command_shape["shell_command_included"] is False
+ assert command_shape["sql_included"] is False
+ assert command_shape["endpoint_execution_included"] is False
+ assert command_shape["database_write_included"] is False
+ assert command_shape["requires_fresh_production_truth_in_same_run"] is True
+ assert command_shape["requires_rollback_binding"] is True
+ assert command_shape["requires_post_apply_verifier_binding"] is True
+ assert receipt["receipt_id"].endswith("-dry-run-receipt-preview")
+ assert receipt["dry_run_status"] == "preview_only_not_executed"
+ assert receipt["execution_performed"] is False
+ assert receipt["stdout_included"] is False
+ assert receipt["stderr_included"] is False
+ assert receipt["database_apply_authorized"] is False
+ assert receipt["executes_shell"] is False
+ assert receipt["executes_endpoint"] is False
+ assert receipt["executes_sql"] is False
+ assert receipt["writes_database"] is False
+ assert receipt["reads_secret"] is False
+ assert receipt["receipt_field_count"] == 8
+ assert dry_run_package["dry_run_only"] is True
+ assert dry_run_package["check_mode_only"] is True
+ assert dry_run_package["accepts_plaintext_secret"] is False
+ assert dry_run_package["reads_secret_in_preview"] is False
+ assert dry_run_package["signature_material_included"] is False
+ assert dry_run_package["secret_material_included"] is False
+ assert dry_run_package["signs_database_apply_authorization"] is False
+ assert dry_run_package["executes_authorization_evidence"] is False
+ assert dry_run_package["executes_database_apply"] is False
+ assert dry_run_package["executes_endpoint_in_preview"] is False
+ assert dry_run_package["executes_sql_in_preview"] is False
+ assert dry_run_package["writes_database_in_preview"] is False
+ assert dry_run_package["ready_for_database_apply_now"] is False
+ assert dry_run_package["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_execution_receipt"
+ ]
+ is False
+ )
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "controlled_apply_final_preflight_ready" in check_keys
+ assert "dry_run_command_shape_preview_only" in check_keys
+ assert "dry_run_execution_receipt_preview_only" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert package["safety"]["reads_secret_in_preview"] is False
+ assert package["safety"]["executes_endpoint"] is False
+ assert package["safety"]["executes_sql"] is False
+ assert package["safety"]["writes_database"] is False
+ assert package["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_package_ready_after_fake_fetch_but_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ package = build_pchome_auto_policy_db_apply_controlled_dry_run_package(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_receipt = package[
+ "future_database_apply_controlled_dry_run_execution_receipt"
+ ]
+ dry_run_package = package["controlled_dry_run_package"]
+ receipt = dry_run_package["dry_run_execution_receipt_preview"]
+ command_shape = dry_run_package["dry_run_command_shape"]
+ contract = package["controlled_dry_run_package_contract"]
+ check_keys = [check["key"] for check in package["controlled_dry_run_package_checks"]]
+ assert package["result"] == "DB_APPLY_CONTROLLED_DRY_RUN_PACKAGE_READY"
+ assert package["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert package["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert package["summary"]["controlled_dry_run_package_pass_count"] == 12
+ assert package["summary"]["controlled_dry_run_package_waiting_count"] == 0
+ assert package["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert package["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert package["summary"]["authorization_evidence_execution_closeout_ready_count"] == 1
+ assert package["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert package["summary"]["authorization_evidence_execution_preflight_ready_count"] == 1
+ assert package["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert package["summary"]["authorization_verifier_receipt_closeout_ready_count"] == 1
+ assert package["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert package["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert package["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 1
+ assert package["summary"]["controlled_dry_run_package_count"] == 1
+ assert package["summary"]["controlled_dry_run_package_field_count"] == 12
+ assert package["summary"]["controlled_dry_run_acceptance_gate_count"] == 10
+ assert package["summary"]["dry_run_execution_receipt_preview_count"] == 1
+ assert package["summary"]["dry_run_execution_receipt_field_count"] == 8
+ assert package["summary"]["controlled_apply_final_preflight_count"] == 1
+ assert package["summary"]["rollback_binding_count"] == 1
+ assert package["summary"]["post_apply_verifier_binding_count"] == 1
+ assert package["summary"]["post_apply_verifier_required_count"] == 1
+ assert package["summary"]["same_run_truth_required_count"] == 1
+ assert package["summary"]["reads_secret_count"] == 0
+ assert package["summary"]["executes_script_count"] == 0
+ assert package["summary"]["executes_migration_count"] == 0
+ assert package["summary"]["executes_endpoint_count"] == 0
+ assert package["summary"]["executes_sql_count"] == 0
+ assert package["summary"]["writes_database_count"] == 0
+ assert package["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future_receipt["dry_run_package_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-package-"
+ )
+ assert (
+ future_receipt[
+ "ready_for_future_database_apply_controlled_dry_run_execution_receipt"
+ ]
+ is True
+ )
+ assert (
+ future_receipt[
+ "can_enter_future_database_apply_controlled_dry_run_receipt_closeout"
+ ]
+ is True
+ )
+ assert future_receipt["controlled_dry_run_package_ready"] is True
+ assert future_receipt["dry_run_execution_performed"] is False
+ assert future_receipt["ready_for_database_apply_now"] is False
+ assert future_receipt["database_apply_authorized"] is False
+ assert future_receipt["issues_database_apply_authorization"] is False
+ assert future_receipt["signs_database_apply_authorization"] is False
+ assert future_receipt["executes_authorization_evidence"] is False
+ assert future_receipt["executes_database_apply"] is False
+ assert future_receipt["executes_endpoint"] is False
+ assert future_receipt["executes_sql"] is False
+ assert future_receipt["writes_database"] is False
+ assert dry_run_package["authorization_material_type"] == "controlled_dry_run_package"
+ assert (
+ dry_run_package["ready_for_future_database_apply_controlled_dry_run_package"]
+ is True
+ )
+ assert dry_run_package["controlled_dry_run_package_field_count"] == 12
+ assert dry_run_package["controlled_dry_run_acceptance_gate_count"] == 10
+ assert "dry_run_execution_receipt_id" in (
+ dry_run_package["controlled_dry_run_package_fields"]
+ )
+ assert "dry_run_receipt_preview_only" in (
+ dry_run_package["controlled_dry_run_acceptance_gates"]
+ )
+ assert dry_run_package["dry_run_execution_receipt_preview_count"] == 1
+ assert dry_run_package["dry_run_execution_receipt_field_count"] == 8
+ assert dry_run_package["rollback_binding_count"] == 1
+ assert dry_run_package["post_apply_verifier_binding_count"] == 1
+ assert dry_run_package["target_file"] == (
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ )
+ assert dry_run_package["hash_matches"] is True
+ assert dry_run_package["target_migration_hash_locked"] is True
+ assert command_shape["dry_run_only"] is True
+ assert command_shape["check_mode_only"] is True
+ assert command_shape["execution_allowed"] is False
+ assert command_shape["shell_command_included"] is False
+ assert command_shape["sql_included"] is False
+ assert command_shape["endpoint_execution_included"] is False
+ assert command_shape["database_write_included"] is False
+ assert command_shape["requires_fresh_production_truth_in_same_run"] is True
+ assert command_shape["requires_rollback_binding"] is True
+ assert command_shape["requires_post_apply_verifier_binding"] is True
+ assert receipt["receipt_id"].endswith("-dry-run-receipt-preview")
+ assert receipt["source_dry_run_package_id"] == dry_run_package["dry_run_package_id"]
+ assert receipt["dry_run_status"] == "preview_only_not_executed"
+ assert receipt["dry_run_command_shape_hash"] == (
+ dry_run_package["dry_run_command_shape_hash"]
+ )
+ assert receipt["execution_performed"] is False
+ assert receipt["stdout_included"] is False
+ assert receipt["stderr_included"] is False
+ assert receipt["database_apply_authorized"] is False
+ assert receipt["executes_shell"] is False
+ assert receipt["executes_endpoint"] is False
+ assert receipt["executes_sql"] is False
+ assert receipt["writes_database"] is False
+ assert receipt["reads_secret"] is False
+ assert receipt["receipt_field_count"] == 8
+ assert dry_run_package["dry_run_only"] is True
+ assert dry_run_package["check_mode_only"] is True
+ assert dry_run_package["requires_fresh_production_truth_in_same_run"] is True
+ assert dry_run_package["requires_post_apply_verifier"] is True
+ assert dry_run_package["accepts_plaintext_secret"] is False
+ assert dry_run_package["reads_secret_in_preview"] is False
+ assert dry_run_package["signature_material_included"] is False
+ assert dry_run_package["secret_material_included"] is False
+ assert dry_run_package["signs_database_apply_authorization"] is False
+ assert dry_run_package["executes_authorization_evidence"] is False
+ assert dry_run_package["executes_database_apply"] is False
+ assert dry_run_package["executes_endpoint_in_preview"] is False
+ assert dry_run_package["executes_sql_in_preview"] is False
+ assert dry_run_package["writes_database_in_preview"] is False
+ assert dry_run_package["ready_for_database_apply_now"] is False
+ assert dry_run_package["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_execution_receipt"
+ ]
+ is True
+ )
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "controlled_apply_final_preflight_ready" in check_keys
+ assert "rollback_binding_carried_forward" in check_keys
+ assert "post_apply_verifier_binding_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "dry_run_command_shape_preview_only" in check_keys
+ assert "dry_run_execution_receipt_preview_only" in check_keys
+ assert "final_preflight_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert package["safety"]["reads_secret_in_preview"] is False
+ assert package["safety"]["executes_endpoint"] is False
+ assert package["safety"]["executes_sql"] is False
+ assert package["safety"]["writes_database"] is False
+ assert package["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_receipt_closeout_waits_without_ready_package():
+ closeout = build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_verification = closeout[
+ "future_database_apply_controlled_dry_run_result_parser_verification"
+ ]
+ receipt_closeout = closeout["controlled_dry_run_receipt_closeout"]
+ parser = receipt_closeout["dry_run_result_parser"]
+ validation = receipt_closeout["receipt_validation_report"]
+ contract = closeout["controlled_dry_run_receipt_closeout_contract"]
+ check_keys = [
+ check["key"] for check in closeout["controlled_dry_run_receipt_closeout_checks"]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_closeout"
+ )
+ assert closeout["result"] == "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_PACKAGE"
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_waiting_count"] > 0
+ assert closeout["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["dry_run_result_parser_count"] == 1
+ assert closeout["summary"]["dry_run_result_parser_field_count"] == 10
+ assert closeout["summary"]["receipt_validation_report_count"] == 1
+ assert closeout["summary"]["receipt_validation_field_count"] == 8
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future_verification["receipt_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-receipt-closeout-"
+ )
+ assert (
+ future_verification[
+ "ready_for_future_database_apply_controlled_dry_run_result_parser_verification"
+ ]
+ is False
+ )
+ assert (
+ future_verification[
+ "can_enter_future_database_apply_controlled_dry_run_runner_readiness"
+ ]
+ is False
+ )
+ assert future_verification["controlled_dry_run_receipt_closeout_ready"] is False
+ assert future_verification["dry_run_execution_performed"] is False
+ assert future_verification["ready_for_database_apply_now"] is False
+ assert future_verification["database_apply_authorized"] is False
+ assert future_verification["executes_database_apply"] is False
+ assert future_verification["executes_endpoint"] is False
+ assert future_verification["executes_sql"] is False
+ assert future_verification["writes_database"] is False
+ assert receipt_closeout["authorization_material_type"] == (
+ "controlled_dry_run_receipt_closeout"
+ )
+ assert (
+ receipt_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_receipt_closeout"
+ ]
+ is False
+ )
+ assert receipt_closeout["controlled_dry_run_receipt_closeout_field_count"] == 12
+ assert (
+ receipt_closeout[
+ "controlled_dry_run_receipt_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert parser["expected_receipt_status"] == "preview_only_not_executed"
+ assert parser["execution_required"] is False
+ assert parser["stdout_allowed"] is False
+ assert parser["stderr_allowed"] is False
+ assert parser["database_apply_authorized"] is False
+ assert parser["parser_field_count"] == 10
+ assert validation["receipt_validation_field_count"] == 8
+ assert validation["execution_performed"] is False
+ assert validation["stdout_included"] is False
+ assert validation["stderr_included"] is False
+ assert validation["database_apply_authorized"] is False
+ assert validation["executes_shell"] is False
+ assert validation["executes_endpoint"] is False
+ assert validation["executes_sql"] is False
+ assert validation["writes_database"] is False
+ assert validation["reads_secret"] is False
+ assert receipt_closeout["receipt_preview_only"] is True
+ assert receipt_closeout["dry_run_only"] is True
+ assert receipt_closeout["check_mode_only"] is True
+ assert receipt_closeout["accepts_plaintext_secret"] is False
+ assert receipt_closeout["reads_secret_in_preview"] is False
+ assert receipt_closeout["signature_material_included"] is False
+ assert receipt_closeout["secret_material_included"] is False
+ assert receipt_closeout["signs_database_apply_authorization"] is False
+ assert receipt_closeout["executes_authorization_evidence"] is False
+ assert receipt_closeout["executes_database_apply"] is False
+ assert receipt_closeout["executes_endpoint_in_preview"] is False
+ assert receipt_closeout["executes_sql_in_preview"] is False
+ assert receipt_closeout["writes_database_in_preview"] is False
+ assert receipt_closeout["ready_for_database_apply_now"] is False
+ assert receipt_closeout["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_runner_readiness"
+ ]
+ is False
+ )
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "controlled_dry_run_package_ready" in check_keys
+ assert "dry_run_result_parser_schema_complete" in check_keys
+ assert "receipt_preview_schema_matches_parser" in check_keys
+ assert "command_shape_hash_matches_receipt" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_receipt_closeout_ready_after_fake_fetch_but_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_verification = closeout[
+ "future_database_apply_controlled_dry_run_result_parser_verification"
+ ]
+ receipt_closeout = closeout["controlled_dry_run_receipt_closeout"]
+ parser = receipt_closeout["dry_run_result_parser"]
+ validation = receipt_closeout["receipt_validation_report"]
+ contract = closeout["controlled_dry_run_receipt_closeout_contract"]
+ check_keys = [
+ check["key"] for check in closeout["controlled_dry_run_receipt_closeout_checks"]
+ ]
+ assert closeout["result"] == "DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_CLOSEOUT_READY"
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_closeout_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_preflight_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert closeout["summary"]["authorization_verifier_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert closeout["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["dry_run_result_parser_count"] == 1
+ assert closeout["summary"]["dry_run_result_parser_field_count"] == 10
+ assert closeout["summary"]["receipt_validation_report_count"] == 1
+ assert closeout["summary"]["receipt_validation_field_count"] == 8
+ assert closeout["summary"]["dry_run_execution_receipt_preview_count"] == 1
+ assert closeout["summary"]["dry_run_execution_receipt_field_count"] == 8
+ assert closeout["summary"]["controlled_dry_run_package_count"] == 1
+ assert closeout["summary"]["rollback_binding_count"] == 1
+ assert closeout["summary"]["post_apply_verifier_binding_count"] == 1
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future_verification["receipt_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-receipt-closeout-"
+ )
+ assert (
+ future_verification[
+ "ready_for_future_database_apply_controlled_dry_run_result_parser_verification"
+ ]
+ is True
+ )
+ assert (
+ future_verification[
+ "can_enter_future_database_apply_controlled_dry_run_runner_readiness"
+ ]
+ is True
+ )
+ assert future_verification["controlled_dry_run_receipt_closeout_ready"] is True
+ assert future_verification["receipt_validation_status"] == (
+ "preview_validated_not_executed"
+ )
+ assert future_verification["dry_run_execution_performed"] is False
+ assert future_verification["ready_for_database_apply_now"] is False
+ assert future_verification["database_apply_authorized"] is False
+ assert future_verification["executes_database_apply"] is False
+ assert future_verification["executes_endpoint"] is False
+ assert future_verification["executes_sql"] is False
+ assert future_verification["writes_database"] is False
+ assert receipt_closeout["authorization_material_type"] == (
+ "controlled_dry_run_receipt_closeout"
+ )
+ assert (
+ receipt_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_receipt_closeout"
+ ]
+ is True
+ )
+ assert receipt_closeout["controlled_dry_run_receipt_closeout_field_count"] == 12
+ assert (
+ receipt_closeout[
+ "controlled_dry_run_receipt_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert "dry_run_result_parser_id" in (
+ receipt_closeout["controlled_dry_run_receipt_closeout_fields"]
+ )
+ assert "receipt_preview_schema_match" in (
+ receipt_closeout["controlled_dry_run_receipt_closeout_acceptance_gates"]
+ )
+ assert parser["parser_id"] == receipt_closeout["dry_run_result_parser_id"]
+ assert parser["expected_receipt_status"] == "preview_only_not_executed"
+ assert parser["required_command_shape_hash"] == (
+ receipt_closeout["dry_run_command_shape_hash"]
+ )
+ assert parser["execution_required"] is False
+ assert parser["stdout_allowed"] is False
+ assert parser["stderr_allowed"] is False
+ assert parser["database_apply_authorized"] is False
+ assert parser["parser_field_count"] == 10
+ assert validation["receipt_validation_status"] == "preview_validated_not_executed"
+ assert validation["receipt_validation_field_count"] == 8
+ assert validation["dry_run_command_shape_hash"] == (
+ receipt_closeout["dry_run_command_shape_hash"]
+ )
+ assert validation["execution_performed"] is False
+ assert validation["stdout_included"] is False
+ assert validation["stderr_included"] is False
+ assert validation["database_apply_authorized"] is False
+ assert validation["executes_shell"] is False
+ assert validation["executes_endpoint"] is False
+ assert validation["executes_sql"] is False
+ assert validation["writes_database"] is False
+ assert validation["reads_secret"] is False
+ assert receipt_closeout["target_file"] == (
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ )
+ assert receipt_closeout["hash_matches"] is True
+ assert receipt_closeout["target_migration_hash_locked"] is True
+ assert receipt_closeout["receipt_preview_only"] is True
+ assert receipt_closeout["dry_run_only"] is True
+ assert receipt_closeout["check_mode_only"] is True
+ assert receipt_closeout["requires_fresh_production_truth_in_same_run"] is True
+ assert receipt_closeout["requires_post_apply_verifier"] is True
+ assert receipt_closeout["accepts_plaintext_secret"] is False
+ assert receipt_closeout["reads_secret_in_preview"] is False
+ assert receipt_closeout["signature_material_included"] is False
+ assert receipt_closeout["secret_material_included"] is False
+ assert receipt_closeout["signs_database_apply_authorization"] is False
+ assert receipt_closeout["executes_authorization_evidence"] is False
+ assert receipt_closeout["executes_database_apply"] is False
+ assert receipt_closeout["executes_endpoint_in_preview"] is False
+ assert receipt_closeout["executes_sql_in_preview"] is False
+ assert receipt_closeout["writes_database_in_preview"] is False
+ assert receipt_closeout["ready_for_database_apply_now"] is False
+ assert receipt_closeout["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_runner_readiness"
+ ]
+ is True
+ )
+ assert contract["executes_authorization_evidence"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["signs_database_apply_authorization"] is False
+ assert contract["writes_database"] is False
+ assert "controlled_dry_run_package_ready" in check_keys
+ assert "dry_run_result_parser_schema_complete" in check_keys
+ assert "receipt_preview_schema_matches_parser" in check_keys
+ assert "command_shape_hash_matches_receipt" in check_keys
+ assert "receipt_preview_only_not_executed" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "package_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_runner_readiness_waits_without_ready_receipt_closeout():
+ readiness = build_pchome_auto_policy_db_apply_controlled_dry_run_runner_readiness(
+ _payload(),
+ batch_size=1,
+ )
+
+ future_plan = readiness[
+ "future_database_apply_controlled_dry_run_execution_plan_binding"
+ ]
+ runner = readiness["controlled_dry_run_runner_readiness"]
+ plan = runner["execution_plan_binding"]
+ validation = runner["receipt_validation_report"]
+ parser = runner["dry_run_result_parser"]
+ contract = readiness["controlled_dry_run_runner_readiness_contract"]
+ check_keys = [
+ check["key"] for check in readiness["controlled_dry_run_runner_readiness_checks"]
+ ]
+ assert readiness["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_readiness"
+ )
+ assert readiness["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_CLOSEOUT"
+ )
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 0
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_waiting_count"] > 0
+ assert readiness["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert readiness["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 0
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_count"] == 1
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_field_count"] == 12
+ assert (
+ readiness["summary"][
+ "controlled_dry_run_runner_readiness_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert readiness["summary"]["execution_plan_binding_count"] == 1
+ assert readiness["summary"]["execution_plan_binding_field_count"] == 12
+ assert readiness["summary"]["dry_run_result_parser_count"] == 1
+ assert readiness["summary"]["dry_run_result_parser_field_count"] == 10
+ assert readiness["summary"]["receipt_validation_report_count"] == 1
+ assert readiness["summary"]["receipt_validation_field_count"] == 8
+ assert readiness["summary"]["reads_secret_count"] == 0
+ assert readiness["summary"]["executes_endpoint_count"] == 0
+ assert readiness["summary"]["executes_sql_count"] == 0
+ assert readiness["summary"]["writes_database_count"] == 0
+ assert readiness["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future_plan["runner_readiness_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-runner-readiness-"
+ )
+ assert (
+ future_plan[
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_binding"
+ ]
+ is False
+ )
+ assert (
+ future_plan[
+ "can_enter_future_database_apply_controlled_dry_run_execution_plan_closeout"
+ ]
+ is False
+ )
+ assert future_plan["controlled_dry_run_runner_readiness_ready"] is False
+ assert future_plan["execution_plan_bound"] is False
+ assert future_plan["dry_run_execution_performed"] is False
+ assert future_plan["runner_execution_authorized"] is False
+ assert future_plan["dry_run_execution_authorized"] is False
+ assert future_plan["ready_for_database_apply_now"] is False
+ assert future_plan["database_apply_authorized"] is False
+ assert future_plan["executes_database_apply"] is False
+ assert future_plan["executes_endpoint"] is False
+ assert future_plan["executes_sql"] is False
+ assert future_plan["writes_database"] is False
+ assert runner["authorization_material_type"] == "controlled_dry_run_runner_readiness"
+ assert (
+ runner["ready_for_future_database_apply_controlled_dry_run_runner_readiness"]
+ is False
+ )
+ assert runner["controlled_dry_run_runner_readiness_field_count"] == 12
+ assert runner["controlled_dry_run_runner_readiness_acceptance_gate_count"] == 10
+ assert runner["execution_plan_binding_count"] == 1
+ assert runner["execution_plan_binding_field_count"] == 12
+ assert plan["runner_mode"] == "future_controlled_dry_run_runner_readiness_only"
+ assert plan["plan_status"] == "plan_binding_preview_not_executable"
+ assert plan["dry_run_only"] is True
+ assert plan["check_mode_only"] is True
+ assert plan["execution_authorized"] is False
+ assert plan["dry_run_execution_authorized"] is False
+ assert plan["runner_execution_authorized"] is False
+ assert plan["shell_execution_included"] is False
+ assert plan["endpoint_execution_included"] is False
+ assert plan["sql_execution_included"] is False
+ assert plan["database_write_included"] is False
+ assert plan["stdout_capture_allowed"] is False
+ assert plan["stderr_capture_allowed"] is False
+ assert plan["database_apply_authorized"] is False
+ assert plan["ready_for_database_apply_now"] is False
+ assert parser["execution_required"] is False
+ assert parser["database_apply_authorized"] is False
+ assert validation["execution_performed"] is False
+ assert validation["database_apply_authorized"] is False
+ assert validation["executes_endpoint"] is False
+ assert validation["executes_sql"] is False
+ assert validation["writes_database"] is False
+ assert runner["runner_readiness_only"] is True
+ assert runner["execution_plan_preview_only"] is True
+ assert runner["runner_execution_authorized"] is False
+ assert runner["dry_run_execution_authorized"] is False
+ assert runner["accepts_plaintext_secret"] is False
+ assert runner["reads_secret_in_preview"] is False
+ assert runner["signature_material_included"] is False
+ assert runner["secret_material_included"] is False
+ assert runner["signs_database_apply_authorization"] is False
+ assert runner["executes_authorization_evidence"] is False
+ assert runner["executes_database_apply"] is False
+ assert runner["executes_endpoint_in_preview"] is False
+ assert runner["executes_sql_in_preview"] is False
+ assert runner["writes_database_in_preview"] is False
+ assert runner["ready_for_database_apply_now"] is False
+ assert runner["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_execution_plan_binding"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "receipt_closeout_ready" in check_keys
+ assert "execution_plan_binding_preview_only" in check_keys
+ assert "runner_execution_gate_closed" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert readiness["safety"]["reads_secret_in_preview"] is False
+ assert readiness["safety"]["executes_endpoint"] is False
+ assert readiness["safety"]["executes_sql"] is False
+ assert readiness["safety"]["writes_database"] is False
+ assert readiness["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_runner_readiness_ready_after_fake_fetch_but_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ readiness = build_pchome_auto_policy_db_apply_controlled_dry_run_runner_readiness(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+
+ future_plan = readiness[
+ "future_database_apply_controlled_dry_run_execution_plan_binding"
+ ]
+ runner = readiness["controlled_dry_run_runner_readiness"]
+ plan = runner["execution_plan_binding"]
+ validation = runner["receipt_validation_report"]
+ parser = runner["dry_run_result_parser"]
+ contract = readiness["controlled_dry_run_runner_readiness_contract"]
+ check_keys = [
+ check["key"] for check in readiness["controlled_dry_run_runner_readiness_checks"]
+ ]
+ assert readiness["result"] == "DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_READINESS_READY"
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_pass_count"] == 12
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_waiting_count"] == 0
+ assert readiness["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert readiness["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert readiness["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert readiness["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert readiness["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert readiness["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert readiness["summary"]["authorization_evidence_execution_closeout_ready_count"] == 1
+ assert readiness["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert readiness["summary"]["authorization_evidence_execution_preflight_ready_count"] == 1
+ assert readiness["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert readiness["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert readiness["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 1
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_count"] == 1
+ assert readiness["summary"]["controlled_dry_run_runner_readiness_field_count"] == 12
+ assert (
+ readiness["summary"][
+ "controlled_dry_run_runner_readiness_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert readiness["summary"]["execution_plan_binding_count"] == 1
+ assert readiness["summary"]["execution_plan_binding_field_count"] == 12
+ assert readiness["summary"]["dry_run_result_parser_count"] == 1
+ assert readiness["summary"]["dry_run_result_parser_field_count"] == 10
+ assert readiness["summary"]["receipt_validation_report_count"] == 1
+ assert readiness["summary"]["receipt_validation_field_count"] == 8
+ assert readiness["summary"]["controlled_dry_run_receipt_closeout_count"] == 1
+ assert readiness["summary"]["controlled_dry_run_receipt_closeout_field_count"] == 12
+ assert (
+ readiness["summary"][
+ "controlled_dry_run_receipt_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert readiness["summary"]["rollback_binding_count"] == 1
+ assert readiness["summary"]["post_apply_verifier_binding_count"] == 1
+ assert readiness["summary"]["post_apply_verifier_required_count"] == 1
+ assert readiness["summary"]["same_run_truth_required_count"] == 1
+ assert readiness["summary"]["reads_secret_count"] == 0
+ assert readiness["summary"]["executes_endpoint_count"] == 0
+ assert readiness["summary"]["executes_sql_count"] == 0
+ assert readiness["summary"]["writes_database_count"] == 0
+ assert readiness["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future_plan["runner_readiness_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-runner-readiness-"
+ )
+ assert (
+ future_plan[
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_binding"
+ ]
+ is True
+ )
+ assert (
+ future_plan[
+ "can_enter_future_database_apply_controlled_dry_run_execution_plan_closeout"
+ ]
+ is True
+ )
+ assert future_plan["controlled_dry_run_runner_readiness_ready"] is True
+ assert future_plan["execution_plan_bound"] is True
+ assert future_plan["dry_run_execution_performed"] is False
+ assert future_plan["runner_execution_authorized"] is False
+ assert future_plan["dry_run_execution_authorized"] is False
+ assert future_plan["ready_for_database_apply_now"] is False
+ assert future_plan["database_apply_authorized"] is False
+ assert future_plan["executes_database_apply"] is False
+ assert future_plan["executes_endpoint"] is False
+ assert future_plan["executes_sql"] is False
+ assert future_plan["writes_database"] is False
+ assert runner["authorization_material_type"] == "controlled_dry_run_runner_readiness"
+ assert (
+ runner["ready_for_future_database_apply_controlled_dry_run_runner_readiness"]
+ is True
+ )
+ assert runner["controlled_dry_run_runner_readiness_field_count"] == 12
+ assert runner["controlled_dry_run_runner_readiness_acceptance_gate_count"] == 10
+ assert "execution_plan_binding_id" in (
+ runner["controlled_dry_run_runner_readiness_fields"]
+ )
+ assert "execution_plan_binding_preview_only" in (
+ runner["controlled_dry_run_runner_readiness_acceptance_gates"]
+ )
+ assert runner["execution_plan_binding_count"] == 1
+ assert runner["execution_plan_binding_field_count"] == 12
+ assert plan["execution_plan_binding_id"] == future_plan["execution_plan_binding_id"]
+ assert plan["runner_mode"] == "future_controlled_dry_run_runner_readiness_only"
+ assert plan["plan_status"] == "plan_binding_preview_not_executable"
+ assert plan["dry_run_only"] is True
+ assert plan["check_mode_only"] is True
+ assert plan["execution_authorized"] is False
+ assert plan["dry_run_execution_authorized"] is False
+ assert plan["runner_execution_authorized"] is False
+ assert plan["shell_execution_included"] is False
+ assert plan["endpoint_execution_included"] is False
+ assert plan["sql_execution_included"] is False
+ assert plan["database_write_included"] is False
+ assert plan["stdout_capture_allowed"] is False
+ assert plan["stderr_capture_allowed"] is False
+ assert plan["database_apply_authorized"] is False
+ assert plan["ready_for_database_apply_now"] is False
+ assert parser["required_command_shape_hash"] == runner["dry_run_command_shape_hash"]
+ assert parser["execution_required"] is False
+ assert parser["database_apply_authorized"] is False
+ assert validation["receipt_validation_status"] == "preview_validated_not_executed"
+ assert validation["dry_run_command_shape_hash"] == runner["dry_run_command_shape_hash"]
+ assert validation["execution_performed"] is False
+ assert validation["database_apply_authorized"] is False
+ assert validation["executes_endpoint"] is False
+ assert validation["executes_sql"] is False
+ assert validation["writes_database"] is False
+ assert runner["target_file"] == "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ assert runner["hash_matches"] is True
+ assert runner["target_migration_hash_locked"] is True
+ assert runner["runner_readiness_only"] is True
+ assert runner["execution_plan_preview_only"] is True
+ assert runner["runner_execution_authorized"] is False
+ assert runner["dry_run_execution_authorized"] is False
+ assert runner["accepts_plaintext_secret"] is False
+ assert runner["reads_secret_in_preview"] is False
+ assert runner["signature_material_included"] is False
+ assert runner["secret_material_included"] is False
+ assert runner["signs_database_apply_authorization"] is False
+ assert runner["executes_authorization_evidence"] is False
+ assert runner["executes_database_apply"] is False
+ assert runner["executes_endpoint_in_preview"] is False
+ assert runner["executes_sql_in_preview"] is False
+ assert runner["writes_database_in_preview"] is False
+ assert runner["ready_for_database_apply_now"] is False
+ assert runner["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_execution_plan_binding"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "receipt_closeout_ready" in check_keys
+ assert "dry_run_result_parser_verified" in check_keys
+ assert "receipt_validation_report_ready" in check_keys
+ assert "command_shape_hash_bound" in check_keys
+ assert "execution_plan_binding_preview_only" in check_keys
+ assert "runner_execution_gate_closed" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert readiness["safety"]["reads_secret_in_preview"] is False
+ assert readiness["safety"]["executes_endpoint"] is False
+ assert readiness["safety"]["executes_sql"] is False
+ assert readiness["safety"]["writes_database"] is False
+ assert readiness["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout_waits_without_ready_runner_readiness():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_command_artifact_verification"
+ ]
+ plan_closeout = closeout["controlled_dry_run_execution_plan_closeout"]
+ artifact = plan_closeout["non_executable_command_artifact"]
+ contract = closeout["controlled_dry_run_execution_plan_closeout_contract"]
+ check_keys = [
+ check["key"]
+ for check in closeout["controlled_dry_run_execution_plan_closeout_checks"]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_READINESS"
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_ready_count"
+ ]
+ == 0
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_check_count"
+ ]
+ == 12
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_waiting_count"
+ ]
+ > 0
+ )
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_count"] == 1
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_field_count"
+ ]
+ == 12
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert closeout["summary"]["non_executable_command_artifact_count"] == 1
+ assert closeout["summary"]["non_executable_command_artifact_field_count"] == 10
+ assert closeout["summary"]["execution_plan_binding_count"] == 1
+ assert closeout["summary"]["execution_plan_binding_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["execution_plan_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-execution-plan-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_verification"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_command_artifact_closeout"
+ ]
+ is False
+ )
+ assert future["execution_plan_closeout_ready"] is False
+ assert future["non_executable_command_artifact_verified"] is False
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["execution_authorized"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert plan_closeout["authorization_material_type"] == (
+ "controlled_dry_run_execution_plan_closeout"
+ )
+ assert (
+ plan_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_closeout"
+ ]
+ is False
+ )
+ assert plan_closeout["execution_plan_closeout_field_count"] == 12
+ assert plan_closeout["execution_plan_closeout_acceptance_gate_count"] == 10
+ assert plan_closeout["non_executable_command_artifact_count"] == 1
+ assert plan_closeout["non_executable_command_artifact_field_count"] == 10
+ assert plan_closeout["execution_plan_closeout_only"] is True
+ assert plan_closeout["non_executable_command_artifact_only"] is True
+ assert plan_closeout["runner_execution_authorized"] is False
+ assert plan_closeout["dry_run_execution_authorized"] is False
+ assert plan_closeout["execution_authorized"] is False
+ assert plan_closeout["accepts_plaintext_secret"] is False
+ assert plan_closeout["reads_secret_in_preview"] is False
+ assert plan_closeout["signature_material_included"] is False
+ assert plan_closeout["secret_material_included"] is False
+ assert plan_closeout["signs_database_apply_authorization"] is False
+ assert plan_closeout["executes_authorization_evidence"] is False
+ assert plan_closeout["executes_database_apply"] is False
+ assert plan_closeout["executes_endpoint_in_preview"] is False
+ assert plan_closeout["executes_sql_in_preview"] is False
+ assert plan_closeout["writes_database_in_preview"] is False
+ assert plan_closeout["ready_for_database_apply_now"] is False
+ assert plan_closeout["database_apply_authorized"] is False
+ assert artifact["artifact_type"] == "non_executable_command_artifact_reference"
+ assert artifact["command_text_included"] is False
+ assert artifact["argv_included"] is False
+ assert artifact.get("command_text") is None
+ assert artifact.get("argv") is None
+ assert artifact["shell_command_included"] is False
+ assert artifact["endpoint_execution_included"] is False
+ assert artifact["sql_execution_included"] is False
+ assert artifact["database_write_included"] is False
+ assert artifact["execution_authorized"] is False
+ assert artifact["database_apply_authorized"] is False
+ assert len(artifact["non_executable_command_artifact_sha256"]) == 64
+ assert contract[
+ "permits_future_database_apply_controlled_dry_run_command_artifact_verification"
+ ] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "runner_readiness_ready" in check_keys
+ assert "non_executable_command_artifact_bound" in check_keys
+ assert "command_artifact_hash_locked" in check_keys
+ assert "runner_execution_gate_closed" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout_ready_after_fake_fetch_but_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_command_artifact_verification"
+ ]
+ plan_closeout = closeout["controlled_dry_run_execution_plan_closeout"]
+ plan = plan_closeout["execution_plan_binding"]
+ artifact = plan_closeout["non_executable_command_artifact"]
+ validation = plan_closeout["receipt_validation_report"]
+ parser = plan_closeout["dry_run_result_parser"]
+ contract = closeout["controlled_dry_run_execution_plan_closeout_contract"]
+ check_keys = [
+ check["key"]
+ for check in closeout["controlled_dry_run_execution_plan_closeout_checks"]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PLAN_CLOSEOUT_READY"
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_ready_count"
+ ]
+ == 1
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_check_count"
+ ]
+ == 12
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_pass_count"
+ ]
+ == 12
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_waiting_count"
+ ]
+ == 0
+ )
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_closeout_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_preflight_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert closeout["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert closeout["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_count"] == 1
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_field_count"
+ ]
+ == 12
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_execution_plan_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert closeout["summary"]["non_executable_command_artifact_count"] == 1
+ assert closeout["summary"]["non_executable_command_artifact_field_count"] == 10
+ assert closeout["summary"]["execution_plan_binding_count"] == 1
+ assert closeout["summary"]["execution_plan_binding_field_count"] == 12
+ assert closeout["summary"]["dry_run_result_parser_count"] == 1
+ assert closeout["summary"]["dry_run_result_parser_field_count"] == 10
+ assert closeout["summary"]["receipt_validation_report_count"] == 1
+ assert closeout["summary"]["receipt_validation_field_count"] == 8
+ assert closeout["summary"]["rollback_binding_count"] == 1
+ assert closeout["summary"]["post_apply_verifier_binding_count"] == 1
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["execution_plan_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-execution-plan-closeout-"
+ )
+ assert future["non_executable_command_artifact_id"] == artifact["artifact_id"]
+ assert (
+ future["non_executable_command_artifact_sha256"]
+ == artifact["non_executable_command_artifact_sha256"]
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_verification"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_command_artifact_closeout"
+ ]
+ is True
+ )
+ assert future["execution_plan_closeout_ready"] is True
+ assert future["non_executable_command_artifact_verified"] is True
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["execution_authorized"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert plan_closeout["authorization_material_type"] == (
+ "controlled_dry_run_execution_plan_closeout"
+ )
+ assert (
+ plan_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_execution_plan_closeout"
+ ]
+ is True
+ )
+ assert plan_closeout["execution_plan_closeout_field_count"] == 12
+ assert plan_closeout["execution_plan_closeout_acceptance_gate_count"] == 10
+ assert "non_executable_command_artifact_id" in (
+ plan_closeout["execution_plan_closeout_fields"]
+ )
+ assert "non_executable_command_artifact_bound" in (
+ plan_closeout["execution_plan_closeout_acceptance_gates"]
+ )
+ assert plan_closeout["non_executable_command_artifact_count"] == 1
+ assert plan_closeout["non_executable_command_artifact_field_count"] == 10
+ assert len(plan_closeout["non_executable_command_artifact_sha256"]) == 64
+ assert plan_closeout["execution_plan_binding_count"] == 1
+ assert plan_closeout["execution_plan_binding_field_count"] == 12
+ assert plan["execution_plan_binding_id"] == future[
+ "source_execution_plan_binding_id"
+ ]
+ assert plan["plan_status"] == "plan_binding_preview_not_executable"
+ assert plan["dry_run_only"] is True
+ assert plan["check_mode_only"] is True
+ assert plan["execution_authorized"] is False
+ assert plan["dry_run_execution_authorized"] is False
+ assert plan["runner_execution_authorized"] is False
+ assert plan["shell_execution_included"] is False
+ assert plan["endpoint_execution_included"] is False
+ assert plan["sql_execution_included"] is False
+ assert plan["database_write_included"] is False
+ assert plan["stdout_capture_allowed"] is False
+ assert plan["stderr_capture_allowed"] is False
+ assert plan["database_apply_authorized"] is False
+ assert plan["ready_for_database_apply_now"] is False
+ assert artifact["artifact_type"] == "non_executable_command_artifact_reference"
+ assert artifact["source_execution_plan_binding_id"] == plan[
+ "execution_plan_binding_id"
+ ]
+ assert artifact["dry_run_command_shape_hash"] == plan_closeout[
+ "dry_run_command_shape_hash"
+ ]
+ assert artifact["command_text_included"] is False
+ assert artifact["argv_included"] is False
+ assert artifact.get("command_text") is None
+ assert artifact.get("argv") is None
+ assert artifact["shell_command_included"] is False
+ assert artifact["endpoint_execution_included"] is False
+ assert artifact["sql_execution_included"] is False
+ assert artifact["database_write_included"] is False
+ assert artifact["stdout_capture_allowed"] is False
+ assert artifact["stderr_capture_allowed"] is False
+ assert artifact["execution_authorized"] is False
+ assert artifact["dry_run_execution_authorized"] is False
+ assert artifact["runner_execution_authorized"] is False
+ assert artifact["database_apply_authorized"] is False
+ assert len(artifact["non_executable_command_artifact_sha256"]) == 64
+ assert parser["required_command_shape_hash"] == plan_closeout[
+ "dry_run_command_shape_hash"
+ ]
+ assert parser["execution_required"] is False
+ assert parser["database_apply_authorized"] is False
+ assert validation["receipt_validation_status"] == "preview_validated_not_executed"
+ assert validation["execution_performed"] is False
+ assert validation["database_apply_authorized"] is False
+ assert validation["executes_endpoint"] is False
+ assert validation["executes_sql"] is False
+ assert validation["writes_database"] is False
+ assert plan_closeout["target_file"] == (
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ )
+ assert plan_closeout["hash_matches"] is True
+ assert plan_closeout["target_migration_hash_locked"] is True
+ assert plan_closeout["execution_plan_closeout_only"] is True
+ assert plan_closeout["non_executable_command_artifact_only"] is True
+ assert plan_closeout["runner_execution_authorized"] is False
+ assert plan_closeout["dry_run_execution_authorized"] is False
+ assert plan_closeout["execution_authorized"] is False
+ assert plan_closeout["accepts_plaintext_secret"] is False
+ assert plan_closeout["reads_secret_in_preview"] is False
+ assert plan_closeout["signature_material_included"] is False
+ assert plan_closeout["secret_material_included"] is False
+ assert plan_closeout["signs_database_apply_authorization"] is False
+ assert plan_closeout["executes_authorization_evidence"] is False
+ assert plan_closeout["executes_database_apply"] is False
+ assert plan_closeout["executes_endpoint_in_preview"] is False
+ assert plan_closeout["executes_sql_in_preview"] is False
+ assert plan_closeout["writes_database_in_preview"] is False
+ assert plan_closeout["ready_for_database_apply_now"] is False
+ assert plan_closeout["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_command_artifact_verification"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "runner_readiness_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "execution_plan_binding_preview_only" in check_keys
+ assert "non_executable_command_artifact_bound" in check_keys
+ assert "command_artifact_hash_locked" in check_keys
+ assert "receipt_validation_and_parser_carried_forward" in check_keys
+ assert "runner_execution_gate_closed" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bindings_carried_forward" in check_keys
+ assert "runner_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout_waits_without_ready_execution_plan_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ ]
+ command_closeout = closeout["controlled_dry_run_command_artifact_closeout"]
+ receipt_preflight = command_closeout["runner_execution_receipt_preflight"]
+ artifact = command_closeout["non_executable_command_artifact"]
+ contract = closeout["controlled_dry_run_command_artifact_closeout_contract"]
+ check_keys = [
+ check["key"]
+ for check in closeout["controlled_dry_run_command_artifact_closeout_checks"]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PLAN_CLOSEOUT"
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_command_artifact_closeout_ready_count"
+ ]
+ == 0
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_command_artifact_closeout_check_count"
+ ]
+ == 12
+ )
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_field_count"] == 12
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_command_artifact_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert closeout["summary"]["runner_execution_receipt_preflight_count"] == 1
+ assert closeout["summary"]["runner_execution_receipt_preflight_field_count"] == 10
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["command_artifact_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-command-artifact-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout"
+ ]
+ is False
+ )
+ assert future["command_artifact_closeout_ready"] is False
+ assert future["runner_execution_receipt_preflight_bound"] is False
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["execution_authorized"] is False
+ assert future["stdout_capture_allowed"] is False
+ assert future["stderr_capture_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert command_closeout["authorization_material_type"] == (
+ "controlled_dry_run_command_artifact_closeout"
+ )
+ assert (
+ command_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_closeout"
+ ]
+ is False
+ )
+ assert command_closeout["command_artifact_closeout_field_count"] == 12
+ assert command_closeout["command_artifact_closeout_acceptance_gate_count"] == 10
+ assert command_closeout["runner_execution_receipt_preflight_count"] == 1
+ assert command_closeout["runner_execution_receipt_preflight_field_count"] == 10
+ assert command_closeout["command_artifact_closeout_only"] is True
+ assert command_closeout["runner_execution_receipt_preflight_only"] is True
+ assert command_closeout["runner_execution_authorized"] is False
+ assert command_closeout["dry_run_execution_authorized"] is False
+ assert command_closeout["execution_authorized"] is False
+ assert command_closeout["accepts_plaintext_secret"] is False
+ assert command_closeout["reads_secret_in_preview"] is False
+ assert command_closeout["signature_material_included"] is False
+ assert command_closeout["secret_material_included"] is False
+ assert command_closeout["signs_database_apply_authorization"] is False
+ assert command_closeout["executes_database_apply"] is False
+ assert command_closeout["executes_endpoint_in_preview"] is False
+ assert command_closeout["executes_sql_in_preview"] is False
+ assert command_closeout["writes_database_in_preview"] is False
+ assert artifact["command_text_included"] is False
+ assert artifact["argv_included"] is False
+ assert artifact.get("command_text") is None
+ assert artifact.get("argv") is None
+ assert receipt_preflight["preflight_status"] == "preflight_only_not_executed"
+ assert receipt_preflight["execution_required"] is False
+ assert receipt_preflight["execution_authorized"] is False
+ assert receipt_preflight["dry_run_execution_authorized"] is False
+ assert receipt_preflight["runner_execution_authorized"] is False
+ assert receipt_preflight["stdout_capture_allowed"] is False
+ assert receipt_preflight["stderr_capture_allowed"] is False
+ assert receipt_preflight["execution_performed"] is False
+ assert receipt_preflight["database_apply_authorized"] is False
+ assert receipt_preflight["writes_database"] is False
+ assert contract[
+ "permits_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ ] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "execution_plan_closeout_ready" in check_keys
+ assert "non_executable_command_artifact_hash_verified" in check_keys
+ assert "runner_execution_receipt_preflight_no_execute" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout_ready_after_fake_fetch_but_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ ]
+ command_closeout = closeout["controlled_dry_run_command_artifact_closeout"]
+ receipt_preflight = command_closeout["runner_execution_receipt_preflight"]
+ artifact = command_closeout["non_executable_command_artifact"]
+ validation = command_closeout["receipt_validation_report"]
+ parser = command_closeout["dry_run_result_parser"]
+ contract = closeout["controlled_dry_run_command_artifact_closeout_contract"]
+ check_keys = [
+ check["key"]
+ for check in closeout["controlled_dry_run_command_artifact_closeout_checks"]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_COMMAND_ARTIFACT_CLOSEOUT_READY"
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_command_artifact_closeout_ready_count"
+ ]
+ == 1
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_command_artifact_closeout_check_count"
+ ]
+ == 12
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_command_artifact_closeout_pass_count"
+ ]
+ == 12
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_command_artifact_closeout_waiting_count"
+ ]
+ == 0
+ )
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_closeout_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_preflight_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert closeout["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert closeout["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_field_count"] == 12
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_command_artifact_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert closeout["summary"]["runner_execution_receipt_preflight_count"] == 1
+ assert closeout["summary"]["runner_execution_receipt_preflight_field_count"] == 10
+ assert closeout["summary"]["non_executable_command_artifact_count"] == 1
+ assert closeout["summary"]["non_executable_command_artifact_field_count"] == 10
+ assert closeout["summary"]["execution_plan_binding_count"] == 1
+ assert closeout["summary"]["execution_plan_binding_field_count"] == 12
+ assert closeout["summary"]["dry_run_result_parser_count"] == 1
+ assert closeout["summary"]["dry_run_result_parser_field_count"] == 10
+ assert closeout["summary"]["receipt_validation_report_count"] == 1
+ assert closeout["summary"]["receipt_validation_field_count"] == 8
+ assert closeout["summary"]["rollback_binding_count"] == 1
+ assert closeout["summary"]["post_apply_verifier_binding_count"] == 1
+ assert closeout["summary"]["post_apply_verifier_required_count"] == 1
+ assert closeout["summary"]["same_run_truth_required_count"] == 1
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["command_artifact_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-command-artifact-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout"
+ ]
+ is True
+ )
+ assert future["command_artifact_closeout_ready"] is True
+ assert future["runner_execution_receipt_preflight_bound"] is True
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["execution_authorized"] is False
+ assert future["stdout_capture_allowed"] is False
+ assert future["stderr_capture_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert command_closeout["authorization_material_type"] == (
+ "controlled_dry_run_command_artifact_closeout"
+ )
+ assert (
+ command_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_command_artifact_closeout"
+ ]
+ is True
+ )
+ assert command_closeout["command_artifact_closeout_field_count"] == 12
+ assert command_closeout["command_artifact_closeout_acceptance_gate_count"] == 10
+ assert "runner_execution_receipt_preflight_id" in (
+ command_closeout["command_artifact_closeout_fields"]
+ )
+ assert "runner_execution_receipt_preflight_no_execute" in (
+ command_closeout["command_artifact_closeout_acceptance_gates"]
+ )
+ assert command_closeout["runner_execution_receipt_preflight_count"] == 1
+ assert command_closeout["runner_execution_receipt_preflight_field_count"] == 10
+ assert command_closeout["non_executable_command_artifact_count"] == 1
+ assert command_closeout["command_artifact_closeout_only"] is True
+ assert command_closeout["runner_execution_receipt_preflight_only"] is True
+ assert command_closeout["runner_execution_authorized"] is False
+ assert command_closeout["dry_run_execution_authorized"] is False
+ assert command_closeout["execution_authorized"] is False
+ assert command_closeout["accepts_plaintext_secret"] is False
+ assert command_closeout["reads_secret_in_preview"] is False
+ assert command_closeout["signature_material_included"] is False
+ assert command_closeout["secret_material_included"] is False
+ assert command_closeout["signs_database_apply_authorization"] is False
+ assert command_closeout["executes_database_apply"] is False
+ assert command_closeout["executes_endpoint_in_preview"] is False
+ assert command_closeout["executes_sql_in_preview"] is False
+ assert command_closeout["writes_database_in_preview"] is False
+ assert artifact["command_text_included"] is False
+ assert artifact["argv_included"] is False
+ assert artifact.get("command_text") is None
+ assert artifact.get("argv") is None
+ assert artifact["shell_command_included"] is False
+ assert artifact["endpoint_execution_included"] is False
+ assert artifact["sql_execution_included"] is False
+ assert artifact["database_write_included"] is False
+ assert artifact["execution_authorized"] is False
+ assert artifact["database_apply_authorized"] is False
+ assert len(artifact["non_executable_command_artifact_sha256"]) == 64
+ assert receipt_preflight["preflight_id"] == future[
+ "runner_execution_receipt_preflight_id"
+ ]
+ assert receipt_preflight["source_non_executable_command_artifact_id"] == artifact[
+ "artifact_id"
+ ]
+ assert receipt_preflight["preflight_status"] == "preflight_only_not_executed"
+ assert receipt_preflight["execution_required"] is False
+ assert receipt_preflight["execution_authorized"] is False
+ assert receipt_preflight["dry_run_execution_authorized"] is False
+ assert receipt_preflight["runner_execution_authorized"] is False
+ assert receipt_preflight["shell_execution_included"] is False
+ assert receipt_preflight["endpoint_execution_included"] is False
+ assert receipt_preflight["sql_execution_included"] is False
+ assert receipt_preflight["database_write_included"] is False
+ assert receipt_preflight["stdout_capture_allowed"] is False
+ assert receipt_preflight["stderr_capture_allowed"] is False
+ assert receipt_preflight["execution_performed"] is False
+ assert receipt_preflight["stdout_included"] is False
+ assert receipt_preflight["stderr_included"] is False
+ assert receipt_preflight["database_apply_authorized"] is False
+ assert receipt_preflight["writes_database"] is False
+ assert parser["required_command_shape_hash"] == command_closeout[
+ "dry_run_command_shape_hash"
+ ]
+ assert parser["execution_required"] is False
+ assert validation["receipt_validation_status"] == "preview_validated_not_executed"
+ assert validation["execution_performed"] is False
+ assert validation["database_apply_authorized"] is False
+ assert validation["executes_endpoint"] is False
+ assert validation["executes_sql"] is False
+ assert validation["writes_database"] is False
+ assert command_closeout["target_file"] == (
+ "migrations/045_pchome_auto_policy_evidence_receipts.sql"
+ )
+ assert command_closeout["hash_matches"] is True
+ assert command_closeout["target_migration_hash_locked"] is True
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "execution_plan_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "non_executable_command_artifact_hash_verified" in check_keys
+ assert "non_executable_artifact_has_no_command_text_or_argv" in check_keys
+ assert "runner_execution_receipt_preflight_bound" in check_keys
+ assert "runner_execution_receipt_preflight_no_execute" in check_keys
+ assert "result_parser_and_receipt_validation_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bindings_carried_forward" in check_keys
+ assert "execution_plan_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout_waits_without_ready_command_artifact_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ ]
+ receipt_closeout = closeout[
+ "controlled_dry_run_runner_execution_receipt_closeout"
+ ]
+ preview = receipt_closeout["receipt_closeout_preview"]
+ parser = receipt_closeout["post_receipt_parser_verification"]
+ preflight = receipt_closeout["runner_execution_receipt_preflight"]
+ contract = closeout[
+ "controlled_dry_run_runner_execution_receipt_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_runner_execution_receipt_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_COMMAND_ARTIFACT_CLOSEOUT"
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count"
+ ]
+ == 0
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count"
+ ]
+ == 12
+ )
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_field_count"] == 12
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert closeout["summary"]["post_receipt_parser_verification_count"] == 1
+ assert closeout["summary"]["post_receipt_parser_verification_field_count"] == 10
+ assert closeout["summary"]["receipt_closeout_preview_count"] == 1
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["runner_execution_receipt_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-runner-execution-receipt-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_post_receipt_parser_closeout"
+ ]
+ is False
+ )
+ assert future["runner_execution_receipt_closeout_ready"] is False
+ assert future["post_receipt_parser_verification_bound"] is False
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["execution_authorized"] is False
+ assert future["stdout_capture_allowed"] is False
+ assert future["stderr_capture_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert receipt_closeout["authorization_material_type"] == (
+ "controlled_dry_run_runner_execution_receipt_closeout"
+ )
+ assert (
+ receipt_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout"
+ ]
+ is False
+ )
+ assert receipt_closeout["runner_execution_receipt_closeout_field_count"] == 12
+ assert receipt_closeout["runner_execution_receipt_closeout_acceptance_gate_count"] == 10
+ assert receipt_closeout["post_receipt_parser_verification_count"] == 1
+ assert receipt_closeout["post_receipt_parser_verification_field_count"] == 10
+ assert receipt_closeout["receipt_closeout_preview_count"] == 1
+ assert receipt_closeout["runner_execution_receipt_closeout_only"] is True
+ assert receipt_closeout["post_receipt_parser_verification_only"] is True
+ assert receipt_closeout["runner_execution_authorized"] is False
+ assert receipt_closeout["dry_run_execution_authorized"] is False
+ assert receipt_closeout["execution_authorized"] is False
+ assert receipt_closeout["accepts_plaintext_secret"] is False
+ assert receipt_closeout["reads_secret_in_preview"] is False
+ assert receipt_closeout["signature_material_included"] is False
+ assert receipt_closeout["secret_material_included"] is False
+ assert receipt_closeout["signs_database_apply_authorization"] is False
+ assert receipt_closeout["executes_database_apply"] is False
+ assert receipt_closeout["executes_endpoint_in_preview"] is False
+ assert receipt_closeout["executes_sql_in_preview"] is False
+ assert receipt_closeout["writes_database_in_preview"] is False
+ assert preflight["preflight_status"] == "preflight_only_not_executed"
+ assert preflight["execution_required"] is False
+ assert preflight["execution_performed"] is False
+ assert preflight["stdout_capture_allowed"] is False
+ assert preflight["stderr_capture_allowed"] is False
+ assert preflight["writes_database"] is False
+ assert preview["receipt_status"] == "receipt_closeout_preview_not_executed"
+ assert preview["execution_required"] is False
+ assert preview["execution_performed"] is False
+ assert preview["stdout_included"] is False
+ assert preview["stderr_included"] is False
+ assert preview["stdout_capture_allowed"] is False
+ assert preview["stderr_capture_allowed"] is False
+ assert preview["database_apply_authorized"] is False
+ assert preview["writes_database"] is False
+ assert preview["executes_endpoint"] is False
+ assert preview["executes_sql"] is False
+ assert parser["expected_preflight_status"] == "preflight_only_not_executed"
+ assert parser["expected_receipt_status"] == "receipt_closeout_preview_not_executed"
+ assert parser["expected_execution_performed"] is False
+ assert parser["expected_stdout_included"] is False
+ assert parser["expected_stderr_included"] is False
+ assert parser["execution_required"] is False
+ assert parser["stdout_allowed"] is False
+ assert parser["stderr_allowed"] is False
+ assert parser["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "command_artifact_closeout_ready" in check_keys
+ assert "runner_execution_receipt_preflight_no_execute" in check_keys
+ assert "post_receipt_parser_verification_bound" in check_keys
+ assert "post_receipt_parser_blocks_execution" in check_keys
+ assert "receipt_closeout_preview_only" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout_ready_after_fake_fetch_but_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ ]
+ receipt_closeout = closeout[
+ "controlled_dry_run_runner_execution_receipt_closeout"
+ ]
+ preview = receipt_closeout["receipt_closeout_preview"]
+ parser = receipt_closeout["post_receipt_parser_verification"]
+ preflight = receipt_closeout["runner_execution_receipt_preflight"]
+ contract = closeout[
+ "controlled_dry_run_runner_execution_receipt_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_runner_execution_receipt_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_EXECUTION_RECEIPT_CLOSEOUT_READY"
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_ready_count"
+ ]
+ == 1
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count"
+ ]
+ == 12
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_pass_count"
+ ]
+ == 12
+ )
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_waiting_count"
+ ]
+ == 0
+ )
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_closeout_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert closeout["summary"]["authorization_evidence_execution_preflight_ready_count"] == 1
+ assert closeout["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert closeout["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert closeout["summary"]["database_apply_authorization_final_verifier_gate_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_field_count"] == 12
+ assert (
+ closeout["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert closeout["summary"]["post_receipt_parser_verification_count"] == 1
+ assert closeout["summary"]["post_receipt_parser_verification_field_count"] == 10
+ assert closeout["summary"]["receipt_closeout_preview_count"] == 1
+ assert closeout["summary"]["runner_execution_receipt_preflight_count"] == 1
+ assert closeout["summary"]["runner_execution_receipt_preflight_field_count"] == 10
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["runner_execution_receipt_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-runner-execution-receipt-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_post_receipt_parser_closeout"
+ ]
+ is True
+ )
+ assert future["runner_execution_receipt_closeout_ready"] is True
+ assert future["post_receipt_parser_verification_bound"] is True
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["execution_authorized"] is False
+ assert future["stdout_capture_allowed"] is False
+ assert future["stderr_capture_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert receipt_closeout["authorization_material_type"] == (
+ "controlled_dry_run_runner_execution_receipt_closeout"
+ )
+ assert (
+ receipt_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_runner_execution_receipt_closeout"
+ ]
+ is True
+ )
+ assert receipt_closeout["runner_execution_receipt_closeout_field_count"] == 12
+ assert receipt_closeout["runner_execution_receipt_closeout_acceptance_gate_count"] == 10
+ assert "post_receipt_parser_verification_id" in (
+ receipt_closeout["runner_execution_receipt_closeout_fields"]
+ )
+ assert "post_receipt_parser_verification_bound" in (
+ receipt_closeout["runner_execution_receipt_closeout_acceptance_gates"]
+ )
+ assert receipt_closeout["post_receipt_parser_verification_count"] == 1
+ assert receipt_closeout["post_receipt_parser_verification_field_count"] == 10
+ assert receipt_closeout["receipt_closeout_preview_count"] == 1
+ assert receipt_closeout["runner_execution_receipt_closeout_only"] is True
+ assert receipt_closeout["post_receipt_parser_verification_only"] is True
+ assert receipt_closeout["runner_execution_authorized"] is False
+ assert receipt_closeout["dry_run_execution_authorized"] is False
+ assert receipt_closeout["execution_authorized"] is False
+ assert receipt_closeout["accepts_plaintext_secret"] is False
+ assert receipt_closeout["reads_secret_in_preview"] is False
+ assert receipt_closeout["signature_material_included"] is False
+ assert receipt_closeout["secret_material_included"] is False
+ assert receipt_closeout["signs_database_apply_authorization"] is False
+ assert receipt_closeout["executes_database_apply"] is False
+ assert receipt_closeout["executes_endpoint_in_preview"] is False
+ assert receipt_closeout["executes_sql_in_preview"] is False
+ assert receipt_closeout["writes_database_in_preview"] is False
+ assert preflight["preflight_status"] == "preflight_only_not_executed"
+ assert preflight["execution_required"] is False
+ assert preflight["execution_performed"] is False
+ assert preflight["stdout_capture_allowed"] is False
+ assert preflight["stderr_capture_allowed"] is False
+ assert preflight["writes_database"] is False
+ assert preview["receipt_status"] == "receipt_closeout_preview_not_executed"
+ assert preview["execution_required"] is False
+ assert preview["execution_performed"] is False
+ assert preview["stdout_included"] is False
+ assert preview["stderr_included"] is False
+ assert preview["stdout_capture_allowed"] is False
+ assert preview["stderr_capture_allowed"] is False
+ assert preview["database_apply_authorized"] is False
+ assert preview["writes_database"] is False
+ assert preview["executes_endpoint"] is False
+ assert preview["executes_sql"] is False
+ assert parser["verification_id"] == future["post_receipt_parser_verification_id"]
+ assert parser["expected_preflight_status"] == "preflight_only_not_executed"
+ assert parser["expected_receipt_status"] == "receipt_closeout_preview_not_executed"
+ assert parser["expected_execution_performed"] is False
+ assert parser["expected_stdout_included"] is False
+ assert parser["expected_stderr_included"] is False
+ assert parser["execution_required"] is False
+ assert parser["stdout_allowed"] is False
+ assert parser["stderr_allowed"] is False
+ assert parser["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "command_artifact_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "runner_execution_receipt_preflight_no_execute" in check_keys
+ assert "post_receipt_parser_verification_bound" in check_keys
+ assert "post_receipt_parser_blocks_execution" in check_keys
+ assert "receipt_closeout_preview_only" in check_keys
+ assert "result_parser_and_receipt_validation_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bindings_carried_forward" in check_keys
+ assert "command_artifact_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout_waits_without_ready_runner_receipt():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ ]
+ parser_closeout = closeout[
+ "controlled_dry_run_post_receipt_parser_closeout"
+ ]
+ enforcement = parser_closeout["no_apply_enforcement_verification"]
+ contract = closeout[
+ "controlled_dry_run_post_receipt_parser_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_post_receipt_parser_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_EXECUTION_RECEIPT_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["no_apply_enforcement_verification_count"] == 1
+ assert closeout["summary"]["no_apply_enforcement_verification_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["post_receipt_parser_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-post-receipt-parser-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_no_apply_enforcement_closeout"
+ ]
+ is False
+ )
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert parser_closeout["authorization_material_type"] == (
+ "controlled_dry_run_post_receipt_parser_closeout"
+ )
+ assert parser_closeout["post_receipt_parser_closeout_field_count"] == 12
+ assert parser_closeout["post_receipt_parser_closeout_acceptance_gate_count"] == 10
+ assert parser_closeout["post_receipt_parser_closeout_only"] is True
+ assert parser_closeout["no_apply_enforcement_verification_only"] is True
+ assert parser_closeout["endpoint_execution_allowed"] is False
+ assert parser_closeout["sql_execution_allowed"] is False
+ assert parser_closeout["database_write_allowed"] is False
+ assert parser_closeout["database_apply_authorized"] is False
+ assert parser_closeout["signs_database_apply_authorization"] is False
+ assert parser_closeout["executes_database_apply"] is False
+ assert parser_closeout["executes_endpoint_in_preview"] is False
+ assert parser_closeout["executes_sql_in_preview"] is False
+ assert parser_closeout["writes_database_in_preview"] is False
+ assert enforcement["enforcement_status"] == "no_apply_enforcement_preview_ready"
+ assert enforcement["endpoint_execution_allowed"] is False
+ assert enforcement["sql_execution_allowed"] is False
+ assert enforcement["database_write_allowed"] is False
+ assert enforcement["database_apply_authorized"] is False
+ assert enforcement["executes_database_apply"] is False
+ assert enforcement["executes_endpoint"] is False
+ assert enforcement["executes_sql"] is False
+ assert enforcement["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "runner_execution_receipt_closeout_ready" in check_keys
+ assert "post_receipt_parser_verification_ready" in check_keys
+ assert "no_apply_enforcement_verification_bound" in check_keys
+ assert "no_apply_enforcement_blocks_endpoint_sql_db_write" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout_ready_after_fake_fetch_but_no_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ ]
+ parser_closeout = closeout[
+ "controlled_dry_run_post_receipt_parser_closeout"
+ ]
+ enforcement = parser_closeout["no_apply_enforcement_verification"]
+ parser = parser_closeout["post_receipt_parser_verification"]
+ preview = parser_closeout["receipt_closeout_preview"]
+ contract = closeout[
+ "controlled_dry_run_post_receipt_parser_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_post_receipt_parser_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_POST_RECEIPT_PARSER_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["no_apply_enforcement_verification_count"] == 1
+ assert closeout["summary"]["no_apply_enforcement_verification_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_no_apply_enforcement_closeout"
+ ]
+ is True
+ )
+ assert future["post_receipt_parser_closeout_ready"] is True
+ assert future["no_apply_enforcement_verification_bound"] is True
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert parser_closeout["ready_for_future_database_apply_controlled_dry_run_post_receipt_parser_closeout"] is True
+ assert parser_closeout["post_receipt_parser_closeout_only"] is True
+ assert parser_closeout["no_apply_enforcement_verification_only"] is True
+ assert parser_closeout["endpoint_execution_allowed"] is False
+ assert parser_closeout["sql_execution_allowed"] is False
+ assert parser_closeout["database_write_allowed"] is False
+ assert parser_closeout["database_apply_authorized"] is False
+ assert parser_closeout["signs_database_apply_authorization"] is False
+ assert parser_closeout["executes_database_apply"] is False
+ assert parser_closeout["executes_endpoint_in_preview"] is False
+ assert parser_closeout["executes_sql_in_preview"] is False
+ assert parser_closeout["writes_database_in_preview"] is False
+ assert parser["parser_verification_status"] == "post_receipt_parser_preview_ready"
+ assert parser["expected_execution_performed"] is False
+ assert parser["expected_stdout_included"] is False
+ assert parser["expected_stderr_included"] is False
+ assert parser["database_apply_authorized"] is False
+ assert preview["receipt_status"] == "receipt_closeout_preview_not_executed"
+ assert preview["execution_performed"] is False
+ assert preview["stdout_included"] is False
+ assert preview["stderr_included"] is False
+ assert preview["writes_database"] is False
+ assert enforcement["verification_id"] == future["no_apply_enforcement_verification_id"]
+ assert enforcement["source_post_receipt_parser_verification_id"] == parser["verification_id"]
+ assert enforcement["endpoint_execution_allowed"] is False
+ assert enforcement["sql_execution_allowed"] is False
+ assert enforcement["database_write_allowed"] is False
+ assert enforcement["database_apply_authorized"] is False
+ assert enforcement["executes_database_apply"] is False
+ assert enforcement["executes_endpoint"] is False
+ assert enforcement["executes_sql"] is False
+ assert enforcement["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "runner_execution_receipt_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "post_receipt_parser_verification_ready" in check_keys
+ assert "post_receipt_parser_blocks_execution" in check_keys
+ assert "receipt_closeout_preview_not_executed" in check_keys
+ assert "no_apply_enforcement_verification_bound" in check_keys
+ assert "no_apply_enforcement_blocks_endpoint_sql_db_write" in check_keys
+ assert "result_parser_and_receipt_validation_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bindings_carried_forward" in check_keys
+ assert "runner_execution_receipt_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout_waits_without_ready_parser_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ ]
+ enforcement_closeout = closeout[
+ "controlled_dry_run_no_apply_enforcement_closeout"
+ ]
+ final_guard = enforcement_closeout["final_dry_run_executor_guard"]
+ contract = closeout[
+ "controlled_dry_run_no_apply_enforcement_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_no_apply_enforcement_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_POST_RECEIPT_PARSER_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["final_dry_run_executor_guard_count"] == 1
+ assert closeout["summary"]["final_dry_run_executor_guard_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["no_apply_enforcement_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-no-apply-enforcement-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_final_executor_guard_closeout"
+ ]
+ is False
+ )
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert enforcement_closeout["authorization_material_type"] == (
+ "controlled_dry_run_no_apply_enforcement_closeout"
+ )
+ assert enforcement_closeout["no_apply_enforcement_closeout_field_count"] == 12
+ assert enforcement_closeout["no_apply_enforcement_closeout_acceptance_gate_count"] == 10
+ assert enforcement_closeout["no_apply_enforcement_closeout_only"] is True
+ assert enforcement_closeout["final_dry_run_executor_guard_only"] is True
+ assert enforcement_closeout["dry_run_executor_invocation_allowed"] is False
+ assert enforcement_closeout["endpoint_execution_allowed"] is False
+ assert enforcement_closeout["sql_execution_allowed"] is False
+ assert enforcement_closeout["database_write_allowed"] is False
+ assert enforcement_closeout["database_apply_authorized"] is False
+ assert enforcement_closeout["signs_database_apply_authorization"] is False
+ assert enforcement_closeout["executes_database_apply"] is False
+ assert enforcement_closeout["executes_endpoint_in_preview"] is False
+ assert enforcement_closeout["executes_sql_in_preview"] is False
+ assert enforcement_closeout["writes_database_in_preview"] is False
+ assert final_guard["guard_status"] == "final_dry_run_executor_guard_preview_ready"
+ assert final_guard["dry_run_executor_invocation_allowed"] is False
+ assert final_guard["endpoint_execution_allowed"] is False
+ assert final_guard["sql_execution_allowed"] is False
+ assert final_guard["database_write_allowed"] is False
+ assert final_guard["database_apply_authorized"] is False
+ assert final_guard["executes_database_apply"] is False
+ assert final_guard["executes_endpoint"] is False
+ assert final_guard["executes_sql"] is False
+ assert final_guard["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "post_receipt_parser_closeout_ready" in check_keys
+ assert "no_apply_enforcement_verification_ready" in check_keys
+ assert "final_dry_run_executor_guard_bound" in check_keys
+ assert "final_executor_guard_blocks_execution" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout_ready_after_fake_fetch_but_executor_guard_does_not_invoke():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ ]
+ enforcement_closeout = closeout[
+ "controlled_dry_run_no_apply_enforcement_closeout"
+ ]
+ enforcement = enforcement_closeout["no_apply_enforcement_verification"]
+ final_guard = enforcement_closeout["final_dry_run_executor_guard"]
+ contract = closeout[
+ "controlled_dry_run_no_apply_enforcement_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_no_apply_enforcement_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_NO_APPLY_ENFORCEMENT_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["final_dry_run_executor_guard_count"] == 1
+ assert closeout["summary"]["final_dry_run_executor_guard_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_final_executor_guard_closeout"
+ ]
+ is True
+ )
+ assert future["no_apply_enforcement_closeout_ready"] is True
+ assert future["final_dry_run_executor_guard_bound"] is True
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert enforcement_closeout["ready_for_future_database_apply_controlled_dry_run_no_apply_enforcement_closeout"] is True
+ assert enforcement_closeout["no_apply_enforcement_closeout_only"] is True
+ assert enforcement_closeout["final_dry_run_executor_guard_only"] is True
+ assert enforcement_closeout["dry_run_executor_invocation_allowed"] is False
+ assert enforcement_closeout["endpoint_execution_allowed"] is False
+ assert enforcement_closeout["sql_execution_allowed"] is False
+ assert enforcement_closeout["database_write_allowed"] is False
+ assert enforcement_closeout["database_apply_authorized"] is False
+ assert enforcement_closeout["executes_database_apply"] is False
+ assert enforcement_closeout["executes_endpoint_in_preview"] is False
+ assert enforcement_closeout["executes_sql_in_preview"] is False
+ assert enforcement_closeout["writes_database_in_preview"] is False
+ assert enforcement["enforcement_status"] == "no_apply_enforcement_preview_ready"
+ assert enforcement["endpoint_execution_allowed"] is False
+ assert enforcement["sql_execution_allowed"] is False
+ assert enforcement["database_write_allowed"] is False
+ assert enforcement["database_apply_authorized"] is False
+ assert enforcement["executes_endpoint"] is False
+ assert enforcement["executes_sql"] is False
+ assert enforcement["writes_database"] is False
+ assert final_guard["guard_id"] == future["final_dry_run_executor_guard_id"]
+ assert final_guard["guard_status"] == "final_dry_run_executor_guard_preview_ready"
+ assert final_guard["dry_run_executor_invocation_allowed"] is False
+ assert final_guard["stdout_capture_allowed"] is False
+ assert final_guard["stderr_capture_allowed"] is False
+ assert final_guard["database_apply_authorized"] is False
+ assert final_guard["executes_database_apply"] is False
+ assert final_guard["executes_endpoint"] is False
+ assert final_guard["executes_sql"] is False
+ assert final_guard["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "post_receipt_parser_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "no_apply_enforcement_verification_ready" in check_keys
+ assert "no_apply_blocks_endpoint_sql_db_write" in check_keys
+ assert "final_dry_run_executor_guard_bound" in check_keys
+ assert "final_executor_guard_blocks_execution" in check_keys
+ assert "parser_and_receipt_preview_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bindings_carried_forward" in check_keys
+ assert "post_receipt_parser_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout_waits_without_ready_no_apply_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ ]
+ guard_closeout = closeout[
+ "controlled_dry_run_final_executor_guard_closeout"
+ ]
+ replay = guard_closeout["pre_apply_replay_verifier"]
+ final_guard = guard_closeout["final_dry_run_executor_guard"]
+ contract = closeout[
+ "controlled_dry_run_final_executor_guard_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_final_executor_guard_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_NO_APPLY_ENFORCEMENT_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["pre_apply_replay_verifier_count"] == 1
+ assert closeout["summary"]["pre_apply_replay_verifier_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["final_executor_guard_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-final-executor-guard-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_pre_apply_replay_closeout"
+ ]
+ is False
+ )
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert guard_closeout["authorization_material_type"] == (
+ "controlled_dry_run_final_executor_guard_closeout"
+ )
+ assert guard_closeout["final_executor_guard_closeout_field_count"] == 12
+ assert guard_closeout["final_executor_guard_closeout_acceptance_gate_count"] == 10
+ assert guard_closeout["final_executor_guard_closeout_only"] is True
+ assert guard_closeout["pre_apply_replay_verifier_only"] is True
+ assert guard_closeout["dry_run_executor_invocation_allowed"] is False
+ assert guard_closeout["endpoint_execution_allowed"] is False
+ assert guard_closeout["sql_execution_allowed"] is False
+ assert guard_closeout["database_write_allowed"] is False
+ assert guard_closeout["database_apply_authorized"] is False
+ assert guard_closeout["executes_database_apply"] is False
+ assert guard_closeout["executes_endpoint_in_preview"] is False
+ assert guard_closeout["executes_sql_in_preview"] is False
+ assert guard_closeout["writes_database_in_preview"] is False
+ assert final_guard["guard_status"] == "final_dry_run_executor_guard_preview_ready"
+ assert final_guard["dry_run_executor_invocation_allowed"] is False
+ assert replay["verifier_status"] == "pre_apply_replay_verifier_preview_ready"
+ assert replay["replay_mode"] == "pre_apply_replay_preview_only"
+ assert replay["dry_run_executor_invocation_allowed"] is False
+ assert replay["endpoint_execution_allowed"] is False
+ assert replay["sql_execution_allowed"] is False
+ assert replay["database_write_allowed"] is False
+ assert replay["database_apply_authorized"] is False
+ assert replay["executes_database_apply"] is False
+ assert replay["executes_endpoint"] is False
+ assert replay["executes_sql"] is False
+ assert replay["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "no_apply_enforcement_closeout_ready" in check_keys
+ assert "final_dry_run_executor_guard_ready" in check_keys
+ assert "pre_apply_replay_verifier_bound" in check_keys
+ assert "pre_apply_replay_verifier_preview_only" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout_ready_after_fake_fetch_but_replay_is_preview_only():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ ]
+ guard_closeout = closeout[
+ "controlled_dry_run_final_executor_guard_closeout"
+ ]
+ replay = guard_closeout["pre_apply_replay_verifier"]
+ final_guard = guard_closeout["final_dry_run_executor_guard"]
+ contract = closeout[
+ "controlled_dry_run_final_executor_guard_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_final_executor_guard_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_FINAL_EXECUTOR_GUARD_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["pre_apply_replay_verifier_count"] == 1
+ assert closeout["summary"]["pre_apply_replay_verifier_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_pre_apply_replay_closeout"
+ ]
+ is True
+ )
+ assert future["final_executor_guard_closeout_ready"] is True
+ assert future["pre_apply_replay_verifier_bound"] is True
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert guard_closeout["ready_for_future_database_apply_controlled_dry_run_final_executor_guard_closeout"] is True
+ assert guard_closeout["final_executor_guard_closeout_only"] is True
+ assert guard_closeout["pre_apply_replay_verifier_only"] is True
+ assert guard_closeout["dry_run_executor_invocation_allowed"] is False
+ assert guard_closeout["endpoint_execution_allowed"] is False
+ assert guard_closeout["sql_execution_allowed"] is False
+ assert guard_closeout["database_write_allowed"] is False
+ assert guard_closeout["database_apply_authorized"] is False
+ assert guard_closeout["executes_database_apply"] is False
+ assert guard_closeout["executes_endpoint_in_preview"] is False
+ assert guard_closeout["executes_sql_in_preview"] is False
+ assert guard_closeout["writes_database_in_preview"] is False
+ assert final_guard["guard_status"] == "final_dry_run_executor_guard_preview_ready"
+ assert final_guard["dry_run_executor_invocation_allowed"] is False
+ assert replay["verification_id"] == future["pre_apply_replay_verifier_id"]
+ assert replay["replay_mode"] == "pre_apply_replay_preview_only"
+ assert replay["dry_run_executor_invocation_allowed"] is False
+ assert replay["stdout_capture_allowed"] is False
+ assert replay["stderr_capture_allowed"] is False
+ assert replay["database_apply_authorized"] is False
+ assert replay["executes_database_apply"] is False
+ assert replay["executes_endpoint"] is False
+ assert replay["executes_sql"] is False
+ assert replay["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["writes_database"] is False
+ assert "no_apply_enforcement_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "final_dry_run_executor_guard_ready" in check_keys
+ assert "final_executor_guard_blocks_invocation" in check_keys
+ assert "pre_apply_replay_verifier_bound" in check_keys
+ assert "pre_apply_replay_verifier_preview_only" in check_keys
+ assert "no_apply_enforcement_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bindings_carried_forward" in check_keys
+ assert "no_apply_enforcement_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout_waits_without_ready_final_executor_guard_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ ]
+ replay_closeout = closeout[
+ "controlled_dry_run_pre_apply_replay_closeout"
+ ]
+ readiness = replay_closeout["apply_executor_readiness_contract"]
+ contract = closeout[
+ "controlled_dry_run_pre_apply_replay_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_pre_apply_replay_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_FINAL_EXECUTOR_GUARD_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["apply_executor_readiness_contract_count"] == 1
+ assert closeout["summary"]["apply_executor_readiness_contract_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["pre_apply_replay_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-pre-apply-replay-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_apply_executor_readiness_closeout"
+ ]
+ is False
+ )
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert replay_closeout["authorization_material_type"] == (
+ "controlled_dry_run_pre_apply_replay_closeout"
+ )
+ assert replay_closeout["pre_apply_replay_closeout_field_count"] == 12
+ assert replay_closeout["pre_apply_replay_closeout_acceptance_gate_count"] == 10
+ assert replay_closeout["pre_apply_replay_closeout_only"] is True
+ assert replay_closeout["apply_executor_readiness_contract_only"] is True
+ assert replay_closeout["dry_run_executor_invocation_allowed"] is False
+ assert replay_closeout["endpoint_execution_allowed"] is False
+ assert replay_closeout["sql_execution_allowed"] is False
+ assert replay_closeout["database_write_allowed"] is False
+ assert replay_closeout["database_apply_authorized"] is False
+ assert replay_closeout["executes_database_apply"] is False
+ assert replay_closeout["executes_endpoint_in_preview"] is False
+ assert replay_closeout["executes_sql_in_preview"] is False
+ assert replay_closeout["writes_database_in_preview"] is False
+ assert readiness["readiness_status"] == "apply_executor_readiness_contract_preview_ready"
+ assert readiness["readiness_mode"] == "apply_executor_readiness_contract_preview_only"
+ assert readiness["dry_run_executor_invocation_allowed"] is False
+ assert readiness["endpoint_execution_allowed"] is False
+ assert readiness["sql_execution_allowed"] is False
+ assert readiness["database_write_allowed"] is False
+ assert readiness["database_apply_authorized"] is False
+ assert readiness["executes_database_apply"] is False
+ assert readiness["executes_endpoint"] is False
+ assert readiness["executes_sql"] is False
+ assert readiness["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["writes_database"] is False
+ assert "final_executor_guard_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "pre_apply_replay_verifier_ready" in check_keys
+ assert "pre_apply_replay_preview_only" in check_keys
+ assert "apply_executor_readiness_contract_bound" in check_keys
+ assert "apply_executor_readiness_contract_blocks_apply" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout_ready_after_fake_fetch_but_executor_readiness_contract_blocks_apply():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ ]
+ replay_closeout = closeout[
+ "controlled_dry_run_pre_apply_replay_closeout"
+ ]
+ readiness = replay_closeout["apply_executor_readiness_contract"]
+ replay = replay_closeout["pre_apply_replay_verifier"]
+ contract = closeout[
+ "controlled_dry_run_pre_apply_replay_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_pre_apply_replay_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_PRE_APPLY_REPLAY_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["apply_executor_readiness_contract_count"] == 1
+ assert closeout["summary"]["apply_executor_readiness_contract_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_apply_executor_readiness_closeout"
+ ]
+ is True
+ )
+ assert future["pre_apply_replay_closeout_ready"] is True
+ assert future["apply_executor_readiness_contract_bound"] is True
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert replay_closeout["ready_for_future_database_apply_controlled_dry_run_pre_apply_replay_closeout"] is True
+ assert replay_closeout["pre_apply_replay_closeout_only"] is True
+ assert replay_closeout["apply_executor_readiness_contract_only"] is True
+ assert replay_closeout["dry_run_executor_invocation_allowed"] is False
+ assert replay_closeout["endpoint_execution_allowed"] is False
+ assert replay_closeout["sql_execution_allowed"] is False
+ assert replay_closeout["database_write_allowed"] is False
+ assert replay_closeout["database_apply_authorized"] is False
+ assert replay_closeout["executes_database_apply"] is False
+ assert replay_closeout["executes_endpoint_in_preview"] is False
+ assert replay_closeout["executes_sql_in_preview"] is False
+ assert replay_closeout["writes_database_in_preview"] is False
+ assert readiness["contract_id"] == future["apply_executor_readiness_contract_id"]
+ assert readiness["readiness_status"] == "apply_executor_readiness_contract_preview_ready"
+ assert readiness["readiness_mode"] == "apply_executor_readiness_contract_preview_only"
+ assert readiness["source_pre_apply_replay_verifier_id"] == replay["verification_id"]
+ assert readiness["apply_executor_readiness_contract_field_count"] == 12
+ assert readiness["dry_run_executor_invocation_allowed"] is False
+ assert readiness["endpoint_execution_allowed"] is False
+ assert readiness["sql_execution_allowed"] is False
+ assert readiness["database_write_allowed"] is False
+ assert readiness["ready_for_database_apply_now"] is False
+ assert readiness["database_apply_authorized"] is False
+ assert readiness["executes_database_apply"] is False
+ assert readiness["executes_endpoint"] is False
+ assert readiness["executes_sql"] is False
+ assert readiness["writes_database"] is False
+ assert replay["replay_mode"] == "pre_apply_replay_preview_only"
+ assert replay["dry_run_executor_invocation_allowed"] is False
+ assert replay["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["writes_database"] is False
+ assert "final_executor_guard_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "pre_apply_replay_verifier_ready" in check_keys
+ assert "pre_apply_replay_preview_only" in check_keys
+ assert "apply_executor_readiness_contract_bound" in check_keys
+ assert "apply_executor_readiness_contract_blocks_apply" in check_keys
+ assert "final_guard_and_no_apply_enforcement_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "final_executor_guard_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout_waits_without_ready_pre_apply_replay_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ ]
+ readiness_closeout = closeout[
+ "controlled_dry_run_apply_executor_readiness_closeout"
+ ]
+ receipt = readiness_closeout["dry_run_invocation_readiness_receipt"]
+ contract = closeout[
+ "controlled_dry_run_apply_executor_readiness_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_apply_executor_readiness_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_PRE_APPLY_REPLAY_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["dry_run_invocation_readiness_receipt_count"] == 1
+ assert closeout["summary"]["dry_run_invocation_readiness_receipt_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["apply_executor_readiness_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-apply-executor-readiness-closeout-"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_invocation_receipt_closeout"
+ ]
+ is False
+ )
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert readiness_closeout["authorization_material_type"] == (
+ "controlled_dry_run_apply_executor_readiness_closeout"
+ )
+ assert readiness_closeout["apply_executor_readiness_closeout_field_count"] == 12
+ assert readiness_closeout["apply_executor_readiness_closeout_acceptance_gate_count"] == 10
+ assert readiness_closeout["apply_executor_readiness_closeout_only"] is True
+ assert readiness_closeout["dry_run_invocation_readiness_receipt_only"] is True
+ assert readiness_closeout["dry_run_executor_invocation_allowed"] is False
+ assert readiness_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert readiness_closeout["endpoint_execution_allowed"] is False
+ assert readiness_closeout["sql_execution_allowed"] is False
+ assert readiness_closeout["database_write_allowed"] is False
+ assert readiness_closeout["database_apply_authorized"] is False
+ assert readiness_closeout["executes_database_apply"] is False
+ assert readiness_closeout["executes_endpoint_in_preview"] is False
+ assert readiness_closeout["executes_sql_in_preview"] is False
+ assert readiness_closeout["writes_database_in_preview"] is False
+ assert receipt["receipt_status"] == "dry_run_invocation_readiness_receipt_preview_ready"
+ assert receipt["receipt_mode"] == "dry_run_invocation_readiness_preview_only"
+ assert receipt["dry_run_executor_invocation_allowed"] is False
+ assert receipt["ready_for_dry_run_executor_invocation_now"] is False
+ assert receipt["endpoint_execution_allowed"] is False
+ assert receipt["sql_execution_allowed"] is False
+ assert receipt["database_write_allowed"] is False
+ assert receipt["database_apply_authorized"] is False
+ assert receipt["executes_database_apply"] is False
+ assert receipt["executes_endpoint"] is False
+ assert receipt["executes_sql"] is False
+ assert receipt["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "pre_apply_replay_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "apply_executor_readiness_contract_ready" in check_keys
+ assert "apply_executor_readiness_contract_blocks_invocation" in check_keys
+ assert "dry_run_invocation_readiness_receipt_bound" in check_keys
+ assert "dry_run_invocation_readiness_receipt_no_execute" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout_ready_after_fake_fetch_but_invocation_receipt_is_preview_only():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ ]
+ readiness_closeout = closeout[
+ "controlled_dry_run_apply_executor_readiness_closeout"
+ ]
+ receipt = readiness_closeout["dry_run_invocation_readiness_receipt"]
+ readiness = readiness_closeout["apply_executor_readiness_contract"]
+ contract = closeout[
+ "controlled_dry_run_apply_executor_readiness_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_apply_executor_readiness_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_APPLY_EXECUTOR_READINESS_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["dry_run_invocation_readiness_receipt_count"] == 1
+ assert closeout["summary"]["dry_run_invocation_readiness_receipt_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_invocation_receipt_closeout"
+ ]
+ is True
+ )
+ assert future["apply_executor_readiness_closeout_ready"] is True
+ assert future["dry_run_invocation_readiness_receipt_bound"] is True
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert readiness_closeout["ready_for_future_database_apply_controlled_dry_run_apply_executor_readiness_closeout"] is True
+ assert readiness_closeout["apply_executor_readiness_closeout_only"] is True
+ assert readiness_closeout["dry_run_invocation_readiness_receipt_only"] is True
+ assert readiness_closeout["dry_run_executor_invocation_allowed"] is False
+ assert readiness_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert readiness_closeout["endpoint_execution_allowed"] is False
+ assert readiness_closeout["sql_execution_allowed"] is False
+ assert readiness_closeout["database_write_allowed"] is False
+ assert readiness_closeout["database_apply_authorized"] is False
+ assert readiness_closeout["executes_database_apply"] is False
+ assert readiness_closeout["executes_endpoint_in_preview"] is False
+ assert readiness_closeout["executes_sql_in_preview"] is False
+ assert readiness_closeout["writes_database_in_preview"] is False
+ assert receipt["receipt_id"] == future["dry_run_invocation_readiness_receipt_id"]
+ assert receipt["receipt_status"] == "dry_run_invocation_readiness_receipt_preview_ready"
+ assert receipt["receipt_mode"] == "dry_run_invocation_readiness_preview_only"
+ assert receipt["dry_run_invocation_readiness_receipt_field_count"] == 12
+ assert receipt["source_apply_executor_readiness_contract_id"] == readiness["contract_id"]
+ assert receipt["dry_run_executor_invocation_allowed"] is False
+ assert receipt["ready_for_dry_run_executor_invocation_now"] is False
+ assert receipt["endpoint_execution_allowed"] is False
+ assert receipt["sql_execution_allowed"] is False
+ assert receipt["database_write_allowed"] is False
+ assert receipt["ready_for_database_apply_now"] is False
+ assert receipt["database_apply_authorized"] is False
+ assert receipt["executes_database_apply"] is False
+ assert receipt["executes_endpoint"] is False
+ assert receipt["executes_sql"] is False
+ assert receipt["writes_database"] is False
+ assert readiness["readiness_mode"] == "apply_executor_readiness_contract_preview_only"
+ assert readiness["dry_run_executor_invocation_allowed"] is False
+ assert readiness["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "pre_apply_replay_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "apply_executor_readiness_contract_ready" in check_keys
+ assert "apply_executor_readiness_contract_blocks_invocation" in check_keys
+ assert "dry_run_invocation_readiness_receipt_bound" in check_keys
+ assert "dry_run_invocation_readiness_receipt_no_execute" in check_keys
+ assert "pre_apply_replay_and_final_guard_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "pre_apply_replay_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout_waits_without_ready_apply_executor_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_no_write_invocation_package"
+ ]
+ invocation_closeout = closeout[
+ "controlled_dry_run_invocation_receipt_closeout"
+ ]
+ package = invocation_closeout["no_write_invocation_package"]
+ contract = closeout[
+ "controlled_dry_run_invocation_receipt_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_invocation_receipt_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_APPLY_EXECUTOR_READINESS_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["no_write_invocation_package_count"] == 1
+ assert closeout["summary"]["no_write_invocation_package_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["invocation_receipt_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-invocation-receipt-closeout-"
+ )
+ assert future["no_write_invocation_package_id"].endswith(
+ "-no-write-invocation-package"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_no_write_invocation_package"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_no_write_invocation_package_closeout"
+ ]
+ is False
+ )
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert invocation_closeout["authorization_material_type"] == (
+ "controlled_dry_run_invocation_receipt_closeout"
+ )
+ assert invocation_closeout["invocation_receipt_closeout_only"] is True
+ assert invocation_closeout["no_write_invocation_package_only"] is True
+ assert invocation_closeout["dry_run_executor_invocation_allowed"] is False
+ assert invocation_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert invocation_closeout["endpoint_execution_allowed"] is False
+ assert invocation_closeout["sql_execution_allowed"] is False
+ assert invocation_closeout["database_write_allowed"] is False
+ assert invocation_closeout["database_apply_authorized"] is False
+ assert invocation_closeout["executes_database_apply"] is False
+ assert invocation_closeout["executes_endpoint_in_preview"] is False
+ assert invocation_closeout["executes_sql_in_preview"] is False
+ assert invocation_closeout["writes_database_in_preview"] is False
+ assert package["package_status"] == "no_write_invocation_package_preview_ready"
+ assert package["package_mode"] == "no_write_invocation_package_preview_only"
+ assert package["no_write_invocation_package_field_count"] == 12
+ assert package["dry_run_executor_invocation_allowed"] is False
+ assert package["ready_for_no_write_dry_run_invocation_package_now"] is False
+ assert package["ready_for_actual_dry_run_execution_now"] is False
+ assert package["endpoint_execution_allowed"] is False
+ assert package["sql_execution_allowed"] is False
+ assert package["database_write_allowed"] is False
+ assert package["database_apply_authorized"] is False
+ assert package["executes_database_apply"] is False
+ assert package["executes_endpoint"] is False
+ assert package["executes_sql"] is False
+ assert package["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_no_write_invocation_package"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "apply_executor_readiness_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "dry_run_invocation_readiness_receipt_ready" in check_keys
+ assert "dry_run_invocation_readiness_receipt_no_execute" in check_keys
+ assert "no_write_invocation_package_bound" in check_keys
+ assert "no_write_invocation_package_blocks_execution" in check_keys
+ assert "apply_executor_readiness_and_replay_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "apply_executor_readiness_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout_ready_after_fake_fetch_but_package_is_no_write():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_no_write_invocation_package"
+ ]
+ invocation_closeout = closeout[
+ "controlled_dry_run_invocation_receipt_closeout"
+ ]
+ package = invocation_closeout["no_write_invocation_package"]
+ receipt = invocation_closeout["dry_run_invocation_readiness_receipt"]
+ readiness_closeout = invocation_closeout["apply_executor_readiness_closeout"]
+ contract = closeout[
+ "controlled_dry_run_invocation_receipt_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_invocation_receipt_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_INVOCATION_RECEIPT_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["no_write_invocation_package_count"] == 1
+ assert closeout["summary"]["no_write_invocation_package_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_no_write_invocation_package"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_no_write_invocation_package_closeout"
+ ]
+ is True
+ )
+ assert future["invocation_receipt_closeout_ready"] is True
+ assert future["no_write_invocation_package_bound"] is True
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert invocation_closeout["ready_for_future_database_apply_controlled_dry_run_invocation_receipt_closeout"] is True
+ assert invocation_closeout["invocation_receipt_closeout_field_count"] == 12
+ assert invocation_closeout["invocation_receipt_closeout_acceptance_gate_count"] == 10
+ assert invocation_closeout["no_write_invocation_package_count"] == 1
+ assert invocation_closeout["no_write_invocation_package_field_count"] == 12
+ assert invocation_closeout["dry_run_executor_invocation_allowed"] is False
+ assert invocation_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert invocation_closeout["endpoint_execution_allowed"] is False
+ assert invocation_closeout["sql_execution_allowed"] is False
+ assert invocation_closeout["database_write_allowed"] is False
+ assert invocation_closeout["database_apply_authorized"] is False
+ assert invocation_closeout["executes_database_apply"] is False
+ assert invocation_closeout["executes_endpoint_in_preview"] is False
+ assert invocation_closeout["executes_sql_in_preview"] is False
+ assert invocation_closeout["writes_database_in_preview"] is False
+ assert package["package_id"] == future["no_write_invocation_package_id"]
+ assert package["source_invocation_receipt_closeout_id"] == future["invocation_receipt_closeout_id"]
+ assert package["source_dry_run_invocation_readiness_receipt_id"] == receipt["receipt_id"]
+ assert package["source_apply_executor_readiness_closeout_id"] == readiness_closeout["apply_executor_readiness_closeout_id"]
+ assert package["required_command_shape_hash"] == receipt["required_command_shape_hash"]
+ assert package["package_status"] == "no_write_invocation_package_preview_ready"
+ assert package["package_mode"] == "no_write_invocation_package_preview_only"
+ assert package["dry_run_executor_invocation_allowed"] is False
+ assert package["ready_for_no_write_dry_run_invocation_package_now"] is False
+ assert package["ready_for_actual_dry_run_execution_now"] is False
+ assert package["endpoint_execution_allowed"] is False
+ assert package["sql_execution_allowed"] is False
+ assert package["database_write_allowed"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["database_apply_authorized"] is False
+ assert package["issues_database_apply_authorization"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert package["executes_authorization_evidence"] is False
+ assert package["executes_database_apply"] is False
+ assert package["executes_endpoint"] is False
+ assert package["executes_sql"] is False
+ assert package["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_no_write_invocation_package"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "apply_executor_readiness_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "dry_run_invocation_readiness_receipt_ready" in check_keys
+ assert "dry_run_invocation_readiness_receipt_no_execute" in check_keys
+ assert "no_write_invocation_package_bound" in check_keys
+ assert "no_write_invocation_package_blocks_execution" in check_keys
+ assert "apply_executor_readiness_and_replay_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "apply_executor_readiness_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout_waits_without_ready_invocation_receipt_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_execution_preflight_guard"
+ ]
+ package_closeout = closeout[
+ "controlled_dry_run_no_write_invocation_package_closeout"
+ ]
+ guard = package_closeout["execution_preflight_guard"]
+ package = package_closeout["no_write_invocation_package"]
+ contract = closeout[
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_no_write_invocation_package_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_INVOCATION_RECEIPT_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["execution_preflight_guard_count"] == 1
+ assert closeout["summary"]["execution_preflight_guard_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["no_write_invocation_package_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-no-write-invocation-package-closeout-"
+ )
+ assert future["execution_preflight_guard_id"].endswith(
+ "-execution-preflight-guard"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_execution_preflight_guard"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_execution_preflight_guard_closeout"
+ ]
+ is False
+ )
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert package_closeout["authorization_material_type"] == (
+ "controlled_dry_run_no_write_invocation_package_closeout"
+ )
+ assert package_closeout["no_write_invocation_package_closeout_only"] is True
+ assert package_closeout["execution_preflight_guard_only"] is True
+ assert package_closeout["dry_run_executor_invocation_allowed"] is False
+ assert package_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert package_closeout["endpoint_execution_allowed"] is False
+ assert package_closeout["sql_execution_allowed"] is False
+ assert package_closeout["database_write_allowed"] is False
+ assert package_closeout["database_apply_authorized"] is False
+ assert package_closeout["executes_database_apply"] is False
+ assert package_closeout["executes_endpoint_in_preview"] is False
+ assert package_closeout["executes_sql_in_preview"] is False
+ assert package_closeout["writes_database_in_preview"] is False
+ assert package["package_mode"] == "no_write_invocation_package_preview_only"
+ assert package["dry_run_executor_invocation_allowed"] is False
+ assert package["executes_database_apply"] is False
+ assert guard["guard_status"] == "execution_preflight_guard_preview_ready"
+ assert guard["guard_mode"] == "execution_preflight_guard_preview_only"
+ assert guard["execution_preflight_guard_field_count"] == 12
+ assert guard["dry_run_executor_invocation_allowed"] is False
+ assert guard["ready_for_execution_preflight_guard_now"] is False
+ assert guard["ready_for_dry_run_executor_invocation_now"] is False
+ assert guard["ready_for_actual_dry_run_execution_now"] is False
+ assert guard["endpoint_execution_allowed"] is False
+ assert guard["sql_execution_allowed"] is False
+ assert guard["database_write_allowed"] is False
+ assert guard["database_apply_authorized"] is False
+ assert guard["executes_database_apply"] is False
+ assert guard["executes_endpoint"] is False
+ assert guard["executes_sql"] is False
+ assert guard["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_execution_preflight_guard"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "invocation_receipt_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "no_write_invocation_package_ready" in check_keys
+ assert "no_write_invocation_package_no_execute" in check_keys
+ assert "execution_preflight_guard_bound" in check_keys
+ assert "execution_preflight_guard_blocks_execution" in check_keys
+ assert "invocation_receipt_and_apply_readiness_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "invocation_receipt_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout_ready_after_fake_fetch_but_guard_is_preview_only():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_execution_preflight_guard"
+ ]
+ package_closeout = closeout[
+ "controlled_dry_run_no_write_invocation_package_closeout"
+ ]
+ guard = package_closeout["execution_preflight_guard"]
+ package = package_closeout["no_write_invocation_package"]
+ invocation_closeout = package_closeout["invocation_receipt_closeout"]
+ contract = closeout[
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_no_write_invocation_package_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_INVOCATION_PACKAGE_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["execution_preflight_guard_count"] == 1
+ assert closeout["summary"]["execution_preflight_guard_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_execution_preflight_guard"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_execution_preflight_guard_closeout"
+ ]
+ is True
+ )
+ assert future["no_write_invocation_package_closeout_ready"] is True
+ assert future["execution_preflight_guard_bound"] is True
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert (
+ package_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_no_write_invocation_package_closeout"
+ ]
+ is True
+ )
+ assert package_closeout["no_write_invocation_package_closeout_field_count"] == 12
+ assert package_closeout["no_write_invocation_package_closeout_acceptance_gate_count"] == 10
+ assert package_closeout["execution_preflight_guard_count"] == 1
+ assert package_closeout["execution_preflight_guard_field_count"] == 12
+ assert package_closeout["dry_run_executor_invocation_allowed"] is False
+ assert package_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert package_closeout["endpoint_execution_allowed"] is False
+ assert package_closeout["sql_execution_allowed"] is False
+ assert package_closeout["database_write_allowed"] is False
+ assert package_closeout["database_apply_authorized"] is False
+ assert package_closeout["executes_database_apply"] is False
+ assert package_closeout["executes_endpoint_in_preview"] is False
+ assert package_closeout["executes_sql_in_preview"] is False
+ assert package_closeout["writes_database_in_preview"] is False
+ assert guard["guard_id"] == future["execution_preflight_guard_id"]
+ assert guard["source_no_write_invocation_package_closeout_id"] == future["no_write_invocation_package_closeout_id"]
+ assert guard["source_no_write_invocation_package_id"] == package["package_id"]
+ assert guard["source_invocation_receipt_closeout_id"] == invocation_closeout["invocation_receipt_closeout_id"]
+ assert guard["required_command_shape_hash"] == package["required_command_shape_hash"]
+ assert guard["guard_status"] == "execution_preflight_guard_preview_ready"
+ assert guard["guard_mode"] == "execution_preflight_guard_preview_only"
+ assert guard["dry_run_executor_invocation_allowed"] is False
+ assert guard["ready_for_execution_preflight_guard_now"] is False
+ assert guard["ready_for_dry_run_executor_invocation_now"] is False
+ assert guard["ready_for_actual_dry_run_execution_now"] is False
+ assert guard["endpoint_execution_allowed"] is False
+ assert guard["sql_execution_allowed"] is False
+ assert guard["database_write_allowed"] is False
+ assert guard["ready_for_database_apply_now"] is False
+ assert guard["database_apply_authorized"] is False
+ assert guard["issues_database_apply_authorization"] is False
+ assert guard["signs_database_apply_authorization"] is False
+ assert guard["executes_authorization_evidence"] is False
+ assert guard["executes_database_apply"] is False
+ assert guard["executes_endpoint"] is False
+ assert guard["executes_sql"] is False
+ assert guard["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_execution_preflight_guard"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "invocation_receipt_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "no_write_invocation_package_ready" in check_keys
+ assert "no_write_invocation_package_no_execute" in check_keys
+ assert "execution_preflight_guard_bound" in check_keys
+ assert "execution_preflight_guard_blocks_execution" in check_keys
+ assert "invocation_receipt_and_apply_readiness_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "invocation_receipt_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout_waits_without_ready_package_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ ]
+ guard_closeout = closeout[
+ "controlled_dry_run_execution_preflight_guard_closeout"
+ ]
+ boundary = guard_closeout["runner_invocation_boundary"]
+ guard = guard_closeout["execution_preflight_guard"]
+ contract = closeout[
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_execution_preflight_guard_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_INVOCATION_PACKAGE_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["runner_invocation_boundary_count"] == 1
+ assert closeout["summary"]["runner_invocation_boundary_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["execution_preflight_guard_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-execution-preflight-guard-closeout-"
+ )
+ assert future["runner_invocation_boundary_id"].endswith(
+ "-runner-invocation-boundary"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_runner_invocation_boundary_closeout"
+ ]
+ is False
+ )
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert guard_closeout["authorization_material_type"] == (
+ "controlled_dry_run_execution_preflight_guard_closeout"
+ )
+ assert guard_closeout["execution_preflight_guard_closeout_only"] is True
+ assert guard_closeout["runner_invocation_boundary_only"] is True
+ assert guard_closeout["dry_run_executor_invocation_allowed"] is False
+ assert guard_closeout["runner_invocation_allowed"] is False
+ assert guard_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert guard_closeout["endpoint_execution_allowed"] is False
+ assert guard_closeout["sql_execution_allowed"] is False
+ assert guard_closeout["database_write_allowed"] is False
+ assert guard_closeout["database_apply_authorized"] is False
+ assert guard_closeout["executes_database_apply"] is False
+ assert guard_closeout["executes_endpoint_in_preview"] is False
+ assert guard_closeout["executes_sql_in_preview"] is False
+ assert guard_closeout["writes_database_in_preview"] is False
+ assert guard["guard_mode"] == "execution_preflight_guard_preview_only"
+ assert guard["dry_run_executor_invocation_allowed"] is False
+ assert guard["executes_database_apply"] is False
+ assert boundary["boundary_status"] == "runner_invocation_boundary_preview_ready"
+ assert boundary["boundary_mode"] == "runner_invocation_boundary_preview_only"
+ assert boundary["runner_invocation_boundary_field_count"] == 12
+ assert boundary["dry_run_executor_invocation_allowed"] is False
+ assert boundary["runner_invocation_allowed"] is False
+ assert boundary["ready_for_runner_invocation_boundary_now"] is False
+ assert boundary["ready_for_dry_run_executor_invocation_now"] is False
+ assert boundary["ready_for_actual_dry_run_execution_now"] is False
+ assert boundary["endpoint_execution_allowed"] is False
+ assert boundary["sql_execution_allowed"] is False
+ assert boundary["database_write_allowed"] is False
+ assert boundary["database_apply_authorized"] is False
+ assert boundary["executes_database_apply"] is False
+ assert boundary["executes_endpoint"] is False
+ assert boundary["executes_sql"] is False
+ assert boundary["writes_database"] is False
+ assert boundary["captures_stdout"] is False
+ assert boundary["captures_stderr"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "no_write_invocation_package_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "execution_preflight_guard_ready" in check_keys
+ assert "execution_preflight_guard_no_execute" in check_keys
+ assert "runner_invocation_boundary_bound" in check_keys
+ assert "runner_invocation_boundary_blocks_execution" in check_keys
+ assert "no_write_package_and_invocation_receipt_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "no_write_invocation_package_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout_ready_after_fake_fetch_but_boundary_blocks_runner():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ ]
+ guard_closeout = closeout[
+ "controlled_dry_run_execution_preflight_guard_closeout"
+ ]
+ boundary = guard_closeout["runner_invocation_boundary"]
+ guard = guard_closeout["execution_preflight_guard"]
+ package_closeout = guard_closeout["no_write_invocation_package_closeout"]
+ contract = closeout[
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_execution_preflight_guard_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PREFLIGHT_GUARD_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["runner_invocation_boundary_count"] == 1
+ assert closeout["summary"]["runner_invocation_boundary_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_runner_invocation_boundary_closeout"
+ ]
+ is True
+ )
+ assert future["execution_preflight_guard_closeout_ready"] is True
+ assert future["runner_invocation_boundary_bound"] is True
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert (
+ guard_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_execution_preflight_guard_closeout"
+ ]
+ is True
+ )
+ assert guard_closeout["execution_preflight_guard_closeout_field_count"] == 12
+ assert guard_closeout["execution_preflight_guard_closeout_acceptance_gate_count"] == 10
+ assert guard_closeout["runner_invocation_boundary_count"] == 1
+ assert guard_closeout["runner_invocation_boundary_field_count"] == 12
+ assert guard_closeout["dry_run_executor_invocation_allowed"] is False
+ assert guard_closeout["runner_invocation_allowed"] is False
+ assert guard_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert guard_closeout["endpoint_execution_allowed"] is False
+ assert guard_closeout["sql_execution_allowed"] is False
+ assert guard_closeout["database_write_allowed"] is False
+ assert guard_closeout["database_apply_authorized"] is False
+ assert guard_closeout["executes_database_apply"] is False
+ assert guard_closeout["executes_endpoint_in_preview"] is False
+ assert guard_closeout["executes_sql_in_preview"] is False
+ assert guard_closeout["writes_database_in_preview"] is False
+ assert boundary["boundary_id"] == future["runner_invocation_boundary_id"]
+ assert boundary["source_execution_preflight_guard_closeout_id"] == future["execution_preflight_guard_closeout_id"]
+ assert boundary["source_execution_preflight_guard_id"] == guard["guard_id"]
+ assert boundary["source_no_write_invocation_package_closeout_id"] == package_closeout["no_write_invocation_package_closeout_id"]
+ assert boundary["required_command_shape_hash"] == guard["required_command_shape_hash"]
+ assert boundary["boundary_status"] == "runner_invocation_boundary_preview_ready"
+ assert boundary["boundary_mode"] == "runner_invocation_boundary_preview_only"
+ assert boundary["dry_run_executor_invocation_allowed"] is False
+ assert boundary["runner_invocation_allowed"] is False
+ assert boundary["ready_for_runner_invocation_boundary_now"] is False
+ assert boundary["ready_for_dry_run_executor_invocation_now"] is False
+ assert boundary["ready_for_actual_dry_run_execution_now"] is False
+ assert boundary["endpoint_execution_allowed"] is False
+ assert boundary["sql_execution_allowed"] is False
+ assert boundary["database_write_allowed"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["database_apply_authorized"] is False
+ assert boundary["issues_database_apply_authorization"] is False
+ assert boundary["signs_database_apply_authorization"] is False
+ assert boundary["executes_authorization_evidence"] is False
+ assert boundary["executes_database_apply"] is False
+ assert boundary["executes_endpoint"] is False
+ assert boundary["executes_sql"] is False
+ assert boundary["writes_database"] is False
+ assert boundary["captures_stdout"] is False
+ assert boundary["captures_stderr"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "no_write_invocation_package_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "execution_preflight_guard_ready" in check_keys
+ assert "execution_preflight_guard_no_execute" in check_keys
+ assert "runner_invocation_boundary_bound" in check_keys
+ assert "runner_invocation_boundary_blocks_execution" in check_keys
+ assert "no_write_package_and_invocation_receipt_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "no_write_invocation_package_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout_waits_without_ready_guard_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ ]
+ boundary_closeout = closeout[
+ "controlled_dry_run_runner_invocation_boundary_closeout"
+ ]
+ handoff = boundary_closeout["no_execution_receipt_handoff"]
+ boundary = boundary_closeout["runner_invocation_boundary"]
+ contract = closeout[
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_runner_invocation_boundary_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_PREFLIGHT_GUARD_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["no_execution_receipt_handoff_count"] == 1
+ assert closeout["summary"]["no_execution_receipt_handoff_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["runner_invocation_boundary_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-runner-invocation-boundary-closeout-"
+ )
+ assert future["no_execution_receipt_handoff_id"].endswith(
+ "-no-execution-receipt-handoff"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_no_execution_receipt_handoff_closeout"
+ ]
+ is False
+ )
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert boundary_closeout["authorization_material_type"] == (
+ "controlled_dry_run_runner_invocation_boundary_closeout"
+ )
+ assert boundary_closeout["runner_invocation_boundary_closeout_only"] is True
+ assert boundary_closeout["no_execution_receipt_handoff_only"] is True
+ assert boundary_closeout["dry_run_executor_invocation_allowed"] is False
+ assert boundary_closeout["runner_invocation_allowed"] is False
+ assert boundary_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert boundary_closeout["endpoint_execution_allowed"] is False
+ assert boundary_closeout["sql_execution_allowed"] is False
+ assert boundary_closeout["database_write_allowed"] is False
+ assert boundary_closeout["database_apply_authorized"] is False
+ assert boundary_closeout["executes_database_apply"] is False
+ assert boundary_closeout["executes_endpoint_in_preview"] is False
+ assert boundary_closeout["executes_sql_in_preview"] is False
+ assert boundary_closeout["writes_database_in_preview"] is False
+ assert boundary_closeout["captures_stdout"] is False
+ assert boundary_closeout["captures_stderr"] is False
+ assert boundary["boundary_mode"] == "runner_invocation_boundary_preview_only"
+ assert boundary["dry_run_executor_invocation_allowed"] is False
+ assert boundary["runner_invocation_allowed"] is False
+ assert handoff["handoff_status"] == "no_execution_receipt_handoff_preview_ready"
+ assert handoff["handoff_mode"] == "no_execution_receipt_handoff_preview_only"
+ assert handoff["no_execution_receipt_handoff_field_count"] == 12
+ assert handoff["execution_receipt_present"] is False
+ assert handoff["execution_receipt_required"] is False
+ assert handoff["dry_run_executor_invocation_allowed"] is False
+ assert handoff["runner_invocation_allowed"] is False
+ assert handoff["ready_for_no_execution_receipt_handoff_now"] is False
+ assert handoff["ready_for_dry_run_executor_invocation_now"] is False
+ assert handoff["ready_for_actual_dry_run_execution_now"] is False
+ assert handoff["endpoint_execution_allowed"] is False
+ assert handoff["sql_execution_allowed"] is False
+ assert handoff["database_write_allowed"] is False
+ assert handoff["database_apply_authorized"] is False
+ assert handoff["executes_database_apply"] is False
+ assert handoff["executes_endpoint"] is False
+ assert handoff["executes_sql"] is False
+ assert handoff["writes_database"] is False
+ assert handoff["stdout_included"] is False
+ assert handoff["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "execution_preflight_guard_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "runner_invocation_boundary_ready" in check_keys
+ assert "runner_invocation_boundary_no_execute" in check_keys
+ assert "no_execution_receipt_handoff_bound" in check_keys
+ assert "no_execution_receipt_handoff_blocks_execution" in check_keys
+ assert "execution_preflight_guard_and_no_write_package_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "execution_preflight_guard_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout_ready_after_fake_fetch_but_handoff_is_no_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ ]
+ boundary_closeout = closeout[
+ "controlled_dry_run_runner_invocation_boundary_closeout"
+ ]
+ handoff = boundary_closeout["no_execution_receipt_handoff"]
+ boundary = boundary_closeout["runner_invocation_boundary"]
+ guard_closeout = boundary_closeout["execution_preflight_guard_closeout"]
+ contract = closeout[
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_runner_invocation_boundary_closeout_checks"
+ ]
+ ]
+ assert (
+ closeout["result"]
+ == "DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_INVOCATION_BOUNDARY_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["no_execution_receipt_handoff_count"] == 1
+ assert closeout["summary"]["no_execution_receipt_handoff_field_count"] == 12
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_no_execution_receipt_handoff_closeout"
+ ]
+ is True
+ )
+ assert future["runner_invocation_boundary_closeout_ready"] is True
+ assert future["no_execution_receipt_handoff_bound"] is True
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["dry_run_execution_performed"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert (
+ boundary_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_runner_invocation_boundary_closeout"
+ ]
+ is True
+ )
+ assert boundary_closeout["runner_invocation_boundary_closeout_field_count"] == 12
+ assert boundary_closeout["runner_invocation_boundary_closeout_acceptance_gate_count"] == 10
+ assert boundary_closeout["no_execution_receipt_handoff_count"] == 1
+ assert boundary_closeout["no_execution_receipt_handoff_field_count"] == 12
+ assert boundary_closeout["dry_run_executor_invocation_allowed"] is False
+ assert boundary_closeout["runner_invocation_allowed"] is False
+ assert boundary_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert boundary_closeout["endpoint_execution_allowed"] is False
+ assert boundary_closeout["sql_execution_allowed"] is False
+ assert boundary_closeout["database_write_allowed"] is False
+ assert boundary_closeout["database_apply_authorized"] is False
+ assert boundary_closeout["executes_database_apply"] is False
+ assert boundary_closeout["executes_endpoint_in_preview"] is False
+ assert boundary_closeout["executes_sql_in_preview"] is False
+ assert boundary_closeout["writes_database_in_preview"] is False
+ assert boundary_closeout["captures_stdout"] is False
+ assert boundary_closeout["captures_stderr"] is False
+ assert handoff["handoff_id"] == future["no_execution_receipt_handoff_id"]
+ assert handoff["source_runner_invocation_boundary_closeout_id"] == future["runner_invocation_boundary_closeout_id"]
+ assert handoff["source_runner_invocation_boundary_id"] == boundary["boundary_id"]
+ assert handoff["source_execution_preflight_guard_closeout_id"] == guard_closeout["execution_preflight_guard_closeout_id"]
+ assert handoff["required_command_shape_hash"] == boundary["required_command_shape_hash"]
+ assert handoff["handoff_status"] == "no_execution_receipt_handoff_preview_ready"
+ assert handoff["handoff_mode"] == "no_execution_receipt_handoff_preview_only"
+ assert handoff["execution_receipt_present"] is False
+ assert handoff["execution_receipt_required"] is False
+ assert handoff["dry_run_executor_invocation_allowed"] is False
+ assert handoff["runner_invocation_allowed"] is False
+ assert handoff["ready_for_no_execution_receipt_handoff_now"] is False
+ assert handoff["ready_for_dry_run_executor_invocation_now"] is False
+ assert handoff["ready_for_actual_dry_run_execution_now"] is False
+ assert handoff["endpoint_execution_allowed"] is False
+ assert handoff["sql_execution_allowed"] is False
+ assert handoff["database_write_allowed"] is False
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["database_apply_authorized"] is False
+ assert handoff["issues_database_apply_authorization"] is False
+ assert handoff["signs_database_apply_authorization"] is False
+ assert handoff["executes_authorization_evidence"] is False
+ assert handoff["executes_database_apply"] is False
+ assert handoff["executes_endpoint"] is False
+ assert handoff["executes_sql"] is False
+ assert handoff["writes_database"] is False
+ assert handoff["captures_stdout"] is False
+ assert handoff["captures_stderr"] is False
+ assert handoff["stdout_included"] is False
+ assert handoff["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "execution_preflight_guard_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "runner_invocation_boundary_ready" in check_keys
+ assert "runner_invocation_boundary_no_execute" in check_keys
+ assert "no_execution_receipt_handoff_bound" in check_keys
+ assert "no_execution_receipt_handoff_blocks_execution" in check_keys
+ assert "execution_preflight_guard_and_no_write_package_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "execution_preflight_guard_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout_waits_without_ready_boundary_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ ]
+ handoff_closeout = closeout[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout"
+ ]
+ proof = handoff_closeout["final_no_runner_execution_proof"]
+ contract = closeout[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RUNNER_INVOCATION_BOUNDARY_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["final_no_runner_execution_proof_count"] == 1
+ assert closeout["summary"]["final_no_runner_execution_proof_field_count"] == 12
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["no_execution_receipt_handoff_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout-"
+ )
+ assert future["final_no_runner_execution_proof_id"].endswith(
+ "-final-no-runner-execution-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_final_no_runner_execution_proof_closeout"
+ ]
+ is False
+ )
+ assert future["no_execution_receipt_handoff_closeout_ready"] is False
+ assert future["final_no_runner_execution_proof_bound"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert handoff_closeout["authorization_material_type"] == (
+ "controlled_dry_run_no_execution_receipt_handoff_closeout"
+ )
+ assert handoff_closeout["no_execution_receipt_handoff_closeout_only"] is True
+ assert handoff_closeout["final_no_runner_execution_proof_only"] is True
+ assert handoff_closeout["dry_run_executor_invoked"] is False
+ assert handoff_closeout["runner_invocation_performed"] is False
+ assert handoff_closeout["endpoint_executed"] is False
+ assert handoff_closeout["sql_executed"] is False
+ assert handoff_closeout["database_written"] is False
+ assert handoff_closeout["dry_run_executor_invocation_allowed"] is False
+ assert handoff_closeout["runner_invocation_allowed"] is False
+ assert handoff_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert handoff_closeout["endpoint_execution_allowed"] is False
+ assert handoff_closeout["sql_execution_allowed"] is False
+ assert handoff_closeout["database_write_allowed"] is False
+ assert handoff_closeout["database_apply_authorized"] is False
+ assert handoff_closeout["executes_database_apply"] is False
+ assert handoff_closeout["executes_endpoint_in_preview"] is False
+ assert handoff_closeout["executes_sql_in_preview"] is False
+ assert handoff_closeout["writes_database_in_preview"] is False
+ assert handoff_closeout["stdout_included"] is False
+ assert handoff_closeout["stderr_included"] is False
+ assert proof["proof_status"] == "final_no_runner_execution_proof_preview_ready"
+ assert proof["proof_mode"] == "final_no_runner_execution_proof_preview_only"
+ assert proof["final_no_runner_execution_proof_field_count"] == 12
+ assert proof["execution_receipt_present"] is False
+ assert proof["execution_receipt_required"] is False
+ assert proof["dry_run_executor_invoked"] is False
+ assert proof["runner_invocation_performed"] is False
+ assert proof["endpoint_executed"] is False
+ assert proof["sql_executed"] is False
+ assert proof["database_written"] is False
+ assert proof["dry_run_executor_invocation_allowed"] is False
+ assert proof["runner_invocation_allowed"] is False
+ assert proof["ready_for_dry_run_executor_invocation_now"] is False
+ assert proof["ready_for_actual_dry_run_execution_now"] is False
+ assert proof["endpoint_execution_allowed"] is False
+ assert proof["sql_execution_allowed"] is False
+ assert proof["database_write_allowed"] is False
+ assert proof["database_apply_authorized"] is False
+ assert proof["executes_database_apply"] is False
+ assert proof["executes_endpoint"] is False
+ assert proof["executes_sql"] is False
+ assert proof["writes_database"] is False
+ assert proof["stdout_included"] is False
+ assert proof["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "runner_invocation_boundary_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "no_execution_receipt_handoff_ready" in check_keys
+ assert "no_execution_receipt_handoff_no_execute" in check_keys
+ assert "final_no_runner_execution_proof_bound" in check_keys
+ assert "final_no_runner_execution_proof_blocks_execution" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "runner_invocation_boundary_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout_ready_after_fake_fetch_but_proof_is_no_runner_execution():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ ]
+ handoff_closeout = closeout[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout"
+ ]
+ proof = handoff_closeout["final_no_runner_execution_proof"]
+ handoff = handoff_closeout["no_execution_receipt_handoff"]
+ boundary_closeout = handoff_closeout["runner_invocation_boundary_closeout"]
+ contract = closeout[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_NO_EXECUTION_RECEIPT_HANDOFF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["final_no_runner_execution_proof_count"] == 1
+ assert closeout["summary"]["final_no_runner_execution_proof_field_count"] == 12
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_final_no_runner_execution_proof_closeout"
+ ]
+ is True
+ )
+ assert future["no_execution_receipt_handoff_closeout_ready"] is True
+ assert future["final_no_runner_execution_proof_bound"] is True
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert (
+ handoff_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_no_execution_receipt_handoff_closeout"
+ ]
+ is True
+ )
+ assert handoff_closeout["no_execution_receipt_handoff_closeout_field_count"] == 12
+ assert handoff_closeout["no_execution_receipt_handoff_closeout_acceptance_gate_count"] == 10
+ assert handoff_closeout["final_no_runner_execution_proof_count"] == 1
+ assert handoff_closeout["final_no_runner_execution_proof_field_count"] == 12
+ assert handoff_closeout["dry_run_executor_invoked"] is False
+ assert handoff_closeout["runner_invocation_performed"] is False
+ assert handoff_closeout["endpoint_executed"] is False
+ assert handoff_closeout["sql_executed"] is False
+ assert handoff_closeout["database_written"] is False
+ assert handoff_closeout["dry_run_executor_invocation_allowed"] is False
+ assert handoff_closeout["runner_invocation_allowed"] is False
+ assert handoff_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert handoff_closeout["endpoint_execution_allowed"] is False
+ assert handoff_closeout["sql_execution_allowed"] is False
+ assert handoff_closeout["database_write_allowed"] is False
+ assert handoff_closeout["database_apply_authorized"] is False
+ assert handoff_closeout["executes_database_apply"] is False
+ assert handoff_closeout["executes_endpoint_in_preview"] is False
+ assert handoff_closeout["executes_sql_in_preview"] is False
+ assert handoff_closeout["writes_database_in_preview"] is False
+ assert handoff_closeout["stdout_included"] is False
+ assert handoff_closeout["stderr_included"] is False
+ assert proof["proof_id"] == future["final_no_runner_execution_proof_id"]
+ assert proof["source_no_execution_receipt_handoff_closeout_id"] == future["no_execution_receipt_handoff_closeout_id"]
+ assert proof["source_no_execution_receipt_handoff_id"] == handoff["handoff_id"]
+ assert proof["source_runner_invocation_boundary_closeout_id"] == boundary_closeout["runner_invocation_boundary_closeout_id"]
+ assert proof["required_command_shape_hash"] == handoff["required_command_shape_hash"]
+ assert proof["proof_status"] == "final_no_runner_execution_proof_preview_ready"
+ assert proof["proof_mode"] == "final_no_runner_execution_proof_preview_only"
+ assert proof["execution_receipt_present"] is False
+ assert proof["execution_receipt_required"] is False
+ assert proof["dry_run_executor_invoked"] is False
+ assert proof["runner_invocation_performed"] is False
+ assert proof["endpoint_executed"] is False
+ assert proof["sql_executed"] is False
+ assert proof["database_written"] is False
+ assert proof["dry_run_executor_invocation_allowed"] is False
+ assert proof["runner_invocation_allowed"] is False
+ assert proof["ready_for_final_no_runner_execution_proof_now"] is False
+ assert proof["ready_for_dry_run_executor_invocation_now"] is False
+ assert proof["ready_for_actual_dry_run_execution_now"] is False
+ assert proof["endpoint_execution_allowed"] is False
+ assert proof["sql_execution_allowed"] is False
+ assert proof["database_write_allowed"] is False
+ assert proof["database_apply_authorized"] is False
+ assert proof["executes_database_apply"] is False
+ assert proof["executes_endpoint"] is False
+ assert proof["executes_sql"] is False
+ assert proof["writes_database"] is False
+ assert proof["captures_stdout"] is False
+ assert proof["captures_stderr"] is False
+ assert proof["stdout_included"] is False
+ assert proof["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "runner_invocation_boundary_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "no_execution_receipt_handoff_ready" in check_keys
+ assert "no_execution_receipt_handoff_no_execute" in check_keys
+ assert "final_no_runner_execution_proof_bound" in check_keys
+ assert "final_no_runner_execution_proof_blocks_execution" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "runner_invocation_boundary_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout_waits_without_ready_handoff_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ ]
+ proof_closeout = closeout[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout"
+ ]
+ quarantine = proof_closeout["controlled_executor_quarantine_proof"]
+ contract = closeout[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_NO_EXECUTION_RECEIPT_HANDOFF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["controlled_executor_quarantine_proof_count"] == 1
+ assert closeout["summary"]["controlled_executor_quarantine_proof_field_count"] == 12
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["final_no_runner_execution_proof_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout-"
+ )
+ assert future["controlled_executor_quarantine_proof_id"].endswith(
+ "-controlled-executor-quarantine-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ ]
+ is False
+ )
+ assert future["final_no_runner_execution_proof_closeout_ready"] is False
+ assert future["controlled_executor_quarantine_proof_bound"] is False
+ assert future["controlled_executor_quarantine_bound"] is True
+ assert future["executor_quarantine_enforced"] is True
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert proof_closeout["authorization_material_type"] == (
+ "controlled_dry_run_final_no_runner_execution_proof_closeout"
+ )
+ assert proof_closeout["final_no_runner_execution_proof_closeout_only"] is True
+ assert proof_closeout["controlled_executor_quarantine_proof_only"] is True
+ assert proof_closeout["controlled_executor_quarantine_bound"] is True
+ assert proof_closeout["executor_quarantine_enforced"] is True
+ assert proof_closeout["dry_run_executor_invoked"] is False
+ assert proof_closeout["runner_invocation_performed"] is False
+ assert proof_closeout["endpoint_executed"] is False
+ assert proof_closeout["sql_executed"] is False
+ assert proof_closeout["database_written"] is False
+ assert proof_closeout["dry_run_executor_invocation_allowed"] is False
+ assert proof_closeout["runner_invocation_allowed"] is False
+ assert proof_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert proof_closeout["endpoint_execution_allowed"] is False
+ assert proof_closeout["sql_execution_allowed"] is False
+ assert proof_closeout["database_write_allowed"] is False
+ assert proof_closeout["database_apply_authorized"] is False
+ assert proof_closeout["executes_database_apply"] is False
+ assert proof_closeout["executes_endpoint_in_preview"] is False
+ assert proof_closeout["executes_sql_in_preview"] is False
+ assert proof_closeout["writes_database_in_preview"] is False
+ assert proof_closeout["stdout_included"] is False
+ assert proof_closeout["stderr_included"] is False
+ assert quarantine["quarantine_status"] == "controlled_executor_quarantine_proof_preview_ready"
+ assert quarantine["quarantine_mode"] == "controlled_executor_quarantine_proof_preview_only"
+ assert quarantine["controlled_executor_quarantine_proof_field_count"] == 12
+ assert quarantine["controlled_executor_quarantine_bound"] is True
+ assert quarantine["executor_quarantine_enforced"] is True
+ assert quarantine["dry_run_executor_invoked"] is False
+ assert quarantine["runner_invocation_performed"] is False
+ assert quarantine["endpoint_executed"] is False
+ assert quarantine["sql_executed"] is False
+ assert quarantine["database_written"] is False
+ assert quarantine["execution_receipt_present"] is False
+ assert quarantine["execution_receipt_required"] is False
+ assert quarantine["dry_run_executor_invocation_allowed"] is False
+ assert quarantine["runner_invocation_allowed"] is False
+ assert quarantine["ready_for_controlled_executor_quarantine_now"] is False
+ assert quarantine["ready_for_dry_run_executor_invocation_now"] is False
+ assert quarantine["ready_for_actual_dry_run_execution_now"] is False
+ assert quarantine["endpoint_execution_allowed"] is False
+ assert quarantine["sql_execution_allowed"] is False
+ assert quarantine["database_write_allowed"] is False
+ assert quarantine["database_apply_authorized"] is False
+ assert quarantine["executes_database_apply"] is False
+ assert quarantine["executes_endpoint"] is False
+ assert quarantine["executes_sql"] is False
+ assert quarantine["writes_database"] is False
+ assert quarantine["stdout_included"] is False
+ assert quarantine["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "no_execution_receipt_handoff_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "final_no_runner_execution_proof_ready" in check_keys
+ assert "final_no_runner_execution_proof_no_execute" in check_keys
+ assert "controlled_executor_quarantine_proof_bound" in check_keys
+ assert "controlled_executor_quarantine_proof_blocks_execution" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "no_execution_receipt_handoff_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout_ready_after_fake_fetch_but_quarantine_is_no_executor():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ ]
+ proof_closeout = closeout[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout"
+ ]
+ quarantine = proof_closeout["controlled_executor_quarantine_proof"]
+ final_proof = proof_closeout["final_no_runner_execution_proof"]
+ handoff_closeout = proof_closeout["no_execution_receipt_handoff_closeout"]
+ contract = closeout[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_FINAL_NO_RUNNER_EXECUTION_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["controlled_executor_quarantine_proof_count"] == 1
+ assert closeout["summary"]["controlled_executor_quarantine_proof_field_count"] == 12
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ ]
+ is True
+ )
+ assert future["final_no_runner_execution_proof_closeout_ready"] is True
+ assert future["controlled_executor_quarantine_proof_bound"] is True
+ assert future["controlled_executor_quarantine_bound"] is True
+ assert future["executor_quarantine_enforced"] is True
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert (
+ proof_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_final_no_runner_execution_proof_closeout"
+ ]
+ is True
+ )
+ assert proof_closeout["final_no_runner_execution_proof_closeout_field_count"] == 12
+ assert proof_closeout["final_no_runner_execution_proof_closeout_acceptance_gate_count"] == 10
+ assert proof_closeout["controlled_executor_quarantine_proof_count"] == 1
+ assert proof_closeout["controlled_executor_quarantine_proof_field_count"] == 12
+ assert proof_closeout["controlled_executor_quarantine_bound"] is True
+ assert proof_closeout["executor_quarantine_enforced"] is True
+ assert proof_closeout["dry_run_executor_invoked"] is False
+ assert proof_closeout["runner_invocation_performed"] is False
+ assert proof_closeout["endpoint_executed"] is False
+ assert proof_closeout["sql_executed"] is False
+ assert proof_closeout["database_written"] is False
+ assert proof_closeout["dry_run_executor_invocation_allowed"] is False
+ assert proof_closeout["runner_invocation_allowed"] is False
+ assert proof_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert proof_closeout["endpoint_execution_allowed"] is False
+ assert proof_closeout["sql_execution_allowed"] is False
+ assert proof_closeout["database_write_allowed"] is False
+ assert proof_closeout["database_apply_authorized"] is False
+ assert proof_closeout["executes_database_apply"] is False
+ assert proof_closeout["executes_endpoint_in_preview"] is False
+ assert proof_closeout["executes_sql_in_preview"] is False
+ assert proof_closeout["writes_database_in_preview"] is False
+ assert proof_closeout["stdout_included"] is False
+ assert proof_closeout["stderr_included"] is False
+ assert quarantine["quarantine_proof_id"] == future["controlled_executor_quarantine_proof_id"]
+ assert quarantine["source_final_no_runner_execution_proof_closeout_id"] == future["final_no_runner_execution_proof_closeout_id"]
+ assert quarantine["source_final_no_runner_execution_proof_id"] == final_proof["proof_id"]
+ assert quarantine["source_no_execution_receipt_handoff_closeout_id"] == handoff_closeout["no_execution_receipt_handoff_closeout_id"]
+ assert quarantine["required_command_shape_hash"] == final_proof["required_command_shape_hash"]
+ assert quarantine["quarantine_status"] == "controlled_executor_quarantine_proof_preview_ready"
+ assert quarantine["quarantine_mode"] == "controlled_executor_quarantine_proof_preview_only"
+ assert quarantine["controlled_executor_quarantine_bound"] is True
+ assert quarantine["executor_quarantine_enforced"] is True
+ assert quarantine["dry_run_executor_invoked"] is False
+ assert quarantine["runner_invocation_performed"] is False
+ assert quarantine["endpoint_executed"] is False
+ assert quarantine["sql_executed"] is False
+ assert quarantine["database_written"] is False
+ assert quarantine["dry_run_executor_invocation_allowed"] is False
+ assert quarantine["runner_invocation_allowed"] is False
+ assert quarantine["ready_for_controlled_executor_quarantine_now"] is False
+ assert quarantine["ready_for_dry_run_executor_invocation_now"] is False
+ assert quarantine["ready_for_actual_dry_run_execution_now"] is False
+ assert quarantine["endpoint_execution_allowed"] is False
+ assert quarantine["sql_execution_allowed"] is False
+ assert quarantine["database_write_allowed"] is False
+ assert quarantine["database_apply_authorized"] is False
+ assert quarantine["executes_database_apply"] is False
+ assert quarantine["executes_endpoint"] is False
+ assert quarantine["executes_sql"] is False
+ assert quarantine["writes_database"] is False
+ assert quarantine["captures_stdout"] is False
+ assert quarantine["captures_stderr"] is False
+ assert quarantine["stdout_included"] is False
+ assert quarantine["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "no_execution_receipt_handoff_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "final_no_runner_execution_proof_ready" in check_keys
+ assert "final_no_runner_execution_proof_no_execute" in check_keys
+ assert "controlled_executor_quarantine_proof_bound" in check_keys
+ assert "controlled_executor_quarantine_proof_blocks_execution" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "no_execution_receipt_handoff_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout_waits_without_ready_final_proof_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ ]
+ quarantine_closeout = closeout[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ ]
+ freeze = quarantine_closeout["dry_run_execution_envelope_freeze_proof"]
+ contract = closeout[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_FINAL_NO_RUNNER_EXECUTION_PROOF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["dry_run_execution_envelope_freeze_proof_count"] == 1
+ assert closeout["summary"]["dry_run_execution_envelope_freeze_proof_field_count"] == 12
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["controlled_executor_quarantine_proof_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout-"
+ )
+ assert future["dry_run_execution_envelope_freeze_proof_id"].endswith(
+ "-dry-run-execution-envelope-freeze-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ ]
+ is False
+ )
+ assert future["controlled_executor_quarantine_proof_closeout_ready"] is False
+ assert future["dry_run_execution_envelope_freeze_proof_bound"] is False
+ assert future["controlled_executor_quarantine_bound"] is True
+ assert future["executor_quarantine_enforced"] is True
+ assert future["execution_envelope_frozen"] is True
+ assert future["execution_envelope_mutation_allowed"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert quarantine_closeout["authorization_material_type"] == (
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ )
+ assert quarantine_closeout["controlled_executor_quarantine_proof_closeout_only"] is True
+ assert quarantine_closeout["dry_run_execution_envelope_freeze_proof_only"] is True
+ assert quarantine_closeout["execution_envelope_frozen"] is True
+ assert quarantine_closeout["execution_envelope_mutation_allowed"] is False
+ assert quarantine_closeout["dry_run_executor_invoked"] is False
+ assert quarantine_closeout["runner_invocation_performed"] is False
+ assert quarantine_closeout["endpoint_executed"] is False
+ assert quarantine_closeout["sql_executed"] is False
+ assert quarantine_closeout["database_written"] is False
+ assert quarantine_closeout["dry_run_executor_invocation_allowed"] is False
+ assert quarantine_closeout["runner_invocation_allowed"] is False
+ assert quarantine_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert quarantine_closeout["endpoint_execution_allowed"] is False
+ assert quarantine_closeout["sql_execution_allowed"] is False
+ assert quarantine_closeout["database_write_allowed"] is False
+ assert quarantine_closeout["database_apply_authorized"] is False
+ assert quarantine_closeout["executes_database_apply"] is False
+ assert quarantine_closeout["executes_endpoint_in_preview"] is False
+ assert quarantine_closeout["executes_sql_in_preview"] is False
+ assert quarantine_closeout["writes_database_in_preview"] is False
+ assert quarantine_closeout["stdout_included"] is False
+ assert quarantine_closeout["stderr_included"] is False
+ assert freeze["freeze_status"] == "dry_run_execution_envelope_freeze_proof_preview_ready"
+ assert freeze["freeze_mode"] == "dry_run_execution_envelope_freeze_proof_preview_only"
+ assert freeze["dry_run_execution_envelope_freeze_proof_field_count"] == 12
+ assert freeze["execution_envelope_frozen"] is True
+ assert freeze["execution_envelope_mutation_allowed"] is False
+ assert freeze["dry_run_executor_invoked"] is False
+ assert freeze["runner_invocation_performed"] is False
+ assert freeze["endpoint_executed"] is False
+ assert freeze["sql_executed"] is False
+ assert freeze["database_written"] is False
+ assert freeze["execution_receipt_present"] is False
+ assert freeze["execution_receipt_required"] is False
+ assert freeze["dry_run_executor_invocation_allowed"] is False
+ assert freeze["runner_invocation_allowed"] is False
+ assert freeze["ready_for_dry_run_executor_invocation_now"] is False
+ assert freeze["ready_for_actual_dry_run_execution_now"] is False
+ assert freeze["endpoint_execution_allowed"] is False
+ assert freeze["sql_execution_allowed"] is False
+ assert freeze["database_write_allowed"] is False
+ assert freeze["database_apply_authorized"] is False
+ assert freeze["executes_database_apply"] is False
+ assert freeze["executes_endpoint"] is False
+ assert freeze["executes_sql"] is False
+ assert freeze["writes_database"] is False
+ assert freeze["stdout_included"] is False
+ assert freeze["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "final_no_runner_execution_proof_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "controlled_executor_quarantine_proof_ready" in check_keys
+ assert "controlled_executor_quarantine_proof_no_execute" in check_keys
+ assert "dry_run_execution_envelope_freeze_proof_bound" in check_keys
+ assert "dry_run_execution_envelope_freeze_proof_blocks_execution" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "final_no_runner_execution_proof_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_after_fake_fetch_but_envelope_is_frozen():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ ]
+ quarantine_closeout = closeout[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ ]
+ freeze = quarantine_closeout["dry_run_execution_envelope_freeze_proof"]
+ quarantine = quarantine_closeout["controlled_executor_quarantine_proof"]
+ source_closeout = quarantine_closeout["final_no_runner_execution_proof_closeout"]
+ final_proof = quarantine_closeout["final_no_runner_execution_proof"]
+ contract = closeout[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_CONTROLLED_EXECUTOR_QUARANTINE_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_preflight_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_write_invocation_package_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_invocation_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_apply_executor_readiness_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_pre_apply_replay_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_executor_guard_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_apply_enforcement_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_post_receipt_parser_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_execution_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_command_artifact_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_plan_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_runner_readiness_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_package_ready_count"] == 1
+ assert closeout["summary"]["controlled_apply_final_preflight_ready_count"] == 1
+ assert closeout["summary"]["dry_run_execution_envelope_freeze_proof_count"] == 1
+ assert closeout["summary"]["dry_run_execution_envelope_freeze_proof_field_count"] == 12
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ ]
+ is True
+ )
+ assert future["controlled_executor_quarantine_proof_closeout_ready"] is True
+ assert future["dry_run_execution_envelope_freeze_proof_bound"] is True
+ assert future["controlled_executor_quarantine_bound"] is True
+ assert future["executor_quarantine_enforced"] is True
+ assert future["execution_envelope_frozen"] is True
+ assert future["execution_envelope_mutation_allowed"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["ready_for_actual_dry_run_execution_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert (
+ quarantine_closeout[
+ "ready_for_future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ ]
+ is True
+ )
+ assert quarantine_closeout["controlled_executor_quarantine_proof_closeout_field_count"] == 12
+ assert quarantine_closeout["controlled_executor_quarantine_proof_closeout_acceptance_gate_count"] == 10
+ assert quarantine_closeout["dry_run_execution_envelope_freeze_proof_count"] == 1
+ assert quarantine_closeout["dry_run_execution_envelope_freeze_proof_field_count"] == 12
+ assert quarantine_closeout["controlled_executor_quarantine_proof_closeout_only"] is True
+ assert quarantine_closeout["dry_run_execution_envelope_freeze_proof_only"] is True
+ assert quarantine_closeout["execution_envelope_frozen"] is True
+ assert quarantine_closeout["execution_envelope_mutation_allowed"] is False
+ assert quarantine_closeout["dry_run_executor_invoked"] is False
+ assert quarantine_closeout["runner_invocation_performed"] is False
+ assert quarantine_closeout["endpoint_executed"] is False
+ assert quarantine_closeout["sql_executed"] is False
+ assert quarantine_closeout["database_written"] is False
+ assert quarantine_closeout["dry_run_executor_invocation_allowed"] is False
+ assert quarantine_closeout["runner_invocation_allowed"] is False
+ assert quarantine_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert quarantine_closeout["endpoint_execution_allowed"] is False
+ assert quarantine_closeout["sql_execution_allowed"] is False
+ assert quarantine_closeout["database_write_allowed"] is False
+ assert quarantine_closeout["database_apply_authorized"] is False
+ assert quarantine_closeout["executes_database_apply"] is False
+ assert quarantine_closeout["executes_endpoint_in_preview"] is False
+ assert quarantine_closeout["executes_sql_in_preview"] is False
+ assert quarantine_closeout["writes_database_in_preview"] is False
+ assert quarantine_closeout["stdout_included"] is False
+ assert quarantine_closeout["stderr_included"] is False
+ assert freeze["freeze_proof_id"] == future["dry_run_execution_envelope_freeze_proof_id"]
+ assert freeze["source_controlled_executor_quarantine_proof_closeout_id"] == future["controlled_executor_quarantine_proof_closeout_id"]
+ assert freeze["source_controlled_executor_quarantine_proof_id"] == quarantine["quarantine_proof_id"]
+ assert freeze["source_final_no_runner_execution_proof_closeout_id"] == source_closeout["final_no_runner_execution_proof_closeout_id"]
+ assert freeze["source_final_no_runner_execution_proof_id"] == final_proof["proof_id"]
+ assert freeze["required_command_shape_hash"] == quarantine["required_command_shape_hash"]
+ assert freeze["freeze_status"] == "dry_run_execution_envelope_freeze_proof_preview_ready"
+ assert freeze["freeze_mode"] == "dry_run_execution_envelope_freeze_proof_preview_only"
+ assert freeze["execution_envelope_frozen"] is True
+ assert freeze["execution_envelope_mutation_allowed"] is False
+ assert freeze["dry_run_executor_invoked"] is False
+ assert freeze["runner_invocation_performed"] is False
+ assert freeze["endpoint_executed"] is False
+ assert freeze["sql_executed"] is False
+ assert freeze["database_written"] is False
+ assert freeze["execution_receipt_present"] is False
+ assert freeze["dry_run_executor_invocation_allowed"] is False
+ assert freeze["runner_invocation_allowed"] is False
+ assert freeze["ready_for_dry_run_executor_invocation_now"] is False
+ assert freeze["ready_for_actual_dry_run_execution_now"] is False
+ assert freeze["endpoint_execution_allowed"] is False
+ assert freeze["sql_execution_allowed"] is False
+ assert freeze["database_write_allowed"] is False
+ assert freeze["database_apply_authorized"] is False
+ assert freeze["executes_database_apply"] is False
+ assert freeze["executes_endpoint"] is False
+ assert freeze["executes_sql"] is False
+ assert freeze["writes_database"] is False
+ assert freeze["captures_stdout"] is False
+ assert freeze["captures_stderr"] is False
+ assert freeze["stdout_included"] is False
+ assert freeze["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert contract["ready_for_actual_dry_run_execution_now"] is False
+ assert contract["writes_database"] is False
+ assert "final_no_runner_execution_proof_closeout_ready" in check_keys
+ assert "controlled_executor_quarantine_proof_ready" in check_keys
+ assert "dry_run_execution_envelope_freeze_proof_bound" in check_keys
+ assert "dry_run_execution_envelope_freeze_proof_blocks_execution" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout_waits_without_ready_quarantine_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ ]
+ freeze_closeout = closeout[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ ]
+ handoff = freeze_closeout["frozen_envelope_verifier_handoff"]
+ contract = closeout[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_CONTROLLED_EXECUTOR_QUARANTINE_PROOF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["frozen_envelope_verifier_handoff_count"] == 1
+ assert closeout["summary"]["frozen_envelope_verifier_handoff_field_count"] == 12
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["verifier_receipt_present_count"] == 0
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["execution_envelope_freeze_proof_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout-"
+ )
+ assert future["frozen_envelope_verifier_handoff_id"].endswith(
+ "-frozen-envelope-verifier-handoff"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ ]
+ is False
+ )
+ assert future["execution_envelope_freeze_proof_closeout_ready"] is False
+ assert future["frozen_envelope_verifier_handoff_bound"] is False
+ assert future["execution_envelope_frozen"] is True
+ assert future["execution_envelope_mutation_allowed"] is False
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["verifier_receipt_present"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_invocation_now"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert freeze_closeout["authorization_material_type"] == (
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ )
+ assert freeze_closeout["execution_envelope_freeze_proof_closeout_only"] is True
+ assert freeze_closeout["frozen_envelope_verifier_handoff_only"] is True
+ assert freeze_closeout["execution_envelope_frozen"] is True
+ assert freeze_closeout["execution_envelope_mutation_allowed"] is False
+ assert freeze_closeout["verifier_invocation_allowed"] is False
+ assert freeze_closeout["verifier_invoked"] is False
+ assert freeze_closeout["verifier_receipt_present"] is False
+ assert freeze_closeout["dry_run_executor_invoked"] is False
+ assert freeze_closeout["runner_invocation_performed"] is False
+ assert freeze_closeout["endpoint_executed"] is False
+ assert freeze_closeout["sql_executed"] is False
+ assert freeze_closeout["database_written"] is False
+ assert freeze_closeout["ready_for_verifier_invocation_now"] is False
+ assert freeze_closeout["ready_for_dry_run_executor_invocation_now"] is False
+ assert freeze_closeout["endpoint_execution_allowed"] is False
+ assert freeze_closeout["sql_execution_allowed"] is False
+ assert freeze_closeout["database_write_allowed"] is False
+ assert freeze_closeout["database_apply_authorized"] is False
+ assert freeze_closeout["executes_database_apply"] is False
+ assert freeze_closeout["executes_endpoint_in_preview"] is False
+ assert freeze_closeout["executes_sql_in_preview"] is False
+ assert freeze_closeout["writes_database_in_preview"] is False
+ assert handoff["handoff_status"] == "frozen_envelope_verifier_handoff_preview_ready"
+ assert handoff["verifier_handoff_mode"] == "frozen_envelope_verifier_handoff_preview_only"
+ assert handoff["frozen_envelope_verifier_handoff_field_count"] == 12
+ assert handoff["execution_envelope_frozen"] is True
+ assert handoff["execution_envelope_mutation_allowed"] is False
+ assert handoff["verifier_handoff_bound"] is True
+ assert handoff["verifier_invocation_allowed"] is False
+ assert handoff["verifier_invoked"] is False
+ assert handoff["verifier_receipt_present"] is False
+ assert handoff["dry_run_executor_invoked"] is False
+ assert handoff["runner_invocation_performed"] is False
+ assert handoff["endpoint_executed"] is False
+ assert handoff["sql_executed"] is False
+ assert handoff["database_written"] is False
+ assert handoff["ready_for_verifier_invocation_now"] is False
+ assert handoff["endpoint_execution_allowed"] is False
+ assert handoff["sql_execution_allowed"] is False
+ assert handoff["database_write_allowed"] is False
+ assert handoff["database_apply_authorized"] is False
+ assert handoff["executes_database_apply"] is False
+ assert handoff["executes_endpoint"] is False
+ assert handoff["executes_sql"] is False
+ assert handoff["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ ]
+ is False
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_invocation_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert "controlled_executor_quarantine_proof_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "dry_run_execution_envelope_freeze_proof_ready" in check_keys
+ assert "dry_run_execution_envelope_freeze_proof_no_execute" in check_keys
+ assert "frozen_envelope_verifier_handoff_bound" in check_keys
+ assert "frozen_envelope_verifier_handoff_blocks_execution" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "controlled_executor_quarantine_proof_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_after_fake_fetch_but_verifier_is_handoff_only():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ ]
+ freeze_closeout = closeout[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ ]
+ handoff = freeze_closeout["frozen_envelope_verifier_handoff"]
+ freeze = freeze_closeout["dry_run_execution_envelope_freeze_proof"]
+ source_closeout = freeze_closeout["controlled_executor_quarantine_proof_closeout"]
+ contract = closeout[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_ENVELOPE_FREEZE_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_ready_count"] == 1
+ assert closeout["summary"]["frozen_envelope_verifier_handoff_count"] == 1
+ assert closeout["summary"]["frozen_envelope_verifier_handoff_field_count"] == 12
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["verifier_receipt_present_count"] == 0
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ ]
+ is True
+ )
+ assert future["execution_envelope_freeze_proof_closeout_ready"] is True
+ assert future["frozen_envelope_verifier_handoff_bound"] is True
+ assert future["execution_envelope_frozen"] is True
+ assert future["execution_envelope_mutation_allowed"] is False
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["verifier_receipt_present"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_invocation_now"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert freeze_closeout["ready_for_future_database_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout"] is True
+ assert freeze_closeout["execution_envelope_freeze_proof_closeout_field_count"] == 12
+ assert freeze_closeout["execution_envelope_freeze_proof_closeout_acceptance_gate_count"] == 10
+ assert freeze_closeout["frozen_envelope_verifier_handoff_count"] == 1
+ assert freeze_closeout["frozen_envelope_verifier_handoff_field_count"] == 12
+ assert freeze_closeout["execution_envelope_freeze_proof_closeout_only"] is True
+ assert freeze_closeout["frozen_envelope_verifier_handoff_only"] is True
+ assert freeze_closeout["execution_envelope_frozen"] is True
+ assert freeze_closeout["execution_envelope_mutation_allowed"] is False
+ assert freeze_closeout["verifier_invocation_allowed"] is False
+ assert freeze_closeout["verifier_invoked"] is False
+ assert freeze_closeout["verifier_receipt_present"] is False
+ assert freeze_closeout["dry_run_executor_invoked"] is False
+ assert freeze_closeout["runner_invocation_performed"] is False
+ assert freeze_closeout["endpoint_executed"] is False
+ assert freeze_closeout["sql_executed"] is False
+ assert freeze_closeout["database_written"] is False
+ assert freeze_closeout["ready_for_verifier_invocation_now"] is False
+ assert freeze_closeout["endpoint_execution_allowed"] is False
+ assert freeze_closeout["sql_execution_allowed"] is False
+ assert freeze_closeout["database_write_allowed"] is False
+ assert freeze_closeout["database_apply_authorized"] is False
+ assert freeze_closeout["executes_database_apply"] is False
+ assert freeze_closeout["executes_endpoint_in_preview"] is False
+ assert freeze_closeout["executes_sql_in_preview"] is False
+ assert freeze_closeout["writes_database_in_preview"] is False
+ assert handoff["handoff_id"] == future["frozen_envelope_verifier_handoff_id"]
+ assert handoff["source_execution_envelope_freeze_proof_closeout_id"] == future["execution_envelope_freeze_proof_closeout_id"]
+ assert handoff["source_dry_run_execution_envelope_freeze_proof_id"] == freeze["freeze_proof_id"]
+ assert handoff["source_controlled_executor_quarantine_proof_closeout_id"] == source_closeout["controlled_executor_quarantine_proof_closeout_id"]
+ assert handoff["required_command_shape_hash"] == freeze["required_command_shape_hash"]
+ assert handoff["handoff_status"] == "frozen_envelope_verifier_handoff_preview_ready"
+ assert handoff["verifier_handoff_mode"] == "frozen_envelope_verifier_handoff_preview_only"
+ assert handoff["execution_envelope_frozen"] is True
+ assert handoff["execution_envelope_mutation_allowed"] is False
+ assert handoff["verifier_handoff_bound"] is True
+ assert handoff["verifier_invocation_allowed"] is False
+ assert handoff["verifier_invoked"] is False
+ assert handoff["verifier_receipt_present"] is False
+ assert handoff["dry_run_executor_invoked"] is False
+ assert handoff["runner_invocation_performed"] is False
+ assert handoff["endpoint_executed"] is False
+ assert handoff["sql_executed"] is False
+ assert handoff["database_written"] is False
+ assert handoff["ready_for_verifier_invocation_now"] is False
+ assert handoff["endpoint_execution_allowed"] is False
+ assert handoff["sql_execution_allowed"] is False
+ assert handoff["database_write_allowed"] is False
+ assert handoff["database_apply_authorized"] is False
+ assert handoff["executes_database_apply"] is False
+ assert handoff["executes_endpoint"] is False
+ assert handoff["executes_sql"] is False
+ assert handoff["writes_database"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ ]
+ is True
+ )
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_invocation_now"] is False
+ assert contract["ready_for_dry_run_executor_invocation_now"] is False
+ assert "controlled_executor_quarantine_proof_closeout_ready" in check_keys
+ assert "dry_run_execution_envelope_freeze_proof_ready" in check_keys
+ assert "frozen_envelope_verifier_handoff_bound" in check_keys
+ assert "frozen_envelope_verifier_handoff_blocks_execution" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout_waits_without_ready_freeze_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ ]
+ handoff_closeout = closeout[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ ]
+ lock = handoff_closeout["verifier_invocation_lock_proof"]
+ contract = closeout[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_EXECUTION_ENVELOPE_FREEZE_PROOF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["verifier_invocation_lock_proof_count"] == 1
+ assert closeout["summary"]["verifier_invocation_lock_proof_field_count"] == 12
+ assert closeout["summary"]["verifier_invocation_locked_count"] == 1
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["verifier_receipt_present_count"] == 0
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["frozen_envelope_verifier_handoff_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout-"
+ )
+ assert future["verifier_invocation_lock_proof_id"].endswith(
+ "-verifier-invocation-lock-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ ]
+ is False
+ )
+ assert future["frozen_envelope_verifier_handoff_closeout_ready"] is False
+ assert future["execution_envelope_freeze_proof_closeout_ready"] is False
+ assert future["verifier_invocation_lock_proof_bound"] is False
+ assert future["verifier_invocation_locked"] is True
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["verifier_receipt_present"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert handoff_closeout["authorization_material_type"] == (
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ )
+ assert handoff_closeout["frozen_envelope_verifier_handoff_closeout_only"] is True
+ assert handoff_closeout["verifier_invocation_lock_proof_only"] is True
+ assert handoff_closeout["verifier_invocation_locked"] is True
+ assert handoff_closeout["verifier_invocation_allowed"] is False
+ assert handoff_closeout["verifier_invoked"] is False
+ assert handoff_closeout["verifier_receipt_present"] is False
+ assert handoff_closeout["dry_run_executor_invoked"] is False
+ assert handoff_closeout["runner_invocation_performed"] is False
+ assert handoff_closeout["endpoint_executed"] is False
+ assert handoff_closeout["sql_executed"] is False
+ assert handoff_closeout["database_written"] is False
+ assert handoff_closeout["ready_for_verifier_invocation_now"] is False
+ assert handoff_closeout["endpoint_execution_allowed"] is False
+ assert handoff_closeout["sql_execution_allowed"] is False
+ assert handoff_closeout["database_write_allowed"] is False
+ assert handoff_closeout["database_apply_authorized"] is False
+ assert handoff_closeout["executes_database_apply"] is False
+ assert handoff_closeout["executes_endpoint_in_preview"] is False
+ assert handoff_closeout["executes_sql_in_preview"] is False
+ assert handoff_closeout["writes_database_in_preview"] is False
+ assert lock["lock_status"] == "verifier_invocation_lock_proof_preview_ready"
+ assert lock["lock_mode"] == "verifier_invocation_lock_proof_preview_only"
+ assert lock["verifier_invocation_lock_proof_field_count"] == 12
+ assert lock["verifier_invocation_locked"] is True
+ assert lock["verifier_invocation_allowed"] is False
+ assert lock["verifier_invoked"] is False
+ assert lock["verifier_receipt_present"] is False
+ assert lock["dry_run_executor_invoked"] is False
+ assert lock["runner_invocation_performed"] is False
+ assert lock["endpoint_executed"] is False
+ assert lock["sql_executed"] is False
+ assert lock["database_written"] is False
+ assert lock["ready_for_verifier_invocation_now"] is False
+ assert lock["endpoint_execution_allowed"] is False
+ assert lock["sql_execution_allowed"] is False
+ assert lock["database_write_allowed"] is False
+ assert lock["database_apply_authorized"] is False
+ assert lock["executes_database_apply"] is False
+ assert lock["executes_endpoint"] is False
+ assert lock["executes_sql"] is False
+ assert lock["writes_database"] is False
+ assert lock["stdout_included"] is False
+ assert lock["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ ]
+ is False
+ )
+ assert contract["verifier_invocation_locked"] is True
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_invocation_now"] is False
+ assert "execution_envelope_freeze_proof_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "frozen_envelope_verifier_handoff_ready" in check_keys
+ assert "frozen_envelope_verifier_handoff_no_execute" in check_keys
+ assert "verifier_invocation_lock_proof_bound" in check_keys
+ assert "verifier_invocation_lock_proof_blocks_execution" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "execution_envelope_freeze_proof_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_after_fake_fetch_but_verifier_invocation_is_locked():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ ]
+ handoff_closeout = closeout[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ ]
+ lock = handoff_closeout["verifier_invocation_lock_proof"]
+ handoff = handoff_closeout["frozen_envelope_verifier_handoff"]
+ source_closeout = handoff_closeout["execution_envelope_freeze_proof_closeout"]
+ freeze = handoff_closeout["dry_run_execution_envelope_freeze_proof"]
+ contract = closeout[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_FROZEN_ENVELOPE_VERIFIER_HANDOFF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["verifier_invocation_lock_proof_count"] == 1
+ assert closeout["summary"]["verifier_invocation_lock_proof_field_count"] == 12
+ assert closeout["summary"]["verifier_invocation_locked_count"] == 1
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["verifier_receipt_present_count"] == 0
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ ]
+ is True
+ )
+ assert future["frozen_envelope_verifier_handoff_closeout_ready"] is True
+ assert future["execution_envelope_freeze_proof_closeout_ready"] is True
+ assert future["frozen_envelope_verifier_handoff_ready"] is True
+ assert future["verifier_invocation_lock_proof_bound"] is True
+ assert future["verifier_invocation_locked"] is True
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["verifier_receipt_present"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert handoff_closeout["ready_for_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"] is True
+ assert handoff_closeout["frozen_envelope_verifier_handoff_closeout_field_count"] == 12
+ assert handoff_closeout["frozen_envelope_verifier_handoff_closeout_acceptance_gate_count"] == 10
+ assert handoff_closeout["verifier_invocation_lock_proof_count"] == 1
+ assert handoff_closeout["verifier_invocation_lock_proof_field_count"] == 12
+ assert handoff_closeout["frozen_envelope_verifier_handoff_closeout_only"] is True
+ assert handoff_closeout["verifier_invocation_lock_proof_only"] is True
+ assert handoff_closeout["verifier_invocation_locked"] is True
+ assert handoff_closeout["verifier_invocation_allowed"] is False
+ assert handoff_closeout["verifier_invoked"] is False
+ assert handoff_closeout["verifier_receipt_present"] is False
+ assert handoff_closeout["dry_run_executor_invoked"] is False
+ assert handoff_closeout["runner_invocation_performed"] is False
+ assert handoff_closeout["endpoint_executed"] is False
+ assert handoff_closeout["sql_executed"] is False
+ assert handoff_closeout["database_written"] is False
+ assert handoff_closeout["ready_for_verifier_invocation_now"] is False
+ assert handoff_closeout["endpoint_execution_allowed"] is False
+ assert handoff_closeout["sql_execution_allowed"] is False
+ assert handoff_closeout["database_write_allowed"] is False
+ assert handoff_closeout["database_apply_authorized"] is False
+ assert handoff_closeout["executes_database_apply"] is False
+ assert handoff_closeout["executes_endpoint_in_preview"] is False
+ assert handoff_closeout["executes_sql_in_preview"] is False
+ assert handoff_closeout["writes_database_in_preview"] is False
+ assert lock["lock_proof_id"] == future["verifier_invocation_lock_proof_id"]
+ assert (
+ lock["source_frozen_envelope_verifier_handoff_closeout_id"]
+ == future["frozen_envelope_verifier_handoff_closeout_id"]
+ )
+ assert lock["source_frozen_envelope_verifier_handoff_id"] == handoff["handoff_id"]
+ assert (
+ lock["source_execution_envelope_freeze_proof_closeout_id"]
+ == source_closeout["execution_envelope_freeze_proof_closeout_id"]
+ )
+ assert lock["source_dry_run_execution_envelope_freeze_proof_id"] == freeze["freeze_proof_id"]
+ assert lock["required_command_shape_hash"] == freeze["required_command_shape_hash"]
+ assert lock["lock_status"] == "verifier_invocation_lock_proof_preview_ready"
+ assert lock["lock_mode"] == "verifier_invocation_lock_proof_preview_only"
+ assert lock["verifier_invocation_locked"] is True
+ assert lock["verifier_invocation_allowed"] is False
+ assert lock["verifier_invoked"] is False
+ assert lock["verifier_receipt_present"] is False
+ assert lock["verifier_receipt_required"] is False
+ assert lock["dry_run_executor_invoked"] is False
+ assert lock["runner_invocation_performed"] is False
+ assert lock["endpoint_executed"] is False
+ assert lock["sql_executed"] is False
+ assert lock["database_written"] is False
+ assert lock["ready_for_verifier_invocation_now"] is False
+ assert lock["endpoint_execution_allowed"] is False
+ assert lock["sql_execution_allowed"] is False
+ assert lock["database_write_allowed"] is False
+ assert lock["database_apply_authorized"] is False
+ assert lock["executes_database_apply"] is False
+ assert lock["executes_endpoint"] is False
+ assert lock["executes_sql"] is False
+ assert lock["writes_database"] is False
+ assert lock["stdout_included"] is False
+ assert lock["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ ]
+ is True
+ )
+ assert contract["verifier_invocation_locked"] is True
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_invocation_now"] is False
+ assert "execution_envelope_freeze_proof_closeout_ready" in check_keys
+ assert "frozen_envelope_verifier_handoff_ready" in check_keys
+ assert "frozen_envelope_verifier_handoff_no_execute" in check_keys
+ assert "verifier_invocation_lock_proof_bound" in check_keys
+ assert "verifier_invocation_lock_proof_blocks_execution" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout_waits_without_ready_lock_closeout():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ ]
+ lock_closeout = closeout[
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ ]
+ receipt = lock_closeout["verifier_no_execution_receipt_proof"]
+ contract = closeout[
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_FROZEN_ENVELOPE_VERIFIER_HANDOFF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["verifier_no_execution_receipt_proof_count"] == 1
+ assert closeout["summary"]["verifier_no_execution_receipt_proof_field_count"] == 12
+ assert closeout["summary"]["verifier_invocation_locked_count"] == 1
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["verifier_receipt_present_count"] == 0
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["verifier_invocation_lock_proof_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout-"
+ )
+ assert future["verifier_no_execution_receipt_proof_id"].endswith(
+ "-verifier-no-execution-receipt-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ ]
+ is False
+ )
+ assert future["verifier_invocation_lock_proof_closeout_ready"] is False
+ assert future["frozen_envelope_verifier_handoff_closeout_ready"] is False
+ assert future["verifier_no_execution_receipt_proof_bound"] is False
+ assert future["verifier_invocation_locked"] is True
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["verifier_receipt_present"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert lock_closeout["authorization_material_type"] == (
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ )
+ assert lock_closeout["verifier_invocation_lock_proof_closeout_only"] is True
+ assert lock_closeout["verifier_no_execution_receipt_proof_only"] is True
+ assert lock_closeout["verifier_invocation_locked"] is True
+ assert lock_closeout["verifier_invocation_allowed"] is False
+ assert lock_closeout["verifier_invoked"] is False
+ assert lock_closeout["verifier_receipt_present"] is False
+ assert lock_closeout["dry_run_executor_invoked"] is False
+ assert lock_closeout["runner_invocation_performed"] is False
+ assert lock_closeout["endpoint_executed"] is False
+ assert lock_closeout["sql_executed"] is False
+ assert lock_closeout["database_written"] is False
+ assert receipt["receipt_status"] == "verifier_no_execution_receipt_proof_preview_ready"
+ assert receipt["receipt_mode"] == "verifier_no_execution_receipt_proof_preview_only"
+ assert receipt["verifier_no_execution_receipt_proof_field_count"] == 12
+ assert receipt["verifier_invocation_locked"] is True
+ assert receipt["verifier_invocation_allowed"] is False
+ assert receipt["verifier_invoked"] is False
+ assert receipt["verifier_receipt_present"] is False
+ assert receipt["dry_run_executor_invoked"] is False
+ assert receipt["runner_invocation_performed"] is False
+ assert receipt["endpoint_executed"] is False
+ assert receipt["sql_executed"] is False
+ assert receipt["database_written"] is False
+ assert receipt["ready_for_verifier_invocation_now"] is False
+ assert receipt["endpoint_execution_allowed"] is False
+ assert receipt["sql_execution_allowed"] is False
+ assert receipt["database_write_allowed"] is False
+ assert receipt["database_apply_authorized"] is False
+ assert receipt["executes_database_apply"] is False
+ assert receipt["executes_endpoint"] is False
+ assert receipt["executes_sql"] is False
+ assert receipt["writes_database"] is False
+ assert receipt["stdout_included"] is False
+ assert receipt["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ ]
+ is False
+ )
+ assert contract["verifier_invocation_locked"] is True
+ assert contract["verifier_invoked"] is False
+ assert contract["verifier_receipt_present"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_invocation_now"] is False
+ assert "frozen_envelope_verifier_handoff_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "verifier_invocation_lock_proof_ready" in check_keys
+ assert "verifier_invocation_lock_proof_no_execute" in check_keys
+ assert "verifier_no_execution_receipt_proof_bound" in check_keys
+ assert "verifier_no_execution_receipt_proof_blocks_execution" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "frozen_envelope_verifier_handoff_closeout_contract_blocks_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_after_fake_fetch_but_verifier_receipt_is_absent():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ ]
+ lock_closeout = closeout[
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ ]
+ receipt = lock_closeout["verifier_no_execution_receipt_proof"]
+ lock = lock_closeout["verifier_invocation_lock_proof"]
+ source_closeout = lock_closeout["frozen_envelope_verifier_handoff_closeout"]
+ contract = closeout[
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_INVOCATION_LOCK_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["verifier_no_execution_receipt_proof_count"] == 1
+ assert closeout["summary"]["verifier_no_execution_receipt_proof_field_count"] == 12
+ assert closeout["summary"]["verifier_invocation_locked_count"] == 1
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["verifier_receipt_present_count"] == 0
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["reads_secret_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ ]
+ is True
+ )
+ assert future["verifier_invocation_lock_proof_closeout_ready"] is True
+ assert future["frozen_envelope_verifier_handoff_closeout_ready"] is True
+ assert future["verifier_invocation_lock_proof_ready"] is True
+ assert future["verifier_no_execution_receipt_proof_bound"] is True
+ assert future["verifier_invocation_locked"] is True
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["verifier_receipt_present"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert lock_closeout["ready_for_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"] is True
+ assert lock_closeout["verifier_invocation_lock_proof_closeout_field_count"] == 12
+ assert lock_closeout["verifier_invocation_lock_proof_closeout_acceptance_gate_count"] == 10
+ assert lock_closeout["verifier_no_execution_receipt_proof_count"] == 1
+ assert lock_closeout["verifier_no_execution_receipt_proof_field_count"] == 12
+ assert lock_closeout["verifier_invocation_lock_proof_closeout_only"] is True
+ assert lock_closeout["verifier_no_execution_receipt_proof_only"] is True
+ assert lock_closeout["verifier_invocation_locked"] is True
+ assert lock_closeout["verifier_invocation_allowed"] is False
+ assert lock_closeout["verifier_invoked"] is False
+ assert lock_closeout["verifier_receipt_present"] is False
+ assert lock_closeout["dry_run_executor_invoked"] is False
+ assert lock_closeout["runner_invocation_performed"] is False
+ assert lock_closeout["endpoint_executed"] is False
+ assert lock_closeout["sql_executed"] is False
+ assert lock_closeout["database_written"] is False
+ assert receipt["receipt_proof_id"] == future["verifier_no_execution_receipt_proof_id"]
+ assert (
+ receipt["source_verifier_invocation_lock_proof_closeout_id"]
+ == future["verifier_invocation_lock_proof_closeout_id"]
+ )
+ assert receipt["source_verifier_invocation_lock_proof_id"] == lock["lock_proof_id"]
+ assert (
+ receipt["source_frozen_envelope_verifier_handoff_closeout_id"]
+ == source_closeout["frozen_envelope_verifier_handoff_closeout_id"]
+ )
+ assert receipt["required_command_shape_hash"] == lock["required_command_shape_hash"]
+ assert receipt["receipt_status"] == "verifier_no_execution_receipt_proof_preview_ready"
+ assert receipt["receipt_mode"] == "verifier_no_execution_receipt_proof_preview_only"
+ assert receipt["verifier_invocation_locked"] is True
+ assert receipt["verifier_invocation_allowed"] is False
+ assert receipt["verifier_invoked"] is False
+ assert receipt["verifier_receipt_present"] is False
+ assert receipt["verifier_receipt_required"] is False
+ assert receipt["dry_run_executor_invoked"] is False
+ assert receipt["runner_invocation_performed"] is False
+ assert receipt["endpoint_executed"] is False
+ assert receipt["sql_executed"] is False
+ assert receipt["database_written"] is False
+ assert receipt["ready_for_verifier_invocation_now"] is False
+ assert receipt["endpoint_execution_allowed"] is False
+ assert receipt["sql_execution_allowed"] is False
+ assert receipt["database_write_allowed"] is False
+ assert receipt["database_apply_authorized"] is False
+ assert receipt["executes_database_apply"] is False
+ assert receipt["executes_endpoint"] is False
+ assert receipt["executes_sql"] is False
+ assert receipt["writes_database"] is False
+ assert receipt["stdout_included"] is False
+ assert receipt["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ ]
+ is True
+ )
+ assert contract["verifier_invocation_locked"] is True
+ assert contract["verifier_invoked"] is False
+ assert contract["verifier_receipt_present"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["executes_endpoint"] is False
+ assert contract["executes_sql"] is False
+ assert contract["database_apply_authorized"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_invocation_now"] is False
+ assert "frozen_envelope_verifier_handoff_closeout_ready" in check_keys
+ assert "verifier_invocation_lock_proof_ready" in check_keys
+ assert "verifier_invocation_lock_proof_no_execute" in check_keys
+ assert "verifier_no_execution_receipt_proof_bound" in check_keys
+ assert "verifier_no_execution_receipt_proof_blocks_execution" in check_keys
+ assert "preview_has_no_side_effects_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["reads_secret_in_preview"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout_waits_without_ready_receipt_proof():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ ]
+ receipt_closeout = closeout[
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ ]
+ guard = receipt_closeout["verifier_receipt_persistence_guard_proof"]
+ contract = closeout[
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_INVOCATION_LOCK_PROOF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["verifier_receipt_persistence_guard_proof_count"] == 1
+ assert closeout["summary"]["verifier_receipt_persistence_guard_proof_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_persistence_locked_count"] == 1
+ assert closeout["summary"]["verifier_receipt_persistence_allowed_count"] == 0
+ assert closeout["summary"]["verifier_receipt_persisted_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["verifier_receipt_present_count"] == 0
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["verifier_no_execution_receipt_proof_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout-"
+ )
+ assert future["verifier_receipt_persistence_guard_proof_id"].endswith(
+ "-verifier-receipt-persistence-guard-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ ]
+ is False
+ )
+ assert future["verifier_no_execution_receipt_proof_closeout_ready"] is False
+ assert future["verifier_invocation_lock_proof_closeout_ready"] is False
+ assert future["verifier_receipt_persistence_guard_proof_bound"] is False
+ assert future["verifier_receipt_persistence_locked"] is True
+ assert future["verifier_receipt_persistence_allowed"] is False
+ assert future["verifier_receipt_persisted"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["verifier_invoked"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_receipt_persistence_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert receipt_closeout["authorization_material_type"] == (
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ )
+ assert receipt_closeout["verifier_no_execution_receipt_proof_closeout_only"] is True
+ assert receipt_closeout["verifier_receipt_persistence_guard_proof_only"] is True
+ assert receipt_closeout["verifier_receipt_persistence_locked"] is True
+ assert receipt_closeout["verifier_receipt_persistence_allowed"] is False
+ assert receipt_closeout["verifier_receipt_persisted"] is False
+ assert receipt_closeout["persists_verifier_receipt"] is False
+ assert receipt_closeout["verifier_invoked"] is False
+ assert receipt_closeout["endpoint_executed"] is False
+ assert receipt_closeout["sql_executed"] is False
+ assert receipt_closeout["database_written"] is False
+ assert guard["guard_status"] == "verifier_receipt_persistence_guard_proof_preview_ready"
+ assert guard["guard_mode"] == "verifier_receipt_persistence_guard_proof_preview_only"
+ assert guard["verifier_receipt_persistence_guard_proof_field_count"] == 12
+ assert guard["verifier_receipt_persistence_locked"] is True
+ assert guard["verifier_receipt_persistence_allowed"] is False
+ assert guard["verifier_receipt_persisted"] is False
+ assert guard["persists_verifier_receipt"] is False
+ assert guard["verifier_invoked"] is False
+ assert guard["endpoint_executed"] is False
+ assert guard["sql_executed"] is False
+ assert guard["database_written"] is False
+ assert guard["ready_for_verifier_receipt_persistence_now"] is False
+ assert guard["endpoint_execution_allowed"] is False
+ assert guard["sql_execution_allowed"] is False
+ assert guard["database_write_allowed"] is False
+ assert guard["database_apply_authorized"] is False
+ assert guard["stdout_included"] is False
+ assert guard["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ ]
+ is False
+ )
+ assert contract["verifier_receipt_persistence_locked"] is True
+ assert contract["verifier_receipt_persisted"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_receipt_persistence_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "verifier_invocation_lock_proof_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "verifier_no_execution_receipt_proof_ready" in check_keys
+ assert "verifier_no_execution_receipt_proof_no_execute" in check_keys
+ assert "verifier_receipt_persistence_guard_proof_bound" in check_keys
+ assert "verifier_receipt_persistence_guard_proof_blocks_persistence" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "verifier_invocation_lock_proof_closeout_contract_blocks_persistence_and_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_persistence_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_after_fake_fetch_but_persistence_is_locked():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ ]
+ receipt_closeout = closeout[
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ ]
+ guard = receipt_closeout["verifier_receipt_persistence_guard_proof"]
+ receipt = receipt_closeout["verifier_no_execution_receipt_proof"]
+ source_closeout = receipt_closeout["verifier_invocation_lock_proof_closeout"]
+ contract = closeout[
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_NO_EXECUTION_RECEIPT_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["verifier_receipt_persistence_guard_proof_count"] == 1
+ assert closeout["summary"]["verifier_receipt_persistence_guard_proof_field_count"] == 12
+ assert closeout["summary"]["verifier_receipt_persistence_locked_count"] == 1
+ assert closeout["summary"]["verifier_receipt_persistence_allowed_count"] == 0
+ assert closeout["summary"]["verifier_receipt_persisted_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["verifier_receipt_present_count"] == 0
+ assert closeout["summary"]["dry_run_executor_invoked_count"] == 0
+ assert closeout["summary"]["runner_invocation_performed_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ ]
+ is True
+ )
+ assert future["verifier_no_execution_receipt_proof_closeout_ready"] is True
+ assert future["verifier_invocation_lock_proof_closeout_ready"] is True
+ assert future["verifier_no_execution_receipt_proof_ready"] is True
+ assert future["verifier_receipt_persistence_guard_proof_bound"] is True
+ assert future["verifier_receipt_persistence_locked"] is True
+ assert future["verifier_receipt_persistence_allowed"] is False
+ assert future["verifier_receipt_persisted"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["verifier_invoked"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_receipt_persistence_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert receipt_closeout["ready_for_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"] is True
+ assert receipt_closeout["verifier_no_execution_receipt_proof_closeout_field_count"] == 12
+ assert receipt_closeout["verifier_no_execution_receipt_proof_closeout_acceptance_gate_count"] == 10
+ assert receipt_closeout["verifier_receipt_persistence_guard_proof_count"] == 1
+ assert receipt_closeout["verifier_receipt_persistence_guard_proof_field_count"] == 12
+ assert receipt_closeout["verifier_no_execution_receipt_proof_closeout_only"] is True
+ assert receipt_closeout["verifier_receipt_persistence_guard_proof_only"] is True
+ assert receipt_closeout["verifier_receipt_persistence_locked"] is True
+ assert receipt_closeout["verifier_receipt_persistence_allowed"] is False
+ assert receipt_closeout["verifier_receipt_persisted"] is False
+ assert receipt_closeout["persists_verifier_receipt"] is False
+ assert receipt_closeout["verifier_invoked"] is False
+ assert receipt_closeout["endpoint_executed"] is False
+ assert receipt_closeout["sql_executed"] is False
+ assert receipt_closeout["database_written"] is False
+ assert guard["guard_proof_id"] == future["verifier_receipt_persistence_guard_proof_id"]
+ assert (
+ guard["source_verifier_no_execution_receipt_proof_closeout_id"]
+ == future["verifier_no_execution_receipt_proof_closeout_id"]
+ )
+ assert (
+ guard["source_verifier_invocation_lock_proof_closeout_id"]
+ == source_closeout["verifier_invocation_lock_proof_closeout_id"]
+ )
+ assert guard["source_verifier_no_execution_receipt_proof_id"] == receipt["receipt_proof_id"]
+ assert guard["required_command_shape_hash"] == receipt["required_command_shape_hash"]
+ assert guard["guard_status"] == "verifier_receipt_persistence_guard_proof_preview_ready"
+ assert guard["guard_mode"] == "verifier_receipt_persistence_guard_proof_preview_only"
+ assert guard["verifier_receipt_persistence_locked"] is True
+ assert guard["verifier_receipt_persistence_allowed"] is False
+ assert guard["verifier_receipt_persisted"] is False
+ assert guard["persists_verifier_receipt"] is False
+ assert guard["persistence_receipt_present"] is False
+ assert guard["verifier_invoked"] is False
+ assert guard["endpoint_executed"] is False
+ assert guard["sql_executed"] is False
+ assert guard["database_written"] is False
+ assert guard["ready_for_verifier_receipt_persistence_now"] is False
+ assert guard["endpoint_execution_allowed"] is False
+ assert guard["sql_execution_allowed"] is False
+ assert guard["database_write_allowed"] is False
+ assert guard["database_apply_authorized"] is False
+ assert guard["executes_database_apply"] is False
+ assert guard["executes_endpoint"] is False
+ assert guard["executes_sql"] is False
+ assert guard["writes_database"] is False
+ assert guard["stdout_included"] is False
+ assert guard["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ ]
+ is True
+ )
+ assert contract["verifier_receipt_persistence_locked"] is True
+ assert contract["verifier_receipt_persistence_allowed"] is False
+ assert contract["verifier_receipt_persisted"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_receipt_persistence_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "verifier_no_execution_receipt_proof_ready" in check_keys
+ assert "verifier_no_execution_receipt_proof_no_execute" in check_keys
+ assert "verifier_receipt_persistence_guard_proof_bound" in check_keys
+ assert "verifier_receipt_persistence_guard_proof_blocks_persistence" in check_keys
+ assert "preview_has_no_side_effects_no_persistence_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_waits_without_ready_storage_boundary():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ ]
+ storage_closeout = closeout[
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ ]
+ storage = storage_closeout["receipt_persistence_storage_boundary_proof"]
+ contract = closeout[
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_NO_EXECUTION_RECEIPT_PROOF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["receipt_persistence_storage_boundary_proof_count"] == 1
+ assert closeout["summary"]["receipt_persistence_storage_boundary_proof_field_count"] == 12
+ assert closeout["summary"]["receipt_persistence_storage_boundary_locked_count"] == 1
+ assert closeout["summary"]["receipt_persistence_storage_write_allowed_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_written_count"] == 0
+ assert closeout["summary"]["verifier_receipt_persistence_allowed_count"] == 0
+ assert closeout["summary"]["verifier_receipt_persisted_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["verifier_receipt_persistence_guard_proof_closeout_id"].startswith(
+ "pchome-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout-"
+ )
+ assert future["receipt_persistence_storage_boundary_proof_id"].endswith(
+ "-receipt-persistence-storage-boundary-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ ]
+ is False
+ )
+ assert future["receipt_persistence_storage_boundary_locked"] is True
+ assert future["receipt_persistence_storage_write_allowed"] is False
+ assert future["receipt_persistence_storage_written"] is False
+ assert future["verifier_receipt_persistence_allowed"] is False
+ assert future["verifier_receipt_persisted"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_receipt_persistence_storage_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert storage_closeout["verifier_receipt_persistence_guard_proof_closeout_only"] is True
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_only"] is True
+ assert storage_closeout["receipt_persistence_storage_boundary_locked"] is True
+ assert storage_closeout["receipt_persistence_storage_write_allowed"] is False
+ assert storage_closeout["receipt_persistence_storage_written"] is False
+ assert storage_closeout["persists_verifier_receipt"] is False
+ assert storage_closeout["endpoint_executed"] is False
+ assert storage_closeout["sql_executed"] is False
+ assert storage_closeout["database_written"] is False
+ assert storage["storage_boundary_status"] == "receipt_persistence_storage_boundary_proof_preview_ready"
+ assert storage["storage_boundary_mode"] == "receipt_persistence_storage_boundary_proof_preview_only"
+ assert storage["receipt_persistence_storage_boundary_proof_field_count"] == 12
+ assert storage["receipt_persistence_storage_boundary_locked"] is True
+ assert storage["receipt_persistence_storage_write_allowed"] is False
+ assert storage["receipt_persistence_storage_written"] is False
+ assert storage["verifier_receipt_persistence_allowed"] is False
+ assert storage["verifier_receipt_persisted"] is False
+ assert storage["persists_verifier_receipt"] is False
+ assert storage["endpoint_execution_allowed"] is False
+ assert storage["sql_execution_allowed"] is False
+ assert storage["database_write_allowed"] is False
+ assert storage["database_apply_authorized"] is False
+ assert storage["stdout_included"] is False
+ assert storage["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ ]
+ is False
+ )
+ assert contract["receipt_persistence_storage_boundary_locked"] is True
+ assert contract["receipt_persistence_storage_write_allowed"] is False
+ assert contract["receipt_persistence_storage_written"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_receipt_persistence_storage_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "verifier_no_execution_receipt_proof_closeout_ready" in check_keys
+ assert "source_chain_ids_match" in check_keys
+ assert "verifier_receipt_persistence_guard_proof_ready" in check_keys
+ assert "verifier_receipt_persistence_guard_proof_no_persistence" in check_keys
+ assert "receipt_persistence_storage_boundary_proof_bound" in check_keys
+ assert "receipt_persistence_storage_boundary_proof_blocks_storage" in check_keys
+ assert "previous_closeouts_carried_forward" in check_keys
+ assert "target_migration_hash_locked" in check_keys
+ assert "rollback_and_post_apply_verifier_bound" in check_keys
+ assert "verifier_no_execution_receipt_proof_closeout_contract_blocks_storage_persistence_and_database_apply" in check_keys
+ assert "preview_has_no_side_effects_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_after_fake_fetch_but_storage_is_locked():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ ]
+ storage_closeout = closeout[
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ ]
+ storage = storage_closeout["receipt_persistence_storage_boundary_proof"]
+ guard = storage_closeout["verifier_receipt_persistence_guard_proof"]
+ contract = closeout[
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_RECEIPT_PERSISTENCE_GUARD_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["receipt_persistence_storage_boundary_proof_count"] == 1
+ assert closeout["summary"]["receipt_persistence_storage_boundary_proof_field_count"] == 12
+ assert closeout["summary"]["receipt_persistence_storage_boundary_locked_count"] == 1
+ assert closeout["summary"]["receipt_persistence_storage_write_allowed_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_written_count"] == 0
+ assert closeout["summary"]["verifier_receipt_persistence_allowed_count"] == 0
+ assert closeout["summary"]["verifier_receipt_persisted_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["endpoint_executed_count"] == 0
+ assert closeout["summary"]["sql_executed_count"] == 0
+ assert closeout["summary"]["database_written_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert closeout["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ ]
+ is True
+ )
+ assert future["verifier_receipt_persistence_guard_proof_closeout_ready"] is True
+ assert future["verifier_no_execution_receipt_proof_closeout_ready"] is True
+ assert future["verifier_receipt_persistence_guard_proof_ready"] is True
+ assert future["receipt_persistence_storage_boundary_proof_bound"] is True
+ assert future["receipt_persistence_storage_boundary_locked"] is True
+ assert future["receipt_persistence_storage_write_allowed"] is False
+ assert future["receipt_persistence_storage_written"] is False
+ assert future["verifier_receipt_persistence_allowed"] is False
+ assert future["verifier_receipt_persisted"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_receipt_persistence_storage_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert storage_closeout["ready_for_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"] is True
+ assert storage_closeout["verifier_receipt_persistence_guard_proof_closeout_field_count"] == 12
+ assert storage_closeout["verifier_receipt_persistence_guard_proof_closeout_acceptance_gate_count"] == 10
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_count"] == 1
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_field_count"] == 12
+ assert storage_closeout["verifier_receipt_persistence_guard_proof_closeout_only"] is True
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_only"] is True
+ assert storage_closeout["receipt_persistence_storage_boundary_locked"] is True
+ assert storage_closeout["receipt_persistence_storage_write_allowed"] is False
+ assert storage_closeout["receipt_persistence_storage_written"] is False
+ assert storage_closeout["verifier_receipt_persistence_allowed"] is False
+ assert storage_closeout["verifier_receipt_persisted"] is False
+ assert storage_closeout["persists_verifier_receipt"] is False
+ assert storage_closeout["endpoint_executed"] is False
+ assert storage_closeout["sql_executed"] is False
+ assert storage_closeout["database_written"] is False
+ assert storage["storage_boundary_proof_id"] == future["receipt_persistence_storage_boundary_proof_id"]
+ assert (
+ storage["source_verifier_receipt_persistence_guard_proof_closeout_id"]
+ == future["verifier_receipt_persistence_guard_proof_closeout_id"]
+ )
+ assert (
+ storage["source_verifier_receipt_persistence_guard_proof_id"]
+ == guard["guard_proof_id"]
+ )
+ assert storage["required_command_shape_hash"] == guard["required_command_shape_hash"]
+ assert storage["storage_boundary_status"] == "receipt_persistence_storage_boundary_proof_preview_ready"
+ assert storage["storage_boundary_mode"] == "receipt_persistence_storage_boundary_proof_preview_only"
+ assert storage["receipt_persistence_storage_boundary_locked"] is True
+ assert storage["receipt_persistence_storage_write_allowed"] is False
+ assert storage["receipt_persistence_storage_written"] is False
+ assert storage["verifier_receipt_persistence_allowed"] is False
+ assert storage["verifier_receipt_persisted"] is False
+ assert storage["persists_verifier_receipt"] is False
+ assert storage["endpoint_executed"] is False
+ assert storage["sql_executed"] is False
+ assert storage["database_written"] is False
+ assert storage["ready_for_receipt_persistence_storage_now"] is False
+ assert storage["endpoint_execution_allowed"] is False
+ assert storage["sql_execution_allowed"] is False
+ assert storage["database_write_allowed"] is False
+ assert storage["database_apply_authorized"] is False
+ assert storage["executes_database_apply"] is False
+ assert storage["executes_endpoint"] is False
+ assert storage["executes_sql"] is False
+ assert storage["writes_database"] is False
+ assert storage["stdout_included"] is False
+ assert storage["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ ]
+ is True
+ )
+ assert contract["receipt_persistence_storage_boundary_locked"] is True
+ assert contract["receipt_persistence_storage_write_allowed"] is False
+ assert contract["receipt_persistence_storage_written"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_receipt_persistence_storage_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "verifier_receipt_persistence_guard_proof_ready" in check_keys
+ assert "verifier_receipt_persistence_guard_proof_no_persistence" in check_keys
+ assert "receipt_persistence_storage_boundary_proof_bound" in check_keys
+ assert "receipt_persistence_storage_boundary_proof_blocks_storage" in check_keys
+ assert "preview_has_no_side_effects_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_waits_without_ready_ledger():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ ]
+ storage_closeout = closeout[
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ ]
+ ledger = storage_closeout["storage_boundary_no_write_ledger_proof"]
+ contract = closeout[
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_VERIFIER_RECEIPT_PERSISTENCE_GUARD_PROOF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["storage_boundary_no_write_ledger_proof_count"] == 1
+ assert closeout["summary"]["storage_boundary_no_write_ledger_proof_field_count"] == 12
+ assert closeout["summary"]["storage_boundary_write_locked_count"] == 1
+ assert closeout["summary"]["storage_boundary_write_allowed_count"] == 0
+ assert closeout["summary"]["storage_boundary_written_count"] == 0
+ assert closeout["summary"]["ledger_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_written_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_write_allowed_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_written_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future["storage_boundary_no_write_ledger_proof_id"].endswith(
+ "-storage-boundary-no-write-ledger-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ ]
+ is False
+ )
+ assert future["storage_boundary_write_locked"] is True
+ assert future["storage_boundary_write_allowed"] is False
+ assert future["storage_boundary_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["receipt_persistence_storage_write_allowed"] is False
+ assert future["receipt_persistence_storage_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_closeout_only"] is True
+ assert storage_closeout["storage_boundary_no_write_ledger_proof_only"] is True
+ assert storage_closeout["storage_boundary_no_write_ledger_proof_count"] == 1
+ assert storage_closeout["storage_boundary_no_write_ledger_proof_field_count"] == 12
+ assert storage_closeout["storage_boundary_write_locked"] is True
+ assert storage_closeout["storage_boundary_write_allowed"] is False
+ assert storage_closeout["storage_boundary_written"] is False
+ assert storage_closeout["ledger_write_allowed"] is False
+ assert storage_closeout["ledger_written"] is False
+ assert storage_closeout["receipt_persistence_storage_write_allowed"] is False
+ assert storage_closeout["receipt_persistence_storage_written"] is False
+ assert storage_closeout["persists_verifier_receipt"] is False
+ assert storage_closeout["endpoint_executed"] is False
+ assert storage_closeout["sql_executed"] is False
+ assert storage_closeout["database_written"] is False
+ assert ledger["ledger_status"] == "storage_boundary_no_write_ledger_proof_preview_ready"
+ assert ledger["ledger_mode"] == "storage_boundary_no_write_ledger_proof_preview_only"
+ assert ledger["storage_boundary_write_locked"] is True
+ assert ledger["storage_boundary_write_allowed"] is False
+ assert ledger["storage_boundary_written"] is False
+ assert ledger["ledger_write_allowed"] is False
+ assert ledger["ledger_written"] is False
+ assert ledger["receipt_persistence_storage_write_allowed"] is False
+ assert ledger["receipt_persistence_storage_written"] is False
+ assert ledger["persists_verifier_receipt"] is False
+ assert ledger["endpoint_executed"] is False
+ assert ledger["sql_executed"] is False
+ assert ledger["database_written"] is False
+ assert ledger["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ ]
+ is False
+ )
+ assert contract["storage_boundary_write_allowed"] is False
+ assert contract["ledger_write_allowed"] is False
+ assert contract["receipt_persistence_storage_write_allowed"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "verifier_receipt_persistence_guard_proof_closeout_ready" in check_keys
+ assert "receipt_persistence_storage_boundary_proof_ready" in check_keys
+ assert "receipt_persistence_storage_boundary_proof_no_write" in check_keys
+ assert "storage_boundary_no_write_ledger_proof_bound" in check_keys
+ assert "storage_boundary_no_write_ledger_proof_blocks_write" in check_keys
+ assert "preview_has_no_side_effects_no_ledger_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_after_fake_fetch_but_ledger_write_is_locked():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ ]
+ storage_closeout = closeout[
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ ]
+ previous_storage_closeout = storage_closeout[
+ "verifier_receipt_persistence_guard_proof_closeout"
+ ]
+ storage = storage_closeout["receipt_persistence_storage_boundary_proof"]
+ ledger = storage_closeout["storage_boundary_no_write_ledger_proof"]
+ contract = closeout[
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_PERSISTENCE_STORAGE_BOUNDARY_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["storage_boundary_no_write_ledger_proof_count"] == 1
+ assert closeout["summary"]["storage_boundary_no_write_ledger_proof_field_count"] == 12
+ assert closeout["summary"]["storage_boundary_write_locked_count"] == 1
+ assert closeout["summary"]["storage_boundary_write_allowed_count"] == 0
+ assert closeout["summary"]["storage_boundary_written_count"] == 0
+ assert closeout["summary"]["ledger_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_written_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_write_allowed_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_written_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+ ]
+ is True
+ )
+ assert future["receipt_persistence_storage_boundary_proof_closeout_ready"] is True
+ assert future["verifier_receipt_persistence_guard_proof_closeout_ready"] is True
+ assert future["receipt_persistence_storage_boundary_proof_ready"] is True
+ assert future["storage_boundary_no_write_ledger_proof_bound"] is True
+ assert future["storage_boundary_write_locked"] is True
+ assert future["storage_boundary_write_allowed"] is False
+ assert future["storage_boundary_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["receipt_persistence_storage_write_allowed"] is False
+ assert future["receipt_persistence_storage_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_closeout_field_count"] == 12
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_closeout_acceptance_gate_count"] == 10
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_closeout_only"] is True
+ assert storage_closeout["storage_boundary_no_write_ledger_proof_only"] is True
+ assert storage_closeout["source_verifier_receipt_persistence_guard_proof_closeout_id"] == previous_storage_closeout["verifier_receipt_persistence_guard_proof_closeout_id"]
+ assert storage_closeout["source_receipt_persistence_storage_boundary_proof_id"] == storage["storage_boundary_proof_id"]
+ assert ledger["source_receipt_persistence_storage_boundary_proof_closeout_id"] == storage_closeout["receipt_persistence_storage_boundary_proof_closeout_id"]
+ assert ledger["source_verifier_receipt_persistence_guard_proof_closeout_id"] == previous_storage_closeout["verifier_receipt_persistence_guard_proof_closeout_id"]
+ assert ledger["source_receipt_persistence_storage_boundary_proof_id"] == storage["storage_boundary_proof_id"]
+ assert ledger["required_command_shape_hash"] == storage["required_command_shape_hash"]
+ assert ledger["ledger_status"] == "storage_boundary_no_write_ledger_proof_preview_ready"
+ assert ledger["ledger_mode"] == "storage_boundary_no_write_ledger_proof_preview_only"
+ assert ledger["storage_boundary_write_locked"] is True
+ assert ledger["storage_boundary_write_allowed"] is False
+ assert ledger["storage_boundary_written"] is False
+ assert ledger["ledger_write_allowed"] is False
+ assert ledger["ledger_written"] is False
+ assert ledger["receipt_persistence_storage_write_allowed"] is False
+ assert ledger["receipt_persistence_storage_written"] is False
+ assert ledger["verifier_receipt_persistence_allowed"] is False
+ assert ledger["verifier_receipt_persisted"] is False
+ assert ledger["persists_verifier_receipt"] is False
+ assert ledger["verifier_invoked"] is False
+ assert ledger["dry_run_executor_invoked"] is False
+ assert ledger["runner_invocation_performed"] is False
+ assert ledger["endpoint_executed"] is False
+ assert ledger["sql_executed"] is False
+ assert ledger["database_written"] is False
+ assert ledger["ready_for_database_apply_now"] is False
+ assert ledger["ready_for_storage_boundary_ledger_write_now"] is False
+ assert ledger["ready_for_receipt_persistence_storage_now"] is False
+ assert ledger["endpoint_execution_allowed"] is False
+ assert ledger["sql_execution_allowed"] is False
+ assert ledger["database_write_allowed"] is False
+ assert ledger["database_apply_authorized"] is False
+ assert ledger["stdout_included"] is False
+ assert ledger["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ ]
+ is True
+ )
+ assert contract["storage_boundary_write_allowed"] is False
+ assert contract["ledger_write_allowed"] is False
+ assert contract["receipt_persistence_storage_write_allowed"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_storage_boundary_ledger_write_now"] is False
+ assert contract["ready_for_receipt_persistence_storage_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "storage_boundary_no_write_ledger_proof_bound" in check_keys
+ assert "storage_boundary_no_write_ledger_proof_blocks_write" in check_keys
+ assert "preview_has_no_side_effects_no_ledger_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_waits_without_ready_retention():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ ]
+ ledger_closeout = closeout[
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+ ]
+ retention = ledger_closeout["no_write_ledger_retention_proof"]
+ contract = closeout[
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_RECEIPT_PERSISTENCE_STORAGE_BOUNDARY_PROOF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["no_write_ledger_retention_proof_count"] == 1
+ assert closeout["summary"]["no_write_ledger_retention_proof_field_count"] == 12
+ assert closeout["summary"]["ledger_retention_write_locked_count"] == 1
+ assert closeout["summary"]["ledger_retention_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_retention_written_count"] == 0
+ assert closeout["summary"]["ledger_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_written_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_write_allowed_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_written_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future["no_write_ledger_retention_proof_id"].endswith(
+ "-no-write-ledger-retention-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ ]
+ is False
+ )
+ assert future["ledger_retention_write_locked"] is True
+ assert future["ledger_retention_write_allowed"] is False
+ assert future["ledger_retention_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["receipt_persistence_storage_write_allowed"] is False
+ assert future["receipt_persistence_storage_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert ledger_closeout["storage_boundary_no_write_ledger_proof_closeout_only"] is True
+ assert ledger_closeout["no_write_ledger_retention_proof_only"] is True
+ assert ledger_closeout["no_write_ledger_retention_proof_count"] == 1
+ assert ledger_closeout["no_write_ledger_retention_proof_field_count"] == 12
+ assert ledger_closeout["ledger_retention_write_locked"] is True
+ assert ledger_closeout["ledger_retention_write_allowed"] is False
+ assert ledger_closeout["ledger_retention_written"] is False
+ assert ledger_closeout["ledger_write_allowed"] is False
+ assert ledger_closeout["ledger_written"] is False
+ assert ledger_closeout["persists_verifier_receipt"] is False
+ assert ledger_closeout["endpoint_executed"] is False
+ assert ledger_closeout["sql_executed"] is False
+ assert ledger_closeout["database_written"] is False
+ assert retention["retention_status"] == "no_write_ledger_retention_proof_preview_ready"
+ assert retention["retention_mode"] == "no_write_ledger_retention_proof_preview_only"
+ assert retention["ledger_retention_write_locked"] is True
+ assert retention["ledger_retention_write_allowed"] is False
+ assert retention["ledger_retention_written"] is False
+ assert retention["ledger_write_allowed"] is False
+ assert retention["ledger_written"] is False
+ assert retention["receipt_persistence_storage_write_allowed"] is False
+ assert retention["receipt_persistence_storage_written"] is False
+ assert retention["persists_verifier_receipt"] is False
+ assert retention["endpoint_executed"] is False
+ assert retention["sql_executed"] is False
+ assert retention["database_written"] is False
+ assert retention["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ ]
+ is False
+ )
+ assert contract["ledger_retention_write_allowed"] is False
+ assert contract["ledger_retention_written"] is False
+ assert contract["ledger_write_allowed"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "receipt_persistence_storage_boundary_proof_closeout_ready" in check_keys
+ assert "storage_boundary_no_write_ledger_proof_ready" in check_keys
+ assert "storage_boundary_no_write_ledger_proof_no_write" in check_keys
+ assert "no_write_ledger_retention_proof_bound" in check_keys
+ assert "no_write_ledger_retention_proof_blocks_persistence" in check_keys
+ assert "preview_has_no_side_effects_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert "manual_review_not_required_for_safe_preview" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_after_fake_fetch_but_retention_write_is_locked():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ ]
+ ledger_closeout = closeout[
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+ ]
+ source_closeout = ledger_closeout[
+ "receipt_persistence_storage_boundary_proof_closeout"
+ ]
+ ledger = ledger_closeout["storage_boundary_no_write_ledger_proof"]
+ retention = ledger_closeout["no_write_ledger_retention_proof"]
+ contract = closeout[
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_STORAGE_BOUNDARY_NO_WRITE_LEDGER_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["no_write_ledger_retention_proof_count"] == 1
+ assert closeout["summary"]["no_write_ledger_retention_proof_field_count"] == 12
+ assert closeout["summary"]["ledger_retention_write_locked_count"] == 1
+ assert closeout["summary"]["ledger_retention_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_retention_written_count"] == 0
+ assert closeout["summary"]["ledger_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_written_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_write_allowed_count"] == 0
+ assert closeout["summary"]["receipt_persistence_storage_written_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout"
+ ]
+ is True
+ )
+ assert future["storage_boundary_no_write_ledger_proof_closeout_ready"] is True
+ assert future["receipt_persistence_storage_boundary_proof_closeout_ready"] is True
+ assert future["storage_boundary_no_write_ledger_proof_ready"] is True
+ assert future["no_write_ledger_retention_proof_bound"] is True
+ assert future["ledger_retention_write_locked"] is True
+ assert future["ledger_retention_write_allowed"] is False
+ assert future["ledger_retention_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert ledger_closeout["storage_boundary_no_write_ledger_proof_closeout_field_count"] == 12
+ assert ledger_closeout["storage_boundary_no_write_ledger_proof_closeout_acceptance_gate_count"] == 10
+ assert ledger_closeout["storage_boundary_no_write_ledger_proof_closeout_only"] is True
+ assert ledger_closeout["no_write_ledger_retention_proof_only"] is True
+ assert ledger_closeout["source_receipt_persistence_storage_boundary_proof_closeout_id"] == source_closeout["receipt_persistence_storage_boundary_proof_closeout_id"]
+ assert ledger_closeout["source_storage_boundary_no_write_ledger_proof_id"] == ledger["ledger_proof_id"]
+ assert retention["source_storage_boundary_no_write_ledger_proof_closeout_id"] == ledger_closeout["storage_boundary_no_write_ledger_proof_closeout_id"]
+ assert retention["source_storage_boundary_no_write_ledger_proof_id"] == ledger["ledger_proof_id"]
+ assert retention["source_receipt_persistence_storage_boundary_proof_closeout_id"] == source_closeout["receipt_persistence_storage_boundary_proof_closeout_id"]
+ assert retention["required_command_shape_hash"] == ledger["required_command_shape_hash"]
+ assert retention["retention_status"] == "no_write_ledger_retention_proof_preview_ready"
+ assert retention["retention_mode"] == "no_write_ledger_retention_proof_preview_only"
+ assert retention["ledger_retention_write_locked"] is True
+ assert retention["ledger_retention_write_allowed"] is False
+ assert retention["ledger_retention_written"] is False
+ assert retention["retention_receipt_present"] is False
+ assert retention["retention_receipt_required"] is False
+ assert retention["ledger_write_allowed"] is False
+ assert retention["ledger_written"] is False
+ assert retention["receipt_persistence_storage_write_allowed"] is False
+ assert retention["receipt_persistence_storage_written"] is False
+ assert retention["verifier_receipt_persistence_allowed"] is False
+ assert retention["verifier_receipt_persisted"] is False
+ assert retention["persists_verifier_receipt"] is False
+ assert retention["verifier_invoked"] is False
+ assert retention["dry_run_executor_invoked"] is False
+ assert retention["runner_invocation_performed"] is False
+ assert retention["endpoint_executed"] is False
+ assert retention["sql_executed"] is False
+ assert retention["database_written"] is False
+ assert retention["ready_for_database_apply_now"] is False
+ assert retention["ready_for_no_write_ledger_retention_now"] is False
+ assert retention["ready_for_storage_boundary_ledger_write_now"] is False
+ assert retention["endpoint_execution_allowed"] is False
+ assert retention["sql_execution_allowed"] is False
+ assert retention["database_write_allowed"] is False
+ assert retention["database_apply_authorized"] is False
+ assert retention["stdout_included"] is False
+ assert retention["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ ]
+ is True
+ )
+ assert contract["ledger_retention_write_allowed"] is False
+ assert contract["ledger_retention_written"] is False
+ assert contract["ledger_write_allowed"] is False
+ assert contract["ledger_written"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_no_write_ledger_retention_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "no_write_ledger_retention_proof_bound" in check_keys
+ assert "no_write_ledger_retention_proof_blocks_persistence" in check_keys
+ assert "preview_has_no_side_effects_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+def test_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout_waits_without_ready_archive():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ ]
+ archive_closeout = closeout[
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout"
+ ]
+ archive = archive_closeout["retention_boundary_no_write_archive_proof"]
+ contract = closeout[
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_STORAGE_BOUNDARY_NO_WRITE_LEDGER_PROOF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["retention_boundary_no_write_archive_proof_count"] == 1
+ assert closeout["summary"]["retention_boundary_no_write_archive_proof_field_count"] == 12
+ assert closeout["summary"]["retention_archive_write_locked_count"] == 1
+ assert closeout["summary"]["retention_archive_write_allowed_count"] == 0
+ assert closeout["summary"]["retention_archive_written_count"] == 0
+ assert closeout["summary"]["ledger_retention_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_retention_written_count"] == 0
+ assert closeout["summary"]["ledger_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_written_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future["retention_boundary_no_write_archive_proof_id"].endswith(
+ "-retention-boundary-no-write-archive-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ ]
+ is False
+ )
+ assert future["retention_archive_write_locked"] is True
+ assert future["retention_archive_write_allowed"] is False
+ assert future["retention_archive_written"] is False
+ assert future["ledger_retention_write_allowed"] is False
+ assert future["ledger_retention_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert archive_closeout["no_write_ledger_retention_proof_closeout_only"] is True
+ assert archive_closeout["retention_boundary_no_write_archive_proof_only"] is True
+ assert archive_closeout["retention_boundary_no_write_archive_proof_count"] == 1
+ assert archive_closeout["retention_boundary_no_write_archive_proof_field_count"] == 12
+ assert archive_closeout["retention_archive_write_locked"] is True
+ assert archive_closeout["retention_archive_write_allowed"] is False
+ assert archive_closeout["retention_archive_written"] is False
+ assert archive_closeout["ledger_retention_write_allowed"] is False
+ assert archive_closeout["ledger_retention_written"] is False
+ assert archive_closeout["persists_verifier_receipt"] is False
+ assert archive_closeout["endpoint_executed"] is False
+ assert archive_closeout["sql_executed"] is False
+ assert archive_closeout["database_written"] is False
+ assert archive["archive_status"] == "retention_boundary_no_write_archive_proof_preview_ready"
+ assert archive["archive_mode"] == "retention_boundary_no_write_archive_proof_preview_only"
+ assert archive["retention_archive_write_locked"] is True
+ assert archive["retention_archive_write_allowed"] is False
+ assert archive["retention_archive_written"] is False
+ assert archive["ledger_retention_write_allowed"] is False
+ assert archive["ledger_retention_written"] is False
+ assert archive["ledger_write_allowed"] is False
+ assert archive["ledger_written"] is False
+ assert archive["persists_verifier_receipt"] is False
+ assert archive["endpoint_executed"] is False
+ assert archive["sql_executed"] is False
+ assert archive["database_written"] is False
+ assert archive["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ ]
+ is False
+ )
+ assert contract["retention_archive_write_allowed"] is False
+ assert contract["ledger_retention_write_allowed"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "storage_boundary_no_write_ledger_proof_closeout_ready" in check_keys
+ assert "no_write_ledger_retention_proof_ready" in check_keys
+ assert "no_write_ledger_retention_proof_no_write" in check_keys
+ assert "retention_boundary_no_write_archive_proof_bound" in check_keys
+ assert "retention_boundary_no_write_archive_proof_blocks_archive" in check_keys
+ assert "preview_has_no_side_effects_no_archive_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_after_fake_fetch_but_archive_write_is_locked():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ ]
+ archive_closeout = closeout[
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout"
+ ]
+ source_closeout = archive_closeout[
+ "storage_boundary_no_write_ledger_proof_closeout"
+ ]
+ retention = archive_closeout["no_write_ledger_retention_proof"]
+ archive = archive_closeout["retention_boundary_no_write_archive_proof"]
+ contract = closeout[
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_LEDGER_RETENTION_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["retention_boundary_no_write_archive_proof_count"] == 1
+ assert closeout["summary"]["retention_boundary_no_write_archive_proof_field_count"] == 12
+ assert closeout["summary"]["retention_archive_write_locked_count"] == 1
+ assert closeout["summary"]["retention_archive_write_allowed_count"] == 0
+ assert closeout["summary"]["retention_archive_written_count"] == 0
+ assert closeout["summary"]["ledger_retention_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_retention_written_count"] == 0
+ assert closeout["summary"]["ledger_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_written_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ ]
+ is True
+ )
+ assert future["no_write_ledger_retention_proof_closeout_ready"] is True
+ assert future["storage_boundary_no_write_ledger_proof_closeout_ready"] is True
+ assert future["no_write_ledger_retention_proof_ready"] is True
+ assert future["retention_boundary_no_write_archive_proof_bound"] is True
+ assert future["retention_archive_write_locked"] is True
+ assert future["retention_archive_write_allowed"] is False
+ assert future["retention_archive_written"] is False
+ assert future["ledger_retention_write_allowed"] is False
+ assert future["ledger_retention_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert archive_closeout["no_write_ledger_retention_proof_closeout_field_count"] == 12
+ assert archive_closeout["no_write_ledger_retention_proof_closeout_acceptance_gate_count"] == 10
+ assert archive_closeout["no_write_ledger_retention_proof_closeout_only"] is True
+ assert archive_closeout["retention_boundary_no_write_archive_proof_only"] is True
+ assert archive_closeout["source_storage_boundary_no_write_ledger_proof_closeout_id"] == source_closeout["storage_boundary_no_write_ledger_proof_closeout_id"]
+ assert archive_closeout["source_no_write_ledger_retention_proof_id"] == retention["retention_proof_id"]
+ assert archive["source_no_write_ledger_retention_proof_closeout_id"] == archive_closeout["no_write_ledger_retention_proof_closeout_id"]
+ assert archive["source_no_write_ledger_retention_proof_id"] == retention["retention_proof_id"]
+ assert archive["source_storage_boundary_no_write_ledger_proof_closeout_id"] == source_closeout["storage_boundary_no_write_ledger_proof_closeout_id"]
+ assert archive["required_command_shape_hash"] == retention["required_command_shape_hash"]
+ assert archive["archive_status"] == "retention_boundary_no_write_archive_proof_preview_ready"
+ assert archive["archive_mode"] == "retention_boundary_no_write_archive_proof_preview_only"
+ assert archive["retention_archive_write_locked"] is True
+ assert archive["retention_archive_write_allowed"] is False
+ assert archive["retention_archive_written"] is False
+ assert archive["archive_receipt_present"] is False
+ assert archive["archive_receipt_required"] is False
+ assert archive["ledger_retention_write_allowed"] is False
+ assert archive["ledger_retention_written"] is False
+ assert archive["ledger_write_allowed"] is False
+ assert archive["ledger_written"] is False
+ assert archive["receipt_persistence_storage_write_allowed"] is False
+ assert archive["receipt_persistence_storage_written"] is False
+ assert archive["verifier_receipt_persistence_allowed"] is False
+ assert archive["verifier_receipt_persisted"] is False
+ assert archive["persists_verifier_receipt"] is False
+ assert archive["verifier_invoked"] is False
+ assert archive["dry_run_executor_invoked"] is False
+ assert archive["runner_invocation_performed"] is False
+ assert archive["endpoint_executed"] is False
+ assert archive["sql_executed"] is False
+ assert archive["database_written"] is False
+ assert archive["ready_for_database_apply_now"] is False
+ assert archive["ready_for_retention_boundary_archive_now"] is False
+ assert archive["endpoint_execution_allowed"] is False
+ assert archive["sql_execution_allowed"] is False
+ assert archive["database_write_allowed"] is False
+ assert archive["database_apply_authorized"] is False
+ assert archive["stdout_included"] is False
+ assert archive["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ ]
+ is True
+ )
+ assert contract["retention_archive_write_allowed"] is False
+ assert contract["ledger_retention_write_allowed"] is False
+ assert contract["ledger_write_allowed"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_retention_boundary_archive_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "retention_boundary_no_write_archive_proof_bound" in check_keys
+ assert "retention_boundary_no_write_archive_proof_blocks_archive" in check_keys
+ assert "preview_has_no_side_effects_no_archive_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_waits_without_ready_handoff():
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout(
+ _payload(),
+ batch_size=1,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ]
+ handoff_closeout = closeout[
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ ]
+ handoff = handoff_closeout["archive_retention_sealed_handoff_proof"]
+ contract = closeout[
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "WAITING_FOR_DB_APPLY_CONTROLLED_DRY_RUN_NO_WRITE_LEDGER_RETENTION_PROOF_CLOSEOUT"
+ )
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_field_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_acceptance_gate_count"] == 10
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_count"] == 0
+ assert closeout["summary"]["archive_retention_sealed_handoff_proof_count"] == 1
+ assert closeout["summary"]["archive_retention_sealed_handoff_proof_field_count"] == 12
+ assert closeout["summary"]["sealed_handoff_write_locked_count"] == 1
+ assert closeout["summary"]["sealed_handoff_write_allowed_count"] == 0
+ assert closeout["summary"]["sealed_handoff_written_count"] == 0
+ assert closeout["summary"]["retention_archive_write_allowed_count"] == 0
+ assert closeout["summary"]["retention_archive_written_count"] == 0
+ assert closeout["summary"]["ledger_retention_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_retention_written_count"] == 0
+ assert closeout["summary"]["ledger_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_written_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert future["archive_retention_sealed_handoff_proof_id"].endswith(
+ "-archive-retention-sealed-handoff-proof"
+ )
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ]
+ is False
+ )
+ assert future["sealed_handoff_write_locked"] is True
+ assert future["sealed_handoff_write_allowed"] is False
+ assert future["sealed_handoff_written"] is False
+ assert future["retention_archive_write_allowed"] is False
+ assert future["retention_archive_written"] is False
+ assert future["ledger_retention_write_allowed"] is False
+ assert future["ledger_retention_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert handoff_closeout["retention_boundary_no_write_archive_proof_closeout_only"] is True
+ assert handoff_closeout["archive_retention_sealed_handoff_proof_only"] is True
+ assert handoff_closeout["archive_retention_sealed_handoff_proof_count"] == 1
+ assert handoff_closeout["archive_retention_sealed_handoff_proof_field_count"] == 12
+ assert handoff_closeout["sealed_handoff_write_locked"] is True
+ assert handoff_closeout["sealed_handoff_write_allowed"] is False
+ assert handoff_closeout["sealed_handoff_written"] is False
+ assert handoff_closeout["retention_archive_write_allowed"] is False
+ assert handoff_closeout["retention_archive_written"] is False
+ assert handoff_closeout["ledger_retention_write_allowed"] is False
+ assert handoff_closeout["ledger_retention_written"] is False
+ assert handoff_closeout["persists_verifier_receipt"] is False
+ assert handoff_closeout["endpoint_executed"] is False
+ assert handoff_closeout["sql_executed"] is False
+ assert handoff_closeout["database_written"] is False
+ assert handoff["handoff_status"] == "archive_retention_sealed_handoff_proof_preview_ready"
+ assert handoff["handoff_mode"] == "archive_retention_sealed_handoff_proof_preview_only"
+ assert len(handoff["sealed_handoff_manifest_hash"]) == 64
+ assert handoff["sealed_handoff_write_locked"] is True
+ assert handoff["sealed_handoff_write_allowed"] is False
+ assert handoff["sealed_handoff_written"] is False
+ assert handoff["retention_archive_write_allowed"] is False
+ assert handoff["retention_archive_written"] is False
+ assert handoff["ledger_retention_write_allowed"] is False
+ assert handoff["ledger_retention_written"] is False
+ assert handoff["ledger_write_allowed"] is False
+ assert handoff["ledger_written"] is False
+ assert handoff["persists_verifier_receipt"] is False
+ assert handoff["endpoint_executed"] is False
+ assert handoff["sql_executed"] is False
+ assert handoff["database_written"] is False
+ assert handoff["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ]
+ is False
+ )
+ assert contract["sealed_handoff_write_allowed"] is False
+ assert contract["retention_archive_write_allowed"] is False
+ assert contract["ledger_retention_write_allowed"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "no_write_ledger_retention_proof_closeout_ready" in check_keys
+ assert "retention_boundary_no_write_archive_proof_ready" in check_keys
+ assert "archive_retention_sealed_handoff_proof_bound" in check_keys
+ assert "archive_retention_sealed_handoff_proof_blocks_handoff_write" in check_keys
+ assert "sealed_handoff_has_nonsecret_machine_readable_manifest" in check_keys
+ assert "preview_has_no_side_effects_no_handoff_no_archive_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_ready_after_fake_fetch_but_sealed_handoff_write_is_locked():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ]
+ handoff_closeout = closeout[
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ ]
+ source_closeout = handoff_closeout[
+ "no_write_ledger_retention_proof_closeout"
+ ]
+ archive = handoff_closeout["retention_boundary_no_write_archive_proof"]
+ handoff = handoff_closeout["archive_retention_sealed_handoff_proof"]
+ contract = closeout[
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_RETENTION_BOUNDARY_NO_WRITE_ARCHIVE_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["archive_retention_sealed_handoff_proof_count"] == 1
+ assert closeout["summary"]["archive_retention_sealed_handoff_proof_field_count"] == 12
+ assert closeout["summary"]["sealed_handoff_write_locked_count"] == 1
+ assert closeout["summary"]["sealed_handoff_write_allowed_count"] == 0
+ assert closeout["summary"]["sealed_handoff_written_count"] == 0
+ assert closeout["summary"]["retention_archive_write_allowed_count"] == 0
+ assert closeout["summary"]["retention_archive_written_count"] == 0
+ assert closeout["summary"]["ledger_retention_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_retention_written_count"] == 0
+ assert closeout["summary"]["ledger_write_allowed_count"] == 0
+ assert closeout["summary"]["ledger_written_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+ ]
+ is True
+ )
+ assert future["retention_boundary_no_write_archive_proof_closeout_ready"] is True
+ assert future["no_write_ledger_retention_proof_closeout_ready"] is True
+ assert future["retention_boundary_no_write_archive_proof_ready"] is True
+ assert future["archive_retention_sealed_handoff_proof_bound"] is True
+ assert future["sealed_handoff_write_locked"] is True
+ assert future["sealed_handoff_write_allowed"] is False
+ assert future["sealed_handoff_written"] is False
+ assert future["retention_archive_write_allowed"] is False
+ assert future["retention_archive_written"] is False
+ assert future["ledger_retention_write_allowed"] is False
+ assert future["ledger_retention_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert handoff_closeout["retention_boundary_no_write_archive_proof_closeout_field_count"] == 12
+ assert handoff_closeout["retention_boundary_no_write_archive_proof_closeout_acceptance_gate_count"] == 10
+ assert handoff_closeout["retention_boundary_no_write_archive_proof_closeout_only"] is True
+ assert handoff_closeout["archive_retention_sealed_handoff_proof_only"] is True
+ assert handoff_closeout["source_no_write_ledger_retention_proof_closeout_id"] == source_closeout["no_write_ledger_retention_proof_closeout_id"]
+ assert handoff_closeout["source_retention_boundary_no_write_archive_proof_id"] == archive["archive_proof_id"]
+ assert handoff["source_no_write_ledger_retention_proof_closeout_id"] == source_closeout["no_write_ledger_retention_proof_closeout_id"]
+ assert handoff["source_retention_boundary_no_write_archive_proof_id"] == archive["archive_proof_id"]
+ assert handoff["sealed_handoff_manifest"]["source_retention_boundary_no_write_archive_proof_id"] == archive["archive_proof_id"]
+ assert len(handoff["sealed_handoff_manifest_hash"]) == 64
+ assert handoff["handoff_status"] == "archive_retention_sealed_handoff_proof_preview_ready"
+ assert handoff["handoff_mode"] == "archive_retention_sealed_handoff_proof_preview_only"
+ assert handoff["sealed_handoff_write_locked"] is True
+ assert handoff["sealed_handoff_write_allowed"] is False
+ assert handoff["sealed_handoff_written"] is False
+ assert handoff["retention_archive_write_allowed"] is False
+ assert handoff["retention_archive_written"] is False
+ assert handoff["ledger_retention_write_allowed"] is False
+ assert handoff["ledger_retention_written"] is False
+ assert handoff["ledger_write_allowed"] is False
+ assert handoff["ledger_written"] is False
+ assert handoff["persists_verifier_receipt"] is False
+ assert handoff["verifier_invoked"] is False
+ assert handoff["dry_run_executor_invoked"] is False
+ assert handoff["runner_invocation_performed"] is False
+ assert handoff["endpoint_executed"] is False
+ assert handoff["sql_executed"] is False
+ assert handoff["database_written"] is False
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["ready_for_archive_retention_sealed_handoff_write_now"] is False
+ assert handoff["endpoint_execution_allowed"] is False
+ assert handoff["sql_execution_allowed"] is False
+ assert handoff["database_write_allowed"] is False
+ assert handoff["database_apply_authorized"] is False
+ assert handoff["stdout_included"] is False
+ assert handoff["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ]
+ is True
+ )
+ assert contract["sealed_handoff_write_allowed"] is False
+ assert contract["retention_archive_write_allowed"] is False
+ assert contract["ledger_retention_write_allowed"] is False
+ assert contract["ledger_write_allowed"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_archive_retention_sealed_handoff_write_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "archive_retention_sealed_handoff_proof_bound" in check_keys
+ assert "archive_retention_sealed_handoff_proof_blocks_handoff_write" in check_keys
+ assert "sealed_handoff_has_nonsecret_machine_readable_manifest" in check_keys
+ assert "preview_has_no_side_effects_no_handoff_no_archive_no_retention_no_ledger_no_storage_no_persistence_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_ready_after_fake_fetch_but_verifier_transfer_is_locked():
+ class FakeResponse:
+ status_code = 200
+ encoding = "utf-8"
+ content = b"""
+
+
+
+ """
+
+ def fake_get(url, timeout, headers):
+ return FakeResponse()
+
+ closeout = (
+ build_pchome_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout(
+ _payload(),
+ batch_size=1,
+ execute_fetch=True,
+ http_get=fake_get,
+ )
+ )
+
+ future = closeout[
+ "future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ ]
+ transfer_closeout = closeout[
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+ ]
+ source_closeout = transfer_closeout[
+ "retention_boundary_no_write_archive_proof_closeout"
+ ]
+ handoff = transfer_closeout["archive_retention_sealed_handoff_proof"]
+ transfer = transfer_closeout["sealed_handoff_verifier_transfer_proof"]
+ contract = closeout[
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_contract"
+ ]
+ check_keys = [
+ check["key"]
+ for check in closeout[
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_checks"
+ ]
+ ]
+ assert closeout["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+ )
+ assert closeout["result"] == (
+ "DB_APPLY_CONTROLLED_DRY_RUN_ARCHIVE_RETENTION_SEALED_HANDOFF_PROOF_CLOSEOUT_READY"
+ )
+ assert closeout["summary"]["controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_pass_count"] == 12
+ assert closeout["summary"]["controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_waiting_count"] == 0
+ assert closeout["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_ready_count"] == 1
+ assert closeout["summary"]["sealed_handoff_verifier_transfer_proof_count"] == 1
+ assert closeout["summary"]["sealed_handoff_verifier_transfer_proof_field_count"] == 12
+ assert closeout["summary"]["sealed_handoff_manifest_hash_locked_count"] == 1
+ assert closeout["summary"]["verifier_transfer_write_locked_count"] == 1
+ assert closeout["summary"]["verifier_transfer_write_allowed_count"] == 0
+ assert closeout["summary"]["verifier_transfer_written_count"] == 0
+ assert closeout["summary"]["persists_verifier_receipt_count"] == 0
+ assert closeout["summary"]["verifier_invoked_count"] == 0
+ assert closeout["summary"]["executes_endpoint_count"] == 0
+ assert closeout["summary"]["executes_sql_count"] == 0
+ assert closeout["summary"]["writes_database_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ ]
+ is True
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof_closeout"
+ ]
+ is True
+ )
+ assert future["archive_retention_sealed_handoff_proof_closeout_ready"] is True
+ assert future["retention_boundary_no_write_archive_proof_closeout_ready"] is True
+ assert future["sealed_handoff_manifest_hash_locked"] is True
+ assert future["sealed_handoff_verifier_transfer_proof_bound"] is True
+ assert future["verifier_transfer_write_locked"] is True
+ assert future["verifier_transfer_write_allowed"] is False
+ assert future["verifier_transfer_written"] is False
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert transfer_closeout["archive_retention_sealed_handoff_proof_closeout_field_count"] == 12
+ assert transfer_closeout["archive_retention_sealed_handoff_proof_closeout_acceptance_gate_count"] == 10
+ assert transfer_closeout["archive_retention_sealed_handoff_proof_closeout_only"] is True
+ assert transfer_closeout["sealed_handoff_verifier_transfer_proof_only"] is True
+ assert transfer_closeout["source_retention_boundary_no_write_archive_proof_closeout_id"] == source_closeout["retention_boundary_no_write_archive_proof_closeout_id"]
+ assert transfer_closeout["source_archive_retention_sealed_handoff_proof_id"] == handoff["archive_retention_sealed_handoff_proof_id"]
+ assert transfer["source_retention_boundary_no_write_archive_proof_closeout_id"] == source_closeout["retention_boundary_no_write_archive_proof_closeout_id"]
+ assert transfer["source_archive_retention_sealed_handoff_proof_id"] == handoff["archive_retention_sealed_handoff_proof_id"]
+ assert len(transfer["sealed_handoff_manifest_hash"]) == 64
+ assert len(transfer["verifier_transfer_manifest_hash"]) == 64
+ assert transfer["verifier_transfer_status"] == "sealed_handoff_verifier_transfer_proof_preview_ready"
+ assert transfer["verifier_transfer_mode"] == "sealed_handoff_verifier_transfer_proof_preview_only"
+ assert transfer["verifier_transfer_write_locked"] is True
+ assert transfer["verifier_transfer_write_allowed"] is False
+ assert transfer["verifier_transfer_written"] is False
+ assert transfer["sealed_handoff_write_allowed"] is False
+ assert transfer["sealed_handoff_written"] is False
+ assert transfer["verifier_invocation_allowed"] is False
+ assert transfer["verifier_invoked"] is False
+ assert transfer["persists_verifier_receipt"] is False
+ assert transfer["endpoint_executed"] is False
+ assert transfer["sql_executed"] is False
+ assert transfer["database_written"] is False
+ assert transfer["ready_for_database_apply_now"] is False
+ assert transfer["ready_for_verifier_transfer_write_now"] is False
+ assert transfer["ready_for_verifier_invocation_now"] is False
+ assert transfer["database_apply_authorized"] is False
+ assert transfer["stdout_included"] is False
+ assert transfer["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ ]
+ is True
+ )
+ assert contract["verifier_transfer_write_allowed"] is False
+ assert contract["verifier_invocation_allowed"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert "archive_retention_sealed_handoff_proof_ready" in check_keys
+ assert "sealed_handoff_manifest_hash_locked" in check_keys
+ assert "sealed_handoff_verifier_transfer_proof_bound" in check_keys
+ assert "sealed_handoff_verifier_transfer_blocks_verifier_invocation" in check_keys
+ assert "sealed_handoff_verifier_transfer_has_nonsecret_machine_readable_manifest" in check_keys
+ assert "preview_has_no_side_effects_no_handoff_no_verifier_no_receipt_no_execution_no_signing" in check_keys
+ assert closeout["safety"]["persists_verifier_receipt"] is False
+ assert closeout["safety"]["executes_endpoint"] is False
+ assert closeout["safety"]["executes_sql"] is False
+ assert closeout["safety"]["writes_database"] is False
+ assert closeout["safety"]["executes_database_apply"] is False
+
+
+def test_build_report_keeps_production_report_policy(monkeypatch):
+ monkeypatch.setattr(report, "fetch_json", lambda url, timeout: _payload())
+ monkeypatch.setattr(
+ report.version_guard,
+ "build_report",
+ lambda health_url, timeout: {
+ "production": {"status": "healthy", "version": "V10.725"},
+ "local": {"config_version": "V10.725", "head_config_version": "V10.725"},
+ "origin_main": {"matches_local_head": True},
+ },
+ )
+ monkeypatch.setattr(report.version_guard, "evaluate", lambda guard_report, allow_local_version_drift: (True, []))
+
+ payload = report.build_report(
+ api_url="https://example.test/path",
+ limit=20,
+ timeout=1,
+ health_url="https://example.test/health",
+ skip_version_truth=False,
+ )
+
+ assert payload["policy"] == "read_only_production_pchome_mapping_backlog"
+ assert payload["result"] == "NEEDS_MAPPING"
+
+
+def test_main_json_uses_build_report(monkeypatch, capsys):
+ monkeypatch.setattr(
+ report,
+ "build_report",
+ lambda **kwargs: {
+ "policy": "read_only_production_pchome_mapping_backlog",
+ "result": "PASS",
+ "api_url": "https://example.test/path?limit=20",
+ "stats": {"mapping_rate": 100.0},
+ "backlog": {},
+ "errors": [],
+ },
+ )
+
+ exit_code = report.main(["--json"])
+ payload = json.loads(capsys.readouterr().out)
+
+ assert exit_code == 0
+ assert payload["policy"] == "read_only_production_pchome_mapping_backlog"
+ assert payload["stats"]["mapping_rate"] == 100.0
+
+
+def test_blocked_report_exits_nonzero(monkeypatch, capsys):
+ monkeypatch.setattr(
+ report,
+ "build_report",
+ lambda **kwargs: {
+ "policy": "read_only_production_pchome_mapping_backlog",
+ "result": "BLOCKED",
+ "api_url": "https://example.test/path?limit=20",
+ "errors": ["version drift"],
+ },
+ )
+
+ exit_code = report.main([])
+
+ assert exit_code == 1
+ assert "version drift" in capsys.readouterr().out
+
+
+def test_mapping_backlog_route_uses_cached_growth_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached mapping backlog should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context("/api/ai/pchome-growth/mapping-backlog?limit=20"):
+ response = routes.api_pchome_growth_mapping_backlog.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_mapping_backlog"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/opportunities"
+ assert payload["backlog"]["direct_mapping_count"] == 2
+ assert payload["backlog"]["review_candidate_count"] == 1
+
+
+def test_operator_preview_route_uses_cached_growth_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached operator preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context("/api/ai/pchome-growth/mapping-backlog/operator-preview?batch_size=1"):
+ response = routes.api_pchome_growth_mapping_operator_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_mapping_operator_preview"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog"
+ assert payload["operator_batch"]["selected_direct_mapping_count"] == 1
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_direct_mapping_auto_search_package_route_defaults_to_no_search_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached direct mapping auto search package should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/direct-mapping-auto-search-package?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_direct_mapping_auto_search_package.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_direct_mapping_auto_search_package"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/operator-preview"
+ assert payload["summary"]["selected_direct_mapping_count"] == 1
+ assert payload["summary"]["search_ready_target_count"] == 1
+ assert payload["summary"]["execute_search_count"] == 0
+ assert payload["search_execution"]["executed"] is False
+ assert payload["search_execution"]["writes_database"] is False
+ assert payload["search_package"]["targets"][0]["pchome_product_id"] == "PCH-2"
+ assert payload["safety"]["executes_search"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_direct_mapping_candidate_decision_package_route_defaults_to_no_search_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached direct mapping candidate decision package should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/direct-mapping-candidate-decision-package?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_direct_mapping_candidate_decision_package.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_direct_mapping_candidate_decision_package"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/direct-mapping-auto-search-package"
+ assert payload["result"] == "WAITING_FOR_DIRECT_MAPPING_CANDIDATES"
+ assert payload["summary"]["selected_direct_mapping_count"] == 1
+ assert payload["summary"]["candidate_decision_count"] == 0
+ assert payload["decision_package"]["manual_review_mode"] == "exception_only"
+ assert payload["safety"]["executes_search"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_ai_automation_readiness_route_defaults_to_no_search_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached AI automation readiness should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context("/api/ai/pchome-growth/ai-automation-readiness?batch_size=1"):
+ response = routes.api_pchome_growth_ai_automation_readiness.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_ai_automation_readiness"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/opportunities"
+ assert payload["summary"]["primary_human_gate_count"] == 0
+ assert payload["automation_policy"]["primary_flow"] == "ai_controlled"
+ assert payload["ai_exception_auto_resolution"]["mode"] == "machine_verifiable_auto_resolution"
+ assert payload["summary"]["manual_required_as_primary_flow_count"] == 0
+ assert payload["manual_policy"]["manual_review_mode"] == "exception_only"
+ assert payload["safety"]["executes_search"] is False
+ assert payload["safety"]["executes_fetch"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_evidence_enrichment_route_uses_cached_growth_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached evidence enrichment preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context("/api/ai/pchome-growth/mapping-backlog/evidence-enrichment-preview?batch_size=1"):
+ response = routes.api_pchome_growth_evidence_enrichment_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_evidence_enrichment_preview"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/operator-preview"
+ assert payload["summary"]["missing_field_counts"]["image"] == 2
+ assert payload["safety"]["fetches_external_sites"] is False
+
+
+def test_evidence_source_preview_route_uses_cached_growth_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached evidence source preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context("/api/ai/pchome-growth/mapping-backlog/evidence-source-preview?batch_size=1"):
+ response = routes.api_pchome_growth_evidence_source_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_evidence_source_preview"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/evidence-enrichment-preview"
+ assert payload["summary"]["field_counts"]["image"]["missing_count"] == 2
+ assert payload["safety"]["fetches_external_sites"] is False
+
+
+def test_evidence_fetch_gate_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached evidence fetch gate should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context("/api/ai/pchome-growth/mapping-backlog/evidence-fetch-gate?batch_size=1"):
+ response = routes.api_pchome_growth_evidence_fetch_gate.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "controlled_read_only_pchome_product_page_evidence_fetch_gate"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/evidence-source-preview"
+ assert payload["fetch_config"]["execute_fetch"] is False
+ assert payload["summary"]["executed_fetch_count"] == 0
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_evidence_merge_preview_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached evidence merge preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context("/api/ai/pchome-growth/mapping-backlog/evidence-merge-preview?batch_size=1"):
+ response = routes.api_pchome_growth_evidence_merge_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_evidence_merge_preview"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/evidence-fetch-gate"
+ assert payload["summary"]["executed_fetch_count"] == 0
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_receipt_gate_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy receipt gate should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context("/api/ai/pchome-growth/mapping-backlog/auto-policy-receipt-gate?batch_size=1"):
+ response = routes.api_pchome_growth_auto_policy_receipt_gate.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_receipt_gate"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/evidence-merge-preview"
+ assert payload["summary"]["persists_receipt_count"] == 0
+ assert payload["safety"]["writes_database"] is False
+ assert payload["safety"]["persists_receipt"] is False
+
+
+def test_auto_policy_persistence_gate_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy persistence gate should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context("/api/ai/pchome-growth/mapping-backlog/auto-policy-persistence-gate?batch_size=1"):
+ response = routes.api_pchome_growth_auto_policy_persistence_gate.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_persistence_gate"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-receipt-gate"
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["summary"]["persists_receipt_count"] == 0
+ assert payload["apply_gate"]["mode"] == "dry_run_only"
+ assert payload["safety"]["writes_database"] is False
+ assert payload["safety"]["persists_receipt"] is False
+
+
+def test_auto_policy_schema_migration_preview_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy schema migration preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-schema-migration-preview?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_schema_migration_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_schema_migration_preview"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-persistence-gate"
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["future_apply_gate"]["current_preview_apply_allowed"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_migration_file_preview_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy migration file preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-preview?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_migration_file_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_migration_file_preview"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-schema-migration-preview"
+ assert payload["summary"]["writes_file_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["migration_file_preview"]["writes_file"] is False
+ assert payload["future_apply_endpoint_verifier"]["executes_endpoint"] is False
+ assert payload["safety"]["writes_file"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_apply_readiness_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy apply readiness closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-apply-readiness-closeout?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_apply_readiness_closeout.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_apply_readiness_closeout"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-preview"
+ assert payload["summary"]["readiness_check_count"] == 9
+ assert payload["closeout"]["ready_for_database_apply"] is False
+ assert payload["summary"]["writes_file_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["writes_file"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_migration_file_generation_request_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy migration file generation request should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-generation-request?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_migration_file_generation_request.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_migration_file_generation_request"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-apply-readiness-closeout"
+ assert payload["summary"]["required_artifact_count"] == 4
+ assert payload["file_generation_request"]["ready_for_database_apply"] is False
+ assert payload["summary"]["writes_file_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["writes_file"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_migration_apply_gate_preview_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy migration apply gate preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-apply-gate-preview?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_migration_apply_gate_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_migration_apply_gate_preview"
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-file-generation-request"
+ )
+ assert payload["summary"]["generated_file_exists_count"] == 1
+ assert payload["summary"]["generated_file_hash_matches_count"] == 1
+ assert payload["apply_gate"]["ready_for_database_apply_now"] is False
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_request_gate_preview_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply request gate preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-request-gate-preview?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_request_gate_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_request_gate_preview"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-migration-apply-gate-preview"
+ assert payload["summary"]["required_artifact_count"] == 5
+ assert payload["db_apply_request_gate"]["ready_for_database_apply_now"] is False
+ assert payload["db_apply_request_gate"]["command_preview"]["reads_secret_in_preview"] is False
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_execution_preflight_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply execution preflight should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-execution-preflight?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_execution_preflight.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_execution_preflight"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-request-gate-preview"
+ assert payload["summary"]["required_artifact_count"] == 6
+ assert payload["summary"]["snapshot_plan_count"] == 5
+ assert payload["summary"]["readback_plan_count"] == 6
+ assert payload["execution_preflight"]["ready_for_database_apply_now"] is False
+ assert payload["execution_preflight"]["reads_secret_in_preview"] is False
+ assert payload["prewrite_snapshot_plan"]["writes_database"] is False
+ assert payload["post_apply_readback_plan"]["executes_sql_in_preview"] is False
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_package_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization package should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-package?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_authorization_package.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_package"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-execution-preflight"
+ assert payload["summary"]["authorization_check_count"] == 11
+ assert payload["summary"]["freshness_requirement_count"] == 5
+ assert payload["summary"]["manifest_step_count"] == 6
+ assert payload["authorization_package"]["ready_for_database_apply_now"] is False
+ assert payload["authorization_package"]["reads_secret_in_preview"] is False
+ assert payload["machine_apply_manifest"]["writes_database"] is False
+ assert payload["verifier_bundle"]["executes_in_preview"] is False
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_verifier_artifact_preview_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply verifier artifact preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_verifier_artifact_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_verifier_artifact_preview"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-package"
+ assert payload["summary"]["artifact_schema_count"] == 3
+ assert payload["summary"]["artifact_generation_step_count"] == 5
+ assert payload["summary"]["verifier_check_count"] == 15
+ assert payload["artifact_preview"]["ready_for_database_apply_now"] is False
+ assert payload["artifact_preview"]["writes_artifact_in_preview"] is False
+ assert payload["artifact_generation_plan"]["writes_database"] is False
+ assert payload["verifier_manifest"]["writes_artifact_in_preview"] is False
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_final_handoff_package_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply final handoff package should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-final-handoff-package?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_final_handoff_package.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_final_handoff_package"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-verifier-artifact-preview"
+ assert payload["summary"]["handoff_section_count"] == 6
+ assert payload["summary"]["final_runbook_step_count"] == 7
+ assert payload["summary"]["command_preview_count"] == 3
+ assert payload["summary"]["abort_gate_count"] == 10
+ assert payload["final_handoff_package"]["ready_for_database_apply_now"] is False
+ assert payload["final_handoff_package"]["reads_secret_in_preview"] is False
+ assert payload["final_runbook_manifest"]["writes_database"] is False
+ assert payload["command_previews"][1]["executes_in_preview"] is False
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_shell_preview_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run shell preview should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-preview?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_preview.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_preview"
+ assert payload["source_endpoint"] == "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-final-handoff-package"
+ assert payload["summary"]["shell_phase_count"] == 9
+ assert payload["summary"]["shell_script_line_count"] == 10
+ assert payload["summary"]["check_mode_required_check_count"] == 6
+ assert payload["summary"]["rollback_hook_count"] == 3
+ assert payload["controlled_dry_run_shell_preview"]["ready_for_database_apply_now"] is False
+ assert payload["controlled_dry_run_shell_preview"]["reads_secret_in_preview"] is False
+ assert payload["shell_script_preview"]["executes_script_in_preview"] is False
+ assert payload["check_mode_contract"]["writes_database"] is False
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_shell_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run shell closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-closeout?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_closeout.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_shell_closeout"
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-preview"
+ )
+ assert payload["summary"]["closeout_check_count"] == 13
+ assert payload["summary"]["future_apply_boundary_count"] == 6
+ assert payload["explicit_authorization_boundary"]["ready_for_database_apply_now"] is False
+ assert payload["explicit_authorization_boundary"]["reads_secret_in_preview"] is False
+ assert payload["explicit_authorization_boundary"]["executes_shell_in_preview"] is False
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_request_intake_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization request intake should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-intake?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_authorization_request_intake.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_request_intake"
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-shell-closeout"
+ )
+ assert payload["summary"]["required_request_evidence_count"] == 7
+ assert payload["summary"]["request_payload_required_field_count"] == 10
+ assert payload["summary"]["authorization_acceptance_gate_count"] == 11
+ assert payload["authorization_request_intake"]["ready_for_database_apply_now"] is False
+ assert payload["authorization_request_intake"]["reads_secret_in_preview"] is False
+ assert payload["authorization_request_intake"]["executes_shell_in_preview"] is False
+ assert payload["authorization_envelope"]["issues_database_apply_authorization"] is False
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_request_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization request closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-closeout?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_authorization_request_closeout.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_request_closeout"
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-intake"
+ )
+ assert payload["summary"]["closeout_check_count"] == 12
+ assert payload["summary"]["exact_request_payload_field_count"] == 10
+ assert payload["summary"]["machine_request_manifest_step_count"] == 6
+ assert payload["final_exact_request_package"]["ready_for_database_apply_now"] is False
+ assert payload["final_exact_request_package"]["issues_database_apply_authorization"] is False
+ assert payload["final_exact_request_package"]["reads_secret_in_preview"] is False
+ assert payload["machine_request_manifest"]["writes_database"] is False
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_lane_guard_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization lane guard should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-lane-guard?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_authorization_lane_guard.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_lane_guard"
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-request-closeout"
+ )
+ assert payload["summary"]["lane_guard_check_count"] == 12
+ assert payload["summary"]["lane_entry_requirement_count"] == 6
+ assert payload["summary"]["exact_request_payload_field_count"] == 10
+ assert payload["summary"]["machine_request_manifest_step_count"] == 6
+ assert payload["future_authorization_lane_guard"]["ready_for_database_apply_now"] is False
+ assert payload["future_authorization_lane_guard"]["issues_database_apply_authorization"] is False
+ assert payload["future_authorization_lane_guard"]["reads_secret_in_preview"] is False
+ assert payload["lane_transfer_contract"]["writes_database"] is False
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_decision_preflight_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization decision preflight should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-preflight?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_authorization_decision_preflight.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_decision_preflight"
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-lane-guard"
+ )
+ assert payload["summary"]["decision_preflight_check_count"] == 12
+ assert payload["summary"]["decision_input_requirement_count"] == 8
+ assert payload["summary"]["decision_rejection_reason_count"] == 10
+ assert payload["future_authorization_decision_preflight"]["ready_for_database_apply_now"] is False
+ assert payload["future_authorization_decision_preflight"]["issues_database_apply_authorization"] is False
+ assert payload["future_authorization_decision_preflight"]["reads_secret_in_preview"] is False
+ assert payload["decision_preflight_envelope"]["requires_post_apply_verifier"] is True
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_decision_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization decision closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-closeout?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_authorization_decision_closeout.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_decision_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-preflight"
+ )
+ assert payload["summary"]["decision_closeout_check_count"] == 12
+ assert payload["summary"]["decision_input_requirement_count"] == 8
+ assert payload["summary"]["decision_rejection_reason_count"] == 10
+ assert payload["future_authorization_decision_closeout"]["ready_for_database_apply_now"] is False
+ assert payload["future_authorization_decision_closeout"]["issues_database_apply_authorization"] is False
+ assert payload["future_authorization_decision_package"]["requires_post_apply_verifier"] is True
+ assert payload["future_authorization_decision_package"]["reads_secret_in_preview"] is False
+ assert payload["future_authorization_decision_package"]["writes_database_in_preview"] is False
+ assert payload["decision_closeout_contract"]["issues_database_apply_authorization"] is False
+ assert payload["decision_closeout_contract"]["ready_for_database_apply_now"] is False
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_issuer_gate_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization issuer gate should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-issuer-gate?batch_size=1"
+ ):
+ response = routes.api_pchome_growth_auto_policy_db_apply_authorization_issuer_gate.__wrapped__()
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == "read_only_pchome_growth_auto_policy_db_apply_authorization_issuer_gate"
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-closeout"
+ )
+ assert payload["summary"]["issuer_gate_check_count"] == 12
+ assert payload["summary"]["required_issuer_evidence_count"] == 9
+ assert payload["summary"]["nonsecret_authorization_claim_count"] == 8
+ assert payload["future_authorization_issuer_gate"]["ready_for_database_apply_now"] is False
+ assert payload["future_authorization_issuer_gate"]["issues_database_apply_authorization"] is False
+ assert payload["future_authorization_issuer_gate"]["signs_database_apply_authorization"] is False
+ assert payload["final_nonsecret_authorization_envelope"]["secret_material_included"] is False
+ assert payload["final_nonsecret_authorization_envelope"]["reads_secret_in_preview"] is False
+ assert payload["final_nonsecret_authorization_envelope"]["writes_database_in_preview"] is False
+ assert payload["issuer_gate_contract"]["issues_database_apply_authorization"] is False
+ assert payload["issuer_gate_contract"]["ready_for_database_apply_now"] is False
+ assert payload["issuer_gate_contract"]["signs_database_apply_authorization"] is False
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_decision_preflight_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization signing decision preflight should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-preflight?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_signing_decision_preflight
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_decision_preflight"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-issuer-gate"
+ )
+ assert payload["summary"]["signing_decision_preflight_check_count"] == 12
+ assert payload["summary"]["signing_decision_input_requirement_count"] == 10
+ assert payload["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_authorization_signing_decision_preflight"]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_decision_preflight"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_decision_preflight"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["signing_decision_preflight_envelope"]["ready_for_database_apply_now"] is False
+ assert (
+ payload["signing_decision_preflight_envelope"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert payload["signing_decision_preflight_envelope"]["secret_material_required_in_preview"] is False
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_issuer_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization signing issuer closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-guard"
+ )
+ assert payload["summary"]["signing_issuer_closeout_check_count"] == 12
+ assert payload["summary"]["signing_issuer_guard_check_count"] == 12
+ assert payload["summary"]["signing_decision_input_requirement_count"] == 10
+ assert payload["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_authorization_signing_issuer_closeout"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_issuer_closeout"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_issuer_closeout"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["final_signable_request_package"]["ready_for_database_apply_now"] is False
+ assert (
+ payload["final_signable_request_package"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert payload["final_signable_request_package"]["secret_material_included"] is False
+ assert (
+ payload["final_signable_request_package"]["secret_material_required_in_preview"]
+ is False
+ )
+ assert (
+ payload["signing_issuer_closeout_contract"]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload["signing_issuer_closeout_contract"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_execution_preflight_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization signing execution preflight should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_signing_execution_preflight
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_execution_preflight"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout"
+ )
+ assert payload["summary"]["signing_execution_preflight_check_count"] == 12
+ assert payload["summary"]["signing_issuer_closeout_check_count"] == 12
+ assert payload["summary"]["operator_held_secret_boundary_count"] == 1
+ assert payload["summary"]["signing_execution_input_requirement_count"] == 10
+ assert payload["summary"]["signing_execution_abort_condition_count"] == 8
+ assert payload["summary"]["rollback_boundary_count"] == 4
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_authorization_signing_execution_preflight"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_execution_preflight"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_execution_preflight"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["signing_execution_preflight_package"]["ready_for_database_apply_now"] is False
+ assert (
+ payload["signing_execution_preflight_package"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert payload["signing_execution_preflight_package"]["secret_material_included"] is False
+ assert (
+ payload["signing_execution_preflight_package"]["secret_material_required_in_preview"]
+ is False
+ )
+ assert (
+ payload["operator_held_secret_boundary_contract"]["secret_reference_mode"]
+ == "external_runtime_reference_only"
+ )
+ assert payload["operator_held_secret_boundary_contract"]["reads_secret_in_preview"] is False
+ assert (
+ payload["operator_held_secret_boundary_contract"]["accepts_plaintext_secret"]
+ is False
+ )
+ assert (
+ payload["signing_execution_preflight_contract"]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload["signing_execution_preflight_contract"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_execution_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization signing execution closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_signing_execution_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_execution_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight"
+ )
+ assert payload["summary"]["signing_execution_closeout_check_count"] == 12
+ assert payload["summary"]["signing_execution_preflight_check_count"] == 12
+ assert payload["summary"]["unsigned_signed_authorization_receipt_boundary_count"] == 1
+ assert payload["summary"]["operator_held_secret_boundary_count"] == 1
+ assert payload["summary"]["signing_execution_input_requirement_count"] == 10
+ assert payload["summary"]["signing_execution_abort_condition_count"] == 8
+ assert payload["summary"]["rollback_boundary_count"] == 4
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_authorization_signing_execution_closeout"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_execution_closeout"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_execution_closeout"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["unsigned_signed_authorization_receipt_boundary"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["unsigned_signed_authorization_receipt_boundary"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["unsigned_signed_authorization_receipt_boundary"][
+ "signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["unsigned_signed_authorization_receipt_boundary"][
+ "signature_material_included"
+ ]
+ is False
+ )
+ assert (
+ payload["unsigned_signed_authorization_receipt_boundary"][
+ "secret_material_included"
+ ]
+ is False
+ )
+ assert (
+ payload["signing_execution_closeout_contract"]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload["signing_execution_closeout_contract"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signed_receipt_preflight_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization signed receipt preflight should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-preflight?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_preflight
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_preflight"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-closeout"
+ )
+ assert payload["summary"]["signed_receipt_preflight_check_count"] == 12
+ assert payload["summary"]["signing_execution_closeout_check_count"] == 12
+ assert payload["summary"]["external_signing_receipt_evidence_boundary_count"] == 1
+ assert payload["summary"]["required_external_receipt_evidence_count"] == 10
+ assert payload["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert payload["summary"]["operator_held_secret_boundary_count"] == 1
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_authorization_signed_receipt_preflight"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signed_receipt_preflight"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signed_receipt_preflight"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signed_receipt_preflight"][
+ "signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["external_signing_receipt_evidence_boundary"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["external_signing_receipt_evidence_boundary"][
+ "signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["external_signing_receipt_evidence_boundary"]["signature_material_included"]
+ is False
+ )
+ assert (
+ payload["external_signing_receipt_evidence_boundary"]["secret_material_included"]
+ is False
+ )
+ assert (
+ payload["external_signing_receipt_evidence_boundary"]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload["signed_receipt_preflight_contract"]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload["signed_receipt_preflight_contract"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signed_receipt_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization signed receipt closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-preflight"
+ )
+ assert payload["summary"]["signed_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["signed_receipt_preflight_check_count"] == 12
+ assert payload["summary"]["detached_receipt_verification_boundary_count"] == 1
+ assert payload["summary"]["required_external_receipt_evidence_count"] == 10
+ assert payload["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert payload["summary"]["detached_receipt_verification_check_count"] == 10
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_authorization_signed_receipt_closeout"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signed_receipt_closeout"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signed_receipt_closeout"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signed_receipt_closeout"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["detached_receipt_verification_boundary"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert (
+ payload["detached_receipt_verification_boundary"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["detached_receipt_verification_boundary"][
+ "signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["detached_receipt_verification_boundary"]["signature_material_included"]
+ is False
+ )
+ assert payload["detached_receipt_verification_boundary"]["secret_material_included"] is False
+ assert payload["detached_receipt_verification_boundary"]["ready_for_database_apply_now"] is False
+ assert payload["signed_receipt_closeout_contract"]["ready_for_database_apply_now"] is False
+ assert (
+ payload["signed_receipt_closeout_contract"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signed_receipt_evidence_intake_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization signed receipt evidence intake should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-evidence-intake?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_evidence_intake
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signed_receipt_evidence_intake"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-closeout"
+ )
+ assert payload["summary"]["signed_receipt_evidence_intake_check_count"] == 12
+ assert payload["summary"]["signed_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["detached_receipt_verification_boundary_count"] == 1
+ assert payload["summary"]["detached_verification_evidence_schema_count"] == 1
+ assert payload["summary"]["required_external_receipt_evidence_count"] == 10
+ assert payload["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert payload["summary"]["detached_receipt_verification_check_count"] == 10
+ assert payload["summary"]["detached_verification_evidence_field_count"] == 12
+ assert payload["summary"]["detached_verification_acceptance_gate_count"] == 10
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_signed_authorization_receipt_evidence_intake"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_signed_authorization_receipt_evidence_intake"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_signed_authorization_receipt_evidence_intake"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_signed_authorization_receipt_evidence_intake"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert (
+ payload["future_signed_authorization_receipt_evidence_intake"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["detached_verification_evidence_schema"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert (
+ payload["detached_verification_evidence_schema"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["detached_verification_evidence_schema"][
+ "signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["detached_verification_evidence_schema"]["signature_material_included"]
+ is False
+ )
+ assert payload["detached_verification_evidence_schema"]["secret_material_included"] is False
+ assert payload["detached_verification_evidence_schema"]["accepts_plaintext_secret"] is False
+ assert payload["detached_verification_evidence_schema"]["ready_for_database_apply_now"] is False
+ assert payload["signed_receipt_evidence_intake_contract"]["ready_for_database_apply_now"] is False
+ assert (
+ payload["signed_receipt_evidence_intake_contract"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["performs_detached_signature_verification"] is False
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_detached_verification_evidence_validation_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization detached verification evidence validation should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-detached-verification-evidence-validation?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_detached_verification_evidence_validation
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_detached_verification_evidence_validation"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signed-receipt-evidence-intake"
+ )
+ assert payload["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert payload["summary"]["signed_receipt_evidence_intake_check_count"] == 12
+ assert payload["summary"]["detached_verification_evidence_schema_count"] == 1
+ assert payload["summary"]["verifier_receipt_closeout_boundary_count"] == 1
+ assert payload["summary"]["required_external_receipt_evidence_count"] == 10
+ assert payload["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert payload["summary"]["detached_receipt_verification_check_count"] == 10
+ assert payload["summary"]["detached_verification_evidence_field_count"] == 12
+ assert payload["summary"]["detached_verification_acceptance_gate_count"] == 10
+ assert payload["summary"]["verifier_receipt_field_count"] == 12
+ assert payload["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_detached_verification_evidence_validation"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_detached_verification_evidence_validation"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_detached_verification_evidence_validation"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_detached_verification_evidence_validation"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert (
+ payload["future_detached_verification_evidence_validation"][
+ "verifier_receipt_persisted"
+ ]
+ is False
+ )
+ assert (
+ payload["future_detached_verification_evidence_validation"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["verifier_receipt_closeout_boundary"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert (
+ payload["verifier_receipt_closeout_boundary"]["verifier_receipt_persisted"]
+ is False
+ )
+ assert (
+ payload["verifier_receipt_closeout_boundary"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["verifier_receipt_closeout_boundary"][
+ "signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["verifier_receipt_closeout_boundary"]["signature_material_included"]
+ is False
+ )
+ assert payload["verifier_receipt_closeout_boundary"]["secret_material_included"] is False
+ assert payload["verifier_receipt_closeout_boundary"]["accepts_plaintext_secret"] is False
+ assert payload["verifier_receipt_closeout_boundary"]["ready_for_database_apply_now"] is False
+ assert (
+ payload["detached_verification_evidence_validation_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["detached_verification_evidence_validation_contract"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["performs_detached_signature_verification"] is False
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_verifier_receipt_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization verifier receipt closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-verifier-receipt-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_verifier_receipt_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_verifier_receipt_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-detached-verification-evidence-validation"
+ )
+ assert payload["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert payload["summary"]["verifier_receipt_closeout_boundary_count"] == 1
+ assert payload["summary"]["verifier_receipt_evidence_handoff_count"] == 1
+ assert payload["summary"]["required_external_receipt_evidence_count"] == 10
+ assert payload["summary"]["external_receipt_acceptance_gate_count"] == 8
+ assert payload["summary"]["verifier_receipt_field_count"] == 12
+ assert payload["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert payload["summary"]["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert payload["summary"]["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_verifier_receipt_closeout"]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload["future_verifier_receipt_closeout"]["issues_database_apply_authorization"]
+ is False
+ )
+ assert (
+ payload["future_verifier_receipt_closeout"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert (
+ payload["future_verifier_receipt_closeout"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert payload["future_verifier_receipt_closeout"]["verifier_receipt_persisted"] is False
+ assert (
+ payload["future_verifier_receipt_closeout"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["verifier_receipt_evidence_handoff"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert payload["verifier_receipt_evidence_handoff"]["verifier_receipt_persisted"] is False
+ assert (
+ payload["verifier_receipt_evidence_handoff"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["verifier_receipt_evidence_handoff"][
+ "signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert payload["verifier_receipt_evidence_handoff"]["signature_material_included"] is False
+ assert payload["verifier_receipt_evidence_handoff"]["secret_material_included"] is False
+ assert payload["verifier_receipt_evidence_handoff"]["accepts_plaintext_secret"] is False
+ assert payload["verifier_receipt_evidence_handoff"]["ready_for_database_apply_now"] is False
+ assert payload["verifier_receipt_closeout_contract"]["ready_for_database_apply_now"] is False
+ assert (
+ payload["verifier_receipt_closeout_contract"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["performs_detached_signature_verification"] is False
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_evidence_execution_preflight_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization evidence execution preflight should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-preflight?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_preflight
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_preflight"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-verifier-receipt-closeout"
+ )
+ assert payload["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert payload["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["detached_verification_evidence_validation_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_preflight_count"] == 1
+ assert payload["summary"]["authorization_evidence_execution_field_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert payload["summary"]["verifier_receipt_field_count"] == 12
+ assert payload["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert payload["summary"]["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert payload["summary"]["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_database_apply_authorization_verifier_handoff"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_verifier_handoff"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_verifier_handoff"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_verifier_handoff"][
+ "executes_authorization_evidence"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_verifier_handoff"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_verifier_handoff"][
+ "verifier_receipt_persisted"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_verifier_handoff"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight"][
+ "verifier_receipt_persisted"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight"][
+ "signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight"][
+ "signature_material_included"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight"]["secret_material_included"]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight"]["accepts_plaintext_secret"]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight"][
+ "executes_authorization_evidence"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight_contract"][
+ "executes_authorization_evidence"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_preflight_contract"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["performs_detached_signature_verification"] is False
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["executes_authorization_evidence"] is False
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_evidence_execution_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization evidence execution closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_evidence_execution_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-preflight"
+ )
+ assert payload["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert payload["summary"]["verifier_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_closeout_count"] == 1
+ assert payload["summary"]["database_apply_final_verifier_gate_count"] == 1
+ assert payload["summary"]["authorization_evidence_execution_closeout_field_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["authorization_evidence_execution_field_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_acceptance_gate_count"] == 10
+ assert payload["summary"]["verifier_receipt_field_count"] == 12
+ assert payload["summary"]["verifier_receipt_acceptance_gate_count"] == 10
+ assert payload["summary"]["verifier_receipt_evidence_handoff_field_count"] == 12
+ assert payload["summary"]["verifier_receipt_handoff_acceptance_gate_count"] == 10
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_database_apply_authorization_final_verifier_gate"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_final_verifier_gate"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_final_verifier_gate"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_final_verifier_gate"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_final_verifier_gate"][
+ "executes_authorization_evidence"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_final_verifier_gate"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_final_verifier_gate"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_final_verifier_gate"][
+ "verifier_receipt_persisted"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_authorization_final_verifier_gate"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_closeout"][
+ "detached_signature_verification_performed"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_closeout"][
+ "verifier_receipt_persisted"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_closeout"][
+ "external_signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_closeout"][
+ "signed_authorization_receipt_included"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_closeout"]["signature_material_included"]
+ is False
+ )
+ assert payload["authorization_evidence_execution_closeout"]["secret_material_included"] is False
+ assert payload["authorization_evidence_execution_closeout"]["accepts_plaintext_secret"] is False
+ assert payload["authorization_evidence_execution_closeout"]["executes_authorization_evidence"] is False
+ assert payload["authorization_evidence_execution_closeout"]["executes_database_apply"] is False
+ assert payload["authorization_evidence_execution_closeout"]["ready_for_database_apply_now"] is False
+ assert payload["authorization_evidence_execution_closeout"]["database_apply_authorized"] is False
+ assert (
+ payload["authorization_evidence_execution_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_closeout_contract"][
+ "executes_authorization_evidence"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert (
+ payload["authorization_evidence_execution_closeout_contract"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["performs_detached_signature_verification"] is False
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["executes_authorization_evidence"] is False
+ assert payload["safety"]["executes_database_apply"] is False
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_apply_final_preflight_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled apply final preflight should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-apply-final-preflight?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_apply_final_preflight
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_apply_final_preflight"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-evidence-execution-closeout"
+ )
+ assert payload["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert payload["summary"]["controlled_apply_final_preflight_count"] == 1
+ assert payload["summary"]["controlled_apply_final_preflight_field_count"] == 12
+ assert payload["summary"]["controlled_apply_final_preflight_acceptance_gate_count"] == 10
+ assert payload["summary"]["rollback_binding_count"] == 1
+ assert payload["summary"]["rollback_binding_field_count"] == 8
+ assert payload["summary"]["post_apply_verifier_binding_count"] == 1
+ assert payload["summary"]["post_apply_verifier_binding_field_count"] == 8
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_database_apply_controlled_apply_final_preflight"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_controlled_apply_final_preflight"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_controlled_apply_final_preflight"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_controlled_apply_final_preflight"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_controlled_apply_final_preflight"][
+ "executes_authorization_evidence"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_controlled_apply_final_preflight"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_controlled_apply_final_preflight"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_controlled_apply_final_preflight"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["future_database_apply_controlled_apply_final_preflight"][
+ "writes_database"
+ ]
+ is False
+ )
+ assert payload["controlled_apply_final_preflight"]["dry_run_only"] is True
+ assert payload["controlled_apply_final_preflight"]["check_mode_only"] is True
+ assert payload["controlled_apply_final_preflight"]["accepts_plaintext_secret"] is False
+ assert payload["controlled_apply_final_preflight"]["reads_secret_in_preview"] is False
+ assert payload["controlled_apply_final_preflight"]["signature_material_included"] is False
+ assert payload["controlled_apply_final_preflight"]["secret_material_included"] is False
+ assert (
+ payload["controlled_apply_final_preflight"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert payload["controlled_apply_final_preflight"]["executes_authorization_evidence"] is False
+ assert payload["controlled_apply_final_preflight"]["executes_database_apply"] is False
+ assert payload["controlled_apply_final_preflight"]["executes_endpoint_in_preview"] is False
+ assert payload["controlled_apply_final_preflight"]["executes_sql_in_preview"] is False
+ assert payload["controlled_apply_final_preflight"]["writes_database_in_preview"] is False
+ assert payload["controlled_apply_final_preflight"]["ready_for_database_apply_now"] is False
+ assert payload["controlled_apply_final_preflight"]["database_apply_authorized"] is False
+ assert (
+ payload["controlled_apply_final_preflight"]["rollback_binding"][
+ "rollback_execution_authorized"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_apply_final_preflight"]["rollback_binding"][
+ "rollback_executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_apply_final_preflight"]["post_apply_verifier_binding"][
+ "verifier_execution_authorized_in_preview"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_apply_final_preflight"]["post_apply_verifier_binding"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_apply_final_preflight_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_apply_final_preflight_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_apply_final_preflight_contract"]["executes_endpoint"]
+ is False
+ )
+ assert payload["controlled_apply_final_preflight_contract"]["executes_sql"] is False
+ assert (
+ payload["controlled_apply_final_preflight_contract"]["database_apply_authorized"]
+ is False
+ )
+ assert (
+ payload["controlled_apply_final_preflight_contract"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run post-receipt parser closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-post-receipt-parser-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_no_apply_enforcement_verification"
+ ]
+ parser_closeout = payload[
+ "controlled_dry_run_post_receipt_parser_closeout"
+ ]
+ enforcement = parser_closeout["no_apply_enforcement_verification"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_post_receipt_parser_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-execution-receipt-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_post_receipt_parser_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_execution_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_command_artifact_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_post_receipt_parser_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_post_receipt_parser_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_post_receipt_parser_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["no_apply_enforcement_verification_count"] == 1
+ assert payload["summary"]["no_apply_enforcement_verification_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert parser_closeout["post_receipt_parser_closeout_only"] is True
+ assert parser_closeout["no_apply_enforcement_verification_only"] is True
+ assert parser_closeout["dry_run_only"] is True
+ assert parser_closeout["check_mode_only"] is True
+ assert parser_closeout["accepts_plaintext_secret"] is False
+ assert parser_closeout["reads_secret_in_preview"] is False
+ assert parser_closeout["signature_material_included"] is False
+ assert parser_closeout["secret_material_included"] is False
+ assert parser_closeout["signs_database_apply_authorization"] is False
+ assert parser_closeout["executes_authorization_evidence"] is False
+ assert parser_closeout["executes_database_apply"] is False
+ assert parser_closeout["executes_endpoint_in_preview"] is False
+ assert parser_closeout["executes_sql_in_preview"] is False
+ assert parser_closeout["writes_database_in_preview"] is False
+ assert enforcement["endpoint_execution_allowed"] is False
+ assert enforcement["sql_execution_allowed"] is False
+ assert enforcement["database_write_allowed"] is False
+ assert enforcement["database_apply_authorized"] is False
+ assert enforcement["executes_database_apply"] is False
+ assert enforcement["executes_endpoint"] is False
+ assert enforcement["executes_sql"] is False
+ assert enforcement["writes_database"] is False
+ assert (
+ payload["controlled_dry_run_post_receipt_parser_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_post_receipt_parser_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_post_receipt_parser_closeout_contract"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_post_receipt_parser_closeout_contract"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_post_receipt_parser_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+ assert payload["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run no-apply enforcement closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-apply-enforcement-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_final_dry_run_executor_guard"
+ ]
+ enforcement_closeout = payload[
+ "controlled_dry_run_no_apply_enforcement_closeout"
+ ]
+ final_guard = enforcement_closeout["final_dry_run_executor_guard"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_apply_enforcement_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-post-receipt-parser-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_no_apply_enforcement_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_post_receipt_parser_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_execution_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_apply_enforcement_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_no_apply_enforcement_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_apply_enforcement_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["final_dry_run_executor_guard_count"] == 1
+ assert payload["summary"]["final_dry_run_executor_guard_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert enforcement_closeout["no_apply_enforcement_closeout_only"] is True
+ assert enforcement_closeout["final_dry_run_executor_guard_only"] is True
+ assert enforcement_closeout["dry_run_only"] is True
+ assert enforcement_closeout["check_mode_only"] is True
+ assert enforcement_closeout["accepts_plaintext_secret"] is False
+ assert enforcement_closeout["reads_secret_in_preview"] is False
+ assert enforcement_closeout["signature_material_included"] is False
+ assert enforcement_closeout["secret_material_included"] is False
+ assert enforcement_closeout["signs_database_apply_authorization"] is False
+ assert enforcement_closeout["executes_authorization_evidence"] is False
+ assert enforcement_closeout["executes_database_apply"] is False
+ assert enforcement_closeout["executes_endpoint_in_preview"] is False
+ assert enforcement_closeout["executes_sql_in_preview"] is False
+ assert enforcement_closeout["writes_database_in_preview"] is False
+ assert final_guard["dry_run_executor_invocation_allowed"] is False
+ assert final_guard["endpoint_execution_allowed"] is False
+ assert final_guard["sql_execution_allowed"] is False
+ assert final_guard["database_write_allowed"] is False
+ assert final_guard["database_apply_authorized"] is False
+ assert final_guard["executes_database_apply"] is False
+ assert final_guard["executes_endpoint"] is False
+ assert final_guard["executes_sql"] is False
+ assert final_guard["writes_database"] is False
+ assert (
+ payload["controlled_dry_run_no_apply_enforcement_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_no_apply_enforcement_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_no_apply_enforcement_closeout_contract"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_no_apply_enforcement_closeout_contract"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_no_apply_enforcement_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run final executor guard closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-executor-guard-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_pre_apply_replay_verifier"
+ ]
+ guard_closeout = payload[
+ "controlled_dry_run_final_executor_guard_closeout"
+ ]
+ replay = guard_closeout["pre_apply_replay_verifier"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_executor_guard_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-apply-enforcement-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_final_executor_guard_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_apply_enforcement_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_post_receipt_parser_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_final_executor_guard_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_final_executor_guard_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_final_executor_guard_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["pre_apply_replay_verifier_count"] == 1
+ assert payload["summary"]["pre_apply_replay_verifier_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert guard_closeout["final_executor_guard_closeout_only"] is True
+ assert guard_closeout["pre_apply_replay_verifier_only"] is True
+ assert guard_closeout["dry_run_only"] is True
+ assert guard_closeout["check_mode_only"] is True
+ assert guard_closeout["accepts_plaintext_secret"] is False
+ assert guard_closeout["reads_secret_in_preview"] is False
+ assert guard_closeout["signature_material_included"] is False
+ assert guard_closeout["secret_material_included"] is False
+ assert guard_closeout["signs_database_apply_authorization"] is False
+ assert guard_closeout["executes_authorization_evidence"] is False
+ assert guard_closeout["executes_database_apply"] is False
+ assert guard_closeout["executes_endpoint_in_preview"] is False
+ assert guard_closeout["executes_sql_in_preview"] is False
+ assert guard_closeout["writes_database_in_preview"] is False
+ assert replay["replay_mode"] == "pre_apply_replay_preview_only"
+ assert replay["dry_run_executor_invocation_allowed"] is False
+ assert replay["endpoint_execution_allowed"] is False
+ assert replay["sql_execution_allowed"] is False
+ assert replay["database_write_allowed"] is False
+ assert replay["database_apply_authorized"] is False
+ assert replay["executes_database_apply"] is False
+ assert replay["executes_endpoint"] is False
+ assert replay["executes_sql"] is False
+ assert replay["writes_database"] is False
+ assert (
+ payload["controlled_dry_run_final_executor_guard_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_final_executor_guard_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_final_executor_guard_closeout_contract"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_final_executor_guard_closeout_contract"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_final_executor_guard_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run pre-apply replay closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-pre-apply-replay-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_apply_executor_readiness_contract"
+ ]
+ replay_closeout = payload[
+ "controlled_dry_run_pre_apply_replay_closeout"
+ ]
+ readiness = replay_closeout["apply_executor_readiness_contract"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_pre_apply_replay_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-executor-guard-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_pre_apply_replay_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_final_executor_guard_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_apply_enforcement_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_pre_apply_replay_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_pre_apply_replay_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_pre_apply_replay_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["apply_executor_readiness_contract_count"] == 1
+ assert payload["summary"]["apply_executor_readiness_contract_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert replay_closeout["pre_apply_replay_closeout_only"] is True
+ assert replay_closeout["apply_executor_readiness_contract_only"] is True
+ assert replay_closeout["dry_run_only"] is True
+ assert replay_closeout["check_mode_only"] is True
+ assert replay_closeout["accepts_plaintext_secret"] is False
+ assert replay_closeout["reads_secret_in_preview"] is False
+ assert replay_closeout["signature_material_included"] is False
+ assert replay_closeout["secret_material_included"] is False
+ assert replay_closeout["signs_database_apply_authorization"] is False
+ assert replay_closeout["executes_authorization_evidence"] is False
+ assert replay_closeout["executes_database_apply"] is False
+ assert replay_closeout["executes_endpoint_in_preview"] is False
+ assert replay_closeout["executes_sql_in_preview"] is False
+ assert replay_closeout["writes_database_in_preview"] is False
+ assert readiness["readiness_mode"] == "apply_executor_readiness_contract_preview_only"
+ assert readiness["dry_run_executor_invocation_allowed"] is False
+ assert readiness["endpoint_execution_allowed"] is False
+ assert readiness["sql_execution_allowed"] is False
+ assert readiness["database_write_allowed"] is False
+ assert readiness["ready_for_database_apply_now"] is False
+ assert readiness["database_apply_authorized"] is False
+ assert readiness["executes_database_apply"] is False
+ assert readiness["executes_endpoint"] is False
+ assert readiness["executes_sql"] is False
+ assert readiness["writes_database"] is False
+ assert (
+ payload["controlled_dry_run_pre_apply_replay_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_pre_apply_replay_closeout_contract"][
+ "ready_for_dry_run_executor_invocation_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_pre_apply_replay_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_pre_apply_replay_closeout_contract"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_pre_apply_replay_closeout_contract"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_pre_apply_replay_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run apply executor readiness closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-apply-executor-readiness-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_invocation_readiness_receipt"
+ ]
+ readiness_closeout = payload[
+ "controlled_dry_run_apply_executor_readiness_closeout"
+ ]
+ receipt = readiness_closeout["dry_run_invocation_readiness_receipt"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_apply_executor_readiness_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-pre-apply-replay-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_apply_executor_readiness_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_pre_apply_replay_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_final_executor_guard_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_apply_executor_readiness_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_apply_executor_readiness_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_apply_executor_readiness_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["dry_run_invocation_readiness_receipt_count"] == 1
+ assert payload["summary"]["dry_run_invocation_readiness_receipt_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert readiness_closeout["apply_executor_readiness_closeout_only"] is True
+ assert readiness_closeout["dry_run_invocation_readiness_receipt_only"] is True
+ assert readiness_closeout["dry_run_only"] is True
+ assert readiness_closeout["check_mode_only"] is True
+ assert readiness_closeout["accepts_plaintext_secret"] is False
+ assert readiness_closeout["reads_secret_in_preview"] is False
+ assert readiness_closeout["signature_material_included"] is False
+ assert readiness_closeout["secret_material_included"] is False
+ assert readiness_closeout["signs_database_apply_authorization"] is False
+ assert readiness_closeout["executes_authorization_evidence"] is False
+ assert readiness_closeout["executes_database_apply"] is False
+ assert readiness_closeout["executes_endpoint_in_preview"] is False
+ assert readiness_closeout["executes_sql_in_preview"] is False
+ assert readiness_closeout["writes_database_in_preview"] is False
+ assert receipt["receipt_mode"] == "dry_run_invocation_readiness_preview_only"
+ assert receipt["dry_run_executor_invocation_allowed"] is False
+ assert receipt["ready_for_dry_run_executor_invocation_now"] is False
+ assert receipt["endpoint_execution_allowed"] is False
+ assert receipt["sql_execution_allowed"] is False
+ assert receipt["database_write_allowed"] is False
+ assert receipt["ready_for_database_apply_now"] is False
+ assert receipt["database_apply_authorized"] is False
+ assert receipt["executes_database_apply"] is False
+ assert receipt["executes_endpoint"] is False
+ assert receipt["executes_sql"] is False
+ assert receipt["writes_database"] is False
+ assert (
+ payload["controlled_dry_run_apply_executor_readiness_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_apply_executor_readiness_closeout_contract"][
+ "ready_for_dry_run_executor_invocation_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_apply_executor_readiness_closeout_contract"][
+ "ready_for_actual_dry_run_execution_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_apply_executor_readiness_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_apply_executor_readiness_closeout_contract"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_apply_executor_readiness_closeout_contract"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_apply_executor_readiness_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run invocation receipt closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-invocation-receipt-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_no_write_invocation_package"
+ ]
+ invocation_closeout = payload[
+ "controlled_dry_run_invocation_receipt_closeout"
+ ]
+ package = invocation_closeout["no_write_invocation_package"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_invocation_receipt_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-apply-executor-readiness-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_invocation_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_apply_executor_readiness_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_pre_apply_replay_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_invocation_receipt_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_invocation_receipt_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_invocation_receipt_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["no_write_invocation_package_count"] == 1
+ assert payload["summary"]["no_write_invocation_package_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert invocation_closeout["invocation_receipt_closeout_only"] is True
+ assert invocation_closeout["no_write_invocation_package_only"] is True
+ assert invocation_closeout["dry_run_only"] is True
+ assert invocation_closeout["check_mode_only"] is True
+ assert invocation_closeout["accepts_plaintext_secret"] is False
+ assert invocation_closeout["reads_secret_in_preview"] is False
+ assert invocation_closeout["signature_material_included"] is False
+ assert invocation_closeout["secret_material_included"] is False
+ assert invocation_closeout["signs_database_apply_authorization"] is False
+ assert invocation_closeout["executes_authorization_evidence"] is False
+ assert invocation_closeout["executes_database_apply"] is False
+ assert invocation_closeout["executes_endpoint_in_preview"] is False
+ assert invocation_closeout["executes_sql_in_preview"] is False
+ assert invocation_closeout["writes_database_in_preview"] is False
+ assert package["package_mode"] == "no_write_invocation_package_preview_only"
+ assert package["dry_run_executor_invocation_allowed"] is False
+ assert package["ready_for_no_write_dry_run_invocation_package_now"] is False
+ assert package["ready_for_actual_dry_run_execution_now"] is False
+ assert package["endpoint_execution_allowed"] is False
+ assert package["sql_execution_allowed"] is False
+ assert package["database_write_allowed"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["database_apply_authorized"] is False
+ assert package["executes_database_apply"] is False
+ assert package["executes_endpoint"] is False
+ assert package["executes_sql"] is False
+ assert package["writes_database"] is False
+ assert (
+ payload["controlled_dry_run_invocation_receipt_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_invocation_receipt_closeout_contract"][
+ "ready_for_dry_run_executor_invocation_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_invocation_receipt_closeout_contract"][
+ "ready_for_actual_dry_run_execution_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_invocation_receipt_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_invocation_receipt_closeout_contract"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_invocation_receipt_closeout_contract"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_invocation_receipt_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run no-write invocation package closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-invocation-package-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_execution_preflight_guard"
+ ]
+ package_closeout = payload[
+ "controlled_dry_run_no_write_invocation_package_closeout"
+ ]
+ guard = package_closeout["execution_preflight_guard"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_invocation_package_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-invocation-receipt-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_no_write_invocation_package_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_invocation_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_apply_executor_readiness_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_write_invocation_package_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_no_write_invocation_package_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_write_invocation_package_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["execution_preflight_guard_count"] == 1
+ assert payload["summary"]["execution_preflight_guard_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert package_closeout["no_write_invocation_package_closeout_only"] is True
+ assert package_closeout["execution_preflight_guard_only"] is True
+ assert package_closeout["dry_run_only"] is True
+ assert package_closeout["check_mode_only"] is True
+ assert package_closeout["accepts_plaintext_secret"] is False
+ assert package_closeout["reads_secret_in_preview"] is False
+ assert package_closeout["signature_material_included"] is False
+ assert package_closeout["secret_material_included"] is False
+ assert package_closeout["signs_database_apply_authorization"] is False
+ assert package_closeout["executes_authorization_evidence"] is False
+ assert package_closeout["executes_database_apply"] is False
+ assert package_closeout["executes_endpoint_in_preview"] is False
+ assert package_closeout["executes_sql_in_preview"] is False
+ assert package_closeout["writes_database_in_preview"] is False
+ assert guard["guard_mode"] == "execution_preflight_guard_preview_only"
+ assert guard["dry_run_executor_invocation_allowed"] is False
+ assert guard["ready_for_execution_preflight_guard_now"] is False
+ assert guard["ready_for_dry_run_executor_invocation_now"] is False
+ assert guard["ready_for_actual_dry_run_execution_now"] is False
+ assert guard["endpoint_execution_allowed"] is False
+ assert guard["sql_execution_allowed"] is False
+ assert guard["database_write_allowed"] is False
+ assert guard["ready_for_database_apply_now"] is False
+ assert guard["database_apply_authorized"] is False
+ assert guard["executes_database_apply"] is False
+ assert guard["executes_endpoint"] is False
+ assert guard["executes_sql"] is False
+ assert guard["writes_database"] is False
+ assert (
+ payload[
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ ]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ ]["ready_for_dry_run_executor_invocation_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ ]["ready_for_actual_dry_run_execution_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ ]["executes_database_apply"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ ]["executes_endpoint"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ ]["executes_sql"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_write_invocation_package_closeout_contract"
+ ]["database_apply_authorized"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run execution preflight guard closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-preflight-guard-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_runner_invocation_boundary"
+ ]
+ guard_closeout = payload[
+ "controlled_dry_run_execution_preflight_guard_closeout"
+ ]
+ boundary = guard_closeout["runner_invocation_boundary"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_preflight_guard_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-invocation-package-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_execution_preflight_guard_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_write_invocation_package_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_invocation_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_execution_preflight_guard_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_execution_preflight_guard_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_execution_preflight_guard_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["runner_invocation_boundary_count"] == 1
+ assert payload["summary"]["runner_invocation_boundary_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert guard_closeout["execution_preflight_guard_closeout_only"] is True
+ assert guard_closeout["runner_invocation_boundary_only"] is True
+ assert guard_closeout["dry_run_only"] is True
+ assert guard_closeout["check_mode_only"] is True
+ assert guard_closeout["accepts_plaintext_secret"] is False
+ assert guard_closeout["reads_secret_in_preview"] is False
+ assert guard_closeout["signature_material_included"] is False
+ assert guard_closeout["secret_material_included"] is False
+ assert guard_closeout["signs_database_apply_authorization"] is False
+ assert guard_closeout["executes_authorization_evidence"] is False
+ assert guard_closeout["executes_database_apply"] is False
+ assert guard_closeout["executes_endpoint_in_preview"] is False
+ assert guard_closeout["executes_sql_in_preview"] is False
+ assert guard_closeout["writes_database_in_preview"] is False
+ assert boundary["boundary_mode"] == "runner_invocation_boundary_preview_only"
+ assert boundary["dry_run_executor_invocation_allowed"] is False
+ assert boundary["runner_invocation_allowed"] is False
+ assert boundary["ready_for_runner_invocation_boundary_now"] is False
+ assert boundary["ready_for_dry_run_executor_invocation_now"] is False
+ assert boundary["ready_for_actual_dry_run_execution_now"] is False
+ assert boundary["endpoint_execution_allowed"] is False
+ assert boundary["sql_execution_allowed"] is False
+ assert boundary["database_write_allowed"] is False
+ assert boundary["ready_for_database_apply_now"] is False
+ assert boundary["database_apply_authorized"] is False
+ assert boundary["executes_database_apply"] is False
+ assert boundary["executes_endpoint"] is False
+ assert boundary["executes_sql"] is False
+ assert boundary["writes_database"] is False
+ assert boundary["captures_stdout"] is False
+ assert boundary["captures_stderr"] is False
+ assert (
+ payload[
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ ]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ ]["ready_for_dry_run_executor_invocation_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ ]["ready_for_actual_dry_run_execution_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ ]["executes_database_apply"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ ]["executes_endpoint"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ ]["executes_sql"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_execution_preflight_guard_closeout_contract"
+ ]["database_apply_authorized"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run runner invocation boundary closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-invocation-boundary-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_no_execution_receipt_handoff"
+ ]
+ boundary_closeout = payload[
+ "controlled_dry_run_runner_invocation_boundary_closeout"
+ ]
+ handoff = boundary_closeout["no_execution_receipt_handoff"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_invocation_boundary_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-preflight-guard-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_execution_preflight_guard_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_write_invocation_package_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["no_execution_receipt_handoff_count"] == 1
+ assert payload["summary"]["no_execution_receipt_handoff_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert boundary_closeout["runner_invocation_boundary_closeout_only"] is True
+ assert boundary_closeout["no_execution_receipt_handoff_only"] is True
+ assert boundary_closeout["dry_run_only"] is True
+ assert boundary_closeout["check_mode_only"] is True
+ assert boundary_closeout["accepts_plaintext_secret"] is False
+ assert boundary_closeout["reads_secret_in_preview"] is False
+ assert boundary_closeout["signature_material_included"] is False
+ assert boundary_closeout["secret_material_included"] is False
+ assert boundary_closeout["signs_database_apply_authorization"] is False
+ assert boundary_closeout["executes_authorization_evidence"] is False
+ assert boundary_closeout["executes_database_apply"] is False
+ assert boundary_closeout["executes_endpoint_in_preview"] is False
+ assert boundary_closeout["executes_sql_in_preview"] is False
+ assert boundary_closeout["writes_database_in_preview"] is False
+ assert boundary_closeout["captures_stdout"] is False
+ assert boundary_closeout["captures_stderr"] is False
+ assert handoff["handoff_mode"] == "no_execution_receipt_handoff_preview_only"
+ assert handoff["execution_receipt_present"] is False
+ assert handoff["execution_receipt_required"] is False
+ assert handoff["dry_run_executor_invocation_allowed"] is False
+ assert handoff["runner_invocation_allowed"] is False
+ assert handoff["ready_for_no_execution_receipt_handoff_now"] is False
+ assert handoff["ready_for_dry_run_executor_invocation_now"] is False
+ assert handoff["ready_for_actual_dry_run_execution_now"] is False
+ assert handoff["endpoint_execution_allowed"] is False
+ assert handoff["sql_execution_allowed"] is False
+ assert handoff["database_write_allowed"] is False
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["database_apply_authorized"] is False
+ assert handoff["executes_database_apply"] is False
+ assert handoff["executes_endpoint"] is False
+ assert handoff["executes_sql"] is False
+ assert handoff["writes_database"] is False
+ assert handoff["stdout_included"] is False
+ assert handoff["stderr_included"] is False
+ assert (
+ payload[
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ ]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ ]["ready_for_dry_run_executor_invocation_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ ]["ready_for_actual_dry_run_execution_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ ]["executes_database_apply"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ ]["executes_endpoint"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ ]["executes_sql"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_runner_invocation_boundary_closeout_contract"
+ ]["database_apply_authorized"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run no-execution receipt handoff closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_final_no_runner_execution_proof"
+ ]
+ handoff_closeout = payload[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout"
+ ]
+ proof = handoff_closeout["final_no_runner_execution_proof"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_execution_receipt_handoff_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-invocation-boundary-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_execution_preflight_guard_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["final_no_runner_execution_proof_count"] == 1
+ assert payload["summary"]["final_no_runner_execution_proof_field_count"] == 12
+ assert payload["summary"]["dry_run_executor_invoked_count"] == 0
+ assert payload["summary"]["runner_invocation_performed_count"] == 0
+ assert payload["summary"]["endpoint_executed_count"] == 0
+ assert payload["summary"]["sql_executed_count"] == 0
+ assert payload["summary"]["database_written_count"] == 0
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert handoff_closeout["no_execution_receipt_handoff_closeout_only"] is True
+ assert handoff_closeout["final_no_runner_execution_proof_only"] is True
+ assert handoff_closeout["dry_run_only"] is True
+ assert handoff_closeout["check_mode_only"] is True
+ assert handoff_closeout["accepts_plaintext_secret"] is False
+ assert handoff_closeout["reads_secret_in_preview"] is False
+ assert handoff_closeout["signature_material_included"] is False
+ assert handoff_closeout["secret_material_included"] is False
+ assert handoff_closeout["signs_database_apply_authorization"] is False
+ assert handoff_closeout["executes_authorization_evidence"] is False
+ assert handoff_closeout["executes_database_apply"] is False
+ assert handoff_closeout["executes_endpoint_in_preview"] is False
+ assert handoff_closeout["executes_sql_in_preview"] is False
+ assert handoff_closeout["writes_database_in_preview"] is False
+ assert handoff_closeout["stdout_included"] is False
+ assert handoff_closeout["stderr_included"] is False
+ assert proof["proof_mode"] == "final_no_runner_execution_proof_preview_only"
+ assert proof["execution_receipt_present"] is False
+ assert proof["execution_receipt_required"] is False
+ assert proof["dry_run_executor_invoked"] is False
+ assert proof["runner_invocation_performed"] is False
+ assert proof["endpoint_executed"] is False
+ assert proof["sql_executed"] is False
+ assert proof["database_written"] is False
+ assert proof["dry_run_executor_invocation_allowed"] is False
+ assert proof["runner_invocation_allowed"] is False
+ assert proof["ready_for_final_no_runner_execution_proof_now"] is False
+ assert proof["ready_for_dry_run_executor_invocation_now"] is False
+ assert proof["ready_for_actual_dry_run_execution_now"] is False
+ assert proof["endpoint_execution_allowed"] is False
+ assert proof["sql_execution_allowed"] is False
+ assert proof["database_write_allowed"] is False
+ assert proof["ready_for_database_apply_now"] is False
+ assert proof["database_apply_authorized"] is False
+ assert proof["executes_database_apply"] is False
+ assert proof["executes_endpoint"] is False
+ assert proof["executes_sql"] is False
+ assert proof["writes_database"] is False
+ assert proof["stdout_included"] is False
+ assert proof["stderr_included"] is False
+ assert (
+ payload[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ ]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ ]["ready_for_dry_run_executor_invocation_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ ]["ready_for_actual_dry_run_execution_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ ]["executes_database_apply"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ ]["executes_endpoint"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ ]["executes_sql"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_no_execution_receipt_handoff_closeout_contract"
+ ]["database_apply_authorized"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run final no-runner proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_controlled_executor_quarantine_proof"
+ ]
+ proof_closeout = payload[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout"
+ ]
+ quarantine = proof_closeout["controlled_executor_quarantine_proof"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_final_no_runner_execution_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-execution-receipt-handoff-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_invocation_boundary_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["controlled_executor_quarantine_proof_count"] == 1
+ assert payload["summary"]["controlled_executor_quarantine_proof_field_count"] == 12
+ assert payload["summary"]["dry_run_executor_invoked_count"] == 0
+ assert payload["summary"]["runner_invocation_performed_count"] == 0
+ assert payload["summary"]["endpoint_executed_count"] == 0
+ assert payload["summary"]["sql_executed_count"] == 0
+ assert payload["summary"]["database_written_count"] == 0
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["controlled_executor_quarantine_bound"] is True
+ assert future["executor_quarantine_enforced"] is True
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert proof_closeout["final_no_runner_execution_proof_closeout_only"] is True
+ assert proof_closeout["controlled_executor_quarantine_proof_only"] is True
+ assert proof_closeout["dry_run_only"] is True
+ assert proof_closeout["check_mode_only"] is True
+ assert proof_closeout["accepts_plaintext_secret"] is False
+ assert proof_closeout["reads_secret_in_preview"] is False
+ assert proof_closeout["signature_material_included"] is False
+ assert proof_closeout["secret_material_included"] is False
+ assert proof_closeout["signs_database_apply_authorization"] is False
+ assert proof_closeout["executes_authorization_evidence"] is False
+ assert proof_closeout["executes_database_apply"] is False
+ assert proof_closeout["executes_endpoint_in_preview"] is False
+ assert proof_closeout["executes_sql_in_preview"] is False
+ assert proof_closeout["writes_database_in_preview"] is False
+ assert proof_closeout["stdout_included"] is False
+ assert proof_closeout["stderr_included"] is False
+ assert quarantine["quarantine_mode"] == "controlled_executor_quarantine_proof_preview_only"
+ assert quarantine["controlled_executor_quarantine_bound"] is True
+ assert quarantine["executor_quarantine_enforced"] is True
+ assert quarantine["execution_receipt_present"] is False
+ assert quarantine["execution_receipt_required"] is False
+ assert quarantine["dry_run_executor_invoked"] is False
+ assert quarantine["runner_invocation_performed"] is False
+ assert quarantine["endpoint_executed"] is False
+ assert quarantine["sql_executed"] is False
+ assert quarantine["database_written"] is False
+ assert quarantine["dry_run_executor_invocation_allowed"] is False
+ assert quarantine["runner_invocation_allowed"] is False
+ assert quarantine["ready_for_controlled_executor_quarantine_now"] is False
+ assert quarantine["ready_for_dry_run_executor_invocation_now"] is False
+ assert quarantine["ready_for_actual_dry_run_execution_now"] is False
+ assert quarantine["endpoint_execution_allowed"] is False
+ assert quarantine["sql_execution_allowed"] is False
+ assert quarantine["database_write_allowed"] is False
+ assert quarantine["ready_for_database_apply_now"] is False
+ assert quarantine["database_apply_authorized"] is False
+ assert quarantine["executes_database_apply"] is False
+ assert quarantine["executes_endpoint"] is False
+ assert quarantine["executes_sql"] is False
+ assert quarantine["writes_database"] is False
+ assert quarantine["stdout_included"] is False
+ assert quarantine["stderr_included"] is False
+ assert (
+ payload[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ ]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ ]["ready_for_dry_run_executor_invocation_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ ]["ready_for_actual_dry_run_execution_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ ]["executes_database_apply"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ ]["executes_endpoint"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ ]["executes_sql"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_final_no_runner_execution_proof_closeout_contract"
+ ]["database_apply_authorized"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run controlled executor quarantine proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_execution_envelope_freeze_proof"
+ ]
+ quarantine_closeout = payload[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ ]
+ freeze = quarantine_closeout["dry_run_execution_envelope_freeze_proof"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_controlled_executor_quarantine_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-final-no-runner-execution-proof-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_final_no_runner_execution_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_execution_receipt_handoff_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["dry_run_execution_envelope_freeze_proof_count"] == 1
+ assert payload["summary"]["dry_run_execution_envelope_freeze_proof_field_count"] == 12
+ assert payload["summary"]["dry_run_executor_invoked_count"] == 0
+ assert payload["summary"]["runner_invocation_performed_count"] == 0
+ assert payload["summary"]["endpoint_executed_count"] == 0
+ assert payload["summary"]["sql_executed_count"] == 0
+ assert payload["summary"]["database_written_count"] == 0
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["controlled_executor_quarantine_bound"] is True
+ assert future["executor_quarantine_enforced"] is True
+ assert future["execution_envelope_frozen"] is True
+ assert future["execution_envelope_mutation_allowed"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["execution_receipt_present"] is False
+ assert future["dry_run_executor_invocation_allowed"] is False
+ assert future["runner_invocation_allowed"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert quarantine_closeout["controlled_executor_quarantine_proof_closeout_only"] is True
+ assert quarantine_closeout["dry_run_execution_envelope_freeze_proof_only"] is True
+ assert quarantine_closeout["dry_run_only"] is True
+ assert quarantine_closeout["check_mode_only"] is True
+ assert quarantine_closeout["execution_envelope_frozen"] is True
+ assert quarantine_closeout["execution_envelope_mutation_allowed"] is False
+ assert quarantine_closeout["accepts_plaintext_secret"] is False
+ assert quarantine_closeout["reads_secret_in_preview"] is False
+ assert quarantine_closeout["signature_material_included"] is False
+ assert quarantine_closeout["secret_material_included"] is False
+ assert quarantine_closeout["signs_database_apply_authorization"] is False
+ assert quarantine_closeout["executes_authorization_evidence"] is False
+ assert quarantine_closeout["executes_database_apply"] is False
+ assert quarantine_closeout["executes_endpoint_in_preview"] is False
+ assert quarantine_closeout["executes_sql_in_preview"] is False
+ assert quarantine_closeout["writes_database_in_preview"] is False
+ assert quarantine_closeout["stdout_included"] is False
+ assert quarantine_closeout["stderr_included"] is False
+ assert freeze["freeze_mode"] == "dry_run_execution_envelope_freeze_proof_preview_only"
+ assert freeze["execution_envelope_frozen"] is True
+ assert freeze["execution_envelope_mutation_allowed"] is False
+ assert freeze["execution_receipt_present"] is False
+ assert freeze["execution_receipt_required"] is False
+ assert freeze["dry_run_executor_invoked"] is False
+ assert freeze["runner_invocation_performed"] is False
+ assert freeze["endpoint_executed"] is False
+ assert freeze["sql_executed"] is False
+ assert freeze["database_written"] is False
+ assert freeze["dry_run_executor_invocation_allowed"] is False
+ assert freeze["runner_invocation_allowed"] is False
+ assert freeze["ready_for_dry_run_executor_invocation_now"] is False
+ assert freeze["ready_for_actual_dry_run_execution_now"] is False
+ assert freeze["endpoint_execution_allowed"] is False
+ assert freeze["sql_execution_allowed"] is False
+ assert freeze["database_write_allowed"] is False
+ assert freeze["ready_for_database_apply_now"] is False
+ assert freeze["database_apply_authorized"] is False
+ assert freeze["executes_database_apply"] is False
+ assert freeze["executes_endpoint"] is False
+ assert freeze["executes_sql"] is False
+ assert freeze["writes_database"] is False
+ assert freeze["stdout_included"] is False
+ assert freeze["stderr_included"] is False
+ assert (
+ payload[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ ]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ ]["ready_for_dry_run_executor_invocation_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ ]["ready_for_actual_dry_run_execution_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ ]["executes_database_apply"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ ]["executes_endpoint"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ ]["executes_sql"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_controlled_executor_quarantine_proof_closeout_contract"
+ ]["database_apply_authorized"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run execution envelope freeze proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_frozen_envelope_verifier_handoff"
+ ]
+ freeze_closeout = payload[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ ]
+ handoff = freeze_closeout["frozen_envelope_verifier_handoff"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_envelope_freeze_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-controlled-executor-quarantine-proof-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_controlled_executor_quarantine_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["frozen_envelope_verifier_handoff_count"] == 1
+ assert payload["summary"]["frozen_envelope_verifier_handoff_field_count"] == 12
+ assert payload["summary"]["verifier_invoked_count"] == 0
+ assert payload["summary"]["verifier_receipt_present_count"] == 0
+ assert payload["summary"]["dry_run_executor_invoked_count"] == 0
+ assert payload["summary"]["runner_invocation_performed_count"] == 0
+ assert payload["summary"]["endpoint_executed_count"] == 0
+ assert payload["summary"]["sql_executed_count"] == 0
+ assert payload["summary"]["database_written_count"] == 0
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["execution_envelope_frozen"] is True
+ assert future["execution_envelope_mutation_allowed"] is False
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["verifier_receipt_present"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_invocation_now"] is False
+ assert future["ready_for_dry_run_executor_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert freeze_closeout["execution_envelope_freeze_proof_closeout_only"] is True
+ assert freeze_closeout["frozen_envelope_verifier_handoff_only"] is True
+ assert freeze_closeout["dry_run_only"] is True
+ assert freeze_closeout["check_mode_only"] is True
+ assert freeze_closeout["execution_envelope_frozen"] is True
+ assert freeze_closeout["execution_envelope_mutation_allowed"] is False
+ assert freeze_closeout["verifier_invocation_allowed"] is False
+ assert freeze_closeout["verifier_invoked"] is False
+ assert freeze_closeout["verifier_receipt_present"] is False
+ assert freeze_closeout["accepts_plaintext_secret"] is False
+ assert freeze_closeout["reads_secret_in_preview"] is False
+ assert freeze_closeout["signature_material_included"] is False
+ assert freeze_closeout["secret_material_included"] is False
+ assert freeze_closeout["signs_database_apply_authorization"] is False
+ assert freeze_closeout["executes_authorization_evidence"] is False
+ assert freeze_closeout["executes_database_apply"] is False
+ assert freeze_closeout["executes_endpoint_in_preview"] is False
+ assert freeze_closeout["executes_sql_in_preview"] is False
+ assert freeze_closeout["writes_database_in_preview"] is False
+ assert handoff["verifier_handoff_mode"] == "frozen_envelope_verifier_handoff_preview_only"
+ assert handoff["execution_envelope_frozen"] is True
+ assert handoff["execution_envelope_mutation_allowed"] is False
+ assert handoff["verifier_invocation_allowed"] is False
+ assert handoff["verifier_invoked"] is False
+ assert handoff["verifier_receipt_present"] is False
+ assert handoff["dry_run_executor_invoked"] is False
+ assert handoff["runner_invocation_performed"] is False
+ assert handoff["endpoint_executed"] is False
+ assert handoff["sql_executed"] is False
+ assert handoff["database_written"] is False
+ assert handoff["ready_for_verifier_invocation_now"] is False
+ assert handoff["ready_for_dry_run_executor_invocation_now"] is False
+ assert handoff["endpoint_execution_allowed"] is False
+ assert handoff["sql_execution_allowed"] is False
+ assert handoff["database_write_allowed"] is False
+ assert handoff["ready_for_database_apply_now"] is False
+ assert handoff["database_apply_authorized"] is False
+ assert handoff["executes_database_apply"] is False
+ assert handoff["executes_endpoint"] is False
+ assert handoff["executes_sql"] is False
+ assert handoff["writes_database"] is False
+ assert (
+ payload[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract"
+ ]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract"
+ ]["ready_for_verifier_invocation_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract"
+ ]["executes_database_apply"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_execution_envelope_freeze_proof_closeout_contract"
+ ]["database_apply_authorized"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run frozen envelope verifier handoff closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_verifier_invocation_lock_proof"
+ ]
+ handoff_closeout = payload[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ ]
+ lock = handoff_closeout["verifier_invocation_lock_proof"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_frozen_envelope_verifier_handoff_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-envelope-freeze-proof-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_execution_envelope_freeze_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["verifier_invocation_lock_proof_count"] == 1
+ assert payload["summary"]["verifier_invocation_lock_proof_field_count"] == 12
+ assert payload["summary"]["verifier_invocation_locked_count"] == 1
+ assert payload["summary"]["verifier_invoked_count"] == 0
+ assert payload["summary"]["verifier_receipt_present_count"] == 0
+ assert payload["summary"]["dry_run_executor_invoked_count"] == 0
+ assert payload["summary"]["runner_invocation_performed_count"] == 0
+ assert payload["summary"]["endpoint_executed_count"] == 0
+ assert payload["summary"]["sql_executed_count"] == 0
+ assert payload["summary"]["database_written_count"] == 0
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["verifier_invocation_locked"] is True
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["verifier_receipt_present"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert future["stdout_included"] is False
+ assert future["stderr_included"] is False
+ assert handoff_closeout["frozen_envelope_verifier_handoff_closeout_only"] is True
+ assert handoff_closeout["verifier_invocation_lock_proof_only"] is True
+ assert handoff_closeout["dry_run_only"] is True
+ assert handoff_closeout["check_mode_only"] is True
+ assert handoff_closeout["verifier_invocation_locked"] is True
+ assert handoff_closeout["verifier_invocation_allowed"] is False
+ assert handoff_closeout["verifier_invoked"] is False
+ assert handoff_closeout["verifier_receipt_present"] is False
+ assert handoff_closeout["accepts_plaintext_secret"] is False
+ assert handoff_closeout["reads_secret_in_preview"] is False
+ assert handoff_closeout["signature_material_included"] is False
+ assert handoff_closeout["secret_material_included"] is False
+ assert handoff_closeout["signs_database_apply_authorization"] is False
+ assert handoff_closeout["executes_authorization_evidence"] is False
+ assert handoff_closeout["executes_database_apply"] is False
+ assert handoff_closeout["executes_endpoint_in_preview"] is False
+ assert handoff_closeout["executes_sql_in_preview"] is False
+ assert handoff_closeout["writes_database_in_preview"] is False
+ assert lock["lock_mode"] == "verifier_invocation_lock_proof_preview_only"
+ assert lock["verifier_invocation_locked"] is True
+ assert lock["verifier_invocation_allowed"] is False
+ assert lock["verifier_invoked"] is False
+ assert lock["verifier_receipt_present"] is False
+ assert lock["dry_run_executor_invoked"] is False
+ assert lock["runner_invocation_performed"] is False
+ assert lock["endpoint_executed"] is False
+ assert lock["sql_executed"] is False
+ assert lock["database_written"] is False
+ assert lock["ready_for_verifier_invocation_now"] is False
+ assert lock["endpoint_execution_allowed"] is False
+ assert lock["sql_execution_allowed"] is False
+ assert lock["database_write_allowed"] is False
+ assert lock["ready_for_database_apply_now"] is False
+ assert lock["database_apply_authorized"] is False
+ assert lock["executes_database_apply"] is False
+ assert lock["executes_endpoint"] is False
+ assert lock["executes_sql"] is False
+ assert lock["writes_database"] is False
+ assert (
+ payload[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract"
+ ]["verifier_invocation_locked"]
+ is True
+ )
+ assert (
+ payload[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract"
+ ]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract"
+ ]["ready_for_verifier_invocation_now"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract"
+ ]["executes_database_apply"]
+ is False
+ )
+ assert (
+ payload[
+ "controlled_dry_run_frozen_envelope_verifier_handoff_closeout_contract"
+ ]["database_apply_authorized"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run verifier invocation lock proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ ]
+ lock_closeout = payload[
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ ]
+ receipt = lock_closeout["verifier_no_execution_receipt_proof"]
+ contract = payload[
+ "controlled_dry_run_verifier_invocation_lock_proof_closeout_contract"
+ ]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_invocation_lock_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-frozen-envelope-verifier-handoff-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_frozen_envelope_verifier_handoff_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["verifier_no_execution_receipt_proof_count"] == 1
+ assert payload["summary"]["verifier_no_execution_receipt_proof_field_count"] == 12
+ assert payload["summary"]["verifier_invocation_locked_count"] == 1
+ assert payload["summary"]["verifier_invoked_count"] == 0
+ assert payload["summary"]["verifier_receipt_present_count"] == 0
+ assert payload["summary"]["dry_run_executor_invoked_count"] == 0
+ assert payload["summary"]["runner_invocation_performed_count"] == 0
+ assert payload["summary"]["endpoint_executed_count"] == 0
+ assert payload["summary"]["sql_executed_count"] == 0
+ assert payload["summary"]["database_written_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["executes_sql_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["verifier_invocation_locked"] is True
+ assert future["verifier_invocation_allowed"] is False
+ assert future["verifier_invoked"] is False
+ assert future["verifier_receipt_present"] is False
+ assert future["dry_run_executor_invoked"] is False
+ assert future["runner_invocation_performed"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_invocation_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert lock_closeout["verifier_invocation_lock_proof_closeout_only"] is True
+ assert lock_closeout["verifier_no_execution_receipt_proof_only"] is True
+ assert lock_closeout["verifier_invocation_locked"] is True
+ assert lock_closeout["verifier_invocation_allowed"] is False
+ assert lock_closeout["verifier_invoked"] is False
+ assert lock_closeout["verifier_receipt_present"] is False
+ assert lock_closeout["dry_run_executor_invoked"] is False
+ assert lock_closeout["runner_invocation_performed"] is False
+ assert lock_closeout["endpoint_executed"] is False
+ assert lock_closeout["sql_executed"] is False
+ assert lock_closeout["database_written"] is False
+ assert receipt["receipt_mode"] == "verifier_no_execution_receipt_proof_preview_only"
+ assert receipt["verifier_invocation_locked"] is True
+ assert receipt["verifier_invocation_allowed"] is False
+ assert receipt["verifier_invoked"] is False
+ assert receipt["verifier_receipt_present"] is False
+ assert receipt["dry_run_executor_invoked"] is False
+ assert receipt["runner_invocation_performed"] is False
+ assert receipt["endpoint_executed"] is False
+ assert receipt["sql_executed"] is False
+ assert receipt["database_written"] is False
+ assert receipt["endpoint_execution_allowed"] is False
+ assert receipt["sql_execution_allowed"] is False
+ assert receipt["database_write_allowed"] is False
+ assert receipt["database_apply_authorized"] is False
+ assert receipt["executes_database_apply"] is False
+ assert receipt["executes_endpoint"] is False
+ assert receipt["executes_sql"] is False
+ assert receipt["writes_database"] is False
+ assert receipt["stdout_included"] is False
+ assert receipt["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_verifier_no_execution_receipt_proof"
+ ]
+ is False
+ )
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_invocation_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run verifier no-execution receipt proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ ]
+ receipt_closeout = payload[
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ ]
+ guard = receipt_closeout["verifier_receipt_persistence_guard_proof"]
+ contract = payload[
+ "controlled_dry_run_verifier_no_execution_receipt_proof_closeout_contract"
+ ]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_no_execution_receipt_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-invocation-lock-proof-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_verifier_invocation_lock_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["verifier_receipt_persistence_guard_proof_count"] == 1
+ assert payload["summary"]["verifier_receipt_persistence_guard_proof_field_count"] == 12
+ assert payload["summary"]["verifier_receipt_persistence_locked_count"] == 1
+ assert payload["summary"]["verifier_receipt_persistence_allowed_count"] == 0
+ assert payload["summary"]["verifier_receipt_persisted_count"] == 0
+ assert payload["summary"]["persists_verifier_receipt_count"] == 0
+ assert payload["summary"]["verifier_invoked_count"] == 0
+ assert payload["summary"]["verifier_receipt_present_count"] == 0
+ assert payload["summary"]["dry_run_executor_invoked_count"] == 0
+ assert payload["summary"]["runner_invocation_performed_count"] == 0
+ assert payload["summary"]["endpoint_executed_count"] == 0
+ assert payload["summary"]["sql_executed_count"] == 0
+ assert payload["summary"]["database_written_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["executes_sql_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["verifier_receipt_persistence_locked"] is True
+ assert future["verifier_receipt_persistence_allowed"] is False
+ assert future["verifier_receipt_persisted"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["verifier_invoked"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_verifier_receipt_persistence_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert receipt_closeout["verifier_no_execution_receipt_proof_closeout_only"] is True
+ assert receipt_closeout["verifier_receipt_persistence_guard_proof_only"] is True
+ assert receipt_closeout["verifier_receipt_persistence_locked"] is True
+ assert receipt_closeout["verifier_receipt_persistence_allowed"] is False
+ assert receipt_closeout["verifier_receipt_persisted"] is False
+ assert receipt_closeout["persists_verifier_receipt"] is False
+ assert receipt_closeout["verifier_invoked"] is False
+ assert receipt_closeout["endpoint_executed"] is False
+ assert receipt_closeout["sql_executed"] is False
+ assert receipt_closeout["database_written"] is False
+ assert guard["guard_mode"] == "verifier_receipt_persistence_guard_proof_preview_only"
+ assert guard["verifier_receipt_persistence_locked"] is True
+ assert guard["verifier_receipt_persistence_allowed"] is False
+ assert guard["verifier_receipt_persisted"] is False
+ assert guard["persists_verifier_receipt"] is False
+ assert guard["endpoint_execution_allowed"] is False
+ assert guard["sql_execution_allowed"] is False
+ assert guard["database_write_allowed"] is False
+ assert guard["database_apply_authorized"] is False
+ assert guard["stdout_included"] is False
+ assert guard["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof"
+ ]
+ is False
+ )
+ assert contract["verifier_receipt_persistence_locked"] is True
+ assert contract["verifier_receipt_persisted"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_verifier_receipt_persistence_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run verifier receipt persistence guard proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ ]
+ storage_closeout = payload[
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ ]
+ storage = storage_closeout["receipt_persistence_storage_boundary_proof"]
+ contract = payload[
+ "controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_contract"
+ ]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-no-execution-receipt-proof-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_verifier_no_execution_receipt_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_verifier_receipt_persistence_guard_proof_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["receipt_persistence_storage_boundary_proof_count"] == 1
+ assert payload["summary"]["receipt_persistence_storage_boundary_proof_field_count"] == 12
+ assert payload["summary"]["receipt_persistence_storage_boundary_locked_count"] == 1
+ assert payload["summary"]["receipt_persistence_storage_write_allowed_count"] == 0
+ assert payload["summary"]["receipt_persistence_storage_written_count"] == 0
+ assert payload["summary"]["verifier_receipt_persistence_allowed_count"] == 0
+ assert payload["summary"]["verifier_receipt_persisted_count"] == 0
+ assert payload["summary"]["persists_verifier_receipt_count"] == 0
+ assert payload["summary"]["endpoint_executed_count"] == 0
+ assert payload["summary"]["sql_executed_count"] == 0
+ assert payload["summary"]["database_written_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["executes_sql_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["receipt_persistence_storage_boundary_locked"] is True
+ assert future["receipt_persistence_storage_write_allowed"] is False
+ assert future["receipt_persistence_storage_written"] is False
+ assert future["verifier_receipt_persistence_allowed"] is False
+ assert future["verifier_receipt_persisted"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["ready_for_receipt_persistence_storage_now"] is False
+ assert future["endpoint_execution_allowed"] is False
+ assert future["sql_execution_allowed"] is False
+ assert future["database_write_allowed"] is False
+ assert future["database_apply_authorized"] is False
+ assert storage_closeout["verifier_receipt_persistence_guard_proof_closeout_only"] is True
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_only"] is True
+ assert storage_closeout["receipt_persistence_storage_boundary_locked"] is True
+ assert storage_closeout["receipt_persistence_storage_write_allowed"] is False
+ assert storage_closeout["receipt_persistence_storage_written"] is False
+ assert storage_closeout["persists_verifier_receipt"] is False
+ assert storage_closeout["endpoint_executed"] is False
+ assert storage_closeout["sql_executed"] is False
+ assert storage_closeout["database_written"] is False
+ assert storage["storage_boundary_mode"] == "receipt_persistence_storage_boundary_proof_preview_only"
+ assert storage["receipt_persistence_storage_boundary_locked"] is True
+ assert storage["receipt_persistence_storage_write_allowed"] is False
+ assert storage["receipt_persistence_storage_written"] is False
+ assert storage["verifier_receipt_persistence_allowed"] is False
+ assert storage["verifier_receipt_persisted"] is False
+ assert storage["persists_verifier_receipt"] is False
+ assert storage["endpoint_execution_allowed"] is False
+ assert storage["sql_execution_allowed"] is False
+ assert storage["database_write_allowed"] is False
+ assert storage["database_apply_authorized"] is False
+ assert storage["stdout_included"] is False
+ assert storage["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof"
+ ]
+ is False
+ )
+ assert contract["receipt_persistence_storage_boundary_locked"] is True
+ assert contract["receipt_persistence_storage_write_allowed"] is False
+ assert contract["receipt_persistence_storage_written"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["ready_for_receipt_persistence_storage_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run receipt persistence storage boundary proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ ]
+ storage_closeout = payload[
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ ]
+ ledger = storage_closeout["storage_boundary_no_write_ledger_proof"]
+ contract = payload[
+ "controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_contract"
+ ]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-verifier-receipt-persistence-guard-proof-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_receipt_persistence_storage_boundary_proof_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["storage_boundary_no_write_ledger_proof_count"] == 1
+ assert payload["summary"]["storage_boundary_no_write_ledger_proof_field_count"] == 12
+ assert payload["summary"]["storage_boundary_write_locked_count"] == 1
+ assert payload["summary"]["storage_boundary_write_allowed_count"] == 0
+ assert payload["summary"]["storage_boundary_written_count"] == 0
+ assert payload["summary"]["ledger_write_allowed_count"] == 0
+ assert payload["summary"]["ledger_written_count"] == 0
+ assert payload["summary"]["receipt_persistence_storage_write_allowed_count"] == 0
+ assert payload["summary"]["receipt_persistence_storage_written_count"] == 0
+ assert payload["summary"]["persists_verifier_receipt_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["executes_sql_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert future["storage_boundary_write_locked"] is True
+ assert future["storage_boundary_write_allowed"] is False
+ assert future["storage_boundary_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["receipt_persistence_storage_write_allowed"] is False
+ assert future["receipt_persistence_storage_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert storage_closeout["receipt_persistence_storage_boundary_proof_closeout_only"] is True
+ assert storage_closeout["storage_boundary_no_write_ledger_proof_only"] is True
+ assert storage_closeout["storage_boundary_write_locked"] is True
+ assert storage_closeout["storage_boundary_write_allowed"] is False
+ assert storage_closeout["storage_boundary_written"] is False
+ assert storage_closeout["ledger_write_allowed"] is False
+ assert storage_closeout["ledger_written"] is False
+ assert storage_closeout["receipt_persistence_storage_write_allowed"] is False
+ assert storage_closeout["receipt_persistence_storage_written"] is False
+ assert storage_closeout["persists_verifier_receipt"] is False
+ assert storage_closeout["endpoint_executed"] is False
+ assert storage_closeout["sql_executed"] is False
+ assert storage_closeout["database_written"] is False
+ assert ledger["ledger_mode"] == "storage_boundary_no_write_ledger_proof_preview_only"
+ assert ledger["storage_boundary_write_allowed"] is False
+ assert ledger["storage_boundary_written"] is False
+ assert ledger["ledger_write_allowed"] is False
+ assert ledger["ledger_written"] is False
+ assert ledger["receipt_persistence_storage_write_allowed"] is False
+ assert ledger["receipt_persistence_storage_written"] is False
+ assert ledger["persists_verifier_receipt"] is False
+ assert ledger["endpoint_execution_allowed"] is False
+ assert ledger["sql_execution_allowed"] is False
+ assert ledger["database_write_allowed"] is False
+ assert ledger["database_apply_authorized"] is False
+ assert ledger["stdout_included"] is False
+ assert ledger["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof"
+ ]
+ is False
+ )
+ assert contract["storage_boundary_write_allowed"] is False
+ assert contract["ledger_write_allowed"] is False
+ assert contract["receipt_persistence_storage_write_allowed"] is False
+ assert contract["persists_verifier_receipt"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run storage boundary no-write ledger proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ ]
+ ledger_closeout = payload[
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+ ]
+ retention = ledger_closeout["no_write_ledger_retention_proof"]
+ contract = payload[
+ "controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_contract"
+ ]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-persistence-storage-boundary-proof-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_storage_boundary_no_write_ledger_proof_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["no_write_ledger_retention_proof_count"] == 1
+ assert payload["summary"]["no_write_ledger_retention_proof_field_count"] == 12
+ assert payload["summary"]["ledger_retention_write_locked_count"] == 1
+ assert payload["summary"]["ledger_retention_write_allowed_count"] == 0
+ assert payload["summary"]["ledger_retention_written_count"] == 0
+ assert payload["summary"]["ledger_write_allowed_count"] == 0
+ assert payload["summary"]["ledger_written_count"] == 0
+ assert payload["summary"]["receipt_persistence_storage_write_allowed_count"] == 0
+ assert payload["summary"]["receipt_persistence_storage_written_count"] == 0
+ assert payload["summary"]["persists_verifier_receipt_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["executes_sql_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert future["ledger_retention_write_locked"] is True
+ assert future["ledger_retention_write_allowed"] is False
+ assert future["ledger_retention_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert ledger_closeout["storage_boundary_no_write_ledger_proof_closeout_only"] is True
+ assert ledger_closeout["no_write_ledger_retention_proof_only"] is True
+ assert ledger_closeout["ledger_retention_write_locked"] is True
+ assert ledger_closeout["ledger_retention_write_allowed"] is False
+ assert ledger_closeout["ledger_retention_written"] is False
+ assert ledger_closeout["ledger_write_allowed"] is False
+ assert ledger_closeout["ledger_written"] is False
+ assert ledger_closeout["persists_verifier_receipt"] is False
+ assert ledger_closeout["endpoint_executed"] is False
+ assert ledger_closeout["sql_executed"] is False
+ assert ledger_closeout["database_written"] is False
+ assert retention["retention_mode"] == "no_write_ledger_retention_proof_preview_only"
+ assert retention["ledger_retention_write_allowed"] is False
+ assert retention["ledger_retention_written"] is False
+ assert retention["ledger_write_allowed"] is False
+ assert retention["ledger_written"] is False
+ assert retention["receipt_persistence_storage_write_allowed"] is False
+ assert retention["receipt_persistence_storage_written"] is False
+ assert retention["persists_verifier_receipt"] is False
+ assert retention["endpoint_execution_allowed"] is False
+ assert retention["sql_execution_allowed"] is False
+ assert retention["database_write_allowed"] is False
+ assert retention["database_apply_authorized"] is False
+ assert retention["stdout_included"] is False
+ assert retention["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_no_write_ledger_retention_proof"
+ ]
+ is False
+ )
+ assert contract["ledger_retention_write_allowed"] is False
+ assert contract["ledger_retention_written"] is False
+ assert contract["ledger_write_allowed"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run no-write ledger retention proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ ]
+ archive_closeout = payload[
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout"
+ ]
+ archive = archive_closeout["retention_boundary_no_write_archive_proof"]
+ contract = payload[
+ "controlled_dry_run_no_write_ledger_retention_proof_closeout_contract"
+ ]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_no_write_ledger_retention_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-storage-boundary-no-write-ledger-proof-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_no_write_ledger_retention_proof_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["retention_boundary_no_write_archive_proof_count"] == 1
+ assert payload["summary"]["retention_boundary_no_write_archive_proof_field_count"] == 12
+ assert payload["summary"]["retention_archive_write_locked_count"] == 1
+ assert payload["summary"]["retention_archive_write_allowed_count"] == 0
+ assert payload["summary"]["retention_archive_written_count"] == 0
+ assert payload["summary"]["ledger_retention_write_allowed_count"] == 0
+ assert payload["summary"]["ledger_retention_written_count"] == 0
+ assert payload["summary"]["ledger_write_allowed_count"] == 0
+ assert payload["summary"]["ledger_written_count"] == 0
+ assert payload["summary"]["persists_verifier_receipt_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["executes_sql_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert future["retention_archive_write_locked"] is True
+ assert future["retention_archive_write_allowed"] is False
+ assert future["retention_archive_written"] is False
+ assert future["ledger_retention_write_allowed"] is False
+ assert future["ledger_retention_written"] is False
+ assert future["ledger_write_allowed"] is False
+ assert future["ledger_written"] is False
+ assert future["persists_verifier_receipt"] is False
+ assert future["endpoint_executed"] is False
+ assert future["sql_executed"] is False
+ assert future["database_written"] is False
+ assert future["database_apply_authorized"] is False
+ assert archive_closeout["no_write_ledger_retention_proof_closeout_only"] is True
+ assert archive_closeout["retention_boundary_no_write_archive_proof_only"] is True
+ assert archive_closeout["retention_archive_write_locked"] is True
+ assert archive_closeout["retention_archive_write_allowed"] is False
+ assert archive_closeout["retention_archive_written"] is False
+ assert archive_closeout["ledger_retention_write_allowed"] is False
+ assert archive_closeout["ledger_retention_written"] is False
+ assert archive_closeout["persists_verifier_receipt"] is False
+ assert archive_closeout["endpoint_executed"] is False
+ assert archive_closeout["sql_executed"] is False
+ assert archive_closeout["database_written"] is False
+ assert archive["archive_mode"] == "retention_boundary_no_write_archive_proof_preview_only"
+ assert archive["retention_archive_write_allowed"] is False
+ assert archive["retention_archive_written"] is False
+ assert archive["ledger_retention_write_allowed"] is False
+ assert archive["ledger_retention_written"] is False
+ assert archive["ledger_write_allowed"] is False
+ assert archive["ledger_written"] is False
+ assert archive["persists_verifier_receipt"] is False
+ assert archive["endpoint_execution_allowed"] is False
+ assert archive["sql_execution_allowed"] is False
+ assert archive["database_write_allowed"] is False
+ assert archive["database_apply_authorized"] is False
+ assert archive["stdout_included"] is False
+ assert archive["stderr_included"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_retention_boundary_no_write_archive_proof"
+ ]
+ is False
+ )
+ assert contract["retention_archive_write_allowed"] is False
+ assert contract["ledger_retention_write_allowed"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run retention boundary no-write archive proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload["future_readiness"]
+ handoff = payload["sealed_handoff_proof"]
+ contract = payload["contract"]
+ assert payload["success"] is True
+ assert payload["response_mode"] == "compact"
+ assert payload["full_payload_hint"] == "append full=1 for the complete nested proof payload"
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-no-write-ledger-retention-proof-closeout"
+ )
+ assert "source_controlled_dry_run_no_write_ledger_retention_proof_closeout" not in payload
+ assert (
+ "future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ not in payload
+ )
+ assert (
+ "controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ not in payload
+ )
+ assert payload["summary"]["controlled_dry_run_retention_boundary_no_write_archive_proof_closeout_check_count"] == 12
+ assert payload["summary"]["archive_retention_sealed_handoff_proof_count"] == 1
+ assert payload["summary"]["archive_retention_sealed_handoff_proof_field_count"] == 12
+ assert payload["summary"]["sealed_handoff_write_locked_count"] == 1
+ assert payload["summary"]["sealed_handoff_write_allowed_count"] == 0
+ assert payload["summary"]["sealed_handoff_written_count"] == 0
+ assert payload["summary"]["retention_archive_write_allowed_count"] == 0
+ assert payload["summary"]["retention_archive_written_count"] == 0
+ assert payload["summary"]["ledger_retention_write_allowed_count"] == 0
+ assert payload["summary"]["ledger_retention_written_count"] == 0
+ assert payload["summary"]["ledger_write_allowed_count"] == 0
+ assert payload["summary"]["ledger_written_count"] == 0
+ assert payload["summary"]["persists_verifier_receipt_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["executes_sql_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+ ]
+ is False
+ )
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert handoff["handoff_mode"] == "archive_retention_sealed_handoff_proof_preview_only"
+ assert len(handoff["sealed_handoff_manifest_hash"]) == 64
+ assert handoff["sealed_handoff_write_locked"] is True
+ assert handoff["sealed_handoff_write_allowed"] is False
+ assert handoff["sealed_handoff_written"] is False
+ assert handoff["retention_archive_write_allowed"] is False
+ assert handoff["retention_archive_written"] is False
+ assert handoff["ledger_retention_write_allowed"] is False
+ assert handoff["ledger_retention_written"] is False
+ assert handoff["ledger_write_allowed"] is False
+ assert handoff["ledger_written"] is False
+ assert handoff["persists_verifier_receipt"] is False
+ assert handoff["endpoint_executed"] is False
+ assert handoff["sql_executed"] is False
+ assert handoff["database_written"] is False
+ assert handoff["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_archive_retention_sealed_handoff_proof"
+ ]
+ is False
+ )
+ assert contract["sealed_handoff_write_allowed"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert len(payload["checks"]) == 12
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_route_defaults_to_compact_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run archive retention sealed handoff proof closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-archive-retention-sealed-handoff-proof-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload["future_readiness"]
+ transfer = payload["sealed_handoff_verifier_transfer_proof"]
+ contract = payload["contract"]
+ assert payload["success"] is True
+ assert payload["response_mode"] == "compact"
+ assert payload["full_payload_hint"] == "append full=1 for the complete nested proof payload"
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-retention-boundary-no-write-archive-proof-closeout"
+ )
+ assert (
+ "source_controlled_dry_run_retention_boundary_no_write_archive_proof_closeout"
+ not in payload
+ )
+ assert (
+ "future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ not in payload
+ )
+ assert (
+ "controlled_dry_run_archive_retention_sealed_handoff_proof_closeout"
+ not in payload
+ )
+ assert payload["summary"]["controlled_dry_run_archive_retention_sealed_handoff_proof_closeout_check_count"] == 12
+ assert payload["summary"]["sealed_handoff_verifier_transfer_proof_count"] == 1
+ assert payload["summary"]["sealed_handoff_verifier_transfer_proof_field_count"] == 12
+ assert payload["summary"]["verifier_transfer_write_locked_count"] == 1
+ assert payload["summary"]["verifier_transfer_write_allowed_count"] == 0
+ assert payload["summary"]["verifier_transfer_written_count"] == 0
+ assert payload["summary"]["persists_verifier_receipt_count"] == 0
+ assert payload["summary"]["verifier_invoked_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["executes_sql_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert (
+ future[
+ "ready_for_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ ]
+ is False
+ )
+ assert (
+ future[
+ "can_enter_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof_closeout"
+ ]
+ is False
+ )
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert transfer["verifier_transfer_mode"] == "sealed_handoff_verifier_transfer_proof_preview_only"
+ assert len(transfer["verifier_transfer_manifest_hash"]) == 64
+ assert transfer["verifier_transfer_write_locked"] is True
+ assert transfer["verifier_transfer_write_allowed"] is False
+ assert transfer["verifier_transfer_written"] is False
+ assert transfer["verifier_invocation_allowed"] is False
+ assert transfer["verifier_invoked"] is False
+ assert transfer["persists_verifier_receipt"] is False
+ assert transfer["endpoint_executed"] is False
+ assert transfer["sql_executed"] is False
+ assert transfer["database_written"] is False
+ assert transfer["database_apply_authorized"] is False
+ assert (
+ contract[
+ "permits_future_database_apply_controlled_dry_run_sealed_handoff_verifier_transfer_proof"
+ ]
+ is False
+ )
+ assert contract["verifier_transfer_write_allowed"] is False
+ assert contract["verifier_invocation_allowed"] is False
+ assert contract["ready_for_database_apply_now"] is False
+ assert contract["executes_database_apply"] is False
+ assert contract["database_apply_authorized"] is False
+ assert len(payload["checks"]) == 12
+ assert payload["safety"]["persists_verifier_receipt"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_package_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run package should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-package?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_package
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ receipt = payload[
+ "future_database_apply_controlled_dry_run_execution_receipt"
+ ]
+ package = payload["controlled_dry_run_package"]
+ command_shape = package["dry_run_command_shape"]
+ receipt_preview = package["dry_run_execution_receipt_preview"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_package"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-apply-final-preflight"
+ )
+ assert payload["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert payload["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_package_count"] == 1
+ assert payload["summary"]["controlled_dry_run_package_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_acceptance_gate_count"] == 10
+ assert payload["summary"]["dry_run_execution_receipt_preview_count"] == 1
+ assert payload["summary"]["dry_run_execution_receipt_field_count"] == 8
+ assert payload["summary"]["rollback_binding_count"] == 1
+ assert payload["summary"]["post_apply_verifier_binding_count"] == 1
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert receipt["dry_run_execution_performed"] is False
+ assert receipt["ready_for_database_apply_now"] is False
+ assert receipt["database_apply_authorized"] is False
+ assert receipt["issues_database_apply_authorization"] is False
+ assert receipt["signs_database_apply_authorization"] is False
+ assert receipt["executes_authorization_evidence"] is False
+ assert receipt["executes_database_apply"] is False
+ assert receipt["executes_endpoint"] is False
+ assert receipt["executes_sql"] is False
+ assert receipt["writes_database"] is False
+ assert package["dry_run_only"] is True
+ assert package["check_mode_only"] is True
+ assert package["accepts_plaintext_secret"] is False
+ assert package["reads_secret_in_preview"] is False
+ assert package["signature_material_included"] is False
+ assert package["secret_material_included"] is False
+ assert package["signs_database_apply_authorization"] is False
+ assert package["executes_authorization_evidence"] is False
+ assert package["executes_database_apply"] is False
+ assert package["executes_endpoint_in_preview"] is False
+ assert package["executes_sql_in_preview"] is False
+ assert package["writes_database_in_preview"] is False
+ assert package["ready_for_database_apply_now"] is False
+ assert package["database_apply_authorized"] is False
+ assert command_shape["execution_allowed"] is False
+ assert command_shape["shell_command_included"] is False
+ assert command_shape["sql_included"] is False
+ assert command_shape["endpoint_execution_included"] is False
+ assert command_shape["database_write_included"] is False
+ assert receipt_preview["execution_performed"] is False
+ assert receipt_preview["stdout_included"] is False
+ assert receipt_preview["stderr_included"] is False
+ assert receipt_preview["database_apply_authorized"] is False
+ assert receipt_preview["executes_shell"] is False
+ assert receipt_preview["executes_endpoint"] is False
+ assert receipt_preview["executes_sql"] is False
+ assert receipt_preview["writes_database"] is False
+ assert receipt_preview["reads_secret"] is False
+ assert (
+ payload["controlled_dry_run_package_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_package_contract"]["executes_database_apply"]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_package_contract"]["executes_endpoint"]
+ is False
+ )
+ assert payload["controlled_dry_run_package_contract"]["executes_sql"] is False
+ assert (
+ payload["controlled_dry_run_package_contract"]["database_apply_authorized"]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_package_contract"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+ assert payload["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_receipt_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run receipt closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_result_parser_verification"
+ ]
+ closeout = payload["controlled_dry_run_receipt_closeout"]
+ parser = closeout["dry_run_result_parser"]
+ validation = closeout["receipt_validation_report"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_receipt_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-package"
+ )
+ assert payload["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert payload["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_receipt_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_receipt_closeout_field_count"] == 12
+ assert payload["summary"]["controlled_dry_run_receipt_closeout_acceptance_gate_count"] == 10
+ assert payload["summary"]["dry_run_result_parser_count"] == 1
+ assert payload["summary"]["dry_run_result_parser_field_count"] == 10
+ assert payload["summary"]["receipt_validation_report_count"] == 1
+ assert payload["summary"]["receipt_validation_field_count"] == 8
+ assert payload["summary"]["dry_run_execution_receipt_preview_count"] == 1
+ assert payload["summary"]["dry_run_execution_receipt_field_count"] == 8
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_execution_performed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert parser["execution_required"] is False
+ assert parser["stdout_allowed"] is False
+ assert parser["stderr_allowed"] is False
+ assert parser["database_apply_authorized"] is False
+ assert parser["parser_field_count"] == 10
+ assert validation["execution_performed"] is False
+ assert validation["stdout_included"] is False
+ assert validation["stderr_included"] is False
+ assert validation["database_apply_authorized"] is False
+ assert validation["executes_shell"] is False
+ assert validation["executes_endpoint"] is False
+ assert validation["executes_sql"] is False
+ assert validation["writes_database"] is False
+ assert validation["reads_secret"] is False
+ assert closeout["receipt_preview_only"] is True
+ assert closeout["dry_run_only"] is True
+ assert closeout["check_mode_only"] is True
+ assert closeout["accepts_plaintext_secret"] is False
+ assert closeout["reads_secret_in_preview"] is False
+ assert closeout["signature_material_included"] is False
+ assert closeout["secret_material_included"] is False
+ assert closeout["signs_database_apply_authorization"] is False
+ assert closeout["executes_authorization_evidence"] is False
+ assert closeout["executes_database_apply"] is False
+ assert closeout["executes_endpoint_in_preview"] is False
+ assert closeout["executes_sql_in_preview"] is False
+ assert closeout["writes_database_in_preview"] is False
+ assert closeout["ready_for_database_apply_now"] is False
+ assert closeout["database_apply_authorized"] is False
+ assert (
+ payload["controlled_dry_run_receipt_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_receipt_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_receipt_closeout_contract"]["executes_endpoint"]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_receipt_closeout_contract"]["executes_sql"]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_receipt_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+ assert payload["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_runner_readiness_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run runner readiness should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-readiness?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_readiness
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_execution_plan_binding"
+ ]
+ runner = payload["controlled_dry_run_runner_readiness"]
+ plan = runner["execution_plan_binding"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_readiness"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-receipt-closeout"
+ )
+ assert payload["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert payload["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_readiness_count"] == 1
+ assert payload["summary"]["controlled_dry_run_runner_readiness_field_count"] == 12
+ assert (
+ payload["summary"][
+ "controlled_dry_run_runner_readiness_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert payload["summary"]["execution_plan_binding_count"] == 1
+ assert payload["summary"]["execution_plan_binding_field_count"] == 12
+ assert payload["summary"]["dry_run_result_parser_count"] == 1
+ assert payload["summary"]["receipt_validation_report_count"] == 1
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["dry_run_execution_performed"] is False
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert runner["runner_readiness_only"] is True
+ assert runner["execution_plan_preview_only"] is True
+ assert runner["runner_execution_authorized"] is False
+ assert runner["dry_run_execution_authorized"] is False
+ assert runner["dry_run_only"] is True
+ assert runner["check_mode_only"] is True
+ assert runner["accepts_plaintext_secret"] is False
+ assert runner["reads_secret_in_preview"] is False
+ assert runner["signature_material_included"] is False
+ assert runner["secret_material_included"] is False
+ assert runner["signs_database_apply_authorization"] is False
+ assert runner["executes_authorization_evidence"] is False
+ assert runner["executes_database_apply"] is False
+ assert runner["executes_endpoint_in_preview"] is False
+ assert runner["executes_sql_in_preview"] is False
+ assert runner["writes_database_in_preview"] is False
+ assert runner["ready_for_database_apply_now"] is False
+ assert runner["database_apply_authorized"] is False
+ assert plan["execution_authorized"] is False
+ assert plan["runner_execution_authorized"] is False
+ assert plan["dry_run_execution_authorized"] is False
+ assert plan["shell_execution_included"] is False
+ assert plan["endpoint_execution_included"] is False
+ assert plan["sql_execution_included"] is False
+ assert plan["database_write_included"] is False
+ assert plan["stdout_capture_allowed"] is False
+ assert plan["stderr_capture_allowed"] is False
+ assert plan["database_apply_authorized"] is False
+ assert (
+ payload["controlled_dry_run_runner_readiness_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_runner_readiness_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_runner_readiness_contract"]["executes_endpoint"]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_runner_readiness_contract"]["executes_sql"]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_runner_readiness_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+ assert payload["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run execution plan closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-plan-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_command_artifact_verification"
+ ]
+ plan_closeout = payload["controlled_dry_run_execution_plan_closeout"]
+ artifact = plan_closeout["non_executable_command_artifact"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_execution_plan_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-readiness"
+ )
+ assert (
+ payload["summary"][
+ "controlled_dry_run_execution_plan_closeout_check_count"
+ ]
+ == 12
+ )
+ assert payload["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert payload["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_execution_plan_closeout_count"] == 1
+ assert (
+ payload["summary"][
+ "controlled_dry_run_execution_plan_closeout_field_count"
+ ]
+ == 12
+ )
+ assert (
+ payload["summary"][
+ "controlled_dry_run_execution_plan_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert payload["summary"]["non_executable_command_artifact_count"] == 1
+ assert payload["summary"]["non_executable_command_artifact_field_count"] == 10
+ assert payload["summary"]["execution_plan_binding_count"] == 1
+ assert payload["summary"]["execution_plan_binding_field_count"] == 12
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["execution_authorized"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert plan_closeout["execution_plan_closeout_only"] is True
+ assert plan_closeout["non_executable_command_artifact_only"] is True
+ assert plan_closeout["runner_execution_authorized"] is False
+ assert plan_closeout["dry_run_execution_authorized"] is False
+ assert plan_closeout["execution_authorized"] is False
+ assert plan_closeout["dry_run_only"] is True
+ assert plan_closeout["check_mode_only"] is True
+ assert plan_closeout["accepts_plaintext_secret"] is False
+ assert plan_closeout["reads_secret_in_preview"] is False
+ assert plan_closeout["signature_material_included"] is False
+ assert plan_closeout["secret_material_included"] is False
+ assert plan_closeout["signs_database_apply_authorization"] is False
+ assert plan_closeout["executes_authorization_evidence"] is False
+ assert plan_closeout["executes_database_apply"] is False
+ assert plan_closeout["executes_endpoint_in_preview"] is False
+ assert plan_closeout["executes_sql_in_preview"] is False
+ assert plan_closeout["writes_database_in_preview"] is False
+ assert plan_closeout["ready_for_database_apply_now"] is False
+ assert plan_closeout["database_apply_authorized"] is False
+ assert artifact["command_text_included"] is False
+ assert artifact["argv_included"] is False
+ assert artifact.get("command_text") is None
+ assert artifact.get("argv") is None
+ assert artifact["shell_command_included"] is False
+ assert artifact["endpoint_execution_included"] is False
+ assert artifact["sql_execution_included"] is False
+ assert artifact["database_write_included"] is False
+ assert artifact["execution_authorized"] is False
+ assert artifact["database_apply_authorized"] is False
+ assert len(artifact["non_executable_command_artifact_sha256"]) == 64
+ assert (
+ payload["controlled_dry_run_execution_plan_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_execution_plan_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_execution_plan_closeout_contract"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_execution_plan_closeout_contract"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_execution_plan_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+ assert payload["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run command artifact closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-command-artifact-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_runner_execution_receipt_preflight"
+ ]
+ command_closeout = payload["controlled_dry_run_command_artifact_closeout"]
+ receipt_preflight = command_closeout["runner_execution_receipt_preflight"]
+ artifact = command_closeout["non_executable_command_artifact"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_command_artifact_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-execution-plan-closeout"
+ )
+ assert (
+ payload["summary"][
+ "controlled_dry_run_command_artifact_closeout_check_count"
+ ]
+ == 12
+ )
+ assert payload["summary"]["controlled_dry_run_execution_plan_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert payload["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_command_artifact_closeout_count"] == 1
+ assert payload["summary"]["controlled_dry_run_command_artifact_closeout_field_count"] == 12
+ assert (
+ payload["summary"][
+ "controlled_dry_run_command_artifact_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert payload["summary"]["runner_execution_receipt_preflight_count"] == 1
+ assert payload["summary"]["runner_execution_receipt_preflight_field_count"] == 10
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["execution_authorized"] is False
+ assert future["stdout_capture_allowed"] is False
+ assert future["stderr_capture_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert command_closeout["command_artifact_closeout_only"] is True
+ assert command_closeout["runner_execution_receipt_preflight_only"] is True
+ assert command_closeout["runner_execution_authorized"] is False
+ assert command_closeout["dry_run_execution_authorized"] is False
+ assert command_closeout["execution_authorized"] is False
+ assert command_closeout["dry_run_only"] is True
+ assert command_closeout["check_mode_only"] is True
+ assert command_closeout["accepts_plaintext_secret"] is False
+ assert command_closeout["reads_secret_in_preview"] is False
+ assert command_closeout["signature_material_included"] is False
+ assert command_closeout["secret_material_included"] is False
+ assert command_closeout["signs_database_apply_authorization"] is False
+ assert command_closeout["executes_authorization_evidence"] is False
+ assert command_closeout["executes_database_apply"] is False
+ assert command_closeout["executes_endpoint_in_preview"] is False
+ assert command_closeout["executes_sql_in_preview"] is False
+ assert command_closeout["writes_database_in_preview"] is False
+ assert command_closeout["ready_for_database_apply_now"] is False
+ assert command_closeout["database_apply_authorized"] is False
+ assert artifact["command_text_included"] is False
+ assert artifact["argv_included"] is False
+ assert artifact.get("command_text") is None
+ assert artifact.get("argv") is None
+ assert artifact["shell_command_included"] is False
+ assert artifact["endpoint_execution_included"] is False
+ assert artifact["sql_execution_included"] is False
+ assert artifact["database_write_included"] is False
+ assert receipt_preflight["preflight_status"] == "preflight_only_not_executed"
+ assert receipt_preflight["execution_required"] is False
+ assert receipt_preflight["execution_authorized"] is False
+ assert receipt_preflight["runner_execution_authorized"] is False
+ assert receipt_preflight["stdout_capture_allowed"] is False
+ assert receipt_preflight["stderr_capture_allowed"] is False
+ assert receipt_preflight["execution_performed"] is False
+ assert receipt_preflight["writes_database"] is False
+ assert (
+ payload["controlled_dry_run_command_artifact_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_command_artifact_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_command_artifact_closeout_contract"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_command_artifact_closeout_contract"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_command_artifact_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+ assert payload["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply controlled dry-run runner execution receipt closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-runner-execution-receipt-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ future = payload[
+ "future_database_apply_controlled_dry_run_post_receipt_parser_verification"
+ ]
+ receipt_closeout = payload[
+ "controlled_dry_run_runner_execution_receipt_closeout"
+ ]
+ preview = receipt_closeout["receipt_closeout_preview"]
+ parser = receipt_closeout["post_receipt_parser_verification"]
+ preflight = receipt_closeout["runner_execution_receipt_preflight"]
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_controlled_dry_run_runner_execution_receipt_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-controlled-dry-run-command-artifact-closeout"
+ )
+ assert (
+ payload["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_check_count"
+ ]
+ == 12
+ )
+ assert payload["summary"]["controlled_dry_run_command_artifact_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_execution_plan_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_readiness_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_receipt_closeout_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_package_check_count"] == 12
+ assert payload["summary"]["controlled_apply_final_preflight_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_closeout_check_count"] == 12
+ assert payload["summary"]["authorization_evidence_execution_preflight_check_count"] == 12
+ assert payload["summary"]["controlled_dry_run_runner_execution_receipt_closeout_count"] == 1
+ assert (
+ payload["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_field_count"
+ ]
+ == 12
+ )
+ assert (
+ payload["summary"][
+ "controlled_dry_run_runner_execution_receipt_closeout_acceptance_gate_count"
+ ]
+ == 10
+ )
+ assert payload["summary"]["post_receipt_parser_verification_count"] == 1
+ assert payload["summary"]["post_receipt_parser_verification_field_count"] == 10
+ assert payload["summary"]["receipt_closeout_preview_count"] == 1
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert future["runner_execution_authorized"] is False
+ assert future["dry_run_execution_authorized"] is False
+ assert future["execution_authorized"] is False
+ assert future["stdout_capture_allowed"] is False
+ assert future["stderr_capture_allowed"] is False
+ assert future["ready_for_database_apply_now"] is False
+ assert future["database_apply_authorized"] is False
+ assert future["issues_database_apply_authorization"] is False
+ assert future["signs_database_apply_authorization"] is False
+ assert future["executes_authorization_evidence"] is False
+ assert future["executes_database_apply"] is False
+ assert future["executes_endpoint"] is False
+ assert future["executes_sql"] is False
+ assert future["writes_database"] is False
+ assert receipt_closeout["runner_execution_receipt_closeout_only"] is True
+ assert receipt_closeout["post_receipt_parser_verification_only"] is True
+ assert receipt_closeout["runner_execution_authorized"] is False
+ assert receipt_closeout["dry_run_execution_authorized"] is False
+ assert receipt_closeout["execution_authorized"] is False
+ assert receipt_closeout["dry_run_only"] is True
+ assert receipt_closeout["check_mode_only"] is True
+ assert receipt_closeout["accepts_plaintext_secret"] is False
+ assert receipt_closeout["reads_secret_in_preview"] is False
+ assert receipt_closeout["signature_material_included"] is False
+ assert receipt_closeout["secret_material_included"] is False
+ assert receipt_closeout["signs_database_apply_authorization"] is False
+ assert receipt_closeout["executes_authorization_evidence"] is False
+ assert receipt_closeout["executes_database_apply"] is False
+ assert receipt_closeout["executes_endpoint_in_preview"] is False
+ assert receipt_closeout["executes_sql_in_preview"] is False
+ assert receipt_closeout["writes_database_in_preview"] is False
+ assert preflight["preflight_status"] == "preflight_only_not_executed"
+ assert preflight["execution_required"] is False
+ assert preflight["execution_performed"] is False
+ assert preflight["stdout_capture_allowed"] is False
+ assert preflight["stderr_capture_allowed"] is False
+ assert preflight["writes_database"] is False
+ assert preview["receipt_status"] == "receipt_closeout_preview_not_executed"
+ assert preview["execution_required"] is False
+ assert preview["execution_performed"] is False
+ assert preview["stdout_included"] is False
+ assert preview["stderr_included"] is False
+ assert preview["writes_database"] is False
+ assert parser["expected_preflight_status"] == "preflight_only_not_executed"
+ assert parser["expected_receipt_status"] == "receipt_closeout_preview_not_executed"
+ assert parser["execution_required"] is False
+ assert parser["stdout_allowed"] is False
+ assert parser["stderr_allowed"] is False
+ assert parser["database_apply_authorized"] is False
+ assert (
+ payload["controlled_dry_run_runner_execution_receipt_closeout_contract"][
+ "ready_for_database_apply_now"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_runner_execution_receipt_closeout_contract"][
+ "executes_database_apply"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_runner_execution_receipt_closeout_contract"][
+ "executes_endpoint"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_runner_execution_receipt_closeout_contract"][
+ "executes_sql"
+ ]
+ is False
+ )
+ assert (
+ payload["controlled_dry_run_runner_execution_receipt_closeout_contract"][
+ "database_apply_authorized"
+ ]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["executes_endpoint_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["reads_secret_in_preview"] is False
+ assert payload["safety"]["executes_endpoint"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+ assert payload["safety"]["executes_database_apply"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_decision_closeout_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization signing decision closeout should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-closeout?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_signing_decision_closeout
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_decision_closeout"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-preflight"
+ )
+ assert payload["summary"]["signing_decision_closeout_check_count"] == 12
+ assert payload["summary"]["signing_decision_input_requirement_count"] == 10
+ assert payload["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_authorization_signing_decision_closeout"]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_decision_closeout"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_decision_closeout"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["unsigned_signing_decision_package"]["ready_for_database_apply_now"] is False
+ assert payload["unsigned_signing_decision_package"]["signs_database_apply_authorization"] is False
+ assert payload["unsigned_signing_decision_package"]["secret_material_included"] is False
+ assert payload["unsigned_signing_decision_package"]["secret_material_required_in_preview"] is False
+ assert payload["signing_decision_closeout_contract"]["ready_for_database_apply_now"] is False
+ assert (
+ payload["signing_decision_closeout_contract"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
+
+
+def test_auto_policy_db_apply_authorization_signing_issuer_guard_route_defaults_to_no_fetch_and_uses_cached_payload(monkeypatch):
+ from flask import Flask
+ from routes import ai_routes as routes
+
+ monkeypatch.setattr(routes, "_get_cached_pchome_growth_payload", lambda: _payload())
+
+ def fail_engine(database_path):
+ raise AssertionError("cached auto-policy DB apply authorization signing issuer guard should not open a DB engine")
+
+ monkeypatch.setattr(routes, "_create_icaim_dashboard_engine", fail_engine)
+
+ app = Flask(__name__)
+ with app.test_request_context(
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-guard?batch_size=1"
+ ):
+ response = (
+ routes
+ .api_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_guard
+ .__wrapped__()
+ )
+
+ payload = response.get_json()
+ assert payload["success"] is True
+ assert payload["policy"] == (
+ "read_only_pchome_growth_auto_policy_db_apply_authorization_signing_issuer_guard"
+ )
+ assert payload["source_endpoint"] == (
+ "/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-closeout"
+ )
+ assert payload["summary"]["signing_issuer_guard_check_count"] == 12
+ assert payload["summary"]["signing_decision_closeout_check_count"] == 12
+ assert payload["summary"]["signing_decision_input_requirement_count"] == 10
+ assert payload["summary"]["signing_decision_rejection_reason_count"] == 11
+ assert payload["summary"]["signs_database_apply_authorization_count"] == 0
+ assert (
+ payload["future_authorization_signing_issuer_guard"]["ready_for_database_apply_now"]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_issuer_guard"][
+ "issues_database_apply_authorization"
+ ]
+ is False
+ )
+ assert (
+ payload["future_authorization_signing_issuer_guard"][
+ "signs_database_apply_authorization"
+ ]
+ is False
+ )
+ assert payload["signable_request_boundary"]["ready_for_database_apply_now"] is False
+ assert payload["signable_request_boundary"]["signs_database_apply_authorization"] is False
+ assert payload["signable_request_boundary"]["secret_material_included"] is False
+ assert payload["signable_request_boundary"]["secret_material_required_in_preview"] is False
+ assert payload["signing_issuer_guard_contract"]["ready_for_database_apply_now"] is False
+ assert (
+ payload["signing_issuer_guard_contract"]["signs_database_apply_authorization"]
+ is False
+ )
+ assert payload["summary"]["reads_secret_count"] == 0
+ assert payload["summary"]["executes_script_count"] == 0
+ assert payload["summary"]["executes_migration_count"] == 0
+ assert payload["summary"]["writes_database_count"] == 0
+ assert payload["safety"]["signs_database_apply_authorization"] is False
+ assert payload["safety"]["executes_sql"] is False
+ assert payload["safety"]["writes_database"] is False
diff --git a/tests/test_production_version_truth.py b/tests/test_production_version_truth.py
new file mode 100644
index 0000000..cfeb62b
--- /dev/null
+++ b/tests/test_production_version_truth.py
@@ -0,0 +1,68 @@
+import json
+
+from scripts.ops import check_production_version_truth as guard
+
+
+def _report(production_version="V10.725", local_version="V10.725", head_version="V10.725"):
+ return {
+ "policy": "production_health_is_latest_version_truth",
+ "health_url": "https://mo.wooo.work/health",
+ "production": {
+ "status": "healthy",
+ "database": "postgresql",
+ "version": production_version,
+ },
+ "local": {
+ "branch": "main",
+ "head": "f3e412cd211f5e4601204b256aeb95eae073b441",
+ "config_version": local_version,
+ "head_config_version": head_version,
+ },
+ "origin_main": {
+ "head": "f3e412cd211f5e4601204b256aeb95eae073b441",
+ "matches_local_head": True,
+ },
+ }
+
+
+def test_parse_config_version():
+ assert guard.parse_config_version('SYSTEM_VERSION = "V10.725"\n') == "V10.725"
+
+
+def test_production_version_truth_passes_when_everything_matches():
+ ok, errors = guard.evaluate(_report(), allow_local_version_drift=False)
+
+ assert ok is True
+ assert errors == []
+
+
+def test_working_tree_version_drift_blocks_by_default():
+ ok, errors = guard.evaluate(_report(local_version="V10.726"), allow_local_version_drift=False)
+
+ assert ok is False
+ assert any("working-tree config.py version differs from production" in error for error in errors)
+
+
+def test_explicit_release_prep_can_allow_working_tree_version_drift():
+ ok, errors = guard.evaluate(_report(local_version="V10.726"), allow_local_version_drift=True)
+
+ assert ok is True
+ assert errors == []
+
+
+def test_head_version_must_still_match_production():
+ ok, errors = guard.evaluate(_report(head_version="V10.724"), allow_local_version_drift=True)
+
+ assert ok is False
+ assert any("HEAD config.py version differs from production" in error for error in errors)
+
+
+def test_json_output_contains_production_truth_policy(monkeypatch, capsys):
+ monkeypatch.setattr(guard, "build_report", lambda health_url, timeout: _report())
+
+ exit_code = guard.main(["--json"])
+ payload = json.loads(capsys.readouterr().out)
+
+ assert exit_code == 0
+ assert payload["policy"] == "production_health_is_latest_version_truth"
+ assert payload["production"]["version"] == "V10.725"