본문으로 건너뛰기
cccskills
무료GitHub에 공개한국어 소개

pr-address

PR 검토 의견을 모아 코드 수정, 커밋, 답변과 스레드 해결을 진행합니다. CI 실패, 충돌, 새 댓글도 계속 확인하며 대응이 끝날 때까지 추적합니다.

원문원문 설명 보기

Address PR review comments and loop until CI green and all comments resolved. TRIGGER when user asks to address comments, fix PR feedback, respond to reviewers, or babysit/monitor a PR.

연동 도구GitHub

실행 점수 미검증

설치 방법 보기

이럴 때 유용합니다

  • PR 검토에서 지적된 코드 수정
  • 사람과 봇의 검토 의견에 답변
  • 푸시 후 CI 실패 조사와 수정
  • PR 충돌과 추가 댓글 모니터링

한국어 소개

할 수 있는 일

PR 검토 의견을 읽고 코드 수정부터 답변, 처리한 스레드의 해결까지 진행합니다. 줄별 댓글, 검토 본문, PR 대화의 모든 페이지를 확인해 미처리 의견을 모읍니다. 수정 사항을 커밋하고 푸시한 뒤 해당 커밋 링크를 첨부해 답변합니다. 오탐이면 수정이 필요하지 않다고 판단한 이유를 설명합니다.

이럴 때 유용합니다

검토가 여러 차례 오가는 PR이나 사람과 봇의 의견이 섞인 PR에 유용합니다. 자동 검사인 CI의 실패와 병합 충돌도 확인하며, 새 댓글 대응과 함께 반복해서 처리합니다.

사용 예시

  • “이 PR의 검토 의견을 수정하고 답변까지 진행해 주세요.”
  • “현재 브랜치의 PR을 확인하고 CI 실패에 대응해 주세요.”
  • “새 의견이 오는지 확인하면서 PR을 계속 추적해 주세요.”

주의 사항

GitHub CLI인 gh와 Git을 사용합니다. 수정 사항 푸시, GitHub 답변 게시, 스레드 해결이 포함됩니다. 서식 정리와 테스트는 AutoGPT 개발 환경을 전제로 합니다. CI 통과 후에도 추가 확인을 하며, 요청한 봇 검토는 최대 60분 기다립니다. 제공된 자료가 일부이므로 확인된 범위만 소개합니다.

이 소개문은 공개된 SKILL.md를 바탕으로 AI(OpenAI Codex)가 작성했습니다. 정확한 사양은 아래 원문을 확인하세요.

문서 점검

AI가 SKILL.md의 내용에서 주의가 필요한 작업과 도입 전제를 확인했습니다. 점수는 실제로 실행한 결과(실행 검증)만으로 매깁니다.

안전성전송·삭제·명령 실행 등을 명시하고 있는가
C확인 필요선택 단계에서 gh pr comment로 /review를 게시하지만 게시 전 확인 절차가 없습니다.
도입의 용이성필요한 도구나 API 키가 적고 적혀 있는가
B일부 부족gh, Git, jq와 AutoGPT 저장소를 사용하지만 설치나 인증 절차는 없습니다.

점수에는 넣지 않았습니다. 2026년 10월 23일 기준으로 AI(OpenAI Codex)가 판정했습니다. 참고용이므로 설치 전에 원문도 확인해 주세요.

평가가 다르다고 생각되면 재검토를 요청하기

어느 항목이 왜 다르다고 생각하는지 적어 주세요. 로그인은 필요 없습니다. SKILL.md와 대조해 평가를 다시 만들고 이곳에 반영합니다.

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

포함된 파일(1)

  • SKILL.md30.3 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

PR Address

Find the PR

gh pr list --head $(git branch --show-current) --repo Significant-Gravitas/AutoGPT
gh pr view {N}

Read the PR description

Understand the Why / What / How before addressing comments — you need context to make good fixes:

gh pr view {N} --json body --jq '.body'

If GraphQL is rate-limited, gh pr view fails. See GitHub rate limits for REST fallbacks.

Optional: trigger the review bot

Only relevant if the PR has no review yet. If the fetch steps below turn up no reviews and no inline threads, there is nothing to address yet — that is not the same as being done. You can summon the repo's review bot yourself:

gh pr comment {N} --body "/review"

This is an option, not a required step. Skip it whenever the PR already has reviews (bot or human) — those are what you are here to address. See the open-pr skill's "Review workflow" step for the same trigger in the PR-creation flow.

