Skip to content

Add bot-guarded scripts for PR review thread reply and resolve#203

Draft
ikuwow wants to merge 2 commits into
mainfrom
feature/bot-pr-review-126
Draft

Add bot-guarded scripts for PR review thread reply and resolve#203
ikuwow wants to merge 2 commits into
mainfrom
feature/bot-pr-review-126

Conversation

@ikuwow

@ikuwow ikuwow commented Jun 7, 2026

Copy link
Copy Markdown
Owner

Closes #126.

Why

gh does not expose review-thread reply or resolve through its native CLI, so today every reply requires an interactive gh api graphql ... -f ... call that lives behind the ask claw-back. That is fine for one-off use but makes a "bot answers bot" workflow impractical, since each step prompts. At the same time, an unguarded auto-reply path on human review threads would be a hard rule violation. The structural fix is to keep the GraphQL behind wrapper scripts that perform the bot check themselves; the scripts then sit in allow without enabling the wrong behavior.

What

  • bin/gh-pr-review-threads — read-only GraphQL list of review threads with their first comments, isResolved, isOutdated, author login and __typename.
  • bin/gh-pr-thread-reply and bin/gh-pr-thread-resolve — write paths gated by the shared bin/gh-pr-thread-bot-guard.sh. The guard accepts a thread iff the first comment author is either __typename == "Bot" or appears in the bot login allowlist (github-actions[bot], dependabot[bot], renovate[bot], copilot-pull-request-reviewer[bot], coderabbitai[bot], devin-ai-integration[bot], claude[bot]).
  • claude/rules/pr-review-response.md — the procedural rule the wrappers serve.
  • claude/settings.jsonallow for the three wrappers; ask for top-level gh pr comment * so direct PR comments still surface a prompt.

Notes

  • After review, the guard switched from reviewThreads(first:100) + jq-side ID filter to a direct node(id: $tid) lookup, plus a regex check on the thread id charset. This closes the 100-thread pagination cap and removes the quoting risk of interpolating the id into a jq filter string.
  • The guard is sourced via realpath "$0" so the executable scripts find their guard helper even when invoked through the ~/bin symlink that scripts/deploy.sh creates.
  • The bot login allowlist intentionally includes both the [bot]-suffixed and unsuffixed forms of copilot-pull-request-reviewer, since GitHub has varied that suffix across the App's lifetime.
  • gh-pr-thread-bot-guard.sh is mode 0644, so scripts/deploy.sh's -perm 0755 filter skips it — the helper stays adjacent to the wrappers in the source tree only.

Verification

  • gh-pr-review-threads ikuwow/dotfiles 207 returns valid JSON (0 threads on that PR, query path exercised end-to-end).
  • gh-pr-thread-reply rejects a malformed thread id ('PRT_kw"; echo pwned') with Error: thread id contains unexpected characters: ... before any GraphQL call.
  • gh-pr-thread-reply against a syntactically-valid-but-unknown id (INVALID_THREAD_ID_xxx) reaches GraphQL and gets Could not resolve to a node with the global id of ... — confirms the node(id:) path is wired correctly.
  • gh-pr-thread-reply against a real human-authored review thread refuses (would require a PR with an existing human thread; deferred to first organic use).
  • gh-pr-thread-reply against a real bot-authored thread successfully posts and gh-pr-thread-resolve resolves it (defer until a bot PR review actually lands; would otherwise post a noise comment).
  • shellcheck bin/gh-pr-* clean.
  • claude/settings.json valid JSON (jq . claude/settings.json).

ikuwow and others added 2 commits June 7, 2026 14:51
Three bin/ scripts: gh-pr-review-threads (list), gh-pr-thread-reply,
gh-pr-thread-resolve. The two write scripts share a bot-guard helper
that refuses to act on threads whose first comment author is not a
recognised bot (GraphQL __typename == Bot, or a login in the known
list). Adds an allow entry per script, a new pr-review-response.md
rule, and an ask entry for top-level gh pr comment.

Closes #126.

Co-authored-by: Claude Opus 4.6 <[email protected]>
The earlier reviewThreads(first:100) + jq-side id filter had two
issues: PRs with >100 threads would silently fail with "not found"
for valid ids beyond the page, and the id was interpolated into the
jq filter string where a stray quote could break it. Direct
node(id: $tid) lookup removes both. A regex check on the id charset
prevents anything outside opaque base64-style strings from reaching
the GraphQL variable.

Co-authored-by: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add scripts and rules for Claude Code to respond to bot PR review comments

1 participant