Skip to content

Add re-review trigger >20LOC change appears after approval#4053

Draft
Copilot wants to merge 10 commits intomainfrom
copilot/add-reapproval-gate-workflow
Draft

Add re-review trigger >20LOC change appears after approval#4053
Copilot wants to merge 10 commits intomainfrom
copilot/add-reapproval-gate-workflow

Conversation

Copy link
Contributor

Copilot AI commented Mar 11, 2026

This is to block stale review-approval where MR has still approve from reviewer but later significant changes were introduced. Arbitrary threshold is +-20LOC change.

Co-authored-by: atobiszei <36039266+atobiszei@users.noreply.github.com>
Copilot AI changed the title [WIP] Add automated re-approval gate for PRs Add automated reapproval gate for PRs with significant post-approval changes Mar 11, 2026
@atobiszei atobiszei requested a review from Copilot March 11, 2026 09:57
…/ready_for_review)

Co-authored-by: atobiszei <36039266+atobiszei@users.noreply.github.com>
Copilot AI changed the title Add automated reapproval gate for PRs with significant post-approval changes Narrow reapproval-gate trigger: run only on new commits and review submissions Mar 11, 2026
@@ -0,0 +1,310 @@
#!/usr/bin/env python3
# Copyright 2024 Intel Corporation
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check for years

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a GitHub Actions “reapproval gate” to prevent PRs from merging after accumulating significant post-approval churn without re-review, with supporting scripts, tests, and documentation.

Changes:

  • Introduces a reapproval-gate required-check workflow that evaluates churn after the latest approval and can re-request review.
  • Adds a stdlib-only Python implementation plus offline unit tests for core decision logic.
  • Documents the gate behavior and updates contribution guidance.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
.github/workflows/reapproval-gate.yml Defines the required-check workflow and permissions, runs tests + gate script on PR/review events.
.github/scripts/reapproval_gate.py Implements approval detection, post-approval churn calculation, merge-from-main skipping, and re-requesting reviewers.
.github/scripts/test_reapproval_gate.py Adds offline unit tests for merge-from-main detection and latest-approval selection logic.
docs/reapproval-gate.md Documents decision flow, configuration, merge-from-main detection, and setup steps.
CONTRIBUTING.md Adds contributor-facing summary of the new gate and standard contribution guidance.

You can also share your feedback on Copilot code review. Take the survey.


steps:
- name: Checkout repository
uses: actions/checkout@v4
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow checks out and executes .github/scripts/reapproval_gate.py from the PR’s code while granting pull-requests: write. For same-repo PRs, a contributor could modify that script in the PR and have it run with a write-scoped GITHUB_TOKEN (workflow remains from base, but checkout pulls untrusted code). Consider checking out the base ref (e.g., the target branch) for running the gate logic, or switching to pull_request_target with an explicit checkout of the base SHA so only trusted code is executed when write permissions are used.

Suggested change
uses: actions/checkout@v4
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.base.sha }}

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +66
with urllib.request.urlopen(req) as response:
return json.loads(response.read())
Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

urllib.request.urlopen(req) is called without a timeout. A transient network stall could hang the workflow job until Actions kills it, making the required check flaky. Set an explicit timeout (and consider a small retry/backoff for 5xx/rate-limit responses) to keep the gate reliable.

Copilot uses AI. Check for mistakes.
print(f" {sha[:8]}: +{additions}/-{deletions} = {churn} LOC", flush=True)
total_churn += churn
evaluated += 1

Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

calculate_churn_after_approval() does one GitHub API call per commit after approval (get_commit_details inside the loop). On PRs with many post-approval commits this can be slow and may hit API rate limits. Consider short-circuiting once total_churn exceeds the threshold and/or using a less chatty API approach (e.g., compare endpoints where possible) to reduce request volume.

Suggested change
if total_churn > LOC_THRESHOLD:
print(
f" Total churn {total_churn} exceeds threshold {LOC_THRESHOLD}; "
"stopping further evaluation.",
flush=True,
)
break

Copilot uses AI. Check for mistakes.
Comment on lines +114 to +123
if not reviewers:
return
url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}/requested_reviewers"
try:
make_github_request(token, "POST", url, {"reviewers": reviewers})
print(f"Re-requested review from: {', '.join(reviewers)}", flush=True)
except Exception as exc: # pylint: disable=broad-except
print(f"Warning: could not re-request reviews: {exc}", flush=True)


Copy link

Copilot AI Mar 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request_re_review() posts the full reviewers list in one call. GitHub’s API limits requested reviewers per request (and certain logins like the PR author/bots can cause 422), which can make the re-request fail entirely. Consider filtering/validating the reviewer list and chunking into multiple requests (handling 422 per chunk) so the best-effort behavior still re-requests as many reviewers as possible.