autogpt-pr-reviewer[bot] typically responds within ~30 minutes. Poll for it rather than sleeping — the loop in Polling for CI + new comments already re-checks every source every 30 seconds, so run that loop and react the moment the review lands. Note the id and created_at of the /review comment you posted; Waiting on a requested review needs them for the exit condition.

Fetch comments (all sources)

1. Inline review threads — GraphQL (primary source of actionable items)

⚠️ WARNING — PAGINATE ALL PAGES BEFORE ADDRESSING ANYTHING

reviewThreads(first: 100) returns at most 100 threads per page AND returns threads oldest-first. On a PR with many review cycles (e.g. 373 threads), the oldest 100–200 threads are from past cycles and are all already resolved. Filtering client-side with select(.isResolved == false) on page 1 therefore yields 0 results — even though pages 2–4 contain many unresolved threads from recent review cycles.

This is the most common failure mode: agent fetches page 1, sees 0 unresolved after filtering, stops pagination, reports "done" — while hundreds of unresolved threads sit on later pages.

One observed PR had 142 total threads: page 1 returned 0 unresolved (all old/resolved), while pages 2–3 had 111 unresolved. Another with 373 threads across 4 pages also had page 1 entirely resolved.

The rule: ALWAYS paginate to hasNextPage == false regardless of the per-page unresolved count. Never stop early because a page returns 0 unresolved.

Step 1 — Fetch total count and sanity-check the newest threads:

# Get total count and the newest 100 threads (last: 100 returns newest-first)
gh api graphql -f query='
{
  repository(owner: "Significant-Gravitas", name: "AutoGPT") {
    pullRequest(number: {N}) {
      reviewThreads { totalCount }
      newest: reviewThreads(last: 100) {
        nodes { isResolved }
      }
    }
  }
}' | jq '{ total: .data.repository.pullRequest.reviewThreads.totalCount, newest_unresolved: [.data.repository.pullRequest.newest.nodes[] | select(.isResolved == false)] | length }'

If total > 100, you have multiple pages — you must paginate all of them regardless of what newest_unresolved shows. The last: 100 check is a sanity signal only; the full loop below is mandatory.

Step 2 — Collect all unresolved thread IDs across all pages:

