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

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

View File

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