Skip to content

fix(address-reviews): Add duplicate prevention and response rules - #301

Merged
openshift-merge-bot[bot] merged 1 commit into
openshift-eng:mainfrom
bryan-cox:fix-review-agent-duplicates
Feb 3, 2026
Merged

openshift-merge-bot[bot] merged 1 commit into
openshift-eng:mainfrom
bryan-cox:fix-review-agent-duplicates

Conversation

@bryan-cox

@bryan-cox bryan-cox commented Jan 22, 2026

Copy link
Copy Markdown
Contributor

Summary

Changes

New Script: plugins/utils/scripts/check_replied.py

A Python script that checks if the bot has already replied to a comment before posting. Supports:

  • Review threads (GraphQL)
  • Issue comments (REST API)
  • Review comments/inline code comments (REST API)

Updated Skill: plugins/utils/commands/address-reviews.md

Added duplicate prevention guidelines:

  1. Call check_replied.py before posting any reply
  2. One response per feedback (inline OR general comment, not both)
  3. Code changes only when explicitly requested (imperative language)
  4. Questions get explanations, not code changes

Related

Test plan

  • Run address-reviews on a PR with existing bot replies
  • Verify script correctly identifies already-replied comments
  • Verify skill follows response rules

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added a CLI reply-check utility to detect whether the bot has already replied to a comment, review thread, or review comment; outputs structured JSON and exit codes for automation.
  • Documentation

    • Added "Duplicate Prevention" guidance for pre-reply checks and response rules (note: the section was accidentally duplicated).
  • Chores

    • Bumped plugin version and updated manifest/marketplace metadata.

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jan 22, 2026
@coderabbitai

coderabbitai Bot commented Jan 22, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Adds a new CLI script plugins/utils/scripts/check_replied.py to detect prior bot replies on PR comments/review threads, inserts a duplicated "Duplicate Prevention" section into plugins/utils/commands/address-reviews.md, and bumps the utils plugin version to 0.0.4 across manifests and docs.

Changes

Cohort / File(s) Summary
Documentation
plugins/utils/commands/address-reviews.md
Inserted a "Duplicate Prevention" section describing pre-reply checks, check_replied usage, accepted types, exit codes, and response rules — the section was added twice (duplication).
New Check Script
plugins/utils/scripts/check_replied.py
Added a new CLI script that queries GitHub via gh (GraphQL + REST) to check review_thread, issue_comment, and review_comment for existing bot replies. Introduces BOT_SIGNATURES, REPLY_SIGNATURE, run_gh, is_bot_reply, three check handlers, JSON output, and exit codes (0 safe, 1 already replied, 2 error).
Version / Metadata
plugins/utils/.claude-plugin/plugin.json, .claude-plugin/marketplace.json, docs/data.json
Bumped the utils plugin version from 0.0.3 to 0.0.4 in plugin manifests and docs.

Sequence Diagram

