diff --git a/.github/agents/dependabot-pr-reviewer.agent.md b/.github/agents/dependabot-pr-reviewer.agent.md index 2a616892..e31d0955 100644 --- a/.github/agents/dependabot-pr-reviewer.agent.md +++ b/.github/agents/dependabot-pr-reviewer.agent.md @@ -93,17 +93,19 @@ Render the review body as markdown in this order: ## Validation Signal -The agent runs AFTER the `PR Validation` orchestrator finishes (`workflow_run` -trigger). Use the deterministic CI conclusion as the canonical validation -signal. Do not invoke `uv`, `pytest`, `npm ci`, `terraform`, or `go` from the -bash tool — those binaries live on the host runner and are not visible -inside the AWF firewall sandbox. +The agent runs via `pull_request_target` and may execute BEFORE the +`PR Validation` orchestrator has completed. Treat the deterministic CI +conclusion as the canonical validation signal when it is available, and as +`pending` otherwise. Do not invoke `uv`, `pytest`, `npm ci`, `terraform`, +or `go` from the bash tool — those binaries live on the host runner and +are not visible inside the AWF firewall sandbox. The orchestrator's overall conclusion is injected into the prompt as -`PR_VALIDATION_CONCLUSION` (one of `success`, `failure`, `cancelled`, -`neutral`, `skipped`, `timed_out`, `action_required`). Map the touched -surfaces to the per-job check runs below and read each conclusion via the -`github` MCP `pull_requests` toolset (or `GET /repos/{owner}/{repo}/commits/{sha}/check-runs`). +`PR_VALIDATION_CONCLUSION` (one of `pending`, `in_progress:`, +`success`, `failure`, `cancelled`, `neutral`, `skipped`, `timed_out`, +`action_required`, or `unknown`). Map the touched surfaces to the +per-job check runs below and read each conclusion via the `github` MCP +`pull_requests` toolset (or `GET /repos/{owner}/{repo}/commits/{sha}/check-runs`). ### Surface to Check Run Map @@ -165,9 +167,10 @@ review body with three parts: violation, peer-dep conflict, breaking-changelog quote), prepend `⚠️ Maintainer review recommended` to the top of the review body once. -If the orchestrator conclusion is unavailable (workflow not yet completed, -PR resolution failed, or check-runs API returns empty), state: -`⚠️ Deterministic CI conclusion unavailable; verdict is advisory only.` +If the orchestrator conclusion is unavailable or still in progress +(`pending`, `in_progress:*`, or `unknown`; PR resolution failed; or the +check-runs API returns empty), state: +`⚠️ Deterministic CI conclusion not yet available; verdict is advisory only.` and keep the verdict at `COMMENT`. ### Verdict Adjustment @@ -181,8 +184,9 @@ and keep the verdict at `COMMENT`. check name plus its `html_url`. Do NOT skip enrichment — maintainers rely on the advisory output to triage which package in a grouped PR caused the failure. -* `PR_VALIDATION_CONCLUSION` is `neutral`, `skipped`, or `action_required` - → verdict stays at `COMMENT`; body explains the inconclusive state. +* `PR_VALIDATION_CONCLUSION` is `neutral`, `skipped`, `action_required`, + `pending`, `in_progress:*`, or `unknown` → verdict stays at `COMMENT`; + body explains the inconclusive or pending state. * The Isaac Sim ABI guard is sticky: a `numpy` 2.x bump keeps the verdict at `COMMENT` and forces the high-risk banner regardless of CI conclusion. diff --git a/.github/workflows/aw-dependabot-pr-review.lock.yml b/.github/workflows/aw-dependabot-pr-review.lock.yml index 6a105923..69063f91 100644 --- a/.github/workflows/aw-dependabot-pr-review.lock.yml +++ b/.github/workflows/aw-dependabot-pr-review.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"9c5977b664a092da5bda82443950d104c747b4835e843f0a79d086a5a5dc8926","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"} +# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"46234d20433a81ce7491f5c6933eb4c75977de922d188a0253fa2392eff22580","compiler_version":"v0.68.3","strict":true,"agent_id":"copilot"} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"de0fac2e4500dabe0009e67214ff5f5447ce83dd","version":"v6.0.2"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"373c709c69115d41ff229c7e5df9f8788daa9553","version":"v9"},{"repo":"actions/setup-go","sha":"4a3601121dd01d1626a1e23e37211e3254c1c06c","version":"v6.4.0"},{"repo":"actions/setup-node","sha":"53b83947a5a98c8d113130e565377fae1a50d02f","version":"v6.3.0"},{"repo":"actions/setup-python","sha":"a309ff8b426b58ec0e2a45f0f869d46889d02405","version":"v6.2.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"astral-sh/setup-uv","sha":"cec208311dfd045dd5311c1add060b2062131d57","version":"v8.0.0"},{"repo":"github/gh-aw-actions/setup","sha":"ba90f2186d7ad780ec640f364005fa24e797b360","version":"v0.68.3"},{"repo":"hashicorp/setup-terraform","sha":"5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85","version":"5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85"},{"repo":"terraform-linters/setup-tflint","sha":"b480b8fcdaa6f2c577f8e4fa799e89e756bb7c93","version":"b480b8fcdaa6f2c577f8e4fa799e89e756bb7c93"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.20"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.20"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.2.19"},{"image":"ghcr.io/github/github-mcp-server:v0.32.0"},{"image":"node:lts-alpine"}]} # ___ _ _ # / _ \ | | (_) @@ -59,39 +59,38 @@ name: "AW Dependabot PR Review" "on": # bots: # Bots processed as bot check in pre-activation job # - dependabot[bot] # Bots processed as bot check in pre-activation job + pull_request_target: + branches: + - main + types: + - opened + - synchronize + - reopened # roles: # Roles processed as role check in pre-activation job # - admin # Roles processed as role check in pre-activation job # - maintainer # Roles processed as role check in pre-activation job # - write # Roles processed as role check in pre-activation job - workflow_run: - # zizmor: ignore[dangerous-triggers] - workflow_run trigger is secured with role and fork validation - branches: - - dependabot/** - types: - - completed - workflows: - - PR Validation permissions: {} concurrency: - group: "gh-aw-${{ github.workflow }}" + group: "gh-aw-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref || github.run_id }}" + cancel-in-progress: true run-name: "AW Dependabot PR Review" jobs: activation: needs: pre_activation - # zizmor: ignore[dangerous-triggers] - workflow_run trigger is secured with role and fork validation if: > - (needs.pre_activation.outputs.activated == 'true' && (github.event.workflow_run.event == 'pull_request' && - github.event.workflow_run.conclusion != null)) && (github.event_name != 'workflow_run' || github.event.workflow_run.repository.id == github.repository_id && - (!(github.event.workflow_run.repository.fork))) + needs.pre_activation.outputs.activated == 'true' && (github.event.pull_request != null && startsWith(github.event.pull_request.head.ref, 'dependabot/') && + github.event.pull_request.user.login == 'dependabot[bot]' && !github.event.pull_request.draft) runs-on: ubuntu-slim permissions: actions: read contents: read outputs: + body: ${{ steps.sanitized.outputs.body }} comment_id: "" comment_repo: "" lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }} @@ -99,6 +98,8 @@ jobs: secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }} setup-trace-id: ${{ steps.setup.outputs.trace-id }} stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }} + text: ${{ steps.sanitized.outputs.text }} + title: ${{ steps.sanitized.outputs.title }} steps: - name: Setup Scripts id: setup @@ -169,6 +170,17 @@ jobs: setupGlobals(core, github, context, exec, io, getOctokit); const { main } = require('${{ runner.temp }}/gh-aw/actions/check_version_updates.cjs'); await main(); + - name: Compute current body text + id: sanitized + uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 + env: + GH_AW_ALLOWED_BOTS: "dependabot[bot]" + with: + script: | + const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs'); + setupGlobals(core, github, context, exec, io, getOctokit); + const { main } = require('${{ runner.temp }}/gh-aw/actions/compute_text.cjs'); + await main(); - name: Create prompt with built-in context env: GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt @@ -185,14 +197,14 @@ jobs: run: | bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh" { - cat << 'GH_AW_PROMPT_2a2f11d60a10f74e_EOF' + cat << 'GH_AW_PROMPT_5734b58a8c868ede_EOF' - GH_AW_PROMPT_2a2f11d60a10f74e_EOF + GH_AW_PROMPT_5734b58a8c868ede_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/xpia.md" cat "${RUNNER_TEMP}/gh-aw/prompts/temp_folder_prompt.md" cat "${RUNNER_TEMP}/gh-aw/prompts/markdown.md" cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md" - cat << 'GH_AW_PROMPT_2a2f11d60a10f74e_EOF' + cat << 'GH_AW_PROMPT_5734b58a8c868ede_EOF' Tools: add_comment(max:2), create_pull_request_review_comment(max:5), submit_pull_request_review, missing_tool, missing_data, noop @@ -224,13 +236,13 @@ jobs: {{/if}} - GH_AW_PROMPT_2a2f11d60a10f74e_EOF + GH_AW_PROMPT_5734b58a8c868ede_EOF cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md" - cat << 'GH_AW_PROMPT_2a2f11d60a10f74e_EOF' + cat << 'GH_AW_PROMPT_5734b58a8c868ede_EOF' {{#runtime-import .github/agents/dependabot-pr-reviewer.agent.md}} {{#runtime-import .github/workflows/aw-dependabot-pr-review.md}} - GH_AW_PROMPT_2a2f11d60a10f74e_EOF + GH_AW_PROMPT_5734b58a8c868ede_EOF } > "$GH_AW_PROMPT" - name: Interpolate variables and render templates uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 @@ -306,8 +318,6 @@ jobs: actions: read contents: read pull-requests: read - concurrency: - group: "gh-aw-copilot-${{ github.workflow }}" env: DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} GH_AW_ASSETS_ALLOWED_EXTS: "" @@ -401,10 +411,10 @@ jobs: with: tflint_version: latest - id: resolve-pr - name: Resolve Dependabot PR context from triggering workflow_run + name: Resolve Dependabot PR context and fetch PR Validation status uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9 with: - script: "const wr = context.payload.workflow_run;\nif (!wr) {\n core.setFailed('workflow_run payload missing');\n return;\n}\ncore.exportVariable('PR_VALIDATION_CONCLUSION', wr.conclusion || 'unknown');\ncore.exportVariable('PR_VALIDATION_RUN_URL', wr.html_url || '');\ncore.exportVariable('PR_HEAD_SHA', wr.head_sha || '');\n\nconst prs = wr.pull_requests || [];\nlet prNumber = prs.length ? prs[0].number : null;\nif (!prNumber && wr.head_branch) {\n // workflow_run may not populate pull_requests for forks; resolve via search.\n const { data: search } = await github.rest.search.issuesAndPullRequests({\n q: `repo:${context.repo.owner}/${context.repo.repo} is:pr head:${wr.head_branch} state:open`,\n per_page: 1,\n });\n if (search.items.length) prNumber = search.items[0].number;\n}\nif (!prNumber) {\n core.warning('Could not resolve a PR for this workflow_run; emitting noop.');\n core.exportVariable('PR_DEPENDABOT_SKIP_REASON', 'no-pr-resolved');\n return;\n}\nconst { data: pr } = await github.rest.pulls.get({\n owner: context.repo.owner,\n repo: context.repo.repo,\n pull_number: prNumber,\n});\nif (pr.user.login !== 'dependabot[bot]') {\n core.exportVariable('PR_DEPENDABOT_SKIP_REASON', 'not-dependabot');\n return;\n}\nif (pr.draft) {\n core.exportVariable('PR_DEPENDABOT_SKIP_REASON', 'draft');\n return;\n}\ncore.exportVariable('PR_NUMBER', String(pr.number));\ncore.exportVariable('PR_TITLE', pr.title);\ncore.exportVariable('PR_HEAD_REF', pr.head.ref);\ncore.exportVariable('PR_BASE_REF', pr.base.ref);\ncore.exportVariable('PR_AUTHOR', pr.user.login);\ncore.info(`Resolved PR #${pr.number} (${pr.title}); PR Validation conclusion: ${wr.conclusion}`);\n" + script: "const pr = context.payload.pull_request;\nif (!pr) {\n core.setFailed('pull_request payload missing');\n return;\n}\nif (pr.user.login !== 'dependabot[bot]') {\n core.exportVariable('PR_DEPENDABOT_SKIP_REASON', 'not-dependabot');\n return;\n}\nif (pr.draft) {\n core.exportVariable('PR_DEPENDABOT_SKIP_REASON', 'draft');\n return;\n}\ncore.exportVariable('PR_NUMBER', String(pr.number));\ncore.exportVariable('PR_TITLE', pr.title);\ncore.exportVariable('PR_HEAD_REF', pr.head.ref);\ncore.exportVariable('PR_BASE_REF', pr.base.ref);\ncore.exportVariable('PR_AUTHOR', pr.user.login);\ncore.exportVariable('PR_HEAD_SHA', pr.head.sha);\n\n// Look up the most recent PR Validation workflow run for this head SHA.\n// It may still be in progress when pull_request_target fires.\nlet conclusion = 'pending';\nlet runUrl = '';\ntry {\n const { data } = await github.rest.actions.listWorkflowRunsForRepo({\n owner: context.repo.owner,\n repo: context.repo.repo,\n head_sha: pr.head.sha,\n event: 'pull_request',\n per_page: 30,\n });\n const validation = (data.workflow_runs || []).find(r => r.name === 'PR Validation');\n if (validation) {\n runUrl = validation.html_url || '';\n conclusion = validation.status === 'completed'\n ? (validation.conclusion || 'unknown')\n : `in_progress:${validation.status}`;\n }\n} catch (err) {\n core.warning(`Failed to look up PR Validation run: ${err.message}`);\n}\ncore.exportVariable('PR_VALIDATION_CONCLUSION', conclusion);\ncore.exportVariable('PR_VALIDATION_RUN_URL', runUrl);\ncore.info(`Resolved PR #${pr.number} (${pr.title}); PR Validation conclusion: ${conclusion}`);\n" - name: Configure Git credentials env: @@ -456,9 +466,9 @@ jobs: mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs" mkdir -p /tmp/gh-aw/safeoutputs mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs - cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_dca91df37c117583_EOF' + cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_a6000a4bbc0dff64_EOF' {"add_comment":{"max":2,"target":"triggering"},"create_pull_request_review_comment":{"max":5,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"report_incomplete":{},"submit_pull_request_review":{"max":1}} - GH_AW_SAFE_OUTPUTS_CONFIG_dca91df37c117583_EOF + GH_AW_SAFE_OUTPUTS_CONFIG_a6000a4bbc0dff64_EOF - name: Write Safe Outputs Tools env: GH_AW_TOOLS_META_JSON: | @@ -699,7 +709,7 @@ jobs: 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 MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD -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_GUARD_MIN_INTEGRITY -e GITHUB_MCP_GUARD_REPOS -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 -e GH_AW_SAFE_OUTPUTS_PORT -e GH_AW_SAFE_OUTPUTS_API_KEY -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.2.19' mkdir -p /home/runner/.copilot - cat << GH_AW_MCP_CONFIG_9807ed4ec462af68_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh" + cat << GH_AW_MCP_CONFIG_658c528aa9fae3bb_EOF | bash "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.sh" { "mcpServers": { "github": { @@ -740,7 +750,7 @@ jobs: "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}" } } - GH_AW_MCP_CONFIG_9807ed4ec462af68_EOF + GH_AW_MCP_CONFIG_658c528aa9fae3bb_EOF - name: Download activation artifact uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: @@ -1248,7 +1258,7 @@ jobs: pre_activation: if: > - github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion != null + github.event.pull_request != null && startsWith(github.event.pull_request.head.ref, 'dependabot/') && github.event.pull_request.user.login == 'dependabot[bot]' && !github.event.pull_request.draft runs-on: ubuntu-slim outputs: activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }} diff --git a/.github/workflows/aw-dependabot-pr-review.md b/.github/workflows/aw-dependabot-pr-review.md index cafc5c6b..d9b7f701 100644 --- a/.github/workflows/aw-dependabot-pr-review.md +++ b/.github/workflows/aw-dependabot-pr-review.md @@ -4,14 +4,15 @@ description: Advisory agentic review of Dependabot dependency update PRs for phy engine: copilot timeout-minutes: 15 if: > - github.event.workflow_run.event == 'pull_request' && - github.event.workflow_run.conclusion != null + github.event.pull_request != null && + startsWith(github.event.pull_request.head.ref, 'dependabot/') && + github.event.pull_request.user.login == 'dependabot[bot]' && + !github.event.pull_request.draft on: - workflow_run: - workflows: ["PR Validation"] - types: [completed] + pull_request_target: + types: [opened, synchronize, reopened] branches: - - "dependabot/**" + - main bots: ["dependabot[bot]"] roles: [admin, maintainer, write] permissions: @@ -63,40 +64,16 @@ steps: uses: terraform-linters/setup-tflint@b480b8fcdaa6f2c577f8e4fa799e89e756bb7c93 # v6.2.2 with: tflint_version: latest - - name: Resolve Dependabot PR context from triggering workflow_run + - name: Resolve Dependabot PR context and fetch PR Validation status id: resolve-pr uses: actions/github-script@373c709c69115d41ff229c7e5df9f8788daa9553 # v9.0.0 with: script: | - const wr = context.payload.workflow_run; - if (!wr) { - core.setFailed('workflow_run payload missing'); + const pr = context.payload.pull_request; + if (!pr) { + core.setFailed('pull_request payload missing'); return; } - core.exportVariable('PR_VALIDATION_CONCLUSION', wr.conclusion || 'unknown'); - core.exportVariable('PR_VALIDATION_RUN_URL', wr.html_url || ''); - core.exportVariable('PR_HEAD_SHA', wr.head_sha || ''); - - const prs = wr.pull_requests || []; - let prNumber = prs.length ? prs[0].number : null; - if (!prNumber && wr.head_branch) { - // workflow_run may not populate pull_requests for forks; resolve via search. - const { data: search } = await github.rest.search.issuesAndPullRequests({ - q: `repo:${context.repo.owner}/${context.repo.repo} is:pr head:${wr.head_branch} state:open`, - per_page: 1, - }); - if (search.items.length) prNumber = search.items[0].number; - } - if (!prNumber) { - core.warning('Could not resolve a PR for this workflow_run; emitting noop.'); - core.exportVariable('PR_DEPENDABOT_SKIP_REASON', 'no-pr-resolved'); - return; - } - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: prNumber, - }); if (pr.user.login !== 'dependabot[bot]') { core.exportVariable('PR_DEPENDABOT_SKIP_REASON', 'not-dependabot'); return; @@ -110,7 +87,33 @@ steps: core.exportVariable('PR_HEAD_REF', pr.head.ref); core.exportVariable('PR_BASE_REF', pr.base.ref); core.exportVariable('PR_AUTHOR', pr.user.login); - core.info(`Resolved PR #${pr.number} (${pr.title}); PR Validation conclusion: ${wr.conclusion}`); + core.exportVariable('PR_HEAD_SHA', pr.head.sha); + + // Look up the most recent PR Validation workflow run for this head SHA. + // It may still be in progress when pull_request_target fires. + let conclusion = 'pending'; + let runUrl = ''; + try { + const { data } = await github.rest.actions.listWorkflowRunsForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + head_sha: pr.head.sha, + event: 'pull_request', + per_page: 30, + }); + const validation = (data.workflow_runs || []).find(r => r.name === 'PR Validation'); + if (validation) { + runUrl = validation.html_url || ''; + conclusion = validation.status === 'completed' + ? (validation.conclusion || 'unknown') + : `in_progress:${validation.status}`; + } + } catch (err) { + core.warning(`Failed to look up PR Validation run: ${err.message}`); + } + core.exportVariable('PR_VALIDATION_CONCLUSION', conclusion); + core.exportVariable('PR_VALIDATION_RUN_URL', runUrl); + core.info(`Resolved PR #${pr.number} (${pr.title}); PR Validation conclusion: ${conclusion}`); tools: github: toolsets: [context, repos, pull_requests] @@ -145,15 +148,20 @@ Advisory-only review of Dependabot-authored pull requests in microsoft/physical- ## Trigger Posture -This workflow runs via `workflow_run` after the `PR Validation` orchestrator completes on a PR targeting `main`. The deterministic CI conclusion is the canonical validation signal — read it from the `PR_VALIDATION_CONCLUSION` environment variable injected by the resolver step. The agent must never attempt to run validation tooling (`uv`, `pytest`, `npm ci`, `terraform`, `go`) from the bash tool because those binaries are not visible inside the AWF firewall sandbox. +This workflow runs via `pull_request_target` on Dependabot PRs targeting `main` (head branch matching `dependabot/**`). +It executes in the base-repository context so safe-output handlers receive a real `github.event.pull_request` payload +and can post inline review comments and a single submitted review. The `PR Validation` orchestrator runs in parallel — +its conclusion is queried at runtime by the resolver step and may be `pending` if the orchestrator has not finished yet. +The agent must never attempt to run validation tooling (`uv`, `pytest`, `npm ci`, `terraform`, `go`) from the bash tool +because those binaries are not visible inside the AWF firewall sandbox. The resolver step exports these environment variables for the agent to read: * `PR_NUMBER` — the Dependabot PR number under review * `PR_TITLE`, `PR_HEAD_REF`, `PR_BASE_REF`, `PR_AUTHOR`, `PR_HEAD_SHA` -* `PR_VALIDATION_CONCLUSION` — `success`, `failure`, `cancelled`, `neutral`, `skipped`, `timed_out`, or `action_required` -* `PR_VALIDATION_RUN_URL` — direct link to the `PR Validation` run -* `PR_DEPENDABOT_SKIP_REASON` (optional) — set when the resolver determined the trigger should be skipped (`no-pr-resolved`, `not-dependabot`, `draft`) +* `PR_VALIDATION_CONCLUSION` — `pending`, `in_progress:`, `success`, `failure`, `cancelled`, `neutral`, `skipped`, `timed_out`, `action_required`, or `unknown` +* `PR_VALIDATION_RUN_URL` — direct link to the `PR Validation` run, or empty when no run exists yet +* `PR_DEPENDABOT_SKIP_REASON` (optional) — set when the resolver determined the trigger should be skipped (`not-dependabot`, `draft`) When `PR_DEPENDABOT_SKIP_REASON` is set, emit a `noop` with the reason as the rationale and stop.