From 89f2475702d1e409fd49a722c2f24ed09e9f1ad6 Mon Sep 17 00:00:00 2001 From: Sasa Junuzovic Date: Fri, 20 Mar 2026 18:08:10 -0700 Subject: [PATCH] fix: pre-fetch review comments via GraphQL before agent starts The MCP pull_request_read tool returns empty [] for review comments inside the gh-aw agent sandbox. This adds a shared import that runs gh api graphql BEFORE the agent starts, writing unresolved review threads to /tmp/gh-aw/review-data/unresolved-threads.json. Changes: - Add shared/fetch-review-comments.md with GraphQL pre-fetch step - Update review-responder.md to import shared step and read from file - Use databaseId for reply-to-review-comment targeting - Fetch up to 100 comments per thread (pagination tracked in #187) - Fail loudly on jq parse errors instead of silent fallback to [] - Recompile review-responder.lock.yml Closes #180 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/review-responder.lock.yml | 16 +++- .github/workflows/review-responder.md | 7 +- .../workflows/shared/fetch-review-comments.md | 91 +++++++++++++++++++ 3 files changed, 111 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/shared/fetch-review-comments.md diff --git a/.github/workflows/review-responder.lock.yml b/.github/workflows/review-responder.lock.yml index 4e7b882..6911e7b 100644 --- a/.github/workflows/review-responder.lock.yml +++ b/.github/workflows/review-responder.lock.yml @@ -22,7 +22,11 @@ # For more information: https://github.github.com/gh-aw/introduction/overview/ # # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"e9dedf9ee2f5a6beffd74f5c1ac40c9263eda4f41e3db146c13a4d8576fd3970","compiler_version":"v0.60.0","strict":true} +# Resolved workflow manifest: +# Imports: +# - shared/fetch-review-comments.md +# +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"99594989482cadf66785af7f0d8a7a3f7ca7c7826a814c07734bb8942a84eae0","compiler_version":"v0.60.0","strict":true} name: "Review Responder" "on": @@ -170,6 +174,9 @@ jobs: GH_AW_PROMPT_EOF cat << 'GH_AW_PROMPT_EOF' + {{#runtime-import .github/workflows/shared/fetch-review-comments.md}} + GH_AW_PROMPT_EOF + cat << 'GH_AW_PROMPT_EOF' {{#runtime-import .github/workflows/review-responder.md}} GH_AW_PROMPT_EOF } > "$GH_AW_PROMPT" @@ -281,6 +288,13 @@ jobs: git -c "http.extraheader=Authorization: Basic ${header}" fetch origin '+refs/heads/*:refs/remotes/origin/*' - name: Create gh-aw temp directory run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ inputs.pr_number }} + name: Fetch PR review comments + run: "mkdir -p /tmp/gh-aw/review-data\n\nOWNER=\"${GITHUB_REPOSITORY_OWNER}\"\nREPO=\"${GITHUB_REPOSITORY#*/}\"\n\necho \"Fetching review comments for PR #${PR_NUMBER}...\"\n\n# Fetch review comment threads via GraphQL (includes resolution status)\ngh api graphql -f query='\n query($owner: String!, $repo: String!, $pr: Int!) {\n repository(owner: $owner, name: $repo) {\n pullRequest(number: $pr) {\n title\n body\n reviewThreads(first: 100) {\n nodes {\n id\n isResolved\n isOutdated\n comments(first: 100) {\n nodes {\n id\n databaseId\n body\n path\n line\n author { login }\n createdAt\n }\n }\n }\n }\n }\n }\n }' -f owner=\"$OWNER\" -f repo=\"$REPO\" -F pr=\"$PR_NUMBER\" \\\n > /tmp/gh-aw/review-data/threads.json\n\n# Extract unresolved threads for easy agent consumption\nif ! jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments = .comments.nodes]' \\\n /tmp/gh-aw/review-data/threads.json \\\n > /tmp/gh-aw/review-data/unresolved-threads.json; then\n echo \"Error: Failed to extract unresolved review threads from GraphQL response\" >&2\n exit 1\nfi\n\nUNRESOLVED=$(jq 'length' /tmp/gh-aw/review-data/unresolved-threads.json)\necho \"Found ${UNRESOLVED} unresolved thread(s)\"\necho \"Data saved to /tmp/gh-aw/review-data/\"" + - name: Configure Git credentials env: REPO_NAME: ${{ github.repository }} diff --git a/.github/workflows/review-responder.md b/.github/workflows/review-responder.md index 195e489..3c40f86 100644 --- a/.github/workflows/review-responder.md +++ b/.github/workflows/review-responder.md @@ -12,6 +12,9 @@ permissions: issues: read pull-requests: read +imports: + - shared/fetch-review-comments.md + checkout: fetch: ["*"] fetch-depth: 0 @@ -57,13 +60,13 @@ This workflow addresses unresolved review comments on a pull request. 2. Add the label `aw-review-response-attempted` to the PR. -3. Read the unresolved review comment threads on the PR using the GitHub REST API: fetch `https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/comments` and `https://api.github.com/repos/$OWNER/$REPO/pulls/$PR_NUMBER/reviews`. If there are more than 10 unresolved threads, address the first 10 and leave a summary comment on the PR noting how many remain for manual follow-up. +3. Read the pre-fetched unresolved review threads from the file `/tmp/gh-aw/review-data/unresolved-threads.json`. This file was populated before you started by a workflow step that queried the GitHub GraphQL API. Each thread contains an `id`, `comments` array (with `databaseId`, `body`, `path`, `line`, `author`), and resolution status. If the file is empty or contains `[]`, there are no unresolved threads — stop and report via noop. If there are more than 10 unresolved threads, address the first 10 and leave a summary comment on the PR noting how many remain for manual follow-up. 4. For each unresolved review comment thread (up to 10): a. Read the comment and understand what change is being requested b. Read the relevant file and surrounding code context c. Make the requested fix in the code - d. Reply to the comment thread explaining what you changed + d. Reply to the comment thread using `reply_to_pull_request_review_comment` with the comment's `databaseId` as the `comment_id` 5. After addressing all comments, run the CI checks locally to make sure your fixes don't break anything: `uv sync && uv run ruff check --fix . && uv run ruff format . && uv run pyright && uv run pytest --cov --cov-fail-under=80 -v` diff --git a/.github/workflows/shared/fetch-review-comments.md b/.github/workflows/shared/fetch-review-comments.md new file mode 100644 index 0000000..d62ecd5 --- /dev/null +++ b/.github/workflows/shared/fetch-review-comments.md @@ -0,0 +1,91 @@ +--- +steps: + - name: Fetch PR review comments + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ inputs.pr_number }} + run: | + mkdir -p /tmp/gh-aw/review-data + + OWNER="${GITHUB_REPOSITORY_OWNER}" + REPO="${GITHUB_REPOSITORY#*/}" + + echo "Fetching review comments for PR #${PR_NUMBER}..." + + # Fetch review comment threads via GraphQL (includes resolution status) + gh api graphql -f query=' + query($owner: String!, $repo: String!, $pr: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $pr) { + title + body + reviewThreads(first: 100) { + nodes { + id + isResolved + isOutdated + comments(first: 100) { + nodes { + id + databaseId + body + path + line + author { login } + createdAt + } + } + } + } + } + } + }' -f owner="$OWNER" -f repo="$REPO" -F pr="$PR_NUMBER" \ + > /tmp/gh-aw/review-data/threads.json + + # Extract unresolved threads for easy agent consumption + if ! jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .comments = .comments.nodes]' \ + /tmp/gh-aw/review-data/threads.json \ + > /tmp/gh-aw/review-data/unresolved-threads.json; then + echo "Error: Failed to extract unresolved review threads from GraphQL response" >&2 + exit 1 + fi + + UNRESOLVED=$(jq 'length' /tmp/gh-aw/review-data/unresolved-threads.json) + echo "Found ${UNRESOLVED} unresolved thread(s)" + echo "Data saved to /tmp/gh-aw/review-data/" +--- + +