From 3631ab122260590eb6bbbfca53b388d88204dbbb Mon Sep 17 00:00:00 2001 From: Your Name Date: Wed, 22 Jul 2026 20:40:42 +0800 Subject: [PATCH] fix(ai-loop): require Wazuh provider delivery --- .../ai_agent_autonomous_runtime_control.py | 53 ++----------------- ...est_ai_agent_autonomous_runtime_control.py | 28 +++++----- 2 files changed, 17 insertions(+), 64 deletions(-) diff --git a/apps/api/src/services/ai_agent_autonomous_runtime_control.py b/apps/api/src/services/ai_agent_autonomous_runtime_control.py index 2d02b3edc..f37407eae 100644 --- a/apps/api/src/services/ai_agent_autonomous_runtime_control.py +++ b/apps/api/src/services/ai_agent_autonomous_runtime_control.py @@ -4000,7 +4000,6 @@ def _durable_controlled_apply_telegram_receipt( return False policy_version = str(row.get("notification_policy_version") or "") provider_delivery = str(row.get("notification_provider_delivery") or "") - suppression_reason = str(row.get("notification_suppression_reason") or "") generic_shadow = bool( policy_version and str(row.get("notification_disposition") or "") == "suppressed" @@ -4009,55 +4008,9 @@ def _durable_controlled_apply_telegram_receipt( ) if not wazuh_posture: return generic_shadow - lifecycle_state = str(row.get("notification_lifecycle_state") or "") - if retry_terminal_receipt: - if not generic_shadow or lifecycle_state != "failed": - return False - execution_kind = str(row.get("notification_execution_kind") or "") - return bool( - ( - execution_kind == "no_write_posture_readback" - and policy_version - == "telegram_posture_lifecycle_transition_v1" - and provider_delivery == "state_transition" - and suppression_reason == "duplicate_lifecycle_state" - ) - or ( - execution_kind == "no_write_posture_readback" - and policy_version == "telegram_posture_state_digest_v1" - and provider_delivery == "shadow_only" - and suppression_reason == "historical_projection_backfill" - ) - or ( - execution_kind == "no_write_replay" - and policy_version == "telegram_failure_digest_v2" - and ( - ( - provider_delivery == "digest" - and suppression_reason - == "duplicate_failure_fingerprint_window" - ) - or ( - provider_delivery == "shadow_only" - and suppression_reason == "historical_projection_backfill" - ) - ) - ) - ) - if not generic_shadow or lifecycle_state != "recovered": - return False - return bool( - ( - policy_version == "telegram_posture_lifecycle_transition_v1" - and provider_delivery == "state_transition" - and suppression_reason == "duplicate_lifecycle_state" - ) - or ( - policy_version == "telegram_posture_state_digest_v1" - and provider_delivery == "shadow_only" - and suppression_reason == "historical_projection_backfill" - ) - ) + # Wazuh suppression is durable no-spam evidence only. It cannot replace + # the recipient-visible provider receipt required to close the incident. + return False def _latest_flow_closure( diff --git a/apps/api/tests/test_ai_agent_autonomous_runtime_control.py b/apps/api/tests/test_ai_agent_autonomous_runtime_control.py index 4b87d9f56..4483252c3 100644 --- a/apps/api/tests/test_ai_agent_autonomous_runtime_control.py +++ b/apps/api/tests/test_ai_agent_autonomous_runtime_control.py @@ -3597,7 +3597,7 @@ def test_runtime_telegram_receipt_queries_include_alert_notifications() -> None: ("catalog_id", "shadow_completes"), [ ("ansible:generic-posture-readback", True), - ("ansible:wazuh-manager-posture-readback", True), + ("ansible:wazuh-manager-posture-readback", False), ], ) def test_same_run_suppressed_shadow_respects_provider_receipt_policy( @@ -3706,7 +3706,7 @@ def test_same_run_suppressed_shadow_respects_provider_receipt_policy( "state_transition", "recovered", "duplicate_lifecycle_state", - True, + False, ), ( "shadow", @@ -3714,7 +3714,7 @@ def test_same_run_suppressed_shadow_respects_provider_receipt_policy( "shadow_only", "recovered", "historical_projection_backfill", - True, + False, ), ( "shadow", @@ -3776,11 +3776,11 @@ def test_wazuh_apply_receipt_requires_exact_recovered_lifecycle_provenance( ) is expected -def test_wazuh_duplicate_lifecycle_shadow_closes_strict_same_run_eighteen_stage_loop() -> ( +def test_wazuh_provider_receipt_closes_strict_same_run_eighteen_stage_loop() -> ( None ): automation_run_id = "wazuh-candidate-op" - incident_id = "INC-WAZUH-SHADOW-STRICT-CLOSURE" + incident_id = "INC-WAZUH-PROVIDER-STRICT-CLOSURE" apply_op_id = "wazuh-apply-op" catalog_id = "ansible:wazuh-manager-posture-readback" non_execution_stages = set(AI_AUTOMATION_REQUIRED_LOOP_STAGES) - set( @@ -3891,20 +3891,20 @@ def test_wazuh_duplicate_lifecycle_shadow_closes_strict_same_run_eighteen_stage_ ], telegram_latest_rows=[ { - "message_id": "wazuh-telegram-shadow", + "message_id": "wazuh-telegram-provider-sent", "automation_run_id": automation_run_id, "incident_id": incident_id, "action": "controlled_apply_result", - "send_status": "shadow", - "provider_message_id": None, + "send_status": "sent", + "provider_message_id": "wazuh-provider-message", "notification_policy_version": ( "telegram_posture_lifecycle_transition_v1" ), - "notification_disposition": "suppressed", + "notification_disposition": "sent", "notification_provider_delivery": "state_transition", "notification_lifecycle_state": "recovered", - "notification_suppression_reason": "duplicate_lifecycle_state", - "notification_provider_send_suppressed": True, + "notification_suppression_reason": None, + "notification_provider_send_suppressed": None, } ], ) @@ -4442,7 +4442,7 @@ def test_retry_terminal_accepts_policy_complete_suppressed_receipt() -> None: "state_transition", "failed", "duplicate_lifecycle_state", - True, + False, ), ( "no_write_replay", @@ -4460,7 +4460,7 @@ def test_retry_terminal_accepts_policy_complete_suppressed_receipt() -> None: "digest", "failed", "duplicate_failure_fingerprint_window", - True, + False, ), ( "no_write_replay", @@ -4469,7 +4469,7 @@ def test_retry_terminal_accepts_policy_complete_suppressed_receipt() -> None: "shadow_only", "failed", "historical_projection_backfill", - True, + False, ), ( "no_write_posture_readback",