sequenceDiagram
    participant User as User / CI
    participant Script as check_replied.py
    participant GH as GitHub CLI (gh)
    participant Output as JSON Result

    User->>Script: Invoke CLI (--type, owner, repo, pr_number, id)
    Script->>Script: Parse args & select handler
    alt review_thread
        Script->>GH: gh api (GraphQL) -> fetch review threads (paginated)
        GH-->>Script: Threads JSON
        Script->>Script: Locate thread by id -> scan comments
    else issue_comment
        Script->>GH: gh api (REST) -> list issue comments
        GH-->>Script: Comments JSON
        Script->>Script: Find target comment -> check subsequent comments
    else review_comment
        Script->>GH: gh api (REST) -> list PR review comments
        GH-->>Script: Comments JSON
        Script->>Script: Find target comment -> inspect replies
    end
    Script->>Script: is_bot_reply() checks author signatures or reply signature
    Script->>Output: Emit JSON {safe_to_reply, reason, existing_reply?} and exit (0/1/2)
    Output-->>User: Receive JSON + exit code
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (2 errors)
Check name Status Explanation Resolution
No Assumed Git Remote Names ❌ Error File contains hardcoded 'origin' git remote references without discovering the actual remote name at runtime. Replace hardcoded 'origin' with dynamic discovery using git commands to determine the remote name, then reference it via variable.
Git Push Safety Rules ❌ Error Documentation contains git push --force-with-lease instruction without explicit user permission requirement, violating safety rules. Remove or replace force push with standard git push including explicit user confirmation requirement.
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix(address-reviews): Add duplicate prevention and response rules' directly aligns with the main changes: introducing duplicate prevention logic via check_replied.py and documenting response rules in address-reviews.md.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.
No Real People Names In Style References ✅ Passed Search results confirm no real people names are used as style references; only generic professional tone guidance and bot account names found.
No Untrusted Mcp Servers ✅ Passed The pull request introduces a new Python script and documentation updates without introducing any MCP server installations or external dependencies.
Ai-Helpers Overlap Detection ✅ Passed PR introduces entirely new PR review automation functionality with no overlapping commands, skills, scripts, or agents in the repository.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@bryan-cox
bryan-cox force-pushed the fix-review-agent-duplicates branch from f146335 to 2c0af67 Compare January 22, 2026 13:23

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In `@plugins/utils/scripts/check_replied.py`:
- Around line 131-139: The exception handler in the block that calls run_gh (the
API call that fetches comments) currently returns "safe_to_reply": True on
RuntimeError; change it to return "safe_to_reply": False with reason "api_error"
(and keep the existing message=str(e)) to avoid the bot treating transient API
failures as safe to reply; locate the try/except around run_gh (the comments =
run_gh([...]) call, likely in check_replied) and update the returned dict
accordingly.
- Around line 190-192: The call that fetches review comments via run_gh
(assigning to comments) is missing pagination; update the run_gh invocation that
builds ["api", f"repos/{owner}/{repo}/pulls/{pr_number}/comments"] to include
the "--paginate" flag so all review comments are retrieved (e.g., add
"--paginate" to the argument list passed to run_gh), and keep using the comments
variable as before.
- Around line 189-198: The current except block for the run_gh call in
check_replied.py returns safe_to_reply: True on API failures; change it to
return safe_to_reply: False (and keep reason "api_error" and message=str(e)) so
temporary API outages don't allow replies. Locate the try/except around the
run_gh(["api", f"repos/{owner}/{repo}/pulls/{pr_number}/comments"]) call and
update the except RuntimeError as e return value to safe_to_reply: False to
match the safer handling used in check_issue_comment.
- Around line 277-280: The except block in check_replied.py currently catches a
bare Exception and sets result = {"error": str(e), "safe_to_reply": True,
"reason": "error_fallback"}, which is too broad and unsafe; change it to catch
only expected exceptions (e.g., ValueError, KeyError, json.JSONDecodeError or
specific custom exceptions used by the parsing logic) and handle unexpected
exceptions by re-raising or logging and exiting non-zero; when an expected
handled error occurs set "safe_to_reply": False (not True) and include the full
error details in the "error" field and "reason"; update the exception handler
around the code that produces result (the except block that assigns result and
calls print(json.dumps(result)) / sys.exit(2)) to implement these narrower
catches and fallback behavior.
- Around line 131-133: The script fetches issue and review comments via
run_gh([...]) without pagination, so change the two run_gh calls that request
"repos/{owner}/{repo}/issues/{pr_number}/comments" and
"repos/{owner}/{repo}/pulls/{pr_number}/comments" to include the "--paginate"
flag (e.g., add "--paginate" as an additional argument in both calls) so all
pages of comments are returned rather than only the first 30 items.
🧹 Nitpick comments (2)
plugins/utils/scripts/check_replied.py (2)

35-36: Use unpacking for more idiomatic list construction.

