@@ -71,32 +71,46 @@ def ensure_observer_discovery_watermark(review_data: dict, surface: str) -> dict
7171 return current
7272
7373
74- def list_deferred_gap_keys (review_data : dict ) -> list [str ]:
75- return list (_deferred_gaps (review_data ))
74+ def _observer_now_iso (bot ) -> str :
75+ return _now_iso (bot )
76+
77+
78+ def record_observer_watermark_event (bot , review_data : dict , surface : str , event_time : str , event_id : str ) -> None :
79+ current = ensure_observer_discovery_watermark (review_data , surface )
80+ current .update (
81+ {
82+ "last_scan_started_at" : current .get ("last_scan_started_at" ) or _observer_now_iso (bot ),
83+ "last_scan_completed_at" : _observer_now_iso (bot ),
84+ "last_safe_event_time" : event_time ,
85+ "last_safe_event_id" : event_id ,
86+ "lookback_seconds" : bot .DEFERRED_DISCOVERY_OVERLAP_SECONDS if hasattr (bot , "DEFERRED_DISCOVERY_OVERLAP_SECONDS" ) else 3600 ,
87+ "bootstrap_window_seconds" : bot .DEFERRED_DISCOVERY_BOOTSTRAP_WINDOW_SECONDS if hasattr (bot , "DEFERRED_DISCOVERY_BOOTSTRAP_WINDOW_SECONDS" ) else 604800 ,
88+ "bootstrap_completed_at" : current .get ("bootstrap_completed_at" ) or _observer_now_iso (bot ),
89+ }
90+ )
7691
7792
78- def _deferred_gap_keys (review_data : dict ) -> list [str ]:
79- return list_deferred_gap_keys (review_data )
93+ def record_observer_watermark_empty_scan (bot , review_data : dict , surface : str ) -> None :
94+ watermark = ensure_observer_discovery_watermark (review_data , surface )
95+ watermark ["last_scan_started_at" ] = watermark .get ("last_scan_started_at" ) or _observer_now_iso (bot )
96+ watermark ["last_scan_completed_at" ] = _observer_now_iso (bot )
97+ watermark ["bootstrap_completed_at" ] = watermark .get ("bootstrap_completed_at" ) or _observer_now_iso (bot )
98+
99+
100+ def list_deferred_gap_keys (review_data : dict ) -> list [str ]:
101+ return list (_deferred_gaps (review_data ))
80102
81103
82104def get_deferred_gap (review_data : dict , source_event_key : str ) -> dict :
83105 gap = _deferred_gaps (review_data ).get (source_event_key )
84106 return gap if isinstance (gap , dict ) else {}
85107
86108
87- def _get_deferred_gap (review_data : dict , source_event_key : str ) -> dict :
88- return get_deferred_gap (review_data , source_event_key )
89-
90-
91109def get_deferred_gap_reason (review_data : dict , source_event_key : str ) -> str | None :
92110 reason = get_deferred_gap (review_data , source_event_key ).get ("reason" )
93111 return reason if isinstance (reason , str ) else None
94112
95113
96- def _deferred_gap_reason (review_data : dict , source_event_key : str ) -> str | None :
97- return get_deferred_gap_reason (review_data , source_event_key )
98-
99-
100114def _now_iso (bot ) -> str :
101115 return bot .clock .now ().isoformat ()
102116
@@ -130,7 +144,9 @@ def clear_deferred_gap(review_data: dict, source_event_key: str) -> bool:
130144 return False
131145
132146
133- def _clear_source_event_key (review_data : dict , source_event_key : str ) -> bool :
147+ def clear_automation_comment_gap (review_data : dict , source_event_key : str ) -> bool :
148+ if not source_event_key .startswith ("issue_comment:" ):
149+ return False
134150 return clear_deferred_gap (review_data , source_event_key )
135151
136152
@@ -144,10 +160,6 @@ def update_deferred_gap_fields(review_data: dict, source_event_key: str, fields:
144160 return previous != existing
145161
146162
147- def _update_deferred_gap_fields (review_data : dict , source_event_key : str , fields : dict ) -> bool :
148- return update_deferred_gap_fields (review_data , source_event_key , fields )
149-
150-
151163def mark_reconciled_source_event (
152164 review_data : dict ,
153165 source_event_key : str ,
@@ -170,35 +182,27 @@ def mark_reconciled_source_event(
170182 return True
171183
172184
173- def _mark_reconciled_source_event (
174- review_data : dict ,
175- source_event_key : str ,
176- * ,
177- reconciled_at : str | None = None ,
178- ) -> bool :
179- return mark_reconciled_source_event (
180- review_data ,
181- source_event_key ,
182- reconciled_at = reconciled_at ,
183- )
184-
185-
186185def was_reconciled_source_event (review_data : dict , source_event_key : str ) -> bool :
187186 return _is_valid_reconciled_source_event (
188187 _reconciled_source_events (review_data ).get (source_event_key ),
189188 source_event_key ,
190189 )
191190
192191
193- def _was_reconciled_source_event (review_data : dict , source_event_key : str ) -> bool :
194- return was_reconciled_source_event (review_data , source_event_key )
195-
196-
197192def _payload_or_existing (payload : dict , existing : dict , key : str ):
198193 value = payload .get (key )
199194 return existing .get (key ) if value is None else value
200195
201196
197+ def _source_event_created_at (payload : dict , existing : dict ):
198+ return (
199+ payload .get ("source_created_at" )
200+ or payload .get ("source_submitted_at" )
201+ or payload .get ("source_dismissed_at" )
202+ or existing .get ("source_event_created_at" )
203+ )
204+
205+
202206def record_deferred_gap_diagnostic (
203207 bot ,
204208 review_data : dict ,
@@ -216,43 +220,26 @@ def record_deferred_gap_diagnostic(
216220 if not isinstance (existing , dict ):
217221 existing = {}
218222 previous = deepcopy (existing )
219- existing .update (
220- {
221- "source_event_key" : source_event_key ,
222- "source_event_kind" : f"{ payload .get ('source_event_name' )} :{ payload .get ('source_event_action' )} " ,
223- "pr_number" : payload .get ("pr_number" ),
224- "reason" : reason ,
225- "source_event_created_at" : payload .get ("source_created_at" ) or payload .get ("source_submitted_at" ),
226- "source_run_id" : _payload_or_existing (payload , existing , "source_run_id" ),
227- "source_run_attempt" : _payload_or_existing (payload , existing , "source_run_attempt" ),
228- "source_workflow_file" : _payload_or_existing (payload , existing , "source_workflow_file" ),
229- "source_artifact_name" : _payload_or_existing (payload , existing , "source_artifact_name" ),
230- "first_noted_at" : existing .get ("first_noted_at" ) or _now_iso (bot ),
231- "last_checked_at" : _now_iso (bot ),
232- "operator_action_required" : True ,
233- "diagnostic_summary" : diagnostic_summary ,
234- "failure_kind" : failure_kind ,
235- }
236- )
223+ fields = {
224+ "source_event_key" : source_event_key ,
225+ "source_event_kind" : f"{ payload .get ('source_event_name' )} :{ payload .get ('source_event_action' )} " ,
226+ "pr_number" : payload .get ("pr_number" ),
227+ "reason" : reason ,
228+ "source_event_created_at" : _source_event_created_at (payload , existing ),
229+ "source_run_id" : _payload_or_existing (payload , existing , "source_run_id" ),
230+ "source_run_attempt" : _payload_or_existing (payload , existing , "source_run_attempt" ),
231+ "source_workflow_file" : _payload_or_existing (payload , existing , "source_workflow_file" ),
232+ "source_artifact_name" : _payload_or_existing (payload , existing , "source_artifact_name" ),
233+ "first_noted_at" : existing .get ("first_noted_at" ) or _now_iso (bot ),
234+ "last_checked_at" : _now_iso (bot ),
235+ "operator_action_required" : True ,
236+ "diagnostic_summary" : diagnostic_summary ,
237+ "failure_kind" : failure_kind ,
238+ }
239+ source_dismissed_at = _payload_or_existing (payload , existing , "source_dismissed_at" )
240+ if source_dismissed_at is not None :
241+ fields ["source_dismissed_at" ] = source_dismissed_at
242+ existing .update (fields )
237243 changed = previous != existing
238244 deferred_gaps [source_event_key ] = existing
239245 return changed
240-
241-
242- def _update_deferred_gap (
243- bot ,
244- review_data : dict ,
245- payload : dict ,
246- reason : str ,
247- diagnostic_summary : str ,
248- * ,
249- failure_kind : str | None = None ,
250- ) -> bool :
251- return record_deferred_gap_diagnostic (
252- bot ,
253- review_data ,
254- payload ,
255- reason ,
256- diagnostic_summary ,
257- failure_kind = failure_kind ,
258- )
0 commit comments