Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .agents/skills/doc_quality_policy/publish_review_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,10 @@
_spec.loader.exec_module(vrs)

_REVIEW_EVENTS = {
"approve": "APPROVE",
"approve with nits": "APPROVE",
"approve_with_nits": "APPROVE",
"request changes": "REQUEST_CHANGES",
"approve": "COMMENT",
"approve with nits": "COMMENT",
"approve_with_nits": "COMMENT",
"request changes": "COMMENT",
}


Expand Down
54 changes: 54 additions & 0 deletions .agents/skills/doc_quality_policy/stale_review_requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python3
"""Print stale GitHub Actions change-request review IDs, one per line."""
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path
from typing import Iterable, Mapping, Optional


def stale_review_ids(
reviews: Iterable[Mapping[str, object]],
head_sha: str,
reviewer_login: str = "github-actions[bot]",
) -> list[int]:
"""Select only prior blocking reviews published by the automation account."""
stale_ids: list[int] = []
for review in reviews:
author = review.get("user")
login = author.get("login") if isinstance(author, Mapping) else None
if (
login == reviewer_login
and review.get("state") == "CHANGES_REQUESTED"
and review.get("commit_id") != head_sha
and isinstance(review.get("id"), int)
):
stale_ids.append(review["id"])
return stale_ids


def main(argv: Optional[list[str]] = None) -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--reviews", required=True, type=Path)
parser.add_argument("--head-sha", required=True)
parser.add_argument("--reviewer-login", default="github-actions[bot]")
args = parser.parse_args(argv)
try:
reviews = json.loads(args.reviews.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
if not isinstance(reviews, list):
print("error: reviews JSON must be an array", file=sys.stderr)
return 1
for review_id in stale_review_ids(
reviews, args.head_sha, args.reviewer_login
):
print(review_id)
return 0


if __name__ == "__main__":
sys.exit(main())
12 changes: 6 additions & 6 deletions .agents/skills/doc_quality_policy/test_publish_review_signal.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ def _signal(verdict: str = "Approve") -> str:


class TestBuildReviewPayload(unittest.TestCase):
def test_approve_maps_to_github_approval(self):
def test_approve_maps_to_non_blocking_github_comment(self):
payload = prs.build_review_payload(_signal(), "1", "sha1", "github-actions[bot]")
self.assertEqual(payload["event"], "APPROVE")
self.assertEqual(payload["event"], "COMMENT")
self.assertEqual(payload["commit_id"], "sha1")
self.assertIn("## Verdict\nApprove", payload["body"])
self.assertNotIn("## Review signal", payload["body"])
Expand All @@ -37,17 +37,17 @@ def test_approve_maps_to_github_approval(self):
self.assertEqual(problems, [])
self.assertEqual(published_signal["reviewer_login"], "github-actions[bot]")

def test_approve_with_nits_maps_to_github_approval(self):
def test_approve_with_nits_maps_to_non_blocking_github_comment(self):
payload = prs.build_review_payload(
_signal("Approve with nits"), "1", "sha1", "github-actions[bot]"
)
self.assertEqual(payload["event"], "APPROVE")
self.assertEqual(payload["event"], "COMMENT")

def test_request_changes_maps_to_github_change_request(self):
def test_request_changes_maps_to_non_blocking_github_comment(self):
payload = prs.build_review_payload(
_signal("Request changes"), "1", "sha1", "github-actions[bot]"
)
self.assertEqual(payload["event"], "REQUEST_CHANGES")
self.assertEqual(payload["event"], "COMMENT")

def test_rejects_signal_for_another_head(self):
with self.assertRaises(ValueError):
Expand Down
53 changes: 53 additions & 0 deletions .agents/skills/doc_quality_policy/test_stale_review_requests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env python3
"""Unit tests for stale_review_requests.py."""
from __future__ import annotations

import importlib.util
import sys
import unittest
from pathlib import Path


_HERE = Path(__file__).resolve().parent
_spec = importlib.util.spec_from_file_location(
"stale_review_requests", _HERE / "stale_review_requests.py"
)
srr = importlib.util.module_from_spec(_spec)
sys.modules[_spec.name] = srr
_spec.loader.exec_module(srr)


class TestStaleReviewIds(unittest.TestCase):
def test_selects_only_prior_automated_change_requests(self):
reviews = [
{
"id": 1,
"state": "CHANGES_REQUESTED",
"commit_id": "old-sha",
"user": {"login": "github-actions[bot]"},
},
{
"id": 2,
"state": "CHANGES_REQUESTED",
"commit_id": "current-sha",
"user": {"login": "github-actions[bot]"},
},
{
"id": 3,
"state": "APPROVED",
"commit_id": "old-sha",
"user": {"login": "github-actions[bot]"},
},
{
"id": 4,
"state": "CHANGES_REQUESTED",
"commit_id": "old-sha",
"user": {"login": "reviewer"},
},
]

self.assertEqual(srr.stale_review_ids(reviews, "current-sha"), [1])


if __name__ == "__main__":
unittest.main()
14 changes: 14 additions & 0 deletions .github/workflows/agent-docs-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ jobs:
4. Emit one [SIGNAL:pr-review] JSON record with this head SHA, verdict, severity
counts, and top categories. Set reviewer_login to `github-actions[bot]`, the
runner account that will publish the review.
- name: Dismiss stale automated change requests
env:
GH_TOKEN: ${{ github.token }}
run: |
gh api "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews?per_page=100" > /tmp/reviews.json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] This fetch only reads the first 100 reviews, so a long-lived agent PR can retain a stale CHANGES_REQUESTED review on a later page and still keep GitHub's review gate blocking merge after this step completes. Paginate and slurp all pages before passing them to stale_review_requests.py.

python3 .agents/skills/doc_quality_policy/stale_review_requests.py \
--reviews /tmp/reviews.json \
--head-sha "${{ github.event.pull_request.head.sha }}" |
while IFS= read -r review_id; do

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ [IMPORTANT] If stale_review_requests.py exits non-zero, this pipeline can still succeed under the default implicit bash shell because only the while loop's status is checked; that would skip dismissals without failing the workflow. Enable pipefail or write the selected IDs to a temp file before looping.

[ -z "$review_id" ] && continue
gh api --method PUT \
"repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews/${review_id}/dismissals" \
-f "message=Superseded by the current Agent docs review check."
done

- name: Publish the independent review
env:
Expand Down
Loading