@@ -195,6 +195,43 @@ def _record_for_current_reviewer(record: dict | None | object, current_reviewer:
195195 return record
196196
197197
198+ def _current_cycle_reviewer_handoff_record (
199+ review_data : dict ,
200+ current_reviewer : str ,
201+ current_head : str | None ,
202+ * ,
203+ issue_is_pull_request : bool ,
204+ ) -> dict | None :
205+ handoff = review_data .get ("current_cycle_reviewer_handoff" )
206+ if not isinstance (handoff , dict ):
207+ return None
208+ if handoff .get ("command_name" ) != "feedback" :
209+ return None
210+ actor = handoff .get ("actor" )
211+ timestamp = handoff .get ("timestamp" )
212+ source_event_key = handoff .get ("source_event_key" )
213+ if not isinstance (actor , str ) or actor .lower () != current_reviewer .lower ():
214+ return None
215+ if not isinstance (timestamp , str ) or not timestamp .strip ():
216+ return None
217+ if not isinstance (source_event_key , str ) or not source_event_key .strip ():
218+ return None
219+ reviewed_head_sha = handoff .get ("reviewed_head_sha" )
220+ if issue_is_pull_request :
221+ if not isinstance (reviewed_head_sha , str ) or reviewed_head_sha != current_head :
222+ return None
223+ elif reviewed_head_sha is not None :
224+ return None
225+ return {
226+ "semantic_key" : source_event_key ,
227+ "timestamp" : timestamp ,
228+ "actor" : actor ,
229+ "reviewed_head_sha" : reviewed_head_sha ,
230+ "source_precedence" : 1 ,
231+ "payload" : {"command_name" : "feedback" },
232+ }
233+
234+
198235def _contributor_revision_handoff_record (review_data : dict , current_head : str | None , reviewer_review : dict | None ) -> dict | None :
199236 contributor_revision = review_data .get ("contributor_revision" , {}).get ("accepted" )
200237 if not isinstance (contributor_revision , dict ):
@@ -283,6 +320,18 @@ def derive_reviewer_response_state(
283320 parse_timestamp = live_review_support .parse_github_timestamp ,
284321 ) > 0 :
285322 latest_reviewer_response = reviewer_review
323+ reviewer_handoff = _current_cycle_reviewer_handoff_record (
324+ review_data ,
325+ current_reviewer ,
326+ None ,
327+ issue_is_pull_request = False ,
328+ )
329+ if reviewer_review_helpers .compare_records (
330+ reviewer_handoff ,
331+ latest_reviewer_response ,
332+ parse_timestamp = live_review_support .parse_github_timestamp ,
333+ ) > 0 :
334+ latest_reviewer_response = reviewer_handoff
286335 completion = review_data .get ("current_cycle_completion" )
287336 if isinstance (completion , dict ) and completion .get ("completed" ):
288337 return _decorate_response (
@@ -292,6 +341,7 @@ def derive_reviewer_response_state(
292341 anchor_timestamp = latest_reviewer_response .get ("timestamp" ) if isinstance (latest_reviewer_response , dict ) else None ,
293342 reviewer_comment = reviewer_comment ,
294343 reviewer_review = reviewer_review ,
344+ current_cycle_reviewer_handoff = reviewer_handoff ,
295345 contributor_comment = contributor_comment ,
296346 contributor_handoff = contributor_comment ,
297347 )
@@ -303,6 +353,7 @@ def derive_reviewer_response_state(
303353 anchor_timestamp = latest_reviewer_response .get ("timestamp" ) if isinstance (latest_reviewer_response , dict ) else None ,
304354 reviewer_comment = reviewer_comment ,
305355 reviewer_review = reviewer_review ,
356+ current_cycle_reviewer_handoff = reviewer_handoff ,
306357 contributor_comment = contributor_comment ,
307358 contributor_handoff = contributor_comment ,
308359 )
@@ -314,6 +365,7 @@ def derive_reviewer_response_state(
314365 anchor_timestamp = _initial_reviewer_anchor (review_data ),
315366 reviewer_comment = reviewer_comment ,
316367 reviewer_review = reviewer_review ,
368+ current_cycle_reviewer_handoff = reviewer_handoff ,
317369 contributor_comment = contributor_comment ,
318370 contributor_handoff = contributor_comment ,
319371 )
@@ -329,6 +381,7 @@ def derive_reviewer_response_state(
329381 anchor_timestamp = contributor_comment .get ("timestamp" ) if isinstance (contributor_comment , dict ) else None ,
330382 reviewer_comment = reviewer_comment ,
331383 reviewer_review = reviewer_review ,
384+ current_cycle_reviewer_handoff = reviewer_handoff ,
332385 contributor_comment = contributor_comment ,
333386 contributor_handoff = contributor_comment ,
334387 )
@@ -339,6 +392,7 @@ def derive_reviewer_response_state(
339392 anchor_timestamp = latest_reviewer_response .get ("timestamp" ) if isinstance (latest_reviewer_response , dict ) else None ,
340393 reviewer_comment = reviewer_comment ,
341394 reviewer_review = reviewer_review ,
395+ current_cycle_reviewer_handoff = reviewer_handoff ,
342396 contributor_comment = contributor_comment ,
343397 contributor_handoff = contributor_comment ,
344398 )
@@ -350,7 +404,14 @@ def derive_reviewer_response_state(
350404 scope_fields = {"current_scope_key" : None , "current_scope_basis" : None },
351405 )
352406
353- if not reviewer_comment and not reviewer_review :
407+ reviewer_handoff = _current_cycle_reviewer_handoff_record (
408+ review_data ,
409+ current_reviewer ,
410+ current_head ,
411+ issue_is_pull_request = True ,
412+ )
413+
414+ if not reviewer_comment and not reviewer_review and not reviewer_handoff :
354415 if not had_reviewer_review :
355416 return _decorate_response (
356417 state = "awaiting_reviewer_response" ,
@@ -359,6 +420,7 @@ def derive_reviewer_response_state(
359420 anchor_timestamp = _initial_reviewer_anchor (review_data ),
360421 reviewer_comment = reviewer_comment ,
361422 reviewer_review = reviewer_review ,
423+ current_cycle_reviewer_handoff = reviewer_handoff ,
362424 contributor_comment = contributor_comment ,
363425 contributor_handoff = None ,
364426 )
@@ -370,6 +432,12 @@ def derive_reviewer_response_state(
370432 parse_timestamp = live_review_support .parse_github_timestamp ,
371433 ) > 0 :
372434 latest_reviewer_response = reviewer_review
435+ if reviewer_review_helpers .compare_records (
436+ reviewer_handoff ,
437+ latest_reviewer_response ,
438+ parse_timestamp = live_review_support .parse_github_timestamp ,
439+ ) > 0 :
440+ latest_reviewer_response = reviewer_handoff
373441
374442 contributor_handoff = contributor_comment
375443 contributor_revision = _contributor_revision_handoff_record (
@@ -402,6 +470,7 @@ def derive_reviewer_response_state(
402470 current_head_sha = current_head ,
403471 reviewer_comment = reviewer_comment ,
404472 reviewer_review = reviewer_review ,
473+ current_cycle_reviewer_handoff = reviewer_handoff ,
405474 contributor_comment = contributor_comment ,
406475 contributor_handoff = contributor_handoff ,
407476 )
@@ -419,6 +488,7 @@ def derive_reviewer_response_state(
419488 current_head_sha = current_head ,
420489 reviewer_comment = reviewer_comment ,
421490 reviewer_review = reviewer_review ,
491+ current_cycle_reviewer_handoff = reviewer_handoff ,
422492 contributor_comment = contributor_comment ,
423493 contributor_handoff = contributor_handoff ,
424494 )
@@ -438,21 +508,24 @@ def derive_reviewer_response_state(
438508 current_head_sha = current_head ,
439509 reviewer_comment = reviewer_comment ,
440510 reviewer_review = reviewer_review ,
511+ current_cycle_reviewer_handoff = reviewer_handoff ,
441512 contributor_comment = contributor_comment ,
442513 contributor_handoff = contributor_handoff ,
443514 )
444515
445516 latest_review_head = reviewer_review .get ("reviewed_head_sha" ) if isinstance (reviewer_review , dict ) else None
446517 if not isinstance (latest_review_head , str ) or latest_review_head != current_head :
447- if isinstance (reviewer_comment , dict ):
518+ if isinstance (reviewer_comment , dict ) or isinstance (reviewer_handoff , dict ):
519+ response_anchor = reviewer_comment if isinstance (reviewer_comment , dict ) else reviewer_handoff
448520 return _decorate_response (
449521 state = "awaiting_contributor_response" ,
450522 reason = "accepted_same_scope_reviewer_activity" ,
451523 scope_fields = _current_scope_fields (review_data , current_reviewer , current_head , contributor_handoff ),
452- anchor_timestamp = reviewer_comment .get ("timestamp" ) if isinstance (reviewer_comment .get ("timestamp" ), str ) else None ,
524+ anchor_timestamp = response_anchor .get ("timestamp" ) if isinstance (response_anchor .get ("timestamp" ), str ) else None ,
453525 current_head_sha = current_head ,
454526 reviewer_comment = reviewer_comment ,
455527 reviewer_review = reviewer_review ,
528+ current_cycle_reviewer_handoff = reviewer_handoff ,
456529 contributor_comment = contributor_comment ,
457530 contributor_handoff = contributor_handoff ,
458531 )
@@ -464,6 +537,7 @@ def derive_reviewer_response_state(
464537 current_head_sha = current_head ,
465538 reviewer_comment = reviewer_comment ,
466539 reviewer_review = reviewer_review ,
540+ current_cycle_reviewer_handoff = reviewer_handoff ,
467541 contributor_comment = contributor_comment ,
468542 contributor_handoff = contributor_handoff ,
469543 )
@@ -486,6 +560,7 @@ def derive_reviewer_response_state(
486560 current_head_sha = current_head ,
487561 reviewer_comment = reviewer_comment ,
488562 reviewer_review = reviewer_review ,
563+ current_cycle_reviewer_handoff = reviewer_handoff ,
489564 contributor_comment = contributor_comment ,
490565 contributor_handoff = contributor_handoff ,
491566 )
@@ -498,6 +573,7 @@ def derive_reviewer_response_state(
498573 current_head_sha = current_head ,
499574 reviewer_comment = reviewer_comment ,
500575 reviewer_review = reviewer_review ,
576+ current_cycle_reviewer_handoff = reviewer_handoff ,
501577 contributor_comment = contributor_comment ,
502578 contributor_handoff = contributor_handoff ,
503579 )
@@ -509,6 +585,7 @@ def derive_reviewer_response_state(
509585 current_head_sha = current_head ,
510586 reviewer_comment = reviewer_comment ,
511587 reviewer_review = reviewer_review ,
588+ current_cycle_reviewer_handoff = reviewer_handoff ,
512589 contributor_comment = contributor_comment ,
513590 contributor_handoff = contributor_handoff ,
514591 )
0 commit comments