# Accumulate all unresolved threads — loop until hasNextPage == false
CURSOR=""
ALL_THREADS="[]"
while true; do
  AFTER=${CURSOR:+", after: \"$CURSOR\""}
  PAGE=$(gh api graphql -f query="
  {
    repository(owner: \"Significant-Gravitas\", name: \"AutoGPT\") {
      pullRequest(number: {N}) {
        reviewThreads(first: 100${AFTER}) {
          pageInfo { hasNextPage endCursor }
          nodes {
            id
            isResolved
            path
            line
            comments(last: 1) {
              nodes { databaseId body author { login } }
            }
          }
        }
      }
    }
  }")
  # Append unresolved nodes from this page
  PAGE_THREADS=$(echo "$PAGE" | jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false)]')
  ALL_THREADS=$(echo "$ALL_THREADS $PAGE_THREADS" | jq -s 'add')
  HAS_NEXT=$(echo "$PAGE" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage')
  CURSOR=$(echo "$PAGE" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor')
  [ "$HAS_NEXT" = "false" ] && break
done

# Reverse so newest threads (last pages) are addressed first — GitHub returns oldest-first
# and the most recent review cycle's comments are the ones blocking approval.
ALL_THREADS=$(echo "$ALL_THREADS" | jq 'reverse')

echo "Total unresolved threads: $(echo "$ALL_THREADS" | jq 'length')"
echo "$ALL_THREADS" | jq '[.[] | {id, path, line, body: .comments.nodes[0].body[:200]}]'

Step 3 — Address every thread in ALL_THREADS, then resolve.

Only after this loop completes (all pages fetched, count confirmed) should you begin making fixes.

Why reverse? GraphQL returns threads oldest-first and exposes no orderBy option. A PR with 373 threads has ~4 pages; threads from the latest review cycle land on the last pages. Processing in reverse ensures the newest, most blocking comments are addressed first — the earlier pages mostly contain outdated threads from prior cycles.

Filter to unresolved threads only — skip any thread where isResolved: true. comments(last: 1) returns the most recent comment in the thread — act on that; it reflects the reviewer's final ask. Use the thread id (Relay global ID) to track threads across polls.

If GraphQL is rate-limited, see GitHub rate limits for the REST fallback (flat comment list — no thread grouping or isResolved).

2. Top-level reviews — REST (MUST paginate)

gh api repos/Significant-Gravitas/AutoGPT/pulls/{N}/reviews --paginate

Already REST — unaffected by GraphQL rate limits or outages. Continue polling reviews normally even when GraphQL is exhausted.

CRITICAL — always --paginate. Reviews default to 30 per page. PRs can have 80–170+ reviews (mostly empty resolution events). Without pagination you miss reviews past position 30 — including autogpt-pr-reviewer[bot]'s structured review which is typically posted after several CI runs and sits well beyond the first page.

Two things to extract:

  • Overall state: look for CHANGES_REQUESTED or APPROVED reviews.
  • Actionable feedback: non-empty bodies only. Empty-body reviews are thread-resolution events — they indicate progress but have no feedback to act on.

Where each reviewer posts:

  • autogpt-pr-reviewer[bot] — posts detailed structured reviews ("Blockers", "Should Fix", "Nice to Have") as top-level reviews. Not present on every PR. Address ALL items.
  • sentry[bot] — posts bug predictions as inline threads. Fix real bugs, explain false positives.
  • coderabbitai[bot] — posts summaries as top-level reviews AND actionable items as inline threads. Address actionable items.
  • Human reviewers — can post in any source. Address ALL non-empty feedback.

3. PR conversation comments — REST

gh api repos/Significant-Gravitas/AutoGPT/issues/{N}/comments --paginate

Already REST — unaffected by GraphQL rate limits.

Mostly contains: bot summaries (coderabbitai[bot]), CI/conflict detection (github-actions[bot]), and author status updates. Scan for non-empty messages from non-bot human reviewers that aren't the PR author — those are the ones that need a response.

Slash-command trigger comments are never actionable items — skip them. A comment whose entire body is a slash command (/review being the common one) exists to summon the review bot; it is not a request directed at you. Do not reply to it, do not treat it as feedback, and do not count it as a "new comment" that restarts the loop. The non-author filter above happens to hide the usual case where the PR author posted it, but that is incidental — a maintainer can post /review on someone else's PR, and it must still be skipped. The actionable content is the review it produces, which arrives as a top-level review and/or inline threads, not as a conversation comment.

For each unaddressed comment

CRITICAL: The only valid sequence is fix → commit → push → reply → resolve. Never resolve a thread without a real code commit.

Resolving a thread via resolveReviewThread without an actual fix is the most common failure mode — it makes unresolved counts drop without any real change, producing a false "done" signal. If the issue was genuinely a false positive (no code change needed), reply explaining why and then resolve. Otherwise:

Address comments one at a time: fix → commit → push → inline reply → resolve.

  1. Read the referenced code, make the fix (or reply explaining why it's not needed)
  2. Commit and push the fix
  3. Reply inline (not as a new top-level comment) referencing the fixing commit — this is what resolves the conversation for bot reviewers (coderabbitai, sentry):

Use a markdown commit link so GitHub renders it as a clickable reference. Always get the full SHA with git rev-parse HEAD after committing — never copy a SHA from a previous commit or hardcode one:

FULL_SHA=$(git rev-parse HEAD)
gh api repos/Significant-Gravitas/AutoGPT/pulls/{N}/comments/{ID}/replies \
  -f body="🤖 Fixed in [${FULL_SHA:0:9}](https://github.com/Significant-Gravitas/AutoGPT/commit/${FULL_SHA}): <description>"
Comment typeHow to reply
Inline review (pulls/{N}/comments)gh api repos/Significant-Gravitas/AutoGPT/pulls/{N}/comments/{ID}/replies -f body="🤖 Fixed in [abc1234](https://github.com/Significant-Gravitas/AutoGPT/commit/FULL_SHA): <description>"
Conversation (issues/{N}/comments)gh api repos/Significant-Gravitas/AutoGPT/issues/{N}/comments -f body="🤖 Fixed in [abc1234](https://github.com/Significant-Gravitas/AutoGPT/commit/FULL_SHA): <description>"

What counts as a valid resolution

Only two situations justify calling resolveReviewThread:

  1. Real code fix: you changed the code, committed + pushed, and replied with the SHA. The commit diff must actually address the concern — not just touch the same file.
  2. Genuine false positive: the reviewer's concern does not apply to this code, and you can give a specific technical reason (e.g. "Not applicable — sdk_cwd is pre-validated by _make_sdk_cwd() which applies normpath + prefix assertion before reaching this point").

Anti-patterns that look resolved but aren't — never do these:

  • "Accepted, tracked as follow-up" — a deferral, not a fix. The concern is still open. Do not resolve.
  • "Acknowledged" or "Same as above" — these are acknowledgements, not fixes. Do not resolve.
  • "Fixed in abc1234" where abc1234 is a commit that doesn't actually change the flagged line/logic — dishonest. Verify git show abc1234 -- path/to/file changes the right thing before posting.
  • Resolving without replying — the reviewer never sees what happened.

When in doubt: if a code change is needed, make it. A deferred issue means the thread stays open until the follow-up PR is merged.

Codecov coverage

Codecov patch target is 80% on changed lines. Checks are informational (not blocking) but should be green.

Running coverage locally

Backend (from autogpt_platform/backend/):

poetry run pytest -s -vv --cov=backend --cov-branch --cov-report term-missing

Frontend (from autogpt_platform/frontend/):

pnpm vitest run --coverage

When codecov/patch fails

  1. Find uncovered files: git diff --name-only $(gh pr view --json baseRefName --jq '.baseRefName')...HEAD
  2. For each uncovered file — extract inline logic to helpers.ts/helpers.py and test those (highest ROI). Colocate tests as *_test.py (backend) or __tests__/*.test.ts (frontend).
  3. Run coverage locally to verify, commit, push.

Format and commit

After fixing, format the changed code:

  • Backend (from autogpt_platform/backend/): poetry run format
  • Frontend (from autogpt_platform/frontend/): pnpm format && pnpm lint && pnpm types

If API routes changed, regenerate the frontend client:

cd autogpt_platform/backend && poetry run rest &
REST_PID=$!
trap "kill $REST_PID 2>/dev/null" EXIT
WAIT=0; until curl -sf http://localhost:8006/health > /dev/null 2>&1; do sleep 1; WAIT=$((WAIT+1)); [ $WAIT -ge 60 ] && echo "Timed out" && exit 1; done
cd ../frontend && pnpm generate:api:force
kill $REST_PID 2>/dev/null; trap - EXIT

Never manually edit files in src/app/api/__generated__/.

Then commit and push immediately — never batch commits without pushing. Each fix should be visible on GitHub right away so CI can start and reviewers can see progress.

Never push empty commits (git commit --allow-empty) to re-trigger CI or bot checks. When a check fails, investigate the root cause (unchecked PR checklist, unaddressed review comments, code issues) and fix those directly. Empty commits add noise to git history.

For backend commits in worktrees: poetry run git commit (pre-commit hooks).

Coverage

Codecov enforces patch coverage on new/changed lines — new code you write must be tested. Before pushing, verify you haven't left new lines uncovered:

cd autogpt_platform/backend
poetry run pytest --cov=. --cov-report=term-missing {path/to/changed/module}

Look for lines marked miss — those are uncovered. Add tests for any new code you wrote as part of addressing comments.

Rules:

  • New code you add should have tests
  • Don't remove existing tests when fixing comments
  • If a reviewer asks you to delete code, also delete its tests, but verify coverage hasn't dropped on remaining lines

The loop

address comments → format → commit → push
→ wait for CI (while addressing new comments) → fix failures → push
→ re-check comments after CI settles
→ repeat until: all comments addressed AND CI green AND no new comments arriving

Polling for CI + new comments

After pushing, poll for both CI status and new comments in a single loop. Do not use gh pr checks --watch — it blocks the tool and prevents reacting to new comments while CI is running.

Note: gh pr checks --watch --fail-fast is tempting but it blocks the entire Bash tool call, meaning the agent cannot check for or address new comments until CI fully completes. Always poll manually instead.

Polling loop — repeat every 30 seconds:

  1. Check CI status:
gh pr checks {N} --repo Significant-Gravitas/AutoGPT --json bucket,name,link

Parse the results: if every check has bucket of "pass" or "skipping", CI is green. If any has "fail", CI has failed. Otherwise CI is still pending.

  1. Check for merge conflicts:
gh pr view {N} --repo Significant-Gravitas/AutoGPT --json mergeable --jq '.mergeable'

If the result is "CONFLICTING", the PR has a merge conflict — see "Resolving merge conflicts" below. If "UNKNOWN", GitHub is still computing mergeability — wait and re-check next poll.

  1. Check for new/changed comments (all three sources):

    Inline threads — re-run the GraphQL query from "Fetch comments". For each unresolved thread, record {thread_id, last_comment_databaseId} as your baseline. On each poll, action is needed if:

    • A new thread id appears that wasn't in the baseline (new thread), OR
    • An existing thread's last_comment_databaseId has changed (new reply on existing thread)

    Conversation comments:

    # Exclude slash-command triggers here, not just when reading — otherwise a
    # `/review` (yours or a maintainer's) counts as a "new comment" and forces
    # an address round over a comment with nothing to address.
    gh api repos/Significant-Gravitas/AutoGPT/issues/{N}/comments --paginate --slurp \
      | jq '[.[][] | select((.body // "") | test("^\\s*/[a-z-]+\\s*$") | not)]'
    

    Compare total count and newest id against baseline. Filter to non-empty, non-bot, non-author-update messages.

    Top-level reviews:

    gh api repos/Significant-Gravitas/AutoGPT/pulls/{N}/reviews --paginate
    

    Watch for new non-empty reviews (CHANGES_REQUESTED or COMMENTED with body). Compare total count and newest id against baseline.

  2. React in this precedence order (first match wins):

What happenedAction
Merge conflict detectedSee "Resolving merge conflicts" below.
Mergeability is UNKNOWNGitHub is still computing mergeability. Sleep 30 seconds, then restart polling from the top.
New comments detectedAddress them (fix → commit → push → reply). After pushing, re-fetch all comments to update your baseline, then restart this polling loop from the top (new commits invalidate CI status).
CI failed (bucket == "fail")Get failed check links: gh pr checks {N} --repo Significant-Gravitas/AutoGPT --json bucket,link --jq '.[] | select(.bucket == "fail") | .link'. Extract run ID from link (format: .../actions/runs/<run-id>/job/...), read logs with gh run view <run-id> --repo Significant-Gravitas/AutoGPT --log-failed. Fix → commit → push → restart polling.
CI green + no new comments, but a requested /review is unansweredThe bot is still working — quiet polls are exactly what that looks like. Keep polling; do not count these as the quiet polls below. See "Waiting on a requested review".
CI green + no new commentsDo not exit immediately. Bots (coderabbitai, sentry) often post reviews shortly after CI settles. Continue polling for 2 more cycles (60s) after CI goes green. Only exit after 2 consecutive green+quiet polls.
CI pending + no new commentsSleep 30 seconds, then poll again.

The loop ends when: CI fully green + all comments addressed + 2 consecutive polls with no new comments after CI settled + no requested-but-unanswered /review (see below).

Waiting on a requested review

If a /review comment was posted on this PR and the bot has not answered it yet, the "2 consecutive green+quiet polls" exit is premature — a pending bot review looks identical to a quiet PR. Keep polling until one of these is true:

  • The review arrived. An answer is either a top-level review or an inline review comment from autogpt-pr-reviewer[bot], whichever is later — an inline-only reply is still an answer, and counting only top-level reviews leaves the gate waiting after its threads have already been addressed. Once it lands, treat it as "New comments detected" and address it normally.
  • The budget ran out. Stop waiting 60 minutes after the /review comment's created_at. Then exit per the normal rule and tell the user the requested review never arrived. Never hang indefinitely on a bot.

Recompute both timestamps from the API on every poll rather than tracking elapsed time in a loop variable — a counter that some branch forgets to increment is how this gate becomes an infinite wait.

# `--slurp` cannot be combined with `--jq`; pipe to standalone jq instead.
REQUESTED_AT=$(gh api "repos/Significant-Gravitas/AutoGPT/issues/{N}/comments" --paginate --slurp \
  | jq -r '[.[][] | select((.body // "") | test("^\\s*/review\\s*$"))] | max_by(.created_at) | .created_at // empty')
REVIEWED_AT=$(gh api "repos/Significant-Gravitas/AutoGPT/pulls/{N}/reviews" --paginate --slurp \
  | jq -r '[.[][] | select(.user.login == "autogpt-pr-reviewer[bot]")] | max_by(.submitted_at) | .submitted_at // empty')
COMMENTED_AT=$(gh api "repos/Significant-Gravitas/AutoGPT/pulls/{N}/comments" --paginate --slurp \
  | jq -r '[.[][] | select(.user.login == "autogpt-pr-reviewer[bot]")] | max_by(.created_at) | .created_at // empty')
ANSWERED_AT=$(printf '%s\n%s\n' "$REVIEWED_AT" "$COMMENTED_AT" | grep -v '^$' | sort | tail -1)

# Pending iff a request exists and no answer is strictly newer than it.
# Start every poll from `false`: a stale `true` from the previous poll would
# otherwise keep the gate waiting after the answer has already landed.
# `[ a \< b ]` is not portable (fails under zsh), so compare via sort.
pending=false
NEWEST=$(printf '%s\n%s\n' "$REQUESTED_AT" "$ANSWERED_AT" | grep -v '^$' | sort | tail -1)
if [ -n "$REQUESTED_AT" ] && { [ -z "$ANSWERED_AT" ] || [ "$NEWEST" = "$REQUESTED_AT" ]; }; then
  pending=true
fi

A /review posted before this session still counts. Budget is 60 minutes from REQUESTED_AT: an observed reply on this repo took 46 minutes, so 45 would have abandoned a review that was about to land. Enforce it in the same poll so the loop cannot wait forever:

# Only meaningful when a request exists: GNU date reads an empty string as
# "now", BSD date errors, and either way there is nothing to time out.
if [ -n "$REQUESTED_AT" ]; then
  WAITED=$(( $(date +%s) - $(date -u -j -f "%Y-%m-%dT%H:%M:%SZ" "$REQUESTED_AT" +%s 2>/dev/null || date -u -d "$REQUESTED_AT" +%s) ))
  [ "$WAITED" -ge 3600 ] && pending=false   # budget spent — stop waiting
fi

Do not post another /review while one is unanswered — check this same condition before triggering, or each round queues a duplicate request.

Resolving merge conflicts

  1. Identify the PR's target branch and remote:
gh pr view {N} --repo Significant-Gravitas/AutoGPT --json baseRefName --jq '.baseRefName'
git remote -v   # find the remote pointing to Significant-Gravitas/AutoGPT (typically 'upstream' in forks, 'origin' for direct contributors)
  1. Pull the latest base branch with a 3-way merge:
git pull {base-remote} {base-branch} --no-rebase
  1. Resolve conflicting files, then verify no conflict markers remain:
if grep -R -n -E '^(<<<<<<<|=======|>>>>>>>)' <conflicted-files>; then
  echo "Unresolved conflict markers found — resolve before proceeding."
  exit 1
fi
  1. Stage and push:
git add <conflicted-files>
git commit -m "Resolve merge conflicts with {base-branch}"
git push
  1. Restart the polling loop from the top — new commits reset CI status.

GitHub rate limits

Three distinct rate limits exist — they have different causes, error shapes, and recovery times:

ErrorHTTP codeCauseRecovery
{"code":"abuse"}403Secondary rate limit — too many write operations (comments, mutations) in a short windowWait 2–3 minutes. 60s is often not enough.
{"message":"API rate limit exceeded"}429Primary REST rate limit — 5000 calls/hr per userWait until X-RateLimit-Reset header timestamp
GraphQL: API rate limit already exceeded for user ID ...403 on stderr, gh exits 1GraphQL-specific per-user limit — distinct from REST's 5000/hr and from the abuse secondary limit. Trips faster than REST because point costs per query.Wait until the GraphQL window resets (typically ~1 hour from the first call in the window). REST still works — use fallbacks below.

Prevention: Add sleep 3 between individual thread reply API calls. When posting >20 replies, increase to sleep 5.

Detection

The gh CLI surfaces the GraphQL limit on stderr with the exact string GraphQL: API rate limit already exceeded for user ID <id> and exits 1 — any gh api graphql ... or gh pr view ... call fails. Check current quota and reset time via the REST endpoint that reports GraphQL quota (this call is REST and still works whether GraphQL is rate-limited OR fully down):

gh api rate_limit --jq '.resources.graphql'   # { "limit": 5000, "used": 5000, "remaining": 0, "reset": 1729...}
# Human-readable reset:
gh api rate_limit --jq '.resources.graphql.reset' | xargs -I{} date -r {}

Retry when remaining > 0. If you need to proceed sooner, sleep 2–5 min and probe again — the limit is per user, not per machine, so other concurrent agents under the same token also consume it.

What keeps working

When GraphQL is unavailable (rate-limited or outage):

  • Keeps working (REST): top-level reviews fetch, conversation comments fetch, all inline-comment replies, CI status (gh pr checks), and the gh api rate_limit probe.
  • Degraded: inline thread list — fall back to flat /pulls/{N}/comments REST, which drops thread grouping, isResolved, and Relay thread IDs. You still get comment bodies and the databaseId as id, enough to read and reply.
  • Blocked: gh pr view, the resolveReviewThread mutation, and any new gh api graphql queries — wait for the quota to reset.

Fall back to REST

PR metadata reads — gh pr view uses GraphQL under the hood; use the REST pulls endpoint instead, which returns the full PR object:

gh api repos/Significant-Gravitas/AutoGPT/pulls/{N} --jq '.body'           # == --json body
gh api repos/Significant-Gravitas/AutoGPT/pulls/{N} --jq '.base.ref'       # == --json baseRefName
gh api repos/Significant-Gravitas/AutoGPT/pulls/{N} --jq '.mergeable'      # == --json mergeable

Note: REST mergeable returns true|false|null; GraphQL returns MERGEABLE|CONFLICTING|UNKNOWN. The null case maps to UNKNOWN — treat it the same (still computing; poll again).

Inline comments (flat list) — no thread grouping or isResolved, but enough to read and reply:

gh api repos/Significant-Gravitas/AutoGPT/pulls/{N}/comments --paginate \
  | jq '[.[] | {id, path, line, user: .user.login, body: .body[:200], in_reply_to_id}]'

Use this degraded mode to make progress on the fix → reply loop, then return to GraphQL for resolveReviewThread once the rate limit resets.

Replies — already REST-native (/pulls/{N}/comments/{ID}/replies); no change needed, use the same command as the main flow.

resolveReviewThread — no REST equivalent; GitHub does not expose a REST endpoint for thread resolution. Queue the thread IDs needing resolution, wait for the GraphQL limit to reset, then run the resolve mutations in a batch (with sleep 3 between calls, per the secondary-limit guidance).

Recovery from secondary rate limit (403 abuse)

  1. Stop all API writes immediately
  2. Wait 2 minutes minimum (not 60s — secondary limits are stricter)
  3. Resume with sleep 3 between each call
  4. If 403 persists after 2 min, wait another 2 min before retrying

Never batch all replies in a tight loop — always space them out.

Parallel thread resolution

When a PR has more than 10 unresolved threads, addressing one commit per thread is slow. Use this strategy instead:

Group by file, batch per commit

  1. Sort ALL_THREADS by path — threads in the same file can share a single commit.
  2. Fix all threads in one file → git commit → git push → reply to all those threads with the same SHA → resolve them all.
  3. Move to the next file group and repeat.

This reduces N commits to (number of files touched), which is usually 3–5 instead of 15–30.

Posting replies concurrently (for large batches)

For truly independent thread groups (different files, no shared logic), you can post replies in parallel using background subshells — but always space out API writes:

# Post replies to a batch of threads concurrently, 3s apart
(
  sleep 3
  gh api repos/Significant-Gravitas/AutoGPT/pulls/{N}/comments/{ID1}/replies \
    -f body="🤖 Fixed in [${FULL_SHA:0:9}](https://github.com/Significant-Gravitas/AutoGPT/commit/${FULL_SHA}): ..."
) &
(
  sleep 6
  gh api repos/Significant-Gravitas/AutoGPT/pulls/{N}/comments/{ID2}/replies \
    -f body="🤖 Fixed in [${FULL_SHA:0:9}](https://github.com/Significant-Gravitas/AutoGPT/commit/${FULL_SHA}): ..."
) &
wait  # wait for all background replies before resolving

Then resolve sequentially (GraphQL mutations):

for THREAD_ID in "$THREAD1" "$THREAD2" "$THREAD3"; do
  gh api graphql -f query="mutation { resolveReviewThread(input: {threadId: \"${THREAD_ID}\"}) { thread { isResolved } } }"
  sleep 3
done

Always sleep 3s between individual API writes — GitHub's secondary rate limit (403) triggers on bursts of >20 writes. Increase to sleep 5 when posting more than 20 replies in a batch.

Resolving threads via GraphQL

Use resolveReviewThread only after the commit is pushed and the reply is posted:

gh api graphql -f query='mutation { resolveReviewThread(input: {threadId: "THREAD_ID"}) { thread { isResolved } } }'

Never call this mutation before committing the fix. The orchestrator will verify actual unresolved counts via GraphQL after you output ORCHESTRATOR:DONE — false resolutions will be caught and you will be re-briefed.

resolveReviewThread is GraphQL-only — no REST equivalent. If GraphQL is rate-limited, see GitHub rate limits for the queue-and-retry flow.

Verify actual count before outputting ORCHESTRATOR:DONE

Before claiming "0 unresolved threads", always query GitHub directly — don't rely on your own bookkeeping. Paginate all pages — a single first: 100 query misses threads beyond page 1:

# Step 1: get total thread count
gh api graphql -f query='
{
  repository(owner: "Significant-Gravitas", name: "AutoGPT") {
    pullRequest(number: {N}) {
      reviewThreads { totalCount }
    }
  }
}' | jq '.data.repository.pullRequest.reviewThreads.totalCount'

# Step 2: paginate all pages, count truly unresolved
CURSOR=""; UNRESOLVED=0
while true; do
  AFTER=${CURSOR:+", after: \"$CURSOR\""}
  PAGE=$(gh api graphql -f query="
  {
    repository(owner: \"Significant-Gravitas\", name: \"AutoGPT\") {
      pullRequest(number: {N}) {
        reviewThreads(first: 100${AFTER}) {
          pageInfo { hasNextPage endCursor }
          nodes { isResolved }
        }
      }
    }
  }")
  UNRESOLVED=$(( UNRESOLVED + $(echo "$PAGE" | jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved==false)] | length') ))
  HAS_NEXT=$(echo "$PAGE" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.hasNextPage')
  CURSOR=$(echo "$PAGE" | jq -r '.data.repository.pullRequest.reviewThreads.pageInfo.endCursor')
  [ "$HAS_NEXT" = "false" ] && break
done
echo "Unresolved threads: $UNRESOLVED"

Only output ORCHESTRATOR:DONE after this loop reports 0.

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

open-pr

무료요약 있음

코드 변경의 테스트와 기존 기능에 미치는 영향을 확인하고 저장소 템플릿에 맞춰 PR을 생성하며, 리뷰 요청부터 지적 사항 대응까지 진행합니다.

  • 지정된 템플릿으로 PR 생성
  • 기존 동작과 새 기능의 테스트 확인
  • 로컬 검증이 어려울 때 봇 리뷰 요청
Significant-Gravitas/AutoGPT18.8만2026년 10월 23일 업데이트

orchestrate

무료요약 있음

별도 작업 폴더에서 여러 Claude Code를 실행하여 개발 작업 배정, 진행 상황 확인, 중단된 세션 복구 지원, PR 완료 확인을 함께 관리합니다.

  • 여러 에이전트에 개발 작업 분담
  • 진행 상황과 빈 worktree 확인
  • 중단된 에이전트 재개
Significant-Gravitas/AutoGPT18.8만2026년 10월 23일 업데이트

perf-compare

무료요약 있음

Sentry 데이터로 변경 전후나 환경 간 속도와 실패율을 비교합니다. 통계적 변동을 고려해 빨라짐, 느려짐, 변화 감지 안 됨, 데이터 부족을 판정합니다.

  • 릴리스 전후 성능 비교
  • 미리 보기 환경과 대조 환경 검증
  • 다른 처리의 성능 저하 확인
Significant-Gravitas/AutoGPT18.8만2026년 10월 23일 업데이트

pr-polish

무료요약 있음

PR 검토와 수정을 반복하며 미해결 의견, CI 실패, 병합 충돌을 확인합니다. 늦게 도착하는 댓글도 기다려 PR을 병합할 수 있는 상태로 정리합니다.

  • PR을 병합 가능한 상태로 정리
  • 새 지적이 없어질 때까지 수정
  • 늦게 도착하는 봇 검토에 대응
Significant-Gravitas/AutoGPT18.8만2026년 10월 23일 업데이트

pr-review

무료요약 있음

코드 변경 제안인 PR의 목적과 차이를 읽고 정확성, 보안, 설계, 테스트를 점검합니다. 기존 의견과 중복되지 않도록 심각도를 붙여 해당 코드 줄에 댓글을 게시합니다.

  • 병합 전 PR 검토
  • 코드 변경의 보안 점검
  • 경계 조건의 테스트 누락 확인
Significant-Gravitas/AutoGPT18.8만2026년 10월 23일 업데이트

pr-test

무료요약 있음

실행 중인 시스템에서 PR이나 브랜치 변경을 조작하고 화면, API, 저장 데이터의 변화를 대조하여 작업 전후 스크린샷과 함께 검증 결과를 보고합니다.

  • 화면과 API로 PR 전체 흐름 테스트
  • 화면, API, 저장 데이터의 변화 대조
  • 잘못된 입력 등 예외 상황 검증
Significant-Gravitas/AutoGPT18.8만2026년 10월 23일 업데이트

Significant-Gravitas의 스킬 모두 보기

이 스킬의 문제 신고