Skip to content

Commit 932599e

Browse files
authored
fix: close retained pr264 incident gap (#559)
* fix: close retained pr264 incident gap * fix: correct pr264 follow-up scope fallback
1 parent 1bcabe0 commit 932599e

3 files changed

Lines changed: 126 additions & 0 deletions

File tree

scripts/reviewer_bot_core/reviewer_response_policy.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ def _initial_cycle_boundary(review_data: dict) -> tuple[str | None, str | None]:
6363
return None, None
6464

6565

66+
def _alternate_current_head_cycle_boundary(review_data: dict, issue_snapshot: dict | None) -> str | None:
67+
if not isinstance(issue_snapshot, dict) or not isinstance(issue_snapshot.get("pull_request"), dict):
68+
return None
69+
if review_data.get("assignment_method") != "claim":
70+
return None
71+
for field in ("active_cycle_started_at", "cycle_started_at"):
72+
value = review_data.get(field)
73+
if isinstance(value, str) and value:
74+
return None
75+
created_at = issue_snapshot.get("created_at")
76+
return created_at if isinstance(created_at, str) and created_at else None
77+
78+
6679
def _scope_basis_and_anchor(review_data: dict, contributor_handoff: dict | None) -> tuple[str | None, str | None]:
6780
if isinstance(contributor_handoff, dict):
6881
semantic_key = str(contributor_handoff.get("semantic_key", ""))
@@ -97,8 +110,11 @@ def _current_scope_fields(
97110
contributor_handoff: dict | None,
98111
*,
99112
alternate_current_head_approval: bool = False,
113+
alternate_current_head_cycle_boundary: str | None = None,
100114
) -> dict[str, object]:
101115
_, cycle_boundary = _initial_cycle_boundary(review_data)
116+
if alternate_current_head_approval and alternate_current_head_cycle_boundary:
117+
cycle_boundary = alternate_current_head_cycle_boundary
102118
if alternate_current_head_approval:
103119
anchor_timestamp = None
104120
basis = "alternate_current_head_approval"
@@ -202,6 +218,7 @@ def derive_reviewer_response_state(
202218
approval_result: dict[str, object] | None = None,
203219
current_head_approval_authors: tuple[str, ...] | None = None,
204220
stored_reviewer_review: dict | None | object = _UNSET,
221+
alternate_current_head_cycle_boundary: str | None = None,
205222
) -> dict[str, object]:
206223
current_reviewer = review_data.get("current_reviewer")
207224
if not isinstance(current_reviewer, str) or not current_reviewer.strip():
@@ -379,6 +396,7 @@ def derive_reviewer_response_state(
379396
current_head,
380397
contributor_handoff,
381398
alternate_current_head_approval=True,
399+
alternate_current_head_cycle_boundary=alternate_current_head_cycle_boundary,
382400
),
383401
anchor_timestamp=latest_reviewer_response.get("timestamp") if isinstance(latest_reviewer_response, dict) else None,
384402
current_head_sha=current_head,
@@ -573,6 +591,7 @@ def compute_reviewer_response_state(
573591
reviews,
574592
parse_timestamp=bot.parse_iso8601_timestamp,
575593
)
594+
alternate_current_head_cycle_boundary = _alternate_current_head_cycle_boundary(review_data, issue_snapshot)
576595

577596
return derive_reviewer_response_state(
578597
review_data,
@@ -585,4 +604,5 @@ def compute_reviewer_response_state(
585604
approval_result=approval_result,
586605
current_head_approval_authors=approval_authors,
587606
stored_reviewer_review=stored_reviewer_review,
607+
alternate_current_head_cycle_boundary=alternate_current_head_cycle_boundary,
588608
)

tests/integration/reviewer_bot/test_app_preview_overdue.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,77 @@ def test_execute_run_preview_check_overdue_uses_frozen_pr264_operational_project
9797
}
9898

9999

100+
def test_execute_run_preview_check_overdue_backfills_claim_cycle_from_pr_creation(monkeypatch, capsys):
101+
harness = AppHarness(monkeypatch)
102+
harness.set_event(
103+
EVENT_NAME="workflow_dispatch",
104+
EVENT_ACTION="",
105+
MANUAL_ACTION="preview-check-overdue",
106+
ISSUE_NUMBER=264,
107+
VALIDATION_NONCE="nonce-pr264-claim",
108+
GITHUB_SHA="workflow-head",
109+
)
110+
111+
state = make_state()
112+
review = make_tracked_review_state(
113+
state,
114+
264,
115+
reviewer="iglesias",
116+
assigned_at="2026-02-26T04:58:03.401345+00:00",
117+
)
118+
review["assignment_method"] = "claim"
119+
accept_reviewer_review(
120+
review,
121+
semantic_key="pull_request_review:77",
122+
timestamp="2026-03-18T01:09:05Z",
123+
actor="iglesias",
124+
reviewed_head_sha="head-old",
125+
)
126+
127+
routes = RouteGitHubApi().add_request(
128+
"GET",
129+
"issues/264",
130+
status_code=200,
131+
payload={
132+
"number": 264,
133+
"state": "open",
134+
"pull_request": {},
135+
"labels": [],
136+
"created_at": "2026-02-10T17:20:07Z",
137+
},
138+
).add_request(
139+
"GET",
140+
"pulls/264",
141+
status_code=200,
142+
payload={
143+
**pull_request_payload(264, head_sha="head-live", author="manhatsu"),
144+
"requested_reviewers": [],
145+
"labels": [],
146+
},
147+
).add_pull_request_reviews(
148+
264,
149+
[review_payload(501, state="APPROVED", submitted_at="2026-03-18T12:10:42Z", commit_id="head-live", author="plaindocs")],
150+
)
151+
harness.runtime.github.stub(routes)
152+
harness.stub_load_state(lambda *, fail_on_unavailable=False: state)
153+
harness.stub_lock(acquire=lambda: (_ for _ in ()).throw(AssertionError("preview should not acquire lock")))
154+
harness.stub_pass_until(lambda current: (_ for _ in ()).throw(AssertionError("preview should skip pass-until processing")))
155+
harness.stub_sync_members(lambda current: (_ for _ in ()).throw(AssertionError("preview should skip member sync")))
156+
harness.stub_save_state(lambda current: (_ for _ in ()).throw(AssertionError("preview should not save state")))
157+
harness.stub_sync_status_labels(lambda current, issue_numbers: (_ for _ in ()).throw(AssertionError("preview should not sync labels")))
158+
harness.runtime.github.get_user_permission_status = lambda username, required_permission="push": "granted"
159+
160+
result = harness.run_execute()
161+
162+
assert result.exit_code == 0
163+
payload = json.loads(capsys.readouterr().out)
164+
assert payload["current_scope_key"] == "reviewer=iglesias|head=head-live|cycle=2026-02-10T17:20:07Z|anchor=none"
165+
assert payload["current_scope_basis"] == "alternate_current_head_approval"
166+
assert payload["suppression_reason"] == "current_head_alternate_approval_present"
167+
assert payload["would_post_warning"] is False
168+
assert payload["would_post_transition"] is False
169+
170+
100171
def test_preview_check_overdue_matches_real_overdue_scope_and_posting_decision(monkeypatch, capsys):
101172
harness = AppHarness(monkeypatch)
102173
harness.set_event(

tests/unit/reviewer_bot/test_reviews_live_fetch.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,41 @@ def test_compute_reviewer_response_state_reports_awaiting_write_approval_after_c
241241
assert response_state["reason"] == "current_head_alternate_approval_present"
242242

243243

244+
def test_compute_reviewer_response_state_uses_issue_created_at_for_claim_alternate_approval_scope(monkeypatch):
245+
state = make_state()
246+
review = make_tracked_review_state(
247+
state,
248+
42,
249+
reviewer="iglesias",
250+
assigned_at="2026-02-26T04:58:03.401345+00:00",
251+
)
252+
review["assignment_method"] = "claim"
253+
accept_reviewer_review(
254+
review,
255+
semantic_key="pull_request_review:99",
256+
timestamp="2026-03-18T01:09:05Z",
257+
actor="iglesias",
258+
reviewed_head_sha="head-old",
259+
source_precedence=1,
260+
)
261+
routes = RouteGitHubApi().add_pull_request_snapshot(42, pull_request_payload(42, head_sha="head-live")).add_pull_request_reviews(
262+
42,
263+
[review_payload(10, state="APPROVED", submitted_at="2026-03-18T12:10:42Z", commit_id="head-live", author="plaindocs")],
264+
)
265+
runtime = _runtime(monkeypatch, routes)
266+
runtime.github.get_issue_or_pr_snapshot = lambda issue_number: {
267+
**issue_snapshot(issue_number, state="open", is_pull_request=True),
268+
"created_at": "2026-02-10T17:20:07Z",
269+
}
270+
271+
response_state = reviews.compute_reviewer_response_state(runtime, 42, review)
272+
273+
assert response_state["state"] == "awaiting_contributor_response"
274+
assert response_state["reason"] == "current_head_alternate_approval_present"
275+
assert response_state["current_scope_basis"] == "alternate_current_head_approval"
276+
assert response_state["current_scope_key"] == "reviewer=iglesias|head=head-live|cycle=2026-02-10T17:20:07Z|anchor=none"
277+
278+
244279
def test_compute_reviewer_response_state_blocks_public_current_head_approval_contradiction_before_refresh(monkeypatch):
245280
state = make_state()
246281
review = make_tracked_review_state(state, 42, reviewer="alice", active_cycle_started_at="2026-03-17T09:00:00Z")

0 commit comments

Comments
 (0)