補齊 PChome controlled apply drift verifier
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
This commit is contained in:
@@ -74,6 +74,9 @@ DIRECT_MAPPING_RETRY_CANDIDATE_EXCEPTION_CONTROLLED_APPLY_EXECUTOR_POLICY = (
|
||||
DIRECT_MAPPING_RETRY_CANDIDATE_EXCEPTION_CONTROLLED_APPLY_RECEIPT_REPLAY_POLICY = (
|
||||
"ai_controlled_pchome_growth_direct_mapping_retry_candidate_exception_controlled_apply_receipt_replay"
|
||||
)
|
||||
DIRECT_MAPPING_RETRY_CANDIDATE_EXCEPTION_CONTROLLED_APPLY_DRIFT_VERIFIER_POLICY = (
|
||||
"ai_controlled_pchome_growth_direct_mapping_retry_candidate_exception_controlled_apply_drift_verifier"
|
||||
)
|
||||
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"
|
||||
@@ -4507,6 +4510,95 @@ def build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_recei
|
||||
}
|
||||
|
||||
|
||||
def build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_drift_verifier_package(
|
||||
*,
|
||||
artifact_root: str | Path | None = None,
|
||||
run_id: str | None = None,
|
||||
engine: Any = None,
|
||||
source_receipt_replay: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Verify that applied PChome product matches still agree with the replay receipt."""
|
||||
replay = source_receipt_replay or build_pchome_direct_mapping_retry_candidate_exception_controlled_apply_receipt_replay_package(
|
||||
artifact_root=artifact_root,
|
||||
run_id=run_id,
|
||||
materialize_artifacts=False,
|
||||
engine=engine,
|
||||
)
|
||||
replay_summary = replay.get("summary") or {}
|
||||
readbacks = list(replay.get("post_apply_readbacks") or [])
|
||||
drift_items = [item for item in readbacks if item.get("passed") is not True]
|
||||
selector_count = int(replay_summary.get("target_selector_count") or 0)
|
||||
pass_count = int(replay_summary.get("post_apply_readback_pass_count") or 0)
|
||||
receipt_hash_match_count = int(replay_summary.get("executor_receipt_hash_match_count") or 0)
|
||||
source_ready = (
|
||||
replay.get("result") == "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_RECEIPT_REPLAYED"
|
||||
and selector_count > 0
|
||||
and receipt_hash_match_count > 0
|
||||
)
|
||||
drift_verified = source_ready and not drift_items and pass_count == selector_count
|
||||
|
||||
if drift_items:
|
||||
result = "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_DRIFT_DETECTED"
|
||||
elif drift_verified:
|
||||
result = "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_DRIFT_VERIFIED"
|
||||
elif replay.get("missing_artifacts"):
|
||||
result = "WAITING_FOR_RETRY_EXCEPTION_CONTROLLED_APPLY_DRIFT_ARTIFACTS"
|
||||
else:
|
||||
result = "WAITING_FOR_RETRY_EXCEPTION_CONTROLLED_APPLY_DRIFT_BASELINE"
|
||||
|
||||
checks = [
|
||||
{"check": "source_replay_loaded", "passed": bool(replay)},
|
||||
{"check": "source_receipt_hash_matches", "passed": receipt_hash_match_count > 0},
|
||||
{"check": "target_selectors_present", "passed": selector_count > 0},
|
||||
{"check": "all_current_readbacks_match_receipt", "passed": not drift_items and pass_count == selector_count},
|
||||
{"check": "drift_verifier_does_not_write_database", "passed": True},
|
||||
]
|
||||
return {
|
||||
"policy": DIRECT_MAPPING_RETRY_CANDIDATE_EXCEPTION_CONTROLLED_APPLY_DRIFT_VERIFIER_POLICY,
|
||||
"result": result,
|
||||
"success": result == "DIRECT_MAPPING_RETRY_EXCEPTION_CONTROLLED_APPLY_DRIFT_VERIFIED",
|
||||
"summary": {
|
||||
"target_selector_count": selector_count,
|
||||
"post_apply_readback_count": int(replay_summary.get("post_apply_readback_count") or 0),
|
||||
"post_apply_readback_pass_count": pass_count,
|
||||
"drift_count": len(drift_items),
|
||||
"drift_verified_count": 1 if drift_verified else 0,
|
||||
"receipt_hash_match_count": receipt_hash_match_count,
|
||||
"missing_artifact_count": int(replay_summary.get("missing_artifact_count") or 0),
|
||||
"writes_database_count": 0,
|
||||
},
|
||||
"drift_verifier": {
|
||||
"stage": "P2_retry_exception_controlled_apply_drift_verifier",
|
||||
"status": result,
|
||||
"source_receipt_replay_result": replay.get("result"),
|
||||
"ready": drift_verified,
|
||||
"requires_production_version_truth": True,
|
||||
},
|
||||
"drift_items": drift_items,
|
||||
"post_apply_readbacks": readbacks,
|
||||
"source_receipt_replay_summary": replay_summary,
|
||||
"checks": checks,
|
||||
"check_count": len(checks),
|
||||
"all_checks_passed": all(check.get("passed") is True for check in checks),
|
||||
"next_actions": [
|
||||
"Keep this verifier on the readiness surface so DB drift is visible without manual table review.",
|
||||
"If drift is detected, use the receipt replay readbacks as rollback or re-apply evidence.",
|
||||
],
|
||||
"safety": {
|
||||
"ai_controlled_apply": True,
|
||||
"reads_artifact_files": True,
|
||||
"reads_database": engine is not None or bool(source_receipt_replay),
|
||||
"writes_database": False,
|
||||
"writes_database_count": 0,
|
||||
"writes_artifact_count": 0,
|
||||
"syncs_external_offers": False,
|
||||
"dispatches_telegram": 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)
|
||||
@@ -5102,6 +5194,7 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
execute_fetch: bool = False,
|
||||
search_func: Any = None,
|
||||
controlled_apply_receipt_replay: dict[str, Any] | None = None,
|
||||
controlled_apply_drift_verifier: dict[str, Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Build a single read-only product-facing AI automation readiness view."""
|
||||
mapping_summary = summarize_pchome_mapping_backlog(payload)
|
||||
@@ -5161,6 +5254,10 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
and receipt_replay_readback_pass_count == receipt_replay_selector_count
|
||||
and int(receipt_replay_summary.get("executor_receipt_ready_count") or 0) > 0
|
||||
)
|
||||
drift_summary = (controlled_apply_drift_verifier or {}).get("summary") or {}
|
||||
controlled_apply_drift_count = int(drift_summary.get("drift_count") or 0)
|
||||
controlled_apply_drift_verified_count = int(drift_summary.get("drift_verified_count") or 0)
|
||||
controlled_apply_drift_selector_count = int(drift_summary.get("target_selector_count") or 0)
|
||||
exception_count = _summary_exception_count(receipt_summary) + int(
|
||||
decision_summary.get("machine_review_decision_count") or 0
|
||||
)
|
||||
@@ -5187,7 +5284,9 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
"writes_database": False,
|
||||
}
|
||||
|
||||
if controlled_apply_closeout_verified:
|
||||
if controlled_apply_drift_count:
|
||||
result = "AI_AUTOMATION_CONTROLLED_APPLY_DRIFT_DETECTED"
|
||||
elif controlled_apply_closeout_verified:
|
||||
result = "AI_AUTOMATION_CONTROLLED_APPLY_CLOSEOUT_VERIFIED"
|
||||
elif not direct_mapping_count and ready_receipt_count:
|
||||
result = "AI_AUTOMATION_READY_FOR_CONTROLLED_APPLY"
|
||||
@@ -5272,6 +5371,18 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
f"hash match {receipt_replay_hash_match_count}",
|
||||
"從 artifact + DB readback 自動證明 apply 已收斂",
|
||||
),
|
||||
_automation_lane(
|
||||
"controlled_apply_drift_verifier",
|
||||
"落地漂移偵測",
|
||||
"blocked" if controlled_apply_drift_count else ("completed" if controlled_apply_drift_verified_count else "waiting"),
|
||||
controlled_apply_drift_count,
|
||||
(
|
||||
f"verified {controlled_apply_drift_selector_count}/{controlled_apply_drift_selector_count}"
|
||||
if controlled_apply_drift_verified_count
|
||||
else f"drift {controlled_apply_drift_count}"
|
||||
),
|
||||
"持續比對 receipt 與正式 DB,偵測後進 rollback / re-apply",
|
||||
),
|
||||
]
|
||||
|
||||
return {
|
||||
@@ -5314,6 +5425,9 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
"controlled_apply_receipt_materialized_count": receipt_replay_materialized_count,
|
||||
"controlled_apply_receipt_hash_match_count": receipt_replay_hash_match_count,
|
||||
"controlled_apply_closeout_verified_count": 1 if controlled_apply_closeout_verified else 0,
|
||||
"controlled_apply_drift_count": controlled_apply_drift_count,
|
||||
"controlled_apply_drift_verified_count": controlled_apply_drift_verified_count,
|
||||
"controlled_apply_drift_selector_count": controlled_apply_drift_selector_count,
|
||||
"exception_count": exception_count,
|
||||
"ai_exception_count": exception_count,
|
||||
AI_EXCEPTION_REQUIRED_COUNT_KEY: exception_count,
|
||||
@@ -5330,6 +5444,7 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
"exception_resolution": "ai_machine_verifiable",
|
||||
"machine_verifiable_decision_required": True,
|
||||
"controlled_apply_closeout": "receipt_replay_machine_verified" if controlled_apply_closeout_verified else "waiting_for_verifier",
|
||||
"controlled_apply_drift": "drift_detected" if controlled_apply_drift_count else ("drift_verified" if controlled_apply_drift_verified_count else "waiting_for_drift_verifier"),
|
||||
},
|
||||
"ai_exception_auto_resolution": ai_exception_auto_resolution,
|
||||
"manual_policy": {
|
||||
@@ -5353,6 +5468,7 @@ def build_pchome_growth_ai_automation_readiness(
|
||||
"llm_calls_in_preview": False,
|
||||
"gemini_allowed": False,
|
||||
"reads_database_for_receipt_replay": bool(controlled_apply_receipt_replay),
|
||||
"reads_database_for_drift_verifier": bool(controlled_apply_drift_verifier),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user