@@ -556,6 +556,23 @@ def ts_text(ts: datetime | None) -> str:
556556 return ts .isoformat () if ts else ""
557557
558558
559+ def is_copilot_review (review : dict [str , Any ]) -> bool :
560+ login = actor_login (review .get ("user" ) or {}).lower ()
561+ if not login :
562+ return False
563+ return "copilot" in login and (login .endswith ("[bot]" ) or "pull-request-reviewer" in login )
564+
565+
566+ def latest_copilot_review_clean (reviews : list [dict [str , Any ]], review_comments : list [dict [str , Any ]]) -> tuple [bool , bool ]:
567+ candidates = [r for r in reviews if is_copilot_review (r ) and r .get ("submitted_at" )]
568+ if not candidates :
569+ return False , False
570+ latest = max (candidates , key = lambda r : r .get ("submitted_at" ) or "" )
571+ review_id = latest .get ("id" )
572+ has_comments = any (c .get ("pull_request_review_id" ) == review_id for c in review_comments )
573+ return True , not has_comments
574+
575+
559576def compute_facts (raw : dict [str , Any ], author : str , events : list [dict [str , Any ]]) -> dict [str , Any ]:
560577 pr = raw ["pr" ]
561578 checks = raw ["checks" ]
@@ -567,6 +584,9 @@ def compute_facts(raw: dict[str, Any], author: str, events: list[dict[str, Any]]
567584 approver_activity_ts = latest_substantive_activity (events , {"approver" })
568585 external_activity_ts = latest_substantive_activity (events , {"outsider" })
569586 api_author = actor_login (pr .get ("author" ) or {})
587+ copilot_reviewed , copilot_review_clean = latest_copilot_review_clean (
588+ raw ["reviews" ], raw ["review_comments" ]
589+ )
570590 return {
571591 "author" : author ,
572592 "is_otelbot_author" : api_author .lower () == "app/otelbot" ,
@@ -575,6 +595,8 @@ def compute_facts(raw: dict[str, Any], author: str, events: list[dict[str, Any]]
575595 "ci_failing_count" : len (failing ),
576596 "ci_pending_count" : len (pending ),
577597 "conflicts" : compute_conflicts (pr ),
598+ "copilot_reviewed" : copilot_reviewed ,
599+ "copilot_review_clean" : copilot_review_clean ,
578600 "created_at" : ts_text (created_ts ),
579601 "last_activity_at" : ts_text (last_activity_ts ),
580602 "last_author_activity_at" : ts_text (author_activity_ts ),
@@ -1044,6 +1066,12 @@ def approved_cell(facts: dict[str, Any]) -> str:
10441066 return "✅" if facts .get ("approved" ) else " "
10451067
10461068
1069+ def copilot_cell (facts : dict [str , Any ]) -> str :
1070+ if facts .get ("copilot_reviewed" ) and facts .get ("copilot_review_clean" ):
1071+ return "✅"
1072+ return " "
1073+
1074+
10471075def age_seconds (facts : dict [str , Any ]) -> int | None :
10481076 value = facts .get ("seconds_since_waiting" )
10491077 if isinstance (value , int ):
@@ -1124,8 +1152,8 @@ def row_sort_key(pr: dict[str, Any]) -> tuple[int, int]:
11241152 rows .sort (key = row_sort_key , reverse = True )
11251153 out .append (f"## { SIDE_LABELS .get (side , side )} " )
11261154 out .append ("" )
1127- out .append ("| PR | Author | CI | Conflicts | Age |" )
1128- out .append ("|---|---|:---:|:---:|:---:|" )
1155+ out .append ("| PR | Author | CI | Conflicts | Copilot | Age |" )
1156+ out .append ("|---|---|:---:|:---:|:---:|:---:| " )
11291157 for pr in rows :
11301158 number = pr ["number" ]
11311159 title = _md_escape (pr .get ("title" , "" ))
@@ -1139,7 +1167,7 @@ def row_sort_key(pr: dict[str, Any]) -> tuple[int, int]:
11391167 pr_cell += " ✅"
11401168 out .append (
11411169 f"| { pr_cell } | { author } | { ci_cell (facts )} | "
1142- f"{ conflicts_cell (facts )} | { activity_cell } |"
1170+ f"{ conflicts_cell (facts )} | { copilot_cell ( facts ) } | { activity_cell } |"
11431171 )
11441172 out .append ("" )
11451173
0 commit comments