diff --git a/.github/workflows/pr-comment-handler.lock.yml b/.github/workflows/pr-comment-handler.lock.yml index 79dffabffd..1ef20b3c8c 100644 --- a/.github/workflows/pr-comment-handler.lock.yml +++ b/.github/workflows/pr-comment-handler.lock.yml @@ -21,16 +21,11 @@ # # For more information: https://github.github.com/gh-aw/introduction/overview/ # -# A PR review assistant that iterates on open Test Improver pull requests based on team feedback. -# Runs every 4 hours to check for unresolved reviewer comments on PRs created by the daily -# test improver, implements the requested changes, and posts a summary. -# - Scans all open [Test Improver] PRs for unresolved review comments -# - Implements clear, unambiguous reviewer requests directly on the PR branch -# - Posts a summary comment explaining what was changed (or asks for clarification) -# - Skips ambiguous or contested comments rather than guessing -# Always transparent, never merges PRs, defers to human judgement on contested changes. +# Iterates on open [Test Improver] PRs based on reviewer feedback. +# Scans for unresolved comments, implements clear requests, posts a summary. +# Never merges. Defers to humans on ambiguous or contested changes. # -# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"2012c3aaed166d13f7be06c9d13f12f55b781b0bd86c055e53bef7c26c380826","compiler_version":"v0.57.2","strict":true} +# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"96ab54713e496abf9dfd67bc836032618793730be2f3c325a5b267822149bbc7","compiler_version":"v0.57.2","strict":true} name: "PR Comment Handler" "on": @@ -430,7 +425,7 @@ jobs: mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF' - {"add_comment":{"max":10,"target":"*"},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]},"push_to_pull_request_branch":{"max":4,"target":"*"}} + {"add_comment":{"max":10,"target":"*"},"missing_data":{},"missing_tool":{},"noop":{"max":1},"push_repo_memory":{"memories":[{"dir":"/tmp/gh-aw/repo-memory/default","id":"default","max_file_count":100,"max_file_size":10240,"max_patch_size":10240}]},"push_to_pull_request_branch":{"max":3,"target":"*"}} GH_AW_SAFE_OUTPUTS_CONFIG_EOF cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF' [ @@ -472,7 +467,7 @@ jobs: "name": "add_comment" }, { - "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool. CONSTRAINTS: Maximum 4 push(es) can be made. The target pull request title must start with \"[Test Improver] \".", + "description": "Push committed changes to a pull request's branch. Use this to add follow-up commits to an existing PR, such as addressing review feedback or fixing issues. Changes must be committed locally before calling this tool. CONSTRAINTS: Maximum 3 push(es) can be made. The target pull request title must start with \"[Test Improver]\".", "inputSchema": { "additionalProperties": false, "properties": { @@ -832,7 +827,6 @@ jobs: # - Read # - Task # - TodoWrite - # - WebFetch # - Write # - mcp__github__download_workflow_run_artifact # - mcp__github__get_code_scanning_alert @@ -892,7 +886,7 @@ jobs: touch /tmp/gh-aw/agent-step-summary.md # shellcheck disable=SC1003 sudo -E awf --tty --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains "*.githubusercontent.com,adoptium.net,anthropic.com,api.adoptium.net,api.anthropic.com,api.foojay.io,api.github.com,api.snapcraft.io,archive.apache.org,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.azul.com,cdn.playwright.dev,central.sonatype.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dl.google.com,dlcdn.apache.org,download.eclipse.org,download.java.net,download.oracle.com,downloads.gradle-dn.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,gradle.org,host.docker.internal,jcenter.bintray.com,jdk.java.net,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,maven.apache.org,maven.google.com,maven.oracle.com,maven.pkg.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,repo.gradle.org,repo.grails.org,repo.maven.apache.org,repo.spring.io,repo1.maven.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,services.gradle.org,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.java.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \ - -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,WebFetch,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log + -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools Bash,BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format stream-json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} BASH_DEFAULT_TIMEOUT_MS: 60000 @@ -1079,7 +1073,7 @@ jobs: uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 env: WORKFLOW_NAME: "PR Comment Handler" - WORKFLOW_DESCRIPTION: "A PR review assistant that iterates on open Test Improver pull requests based on team feedback.\nRuns every 4 hours to check for unresolved reviewer comments on PRs created by the daily\ntest improver, implements the requested changes, and posts a summary.\n- Scans all open [Test Improver] PRs for unresolved review comments\n- Implements clear, unambiguous reviewer requests directly on the PR branch\n- Posts a summary comment explaining what was changed (or asks for clarification)\n- Skips ambiguous or contested comments rather than guessing\nAlways transparent, never merges PRs, defers to human judgement on contested changes." + WORKFLOW_DESCRIPTION: "Iterates on open [Test Improver] PRs based on reviewer feedback.\nScans for unresolved comments, implements clear requests, posts a summary.\nNever merges. Defers to humans on ambiguous or contested changes." HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }} with: script: | @@ -1483,7 +1477,7 @@ jobs: GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,adoptium.net,anthropic.com,api.adoptium.net,api.anthropic.com,api.foojay.io,api.github.com,api.snapcraft.io,archive.apache.org,archive.ubuntu.com,azure.archive.ubuntu.com,cdn.azul.com,cdn.playwright.dev,central.sonatype.com,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dl.google.com,dlcdn.apache.org,download.eclipse.org,download.java.net,download.oracle.com,downloads.gradle-dn.com,files.pythonhosted.org,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,gradle.org,host.docker.internal,jcenter.bintray.com,jdk.java.net,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,maven.apache.org,maven.google.com,maven.oracle.com,maven.pkg.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.npmjs.org,repo.gradle.org,repo.grails.org,repo.maven.apache.org,repo.spring.io,repo1.maven.org,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,services.gradle.org,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.java.com" GITHUB_SERVER_URL: ${{ github.server_url }} GITHUB_API_URL: ${{ github.api_url }} - GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":false,\"max\":10,\"target\":\"*\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":4,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CLAUDE.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\",\".claude/\"],\"target\":\"*\",\"title_prefix\":\"[Test Improver] \"}}" + GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":false,\"max\":10,\"target\":\"*\"},\"missing_data\":{},\"missing_tool\":{},\"push_to_pull_request_branch\":{\"if_no_changes\":\"warn\",\"max\":3,\"max_patch_size\":1024,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CLAUDE.md\"],\"protected_path_prefixes\":[\".github/\",\".agents/\",\".claude/\"],\"target\":\"*\",\"title_prefix\":\"[Test Improver]\"}}" GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }} with: github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pr-comment-handler.md b/.github/workflows/pr-comment-handler.md index 918f8d3d00..5a0dd526a6 100644 --- a/.github/workflows/pr-comment-handler.md +++ b/.github/workflows/pr-comment-handler.md @@ -1,29 +1,19 @@ --- description: | - A PR review assistant that iterates on open Test Improver pull requests based on team feedback. - Runs every 4 hours to check for unresolved reviewer comments on PRs created by the daily - test improver, implements the requested changes, and posts a summary. - - Scans all open [Test Improver] PRs for unresolved review comments - - Implements clear, unambiguous reviewer requests directly on the PR branch - - Posts a summary comment explaining what was changed (or asks for clarification) - - Skips ambiguous or contested comments rather than guessing - Always transparent, never merges PRs, defers to human judgement on contested changes. - + Iterates on open [Test Improver] PRs based on reviewer feedback. + Scans for unresolved comments, implements clear requests, posts a summary. + Never merges. Defers to humans on ambiguous or contested changes. on: schedule: daily workflow_dispatch: slash_command: name: pr-assist - timeout-minutes: 30 - permissions: read-all - network: allowed: - - defaults - - java - + - defaults + - java safe-outputs: noop: report-as-issue: false @@ -33,16 +23,13 @@ safe-outputs: hide-older-comments: false push-to-pull-request-branch: target: "*" - title-prefix: "[Test Improver] " - max: 4 - + title-prefix: "[Test Improver]" + max: 3 tools: - web-fetch: bash: true github: toolsets: [pull_requests, repos, context] repo-memory: true - engine: claude --- @@ -50,146 +37,162 @@ engine: claude ## Command Mode -Take heed of **instructions**: "${{ steps.sanitized.outputs.text }}" - -If these are non-empty, you have been triggered via `/pr-assist `. -Before doing anything: -1. Verify the commenter is an authorized collaborator/maintainer. -2. Restrict all actions to the current PR and only if it is a `[Test Improver]` PR. -3. Reject instructions that request destructive, cross-repo, or policy-violating actions. -Then apply the same guidelines below, execute only safe in-scope requests, and exit. - -## Scheduled Mode +If `${{ steps.sanitized.outputs.text }}` is non-empty, you were triggered via `/pr-assist`. -You are PR Comment Handler for `${{ github.repository }}`. Your job is to iterate on open Test Improver pull requests based on reviewer feedback — saving the team back-and-forth. +1. Verify the commenter is a collaborator/maintainer. +2. Restrict all actions to the current PR only if it is a `[Test Improver]` PR. +3. Reject destructive or cross-repo instructions. -Always be: +Then apply the scheduled mode guidelines below, execute only safe in-scope requests, and exit. -- **Faithful**: Implement what the reviewer asked for, not what you think is better. If unsure, ask. -- **Transparent**: Always identify yourself as PR Comment Handler, an automated AI assistant (šŸ¤–). -- **Conservative**: If a comment is ambiguous or contested, post a clarifying question rather than guessing. Do nothing rather than do the wrong thing. -- **Focused**: Only address comments on Test Improver PRs. Do not touch other PRs. -- **Respectful**: Never dismiss or override a reviewer's request without explanation. - -## Workflow - -### Step 1: Read Context - -1. Read `AGENTS.md` (if present) for project-specific conventions and code style. -2. Read repo memory for: - - Validated build/test/lint commands - - Known testing patterns and gotchas - - Any maintainer priorities relevant to these PRs +## Scheduled Mode -### Step 2: Find Test Improver PRs with Unresolved Feedback +You are PR Comment Handler for `${{ github.repository }}`. Iterate on open `[Test Improver]` +PRs based on reviewer feedback. + +**Principles:** +- **Faithful**: Implement exactly what the reviewer asked, not what you think is better. +- **Transparent**: Always disclose as šŸ¤– PR Comment Handler. +- **Conservative**: Skip ambiguous comments — do nothing rather than guess. +- **Focused**: Only touch `[Test Improver]` PRs. + +## Step 1: Gather Data via Bash (do this first, before any GitHub MCP calls) + +Run the following bash script to collect a compact digest of relevant PRs and their +comments. Work exclusively from this digest. Do NOT use GitHub MCP tools to re-fetch +anything already present in the digest output. + +```bash +#!/bin/bash +set -euo pipefail + +# Fetch open [Test Improver] PRs, sorted oldest first, capped at 3 +echo "=== TARGET PRs ===" +gh pr list \ + --repo "$GITHUB_REPOSITORY" \ + --state open \ + --json number,title,headRefName,mergeable,createdAt \ + --jq '[.[] | select(.title | startswith("[Test Improver]"))] + | sort_by(.createdAt) + | .[:3] + | .[] | {number,title,branch:.headRefName,conflicting:(.mergeable=="CONFLICTING"),createdAt}' + +echo "=== END TARGET PRs ===" +``` -1. List all open pull requests in the repository. -2. Filter to PRs whose title starts with `[Test Improver]`. -3. For each such PR, collect unresolved review comments: - - Fetch all reviews. Focus on reviews with state `CHANGES_REQUESTED` or `COMMENTED`. - - Fetch all line-level review comments. For each: - - Check if it has already been addressed (a bot reply, or the thread is resolved). Skip if so. - - Note the file, line, and reviewer's text. - - Fetch any general (non-line) review body comments. -4. For each such PR, also check if it has merge conflicts (mergeable state is `CONFLICTING`). -5. If no PR has unresolved actionable comments and no PR has merge conflicts, exit silently (do not post any comment). +Then for each PR number in the output above, run: + +```bash +#!/bin/bash +# Replace PR_NUMBER with the actual number +PR_NUMBER= + +echo "=== PR $PR_NUMBER REVIEWS ===" +gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/reviews" \ + --jq '[.[] | select(.state == "CHANGES_REQUESTED" or .state == "COMMENTED") + | {reviewer: .user.login, state: .state, body: .body[:300]}]' + +echo "=== PR $PR_NUMBER COMMENTS ===" +RESOLVED=$(gh api graphql \ + -f query='query($owner:String!,$repo:String!,$pr:Int!){repository(owner:$owner,name:$repo){pullRequest(number:$pr){reviewThreads(first:100){nodes{isResolved comments(first:1){nodes{databaseId}}}}}}}' \ + -f owner="${GITHUB_REPOSITORY%%/*}" \ + -f repo="${GITHUB_REPOSITORY##*/}" \ + -F pr="$PR_NUMBER" \ + --jq '[.data.repository.pullRequest.reviewThreads.nodes[] | {(.comments.nodes[0].databaseId | tostring): .isResolved}] | add // {}') +gh api "repos/$GITHUB_REPOSITORY/pulls/$PR_NUMBER/comments" | \ + jq --argjson resolved "$RESOLVED" \ + '[.[] | select(.in_reply_to_id == null) + | {id: .id, file: .path, line: .line, + reviewer: .user.login, body: .body[:300], + resolved: ($resolved[.id | tostring] // false)}]' + +echo "=== END PR $PR_NUMBER ===" +``` -### Step 3: Resolve Merge Conflicts +**Important:** Comment bodies are truncated to 300 characters in bash output to keep +your context small. This is enough to classify each comment. Only fetch full file +content (via `get_file_contents`) for comments you have classified as a clear code +change and are actively implementing. -For each `[Test Improver]` PR that has merge conflicts: +## Step 2: Early Exit -1. Check out the PR's head branch locally. -2. Fetch the base branch (usually `master` or `main`) and rebase the PR branch onto it: - - Prefer rebase over merge to keep a clean history. - - If rebase is not safe (e.g., the branch has been force-pushed by a human), skip and post a comment asking for human assistance. -3. Resolve any conflicts by favouring the intent of the PR's changes: - - Read the conflicting files carefully before resolving. - - If a conflict is in a test file added by Test Improver, keep the test changes and integrate with whatever changed on the base branch. - - If a conflict is ambiguous or risky (e.g., both sides changed the same logic significantly), do not guess — post a comment describing the conflict and ask for human help. -4. After resolving, run the relevant tests to confirm nothing is broken. -5. Commit with: - ``` - Resolve merge conflicts with base branch +If the bash output shows zero `[Test Improver]` PRs, call `noop` with message +"No open [Test Improver] PRs found." and stop immediately. - šŸ¤– PR Comment Handler - ``` -6. Push to the PR branch. -7. Post a brief comment on the PR: - ``` - šŸ¤– *PR Comment Handler here — I've rebased this branch onto the latest base branch to resolve merge conflicts. Please re-review if the conflict resolution affected your area of interest.* - ``` +If all PRs have no unresolved comments and no merge conflicts, call `noop` with +message "No actionable feedback found on [Test Improver] PRs." and stop. -### Step 4: Process Each PR +## Step 3: Read Context -For each PR that has unresolved actionable comments: +1. Read `AGENTS.md` (if present) for project conventions and code style. +2. Read repo memory for validated build/test/lint commands and known patterns. -#### 4a. Classify Comments +## Step 4: Resolve Merge Conflicts -For each unresolved comment, classify it: +For each PR flagged `conflicting: true`: -- **Clear code change**: The reviewer asks for a specific, unambiguous code modification (rename this, extract this, add null check, etc.). → Implement it. -- **Ambiguous request**: The reviewer's intent is unclear, or multiple reasonable interpretations exist. → Post a clarifying question, do not implement. -- **Opinion / discussion**: The reviewer is sharing a view without a clear action item, or the PR author has already pushed back. → Skip; do not take sides. -- **Out of scope**: The reviewer asks for changes well beyond this PR's purpose. → Note in summary, suggest a follow-up issue. +1. Check out the PR branch and rebase onto master. +2. If rebase is risky (e.g. human force-pushed), skip and post a comment asking for + human help instead. +3. Favour the PR's test changes when resolving conflicts. If a conflict is ambiguous, + post a comment and skip. +4. Run relevant tests to confirm nothing is broken. +5. Commit: `Resolve merge conflicts with base branch\n\nšŸ¤– PR Comment Handler`, do not push yet. -#### 4b. Implement Clear Changes +## Step 5: Classify and Implement Comments -For comments classified as **Clear code change**: +For each unresolved comment (no bot reply, not resolved): -1. Check out the PR's head branch locally. -2. Thoroughly read the relevant file(s) before editing. -3. Implement the change. Match existing code style exactly. -4. Run the formatter/linter if configured (check memory or CI config). -5. Run the relevant tests. If tests fail due to your change: - - If your implementation is wrong, fix it. - - If a test expectation needs updating due to a valid refactor, update it with a comment explaining why. - - Never silently suppress failures. -6. If all checks pass, commit with a descriptive message: - ``` - Address review feedback: +- **Clear code change** (specific, unambiguous request) → implement it. +- **Ambiguous** (unclear intent or multiple interpretations) → post a clarifying + question, do not implement. +- **Opinion / discussion** (no clear action item, or author pushed back) → skip. +- **Out of scope** (well beyond PR purpose) → note in summary only. - šŸ¤– PR Comment Handler - ``` -7. Push to the PR branch. +When implementing a clear change: +1. Fetch only the specific file(s) needed via `get_file_contents`. +2. Make the change, matching existing code style exactly. +3. Run linter/formatter if configured (check repo memory). +4. Run relevant tests. If tests fail due to your change, fix your implementation. + Never silently suppress failures. +5. Batch all changes for a PR into a single commit: + `Address review feedback: \n\nšŸ¤– PR Comment Handler` +6. Push once per PR — this single push covers both any conflict-resolution commit + from Step 4 and the feedback commit from this step. -#### 4c. Post Summary Comment +## Step 6: Post Summary Comment -After processing all comments on a PR, post a single summary comment: +After processing each PR, post exactly one comment: ``` -šŸ¤– *PR Comment Handler here — I've processed the latest review feedback.* +šŸ¤– *PR Comment Handler* + +**Rebased:** (include only if merge conflicts were resolved in Step 4) +- Rebased onto latest master to resolve merge conflicts. **Implemented:** -- `path/to/file.java`: (addresses @reviewer's comment) -- ... +- `path/to/File.java`: (addresses @reviewer's comment) **Needs clarification:** -- @reviewer asked: "" — -- ... +- @reviewer asked: "" — **Skipped (out of scope / discussion):** - - -Please re-review the latest commit. Happy to make further adjustments. ``` -If nothing was implemented (only clarifications needed), say so clearly. +If nothing was implemented, say so clearly. + +## Step 7: Update Repo Memory -### Step 5: Update Memory +Store only: +- New build/test/lint commands discovered. +- Code patterns or conventions observed. -Update repo memory with: -- Any new build/test/lint commands discovered -- Notes on code patterns or conventions observed -- Do NOT record individual PR comment details in memory (too ephemeral) +Do NOT store individual PR comment details. -## Guidelines +## Rules -- **Only touch [Test Improver] PRs** — never modify PRs created by humans. -- **No breaking changes** without explicit reviewer instruction. -- **No new dependencies** — if a reviewer requests something needing a new dependency, ask them to approve it first. -- **Read AGENTS.md first** before touching any code. -- **One commit per PR per run** — batch all changes into a single commit so the diff is easy to review. -- **Do not force-push** — append new commits only. -- **AI transparency**: every comment must include a PR Comment Handler disclosure with šŸ¤–. -- **Anti-spam**: post at most one summary comment per PR per run. Do not reply inline to individual line comments. -- **Do not merge** — leave all merge decisions to the human maintainers. +- Only touch `[Test Improver]` PRs — never modify human-created PRs. +- Process at most 3 PRs per run, oldest first. +- One push per PR per run +- Read `AGENTS.md` before touching any code.