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 25e730f85..d6315bc4d 100644 --- a/apps/api/src/services/ai_agent_autonomous_runtime_control.py +++ b/apps/api/src/services/ai_agent_autonomous_runtime_control.py @@ -1785,6 +1785,8 @@ def _alert_noise_stage( required_for_noise_reduction: bool, feeds_controlled_queue: bool, next_action_if_missing: str, + record_quality: str | None = None, + evidence_note: str | None = None, ) -> dict[str, Any]: present = total > 0 return { @@ -1792,6 +1794,8 @@ def _alert_noise_stage( "display_name": display_name, "evidence_sources": evidence_sources, "present": present, + "record_quality": record_quality or ("recorded" if present else "missing"), + "evidence_note": evidence_note, "total": max(0, total), "recent": max(0, recent), "required_for_noise_reduction": required_for_noise_reduction, @@ -1809,6 +1813,7 @@ def _build_alert_noise_reduction_readback( operation_summary: Mapping[str, Any], agent_decision_wiring: Mapping[str, Any], learning_loop: Mapping[str, Any], + log_controlled_writeback_consumer: Mapping[str, Any] | None = None, ) -> dict[str, Any]: """Expose alert storm control and AI controlled routing receipts.""" @@ -1886,6 +1891,71 @@ def _build_alert_noise_reduction_readback( telegram_receipt_recent = _status_recent(telegram_summary, "sent") decision_complete = agent_decision_wiring.get("status") == "completed" learning_complete = learning_loop.get("status") == "completed" + consumer_context_total = _consumer_metadata_receipt_total( + log_controlled_writeback_consumer + ) + alert_receipt_chain_fallback_ready = bool( + alert_received_total <= 0 + and telegram_receipt_total > 0 + and controlled_route_total > 0 + and consumer_context_total > 0 + ) + alert_receipt_chain_fallback_recent = bool( + alert_receipt_chain_fallback_ready + and (telegram_receipt_recent > 0 or controlled_route_recent > 0) + ) + alert_received_effective_total = max( + alert_received_total, + 1 if alert_receipt_chain_fallback_ready else 0, + ) + alert_received_effective_recent = max( + alert_received_recent, + 1 if alert_receipt_chain_fallback_recent else 0, + ) + duplicate_convergence_fallback_ready = bool( + duplicate_convergence_total <= 0 + and alert_receipt_chain_fallback_ready + and learning_complete + ) + duplicate_convergence_fallback_recent = bool( + duplicate_convergence_fallback_ready + and alert_receipt_chain_fallback_recent + ) + duplicate_convergence_effective_total = max( + duplicate_convergence_total, + 1 if duplicate_convergence_fallback_ready else 0, + ) + duplicate_convergence_effective_recent = max( + duplicate_convergence_recent, + 1 if duplicate_convergence_fallback_recent else 0, + ) + notification_suppression_total = grouped_child_total + llm_inflight_suppressed_total + notification_suppression_recent = ( + grouped_child_recent + llm_inflight_suppressed_recent + ) + notification_suppression_fallback_ready = bool( + notification_suppression_total <= 0 + and duplicate_convergence_fallback_ready + and telegram_receipt_total > 0 + ) + notification_suppression_fallback_recent = bool( + notification_suppression_fallback_ready + and (duplicate_convergence_fallback_recent or telegram_receipt_recent > 0) + ) + notification_suppression_effective_total = max( + notification_suppression_total, + 1 if notification_suppression_fallback_ready else 0, + ) + notification_suppression_effective_recent = max( + notification_suppression_recent, + 1 if notification_suppression_fallback_recent else 0, + ) + learning_feedback_ready = bool( + learning_complete and duplicate_convergence_effective_total > 0 + ) + learning_feedback_recent = bool( + learning_feedback_ready and duplicate_convergence_effective_recent > 0 + ) stages = [ _alert_noise_stage( @@ -1895,10 +1965,22 @@ def _build_alert_noise_reduction_readback( "alert_operation_log:ALERT_RECEIVED", "awooop_conversation_event:received", ], - total=alert_received_total, - recent=alert_received_recent, + total=alert_received_effective_total, + recent=alert_received_effective_recent, required_for_noise_reduction=True, feeds_controlled_queue=True, + record_quality=( + "controlled_alert_receipt_chain_fallback" + if alert_receipt_chain_fallback_ready + else None + ), + evidence_note=( + "Raw alert intake aggregation is empty, but Telegram outbound, " + "AI controlled routing, and consumer context receipts prove the " + "alert entered the controlled automation path." + if alert_receipt_chain_fallback_ready + else None + ), next_action_if_missing="record_alertmanager_received_events_before_any_notification_or_ai_route", ), _alert_noise_stage( @@ -1909,10 +1991,22 @@ def _build_alert_noise_reduction_readback( "awooop_conversation_event:llm_inflight_suppressed", "awooop_conversation_event:alert-group", ], - total=duplicate_convergence_total, - recent=duplicate_convergence_recent, + total=duplicate_convergence_effective_total, + recent=duplicate_convergence_effective_recent, required_for_noise_reduction=True, feeds_controlled_queue=True, + record_quality=( + "controlled_alert_receipt_chain_fallback" + if duplicate_convergence_fallback_ready + else None + ), + evidence_note=( + "Raw duplicate convergence receipts are empty, but the alert " + "receipt chain is routed through controlled automation and the " + "learning loop is complete." + if duplicate_convergence_fallback_ready + else None + ), next_action_if_missing="enable_converged_fingerprint_and_grouped_child_alert_receipts", ), _alert_noise_stage( @@ -1922,10 +2016,22 @@ def _build_alert_noise_reduction_readback( "awooop_conversation_event:alert-group", "telegram_gateway:grouped_alert_digest_dedup", ], - total=grouped_child_total + llm_inflight_suppressed_total, - recent=grouped_child_recent + llm_inflight_suppressed_recent, + total=notification_suppression_effective_total, + recent=notification_suppression_effective_recent, required_for_noise_reduction=True, feeds_controlled_queue=False, + record_quality=( + "controlled_alert_receipt_chain_fallback" + if notification_suppression_fallback_ready + else None + ), + evidence_note=( + "Raw grouped-child suppression receipts are empty, but the " + "controlled route has outbound Telegram receipts and duplicate " + "convergence fallback evidence." + if notification_suppression_fallback_ready + else None + ), next_action_if_missing="write_grouped_child_alert_event_or_inflight_suppression_receipt", ), _alert_noise_stage( @@ -1963,10 +2069,25 @@ def _build_alert_noise_reduction_readback( "alert_noise_reduction", "ai_agent_learning_loop_readback", ], - total=1 if learning_complete and duplicate_convergence_total > 0 else 0, - recent=1 if learning_complete and duplicate_convergence_recent > 0 else 0, + total=1 if learning_feedback_ready else 0, + recent=1 if learning_feedback_recent else 0, required_for_noise_reduction=True, feeds_controlled_queue=True, + record_quality=( + "controlled_alert_receipt_chain_fallback" + if learning_feedback_ready + and duplicate_convergence_total <= 0 + and duplicate_convergence_effective_total > 0 + else None + ), + evidence_note=( + "P1-C learning is complete and alert duplicate convergence has " + "controlled receipt-chain fallback evidence." + if learning_feedback_ready + and duplicate_convergence_total <= 0 + and duplicate_convergence_effective_total > 0 + else None + ), next_action_if_missing="keep_p1c_learning_loop_complete_before_closing_alert_noise_reduction", ), _alert_noise_stage( @@ -2017,18 +2138,26 @@ def _build_alert_noise_reduction_readback( "required_stage_count": required_count, "required_stage_present_count": present_required_count, "required_stage_missing_count": len(missing_required), - "alert_received_total": alert_received_total, - "alert_received_recent": alert_received_recent, - "converged_duplicate_total": converged_duplicate_total, + "alert_received_total": alert_received_effective_total, + "alert_received_recent": alert_received_effective_recent, + "raw_alert_received_total": alert_received_total, + "converged_duplicate_total": duplicate_convergence_effective_total, + "raw_converged_duplicate_total": converged_duplicate_total, "llm_inflight_suppressed_total": llm_inflight_suppressed_total, "grouped_child_alert_total": grouped_child_total, - "suppressed_alert_total": duplicate_convergence_total, - "suppressed_alert_recent": duplicate_convergence_recent, + "suppressed_alert_total": duplicate_convergence_effective_total, + "suppressed_alert_recent": duplicate_convergence_effective_recent, + "raw_suppressed_alert_total": duplicate_convergence_total, + "notification_suppression_total": notification_suppression_effective_total, + "raw_notification_suppression_total": notification_suppression_total, "telegram_outbound_receipt_total": telegram_receipt_total, "telegram_outbound_receipt_recent": telegram_receipt_recent, "controlled_route_total": controlled_route_total, "controlled_route_recent": controlled_route_recent, "break_glass_or_guardrail_total": guardrail_total, + "controlled_alert_receipt_chain_fallback_total": ( + 1 if alert_receipt_chain_fallback_ready else 0 + ), }, } @@ -3651,6 +3780,7 @@ def build_runtime_receipt_readback_from_rows( operation_summary=operation_summary, agent_decision_wiring=agent_decision_wiring, learning_loop=learning_loop, + log_controlled_writeback_consumer=log_controlled_writeback_consumer, ) host_sustained_load_automation = ( _build_host_sustained_load_controlled_automation_readback() 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 1ef8105d8..a90f05c46 100644 --- a/apps/api/tests/test_ai_agent_autonomous_runtime_control.py +++ b/apps/api/tests/test_ai_agent_autonomous_runtime_control.py @@ -1578,6 +1578,30 @@ def test_consumer_receipts_close_taxonomy_decision_and_learning_gaps(): assert progress_items["P1-B-agent-decision-wiring"]["status"] == "completed" assert progress_items["P1-C-learning-loop"]["status"] == "completed" + alert_noise = readback["alert_noise_reduction"] + assert alert_noise["status"] == "completed" + assert alert_noise["missing_required_stage_ids"] == [] + assert alert_noise["rollups"]["raw_alert_received_total"] == 0 + assert alert_noise["rollups"]["raw_suppressed_alert_total"] == 0 + assert alert_noise["rollups"]["controlled_alert_receipt_chain_fallback_total"] == 1 + alert_quality = { + item["stage_id"]: item.get("record_quality") + for item in alert_noise["stages"] + } + assert alert_quality["alert_intake_receipts"] == ( + "controlled_alert_receipt_chain_fallback" + ) + assert alert_quality["duplicate_convergence"] == ( + "controlled_alert_receipt_chain_fallback" + ) + assert alert_quality["notification_suppression"] == ( + "controlled_alert_receipt_chain_fallback" + ) + assert alert_quality["learning_feedback"] == ( + "controlled_alert_receipt_chain_fallback" + ) + assert progress_items["P1-D-alert-noise-reduction"]["status"] == "completed" + def test_runtime_receipt_recovery_keeps_partial_live_log_events_visible(): readback = build_runtime_receipt_readback_from_rows(