♻️ Suggested improvement
-    result = subprocess.run(
-        ["gh"] + args,
+    result = subprocess.run(
+        ["gh", *args],
         capture_output=True,
         text=True,
     )

269-272: Remove unreachable code.

The else branch on line 269 is unreachable because argparse enforces the choices parameter, ensuring args.type is always one of the three valid values.

♻️ Simplify by removing unreachable branch
         if args.type == "review_thread":
             result = check_review_thread(args.owner, args.repo, args.pr_number, args.comment_id)
         elif args.type == "issue_comment":
             result = check_issue_comment(args.owner, args.repo, args.pr_number, args.comment_id)
-        elif args.type == "review_comment":
+        else:  # args.type == "review_comment"
             result = check_review_comment(args.owner, args.repo, args.pr_number, args.comment_id)
-        else:
-            result = {"error": f"Unknown type: {args.type}"}
-            print(json.dumps(result, indent=2))
-            sys.exit(2)

Comment thread plugins/utils/scripts/check_replied.py
Comment thread plugins/utils/scripts/check_replied.py
Comment thread plugins/utils/scripts/check_replied.py
Comment thread plugins/utils/scripts/check_replied.py
Comment thread plugins/utils/scripts/check_replied.py Outdated
@bryan-cox
bryan-cox force-pushed the fix-review-agent-duplicates branch 2 times, most recently from eca929f to 3f94e24 Compare January 22, 2026 13:29

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@plugins/utils/scripts/check_replied.py`:
- Around line 45-54: The is_bot_reply function currently treats any account
whose login endswith("[bot]") as our bot and thus skips replies; remove that
heuristic and only detect bot replies by membership in BOT_SIGNATURES and by the
REPLY_SIGNATURE in the body. Edit is_bot_reply to drop the
login.endswith("[bot]") condition so it returns True only when login is in
BOT_SIGNATURES or when REPLY_SIGNATURE appears in body; reference the
is_bot_reply function and the BOT_SIGNATURES and REPLY_SIGNATURE constants when
making this change.
- Around line 60-79: Update the GraphQL query in check_replied.py to include
pageInfo { hasNextPage endCursor } for both reviewThreads and comments and
implement cursor-based pagination: paginate reviewThreads by repeatedly querying
reviewThreads(first:100, after:$threadsCursor) using the returned endCursor
until hasNextPage is false, and for any thread whose comments may exceed 100,
fetch comments either by including comments(first:100, after:$commentsCursor) in
the same loop or by re-querying that thread with comments pagination using the
comments endCursor until hasNextPage is false; ensure the query variables (e.g.,
$threadsCursor, $commentsCursor) are added and the code that inspects
reviewThreads.nodes and comments.nodes in the function that checks replies
aggregates results from all pages before deciding "safe to reply."

Comment thread plugins/utils/scripts/check_replied.py
Comment on lines +60 to +79
query = '''
query($owner: String!, $repo: String!, $number: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $number) {
reviewThreads(first: 100) {
nodes {
id
comments(first: 100) {
nodes {
id
author { login }
body
createdAt
}
}
}
}
}
}
}

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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -name "check_replied.py" -type f

Repository: openshift-eng/ai-helpers

Length of output: 108


🏁 Script executed:

fd check_replied.py

Repository: openshift-eng/ai-helpers

Length of output: 106


🏁 Script executed:

git ls-files "*check_replied*"

Repository: openshift-eng/ai-helpers

Length of output: 106


🏁 Script executed:

cat -n plugins/utils/scripts/check_replied.py

Repository: openshift-eng/ai-helpers

Length of output: 10937


🌐 Web query:

GitHub GraphQL API reviewThreads pagination pageInfo endCursor

💡 Result:

Use cursor-based pagination: request pageInfo (hasNextPage + endCursor) on the PullRequest.reviewThreads connection, then loop supplying pageInfo.endCursor as the after argument until hasNextPage is false.

Example (forward pagination):
query:
{
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: 123) {
reviewThreads(first: 50, after: null) {
nodes {
id
isResolved
comments(first: 10) { nodes { id body } }
}
pageInfo {
endCursor
hasNextPage
}
}
}
}
}

If pageInfo.hasNextPage is true, run the same query with after: "" (and optionally keep first: 50) and repeat until hasNextPage is false. The reviewThreads connection is PullRequestReviewThreadConnection and exposes pageInfo.endCursor/hasNextPage. [1][2]

Sources:
[1] GitHub Docs — Using pagination in the GraphQL API.
[2] GitHub GraphQL schema docs — PullRequest / PullRequestReviewThreadConnection.


🌐 Web query:

GitHub GraphQL API comments pagination nested within reviewThreads first 100 limit

💡 Result:

Short answer: each GraphQL connection (including reviewThreads and the comments connection inside each thread) is limited to first/last ≤ 100 and must be paginated independently — you can’t get “more than 100 comments” for a nested connection in one shot. Paginate the outer reviewThreads (first:100 → use pageInfo.endCursor → after), and for any thread whose comments exceed 100 paginate that thread’s comments connection (comments(first:100, after: ...)) using that thread’s comment cursors or by issuing follow-up queries per thread. The Octokit graphql paginator does not handle nested/parallel pagination for you, so you must drive nested pagination yourself (or request comment pages separately by thread id).

Key references:

  • GitHub GraphQL pagination docs (max first/last = 100 and cursor-based paging). [1]
  • Octokit plugin note: “This plugin only supports pagination of a single resource — nested pagination is not supported.” [2]
  • Community discussion pointing out UX/implementation complexities of reviewThreads → comments and need to paginate threads and comments separately. [3]

If you want, I can sketch a concrete pagination flow or example queries (server-side loop or async per-thread) for your use case.


Add pagination to GraphQL query to handle PRs with >100 review threads or comments.

The reviewThreads(first: 100) and nested comments(first: 100) queries only fetch the first page. On large PRs with many review threads or comments, bot replies beyond the first 100 will be missed, returning false "safe to reply" results.

Add pageInfo { hasNextPage endCursor } to both reviewThreads and comments connections, then implement a pagination loop to fetch all pages. Since nested pagination is not automatically handled, you'll need to paginate reviewThreads with cursor-based queries, and potentially re-query comments for threads that have more than 100 comments.

🤖 Prompt for AI Agents
In `@plugins/utils/scripts/check_replied.py` around lines 60 - 79, Update the
GraphQL query in check_replied.py to include pageInfo { hasNextPage endCursor }
for both reviewThreads and comments and implement cursor-based pagination:
paginate reviewThreads by repeatedly querying reviewThreads(first:100,
after:$threadsCursor) using the returned endCursor until hasNextPage is false,
and for any thread whose comments may exceed 100, fetch comments either by
including comments(first:100, after:$commentsCursor) in the same loop or by
re-querying that thread with comments pagination using the comments endCursor
until hasNextPage is false; ensure the query variables (e.g., $threadsCursor,
$commentsCursor) are added and the code that inspects reviewThreads.nodes and
comments.nodes in the function that checks replies aggregates results from all
pages before deciding "safe to reply."

- Add check_replied.py script to verify bot hasn't already replied
- Add response rules to prevent duplicate responses and unwanted code changes
- Only match our specific bot accounts, not all [bot] accounts
- Add GraphQL pagination for PRs with >100 review threads
- Use --paginate flag for REST API calls to handle >30 comments
- Return safe_to_reply=False on API errors (fail safe)
- Narrow exception handling to specific types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@bryan-cox
bryan-cox force-pushed the fix-review-agent-duplicates branch from 89d7110 to 8dbce90 Compare January 22, 2026 14:15
@coderabbitai

coderabbitai Bot commented Jan 22, 2026

Copy link
Copy Markdown
Contributor

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":401,"request":{"method":"PATCH","url":"https://api.github.com/repos/openshift-eng/ai-helpers/issues/comments/3784386992","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- walkthrough_start -->\n\n## Walkthrough\n\nThis PR adds a duplicated \"Duplicate Prevention\" section to the address-reviews documentation and introduces a new CLI script, `check_replied.py`, which queries GitHub (via the `gh` CLI) to determine whether a bot has already replied to an issue comment, review comment, or review thread.\n\n## Changes\n\n| Cohort / File(s) | Summary |\n|---|---|\n| **Documentation** <br> `plugins/utils/commands/address-reviews.md` | Added a \"Duplicate Prevention\" section (pre-reply checks, verification command, type parameter, and response rules); the section was inserted twice, causing duplicated guidance. |\n| **New Check Script** <br> `plugins/utils/scripts/check_replied.py` | New CLI script to detect prior bot replies. Adds bot signature constants, `gh`-based GraphQL/REST calls, handlers for `review_thread` / `issue_comment` / `review_comment`, structured JSON output, and exit codes (0 safe, 1 already replied, 2 error). |\n| **Version / Metadata** <br> `plugins/utils/.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `docs/data.json` | Plugin version bumped from `0.0.3` to `0.0.4` in plugin metadata and docs. |\n\n## Sequence Diagram\n\n```mermaid\nsequenceDiagram\n    participant User as User / CI\n    participant Script as check_replied.py\n    participant GH as GitHub API (gh CLI)\n    participant Output as JSON Result\n\n    User->>Script: Invoke CLI (--type, owner, repo, pr_number, id)\n    Script->>Script: Parse args & select handler\n    alt review_thread\n        Script->>GH: gh api / GraphQL -> fetch PR review threads\n        GH-->>Script: Threads JSON\n        Script->>Script: Locate thread by id -> scan comments\n    else issue_comment\n        Script->>GH: gh api -> list issue comments\n        GH-->>Script: Comments JSON\n        Script->>Script: Find comment -> check subsequent replies\n    else review_comment\n        Script->>GH: gh api -> list PR comments\n        GH-->>Script: Comments JSON\n        Script->>Script: Find comment -> scan replies\n    end\n    Script->>Script: is_bot_reply() by author login or reply signature\n    Script->>Output: Emit JSON { safe: bool, reason?: string }\n    Output-->>User: Exit code (0 safe, 1 already replied, 2 error)\n```\n\n## Estimated code review effort\n\n🎯 3 (Moderate) | ⏱️ ~20 minutes\n\n<!-- walkthrough_end -->\n\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n---\n\n<!-- pre_merge_checks_override_start -->\n> [!IMPORTANT]\n> ## Pre-merge checks failed\n> \n> Please resolve all errors before merging. Addressing warnings is optional.\n<!-- pre_merge_checks_override_end -->\n<details>\n<summary>❌ Failed checks (1 error)</summary>\n\n|          Check name         | Status  | Explanation                                                                                                                          | Resolution                                                                                                                  |\n| :-------------------------: | :------ | :----------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------- |\n| No Assumed Git Remote Names | ❌ Error | The address-reviews.md file contains hardcoded git remote name 'origin' in git commands without first discovering the actual remote. | Update documentation to discover actual remote name via 'git remote -v' before using it in git log and git rebase commands. |\n\n</details>\n<details>\n<summary>✅ Passed checks (7 passed)</summary>\n\n|                Check name                | Status   | Explanation                                                                                                                                                                                                                                            |\n| :--------------------------------------: | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n|             Description Check            | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                                                                                                            |\n|                Title check               | ✅ Passed | The title accurately reflects the main purpose of the PR: adding duplicate prevention functionality and response rules to the address-reviews system.                                                                                                  |\n|            Docstring Coverage            | ✅ Passed | Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.                                                                                                                                                                    |\n| No Real People Names In Style References | ✅ Passed | Pull request contains no references to real people by name in plugin commands, documentation, example prompts, or style references. All guidance uses technical and behavioral descriptions only.                                                      |\n|           Git Push Safety Rules          | ✅ Passed | No git push commands, force push operations, or autonomous push workflows detected in the modified files.                                                                                                                                              |\n|         No Untrusted Mcp Servers         | ✅ Passed | Pull request does not introduce MCP server installations from untrusted sources; uses only standard libraries and official GitHub CLI.                                                                                                                 |\n|       Ai-Helpers Overlap Detection       | ✅ Passed | The PR adds a novel utility script check_replied.py for duplicate bot reply prevention and enhances address-reviews.md with specific rules, with semantic similarity scores below 20% against all existing utils commands and no conflicting open PRs. |\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing touches</summary>\n\n- [ ] <!-- {\"checkboxId\": \"7962f53c-55bc-4827-bfbf-6a18da830691\"} --> 📝 Generate docstrings\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- This is an auto-generated comment: resource warnings by coderabbit.ai -->\n\n> [!WARNING]\n> ## Review ran into problems\n> \n> <details>\n> <summary>🔥 Problems</summary>\n> \n> Git: Failed to clone repository. Please run the `@coderabbitai full review` command to re-trigger a full review. If the issue persists, set `path_filters` to include or exclude specific files.\n> \n> </details>\n\n<!-- end of auto-generated comment: resource warnings by coderabbit.ai -->\n\n<!-- tips_start -->\n\n---\n\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- DwQgtGAEAqAWCWBnSTIEMB26CuAXA9mAOYCmGJATmriQCaQDG+Ats2bgFyQAOFk+AIwBWJBrngA3EsgEBPRvlqU0AgfFwA6NPEgQAfACgjoCEYDEZyAAUASpETZWaCrKPR1AGxJcAZvAAeABRotLQU0ohg4RLwJADuiACUXACCoZC02Nwe8AzUJDzR7PD4WJj04YjcpYgFFNheyIG2kGYAzAAMAIyJkJAGAKo2ADJcsLi43IgcAPQzROqw2AIaTMwz+NxkiAg+uGBkRDPaYLAkHlsUiDPcDR4znV19g7UUXAIumGBM/pCAKASQADK+GwFAYBQ+mAYsF8ASiJBi8TAaFIGH2mWyuXyyEASYQwZykXCQSEYaFcZjaLD9QG4ajYab8LZYAE2BGxOKUBm0Ij4RRxeAYAA01ioO0wuFgADYABwAFn+kGGKnODLQ3F4+Ck9H6AGFwvl6NQuAAmDrGyVgbpgY3G6BdNocLoATg4bVlAC0jIDHBSXBwDFA0rRkAADbLYBYYa54eAea6IBgUeDcXDXaGiADWAH1wpi6BpuLIQ8K0NZZBLSvZE8miQRIFIkz55BKIfgibA0IgMAByIloDz62jyXM5OjEkg+fDhHj4RDiDBEDRArLVCipyDRdmQCWDpoAcSo3FgAEVholhUgHAU1mw0U0bABRQHQSApKwASXP6AwFTZ8RmAo5OQChKAorDsPeT4vm+n4aAGkADNwtDYpAYYeBGArRuIcYzDe5TXCEYQRPCiIJBozC0CGkD8hKGRZDkeQ0GAvAIsUlblBu0jVFGdQNNI/p9F0S46v2HioemDDZiOsS0AWRbjpO07VHOApEN+w4kNkshwX0xpLgA8sBlTcbUPCUDwsTgvwPiQD4JB0AIaCSZAgSAQKBT6XYqLKGJN7sMKGBtsSbawIkOmQG0S4ALJoBm16KNeHYLtI/AYB48hxGcWAkP4mIMJ4GkAI7YNIND0IE9IFPAzCXNQkgFB4mBENgKIkGFBh9LKS4AGJTpAxWlSUUbChqMSgTl2SYHVNQbtQZx8BKmAgYlTXSHBUAAHJtilazcJgQ2cY14g1Ag3DbvgjLbLsuAzOEXidiQZgAOxtJKsrGi5hHIH5aLfv2sgAF7mXWfgeDQfC2DIE5TgUGrgogiCqe1BjQKVPCNRg/pQDY2BlKElSRJu8TIOx1h2DRsCQDlSDzmpAhBdJa3wQAapQ8BNlWSYpgoFDhGI6UoEoaLs7EyD9oOsjwnm9A/am62QKzjbyIgGaxmJk4eB4+AJJxVQ1LxjRwQYFgIUhBr8MIojiFIyAOE4LhuJ43i2QEwT48RRMJMkr7pBiDH5IUrHC+xP66yZBspf0G2OAIlBcI8zxDKMkDjJM0xzAsErLKsLAbEyOzs/shzHPApznJc1y3JrDzdInrzvJ8GDfPgvwAsCoJWSSZIu/4JHssiqLovRWI0Li+IUISxJUKSMKQBSArPDSdIMpsZAKqypGclw3K8rQ/JCiKnZJRKMrygCSqx3GXBqqNY66vqZXoJwkCmualpdNatr2o6Lpup6BjentrILGPtgyoXDJGLCsZ4zVhTGmM4kkcyaVHLJQsVE6wSQzNRM4LYFpnGCu2Ts6ABwkBCBpaWCkYYzhUguAA3PYFcU51ye23LAXcLkDxqhPGeC8CMSpgVvOuQIj5nyvg/F+DinsAJpXcstAREEXIiOguI+WiFkKj3AehSBMwYw4Twj+Ai7sEZ92JuRSi1FFgbj4sgOsLEpC/T9iPOoXF9Zi1DjkZg6hIC4ziOKMcTBQLQlWtMDqkAhKQBEprcSCCpLIJknJKisdFKw1nLTdSnEtLhT0pAQyzi9Y8TMnwOyDknJYNcjI4CfVvJUF8iwQRAUgr0wlO1PokUclpXkJcRSzA5FBOSsgTKa8JoMQKpxAac4xyuRqsoa2DUmotVIC0yAXVIC9T4OM46w1CiangONXKGNppRhQFGGgIRrK9KSqQRA8sdQsD2hgA6dZV5Rmurdc4pDajPVeu9T6oRvp1PYH9dKQMFoXVBuDMmUNknbPhojBcyNUZznRpgEBOM8ZEWMZ7EmZQyYKDRJSVSVN/A00JU0jJo5rkszZhzFsnMaw8z5rgAWuzih+BSuLUhQ4pYoPkXeeWit2bNnwSrNW6BaDzRSnWWl5A4hh31lYw2Rh9DGHAFAMg9B8A2TQHgQg1Tzayy4LwC2IgxD1RkPIAJyhVDqC0DoZVJgoBwFQKgJa2qCDEDIDM/xAK0RcCoLKu2vp5ByGWlQa1mhtC6DAIYFVpgDAQMwjo7CaY6n4WOEYwmf4yIUX9AAInzcbSwKR3wevIFQR+gbnDyE1YwS50gjBBjHKWGVkBAA4BAAEWHoxAoVgijBwwIAXAIMj4AYI4dghz7BW0eRdQiBMTGyuarsqEJAlxwAKLUU17F1SkKuNufkVkF60tBs7UoB6sC0upjQumih5AcVPQUG+6qm21nweq4UE47KmqkALRxjFCW0sXchUkK7IBbTkX1WOHYYhTn7JALWCwGAoGQMwRQIs6D0NKALOcdVEO0BHWO/Fmy8U0F+qgCa5Qm2h1/QaVd+CW0bqIyCJl7lkAsW+DEmxM6JD4F2XRPKAdGaIGFLS/R9A6wNkFeS5WtJcD0mE/g3Asgtg8GcGgNg4MSyhx3PZOVBT6iNBcqUPJ4dCm2XsrQRyklhSWtrcE1KAtKzDNyF48IGyhPfhljEwlGyhpQqoU5WmyMTbmCLWDGZvnzqsIKEoBgjVy0RZrRNJhY4+q3AEAxKmwtxD1oDCFyAMUHl2SRd1WMBQUgYH+iCowwwWO2eSrQLgABqY0bQZiWiMA+FSFJH42ZYR+phXAop0HgI4Aw+bc1KrjQmqMSboEzATFzVMuEYlIOlnJPNBaTbFtLV6+glaXDnL6VcowG14h0u5tNqBOEFs1ngZmVbKC5JiqULJEUOylBiwicMd8518BiTrEocGnjgLs3QAQlORCOVkPJTJSLpYWiy14VeXluBhQsO0yEYUfUWGy2ElQnwWs4ggPbROWrTSwAsuFk2QlTATniiaAAIX0tALMgJ3x7g2ikaAQwnzCkfFYYYABNVn7POfc8fO1KA9QMBZiIJTaXyA5dfZ+6Jtx9A9pXBSgAKUBPpDa/A8C3FR7NJAhLKyBSbiCi61NNDwSQFmJpD35CA6tjIBmcSUoxFLG62AfV4MLz6iQZgsdQhjhHMreARAKuyfCPLTBSDSJZgx/QOyuB0zIHR2wzlyAveQE4UeU8wotbdpsQpgkJAX2Dk03tvIRzFLg8ExYiUC91D/PAny+C8fLwlSzLLOD+AQjIG7/Fdv65c9K7VPAIvI6UK0tpBPCvKPq+1szMgevZLG9oD2OZVv25qqlTU9wOPK3Pa959USVP6eeB3DGSVJFst3PF9n2XhfRIcfn+X5gtfEH3d5iH1gRvKVDsIkXHeCeeLADXWoZAHUb7dACeAjIRCARTLYLgYfM/UfNHLNJPLPTHfgPgU/WWXoDiJQLwIgWfC6WlG+CgTYJMAOTBSgAyQ3PAZAHXPXJveXaQBodcDiG3ZADoFAGyRALfAoOscPYUJ4UHMlKHIcGHOgYUD6RzXmKceWB8JQvgJKWgICNSOIQ8CuTiGPLAOceoMQUEMcVg/XCmTiTsGaQIFdRcEsbgeALMSgagigeTQcLMQKXALMScXGWgazc/TwtsHwkEH8ZGKwZYDLYobLf/XAagzIcEBreCJnFnNnDnLnHnQEFyHIOcRIeCfnIXEXdI8XB8FyIwpGeCaXWXWAYICeBkHI3AAAbSMIAF1eho1XwMBXAoB7dHdw9Ah/dMZ7A4jhR6YhwuAjD2i9Bgo/tO8T8sDk9AhtYy0JiRiMl8BVi3DChPCY445jljdk8sxdlNipiMhchcA5j7s0DZYli4gVjhitjcwNiHiRoKAdjg89iBRjdZYjiGsHjTjaBzjLjEECDz9bj7ijDMDqhNjXj3jY43h9iAjR9fiTjdBpjASxAwDKRAhTitpyBqtasjs6AmtjQ5Q2sOgOsut9UEpOJSIqYfBFJn5hhtYCTyBvo60/jmsyT2sDBOtxButvVQI+sGSBt8thtRsQtWSdoOSms2gugOhySxsC0Aw41VhGpsAlBmItEBQZhfQ4pJhGpwQNAhBEBSgNsJstsS09UK0fQq1Ds61EAlVIAGdHApgot6xOQDoa1aVdFWNtSsAfBqCekOgNAQy2hIsQyQz5QF41TtVNTptdTnB9TJojSTTSglwwMQpzIiTKV4IhsKxDRQ8/iLcSA8yK9fc9tI9o9TDvEzYyouASyWZnB4AVAvBOIKs2BiyjMpT2TVpOSug2suhKT+TqShSs16TGTBsBQpwey6tSB+zBzhzqpRznE6T+s1xBtxTmAlSJsVSwAjA8MGBrh1E0BjTTTMYdzC1XwrTPVy0xx9tq0bIcyjBDIBZaUGxEZKw2UPANUbJaVc1fTc10YMIsBfF/9Ewg92AxxAyWBIBIyNBwy6x4KVlMzcEHjsATDwgkSKQDEsc+BIM0BoM+Acy4CChBBXgtQjYoAdQ61jlr90tchMtxBFMuAPyDoJB+x+F68L1cpkt1d/TjUrZIAALk0gKYKelc14K2ggK6xJLQyNBZQgKKYF5DzjzqBTy0yMAjYas2S5ziTIBGsBywAhzxtJt9z41/SrtrhYyNSSAtSQKbh/SzyzTLzLSds7y9tbSDsa1nz4IStzhPM+zWLPTKwF5j1gLtFfSZgbL4z/THKQLnKsA8RxLhKpKZKLo5KozdyNo9cHxZyiSFzjKlyBSZYaThTJzFQWSjAnT8yKznt9KQwlAnyEow01BvDCJAh/BhRZBEgqIF5YtOxQxIlR0jopw+qsAQxEAwRcJ+wRrqBlC0EyyCz7Aqy6Rpwsh1EGqmrQ0VA2qQiKBmAGg0BOrureq6KBqEZUJhqjqCAKBxrUIpqGAZqPA5rbqElIA8RGqJwdrw19rDrGoTrIBZBhQAYzr+rGpLqQxrrRq7q6LJrpq8gXqbqFqiwmzaD0tnEOyGqOK+A/taBZctZHIPAswcbIAABeMJe6+Gp6xG16lGqiT60mmVAmwQfsEm5wcmymuGx656umigBJfKmUgyoyoc3kqknrMq8c9c5+IbQE0bUylUmNB1TLX8nAd1a071UfP1NAANLy4NC1Fq3am1SNe1VVARdQX4xABPdkOgLMbDNcKNJWs2nwLoWUSUNoNoAAVh8AYC6B8FlBIE9tdtNFEFoGlAEA6B8FJKcjQGlFoA6A6FoAYCdFUElEdoMGVraB8CdADuNFlGQklB8A6BIADq6E9qelNAYBek9slE9oEDds6DQEjuNCeiDtenTuVrjoEHBCdGLs9ulCdBIFNE9rQBIElFUE9rdEDrQDaHrslGdAEFbslCdFrrTuVQzrNpvAtt2Sts9ltvVQ7rNpYizDYAX17w4ztvnyJHXoAG9Qlc0kBbAGdi84paBblR8rBUk6Bc1fB+xahBR76dgQQfzn6R0MxbAf7bI/6SAAG+gH7EB9IGwkxQ8MBIGfBoHYHhLATaA0V20R0aQkwFxEAaLMxIG4iSpMHc1sG0V3AmUSASHJIyH6gYH77qHcYScbsUwhoGGMw0GMH76gJX73w+FpACHIH81KGIbcAeHWQHAwZEBIHGjQk+g76+g1HhLMENo1MSBxGOHYEiMeHc1MH1Hc1sNZMFGuByGWH1G4HhkppNlxGeH7BVZt16BqKEobAjbcBABMAmQAQDlzAC8G/QYSASQ0y1bPzCMeUbUdzRQyUHEd8QoAeQXCiZseEqnEjwFH7B4a0bYHEY+30aGgm3UYAF9jHIBVGbHc1NHtHxHaG2zMFUmqmzH6QmGKHonbH9l7GimuBc010986H0AGBR07yBZwgCdXd3TwDr8KBlIyK/z8FbBr5QhCVqMaBA57EiMfBcZN0KschFMPNdNTJ9NJUKD8FZ0PYs1bZZAJlmANAmmTHKg/sYxShIGMA7hym4G4mdHenEnkmiAHmYm+ZSg/Bmpwg+G4xrGqmMnIxsmYlcmfnhLmKvBim1GynonKmTGam8nem8Gjy4jCVbkGxWpAW4GWmLHtxmHPnhK7Ho8enhK8XyiFwFBiXSAwnpRIoPaABSbBXISmVABwBk5zdgWjZxYqeAcIMTLPIBn89lyMjoLl+56l2JhKBJ5wf50l9JpMWFjwHJ2p3p1Sgloh1FvodF9RzFmJ2lw5cRiIqJVzO/EA0oWkTCSAQKTiOycIYDTjKwsSLYTYNskNLGui6bARfCYUQ8hAw5d9fwQ/NsjUGqVMPC4Y2QNs8ZygMgeGJcFIKJQDZdbxKA7cUQWAB5RGw5gioi2DApxbBLDpJVjpjR+F/V4SsDVkWDKwEgf1goBF5Ad8LAGkFNgoVkD1jN6QTVlV+J3plwqcMd8ltpqFx50QEFyPUwiF/++t3NGFrJ3VxtnFjR+kAgZgE1yAM1tRi1zpyaOl153p/pi5zFK5sxF2Bpp1ykPx5wJOhKegTOTiFDdZoN7sTdnsOir91XdgpjF2K4IkQEhMTUNmZlygkw2DcIH9ldMdp59CBx3ptRAOCNwRCdAHJAJgBsIZ2TRDoPbaV17R+sFsyAbsL9pD8jsACQbsShdauFNSLxBeL9+DQ5ujkgRyUyVXOttJ6pndxF3NMDFIPhTsvPLxVkZD0DbRhR5V758RqdigGdmTVp3+yF5V4FjAUFld7Ttd4TgDuFzMBF8R0dOcFgI9k9lR9dq1jD5ti6L924HYUN3C2yKcKyNzymVeeLGoJNt1UoFgEEVjekSmOIKcDMAnbWZAF3MQMcQ9fBOJtDFPUra5Md7FsTvcLxCI9zwEYQg5nGRoMdlTydtQjT5eOd3Txd/T5d8Foz+dmJ0z7d8zpt6p/dmz6Juziphzrpy91B3p21sSe1tGPDFKLw/Y+IjCgoKKHUKwSdCgIjzCWkTWQ5NfIM7xNEeoCZPbEEMEaQehSqbFLDWkH8N9uDeASEJMdlUOTVPwfKWDXL3AAACWWGVyE6qey/EbAwGB2/3bHCigYDOkBEoA/LK9VYq9cKq/MZq/Xb04M8a6gZ0/Xda71d3c6+s8PZ6/KbPZpYG+tevcWbsC+jB0CmCd0XUGVn0ZXxBI91QXkHrzWdbDf2QU6T7SIx4IwCSnhmeznSxQfcsKqFEBFkQxOfc2F6D3FEYsRk8Ti2p6rBhihkJxfgVfQDINW+ITEkvTSV9I87AQ4jddpwJ3OLNyZEhS+6xdE/EZSFLle/Lk5ByQbEajOhJxoF2ch4neEr+dUlh608pfaeE8R4a8RfQdR5M+1a3Yx7E6s4Pds9CRaMkc7FwFsD0eraveEq6GXo6Hnp8AruTp8AEElG6AYFNG6ATtoHNFlBHqeh8E9pbptAECr9oCel9slCeloAZLemlG6DlIrpno6E9pLsUuT7nFsHqdj+dEjqelHvz+lE74ECLrelntdtbuNBrosyHqHsHoYDaCaptDdplCeidBTpLo6EHsHrdqdCelUGNCaaoZHSZaICJeUFIF7fBj2aXhoEgYtdzUu1mzXZ9Gd2enmtkLC/912BANbt1B2abIKWkoZVts1JBwCAA6osEZZGsiAFLT2j11CQlMDA+AjehAEDgn1KApAc+qvmcKhx9AQAA== -->\n\n<!-- internal state end -->"},"request":{"retryCount":1}},"response":{"url":"https://api.github.com/repos/openshift-eng/ai-helpers/issues/comments/3784386992","status":401,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","connection":"close","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Thu, 22 Jan 2026 14:21:15 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","vary":"Accept-Encoding, Accept, X-Requested-With","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-media-type":"github.v3; format=json","x-github-request-id":"80B0:3D5A1E:DD15C4:3D64CEC:69723258","x-xss-protection":"0"},"data":{"message":"Requires authentication","documentation_url":"https://docs.github.com/rest","status":"401"}}}

@cblecker

cblecker commented Feb 3, 2026

Copy link
Copy Markdown
Collaborator

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Feb 3, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@cblecker

cblecker commented Feb 3, 2026

Copy link
Copy Markdown
Collaborator

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Feb 3, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Full review triggered.

@cblecker

cblecker commented Feb 3, 2026

Copy link
Copy Markdown
Collaborator

It's odd that CodeRabbit seems to have wedged itself on this PR, but a manual review looks good to me

/lgtm

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Feb 3, 2026
@openshift-ci

openshift-ci Bot commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: bryan-cox, cblecker

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-merge-bot
openshift-merge-bot Bot merged commit 2fca130 into openshift-eng:main Feb 3, 2026
5 checks passed
@bryan-cox
bryan-cox deleted the fix-review-agent-duplicates branch February 3, 2026 20:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants