@@ -258,6 +258,30 @@ def test_release_command_accepts_confirmed_pr_reviewer_when_live_reviewers_are_e
258258 assert review ["current_reviewer" ] is None
259259
260260
261+ def test_release_command_resolves_reviewer_authority_before_triage_fallback (monkeypatch ):
262+ harness = CommandHarness (monkeypatch )
263+ state = make_state ()
264+ review = review_state .ensure_review_entry (state , 42 , create = True )
265+ assert review is not None
266+ review ["current_reviewer" ] = "bob"
267+ harness .stub_assignees (["bob" ])
268+ calls = []
269+ original_resolver = commands .assignment_flow .resolve_reviewer_command_authority
270+
271+ def resolve_with_order (* args , ** kwargs ):
272+ calls .append ("resolver" )
273+ return original_resolver (* args , ** kwargs )
274+
275+ monkeypatch .setattr (commands .assignment_flow , "resolve_reviewer_command_authority" , resolve_with_order )
276+ harness .runtime .github .get_user_permission_status = lambda username , required_permission = "triage" : calls .append ("permission" ) or "granted"
277+
278+ response , success = harness .handle_release (state , 42 , "alice" , ["@bob" ])
279+
280+ assert success is True
281+ assert "@alice has released @bob" in response
282+ assert calls [:2 ] == ["resolver" , "permission" ]
283+
284+
261285def test_rectify_command_accepts_confirmed_pr_reviewer_when_live_reviewers_are_empty (monkeypatch ):
262286 harness = CommandHarness (monkeypatch )
263287 state = make_state ()
@@ -279,6 +303,31 @@ def test_rectify_command_accepts_confirmed_pr_reviewer_when_live_reviewers_are_e
279303 assert calls == [(harness .runtime , state , 42 )]
280304
281305
306+ def test_rectify_command_resolves_reviewer_authority_before_triage_fallback (monkeypatch ):
307+ harness = CommandHarness (monkeypatch )
308+ state = make_state ()
309+ harness .stub_assignees ([])
310+ calls = []
311+ original_resolver = reconcile .assignment_flow .resolve_reviewer_command_authority
312+
313+ def resolve_with_order (* args , ** kwargs ):
314+ calls .append ("resolver" )
315+ return original_resolver (* args , ** kwargs )
316+
317+ monkeypatch .setattr (reconcile .assignment_flow , "resolve_reviewer_command_authority" , resolve_with_order )
318+ harness .runtime .github .get_user_permission_status = lambda username , required_permission = "triage" : calls .append ("permission" ) or "granted"
319+ monkeypatch .setattr (
320+ reconcile ,
321+ "reconcile_active_review_entry" ,
322+ lambda bot , current_state , issue_number : calls .append ("reconcile" ) or ("rectified" , True , False ),
323+ )
324+
325+ message , success , changed = harness .handle_rectify (state , 42 , "maintainer" )
326+
327+ assert (message , success , changed ) == ("rectified" , True , False )
328+ assert calls == ["resolver" , "permission" , "reconcile" ]
329+
330+
282331def test_assign_from_queue_posts_guidance_only_once (monkeypatch ):
283332 harness = CommandHarness (monkeypatch )
284333 state = make_state ()
@@ -493,6 +542,10 @@ def test_claim_command_fails_closed_when_assignees_unavailable(monkeypatch):
493542def test_release_command_fails_closed_when_permission_unavailable (monkeypatch ):
494543 harness = CommandHarness (monkeypatch )
495544 state = make_state ()
545+ review = review_state .ensure_review_entry (state , 42 , create = True )
546+ assert review is not None
547+ review ["current_reviewer" ] = "bob"
548+ harness .stub_assignees (["bob" ])
496549 harness .runtime .github .get_user_permission_status = lambda username , required_permission = "triage" : "unavailable"
497550
498551 response , success = harness .handle_release (state , 42 , "alice" , ["@bob" ])
@@ -894,11 +947,138 @@ def test_feedback_command_does_not_overwrite_newer_handoff(monkeypatch):
894947 "reviewed_head_sha" : None ,
895948 }
896949 assert side_effects .comments == [
897- (42 , "ℹ️ Ignored stale `/feedback` handoff because a newer reviewer handoff is already recorded." )
950+ (42 , "ℹ️ Ignored stale `/feedback` handoff because a newer or same-time reviewer handoff is already recorded." )
898951 ]
899952 assert side_effects .reactions == [(100 , "eyes" ), (100 , "+1" )]
900953
901954
955+ def test_feedback_command_does_not_overwrite_same_timestamp_different_event (monkeypatch ):
956+ harness = CommandHarness (monkeypatch )
957+ state = make_state ()
958+ review = review_state .ensure_review_entry (state , 42 , create = True )
959+ assert review is not None
960+ review ["current_reviewer" ] = "alice"
961+ review ["current_cycle_reviewer_handoff" ] = {
962+ "source_event_key" : "issue_comment:100" ,
963+ "timestamp" : "2026-03-17T10:00:00Z" ,
964+ "actor" : "alice" ,
965+ "command_name" : "feedback" ,
966+ "reviewed_head_sha" : None ,
967+ }
968+ harness .stub_assignees (["alice" ])
969+ side_effects = harness .capture_comment_side_effects ()
970+ request = harness .typed_comment_request (
971+ issue_number = 42 ,
972+ actor = "alice" ,
973+ body = "@guidelines-bot /feedback" ,
974+ issue_author = "dana" ,
975+ is_pull_request = False ,
976+ comment_id = 101 ,
977+ created_at = "2026-03-17T10:00:00Z" ,
978+ )
979+
980+ changed = comment_application .apply_comment_command (
981+ harness .runtime ,
982+ state ,
983+ request ,
984+ {"command" : "feedback" , "args" : [], "command_count" : 1 },
985+ classify_issue_comment_actor = lambda current_request : "repo_user_principal" ,
986+ )
987+
988+ assert changed is False
989+ assert review ["current_cycle_reviewer_handoff" ]["source_event_key" ] == "issue_comment:100"
990+ assert side_effects .comments == [
991+ (42 , "ℹ️ Ignored stale `/feedback` handoff because a newer or same-time reviewer handoff is already recorded." )
992+ ]
993+
994+
995+ def test_feedback_command_replays_same_event_idempotently (monkeypatch ):
996+ harness = CommandHarness (monkeypatch )
997+ state = make_state ()
998+ review = review_state .ensure_review_entry (state , 42 , create = True )
999+ assert review is not None
1000+ review ["current_reviewer" ] = "alice"
1001+ review ["last_reviewer_activity" ] = "2026-03-17T10:00:00Z"
1002+ review ["current_cycle_reviewer_handoff" ] = {
1003+ "source_event_key" : "issue_comment:100" ,
1004+ "timestamp" : "2026-03-17T10:00:00Z" ,
1005+ "actor" : "alice" ,
1006+ "command_name" : "feedback" ,
1007+ "reviewed_head_sha" : None ,
1008+ }
1009+ harness .stub_assignees (["alice" ])
1010+ harness .capture_comment_side_effects ()
1011+ request = harness .typed_comment_request (
1012+ issue_number = 42 ,
1013+ actor = "alice" ,
1014+ body = "@guidelines-bot /feedback" ,
1015+ issue_author = "dana" ,
1016+ is_pull_request = False ,
1017+ comment_id = 100 ,
1018+ created_at = "2026-03-17T10:00:00Z" ,
1019+ )
1020+
1021+ changed = comment_application .apply_comment_command (
1022+ harness .runtime ,
1023+ state ,
1024+ request ,
1025+ {"command" : "feedback" , "args" : [], "command_count" : 1 },
1026+ classify_issue_comment_actor = lambda current_request : "repo_user_principal" ,
1027+ )
1028+
1029+ assert changed is False
1030+ assert review ["current_cycle_reviewer_handoff" ] == {
1031+ "source_event_key" : "issue_comment:100" ,
1032+ "timestamp" : "2026-03-17T10:00:00Z" ,
1033+ "actor" : "alice" ,
1034+ "command_name" : "feedback" ,
1035+ "reviewed_head_sha" : None ,
1036+ }
1037+
1038+
1039+ def test_feedback_command_overwrites_older_handoff (monkeypatch ):
1040+ harness = CommandHarness (monkeypatch )
1041+ state = make_state ()
1042+ review = review_state .ensure_review_entry (state , 42 , create = True )
1043+ assert review is not None
1044+ review ["current_reviewer" ] = "alice"
1045+ review ["current_cycle_reviewer_handoff" ] = {
1046+ "source_event_key" : "issue_comment:100" ,
1047+ "timestamp" : "2026-03-17T09:00:00Z" ,
1048+ "actor" : "alice" ,
1049+ "command_name" : "feedback" ,
1050+ "reviewed_head_sha" : None ,
1051+ }
1052+ harness .stub_assignees (["alice" ])
1053+ harness .capture_comment_side_effects ()
1054+ request = harness .typed_comment_request (
1055+ issue_number = 42 ,
1056+ actor = "alice" ,
1057+ body = "@guidelines-bot /feedback" ,
1058+ issue_author = "dana" ,
1059+ is_pull_request = False ,
1060+ comment_id = 101 ,
1061+ created_at = "2026-03-17T10:00:00Z" ,
1062+ )
1063+
1064+ changed = comment_application .apply_comment_command (
1065+ harness .runtime ,
1066+ state ,
1067+ request ,
1068+ {"command" : "feedback" , "args" : [], "command_count" : 1 },
1069+ classify_issue_comment_actor = lambda current_request : "repo_user_principal" ,
1070+ )
1071+
1072+ assert changed is True
1073+ assert review ["current_cycle_reviewer_handoff" ] == {
1074+ "source_event_key" : "issue_comment:101" ,
1075+ "timestamp" : "2026-03-17T10:00:00Z" ,
1076+ "actor" : "alice" ,
1077+ "command_name" : "feedback" ,
1078+ "reviewed_head_sha" : None ,
1079+ }
1080+
1081+
9021082def test_feedback_command_plus_text_does_not_record_reviewer_comment_channel (monkeypatch ):
9031083 harness = CommentRoutingHarness (monkeypatch )
9041084 state = make_state ()
0 commit comments