From d01bd475c56bac871210f11c0745db9f5b90c90d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 15:39:34 +0000 Subject: [PATCH 1/8] Initial plan From da9e1858781f39698a46a385388897cba5665ddb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 15:47:07 +0000 Subject: [PATCH 2/8] Add rate-limit PR detector workflow and skill Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../workflows/rate-limit-pr-detector.lock.yml | 454 ++++++++++++++++++ .github/workflows/rate-limit-pr-detector.md | 136 ++++++ skills/detect-rate-limited-prs/SKILL.md | 151 ++++++ .../detect-rate-limited-prs.sh | 249 ++++++++++ 4 files changed, 990 insertions(+) create mode 100644 .github/workflows/rate-limit-pr-detector.lock.yml create mode 100644 .github/workflows/rate-limit-pr-detector.md create mode 100644 skills/detect-rate-limited-prs/SKILL.md create mode 100755 skills/detect-rate-limited-prs/detect-rate-limited-prs.sh diff --git a/.github/workflows/rate-limit-pr-detector.lock.yml b/.github/workflows/rate-limit-pr-detector.lock.yml new file mode 100644 index 00000000000..fdd42b0c100 --- /dev/null +++ b/.github/workflows/rate-limit-pr-detector.lock.yml @@ -0,0 +1,454 @@ +# +# ___ _ _ +# / _ \ | | (_) +# | |_| | __ _ ___ _ __ | |_ _ ___ +# | _ |/ _` |/ _ \ '_ \| __| |/ __| +# | | | | (_| | __/ | | | |_| | (__ +# \_| |_/\__, |\___|_| |_|\__|_|\___| +# __/ | +# _ _ |___/ +# | | | | / _| | +# | | | | ___ _ __ _ __| |_| | _____ ____ +# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| +# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ +# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ +# +# This file was automatically generated by gh-aw. DO NOT EDIT. +# +# To update this file, edit the corresponding .md file and run: +# gh aw compile +# For more information: https://github.com/github/gh-aw/blob/main/.github/aw/github-agentic-workflows.md +# +# Detect and report PRs that failed due to rate limiting +# +# frontmatter-hash: 11ab7f7382f6ba04045d6cce2e889c206b992ad642eb4f03a77473f155545728 + +name: "Rate Limit PR Detector" +"on": + workflow_dispatch: + +permissions: {} + +concurrency: + group: "gh-aw-${{ github.workflow }}" + +run-name: "Rate Limit PR Detector" + +jobs: + activation: + runs-on: ubuntu-slim + permissions: + contents: read + outputs: + comment_id: "" + comment_repo: "" + steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false + - name: Setup Scripts + uses: ./actions/setup + with: + destination: /opt/gh-aw/actions + - name: Check workflow file timestamps + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_WORKFLOW_FILE: "rate-limit-pr-detector.lock.yml" + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); + await main(); + + agent: + needs: activation + runs-on: ubuntu-latest + permissions: + contents: read + issues: read + pull-requests: read + concurrency: + group: "gh-aw-copilot-${{ github.workflow }}" + outputs: + checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} + model: ${{ steps.generate_aw_info.outputs.model }} + secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} + steps: + - name: Checkout actions folder + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + sparse-checkout: | + actions + persist-credentials: false + - name: Setup Scripts + uses: ./actions/setup + with: + destination: /opt/gh-aw/actions + - name: Checkout repository + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 + with: + persist-credentials: false + - name: Create gh-aw temp directory + run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh + - name: Configure Git credentials + env: + REPO_NAME: ${{ github.repository }} + SERVER_URL: ${{ github.server_url }} + run: | + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git config --global user.name "github-actions[bot]" + # Re-authenticate git with GitHub token + SERVER_URL_STRIPPED="${SERVER_URL#https://}" + git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" + echo "Git configured with standard GitHub Actions identity" + - name: Checkout PR branch + id: checkout-pr + if: | + github.event.pull_request + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + with: + github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); + await main(); + - name: Validate COPILOT_GITHUB_TOKEN secret + id: validate-secret + run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default + env: + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + - name: Install GitHub Copilot CLI + run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.405 + - name: Install awf binary + run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.13.12 + - name: Determine automatic lockdown mode for GitHub MCP server + id: determine-automatic-lockdown + env: + TOKEN_CHECK: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + if: env.TOKEN_CHECK != '' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); + await determineAutomaticLockdown(github, context, core); + - name: Download container images + run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.13.12 ghcr.io/github/gh-aw-firewall/squid:0.13.12 ghcr.io/github/gh-aw-mcpg:v0.0.103 ghcr.io/github/github-mcp-server:v0.30.3 + - name: Start MCP gateway + id: start-mcp-gateway + env: + GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} + GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} + run: | + set -eo pipefail + mkdir -p /tmp/gh-aw/mcp-config + + # Export gateway environment variables for MCP config and gateway script + export MCP_GATEWAY_PORT="80" + export MCP_GATEWAY_DOMAIN="host.docker.internal" + MCP_GATEWAY_API_KEY="" + MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') + export MCP_GATEWAY_API_KEY + export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" + mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" + export DEBUG="*" + + # Register API key as secret to mask it from logs + echo "::add-mask::${MCP_GATEWAY_API_KEY}" + export GH_AW_ENGINE="copilot" + export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.0.103' + + mkdir -p /home/runner/.copilot + cat << MCPCONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh + { + "mcpServers": { + "github": { + "type": "stdio", + "container": "ghcr.io/github/github-mcp-server:v0.30.3", + "env": { + "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", + "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", + "GITHUB_READ_ONLY": "1", + "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" + } + } + }, + "gateway": { + "port": $MCP_GATEWAY_PORT, + "domain": "${MCP_GATEWAY_DOMAIN}", + "apiKey": "${MCP_GATEWAY_API_KEY}", + "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" + } + } + MCPCONFIG_EOF + - name: Generate agentic run info + id: generate_aw_info + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const fs = require('fs'); + + const awInfo = { + engine_id: "copilot", + engine_name: "GitHub Copilot CLI", + model: "gpt-5.1-codex-mini", + version: "", + agent_version: "0.0.405", + workflow_name: "Rate Limit PR Detector", + experimental: false, + supports_tools_allowlist: true, + supports_http_transport: true, + run_id: context.runId, + run_number: context.runNumber, + run_attempt: process.env.GITHUB_RUN_ATTEMPT, + repository: context.repo.owner + '/' + context.repo.repo, + ref: context.ref, + sha: context.sha, + actor: context.actor, + event_name: context.eventName, + staged: false, + allowed_domains: ["defaults"], + firewall_enabled: true, + awf_version: "v0.13.12", + awmg_version: "v0.0.103", + steps: { + firewall: "squid" + }, + created_at: new Date().toISOString() + }; + + // Write to /tmp/gh-aw directory to avoid inclusion in PR + const tmpPath = '/tmp/gh-aw/aw_info.json'; + fs.writeFileSync(tmpPath, JSON.stringify(awInfo, null, 2)); + console.log('Generated aw_info.json at:', tmpPath); + console.log(JSON.stringify(awInfo, null, 2)); + + // Set model as output for reuse in other steps/jobs + core.setOutput('model', awInfo.model); + - name: Generate workflow overview + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs'); + await generateWorkflowOverview(core); + - name: Create prompt with built-in context + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + run: | + bash /opt/gh-aw/actions/create_prompt_first.sh + cat << 'PROMPT_EOF' > "$GH_AW_PROMPT" + + PROMPT_EOF + cat "/opt/gh-aw/prompts/temp_folder_prompt.md" >> "$GH_AW_PROMPT" + cat "/opt/gh-aw/prompts/markdown.md" >> "$GH_AW_PROMPT" + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + + The following GitHub context information is available for this workflow: + {{#if __GH_AW_GITHUB_ACTOR__ }} + - **actor**: __GH_AW_GITHUB_ACTOR__ + {{/if}} + {{#if __GH_AW_GITHUB_REPOSITORY__ }} + - **repository**: __GH_AW_GITHUB_REPOSITORY__ + {{/if}} + {{#if __GH_AW_GITHUB_WORKSPACE__ }} + - **workspace**: __GH_AW_GITHUB_WORKSPACE__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} + - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} + - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} + - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ + {{/if}} + {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} + - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ + {{/if}} + {{#if __GH_AW_GITHUB_RUN_ID__ }} + - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ + {{/if}} + + + PROMPT_EOF + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + + PROMPT_EOF + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + {{#runtime-import .github/workflows/rate-limit-pr-detector.md}} + PROMPT_EOF + - name: Substitute placeholders + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GH_AW_GITHUB_ACTOR: ${{ github.actor }} + GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} + GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} + GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} + GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} + with: + script: | + const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); + + // Call the substitution function + return await substitutePlaceholders({ + file: process.env.GH_AW_PROMPT, + substitutions: { + GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, + GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, + GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, + GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, + GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, + GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, + GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, + GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE + } + }); + - name: Interpolate variables and render templates + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); + await main(); + - name: Validate prompt placeholders + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh + - name: Print prompt + env: + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + run: bash /opt/gh-aw/actions/print_prompt_summary.sh + - name: Execute GitHub Copilot CLI + id: agentic_execution + # Copilot CLI tool arguments (sorted): + timeout-minutes: 15 + run: | + set -o pipefail + sudo -E awf --enable-chroot --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.13.12 --skip-pull \ + -- '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --model gpt-5.1-codex-mini --allow-all-tools --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' \ + 2>&1 | tee /tmp/gh-aw/agent-stdio.log + env: + COPILOT_AGENT_RUNNER_TYPE: STANDALONE + COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json + GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_REF_NAME: ${{ github.ref_name }} + GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} + GITHUB_WORKSPACE: ${{ github.workspace }} + XDG_CONFIG_HOME: /home/runner + - name: Copy Copilot session state files to logs + if: always() + continue-on-error: true + run: | + # Copy Copilot session state files to logs folder for artifact collection + # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them + SESSION_STATE_DIR="$HOME/.copilot/session-state" + LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" + + if [ -d "$SESSION_STATE_DIR" ]; then + echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" + mkdir -p "$LOGS_DIR" + cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true + echo "Session state files copied successfully" + else + echo "No session-state directory found at $SESSION_STATE_DIR" + fi + - name: Stop MCP gateway + if: always() + continue-on-error: true + env: + MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} + MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} + GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} + run: | + bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" + - name: Redact secrets in logs + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); + await main(); + env: + GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' + SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} + SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} + SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} + SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Upload engine output files + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: agent_outputs + path: | + /tmp/gh-aw/sandbox/agent/logs/ + /tmp/gh-aw/redacted-urls.log + if-no-files-found: ignore + - name: Parse agent logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + env: + GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); + await main(); + - name: Parse MCP gateway logs for step summary + if: always() + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 + with: + script: | + const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io); + const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); + await main(); + - name: Print firewall logs + if: always() + continue-on-error: true + env: + AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs + run: | + # Fix permissions on firewall logs so they can be uploaded as artifacts + # AWF runs with sudo, creating files owned by root + sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true + awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" + - name: Upload agent artifacts + if: always() + continue-on-error: true + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: agent-artifacts + path: | + /tmp/gh-aw/aw-prompts/prompt.txt + /tmp/gh-aw/aw_info.json + /tmp/gh-aw/mcp-logs/ + /tmp/gh-aw/sandbox/firewall/logs/ + /tmp/gh-aw/agent-stdio.log + /tmp/gh-aw/agent/ + if-no-files-found: ignore + diff --git a/.github/workflows/rate-limit-pr-detector.md b/.github/workflows/rate-limit-pr-detector.md new file mode 100644 index 00000000000..7e97c84c83c --- /dev/null +++ b/.github/workflows/rate-limit-pr-detector.md @@ -0,0 +1,136 @@ +--- +name: Rate Limit PR Detector +description: Detect and report PRs that failed due to rate limiting +on: + workflow_dispatch: + +permissions: + contents: read + pull-requests: read + issues: read + +engine: + id: copilot + model: gpt-5.1-codex-mini + +timeout-minutes: 15 + +tools: + github: + toolsets: [default, pull_requests] +--- + +# Rate Limit PR Detector + +Your mission is to detect pull requests that have failed due to rate limiting and provide a comprehensive report. + +## Background + +Some Copilot-created PRs fail because the GitHub API rate limit is exceeded during execution. These PRs typically have: +- Comments from the Copilot agent or GitHub Actions bot indicating rate limiting +- Specific error messages containing "rate limit" or "API rate limit exceeded" +- Failed workflow runs with rate limit errors + +## Your Task + +1. **Query Recent PRs**: Search for recent pull requests (last 30 days) from the Copilot agent + - Focus on PRs authored by `app/copilot-swe-agent` or similar Copilot bots + - Include both open and closed PRs + +2. **Examine Timeline Events**: For each PR, check the timeline for: + - Comments containing "rate limit", "API rate limit", or "rate limited" + - Workflow run failures with rate limit messages + - GitHub Actions bot comments about rate limiting + +3. **Identify Rate-Limited PRs**: Create a list of PRs that show evidence of rate limiting + +4. **Generate Report**: Create a summary report that includes: + - PR number and title + - When the rate limiting occurred + - The specific error message or comment + - Current PR state (open/closed/merged) + - Suggestions for prevention or retry + +## Search Query Pattern + +Use the GitHub search API or GraphQL to find PRs. Example search query: +``` +is:pr author:app/copilot-swe-agent created:>2026-01-01 +``` + +## GraphQL Query for Timeline + +Use GraphQL to fetch PR timeline events: +```graphql +query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $number) { + number + title + state + author { + login + } + timelineItems(first: 100, itemTypes: [ISSUE_COMMENT, CLOSED_EVENT]) { + nodes { + __typename + ... on IssueComment { + author { + login + } + body + createdAt + } + } + } + } + } +} +``` + +## Detection Criteria + +A PR is considered rate-limited if it contains any of these patterns: +- "rate limit" (case-insensitive) +- "API rate limit exceeded" +- "403" with "rate" or "limit" +- "secondary rate limit" +- "abuse detection" + +## Example PR + +Reference PR #14368 as an example of a rate-limited PR to understand the pattern. + +## Output Format + +Present your findings in a structured format: + +``` +# Rate-Limited PRs Report + +## Summary +- Total PRs examined: X +- Rate-limited PRs found: Y +- Date range: [start] to [end] + +## Rate-Limited PRs + +### PR #XXXXX: [Title] +- **State**: [open/closed/merged] +- **Created**: [date] +- **Rate Limit Event**: [date] +- **Error Message**: [excerpt from comment/log] +- **URL**: [PR URL] + +## Recommendations +[Your suggestions for preventing or handling rate-limited PRs] +``` + +## Important Notes + +- Be thorough but efficient - don't fetch more data than needed +- Use pagination if there are many PRs to examine +- Focus on recent PRs (last 30 days) unless asked otherwise +- Provide actionable insights in your recommendations + +Good luck! 🔍 diff --git a/skills/detect-rate-limited-prs/SKILL.md b/skills/detect-rate-limited-prs/SKILL.md new file mode 100644 index 00000000000..cc2c2735ae5 --- /dev/null +++ b/skills/detect-rate-limited-prs/SKILL.md @@ -0,0 +1,151 @@ +--- +name: detect-rate-limited-prs +description: Detect and analyze PRs that failed due to GitHub API rate limiting +--- + +# Detect Rate-Limited PRs Skill + +This skill helps identify pull requests that have failed or encountered issues due to GitHub API rate limiting. + +## Overview + +GitHub API rate limiting can cause Copilot PRs and automated workflows to fail. This skill provides utilities to: +- Search for PRs with rate limit indicators +- Analyze PR timeline events for rate limit messages +- Generate reports on rate-limited PRs + +## Usage + +### Basic Detection + +To search for rate-limited PRs in the current repository: + +```bash +./detect-rate-limited-prs.sh +``` + +### With Custom Date Range + +Search for rate-limited PRs created after a specific date: + +```bash +./detect-rate-limited-prs.sh --since 2026-01-01 +``` + +### Specific Repository + +Search in a different repository: + +```bash +./detect-rate-limited-prs.sh --repo github/gh-aw +``` + +### Detailed Analysis + +Get detailed timeline analysis for a specific PR: + +```bash +./detect-rate-limited-prs.sh --pr 14368 +``` + +## Detection Patterns + +The script looks for these indicators of rate limiting: + +1. **Comment Patterns**: + - "rate limit" + - "API rate limit exceeded" + - "secondary rate limit" + - "abuse detection" + +2. **HTTP Status Codes**: + - 403 (Forbidden) with rate limit context + - 429 (Too Many Requests) + +3. **GitHub Actions Failures**: + - Workflow runs that failed with rate limit errors + - GitHub Actions bot comments about rate limiting + +## Search Query Examples + +### Find Copilot PRs from Last 30 Days + +``` +is:pr author:app/copilot-swe-agent created:>2026-01-07 +``` + +### Find Closed PRs with Comments + +``` +is:pr is:closed comments:>0 created:>2026-01-01 +``` + +### GraphQL Query for PR Timeline + +```graphql +query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $number) { + number + title + state + timelineItems(first: 100, itemTypes: [ISSUE_COMMENT]) { + nodes { + __typename + ... on IssueComment { + author { login } + body + createdAt + } + } + } + } + } +} +``` + +## Output Format + +The script outputs JSON with rate-limited PR information: + +```json +{ + "rate_limited_prs": [ + { + "number": 14368, + "title": "PR Title", + "state": "closed", + "created_at": "2026-01-15T10:00:00Z", + "rate_limit_detected_at": "2026-01-15T10:05:00Z", + "error_message": "API rate limit exceeded", + "url": "https://github.com/github/gh-aw/pull/14368" + } + ], + "total_examined": 50, + "total_rate_limited": 1 +} +``` + +## Requirements + +- GitHub CLI (`gh`) authenticated with appropriate permissions +- `jq` for JSON processing +- `pull-requests: read` permission + +## Example Workflow Integration + +Use this skill in a workflow: + +```yaml +--- +name: Check Rate Limited PRs +on: workflow_dispatch + +jobs: + detect: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Run detection + run: ./skills/detect-rate-limited-prs/detect-rate-limited-prs.sh +``` diff --git a/skills/detect-rate-limited-prs/detect-rate-limited-prs.sh b/skills/detect-rate-limited-prs/detect-rate-limited-prs.sh new file mode 100755 index 00000000000..3d7ddfba0d3 --- /dev/null +++ b/skills/detect-rate-limited-prs/detect-rate-limited-prs.sh @@ -0,0 +1,249 @@ +#!/bin/bash +# Detect PRs that failed due to GitHub API rate limiting +# +# Usage: ./detect-rate-limited-prs.sh [OPTIONS] +# +# Options: +# --repo OWNER/REPO Repository to query (default: current repo) +# --since DATE Only check PRs created after this date (ISO 8601) +# --pr NUMBER Analyze a specific PR number +# --state STATE PR state: open, closed, all (default: all) +# --limit N Maximum number of PRs to check (default: 50) + +set -e + +# Default values +REPO="" +SINCE_DATE="" +SPECIFIC_PR="" +STATE="all" +LIMIT=50 + +# Parse arguments +while [[ $# -gt 0 ]]; do + case $1 in + --repo) + REPO="$2" + shift 2 + ;; + --since) + SINCE_DATE="$2" + shift 2 + ;; + --pr) + SPECIFIC_PR="$2" + shift 2 + ;; + --state) + STATE="$2" + shift 2 + ;; + --limit) + LIMIT="$2" + shift 2 + ;; + *) + echo "Unknown option: $1" >&2 + exit 1 + ;; + esac +done + +# Function to check if a PR has rate limit indicators in its timeline +check_pr_for_rate_limiting() { + local pr_number=$1 + local repo_arg="" + + if [[ -n "$REPO" ]]; then + repo_arg="--repo $REPO" + fi + + # Get the repository owner and name + if [[ -n "$REPO" ]]; then + OWNER=$(echo "$REPO" | cut -d'/' -f1) + REPO_NAME=$(echo "$REPO" | cut -d'/' -f2) + else + OWNER=$(gh repo view --json owner --jq '.owner.login') + REPO_NAME=$(gh repo view --json name --jq '.name') + fi + + # Use GraphQL to fetch PR timeline with comments + TIMELINE_RESULT=$(gh api graphql -f query=" +query { + repository(owner: \"$OWNER\", name: \"$REPO_NAME\") { + pullRequest(number: $pr_number) { + number + title + state + createdAt + closedAt + author { + login + } + url + timelineItems(first: 100, itemTypes: [ISSUE_COMMENT, CLOSED_EVENT]) { + nodes { + __typename + ... on IssueComment { + author { + login + } + body + createdAt + } + } + } + } + } +} +") + + # Extract PR info + PR_TITLE=$(echo "$TIMELINE_RESULT" | jq -r '.data.repository.pullRequest.title') + PR_STATE=$(echo "$TIMELINE_RESULT" | jq -r '.data.repository.pullRequest.state') + PR_CREATED=$(echo "$TIMELINE_RESULT" | jq -r '.data.repository.pullRequest.createdAt') + PR_URL=$(echo "$TIMELINE_RESULT" | jq -r '.data.repository.pullRequest.url') + + # Search for rate limit indicators in comments + RATE_LIMIT_COMMENTS=$(echo "$TIMELINE_RESULT" | jq -r ' + .data.repository.pullRequest.timelineItems.nodes[] | + select(.body != null) | + select(.body | test("rate limit|API rate limit|secondary rate limit|abuse detection|429|too many requests"; "i")) | + { + author: .author.login, + body: .body, + created_at: .createdAt + } + ') + + if [[ -n "$RATE_LIMIT_COMMENTS" && "$RATE_LIMIT_COMMENTS" != "null" ]]; then + # Extract the first rate limit message + FIRST_COMMENT=$(echo "$RATE_LIMIT_COMMENTS" | jq -s '.[0]') + ERROR_MESSAGE=$(echo "$FIRST_COMMENT" | jq -r '.body' | head -c 200) + DETECTED_AT=$(echo "$FIRST_COMMENT" | jq -r '.created_at') + + # Output the rate-limited PR info + jq -n \ + --arg number "$pr_number" \ + --arg title "$PR_TITLE" \ + --arg state "$PR_STATE" \ + --arg created_at "$PR_CREATED" \ + --arg detected_at "$DETECTED_AT" \ + --arg error_message "$ERROR_MESSAGE" \ + --arg url "$PR_URL" \ + '{ + number: ($number | tonumber), + title: $title, + state: $state, + created_at: $created_at, + rate_limit_detected_at: $detected_at, + error_message: $error_message, + url: $url + }' + return 0 + else + return 1 + fi +} + +# If specific PR is provided, just check that one +if [[ -n "$SPECIFIC_PR" ]]; then + echo "Checking PR #$SPECIFIC_PR for rate limiting..." >&2 + + RESULT=$(check_pr_for_rate_limiting "$SPECIFIC_PR") + + if [[ $? -eq 0 ]]; then + echo "$RESULT" | jq -s '{ + rate_limited_prs: ., + total_examined: 1, + total_rate_limited: 1 + }' + else + echo "No rate limiting detected in PR #$SPECIFIC_PR" >&2 + jq -n '{ + rate_limited_prs: [], + total_examined: 1, + total_rate_limited: 0 + }' + fi + exit 0 +fi + +# Build search query for multiple PRs +SEARCH_QUERY="is:pr" + +# Add state filter +if [[ "$STATE" != "all" ]]; then + SEARCH_QUERY="$SEARCH_QUERY is:$STATE" +fi + +# Add date filter +if [[ -n "$SINCE_DATE" ]]; then + SEARCH_QUERY="$SEARCH_QUERY created:>$SINCE_DATE" +fi + +# Add Copilot author filter (common source of rate-limited PRs) +SEARCH_QUERY="$SEARCH_QUERY author:app/copilot-swe-agent" + +# Add repo filter +if [[ -n "$REPO" ]]; then + SEARCH_QUERY="$SEARCH_QUERY repo:$REPO" +fi + +echo "Searching for PRs: $SEARCH_QUERY" >&2 + +# Search for PRs +if [[ -n "$REPO" ]]; then + PRS=$(gh pr list --repo "$REPO" --state "$STATE" --limit "$LIMIT" --json number --jq '.[].number') +else + PRS=$(gh pr list --state "$STATE" --limit "$LIMIT" --json number --jq '.[].number') +fi + +if [[ -z "$PRS" ]]; then + echo "No PRs found matching criteria" >&2 + jq -n '{ + rate_limited_prs: [], + total_examined: 0, + total_rate_limited: 0 + }' + exit 0 +fi + +# Check each PR for rate limiting +RATE_LIMITED_PRS=() +TOTAL_EXAMINED=0 + +for pr_number in $PRS; do + echo "Checking PR #$pr_number..." >&2 + TOTAL_EXAMINED=$((TOTAL_EXAMINED + 1)) + + if RESULT=$(check_pr_for_rate_limiting "$pr_number" 2>/dev/null); then + RATE_LIMITED_PRS+=("$RESULT") + echo " ✗ Rate limiting detected in PR #$pr_number" >&2 + else + echo " ✓ No rate limiting in PR #$pr_number" >&2 + fi +done + +# Output final results +TOTAL_RATE_LIMITED=${#RATE_LIMITED_PRS[@]} + +if [[ $TOTAL_RATE_LIMITED -eq 0 ]]; then + jq -n \ + --arg examined "$TOTAL_EXAMINED" \ + '{ + rate_limited_prs: [], + total_examined: ($examined | tonumber), + total_rate_limited: 0 + }' +else + # Combine all rate-limited PRs into a JSON array + printf '%s\n' "${RATE_LIMITED_PRS[@]}" | jq -s \ + --arg examined "$TOTAL_EXAMINED" \ + --arg rate_limited "$TOTAL_RATE_LIMITED" \ + '{ + rate_limited_prs: ., + total_examined: ($examined | tonumber), + total_rate_limited: ($rate_limited | tonumber) + }' +fi From f740a58bb37b9b42278bb1171dd7641c43783b8d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 15:48:20 +0000 Subject: [PATCH 3/8] Add documentation for detecting rate-limited PRs Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- docs/detecting-rate-limited-prs.md | 211 +++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 docs/detecting-rate-limited-prs.md diff --git a/docs/detecting-rate-limited-prs.md b/docs/detecting-rate-limited-prs.md new file mode 100644 index 00000000000..87d6c239e9f --- /dev/null +++ b/docs/detecting-rate-limited-prs.md @@ -0,0 +1,211 @@ +# Detecting Rate-Limited Pull Requests + +This document provides search queries and GraphQL patterns for detecting pull requests that have failed due to GitHub API rate limiting. + +## Problem Statement + +Copilot-created PRs and automated workflows sometimes fail because the GitHub API rate limit is exceeded during execution. These failures can be difficult to track down without proper tooling. + +## Detection Patterns + +### 1. Text-Based Indicators + +Rate-limited PRs typically contain these text patterns in comments or logs: +- `rate limit` (case-insensitive) +- `API rate limit exceeded` +- `secondary rate limit` +- `abuse detection` +- `403` (HTTP status) combined with "rate" or "limit" +- `429` (HTTP status - Too Many Requests) + +### 2. GitHub Search Queries + +#### Basic Search for Copilot PRs + +Find recent PRs from Copilot agent: +``` +is:pr author:app/copilot-swe-agent +``` + +#### Date-Filtered Search + +Find PRs created in the last 30 days: +``` +is:pr author:app/copilot-swe-agent created:>2026-01-07 +``` + +#### Closed PRs with Comments + +Find closed PRs that have comments (where rate limit messages might appear): +``` +is:pr is:closed comments:>0 created:>2026-01-01 +``` + +#### Repository-Specific Search + +Search in a specific repository: +``` +is:pr repo:github/gh-aw author:app/copilot-swe-agent +``` + +### 3. GraphQL Queries + +#### Query PR Timeline for Comments + +This query fetches a PR's timeline including all comments where rate limit messages typically appear: + +```graphql +query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $number) { + number + title + state + createdAt + closedAt + author { + login + } + url + timelineItems(first: 100, itemTypes: [ISSUE_COMMENT, CLOSED_EVENT]) { + nodes { + __typename + ... on IssueComment { + author { + login + } + body + createdAt + } + } + } + } + } +} +``` + +**Usage with GitHub CLI:** +```bash +gh api graphql -f query='...' -f owner="github" -f repo="gh-aw" -F number=14368 +``` + +#### Search Multiple PRs + +To search multiple PRs efficiently, combine REST API search with GraphQL timeline queries: + +1. First, search for PRs using the REST API: +```bash +gh pr list --state all --limit 50 --json number,title,state,author +``` + +2. Then, for each PR, fetch the timeline using GraphQL to check for rate limit messages. + +### 4. Using the Detection Script + +The `skills/detect-rate-limited-prs/detect-rate-limited-prs.sh` script automates this process: + +#### Check a Specific PR + +```bash +./detect-rate-limited-prs.sh --pr 14368 +``` + +#### Check All Recent PRs + +```bash +./detect-rate-limited-prs.sh --since 2026-01-01 --limit 50 +``` + +#### Check Specific Repository + +```bash +./detect-rate-limited-prs.sh --repo github/gh-aw --state all +``` + +## Output Format + +The detection script outputs JSON: + +```json +{ + "rate_limited_prs": [ + { + "number": 14368, + "title": "Fix rate limiting issue", + "state": "closed", + "created_at": "2026-01-15T10:00:00Z", + "rate_limit_detected_at": "2026-01-15T10:05:00Z", + "error_message": "API rate limit exceeded for installation ID...", + "url": "https://github.com/github/gh-aw/pull/14368" + } + ], + "total_examined": 50, + "total_rate_limited": 1 +} +``` + +## Example: PR #14368 + +This PR is a reference example of a rate-limited PR. To examine it: + +```bash +# Using the detection script +./detect-rate-limited-prs.sh --pr 14368 + +# Using GitHub CLI directly +gh pr view 14368 --json comments,timelineItems + +# Using GraphQL +gh api graphql -f query='query { repository(owner: "github", name: "gh-aw") { pullRequest(number: 14368) { timelineItems(first: 100) { nodes { __typename } } } } }' +``` + +## Rate Limit Prevention Strategies + +1. **Implement Retry Logic**: Add exponential backoff for GitHub API calls +2. **Use GraphQL**: GraphQL queries can be more efficient than REST API for complex data +3. **Cache Results**: Cache API responses when possible +4. **Monitor Rate Limits**: Check rate limit status before making requests +5. **Use Personal Access Tokens**: Different tokens have different rate limits + +## Integration with Workflows + +Use the rate-limit-pr-detector.md workflow to automatically scan for rate-limited PRs: + +```bash +gh workflow run rate-limit-pr-detector.md +``` + +The workflow will: +1. Search for recent Copilot PRs +2. Check each PR's timeline for rate limit indicators +3. Generate a comprehensive report +4. Provide recommendations for prevention + +## Related Resources + +- [GitHub API Rate Limits Documentation](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting) +- [Secondary Rate Limits](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits) +- [Best Practices for Integrators](https://docs.github.com/en/rest/guides/best-practices-for-integrators) + +## Troubleshooting + +### No Results Found + +If the detection script finds no rate-limited PRs: +- Verify the date range is correct +- Check if Copilot PRs use a different author name +- Manually inspect a known rate-limited PR to confirm the detection pattern + +### GraphQL Errors + +If GraphQL queries fail: +- Ensure you have proper authentication: `gh auth status` +- Check you have the required permissions: `pull-requests: read` +- Verify the repository exists and is accessible + +### Rate Limiting While Detecting Rate Limits + +If the detection script itself gets rate limited: +- Reduce the `--limit` parameter to check fewer PRs at once +- Add delays between PR checks +- Use a different authentication token with higher limits From e3f0d4ecdd64e08361198820358daaeb11283852 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 15:52:21 +0000 Subject: [PATCH 4/8] Add quick start guide for rate-limited PR detection Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- docs/rate-limited-prs-quickstart.md | 148 ++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 docs/rate-limited-prs-quickstart.md diff --git a/docs/rate-limited-prs-quickstart.md b/docs/rate-limited-prs-quickstart.md new file mode 100644 index 00000000000..e2927d43945 --- /dev/null +++ b/docs/rate-limited-prs-quickstart.md @@ -0,0 +1,148 @@ +# Rate-Limited PR Detection - Quick Start + +This guide shows you how to quickly detect and analyze pull requests that have failed due to GitHub API rate limiting. + +## Quick Detection Methods + +### Method 1: Run the Workflow (Automated) + +The easiest way to detect rate-limited PRs is to run the automated workflow: + +```bash +gh workflow run rate-limit-pr-detector.md +``` + +This will: +- Search for recent Copilot PRs +- Check each PR's timeline for rate limit indicators +- Generate a comprehensive report +- Provide recommendations + +### Method 2: Use the Detection Script (Manual) + +For manual investigation, use the detection script: + +```bash +# Check a specific PR (e.g., PR #14368) +cd skills/detect-rate-limited-prs +./detect-rate-limited-prs.sh --pr 14368 + +# Check all PRs from the last 30 days +./detect-rate-limited-prs.sh --since 2026-01-01 --limit 50 + +# Check a specific repository +./detect-rate-limited-prs.sh --repo github/gh-aw --state all +``` + +### Method 3: GitHub Search (Quick Lookup) + +Use GitHub's web interface or CLI to search: + +```bash +# Find recent Copilot PRs +gh pr list --search "author:app/copilot-swe-agent" + +# View a specific PR with comments +gh pr view 14368 --comments +``` + +## What Gets Detected + +The detection looks for these indicators: + +- **Text patterns**: "rate limit", "API rate limit exceeded", "secondary rate limit" +- **HTTP codes**: 403 (Forbidden), 429 (Too Many Requests) +- **Comments**: From GitHub Actions bot or Copilot agent mentioning rate limiting + +## Example Output + +```json +{ + "rate_limited_prs": [ + { + "number": 14368, + "title": "Fix issue with workflow", + "state": "closed", + "created_at": "2026-01-15T10:00:00Z", + "rate_limit_detected_at": "2026-01-15T10:05:00Z", + "error_message": "API rate limit exceeded for installation ID...", + "url": "https://github.com/github/gh-aw/pull/14368" + } + ], + "total_examined": 50, + "total_rate_limited": 1 +} +``` + +## Understanding the Results + +- **number**: The PR number +- **state**: Current state (open, closed, merged) +- **rate_limit_detected_at**: When the rate limit was first detected +- **error_message**: The actual error message from the PR timeline +- **url**: Direct link to the PR + +## Prevention Strategies + +To avoid rate limiting in the future: + +1. **Add retry logic** with exponential backoff +2. **Use GraphQL** instead of REST API where possible +3. **Cache API responses** to reduce duplicate calls +4. **Monitor rate limits** before making requests +5. **Use different tokens** for different operations + +## Need More Details? + +See the complete documentation: + +- **Full Documentation**: `docs/detecting-rate-limited-prs.md` +- **Skill Documentation**: `skills/detect-rate-limited-prs/SKILL.md` +- **Workflow Details**: `.github/workflows/rate-limit-pr-detector.md` + +## GraphQL Query Example + +For custom queries, use this GraphQL pattern: + +```graphql +query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $number) { + timelineItems(first: 100, itemTypes: [ISSUE_COMMENT]) { + nodes { + ... on IssueComment { + body + createdAt + } + } + } + } + } +} +``` + +## Troubleshooting + +**Script finds no results?** +- Check the date range with `--since` +- Verify Copilot PRs use `app/copilot-swe-agent` as author +- Try manually checking a known rate-limited PR first + +**GraphQL errors?** +- Ensure you're authenticated: `gh auth status` +- Check you have `pull-requests: read` permission + +**Script itself gets rate limited?** +- Reduce the `--limit` parameter +- Add delays between PR checks +- Use a token with higher rate limits + +## Example Real-World Case + +PR #14368 is a reference example of a rate-limited PR. To examine it: + +```bash +./detect-rate-limited-prs.sh --pr 14368 +``` + +This will show you the exact error message and when the rate limiting occurred. From 48224bd833ae3fa27e7dedcc76808f05ab3fa4a5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:44:56 +0000 Subject: [PATCH 5/8] Add rate limiting detection to Issue Monster, remove standalone detector files - Add rate limit check in Issue Monster to prevent scheduling work when rate limited - Check recent Copilot PRs for rate limit indicators before assigning issues - Skip all assignments if rate limiting detected in last hour - Remove standalone rate-limit-pr-detector workflow - Remove docs and skill files for standalone detector - Update Issue Monster documentation to reflect rate limiting protection Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/issue-monster.lock.yml | 74 ++- .github/workflows/issue-monster.md | 107 ++++- .../workflows/rate-limit-pr-detector.lock.yml | 454 ------------------ .github/workflows/rate-limit-pr-detector.md | 136 ------ docs/detecting-rate-limited-prs.md | 211 -------- docs/rate-limited-prs-quickstart.md | 148 ------ skills/detect-rate-limited-prs/SKILL.md | 151 ------ .../detect-rate-limited-prs.sh | 249 ---------- 8 files changed, 166 insertions(+), 1364 deletions(-) delete mode 100644 .github/workflows/rate-limit-pr-detector.lock.yml delete mode 100644 .github/workflows/rate-limit-pr-detector.md delete mode 100644 docs/detecting-rate-limited-prs.md delete mode 100644 docs/rate-limited-prs-quickstart.md delete mode 100644 skills/detect-rate-limited-prs/SKILL.md delete mode 100755 skills/detect-rate-limited-prs/detect-rate-limited-prs.sh diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index f9a2965011f..1050144feb1 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -21,7 +21,7 @@ # # The Cookie Monster of issues - assigns issues to Copilot agents one at a time # -# frontmatter-hash: fda3821e70fae5f450a65c2b0ce82f1b94aba2c48f8cb148c6d788f9678cb214 +# frontmatter-hash: 6ee7ace3c0aa48b0df83c01e3d00b75cfd4a988e329f43b1310131cebf57ce06 name: "Issue Monster" "on": @@ -1159,6 +1159,78 @@ jobs: const { owner, repo } = context.repo; try { + // Check for recent rate-limited PRs to avoid scheduling more work during rate limiting + core.info('Checking for recent rate-limited PRs...'); + const rateLimitCheckDate = new Date(); + rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour + const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('T')[0]; + + const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`; + const recentPRsResponse = await github.rest.search.issuesAndPullRequests({ + q: recentPRsQuery, + per_page: 10, + sort: 'created', + order: 'desc' + }); + + core.info(`Found ${recentPRsResponse.data.total_count} recent Copilot PRs to check for rate limiting`); + + // Check if any recent PRs have rate limit indicators + let rateLimitDetected = false; + for (const pr of recentPRsResponse.data.items) { + try { + const prTimelineQuery = ` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $number) { + timelineItems(first: 50, itemTypes: [ISSUE_COMMENT]) { + nodes { + __typename + ... on IssueComment { + body + createdAt + } + } + } + } + } + } + `; + + const prTimelineResult = await github.graphql(prTimelineQuery, { + owner, + repo, + number: pr.number + }); + + const comments = prTimelineResult?.repository?.pullRequest?.timelineItems?.nodes || []; + const rateLimitPattern = /rate limit|API rate limit|secondary rate limit|abuse detection|429|too many requests/i; + + for (const comment of comments) { + if (comment.body && rateLimitPattern.test(comment.body)) { + core.warning(`Rate limiting detected in PR #${pr.number}: ${comment.body.substring(0, 200)}`); + rateLimitDetected = true; + break; + } + } + + if (rateLimitDetected) break; + } catch (error) { + core.warning(`Could not check PR #${pr.number} for rate limiting: ${error.message}`); + } + } + + if (rateLimitDetected) { + core.warning('🛑 Rate limiting detected in recent PRs. Skipping issue assignment to prevent further rate limit issues.'); + core.setOutput('issue_count', 0); + core.setOutput('issue_numbers', ''); + core.setOutput('issue_list', ''); + core.setOutput('has_issues', 'false'); + return; + } + + core.info('✓ No rate limiting detected. Proceeding with issue search.'); + // Labels that indicate an issue should NOT be auto-assigned const excludeLabels = [ 'wontfix', diff --git a/.github/workflows/issue-monster.md b/.github/workflows/issue-monster.md index ae158e5032b..7c3c723be3e 100644 --- a/.github/workflows/issue-monster.md +++ b/.github/workflows/issue-monster.md @@ -47,6 +47,78 @@ jobs: const { owner, repo } = context.repo; try { + // Check for recent rate-limited PRs to avoid scheduling more work during rate limiting + core.info('Checking for recent rate-limited PRs...'); + const rateLimitCheckDate = new Date(); + rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour + const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('T')[0]; + + const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`; + const recentPRsResponse = await github.rest.search.issuesAndPullRequests({ + q: recentPRsQuery, + per_page: 10, + sort: 'created', + order: 'desc' + }); + + core.info(`Found ${recentPRsResponse.data.total_count} recent Copilot PRs to check for rate limiting`); + + // Check if any recent PRs have rate limit indicators + let rateLimitDetected = false; + for (const pr of recentPRsResponse.data.items) { + try { + const prTimelineQuery = ` + query($owner: String!, $repo: String!, $number: Int!) { + repository(owner: $owner, name: $repo) { + pullRequest(number: $number) { + timelineItems(first: 50, itemTypes: [ISSUE_COMMENT]) { + nodes { + __typename + ... on IssueComment { + body + createdAt + } + } + } + } + } + } + `; + + const prTimelineResult = await github.graphql(prTimelineQuery, { + owner, + repo, + number: pr.number + }); + + const comments = prTimelineResult?.repository?.pullRequest?.timelineItems?.nodes || []; + const rateLimitPattern = /rate limit|API rate limit|secondary rate limit|abuse detection|429|too many requests/i; + + for (const comment of comments) { + if (comment.body && rateLimitPattern.test(comment.body)) { + core.warning(`Rate limiting detected in PR #${pr.number}: ${comment.body.substring(0, 200)}`); + rateLimitDetected = true; + break; + } + } + + if (rateLimitDetected) break; + } catch (error) { + core.warning(`Could not check PR #${pr.number} for rate limiting: ${error.message}`); + } + } + + if (rateLimitDetected) { + core.warning('🛑 Rate limiting detected in recent PRs. Skipping issue assignment to prevent further rate limit issues.'); + core.setOutput('issue_count', 0); + core.setOutput('issue_numbers', ''); + core.setOutput('issue_list', ''); + core.setOutput('has_issues', 'false'); + return; + } + + core.info('✓ No rate limiting detected. Proceeding with issue search.'); + // Labels that indicate an issue should NOT be auto-assigned const excludeLabels = [ 'wontfix', @@ -324,6 +396,11 @@ Find up to three issues that need work and assign them to the Copilot agent for The issue search has already been performed in a previous job with smart filtering and prioritization: +**Rate Limiting Protection:** +- 🛡️ **Checks for rate-limited PRs in the last hour** before scheduling new work +- If rate limiting is detected in recent Copilot PRs, the workflow skips all assignments to prevent further API issues +- Looks for patterns: "rate limit", "API rate limit", "secondary rate limit", "abuse detection", "429", "too many requests" + **Filtering Applied:** - ✅ Only open issues **with "cookie" label** (indicating approved work queue items from automated workflows) - ✅ Excluded issues with labels: wontfix, duplicate, invalid, question, discussion, needs-discussion, blocked, on-hold, waiting-for-feedback, needs-more-info, no-bot, no-campaign @@ -467,24 +544,26 @@ safeoutputs/add_comment(item_number=, body="🍪 **Issue Monster h ## Success Criteria A successful run means: -1. You reviewed the pre-searched, filtered, and prioritized issue list -2. The search already excluded issues with problematic labels (wontfix, question, discussion, etc.) -3. The search already excluded issues with campaign labels (campaign:*) as these are managed by campaign orchestrators -4. The search already excluded issues that already have assignees -5. The search already excluded issues that have sub-issues (parent/organizing issues are not tasks) -6. The search already excluded issues with closed or merged PRs (treated as complete) -7. The search already excluded issues with open PRs from Copilot agent (already being worked on) -8. Issues are sorted by priority score (good-first-issue, bug, security, etc. get higher scores) -9. For "task" or "plan" issues: You checked for parent issues and sibling sub-issue PRs if necessary -10. You selected up to three appropriate issues from the top of the priority list that are completely separate in topic -11. You read and understood each issue -12. You verified that the selected issues don't have overlapping concerns or file changes -13. You assigned each issue to the Copilot agent using `assign_to_agent` -14. You commented on each issue being assigned +1. **Rate limiting check passed** - The search verified no recent PRs are rate-limited (or workflow skipped if rate limiting detected) +2. You reviewed the pre-searched, filtered, and prioritized issue list +3. The search already excluded issues with problematic labels (wontfix, question, discussion, etc.) +4. The search already excluded issues with campaign labels (campaign:*) as these are managed by campaign orchestrators +5. The search already excluded issues that already have assignees +6. The search already excluded issues that have sub-issues (parent/organizing issues are not tasks) +7. The search already excluded issues with closed or merged PRs (treated as complete) +8. The search already excluded issues with open PRs from Copilot agent (already being worked on) +9. Issues are sorted by priority score (good-first-issue, bug, security, etc. get higher scores) +10. For "task" or "plan" issues: You checked for parent issues and sibling sub-issue PRs if necessary +11. You selected up to three appropriate issues from the top of the priority list that are completely separate in topic +12. You read and understood each issue +13. You verified that the selected issues don't have overlapping concerns or file changes +14. You assigned each issue to the Copilot agent using `assign_to_agent` +15. You commented on each issue being assigned ## Error Handling If anything goes wrong or no work can be assigned: +- **Rate limiting detected**: The workflow automatically skips (no action needed - the search job handles this) - **No issues found**: Use the `noop` tool with message: "🍽️ No suitable candidate issues - the plate is empty!" - **All issues assigned**: Use the `noop` tool with message: "🍽️ All issues are already being worked on!" - **No suitable separate issues**: Use the `noop` tool explaining which issues were considered and why they couldn't be assigned (e.g., overlapping topics, sibling PRs, etc.) diff --git a/.github/workflows/rate-limit-pr-detector.lock.yml b/.github/workflows/rate-limit-pr-detector.lock.yml deleted file mode 100644 index fdd42b0c100..00000000000 --- a/.github/workflows/rate-limit-pr-detector.lock.yml +++ /dev/null @@ -1,454 +0,0 @@ -# -# ___ _ _ -# / _ \ | | (_) -# | |_| | __ _ ___ _ __ | |_ _ ___ -# | _ |/ _` |/ _ \ '_ \| __| |/ __| -# | | | | (_| | __/ | | | |_| | (__ -# \_| |_/\__, |\___|_| |_|\__|_|\___| -# __/ | -# _ _ |___/ -# | | | | / _| | -# | | | | ___ _ __ _ __| |_| | _____ ____ -# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___| -# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \ -# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/ -# -# This file was automatically generated by gh-aw. DO NOT EDIT. -# -# To update this file, edit the corresponding .md file and run: -# gh aw compile -# For more information: https://github.com/github/gh-aw/blob/main/.github/aw/github-agentic-workflows.md -# -# Detect and report PRs that failed due to rate limiting -# -# frontmatter-hash: 11ab7f7382f6ba04045d6cce2e889c206b992ad642eb4f03a77473f155545728 - -name: "Rate Limit PR Detector" -"on": - workflow_dispatch: - -permissions: {} - -concurrency: - group: "gh-aw-${{ github.workflow }}" - -run-name: "Rate Limit PR Detector" - -jobs: - activation: - runs-on: ubuntu-slim - permissions: - contents: read - outputs: - comment_id: "" - comment_repo: "" - steps: - - name: Checkout actions folder - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - with: - sparse-checkout: | - actions - persist-credentials: false - - name: Setup Scripts - uses: ./actions/setup - with: - destination: /opt/gh-aw/actions - - name: Check workflow file timestamps - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_WORKFLOW_FILE: "rate-limit-pr-detector.lock.yml" - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/check_workflow_timestamp_api.cjs'); - await main(); - - agent: - needs: activation - runs-on: ubuntu-latest - permissions: - contents: read - issues: read - pull-requests: read - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}" - outputs: - checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }} - model: ${{ steps.generate_aw_info.outputs.model }} - secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} - steps: - - name: Checkout actions folder - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - with: - sparse-checkout: | - actions - persist-credentials: false - - name: Setup Scripts - uses: ./actions/setup - with: - destination: /opt/gh-aw/actions - - name: Checkout repository - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6 - with: - persist-credentials: false - - name: Create gh-aw temp directory - run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh - - name: Configure Git credentials - env: - REPO_NAME: ${{ github.repository }} - SERVER_URL: ${{ github.server_url }} - run: | - git config --global user.email "github-actions[bot]@users.noreply.github.com" - git config --global user.name "github-actions[bot]" - # Re-authenticate git with GitHub token - SERVER_URL_STRIPPED="${SERVER_URL#https://}" - git remote set-url origin "https://x-access-token:${{ github.token }}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git" - echo "Git configured with standard GitHub Actions identity" - - name: Checkout PR branch - id: checkout-pr - if: | - github.event.pull_request - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - with: - github-token: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/checkout_pr_branch.cjs'); - await main(); - - name: Validate COPILOT_GITHUB_TOKEN secret - id: validate-secret - run: /opt/gh-aw/actions/validate_multi_secret.sh COPILOT_GITHUB_TOKEN 'GitHub Copilot CLI' https://github.github.com/gh-aw/reference/engines/#github-copilot-default - env: - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - - name: Install GitHub Copilot CLI - run: /opt/gh-aw/actions/install_copilot_cli.sh 0.0.405 - - name: Install awf binary - run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.13.12 - - name: Determine automatic lockdown mode for GitHub MCP server - id: determine-automatic-lockdown - env: - TOKEN_CHECK: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - if: env.TOKEN_CHECK != '' - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - with: - script: | - const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs'); - await determineAutomaticLockdown(github, context, core); - - name: Download container images - run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.13.12 ghcr.io/github/gh-aw-firewall/squid:0.13.12 ghcr.io/github/gh-aw-mcpg:v0.0.103 ghcr.io/github/github-mcp-server:v0.30.3 - - name: Start MCP gateway - id: start-mcp-gateway - env: - GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }} - GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }} - run: | - set -eo pipefail - mkdir -p /tmp/gh-aw/mcp-config - - # Export gateway environment variables for MCP config and gateway script - export MCP_GATEWAY_PORT="80" - export MCP_GATEWAY_DOMAIN="host.docker.internal" - MCP_GATEWAY_API_KEY="" - MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=') - export MCP_GATEWAY_API_KEY - export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads" - mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}" - export DEBUG="*" - - # Register API key as secret to mask it from logs - echo "::add-mask::${MCP_GATEWAY_API_KEY}" - export GH_AW_ENGINE="copilot" - export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host -v /var/run/docker.sock:/var/run/docker.sock -e MCP_GATEWAY_PORT -e MCP_GATEWAY_DOMAIN -e MCP_GATEWAY_API_KEY -e MCP_GATEWAY_PAYLOAD_DIR -e DEBUG -e MCP_GATEWAY_LOG_DIR -e GH_AW_MCP_LOG_DIR -e GH_AW_SAFE_OUTPUTS -e GH_AW_SAFE_OUTPUTS_CONFIG_PATH -e GH_AW_SAFE_OUTPUTS_TOOLS_PATH -e GH_AW_ASSETS_BRANCH -e GH_AW_ASSETS_MAX_SIZE_KB -e GH_AW_ASSETS_ALLOWED_EXTS -e DEFAULT_BRANCH -e GITHUB_MCP_SERVER_TOKEN -e GITHUB_MCP_LOCKDOWN -e GITHUB_REPOSITORY -e GITHUB_SERVER_URL -e GITHUB_SHA -e GITHUB_WORKSPACE -e GITHUB_TOKEN -e GITHUB_RUN_ID -e GITHUB_RUN_NUMBER -e GITHUB_RUN_ATTEMPT -e GITHUB_JOB -e GITHUB_ACTION -e GITHUB_EVENT_NAME -e GITHUB_EVENT_PATH -e GITHUB_ACTOR -e GITHUB_ACTOR_ID -e GITHUB_TRIGGERING_ACTOR -e GITHUB_WORKFLOW -e GITHUB_WORKFLOW_REF -e GITHUB_WORKFLOW_SHA -e GITHUB_REF -e GITHUB_REF_NAME -e GITHUB_REF_TYPE -e GITHUB_HEAD_REF -e GITHUB_BASE_REF -v /tmp/gh-aw/mcp-payloads:/tmp/gh-aw/mcp-payloads:rw -v /opt:/opt:ro -v /tmp:/tmp:rw -v '"${GITHUB_WORKSPACE}"':'"${GITHUB_WORKSPACE}"':rw ghcr.io/github/gh-aw-mcpg:v0.0.103' - - mkdir -p /home/runner/.copilot - cat << MCPCONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh - { - "mcpServers": { - "github": { - "type": "stdio", - "container": "ghcr.io/github/github-mcp-server:v0.30.3", - "env": { - "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN", - "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}", - "GITHUB_READ_ONLY": "1", - "GITHUB_TOOLSETS": "context,repos,issues,pull_requests" - } - } - }, - "gateway": { - "port": $MCP_GATEWAY_PORT, - "domain": "${MCP_GATEWAY_DOMAIN}", - "apiKey": "${MCP_GATEWAY_API_KEY}", - "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" - } - } - MCPCONFIG_EOF - - name: Generate agentic run info - id: generate_aw_info - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const fs = require('fs'); - - const awInfo = { - engine_id: "copilot", - engine_name: "GitHub Copilot CLI", - model: "gpt-5.1-codex-mini", - version: "", - agent_version: "0.0.405", - workflow_name: "Rate Limit PR Detector", - experimental: false, - supports_tools_allowlist: true, - supports_http_transport: true, - run_id: context.runId, - run_number: context.runNumber, - run_attempt: process.env.GITHUB_RUN_ATTEMPT, - repository: context.repo.owner + '/' + context.repo.repo, - ref: context.ref, - sha: context.sha, - actor: context.actor, - event_name: context.eventName, - staged: false, - allowed_domains: ["defaults"], - firewall_enabled: true, - awf_version: "v0.13.12", - awmg_version: "v0.0.103", - steps: { - firewall: "squid" - }, - created_at: new Date().toISOString() - }; - - // Write to /tmp/gh-aw directory to avoid inclusion in PR - const tmpPath = '/tmp/gh-aw/aw_info.json'; - fs.writeFileSync(tmpPath, JSON.stringify(awInfo, null, 2)); - console.log('Generated aw_info.json at:', tmpPath); - console.log(JSON.stringify(awInfo, null, 2)); - - // Set model as output for reuse in other steps/jobs - core.setOutput('model', awInfo.model); - - name: Generate workflow overview - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const { generateWorkflowOverview } = require('/opt/gh-aw/actions/generate_workflow_overview.cjs'); - await generateWorkflowOverview(core); - - name: Create prompt with built-in context - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - run: | - bash /opt/gh-aw/actions/create_prompt_first.sh - cat << 'PROMPT_EOF' > "$GH_AW_PROMPT" - - PROMPT_EOF - cat "/opt/gh-aw/prompts/temp_folder_prompt.md" >> "$GH_AW_PROMPT" - cat "/opt/gh-aw/prompts/markdown.md" >> "$GH_AW_PROMPT" - cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" - - The following GitHub context information is available for this workflow: - {{#if __GH_AW_GITHUB_ACTOR__ }} - - **actor**: __GH_AW_GITHUB_ACTOR__ - {{/if}} - {{#if __GH_AW_GITHUB_REPOSITORY__ }} - - **repository**: __GH_AW_GITHUB_REPOSITORY__ - {{/if}} - {{#if __GH_AW_GITHUB_WORKSPACE__ }} - - **workspace**: __GH_AW_GITHUB_WORKSPACE__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ }} - - **issue-number**: #__GH_AW_GITHUB_EVENT_ISSUE_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ }} - - **discussion-number**: #__GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ }} - - **pull-request-number**: #__GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER__ - {{/if}} - {{#if __GH_AW_GITHUB_EVENT_COMMENT_ID__ }} - - **comment-id**: __GH_AW_GITHUB_EVENT_COMMENT_ID__ - {{/if}} - {{#if __GH_AW_GITHUB_RUN_ID__ }} - - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__ - {{/if}} - - - PROMPT_EOF - cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" - - PROMPT_EOF - cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" - {{#runtime-import .github/workflows/rate-limit-pr-detector.md}} - PROMPT_EOF - - name: Substitute placeholders - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GH_AW_GITHUB_ACTOR: ${{ github.actor }} - GH_AW_GITHUB_EVENT_COMMENT_ID: ${{ github.event.comment.id }} - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: ${{ github.event.discussion.number }} - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} - GH_AW_GITHUB_REPOSITORY: ${{ github.repository }} - GH_AW_GITHUB_RUN_ID: ${{ github.run_id }} - GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }} - with: - script: | - const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs'); - - // Call the substitution function - return await substitutePlaceholders({ - file: process.env.GH_AW_PROMPT, - substitutions: { - GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR, - GH_AW_GITHUB_EVENT_COMMENT_ID: process.env.GH_AW_GITHUB_EVENT_COMMENT_ID, - GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER: process.env.GH_AW_GITHUB_EVENT_DISCUSSION_NUMBER, - GH_AW_GITHUB_EVENT_ISSUE_NUMBER: process.env.GH_AW_GITHUB_EVENT_ISSUE_NUMBER, - GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER: process.env.GH_AW_GITHUB_EVENT_PULL_REQUEST_NUMBER, - GH_AW_GITHUB_REPOSITORY: process.env.GH_AW_GITHUB_REPOSITORY, - GH_AW_GITHUB_RUN_ID: process.env.GH_AW_GITHUB_RUN_ID, - GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE - } - }); - - name: Interpolate variables and render templates - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/interpolate_prompt.cjs'); - await main(); - - name: Validate prompt placeholders - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/validate_prompt_placeholders.sh - - name: Print prompt - env: - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - run: bash /opt/gh-aw/actions/print_prompt_summary.sh - - name: Execute GitHub Copilot CLI - id: agentic_execution - # Copilot CLI tool arguments (sorted): - timeout-minutes: 15 - run: | - set -o pipefail - sudo -E awf --enable-chroot --env-all --container-workdir "${GITHUB_WORKSPACE}" --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.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,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.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,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.13.12 --skip-pull \ - -- '/usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --add-dir "${GITHUB_WORKSPACE}" --disable-builtin-mcps --model gpt-5.1-codex-mini --allow-all-tools --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' \ - 2>&1 | tee /tmp/gh-aw/agent-stdio.log - env: - COPILOT_AGENT_RUNNER_TYPE: STANDALONE - COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json - GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_REF_NAME: ${{ github.ref_name }} - GITHUB_STEP_SUMMARY: ${{ env.GITHUB_STEP_SUMMARY }} - GITHUB_WORKSPACE: ${{ github.workspace }} - XDG_CONFIG_HOME: /home/runner - - name: Copy Copilot session state files to logs - if: always() - continue-on-error: true - run: | - # Copy Copilot session state files to logs folder for artifact collection - # This ensures they are in /tmp/gh-aw/ where secret redaction can scan them - SESSION_STATE_DIR="$HOME/.copilot/session-state" - LOGS_DIR="/tmp/gh-aw/sandbox/agent/logs" - - if [ -d "$SESSION_STATE_DIR" ]; then - echo "Copying Copilot session state files from $SESSION_STATE_DIR to $LOGS_DIR" - mkdir -p "$LOGS_DIR" - cp -v "$SESSION_STATE_DIR"/*.jsonl "$LOGS_DIR/" 2>/dev/null || true - echo "Session state files copied successfully" - else - echo "No session-state directory found at $SESSION_STATE_DIR" - fi - - name: Stop MCP gateway - if: always() - continue-on-error: true - env: - MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }} - MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }} - GATEWAY_PID: ${{ steps.start-mcp-gateway.outputs.gateway-pid }} - run: | - bash /opt/gh-aw/actions/stop_mcp_gateway.sh "$GATEWAY_PID" - - name: Redact secrets in logs - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/redact_secrets.cjs'); - await main(); - env: - GH_AW_SECRET_NAMES: 'COPILOT_GITHUB_TOKEN,GH_AW_GITHUB_MCP_SERVER_TOKEN,GH_AW_GITHUB_TOKEN,GITHUB_TOKEN' - SECRET_COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }} - SECRET_GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }} - SECRET_GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }} - SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Upload engine output files - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 - with: - name: agent_outputs - path: | - /tmp/gh-aw/sandbox/agent/logs/ - /tmp/gh-aw/redacted-urls.log - if-no-files-found: ignore - - name: Parse agent logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - env: - GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/ - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_copilot_log.cjs'); - await main(); - - name: Parse MCP gateway logs for step summary - if: always() - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 - with: - script: | - const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs'); - setupGlobals(core, github, context, exec, io); - const { main } = require('/opt/gh-aw/actions/parse_mcp_gateway_log.cjs'); - await main(); - - name: Print firewall logs - if: always() - continue-on-error: true - env: - AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs - run: | - # Fix permissions on firewall logs so they can be uploaded as artifacts - # AWF runs with sudo, creating files owned by root - sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall/logs 2>/dev/null || true - awf logs summary | tee -a "$GITHUB_STEP_SUMMARY" - - name: Upload agent artifacts - if: always() - continue-on-error: true - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 - with: - name: agent-artifacts - path: | - /tmp/gh-aw/aw-prompts/prompt.txt - /tmp/gh-aw/aw_info.json - /tmp/gh-aw/mcp-logs/ - /tmp/gh-aw/sandbox/firewall/logs/ - /tmp/gh-aw/agent-stdio.log - /tmp/gh-aw/agent/ - if-no-files-found: ignore - diff --git a/.github/workflows/rate-limit-pr-detector.md b/.github/workflows/rate-limit-pr-detector.md deleted file mode 100644 index 7e97c84c83c..00000000000 --- a/.github/workflows/rate-limit-pr-detector.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -name: Rate Limit PR Detector -description: Detect and report PRs that failed due to rate limiting -on: - workflow_dispatch: - -permissions: - contents: read - pull-requests: read - issues: read - -engine: - id: copilot - model: gpt-5.1-codex-mini - -timeout-minutes: 15 - -tools: - github: - toolsets: [default, pull_requests] ---- - -# Rate Limit PR Detector - -Your mission is to detect pull requests that have failed due to rate limiting and provide a comprehensive report. - -## Background - -Some Copilot-created PRs fail because the GitHub API rate limit is exceeded during execution. These PRs typically have: -- Comments from the Copilot agent or GitHub Actions bot indicating rate limiting -- Specific error messages containing "rate limit" or "API rate limit exceeded" -- Failed workflow runs with rate limit errors - -## Your Task - -1. **Query Recent PRs**: Search for recent pull requests (last 30 days) from the Copilot agent - - Focus on PRs authored by `app/copilot-swe-agent` or similar Copilot bots - - Include both open and closed PRs - -2. **Examine Timeline Events**: For each PR, check the timeline for: - - Comments containing "rate limit", "API rate limit", or "rate limited" - - Workflow run failures with rate limit messages - - GitHub Actions bot comments about rate limiting - -3. **Identify Rate-Limited PRs**: Create a list of PRs that show evidence of rate limiting - -4. **Generate Report**: Create a summary report that includes: - - PR number and title - - When the rate limiting occurred - - The specific error message or comment - - Current PR state (open/closed/merged) - - Suggestions for prevention or retry - -## Search Query Pattern - -Use the GitHub search API or GraphQL to find PRs. Example search query: -``` -is:pr author:app/copilot-swe-agent created:>2026-01-01 -``` - -## GraphQL Query for Timeline - -Use GraphQL to fetch PR timeline events: -```graphql -query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - pullRequest(number: $number) { - number - title - state - author { - login - } - timelineItems(first: 100, itemTypes: [ISSUE_COMMENT, CLOSED_EVENT]) { - nodes { - __typename - ... on IssueComment { - author { - login - } - body - createdAt - } - } - } - } - } -} -``` - -## Detection Criteria - -A PR is considered rate-limited if it contains any of these patterns: -- "rate limit" (case-insensitive) -- "API rate limit exceeded" -- "403" with "rate" or "limit" -- "secondary rate limit" -- "abuse detection" - -## Example PR - -Reference PR #14368 as an example of a rate-limited PR to understand the pattern. - -## Output Format - -Present your findings in a structured format: - -``` -# Rate-Limited PRs Report - -## Summary -- Total PRs examined: X -- Rate-limited PRs found: Y -- Date range: [start] to [end] - -## Rate-Limited PRs - -### PR #XXXXX: [Title] -- **State**: [open/closed/merged] -- **Created**: [date] -- **Rate Limit Event**: [date] -- **Error Message**: [excerpt from comment/log] -- **URL**: [PR URL] - -## Recommendations -[Your suggestions for preventing or handling rate-limited PRs] -``` - -## Important Notes - -- Be thorough but efficient - don't fetch more data than needed -- Use pagination if there are many PRs to examine -- Focus on recent PRs (last 30 days) unless asked otherwise -- Provide actionable insights in your recommendations - -Good luck! 🔍 diff --git a/docs/detecting-rate-limited-prs.md b/docs/detecting-rate-limited-prs.md deleted file mode 100644 index 87d6c239e9f..00000000000 --- a/docs/detecting-rate-limited-prs.md +++ /dev/null @@ -1,211 +0,0 @@ -# Detecting Rate-Limited Pull Requests - -This document provides search queries and GraphQL patterns for detecting pull requests that have failed due to GitHub API rate limiting. - -## Problem Statement - -Copilot-created PRs and automated workflows sometimes fail because the GitHub API rate limit is exceeded during execution. These failures can be difficult to track down without proper tooling. - -## Detection Patterns - -### 1. Text-Based Indicators - -Rate-limited PRs typically contain these text patterns in comments or logs: -- `rate limit` (case-insensitive) -- `API rate limit exceeded` -- `secondary rate limit` -- `abuse detection` -- `403` (HTTP status) combined with "rate" or "limit" -- `429` (HTTP status - Too Many Requests) - -### 2. GitHub Search Queries - -#### Basic Search for Copilot PRs - -Find recent PRs from Copilot agent: -``` -is:pr author:app/copilot-swe-agent -``` - -#### Date-Filtered Search - -Find PRs created in the last 30 days: -``` -is:pr author:app/copilot-swe-agent created:>2026-01-07 -``` - -#### Closed PRs with Comments - -Find closed PRs that have comments (where rate limit messages might appear): -``` -is:pr is:closed comments:>0 created:>2026-01-01 -``` - -#### Repository-Specific Search - -Search in a specific repository: -``` -is:pr repo:github/gh-aw author:app/copilot-swe-agent -``` - -### 3. GraphQL Queries - -#### Query PR Timeline for Comments - -This query fetches a PR's timeline including all comments where rate limit messages typically appear: - -```graphql -query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - pullRequest(number: $number) { - number - title - state - createdAt - closedAt - author { - login - } - url - timelineItems(first: 100, itemTypes: [ISSUE_COMMENT, CLOSED_EVENT]) { - nodes { - __typename - ... on IssueComment { - author { - login - } - body - createdAt - } - } - } - } - } -} -``` - -**Usage with GitHub CLI:** -```bash -gh api graphql -f query='...' -f owner="github" -f repo="gh-aw" -F number=14368 -``` - -#### Search Multiple PRs - -To search multiple PRs efficiently, combine REST API search with GraphQL timeline queries: - -1. First, search for PRs using the REST API: -```bash -gh pr list --state all --limit 50 --json number,title,state,author -``` - -2. Then, for each PR, fetch the timeline using GraphQL to check for rate limit messages. - -### 4. Using the Detection Script - -The `skills/detect-rate-limited-prs/detect-rate-limited-prs.sh` script automates this process: - -#### Check a Specific PR - -```bash -./detect-rate-limited-prs.sh --pr 14368 -``` - -#### Check All Recent PRs - -```bash -./detect-rate-limited-prs.sh --since 2026-01-01 --limit 50 -``` - -#### Check Specific Repository - -```bash -./detect-rate-limited-prs.sh --repo github/gh-aw --state all -``` - -## Output Format - -The detection script outputs JSON: - -```json -{ - "rate_limited_prs": [ - { - "number": 14368, - "title": "Fix rate limiting issue", - "state": "closed", - "created_at": "2026-01-15T10:00:00Z", - "rate_limit_detected_at": "2026-01-15T10:05:00Z", - "error_message": "API rate limit exceeded for installation ID...", - "url": "https://github.com/github/gh-aw/pull/14368" - } - ], - "total_examined": 50, - "total_rate_limited": 1 -} -``` - -## Example: PR #14368 - -This PR is a reference example of a rate-limited PR. To examine it: - -```bash -# Using the detection script -./detect-rate-limited-prs.sh --pr 14368 - -# Using GitHub CLI directly -gh pr view 14368 --json comments,timelineItems - -# Using GraphQL -gh api graphql -f query='query { repository(owner: "github", name: "gh-aw") { pullRequest(number: 14368) { timelineItems(first: 100) { nodes { __typename } } } } }' -``` - -## Rate Limit Prevention Strategies - -1. **Implement Retry Logic**: Add exponential backoff for GitHub API calls -2. **Use GraphQL**: GraphQL queries can be more efficient than REST API for complex data -3. **Cache Results**: Cache API responses when possible -4. **Monitor Rate Limits**: Check rate limit status before making requests -5. **Use Personal Access Tokens**: Different tokens have different rate limits - -## Integration with Workflows - -Use the rate-limit-pr-detector.md workflow to automatically scan for rate-limited PRs: - -```bash -gh workflow run rate-limit-pr-detector.md -``` - -The workflow will: -1. Search for recent Copilot PRs -2. Check each PR's timeline for rate limit indicators -3. Generate a comprehensive report -4. Provide recommendations for prevention - -## Related Resources - -- [GitHub API Rate Limits Documentation](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting) -- [Secondary Rate Limits](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#secondary-rate-limits) -- [Best Practices for Integrators](https://docs.github.com/en/rest/guides/best-practices-for-integrators) - -## Troubleshooting - -### No Results Found - -If the detection script finds no rate-limited PRs: -- Verify the date range is correct -- Check if Copilot PRs use a different author name -- Manually inspect a known rate-limited PR to confirm the detection pattern - -### GraphQL Errors - -If GraphQL queries fail: -- Ensure you have proper authentication: `gh auth status` -- Check you have the required permissions: `pull-requests: read` -- Verify the repository exists and is accessible - -### Rate Limiting While Detecting Rate Limits - -If the detection script itself gets rate limited: -- Reduce the `--limit` parameter to check fewer PRs at once -- Add delays between PR checks -- Use a different authentication token with higher limits diff --git a/docs/rate-limited-prs-quickstart.md b/docs/rate-limited-prs-quickstart.md deleted file mode 100644 index e2927d43945..00000000000 --- a/docs/rate-limited-prs-quickstart.md +++ /dev/null @@ -1,148 +0,0 @@ -# Rate-Limited PR Detection - Quick Start - -This guide shows you how to quickly detect and analyze pull requests that have failed due to GitHub API rate limiting. - -## Quick Detection Methods - -### Method 1: Run the Workflow (Automated) - -The easiest way to detect rate-limited PRs is to run the automated workflow: - -```bash -gh workflow run rate-limit-pr-detector.md -``` - -This will: -- Search for recent Copilot PRs -- Check each PR's timeline for rate limit indicators -- Generate a comprehensive report -- Provide recommendations - -### Method 2: Use the Detection Script (Manual) - -For manual investigation, use the detection script: - -```bash -# Check a specific PR (e.g., PR #14368) -cd skills/detect-rate-limited-prs -./detect-rate-limited-prs.sh --pr 14368 - -# Check all PRs from the last 30 days -./detect-rate-limited-prs.sh --since 2026-01-01 --limit 50 - -# Check a specific repository -./detect-rate-limited-prs.sh --repo github/gh-aw --state all -``` - -### Method 3: GitHub Search (Quick Lookup) - -Use GitHub's web interface or CLI to search: - -```bash -# Find recent Copilot PRs -gh pr list --search "author:app/copilot-swe-agent" - -# View a specific PR with comments -gh pr view 14368 --comments -``` - -## What Gets Detected - -The detection looks for these indicators: - -- **Text patterns**: "rate limit", "API rate limit exceeded", "secondary rate limit" -- **HTTP codes**: 403 (Forbidden), 429 (Too Many Requests) -- **Comments**: From GitHub Actions bot or Copilot agent mentioning rate limiting - -## Example Output - -```json -{ - "rate_limited_prs": [ - { - "number": 14368, - "title": "Fix issue with workflow", - "state": "closed", - "created_at": "2026-01-15T10:00:00Z", - "rate_limit_detected_at": "2026-01-15T10:05:00Z", - "error_message": "API rate limit exceeded for installation ID...", - "url": "https://github.com/github/gh-aw/pull/14368" - } - ], - "total_examined": 50, - "total_rate_limited": 1 -} -``` - -## Understanding the Results - -- **number**: The PR number -- **state**: Current state (open, closed, merged) -- **rate_limit_detected_at**: When the rate limit was first detected -- **error_message**: The actual error message from the PR timeline -- **url**: Direct link to the PR - -## Prevention Strategies - -To avoid rate limiting in the future: - -1. **Add retry logic** with exponential backoff -2. **Use GraphQL** instead of REST API where possible -3. **Cache API responses** to reduce duplicate calls -4. **Monitor rate limits** before making requests -5. **Use different tokens** for different operations - -## Need More Details? - -See the complete documentation: - -- **Full Documentation**: `docs/detecting-rate-limited-prs.md` -- **Skill Documentation**: `skills/detect-rate-limited-prs/SKILL.md` -- **Workflow Details**: `.github/workflows/rate-limit-pr-detector.md` - -## GraphQL Query Example - -For custom queries, use this GraphQL pattern: - -```graphql -query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - pullRequest(number: $number) { - timelineItems(first: 100, itemTypes: [ISSUE_COMMENT]) { - nodes { - ... on IssueComment { - body - createdAt - } - } - } - } - } -} -``` - -## Troubleshooting - -**Script finds no results?** -- Check the date range with `--since` -- Verify Copilot PRs use `app/copilot-swe-agent` as author -- Try manually checking a known rate-limited PR first - -**GraphQL errors?** -- Ensure you're authenticated: `gh auth status` -- Check you have `pull-requests: read` permission - -**Script itself gets rate limited?** -- Reduce the `--limit` parameter -- Add delays between PR checks -- Use a token with higher rate limits - -## Example Real-World Case - -PR #14368 is a reference example of a rate-limited PR. To examine it: - -```bash -./detect-rate-limited-prs.sh --pr 14368 -``` - -This will show you the exact error message and when the rate limiting occurred. diff --git a/skills/detect-rate-limited-prs/SKILL.md b/skills/detect-rate-limited-prs/SKILL.md deleted file mode 100644 index cc2c2735ae5..00000000000 --- a/skills/detect-rate-limited-prs/SKILL.md +++ /dev/null @@ -1,151 +0,0 @@ ---- -name: detect-rate-limited-prs -description: Detect and analyze PRs that failed due to GitHub API rate limiting ---- - -# Detect Rate-Limited PRs Skill - -This skill helps identify pull requests that have failed or encountered issues due to GitHub API rate limiting. - -## Overview - -GitHub API rate limiting can cause Copilot PRs and automated workflows to fail. This skill provides utilities to: -- Search for PRs with rate limit indicators -- Analyze PR timeline events for rate limit messages -- Generate reports on rate-limited PRs - -## Usage - -### Basic Detection - -To search for rate-limited PRs in the current repository: - -```bash -./detect-rate-limited-prs.sh -``` - -### With Custom Date Range - -Search for rate-limited PRs created after a specific date: - -```bash -./detect-rate-limited-prs.sh --since 2026-01-01 -``` - -### Specific Repository - -Search in a different repository: - -```bash -./detect-rate-limited-prs.sh --repo github/gh-aw -``` - -### Detailed Analysis - -Get detailed timeline analysis for a specific PR: - -```bash -./detect-rate-limited-prs.sh --pr 14368 -``` - -## Detection Patterns - -The script looks for these indicators of rate limiting: - -1. **Comment Patterns**: - - "rate limit" - - "API rate limit exceeded" - - "secondary rate limit" - - "abuse detection" - -2. **HTTP Status Codes**: - - 403 (Forbidden) with rate limit context - - 429 (Too Many Requests) - -3. **GitHub Actions Failures**: - - Workflow runs that failed with rate limit errors - - GitHub Actions bot comments about rate limiting - -## Search Query Examples - -### Find Copilot PRs from Last 30 Days - -``` -is:pr author:app/copilot-swe-agent created:>2026-01-07 -``` - -### Find Closed PRs with Comments - -``` -is:pr is:closed comments:>0 created:>2026-01-01 -``` - -### GraphQL Query for PR Timeline - -```graphql -query($owner: String!, $repo: String!, $number: Int!) { - repository(owner: $owner, name: $repo) { - pullRequest(number: $number) { - number - title - state - timelineItems(first: 100, itemTypes: [ISSUE_COMMENT]) { - nodes { - __typename - ... on IssueComment { - author { login } - body - createdAt - } - } - } - } - } -} -``` - -## Output Format - -The script outputs JSON with rate-limited PR information: - -```json -{ - "rate_limited_prs": [ - { - "number": 14368, - "title": "PR Title", - "state": "closed", - "created_at": "2026-01-15T10:00:00Z", - "rate_limit_detected_at": "2026-01-15T10:05:00Z", - "error_message": "API rate limit exceeded", - "url": "https://github.com/github/gh-aw/pull/14368" - } - ], - "total_examined": 50, - "total_rate_limited": 1 -} -``` - -## Requirements - -- GitHub CLI (`gh`) authenticated with appropriate permissions -- `jq` for JSON processing -- `pull-requests: read` permission - -## Example Workflow Integration - -Use this skill in a workflow: - -```yaml ---- -name: Check Rate Limited PRs -on: workflow_dispatch - -jobs: - detect: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Run detection - run: ./skills/detect-rate-limited-prs/detect-rate-limited-prs.sh -``` diff --git a/skills/detect-rate-limited-prs/detect-rate-limited-prs.sh b/skills/detect-rate-limited-prs/detect-rate-limited-prs.sh deleted file mode 100755 index 3d7ddfba0d3..00000000000 --- a/skills/detect-rate-limited-prs/detect-rate-limited-prs.sh +++ /dev/null @@ -1,249 +0,0 @@ -#!/bin/bash -# Detect PRs that failed due to GitHub API rate limiting -# -# Usage: ./detect-rate-limited-prs.sh [OPTIONS] -# -# Options: -# --repo OWNER/REPO Repository to query (default: current repo) -# --since DATE Only check PRs created after this date (ISO 8601) -# --pr NUMBER Analyze a specific PR number -# --state STATE PR state: open, closed, all (default: all) -# --limit N Maximum number of PRs to check (default: 50) - -set -e - -# Default values -REPO="" -SINCE_DATE="" -SPECIFIC_PR="" -STATE="all" -LIMIT=50 - -# Parse arguments -while [[ $# -gt 0 ]]; do - case $1 in - --repo) - REPO="$2" - shift 2 - ;; - --since) - SINCE_DATE="$2" - shift 2 - ;; - --pr) - SPECIFIC_PR="$2" - shift 2 - ;; - --state) - STATE="$2" - shift 2 - ;; - --limit) - LIMIT="$2" - shift 2 - ;; - *) - echo "Unknown option: $1" >&2 - exit 1 - ;; - esac -done - -# Function to check if a PR has rate limit indicators in its timeline -check_pr_for_rate_limiting() { - local pr_number=$1 - local repo_arg="" - - if [[ -n "$REPO" ]]; then - repo_arg="--repo $REPO" - fi - - # Get the repository owner and name - if [[ -n "$REPO" ]]; then - OWNER=$(echo "$REPO" | cut -d'/' -f1) - REPO_NAME=$(echo "$REPO" | cut -d'/' -f2) - else - OWNER=$(gh repo view --json owner --jq '.owner.login') - REPO_NAME=$(gh repo view --json name --jq '.name') - fi - - # Use GraphQL to fetch PR timeline with comments - TIMELINE_RESULT=$(gh api graphql -f query=" -query { - repository(owner: \"$OWNER\", name: \"$REPO_NAME\") { - pullRequest(number: $pr_number) { - number - title - state - createdAt - closedAt - author { - login - } - url - timelineItems(first: 100, itemTypes: [ISSUE_COMMENT, CLOSED_EVENT]) { - nodes { - __typename - ... on IssueComment { - author { - login - } - body - createdAt - } - } - } - } - } -} -") - - # Extract PR info - PR_TITLE=$(echo "$TIMELINE_RESULT" | jq -r '.data.repository.pullRequest.title') - PR_STATE=$(echo "$TIMELINE_RESULT" | jq -r '.data.repository.pullRequest.state') - PR_CREATED=$(echo "$TIMELINE_RESULT" | jq -r '.data.repository.pullRequest.createdAt') - PR_URL=$(echo "$TIMELINE_RESULT" | jq -r '.data.repository.pullRequest.url') - - # Search for rate limit indicators in comments - RATE_LIMIT_COMMENTS=$(echo "$TIMELINE_RESULT" | jq -r ' - .data.repository.pullRequest.timelineItems.nodes[] | - select(.body != null) | - select(.body | test("rate limit|API rate limit|secondary rate limit|abuse detection|429|too many requests"; "i")) | - { - author: .author.login, - body: .body, - created_at: .createdAt - } - ') - - if [[ -n "$RATE_LIMIT_COMMENTS" && "$RATE_LIMIT_COMMENTS" != "null" ]]; then - # Extract the first rate limit message - FIRST_COMMENT=$(echo "$RATE_LIMIT_COMMENTS" | jq -s '.[0]') - ERROR_MESSAGE=$(echo "$FIRST_COMMENT" | jq -r '.body' | head -c 200) - DETECTED_AT=$(echo "$FIRST_COMMENT" | jq -r '.created_at') - - # Output the rate-limited PR info - jq -n \ - --arg number "$pr_number" \ - --arg title "$PR_TITLE" \ - --arg state "$PR_STATE" \ - --arg created_at "$PR_CREATED" \ - --arg detected_at "$DETECTED_AT" \ - --arg error_message "$ERROR_MESSAGE" \ - --arg url "$PR_URL" \ - '{ - number: ($number | tonumber), - title: $title, - state: $state, - created_at: $created_at, - rate_limit_detected_at: $detected_at, - error_message: $error_message, - url: $url - }' - return 0 - else - return 1 - fi -} - -# If specific PR is provided, just check that one -if [[ -n "$SPECIFIC_PR" ]]; then - echo "Checking PR #$SPECIFIC_PR for rate limiting..." >&2 - - RESULT=$(check_pr_for_rate_limiting "$SPECIFIC_PR") - - if [[ $? -eq 0 ]]; then - echo "$RESULT" | jq -s '{ - rate_limited_prs: ., - total_examined: 1, - total_rate_limited: 1 - }' - else - echo "No rate limiting detected in PR #$SPECIFIC_PR" >&2 - jq -n '{ - rate_limited_prs: [], - total_examined: 1, - total_rate_limited: 0 - }' - fi - exit 0 -fi - -# Build search query for multiple PRs -SEARCH_QUERY="is:pr" - -# Add state filter -if [[ "$STATE" != "all" ]]; then - SEARCH_QUERY="$SEARCH_QUERY is:$STATE" -fi - -# Add date filter -if [[ -n "$SINCE_DATE" ]]; then - SEARCH_QUERY="$SEARCH_QUERY created:>$SINCE_DATE" -fi - -# Add Copilot author filter (common source of rate-limited PRs) -SEARCH_QUERY="$SEARCH_QUERY author:app/copilot-swe-agent" - -# Add repo filter -if [[ -n "$REPO" ]]; then - SEARCH_QUERY="$SEARCH_QUERY repo:$REPO" -fi - -echo "Searching for PRs: $SEARCH_QUERY" >&2 - -# Search for PRs -if [[ -n "$REPO" ]]; then - PRS=$(gh pr list --repo "$REPO" --state "$STATE" --limit "$LIMIT" --json number --jq '.[].number') -else - PRS=$(gh pr list --state "$STATE" --limit "$LIMIT" --json number --jq '.[].number') -fi - -if [[ -z "$PRS" ]]; then - echo "No PRs found matching criteria" >&2 - jq -n '{ - rate_limited_prs: [], - total_examined: 0, - total_rate_limited: 0 - }' - exit 0 -fi - -# Check each PR for rate limiting -RATE_LIMITED_PRS=() -TOTAL_EXAMINED=0 - -for pr_number in $PRS; do - echo "Checking PR #$pr_number..." >&2 - TOTAL_EXAMINED=$((TOTAL_EXAMINED + 1)) - - if RESULT=$(check_pr_for_rate_limiting "$pr_number" 2>/dev/null); then - RATE_LIMITED_PRS+=("$RESULT") - echo " ✗ Rate limiting detected in PR #$pr_number" >&2 - else - echo " ✓ No rate limiting in PR #$pr_number" >&2 - fi -done - -# Output final results -TOTAL_RATE_LIMITED=${#RATE_LIMITED_PRS[@]} - -if [[ $TOTAL_RATE_LIMITED -eq 0 ]]; then - jq -n \ - --arg examined "$TOTAL_EXAMINED" \ - '{ - rate_limited_prs: [], - total_examined: ($examined | tonumber), - total_rate_limited: 0 - }' -else - # Combine all rate-limited PRs into a JSON array - printf '%s\n' "${RATE_LIMITED_PRS[@]}" | jq -s \ - --arg examined "$TOTAL_EXAMINED" \ - --arg rate_limited "$TOTAL_RATE_LIMITED" \ - '{ - rate_limited_prs: ., - total_examined: ($examined | tonumber), - total_rate_limited: ($rate_limited | tonumber) - }' -fi From 312ceb704eb25f3fb255ded4b2ab67e802d6a7f9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:45:56 +0000 Subject: [PATCH 6/8] Fix rate limit date check to use full ISO timestamp Use full ISO timestamp instead of just date for accurate last-hour filtering Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/issue-monster.lock.yml | 4 ++-- .github/workflows/issue-monster.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 1050144feb1..537c136acdd 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -21,7 +21,7 @@ # # The Cookie Monster of issues - assigns issues to Copilot agents one at a time # -# frontmatter-hash: 6ee7ace3c0aa48b0df83c01e3d00b75cfd4a988e329f43b1310131cebf57ce06 +# frontmatter-hash: 05a7be74ddab6a1c956c2f32d4c61db6e88a8f4800e5e339173dc218faf19b7f name: "Issue Monster" "on": @@ -1163,7 +1163,7 @@ jobs: core.info('Checking for recent rate-limited PRs...'); const rateLimitCheckDate = new Date(); rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour - const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('T')[0]; + const rateLimitCheckISO = rateLimitCheckDate.toISOString(); const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`; const recentPRsResponse = await github.rest.search.issuesAndPullRequests({ diff --git a/.github/workflows/issue-monster.md b/.github/workflows/issue-monster.md index 7c3c723be3e..ad4f89e40ee 100644 --- a/.github/workflows/issue-monster.md +++ b/.github/workflows/issue-monster.md @@ -51,7 +51,7 @@ jobs: core.info('Checking for recent rate-limited PRs...'); const rateLimitCheckDate = new Date(); rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour - const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('T')[0]; + const rateLimitCheckISO = rateLimitCheckDate.toISOString(); const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`; const recentPRsResponse = await github.rest.search.issuesAndPullRequests({ From b7c7191e0e699f18c21b34bf63913f09d454541e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:46:53 +0000 Subject: [PATCH 7/8] Improve timestamp format for GitHub search API compatibility Format timestamp as YYYY-MM-DDTHH:MM:SSZ without milliseconds Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/issue-monster.lock.yml | 5 +++-- .github/workflows/issue-monster.md | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 537c136acdd..1b5fe285de4 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -21,7 +21,7 @@ # # The Cookie Monster of issues - assigns issues to Copilot agents one at a time # -# frontmatter-hash: 05a7be74ddab6a1c956c2f32d4c61db6e88a8f4800e5e339173dc218faf19b7f +# frontmatter-hash: 4f5ae406acc40ef2c9e567820a3be71a99715c6122346359b37b2b3cdfd12bbe name: "Issue Monster" "on": @@ -1163,7 +1163,8 @@ jobs: core.info('Checking for recent rate-limited PRs...'); const rateLimitCheckDate = new Date(); rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour - const rateLimitCheckISO = rateLimitCheckDate.toISOString(); + // Format as YYYY-MM-DDTHH:MM:SS for GitHub search API + const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('.')[0] + 'Z'; const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`; const recentPRsResponse = await github.rest.search.issuesAndPullRequests({ diff --git a/.github/workflows/issue-monster.md b/.github/workflows/issue-monster.md index ad4f89e40ee..a6e3c968ee4 100644 --- a/.github/workflows/issue-monster.md +++ b/.github/workflows/issue-monster.md @@ -51,7 +51,8 @@ jobs: core.info('Checking for recent rate-limited PRs...'); const rateLimitCheckDate = new Date(); rateLimitCheckDate.setHours(rateLimitCheckDate.getHours() - 1); // Check last hour - const rateLimitCheckISO = rateLimitCheckDate.toISOString(); + // Format as YYYY-MM-DDTHH:MM:SS for GitHub search API + const rateLimitCheckISO = rateLimitCheckDate.toISOString().split('.')[0] + 'Z'; const recentPRsQuery = `is:pr author:app/copilot-swe-agent created:>${rateLimitCheckISO} repo:${owner}/${repo}`; const recentPRsResponse = await github.rest.search.issuesAndPullRequests({ From ccd3b88032001296ffa97142e4b8b926a4ab748f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:58:06 +0000 Subject: [PATCH 8/8] Merge main and recompile workflows - Merged main branch - Recompiled all workflows with updated configurations - Issue Monster schedule: every 10m -> every 30m - Issue Monster skip-if-match max: 9 -> 5 - Added mood.md import from main branch Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .github/workflows/issue-monster.lock.yml | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.github/workflows/issue-monster.lock.yml b/.github/workflows/issue-monster.lock.yml index 1b5fe285de4..15a077173d8 100644 --- a/.github/workflows/issue-monster.lock.yml +++ b/.github/workflows/issue-monster.lock.yml @@ -21,15 +21,19 @@ # # The Cookie Monster of issues - assigns issues to Copilot agents one at a time # -# frontmatter-hash: 4f5ae406acc40ef2c9e567820a3be71a99715c6122346359b37b2b3cdfd12bbe +# Resolved workflow manifest: +# Imports: +# - shared/mood.md +# +# frontmatter-hash: 6d27381fbefe1f8df297e560d35c99e7954fe5d862a9dfc1d1233ac4d11711d9 name: "Issue Monster" "on": schedule: - - cron: "*/10 * * * *" - # Friendly format: every 10m + - cron: "*/30 * * * *" + # Friendly format: every 30m # skip-if-match: # Skip-if-match processed as search check in pre-activation job - # max: 9 + # max: 5 # query: is:pr is:open is:draft author:app/copilot-swe-agent # skip-if-no-match: is:issue is:open # Skip-if-no-match processed as search check in pre-activation job workflow_dispatch: @@ -577,6 +581,13 @@ jobs: PROMPT_EOF cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + PROMPT_EOF + cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" + ## Team Mood + + We are in release mode, focusing on quality and stability. Skip any work that does not directly improve quality or stability. Non-critical fixes or updates are not needed and should be skipped or canceled. If your task does not clearly contribute to product stability or is not mission critical, you should do nothing (call noop). + + PROMPT_EOF cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT" {{#runtime-import .github/workflows/issue-monster.md}} @@ -1039,7 +1050,7 @@ jobs: env: GH_AW_SKIP_QUERY: "is:pr is:open is:draft author:app/copilot-swe-agent" GH_AW_WORKFLOW_NAME: "Issue Monster" - GH_AW_SKIP_MAX_MATCHES: "9" + GH_AW_SKIP_MAX_MATCHES: "5" with: script: | const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');