From 794437548141cb53f9796773ecd2dac228e5fb75 Mon Sep 17 00:00:00 2001 From: rnetser Date: Wed, 10 Jun 2026 15:41:01 +0300 Subject: [PATCH 1/2] fix: do not unresolve threads verified by CodeRabbit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unresolve workflow only checked for PR-author replies. When CodeRabbit itself verified a fix (e.g., "The concern is fully addressed"), there was no PR-author reply, so the workflow incorrectly unresolved the thread. Now treats CodeRabbit verification replies (containing "addressed", "verified", "resolved", or checkmark) as substantive — these threads stay resolved. Assisted-by: Claude Signed-off-by: rnetser --- .../unresolve-coderabbit-threads.yml | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 .github/workflows/unresolve-coderabbit-threads.yml diff --git a/.github/workflows/unresolve-coderabbit-threads.yml b/.github/workflows/unresolve-coderabbit-threads.yml new file mode 100644 index 0000000000..01e38bbe17 --- /dev/null +++ b/.github/workflows/unresolve-coderabbit-threads.yml @@ -0,0 +1,142 @@ +# description: Unresolve CodeRabbit review threads that were resolved by the PR author +# without addressing them. Runs on every push to a PR. +# +# Logic: If a CodeRabbit review thread was resolved by someone other than +# coderabbitai[bot], and the resolver's last reply does NOT contain a substantive +# response (e.g., "fixed", "addressed", code snippet, or explanation), the thread +# is unresolved so CodeRabbit can re-evaluate it. + +name: Unresolve unaddressed CodeRabbit threads + +on: + pull_request_target: + types: [synchronize] + +concurrency: + group: unresolve-threads-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + # Required to unresolve review threads and add comments + pull-requests: write + contents: read + +jobs: + unresolve-threads: + if: "!endsWith(github.event.pull_request.user.login, '[bot]')" + runs-on: ubuntu-latest + timeout-minutes: 5 + + steps: + - name: Unresolve prematurely resolved CodeRabbit threads + env: + GH_TOKEN: ${{ secrets.BOT3_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + run: | + set -euo pipefail + + # Fetch resolved review threads with pagination + ALL_THREADS="[]" + CURSOR="" + while true; do + if [ -n "$CURSOR" ]; then + AFTER_ARG="-f after=$CURSOR" + else + AFTER_ARG="" + fi + PAGE=$(gh api graphql -f query=' + query($owner: String!, $repo: String!, $pr: Int!, $after: String) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $pr) { + reviewThreads(first: 100, after: $after) { + nodes { + id + isResolved + opening_comment: comments(first: 1) { + nodes { + author { + login + } + } + } + recent_comments: comments(last: 5) { + nodes { + author { + login + } + body + } + } + } + pageInfo { + hasNextPage + endCursor + } + } + } + } + } + ' -f owner="${REPO%%/*}" -f repo="${REPO##*/}" -F pr="$PR_NUMBER" $AFTER_ARG) + + # Append nodes to accumulated list + NODES=$(echo "$PAGE" | jq '.data.repository.pullRequest.reviewThreads.nodes') + ALL_THREADS=$(echo "$ALL_THREADS $NODES" | jq -s 'add') + + # Check for next page + HAS_NEXT=$(echo "$PAGE" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage') + if [ "$HAS_NEXT" != "true" ]; then + break + fi + CURSOR=$(echo "$PAGE" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor') + done + + # Process each resolved thread + echo "$ALL_THREADS" | jq -r --arg pr_author "$PR_AUTHOR" ' + .[] + | select(.isResolved == true) + | select(.opening_comment.nodes[0].author.login == "coderabbitai") + | select( + # No substantive reply from PR author OR CodeRabbit verification + (any(.recent_comments.nodes[]; + ( + # PR author posted a substantive reply (>= 15 chars) + (.author.login == $pr_author and ((.body // "") | length) >= 15) + or + # CodeRabbit verified the fix (contains confirmation markers) + (.author.login == "coderabbitai" and ((.body // "") | test("addressed|verified|resolved|✅|concern is fully"))) + ) + ) | not) + ) + | .id + ' | while read -r thread_id; do + if [ -n "$thread_id" ]; then + echo "Unresolving thread: $thread_id" + if gh api graphql -f query=" + mutation { + unresolveReviewThread(input: {threadId: \"$thread_id\"}) { + thread { + id + isResolved + } + } + } + "; then + # Add a comment to the thread explaining why it was unresolved + gh api graphql -f query=" + mutation { + addPullRequestReviewThreadReply(input: {pullRequestReviewThreadId: \"$thread_id\", body: \"\u26a0\ufe0f This thread was automatically unresolved because it was resolved without a substantive response. Please address the review comment and explain how it was resolved before resolving this thread again.\"}) { + comment { + id + } + } + } + " || echo " Warning: failed to add comment to $thread_id" + else + echo " Warning: failed to unresolve $thread_id" + fi + fi + done + + echo "Done checking CodeRabbit threads." From a2d28e6590e54390d520c1d47b2a37924f8a7598 Mon Sep 17 00:00:00 2001 From: rnetser Date: Wed, 10 Jun 2026 16:31:08 +0300 Subject: [PATCH 2/2] =?UTF-8?q?fix:=20address=20CodeRabbit=20review=20?= =?UTF-8?q?=E2=80=94=20bot=20suffix,=20case-insensitive=20match?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add [bot] suffix to coderabbitai login checks (GitHub bot accounts use "coderabbitai[bot]" not "coderabbitai") - Use case-insensitive regex for verification keywords (matches "Addressed", "Resolved", etc.) - Remove unused contents: read permission - Add job name for GitHub UI clarity Assisted-by: Claude Signed-off-by: rnetser --- .github/workflows/unresolve-coderabbit-threads.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/unresolve-coderabbit-threads.yml b/.github/workflows/unresolve-coderabbit-threads.yml index 01e38bbe17..6e82765273 100644 --- a/.github/workflows/unresolve-coderabbit-threads.yml +++ b/.github/workflows/unresolve-coderabbit-threads.yml @@ -19,10 +19,10 @@ concurrency: permissions: # Required to unresolve review threads and add comments pull-requests: write - contents: read jobs: unresolve-threads: + name: Unresolve prematurely resolved threads if: "!endsWith(github.event.pull_request.user.login, '[bot]')" runs-on: ubuntu-latest timeout-minutes: 5 @@ -96,7 +96,7 @@ jobs: echo "$ALL_THREADS" | jq -r --arg pr_author "$PR_AUTHOR" ' .[] | select(.isResolved == true) - | select(.opening_comment.nodes[0].author.login == "coderabbitai") + | select(.opening_comment.nodes[0].author.login == "coderabbitai[bot]") | select( # No substantive reply from PR author OR CodeRabbit verification (any(.recent_comments.nodes[]; @@ -105,7 +105,7 @@ jobs: (.author.login == $pr_author and ((.body // "") | length) >= 15) or # CodeRabbit verified the fix (contains confirmation markers) - (.author.login == "coderabbitai" and ((.body // "") | test("addressed|verified|resolved|✅|concern is fully"))) + (.author.login == "coderabbitai[bot]" and ((.body // "") | test("addressed|verified|resolved|✅|concern is fully"; "i"))) ) ) | not) )