Skip to content
Merged
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
67 changes: 67 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,67 @@
#!/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 flatten_review_pages(payload: object) -> list[Mapping[str, object]]:
"""Accept one review page or the nested result from gh api --slurp."""
if not isinstance(payload, list):
raise ValueError("reviews JSON must be an array")
if all(isinstance(item, list) for item in payload):
payload = [review for page in payload for review in page]
if not all(isinstance(review, Mapping) for review in payload):
raise ValueError("reviews JSON must contain review objects")
return list(payload)


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
try:
reviews = flatten_review_pages(reviews)
except ValueError as exc:
print(f"error: {exc}", 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 @@ -37,9 +37,9 @@ def _signal(


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 @@ -51,13 +51,13 @@ 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",
Expand All @@ -71,7 +71,7 @@ def test_request_changes_maps_to_github_change_request(self):
"sha1",
"github-actions[bot]",
)
self.assertEqual(payload["event"], "REQUEST_CHANGES")
self.assertEqual(payload["event"], "COMMENT")
self.assertIn("canonical subagent terminology", payload["body"])

def test_rejects_blocking_verdict_without_actionable_findings(self):
Expand Down
81 changes: 81 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,81 @@
#!/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])

def test_flattens_paginated_review_results(self):
pages = [
[
{
"id": 1,
"state": "CHANGES_REQUESTED",
"commit_id": "old-sha",
"user": {"login": "github-actions[bot]"},
}
],
[
{
"id": 2,
"state": "CHANGES_REQUESTED",
"commit_id": "old-sha",
"user": {"login": "human"},
}
],
]

reviews = srr.flatten_review_pages(pages)

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

def test_rejects_non_review_payloads(self):
with self.assertRaises(ValueError):
srr.flatten_review_pages([{"id": 1}, "not a review"])


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 @@ -60,6 +60,20 @@ jobs:
Each string must name the changed file and line or quoted text, explain the
problem, and state the requested resolution. 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 --paginate --slurp "repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/reviews?per_page=100" > /tmp/reviews.json
python3 .agents/skills/doc_quality_policy/stale_review_requests.py \
--reviews /tmp/reviews.json \
--head-sha "${{ github.event.pull_request.head.sha }}" > /tmp/stale-review-ids.txt
while IFS= read -r review_id; do
[ -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 < /tmp/stale-review-ids.txt

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