Suggested change
if not reviewers:
return
url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}/requested_reviewers"
try:
make_github_request(token, "POST", url, {"reviewers": reviewers})
print(f"Re-requested review from: {', '.join(reviewers)}", flush=True)
except Exception as exc: # pylint: disable=broad-except
print(f"Warning: could not re-request reviews: {exc}", flush=True)
# Normalize and de-duplicate reviewers while preserving order.
seen = set()
normalized_reviewers: List[str] = []
for reviewer in reviewers:
if not reviewer:
continue
if reviewer in seen:
continue
seen.add(reviewer)
normalized_reviewers.append(reviewer)
if not normalized_reviewers:
return
url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}/requested_reviewers"
max_reviewers_per_request = 50
successful: List[str] = []
for i in range(0, len(normalized_reviewers), max_reviewers_per_request):
chunk = normalized_reviewers[i : i + max_reviewers_per_request]
try:
make_github_request(token, "POST", url, {"reviewers": chunk})
successful.extend(chunk)
except urllib.error.HTTPError as http_exc:
# 422 often indicates one or more invalid reviewers in the chunk.
if http_exc.code == 422:
print(
"Warning: bulk re-request failed with 422; "
"retrying reviewers individually.",
flush=True,
)
for reviewer in chunk:
try:
make_github_request(
token,
"POST",
url,
{"reviewers": [reviewer]},
)
successful.append(reviewer)
except urllib.error.HTTPError as inner_http_exc:
print(
f"Warning: could not re-request review from "
f"{reviewer}: HTTP {inner_http_exc.code}",
flush=True,
)
except Exception as inner_exc: # pylint: disable=broad-except
print(
f"Warning: unexpected error while re-requesting "
f"review from {reviewer}: {inner_exc}",
flush=True,
)
else:
print(
f"Warning: HTTP {http_exc.code} while re-requesting "
f"reviews for chunk {chunk}",
flush=True,
)
except Exception as exc: # pylint: disable=broad-except
print(
f"Warning: unexpected error while re-requesting reviews for "
f"chunk {chunk}: {exc}",
flush=True,
)
if successful:
print(
f"Re-requested review from: {', '.join(successful)}",
flush=True,
)

Copilot uses AI. Check for mistakes.
Copilot AI and others added 2 commits March 11, 2026 10:08
Co-authored-by: atobiszei <36039266+atobiszei@users.noreply.github.com>
Co-authored-by: atobiszei <36039266+atobiszei@users.noreply.github.com>
Copilot AI changed the title Narrow reapproval-gate trigger: run only on new commits and review submissions Reapproval gate: per-reviewer staleness, synchronize-only trigger, rename churn→changes Mar 11, 2026
Copilot stopped work on behalf of atobiszei due to an error March 11, 2026 10:24
Co-authored-by: atobiszei <36039266+atobiszei@users.noreply.github.com>
Copilot AI changed the title Reapproval gate: per-reviewer staleness, synchronize-only trigger, rename churn→changes Reapproval gate: address PR review comments Mar 12, 2026
Co-authored-by: atobiszei <36039266+atobiszei@users.noreply.github.com>
Copilot AI changed the title Reapproval gate: address PR review comments Reapproval gate: address review comments + move docs into .github/ Mar 12, 2026
Co-authored-by: atobiszei <36039266+atobiszei@users.noreply.github.com>
Copilot AI changed the title Reapproval gate: address review comments + move docs into .github/ Add reapproval gate workflow with docs in .github/ Mar 12, 2026
Comment on lines +249 to +262
# ---------------------------------------------------------------------------
# LOC_THRESHOLD constant
# ---------------------------------------------------------------------------

class TestLOCThreshold(unittest.TestCase):
"""Sanity-check the published threshold value."""

def test_threshold_is_ten(self):
self.assertEqual(LOC_THRESHOLD, 10)

def test_threshold_is_int(self):
self.assertIsInstance(LOC_THRESHOLD, int)


Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove.

Co-authored-by: atobiszei <36039266+atobiszei@users.noreply.github.com>
Copilot AI changed the title Add reapproval gate workflow with docs in .github/ Reapproval gate: raise LOC threshold to 20, remove brittle threshold test, fix spell CI Mar 12, 2026
…LD=20

Co-authored-by: atobiszei <36039266+atobiszei@users.noreply.github.com>
Copilot AI changed the title Reapproval gate: raise LOC threshold to 20, remove brittle threshold test, fix spell CI Reapproval gate: raise LOC threshold to 20, add offline threshold tests Mar 12, 2026
@atobiszei atobiszei changed the title Reapproval gate: raise LOC threshold to 20, add offline threshold tests Add re-review trigger >20LOC change appears after approval Mar 13, 2026
@atobiszei atobiszei requested review from dkalinowski and mzegla March 13, 2026 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants