From 500ebf4f7ae1e8fd89011055e7cecaafd29c354f Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 1 Nov 2026 17:04:08 +0000 Subject: [PATCH 1/2] docs: add pr-triage agent skill --- .agents/skills/pr-triage/SKILL.md | 62 +++++++++++++++++++++++++++++++ AGENTS.md | 2 + 2 files changed, 64 insertions(+) create mode 100644 .agents/skills/pr-triage/SKILL.md diff --git a/.agents/skills/pr-triage/SKILL.md b/.agents/skills/pr-triage/SKILL.md new file mode 100644 index 00000000..785e07ff --- /dev/null +++ b/.agents/skills/pr-triage/SKILL.md @@ -0,0 +1,62 @@ +--- +name: pr-triage +description: >- + Assess open pull requests and review queue state in python-roborock, optionally syncing the local workspace with upstream/main. Use when asked what needs to be reviewed, to triage open PRs, or to sync and check the review backlog. +--- + +# PR Triage & Review Queue Skill + +Use this skill when assessing open pull requests, checking which PRs need initial review or re-review, or syncing the workspace before starting a review session. + +Once the user selects a specific PR to review, switch to the [Code Review Skill](../code-review/SKILL.md). + +--- + +## Workflow + +1. **Optional Workspace Sync**: + If the user asks to sync (e.g., "sync and check reviews") or the workspace is a clean review branch behind `upstream/main`: + - Verify clean working tree (`git status --short`). Do **not** fast-forward or rebase if uncommitted local changes exist unless asked. + - Fetch remotes (`git fetch --all --prune`). + - Fast-forward the local tracking branch to `upstream/main` (or `origin/main` if `upstream` is absent): `git merge --ff-only upstream/main`. + +2. **Query Open PR & Review Metadata**: + Identify the current GitHub user and fetch open PR state in a single `gh` pass so no per-PR polling is needed: + ```bash + REVIEWER=$(gh api user --jq .login) + gh pr list --repo Python-roborock/python-roborock --state open --limit 30 \ + --json number,title,author,isDraft,createdAt,updatedAt,additions,deletions,mergeable,reviewDecision,reviews,commits,comments \ + | jq --arg me "$REVIEWER" ' + sort_by(.number) | reverse | .[] | { + number, + title, + author: .author.login, + isDraft, + mergeable, + size: "+\(.additions)/-\(.deletions)", + reviewDecision, + lastCommit: (.commits | last | .committedDate), + myLastReview: ([.reviews[] | select(.author.login == $me)] | last | {state, submittedAt}), + otherLastReview: ([.reviews[] | select(.author.login != $me and .author.login != "copilot-pull-request-reviewer")] | last | {author: .author.login, state, submittedAt}), + lastComment: ([.comments[] | select(.author.login != "codecov")] | last | {author: .author.login, createdAt}) + }' + ``` + +3. **Classify PRs into Actionable Buckets**: + Group open PRs in priority order for the reviewer (`$REVIEWER`): + - **1. New PRs Awaiting Initial Review**: + Non-draft PRs not authored by `$REVIEWER` where `myLastReview.submittedAt == null`. Flag competing/overlapping PRs targeting the same module/issue and note if another maintainer already commented/approved. + - **2. Re-reviews Needed (Updated Since Last Review)**: + Non-draft PRs not authored by `$REVIEWER` where `myLastReview.submittedAt != null` AND (`lastCommit > myLastReview.submittedAt` OR a new author reply `lastComment.createdAt > myLastReview.submittedAt`). + - **3. Reviewer's Own PRs Ready / Updated**: + PRs authored by `$REVIEWER` that are `APPROVED` and ready to merge, or have new feedback/conflicts. + - **4. Waiting on Author / Drafts (Compact Summary)**: + - Non-draft PRs with `CHANGES_REQUESTED` or `COMMENTED` where no commits or author replies have arrived since `myLastReview.submittedAt`. + - Draft PRs (`isDraft == true`), summarized compactly on 1–2 lines unless a draft has notable recent maintainer discussion. + - Conflicting (`mergeable == "CONFLICTING"`) or stale backlog items. + +4. **Present Concise Triage Report**: + - Report sync status in one line if a sync was performed (commit hash + any new release tags). + - List actionable PRs (`#number`, title, `@author`, `+additions/-deletions`, and why it needs attention—e.g., *opened Oct 31* or *updated Oct 22 after your Oct 17 review*). + - Keep "Waiting on Author / Drafts" brief so actionable reviews stand out. + - Ask which PR the user wants to review first. diff --git a/AGENTS.md b/AGENTS.md index d3cea4d8..4e7c6daa 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -99,3 +99,5 @@ Portable agent skills following the [Agent Skills specification](https://agentsk - **Code Review Skill** (`.agents/skills/code-review/SKILL.md`): A structured, interactive review workflow for evaluating pull requests and diffs against the repository's architectural hierarchy, typing rules, and testing standards. To invoke during interactive sessions, refer to [.agents/skills/code-review/SKILL.md](.agents/skills/code-review/SKILL.md). +- **PR Triage Skill** (`.agents/skills/pr-triage/SKILL.md`): + A repeatable workflow for syncing the workspace and classifying open pull requests into actionable review queues (initial reviews, re-reviews after author updates, ready-to-merge PRs, and waiting/draft items). Refer to [.agents/skills/pr-triage/SKILL.md](.agents/skills/pr-triage/SKILL.md). From 0540411e8cab903e6369c78c29290426f49e4557 Mon Sep 17 00:00:00 2001 From: Allen Porter Date: Sun, 1 Nov 2026 17:24:17 +0000 Subject: [PATCH 2/2] docs: refine pr-triage SHA comparison, author replies, and approved bucket --- .agents/skills/pr-triage/SKILL.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/.agents/skills/pr-triage/SKILL.md b/.agents/skills/pr-triage/SKILL.md index 785e07ff..0d154d89 100644 --- a/.agents/skills/pr-triage/SKILL.md +++ b/.agents/skills/pr-triage/SKILL.md @@ -25,33 +25,41 @@ Once the user selects a specific PR to review, switch to the [Code Review Skill] ```bash REVIEWER=$(gh api user --jq .login) gh pr list --repo Python-roborock/python-roborock --state open --limit 30 \ - --json number,title,author,isDraft,createdAt,updatedAt,additions,deletions,mergeable,reviewDecision,reviews,commits,comments \ + --json number,title,author,isDraft,createdAt,updatedAt,additions,deletions,mergeable,reviewDecision,headRefOid,reviews,commits,comments \ | jq --arg me "$REVIEWER" ' - sort_by(.number) | reverse | .[] | { + sort_by(.number) | reverse | .[] | .author.login as $pr_author | { number, title, - author: .author.login, + author: $pr_author, isDraft, mergeable, size: "+\(.additions)/-\(.deletions)", reviewDecision, + headRefOid, lastCommit: (.commits | last | .committedDate), - myLastReview: ([.reviews[] | select(.author.login == $me)] | last | {state, submittedAt}), - otherLastReview: ([.reviews[] | select(.author.login != $me and .author.login != "copilot-pull-request-reviewer")] | last | {author: .author.login, state, submittedAt}), - lastComment: ([.comments[] | select(.author.login != "codecov")] | last | {author: .author.login, createdAt}) + myLastReview: ([.reviews[] | select(.author.login == $me)] | last | {state, submittedAt, commitOid: .commit.oid}), + otherLastReview: ([.reviews[] | select(.author.login != $me and .author.login != $pr_author and .author.login != "copilot-pull-request-reviewer")] | last | {author: .author.login, state, submittedAt}), + lastAuthorReplyAt: ( + [ + (.comments[] | select(.author.login == $pr_author) | .createdAt), + (.reviews[] | select(.author.login == $pr_author) | .submittedAt) + ] | max + ) }' ``` + *(Note: GitHub's GraphQL `reviews` array includes `COMMENTED` entries whenever an author replies in an inline review thread, so combining `.comments[]` and `.reviews[]` filtered to `$pr_author` captures both top-level PR comments and inline review-thread replies without extra API calls.)* 3. **Classify PRs into Actionable Buckets**: Group open PRs in priority order for the reviewer (`$REVIEWER`): - **1. New PRs Awaiting Initial Review**: Non-draft PRs not authored by `$REVIEWER` where `myLastReview.submittedAt == null`. Flag competing/overlapping PRs targeting the same module/issue and note if another maintainer already commented/approved. - **2. Re-reviews Needed (Updated Since Last Review)**: - Non-draft PRs not authored by `$REVIEWER` where `myLastReview.submittedAt != null` AND (`lastCommit > myLastReview.submittedAt` OR a new author reply `lastComment.createdAt > myLastReview.submittedAt`). - - **3. Reviewer's Own PRs Ready / Updated**: - PRs authored by `$REVIEWER` that are `APPROVED` and ready to merge, or have new feedback/conflicts. + Non-draft PRs not authored by `$REVIEWER` where `myLastReview.submittedAt != null` AND (`headRefOid != myLastReview.commitOid` OR `(lastAuthorReplyAt != null and lastAuthorReplyAt > myLastReview.submittedAt)`). Comparing `headRefOid != myLastReview.commitOid` catches amended, rebased, or cherry-picked commits whose `committedDate` precedes `myLastReview.submittedAt`. + - **3. Approved & Ready to Merge / Own PR Updates**: + - Non-draft PRs (authored by `$REVIEWER` or contributors) that are `APPROVED` (`reviewDecision == "APPROVED"`), `MERGEABLE`, and have no unreviewed commits since approval (`headRefOid == myLastReview.commitOid` or authored by `$REVIEWER`). + - Non-draft PRs authored by `$REVIEWER` with new review feedback or merge conflicts requiring action. - **4. Waiting on Author / Drafts (Compact Summary)**: - - Non-draft PRs with `CHANGES_REQUESTED` or `COMMENTED` where no commits or author replies have arrived since `myLastReview.submittedAt`. + - Non-draft PRs with `CHANGES_REQUESTED` or `COMMENTED` where `headRefOid == myLastReview.commitOid` and no new author replies (`lastAuthorReplyAt <= myLastReview.submittedAt`) have arrived. - Draft PRs (`isDraft == true`), summarized compactly on 1–2 lines unless a draft has notable recent maintainer discussion. - Conflicting (`mergeable == "CONFLICTING"`) or stale backlog items.