@@ -59,6 +59,7 @@ def notify(self, facts: Dict[str, Any]) -> None:
5959 pr_number = self ._get_pr_number ()
6060 if pr_number :
6161 self ._reconcile_pr_labels (pr_number , [])
62+ self ._replace_existing_sections_with_all_clear (pr_number )
6263 else :
6364 logger .warning ('GithubPRNotifier: unable to determine PR number for label reconciliation' )
6465 logger .info ('GithubPRNotifier: no notifications present; skipping comments' )
@@ -288,6 +289,25 @@ def _extract_section_markers(self, content: str) -> Optional[Dict[str, str]]:
288289
289290 return None
290291
292+ def _extract_all_section_types (self , comment_body : str ) -> List [str ]:
293+ """Extract all managed section markers from a comment body."""
294+ import re
295+
296+ pattern = r'<!-- ([a-zA-Z0-9\-_]+) start -->'
297+ return re .findall (pattern , comment_body or '' )
298+
299+ def _extract_section_title (self , section_content : str ) -> str :
300+ """Extract the display title from a wrapped PR comment section."""
301+ import re
302+
303+ for line in (section_content or '' ).splitlines ():
304+ stripped = line .strip ()
305+ if stripped .startswith ('## ' ):
306+ title = stripped [3 :].strip ()
307+ title = re .sub (r'<img[^>]+>\s*' , '' , title ).strip ()
308+ return title or 'Socket Security'
309+ return 'Socket Security'
310+
291311 def _find_comment_with_section (self , comments : List [Dict [str , Any ]], section_type : str ) -> Optional [Dict [str , Any ]]:
292312 """Find an existing comment that contains the given section type."""
293313 import re
@@ -313,6 +333,49 @@ def _update_section_in_comment(self, comment_body: str, section_type: str, new_s
313333
314334 return updated_body
315335
336+ def _build_all_clear_section (self , section_type : str , existing_section_content : str ) -> str :
337+ """Build an all-clear replacement for an existing managed section."""
338+ from socket_basics .core .notification import github_pr_helpers as helpers
339+
340+ title = self ._extract_section_title (existing_section_content )
341+ body = "✅ Socket Basics found no active findings in the latest run."
342+ return helpers .wrap_pr_comment_section (section_type , title , body , self .full_scan_url )
343+
344+ def _replace_existing_sections_with_all_clear (self , pr_number : int ) -> None :
345+ """Rewrite existing managed PR comment sections to an all-clear state."""
346+ existing_comments = self ._get_pr_comments (pr_number )
347+ for comment in existing_comments :
348+ original_body = comment .get ('body' , '' )
349+ if not original_body :
350+ continue
351+
352+ updated_body = original_body
353+ changed = False
354+ for section_type in self ._extract_all_section_types (original_body ):
355+ section_match = self ._extract_section_markers (updated_body )
356+ if not section_match or section_match .get ('type' ) != section_type :
357+ import re
358+ pattern = rf'<!-- { re .escape (section_type )} start -->.*?<!-- { re .escape (section_type )} end -->'
359+ match = re .search (pattern , updated_body , re .DOTALL )
360+ if not match :
361+ continue
362+ section_content = match .group (0 )
363+ else :
364+ section_content = section_match ['content' ]
365+
366+ all_clear_section = self ._build_all_clear_section (section_type , section_content )
367+ next_body = self ._update_section_in_comment (updated_body , section_type , all_clear_section )
368+ if next_body != updated_body :
369+ updated_body = next_body
370+ changed = True
371+
372+ if changed :
373+ success = self ._update_comment (pr_number , comment ['id' ], updated_body )
374+ if success :
375+ logger .info ('GithubPRNotifier: updated existing comment %s to all-clear state' , comment ['id' ])
376+ else :
377+ logger .error ('GithubPRNotifier: failed to update comment %s to all-clear state' , comment ['id' ])
378+
316379 def _truncate_comment_if_needed (self , comment_body : str , full_scan_url : Optional [str ] = None ) -> str :
317380 """Truncate comment if it exceeds GitHub's character limit.
318381
0 commit comments