@@ -78,6 +78,23 @@ def _render_notification_context_text(
7878 return fallback if rendered == key else str (rendered )
7979
8080
81+ def _should_suppress_noop_notification (signal_metadata : Mapping [str , object ] | None ) -> bool :
82+ metadata = signal_metadata if isinstance (signal_metadata , Mapping ) else {}
83+ no_op_reason = str (metadata .get ("no_op_reason" ) or "" ).strip ()
84+ if no_op_reason .startswith (("outside_execution_window" , "outside_monthly_execution_window" )):
85+ return True
86+ notification_context = metadata .get ("notification_context" )
87+ if not isinstance (notification_context , Mapping ):
88+ return False
89+ status_context = notification_context .get ("status" )
90+ if not isinstance (status_context , Mapping ):
91+ return False
92+ return str (status_context .get ("code" ) or "" ).strip () in {
93+ "status_monthly_snapshot_waiting_window" ,
94+ "status_no_execution_window_after_snapshot" ,
95+ }
96+
97+
8198def _translate_snapshot_guard_decision (decision : object , * , translator ) -> str :
8299 value = str (decision or "" ).strip ()
83100 if not value :
@@ -632,22 +649,36 @@ def run_strategy_core(
632649 + json .dumps ({"path" : str (record_path ), "status" : record .get ("execution_status" ), "no_op_reason" : record .get ("no_op_reason" )}, ensure_ascii = False ),
633650 flush = True ,
634651 )
635- notification_publisher .publish (
636- notification_renderers .render_heartbeat_notification (
637- dashboard = dashboard ,
638- strategy_dashboard = strategy_dashboard ,
639- no_op_text = no_op_text ,
640- signal_desc = signal_desc ,
641- status_desc = status_desc ,
642- status_icon = signal_metadata .get ("status_icon" , "🐤" ),
643- translator = config .translator ,
644- separator = config .separator ,
645- strategy_display_name = config .strategy_display_name ,
646- extra_notification_lines = config .extra_notification_lines ,
652+ notification_suppressed = _should_suppress_noop_notification (signal_metadata )
653+ if notification_suppressed :
654+ print (
655+ "notification_suppressed "
656+ + json .dumps (
657+ {
658+ "reason" : no_op_reason or fail_reason or decision ,
659+ "strategy_profile" : signal_metadata .get ("strategy_profile" ),
660+ },
661+ ensure_ascii = False ,
662+ ),
663+ flush = True ,
664+ )
665+ else :
666+ notification_publisher .publish (
667+ notification_renderers .render_heartbeat_notification (
668+ dashboard = dashboard ,
669+ strategy_dashboard = strategy_dashboard ,
670+ no_op_text = no_op_text ,
671+ signal_desc = signal_desc ,
672+ status_desc = status_desc ,
673+ status_icon = signal_metadata .get ("status_icon" , "🐤" ),
674+ translator = config .translator ,
675+ separator = config .separator ,
676+ strategy_display_name = config .strategy_display_name ,
677+ extra_notification_lines = config .extra_notification_lines ,
678+ )
647679 )
648- )
649680 return StrategyCycleResult (
650- result = "OK - heartbeat" ,
681+ result = "OK - no-op" if notification_suppressed else "OK - heartbeat" ,
651682 signal_metadata = dict (signal_metadata or {}),
652683 target_weights = None ,
653684 execution_summary = {},
0 commit comments