diff --git a/.github/aw/actions-lock.json b/.github/aw/actions-lock.json
index 921c423791..139f2b18c6 100644
--- a/.github/aw/actions-lock.json
+++ b/.github/aw/actions-lock.json
@@ -25,6 +25,11 @@
"version": "v9.0.0",
"sha": "3a2844b7e9c422d3c10d287c895573f7108da1b3"
},
+ "actions/setup-dotnet@v5": {
+ "repo": "actions/setup-dotnet",
+ "version": "v5",
+ "sha": "9a946fdbd5fb07b82b2f5a4466058b876ab72bb2"
+ },
"actions/setup-python@v6.2.0": {
"repo": "actions/setup-python",
"version": "v6.2.0",
@@ -40,10 +45,10 @@
"version": "v4.36.0",
"sha": "7211b7c8077ea37d8641b6271f6a365a22a5fbfa"
},
- "github/gh-aw-actions/setup@v0.76.1": {
+ "github/gh-aw-actions/setup@v0.75.4": {
"repo": "github/gh-aw-actions/setup",
- "version": "v0.76.1",
- "sha": "46d564922b082d0db93244972e8005ea6904ee5f"
+ "version": "v0.75.4",
+ "sha": "9f050961da586148d135e113d8bb025185cdf2b8"
},
"super-linter/super-linter@v8.6.0": {
"repo": "super-linter/super-linter",
diff --git a/.github/workflows/build-failure-analysis-command.lock.yml b/.github/workflows/build-failure-analysis-command.lock.yml
index 8249a9d099..dad484f449 100644
--- a/.github/workflows/build-failure-analysis-command.lock.yml
+++ b/.github/workflows/build-failure-analysis-command.lock.yml
@@ -1,5 +1,5 @@
-# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"cdf7e6dd50799a6020e37115183a80c698040971ea601872f5f10f6fc7484afc","compiler_version":"v0.75.4","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":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7","version":"v5.2.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"9f050961da586148d135e113d8bb025185cdf2b8","version":"v0.75.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.53"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.18"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]}
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"8c43d00c2a30f216209a4a2279555645a8844600bafa122cef9d19bf7356798e","compiler_version":"v0.75.4","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":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"9a946fdbd5fb07b82b2f5a4466058b876ab72bb2","version":"v5"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"9f050961da586148d135e113d8bb025185cdf2b8","version":"v0.75.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.53"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.18"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]}
# ___ _ _
# / _ \ | | (_)
# | |_| | __ _ ___ _ __ | |_ _ ___
@@ -40,12 +40,15 @@
#
# Custom actions used:
# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (source v6)
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8)
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
-# - actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
+# - actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7)
# - github/gh-aw-actions/setup@9f050961da586148d135e113d8bb025185cdf2b8 # v0.75.4
#
# Container images used:
@@ -84,8 +87,10 @@ env:
jobs:
activation:
- needs: pre_activation
- if: needs.pre_activation.outputs.activated == 'true'
+ needs:
+ - build
+ - pre_activation
+ if: needs.pre_activation.outputs.activated == 'true' && (needs.build.outputs.outcome == 'failure')
runs-on: ubuntu-slim
permissions:
actions: read
@@ -250,20 +255,20 @@ jobs:
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_d129ae11e5fa59d9_EOF'
+ cat << 'GH_AW_PROMPT_dd24a643e7187558_EOF'
- GH_AW_PROMPT_d129ae11e5fa59d9_EOF
+ GH_AW_PROMPT_dd24a643e7187558_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_d129ae11e5fa59d9_EOF'
+ cat << 'GH_AW_PROMPT_dd24a643e7187558_EOF'
- Tools: add_comment, create_pull_request_review_comment(max:10), missing_tool, missing_data, noop
+ Tools: add_comment(max:5), create_pull_request_review_comment(max:25), missing_tool, missing_data, noop(max:5)
- GH_AW_PROMPT_d129ae11e5fa59d9_EOF
+ GH_AW_PROMPT_dd24a643e7187558_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_d129ae11e5fa59d9_EOF'
+ cat << 'GH_AW_PROMPT_dd24a643e7187558_EOF'
The following GitHub context information is available for this workflow:
{{#if github.actor}}
@@ -292,16 +297,16 @@ jobs:
{{/if}}
- GH_AW_PROMPT_d129ae11e5fa59d9_EOF
+ GH_AW_PROMPT_dd24a643e7187558_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
if [ "$GITHUB_EVENT_NAME" = "issue_comment" ] && [ -n "$GH_AW_IS_PR_COMMENT" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review_comment" ] || [ "$GITHUB_EVENT_NAME" = "pull_request_review" ]; then
cat "${RUNNER_TEMP}/gh-aw/prompts/pr_context_prompt.md"
fi
- cat << 'GH_AW_PROMPT_d129ae11e5fa59d9_EOF'
+ cat << 'GH_AW_PROMPT_dd24a643e7187558_EOF'
{{#runtime-import .github/workflows/shared/build-failure-analysis-shared.md}}
{{#runtime-import .github/workflows/build-failure-analysis-command.md}}
- GH_AW_PROMPT_d129ae11e5fa59d9_EOF
+ GH_AW_PROMPT_dd24a643e7187558_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@@ -383,7 +388,10 @@ jobs:
retention-days: 1
agent:
- needs: activation
+ needs:
+ - activation
+ - build
+ if: needs.build.outputs.outcome == 'failure'
runs-on: ubuntu-latest
permissions:
contents: read
@@ -437,41 +445,24 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- - name: Setup .NET
- uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
- with:
- dotnet-version: '8.0'
- name: Create gh-aw temp directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
- name: Configure gh CLI for GitHub Enterprise
run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
env:
GH_TOKEN: ${{ github.token }}
+ - name: Download analysis artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8)
+ with:
+ name: build-failure-analysis-data
+ path: /tmp/
+ - name: Setup .NET (for NuGet MCP Server)
+ uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
+ with:
+ dotnet-version: 9.0.x
- continue-on-error: true
- id: build
- name: Build with binary log
- run: |
- set -o pipefail
- ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log
- - if: always()
- name: Put dotnet on the path
- run: echo "$PWD/.dotnet" >> $GITHUB_PATH
- - id: find-binlog
- name: Locate binlog
- run: "BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\\n' 2>/dev/null \\\n | sort -rn | head -1 | cut -d' ' -f2-)\nif [ -n \"$BINLOG\" ] && [ -f \"$BINLOG\" ]; then\n BINLOG=$(realpath \"$BINLOG\")\n echo \"found=true\" >> \"$GITHUB_OUTPUT\"\n echo \"path=$BINLOG\" >> \"$GITHUB_OUTPUT\"\nelse\n echo \"found=false\" >> \"$GITHUB_OUTPUT\"\nfi\n"
- - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
- name: Install binlog-mcp
- run: "mkdir -p /tmp/binlog-tool\ncat > /tmp/binlog-tool/nuget.config <<'EOF'\n\n\n \n \n \n \n\nEOF\ndotnet tool install --global Microsoft.AITools.BinlogMcp \\\n --configfile /tmp/binlog-tool/nuget.config \\\n --version \"$BINLOG_MCP_VERSION\"\necho \"$HOME/.dotnet/tools\" >> \"$GITHUB_PATH\"\n"
- - continue-on-error: true
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
name: Install NuGet MCP Server
run: dotnet tool install --global NuGet.Mcp.Server --version "$NUGET_MCP_VERSION"
- - continue-on-error: true
- env:
- BINLOG_PATH: ${{ steps.find-binlog.outputs.path }}
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
- name: Dump binlog as JSON
- run: "mkdir -p /tmp/binlog-data\ntimeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \\\n \"$BINLOG_PATH\" \\\n /tmp/binlog-data\n"
- env:
GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
@@ -482,13 +473,24 @@ jobs:
SHA=$(gh api "repos/${GH_AW_GITHUB_REPOSITORY}/pulls/${GH_AW_GITHUB_EVENT_ISSUE_NUMBER}" --jq .head.sha)
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
- env:
- GH_AW_BINLOG_PATH_VALUE: ${{ steps.find-binlog.outputs.path }}
+ GH_AW_BINLOG_REL_VALUE: ${{ needs.build.outputs.binlog-relative-path }}
+ GH_AW_BUILD_OUTCOME_VALUE: ${{ needs.build.outputs.outcome }}
GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_PR_HEAD_SHA_VALUE: ${{ steps.resolve-pr-sha.outputs.sha || github.sha }}
- GH_AW_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }}
name: Export agent context
- run: "{\n echo \"GH_AW_BUILD_OUTCOME=${GH_AW_STEPS_BUILD_OUTCOME}\"\n echo \"GH_AW_BINLOG_PATH=${GH_AW_BINLOG_PATH_VALUE}\"\n echo \"GH_AW_PR_NUMBER=${GH_AW_GITHUB_EVENT_ISSUE_NUMBER}\"\n echo \"GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}\"\n echo \"GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}\"\n} >> \"$GITHUB_ENV\"\n"
+ run: |-
+ BINLOG_PATH=""
+ if [ -n "${GH_AW_BINLOG_REL_VALUE:-}" ]; then
+ BINLOG_PATH="${GH_AW_GITHUB_WORKSPACE}/${GH_AW_BINLOG_REL_VALUE}"
+ fi
+ {
+ echo "GH_AW_BUILD_OUTCOME=${GH_AW_BUILD_OUTCOME_VALUE}"
+ echo "GH_AW_BINLOG_PATH=${BINLOG_PATH}"
+ echo "GH_AW_PR_NUMBER=${GH_AW_GITHUB_EVENT_ISSUE_NUMBER}"
+ echo "GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}"
+ echo "GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}"
+ } >> "$GITHUB_ENV"
- name: Configure Git credentials
env:
@@ -556,16 +558,16 @@ 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_5068ae042e1283c8_EOF'
- {"add_comment":{"hide_older_comments":true,"max":1},"create_pull_request_review_comment":{"max":10,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_5068ae042e1283c8_EOF
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_5d45d569ae4d0c5b_EOF'
+ {"add_comment":{"hide_older_comments":true,"max":5},"create_pull_request_review_comment":{"max":25,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":5,"report-as-issue":"false"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_5d45d569ae4d0c5b_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
"description_suffixes": {
- "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Supports reply_to_id for discussion threading.",
- "create_pull_request_review_comment": " CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff."
+ "add_comment": " CONSTRAINTS: Maximum 5 comment(s) can be added. Supports reply_to_id for discussion threading.",
+ "create_pull_request_review_comment": " CONSTRAINTS: Maximum 25 review comment(s) can be created. Comments will be on the RIGHT side of the diff."
},
"repo_params": {},
"dynamic_tools": []
@@ -790,7 +792,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_46d2e953909d7ce5_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ cat << GH_AW_MCP_CONFIG_626cbfb26bdaa019_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"github": {
@@ -831,7 +833,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_46d2e953909d7ce5_EOF
+ GH_AW_MCP_CONFIG_626cbfb26bdaa019_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -1079,10 +1081,105 @@ jobs:
/tmp/gh-aw/sandbox/firewall/awf-reflect.json
if-no-files-found: ignore
+ build:
+ name: Build (for analysis)
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+
+ timeout-minutes: 30
+ env:
+ BINLOG_MCP_VERSION: 1.0.0-preview.26272.1
+ outputs:
+ binlog-found: ${{ steps.find-binlog.outputs.found }}
+ binlog-relative-path: ${{ steps.find-binlog.outputs.relative-path }}
+ outcome: ${{ steps.build.outcome }}
+ steps:
+ - name: Configure GH_HOST for enterprise compatibility
+ id: ghes-host-config
+ shell: bash
+ run: |
+ # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
+ # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
+ GH_HOST="${GITHUB_SERVER_URL#https://}"
+ GH_HOST="${GH_HOST#http://}"
+ echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (source v6)
+ with:
+ ref: refs/pull/${{ github.event.issue.number }}/merge
+ - name: Build with binary log
+ id: build
+ run: |
+ set -uo pipefail
+ ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log
+ # `tee` is best-effort: rely on the build's own exit code so a
+ # logging-pipeline glitch never misclassifies a green build as
+ # failed (which would otherwise trigger the AI agent and
+ # re-expose us to the Copilot-flake red-X bug).
+ exit "${PIPESTATUS[0]}"
+ continue-on-error: true
+ - name: Put dotnet on the path
+ if: always()
+ run: echo "$PWD/.dotnet" >> $GITHUB_PATH
+ - name: Locate binlog
+ id: find-binlog
+ if: always()
+ run: |
+ BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \
+ | sort -rn | head -1 | cut -d' ' -f2-)
+ if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then
+ REL=$(realpath --relative-to="$PWD" "$BINLOG")
+ echo "found=true" >> "$GITHUB_OUTPUT"
+ echo "relative-path=$REL" >> "$GITHUB_OUTPUT"
+ else
+ echo "found=false" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Install binlog-mcp
+ if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
+ run: |
+ mkdir -p /tmp/binlog-tool
+ cat > /tmp/binlog-tool/nuget.config <<'EOF'
+
+
+
+
+
+
+
+ EOF
+ dotnet tool install --global Microsoft.AITools.BinlogMcp \
+ --configfile /tmp/binlog-tool/nuget.config \
+ --version "$BINLOG_MCP_VERSION"
+ echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
+ continue-on-error: true
+ - name: Dump binlog as JSON
+ if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
+ run: |
+ mkdir -p /tmp/binlog-data
+ timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \
+ "$BINLOG_REL_PATH" \
+ /tmp/binlog-data
+ env:
+ BINLOG_REL_PATH: ${{ steps.find-binlog.outputs.relative-path }}
+ continue-on-error: true
+ - name: Upload analysis artifact
+ if: always() && steps.build.outcome == 'failure'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7)
+ with:
+ if-no-files-found: warn
+ name: build-failure-analysis-data
+ path: |
+ /tmp/binlog-data/
+ /tmp/build-output.log
+ retention-days: 1
+ continue-on-error: true
+
conclusion:
needs:
- activation
- agent
+ - build
- detection
- safe_outputs
if: >
@@ -1137,7 +1234,7 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_NOOP_MAX: "1"
+ GH_AW_NOOP_MAX: "5"
GH_AW_WORKFLOW_NAME: "Build Failure Analysis (command)"
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/build-failure-analysis-command.md"
GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
@@ -1458,6 +1555,7 @@ jobs:
}
pre_activation:
+ needs: build
runs-on: ubuntu-slim
outputs:
activated: ${{ steps.check_membership.outputs.is_team_member == 'true' && steps.check_command_position.outputs.command_position_ok == 'true' }}
@@ -1582,7 +1680,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.vsblob.vsassets.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,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,dc.services.visualstudio.com,dist.nuget.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,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,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.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,www.googleapis.com,www.microsoft.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"create_pull_request_review_comment\":{\"max\":10,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":5},\"create_pull_request_review_comment\":{\"max\":25,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":5,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
diff --git a/.github/workflows/build-failure-analysis-command.md b/.github/workflows/build-failure-analysis-command.md
index 2d7b6dee03..7e1c512902 100644
--- a/.github/workflows/build-failure-analysis-command.md
+++ b/.github/workflows/build-failure-analysis-command.md
@@ -15,6 +15,16 @@ on:
strategy: centralized
roles: [admin, maintainer, write]
reaction: "eyes"
+ # Make `pre_activation` and `activation` wait for the custom `build` job
+ # defined below so the agent only runs when there is actually something
+ # to analyse, mirroring the auto-trigger workflow.
+ needs: [build]
+
+# Skip activation (and therefore the agent job) when the build job reported
+# success — even when invoked explicitly via `/analyze-build-failure`, there
+# is nothing to analyse on a green build and running the agent would just
+# expose us to transient Copilot AI flakes (see issue #8685).
+if: needs.build.outputs.outcome == 'failure'
permissions:
contents: read
@@ -38,69 +48,125 @@ network:
imports:
- shared/build-failure-analysis-shared.md
-# Same deterministic setup as build-failure-analysis.md. The slash-command
-# trigger fires on a `pull_request_comment` event; gh-aw handles the PR
-# checkout when the comment originates on a PR.
+# Custom build job that runs on every slash-command invocation. Mirrors the
+# `build` job in build-failure-analysis.md so the slash-command variant
+# benefits from the same skip-on-success gating.
+jobs:
+ build:
+ name: Build (for analysis)
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ permissions:
+ contents: read
+ outputs:
+ outcome: ${{ steps.build.outcome }}
+ binlog-found: ${{ steps.find-binlog.outputs.found }}
+ binlog-relative-path: ${{ steps.find-binlog.outputs.relative-path }}
+ env:
+ BINLOG_MCP_VERSION: '1.0.0-preview.26272.1'
+ steps:
+ # `pull_request_comment` events have the `issues` event payload, so
+ # the default checkout would build the default branch — NOT the PR
+ # the maintainer ran `/analyze-build-failure` on. Check out the PR's
+ # merge ref explicitly so we analyse the same code that the auto
+ # `pull_request` workflow would build.
+ - uses: actions/checkout@v6
+ with:
+ ref: refs/pull/${{ github.event.issue.number }}/merge
+
+ - name: Build with binary log
+ id: build
+ continue-on-error: true
+ run: |
+ set -uo pipefail
+ ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log
+ # `tee` is best-effort: rely on the build's own exit code so a
+ # logging-pipeline glitch never misclassifies a green build as
+ # failed (which would otherwise trigger the AI agent and
+ # re-expose us to the Copilot-flake red-X bug).
+ exit "${PIPESTATUS[0]}"
+
+ - name: Put dotnet on the path
+ if: always()
+ run: echo "$PWD/.dotnet" >> $GITHUB_PATH
+
+ - name: Locate binlog
+ id: find-binlog
+ if: always()
+ run: |
+ BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \
+ | sort -rn | head -1 | cut -d' ' -f2-)
+ if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then
+ REL=$(realpath --relative-to="$PWD" "$BINLOG")
+ echo "found=true" >> "$GITHUB_OUTPUT"
+ echo "relative-path=$REL" >> "$GITHUB_OUTPUT"
+ else
+ echo "found=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ - name: Install binlog-mcp
+ if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
+ continue-on-error: true
+ run: |
+ mkdir -p /tmp/binlog-tool
+ cat > /tmp/binlog-tool/nuget.config <<'EOF'
+
+
+
+
+
+
+
+ EOF
+ dotnet tool install --global Microsoft.AITools.BinlogMcp \
+ --configfile /tmp/binlog-tool/nuget.config \
+ --version "$BINLOG_MCP_VERSION"
+ echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
+
+ - name: Dump binlog as JSON
+ if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
+ continue-on-error: true
+ env:
+ BINLOG_REL_PATH: ${{ steps.find-binlog.outputs.relative-path }}
+ run: |
+ mkdir -p /tmp/binlog-data
+ timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \
+ "$BINLOG_REL_PATH" \
+ /tmp/binlog-data
+
+ - name: Upload analysis artifact
+ if: always() && steps.build.outcome == 'failure'
+ continue-on-error: true
+ uses: actions/upload-artifact@v7
+ with:
+ name: build-failure-analysis-data
+ path: |
+ /tmp/binlog-data/
+ /tmp/build-output.log
+ if-no-files-found: warn
+ retention-days: 1
+
+# Steps that run in the agent job. The top-level `if:` gates these on a
+# failed build, so the slash-command never invokes the AI agent on a green
+# build (and thus cannot surface as a red workflow check from a transient
+# Copilot AI flake).
steps:
- - name: Build with binary log
- id: build
- continue-on-error: true
- run: |
- set -o pipefail
- ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log
-
- - name: Put dotnet on the path
- if: always()
- run: echo "$PWD/.dotnet" >> $GITHUB_PATH
-
- - name: Locate binlog
- id: find-binlog
- run: |
- BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \
- | sort -rn | head -1 | cut -d' ' -f2-)
- if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then
- BINLOG=$(realpath "$BINLOG")
- echo "found=true" >> "$GITHUB_OUTPUT"
- echo "path=$BINLOG" >> "$GITHUB_OUTPUT"
- else
- echo "found=false" >> "$GITHUB_OUTPUT"
- fi
+ - name: Download analysis artifact
+ uses: actions/download-artifact@v8
+ with:
+ name: build-failure-analysis-data
+ path: /tmp/
- - name: Install binlog-mcp
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
- run: |
- mkdir -p /tmp/binlog-tool
- cat > /tmp/binlog-tool/nuget.config <<'EOF'
-
-
-
-
-
-
-
- EOF
- dotnet tool install --global Microsoft.AITools.BinlogMcp \
- --configfile /tmp/binlog-tool/nuget.config \
- --version "$BINLOG_MCP_VERSION"
- echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
+ - name: Setup .NET (for NuGet MCP Server)
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: '9.0.x'
- name: Install NuGet MCP Server
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
continue-on-error: true
run: dotnet tool install --global NuGet.Mcp.Server --version "$NUGET_MCP_VERSION"
- - name: Dump binlog as JSON
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
- continue-on-error: true
- env:
- BINLOG_PATH: ${{ steps.find-binlog.outputs.path }}
- run: |
- mkdir -p /tmp/binlog-data
- timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \
- "$BINLOG_PATH" \
- /tmp/binlog-data
-
# `pull_request_comment` events use the `issues` event payload, so
# `github.sha` is the default branch tip — NOT the PR head. Always resolve
# the real PR head SHA via the API so permalinks and inline comment
@@ -117,15 +183,19 @@ steps:
- name: Export agent context
env:
- GH_AW_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }}
- GH_AW_BINLOG_PATH_VALUE: ${{ steps.find-binlog.outputs.path }}
+ GH_AW_BUILD_OUTCOME_VALUE: ${{ needs.build.outputs.outcome }}
+ GH_AW_BINLOG_REL_VALUE: ${{ needs.build.outputs.binlog-relative-path }}
GH_AW_GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
GH_AW_PR_HEAD_SHA_VALUE: ${{ steps.resolve-pr-sha.outputs.sha || github.sha }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
+ BINLOG_PATH=""
+ if [ -n "${GH_AW_BINLOG_REL_VALUE:-}" ]; then
+ BINLOG_PATH="${GH_AW_GITHUB_WORKSPACE}/${GH_AW_BINLOG_REL_VALUE}"
+ fi
{
- echo "GH_AW_BUILD_OUTCOME=${GH_AW_STEPS_BUILD_OUTCOME}"
- echo "GH_AW_BINLOG_PATH=${GH_AW_BINLOG_PATH_VALUE}"
+ echo "GH_AW_BUILD_OUTCOME=${GH_AW_BUILD_OUTCOME_VALUE}"
+ echo "GH_AW_BINLOG_PATH=${BINLOG_PATH}"
echo "GH_AW_PR_NUMBER=${GH_AW_GITHUB_EVENT_ISSUE_NUMBER}"
echo "GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}"
echo "GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}"
@@ -148,15 +218,29 @@ tools:
- "NuGet.Mcp.Server"
safe-outputs:
- # Match build-failure-analysis.md: suppress tracking-issue spam from
- # transient AI service flakes (see issue #8685 and comments there).
+ # The agent runs only when the build job reports failure (see top-level
+ # `if:` above). On a failed build the agent normally emits at most one
+ # `noop`, one summary comment, and a small set of inline review comments,
+ # but the Copilot CLI harness retries with `--continue` on
+ # mid-conversation AI flakes (up to 3 retries) and each retry re-emits
+ # every safe-output call it has issued so far. The caps below absorb that
+ # retry budget without spurious safe-output validation warnings:
+ # - noop max=5: covers 1 happy-path + 4 retry-amplified noops.
+ # - add-comment max=5: covers 1 summary + 4 retries (hide-older-comments
+ # auto-collapses the duplicates anyway).
+ # - create-pull-request-review-comment max=25: shared body asks the
+ # agent for "top 5 highest-priority issues" per run, so 5 × (1 + 3
+ # retries) = 20 is the worst case under flake amplification.
+ # We also disable `report-as-issue` / `report-failure-as-issue` so
+ # transient flakes never spam tracking issues (see issue #8685).
report-failure-as-issue: false
add-comment:
- max: 1
+ max: 5
hide-older-comments: true
create-pull-request-review-comment:
- max: 10
+ max: 25
noop:
+ max: 5
report-as-issue: false
---
diff --git a/.github/workflows/build-failure-analysis.lock.yml b/.github/workflows/build-failure-analysis.lock.yml
index 5a4a993e8c..be3a4c3d1b 100644
--- a/.github/workflows/build-failure-analysis.lock.yml
+++ b/.github/workflows/build-failure-analysis.lock.yml
@@ -1,5 +1,5 @@
-# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"455b908fb91e2873c51b4690c39505ae86cc92158666b7e14a6083907c1cd383","compiler_version":"v0.75.4","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":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7","version":"v5.2.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"9f050961da586148d135e113d8bb025185cdf2b8","version":"v0.75.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.53"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.18"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]}
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"36668e910355f18bfec1b7fe7525746a9f4bbdf7661589c31b5b1afc318004d8","compiler_version":"v0.75.4","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":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-dotnet","sha":"9a946fdbd5fb07b82b2f5a4466058b876ab72bb2","version":"v5"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"9f050961da586148d135e113d8bb025185cdf2b8","version":"v0.75.4"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.53"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.53"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.18"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4","digest":"sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]}
# ___ _ _
# / _ \ | | (_)
# | |_| | __ _ ___ _ __ | |_ _ ___
@@ -40,12 +40,15 @@
#
# Custom actions used:
# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (source v6)
# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8)
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
-# - actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
+# - actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7)
# - github/gh-aw-actions/setup@9f050961da586148d135e113d8bb025185cdf2b8 # v0.75.4
#
# Container images used:
@@ -97,9 +100,12 @@ env:
jobs:
activation:
- needs: pre_activation
+ needs:
+ - build
+ - pre_activation
if: >
- needs.pre_activation.outputs.activated == 'true' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id)
+ needs.pre_activation.outputs.activated == 'true' && ((needs.build.outputs.outcome == 'failure') && (github.event_name != 'pull_request' ||
+ github.event.pull_request.head.repo.id == github.repository_id))
runs-on: ubuntu-slim
permissions:
actions: read
@@ -249,20 +255,20 @@ jobs:
run: |
bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_d01b03d1807f525c_EOF'
+ cat << 'GH_AW_PROMPT_ba091b964d18ff2d_EOF'
- GH_AW_PROMPT_d01b03d1807f525c_EOF
+ GH_AW_PROMPT_ba091b964d18ff2d_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_d01b03d1807f525c_EOF'
+ cat << 'GH_AW_PROMPT_ba091b964d18ff2d_EOF'
- Tools: add_comment, create_pull_request_review_comment(max:10), missing_tool, missing_data, noop
+ Tools: add_comment(max:5), create_pull_request_review_comment(max:25), missing_tool, missing_data, noop(max:5)
- GH_AW_PROMPT_d01b03d1807f525c_EOF
+ GH_AW_PROMPT_ba091b964d18ff2d_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
- cat << 'GH_AW_PROMPT_d01b03d1807f525c_EOF'
+ cat << 'GH_AW_PROMPT_ba091b964d18ff2d_EOF'
The following GitHub context information is available for this workflow:
{{#if github.actor}}
@@ -291,13 +297,13 @@ jobs:
{{/if}}
- GH_AW_PROMPT_d01b03d1807f525c_EOF
+ GH_AW_PROMPT_ba091b964d18ff2d_EOF
cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
- cat << 'GH_AW_PROMPT_d01b03d1807f525c_EOF'
+ cat << 'GH_AW_PROMPT_ba091b964d18ff2d_EOF'
{{#runtime-import .github/workflows/shared/build-failure-analysis-shared.md}}
{{#runtime-import .github/workflows/build-failure-analysis.md}}
- GH_AW_PROMPT_d01b03d1807f525c_EOF
+ GH_AW_PROMPT_ba091b964d18ff2d_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
@@ -375,7 +381,11 @@ jobs:
retention-days: 1
agent:
- needs: activation
+ needs:
+ - activation
+ - build
+ if: >
+ (needs.build.outputs.outcome == 'failure') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id)
runs-on: ubuntu-latest
permissions:
contents: read
@@ -429,41 +439,24 @@ jobs:
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- - name: Setup .NET
- uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
- with:
- dotnet-version: '8.0'
- name: Create gh-aw temp directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
- name: Configure gh CLI for GitHub Enterprise
run: bash "${RUNNER_TEMP}/gh-aw/actions/configure_gh_for_ghe.sh"
env:
GH_TOKEN: ${{ github.token }}
+ - name: Download analysis artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 (source v8)
+ with:
+ name: build-failure-analysis-data
+ path: /tmp/
+ - name: Setup .NET (for NuGet MCP Server)
+ uses: actions/setup-dotnet@9a946fdbd5fb07b82b2f5a4466058b876ab72bb2 # v5
+ with:
+ dotnet-version: 9.0.x
- continue-on-error: true
- id: build
- name: Build with binary log
- run: |
- set -o pipefail
- ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log
- - if: always()
- name: Put dotnet on the path
- run: echo "$PWD/.dotnet" >> $GITHUB_PATH
- - id: find-binlog
- name: Locate binlog
- run: "BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\\n' 2>/dev/null \\\n | sort -rn | head -1 | cut -d' ' -f2-)\nif [ -n \"$BINLOG\" ] && [ -f \"$BINLOG\" ]; then\n BINLOG=$(realpath \"$BINLOG\")\n echo \"found=true\" >> \"$GITHUB_OUTPUT\"\n echo \"path=$BINLOG\" >> \"$GITHUB_OUTPUT\"\nelse\n echo \"found=false\" >> \"$GITHUB_OUTPUT\"\nfi\n"
- - if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
- name: Install binlog-mcp
- run: "mkdir -p /tmp/binlog-tool\ncat > /tmp/binlog-tool/nuget.config <<'EOF'\n\n\n \n \n \n \n\nEOF\ndotnet tool install --global Microsoft.AITools.BinlogMcp \\\n --configfile /tmp/binlog-tool/nuget.config \\\n --version \"$BINLOG_MCP_VERSION\"\necho \"$HOME/.dotnet/tools\" >> \"$GITHUB_PATH\"\n"
- - continue-on-error: true
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
name: Install NuGet MCP Server
run: dotnet tool install --global NuGet.Mcp.Server --version "$NUGET_MCP_VERSION"
- - continue-on-error: true
- env:
- BINLOG_PATH: ${{ steps.find-binlog.outputs.path }}
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
- name: Dump binlog as JSON
- run: "mkdir -p /tmp/binlog-data\ntimeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \\\n \"$BINLOG_PATH\" \\\n /tmp/binlog-data\n"
- env:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_INPUTS_PR_NUMBER: ${{ inputs.pr-number }}
@@ -475,13 +468,13 @@ jobs:
SHA=$(gh api "repos/${GH_AW_GITHUB_REPOSITORY}/pulls/${GH_AW_INPUTS_PR_NUMBER}" --jq .head.sha)
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
- env:
- GH_AW_BINLOG_PATH_VALUE: ${{ steps.find-binlog.outputs.path }}
+ GH_AW_BINLOG_REL_VALUE: ${{ needs.build.outputs.binlog-relative-path }}
+ GH_AW_BUILD_OUTCOME_VALUE: ${{ needs.build.outputs.outcome }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
GH_AW_PR_HEAD_SHA_VALUE: ${{ steps.resolve-pr-sha.outputs.sha || github.event.pull_request.head.sha || github.sha }}
GH_AW_PR_NUMBER_VALUE: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr-number }}
- GH_AW_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }}
name: Export agent context
- run: "{\n echo \"GH_AW_BUILD_OUTCOME=${GH_AW_STEPS_BUILD_OUTCOME}\"\n echo \"GH_AW_BINLOG_PATH=${GH_AW_BINLOG_PATH_VALUE}\"\n echo \"GH_AW_PR_NUMBER=${GH_AW_PR_NUMBER_VALUE}\"\n echo \"GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}\"\n echo \"GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}\"\n} >> \"$GITHUB_ENV\"\n"
+ run: "# The binlog file itself is not transported between jobs (it is large\n# and the agent only needs the pre-dumped JSON files). Set\n# GH_AW_BINLOG_PATH to a synthetic workspace-relative path purely for\n# display / permalink purposes; the agent must rely on\n# /tmp/binlog-data/*.json for actual data (see shared body).\nBINLOG_PATH=\"\"\nif [ -n \"${GH_AW_BINLOG_REL_VALUE:-}\" ]; then\n BINLOG_PATH=\"${GH_AW_GITHUB_WORKSPACE}/${GH_AW_BINLOG_REL_VALUE}\"\nfi\n{\n echo \"GH_AW_BUILD_OUTCOME=${GH_AW_BUILD_OUTCOME_VALUE}\"\n echo \"GH_AW_BINLOG_PATH=${BINLOG_PATH}\"\n echo \"GH_AW_PR_NUMBER=${GH_AW_PR_NUMBER_VALUE}\"\n echo \"GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}\"\n echo \"GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}\"\n} >> \"$GITHUB_ENV\"\n"
- name: Configure Git credentials
env:
@@ -549,16 +542,16 @@ 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_d63b713a200ed2d5_EOF'
- {"add_comment":{"hide_older_comments":true,"max":1},"create_pull_request_review_comment":{"max":10,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
- GH_AW_SAFE_OUTPUTS_CONFIG_d63b713a200ed2d5_EOF
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_3599610bf1d695d8_EOF'
+ {"add_comment":{"hide_older_comments":true,"max":5},"create_pull_request_review_comment":{"max":25,"side":"RIGHT"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":5,"report-as-issue":"false"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_3599610bf1d695d8_EOF
- name: Generate Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
"description_suffixes": {
- "add_comment": " CONSTRAINTS: Maximum 1 comment(s) can be added. Supports reply_to_id for discussion threading.",
- "create_pull_request_review_comment": " CONSTRAINTS: Maximum 10 review comment(s) can be created. Comments will be on the RIGHT side of the diff."
+ "add_comment": " CONSTRAINTS: Maximum 5 comment(s) can be added. Supports reply_to_id for discussion threading.",
+ "create_pull_request_review_comment": " CONSTRAINTS: Maximum 25 review comment(s) can be created. Comments will be on the RIGHT side of the diff."
},
"repo_params": {},
"dynamic_tools": []
@@ -783,7 +776,7 @@ jobs:
mkdir -p /home/runner/.copilot
GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
- cat << GH_AW_MCP_CONFIG_18f7fc77e234b36e_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ cat << GH_AW_MCP_CONFIG_d9ac6c610fd39d46_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"github": {
@@ -824,7 +817,7 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_18f7fc77e234b36e_EOF
+ GH_AW_MCP_CONFIG_d9ac6c610fd39d46_EOF
- name: Mount MCP servers as CLIs
id: mount-mcp-clis
continue-on-error: true
@@ -1071,10 +1064,104 @@ jobs:
/tmp/gh-aw/sandbox/firewall/awf-reflect.json
if-no-files-found: ignore
+ build:
+ name: Build (for analysis)
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+
+ timeout-minutes: 30
+ env:
+ BINLOG_MCP_VERSION: 1.0.0-preview.26272.1
+ outputs:
+ binlog-found: ${{ steps.find-binlog.outputs.found }}
+ binlog-relative-path: ${{ steps.find-binlog.outputs.relative-path }}
+ outcome: ${{ steps.build.outcome }}
+ steps:
+ - name: Configure GH_HOST for enterprise compatibility
+ id: ghes-host-config
+ shell: bash
+ run: |
+ # Derive GH_HOST from GITHUB_SERVER_URL so the gh CLI targets the correct
+ # GitHub instance (GHES/GHEC). On github.com this is a harmless no-op.
+ GH_HOST="${GITHUB_SERVER_URL#https://}"
+ GH_HOST="${GH_HOST#http://}"
+ echo "GH_HOST=${GH_HOST}" >> "$GITHUB_ENV"
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 (source v6)
+ - name: Build with binary log
+ id: build
+ run: |
+ set -uo pipefail
+ ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log
+ # `tee` is best-effort: rely on the build's own exit code so a
+ # logging-pipeline glitch never misclassifies a green build as
+ # failed (which would otherwise trigger the AI agent and
+ # re-expose us to the Copilot-flake red-X bug).
+ exit "${PIPESTATUS[0]}"
+ continue-on-error: true
+ - name: Put dotnet on the path
+ if: always()
+ run: echo "$PWD/.dotnet" >> $GITHUB_PATH
+ - name: Locate binlog
+ id: find-binlog
+ if: always()
+ run: |
+ BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \
+ | sort -rn | head -1 | cut -d' ' -f2-)
+ if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then
+ REL=$(realpath --relative-to="$PWD" "$BINLOG")
+ echo "found=true" >> "$GITHUB_OUTPUT"
+ echo "relative-path=$REL" >> "$GITHUB_OUTPUT"
+ else
+ echo "found=false" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Install binlog-mcp
+ if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
+ run: |
+ mkdir -p /tmp/binlog-tool
+ cat > /tmp/binlog-tool/nuget.config <<'EOF'
+
+
+
+
+
+
+
+ EOF
+ dotnet tool install --global Microsoft.AITools.BinlogMcp \
+ --configfile /tmp/binlog-tool/nuget.config \
+ --version "$BINLOG_MCP_VERSION"
+ echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
+ continue-on-error: true
+ - name: Dump binlog as JSON
+ if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
+ run: |
+ mkdir -p /tmp/binlog-data
+ timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \
+ "$BINLOG_REL_PATH" \
+ /tmp/binlog-data
+ env:
+ BINLOG_REL_PATH: ${{ steps.find-binlog.outputs.relative-path }}
+ continue-on-error: true
+ - name: Upload analysis artifact
+ if: always() && steps.build.outcome == 'failure'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 (source v7)
+ with:
+ if-no-files-found: warn
+ name: build-failure-analysis-data
+ path: |
+ /tmp/binlog-data/
+ /tmp/build-output.log
+ retention-days: 1
+ continue-on-error: true
+
conclusion:
needs:
- activation
- agent
+ - build
- detection
- safe_outputs
if: >
@@ -1129,7 +1216,7 @@ jobs:
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
- GH_AW_NOOP_MAX: "1"
+ GH_AW_NOOP_MAX: "5"
GH_AW_WORKFLOW_NAME: "Build Failure Analysis"
GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/build-failure-analysis.md"
GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
@@ -1430,7 +1517,7 @@ jobs:
}
pre_activation:
- if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.id == github.repository_id
+ needs: build
runs-on: ubuntu-slim
outputs:
activated: ${{ steps.check_membership.outputs.is_team_member == 'true' }}
@@ -1544,7 +1631,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "*.vsblob.vsassets.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,builds.dotnet.microsoft.com,ci.dot.net,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,dc.services.visualstudio.com,dist.nuget.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,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,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pkgs.dev.azure.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,www.googleapis.com,www.microsoft.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":1},\"create_pull_request_review_comment\":{\"max\":10,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_comment\":{\"hide_older_comments\":true,\"max\":5},\"create_pull_request_review_comment\":{\"max\":25,\"side\":\"RIGHT\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":5,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
diff --git a/.github/workflows/build-failure-analysis.md b/.github/workflows/build-failure-analysis.md
index b18ba6c7cd..669aebf46a 100644
--- a/.github/workflows/build-failure-analysis.md
+++ b/.github/workflows/build-failure-analysis.md
@@ -34,6 +34,18 @@ on:
# the companion `build-failure-analysis-command.md` workflow.
roles: [admin, maintainer, write]
reaction: "eyes"
+ # Make `pre_activation` and `activation` wait for the custom `build` job
+ # defined below. Combined with the top-level `if:`, this gates the entire
+ # AI agent pipeline on build failure — so transient Copilot AI flakes can
+ # never surface as a red workflow check on a successful build.
+ needs: [build]
+
+# Skip activation (and therefore the agent job) when the build job reported
+# success. gh-aw applies top-level `if:` to the `activation` job, which is a
+# dependency of `agent`, so a skipped activation cascades into a skipped
+# agent — no AI calls, no safe-output validation, no chance of a noop-loop
+# from a transient AI server error on an otherwise green build.
+if: needs.build.outputs.outcome == 'failure'
permissions:
contents: read
@@ -57,74 +69,129 @@ network:
imports:
- shared/build-failure-analysis-shared.md
-# Deterministic setup that runs before the AI agent starts. By the time the
-# agent boots: dotnet is on PATH, the binlog has been produced (whether the
-# build succeeded or failed), the binlog path and build outcome are exported
-# as `GH_AW_*` env vars, `binlog-mcp` is installed, and the binlog data has
-# been dumped to `/tmp/binlog-data/*.json` files for the agent to `cat`.
-#
-# `continue-on-error: true` is essential on the build step: a failed build
-# must not abort the job before the agent gets to analyse it.
-steps:
- - name: Build with binary log
- id: build
- continue-on-error: true
- run: |
- set -o pipefail
- ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log
+# Custom build job that runs unconditionally on every PR. It produces the
+# binlog and (on failure) dumps it to JSON files which are uploaded as an
+# artifact for the agent job to consume. The agent pipeline only runs when
+# this job reports `outcome == 'failure'` (see top-level `if:` above).
+jobs:
+ build:
+ name: Build (for analysis)
+ runs-on: ubuntu-latest
+ timeout-minutes: 30
+ # Mirror the workflow's `forks: []` trigger filter: skip fork PRs at the
+ # build-job level too. Without this guard the build job would still run
+ # for fork PRs (paying CI time and exposing dotnet-tools auth-gated
+ # installs to forks) even though the agent pipeline never runs for them.
+ if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
+ permissions:
+ contents: read
+ outputs:
+ outcome: ${{ steps.build.outcome }}
+ binlog-found: ${{ steps.find-binlog.outputs.found }}
+ binlog-relative-path: ${{ steps.find-binlog.outputs.relative-path }}
+ env:
+ BINLOG_MCP_VERSION: '1.0.0-preview.26272.1'
+ steps:
+ - uses: actions/checkout@v6
- - name: Put dotnet on the path
- if: always()
- run: echo "$PWD/.dotnet" >> $GITHUB_PATH
+ - name: Build with binary log
+ id: build
+ continue-on-error: true
+ run: |
+ set -uo pipefail
+ ./build.sh --binaryLog 2>&1 | tee /tmp/build-output.log
+ # `tee` is best-effort: rely on the build's own exit code so a
+ # logging-pipeline glitch never misclassifies a green build as
+ # failed (which would otherwise trigger the AI agent and
+ # re-expose us to the Copilot-flake red-X bug).
+ exit "${PIPESTATUS[0]}"
- - name: Locate binlog
- id: find-binlog
- run: |
- BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \
- | sort -rn | head -1 | cut -d' ' -f2-)
- if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then
- BINLOG=$(realpath "$BINLOG")
- echo "found=true" >> "$GITHUB_OUTPUT"
- echo "path=$BINLOG" >> "$GITHUB_OUTPUT"
- else
- echo "found=false" >> "$GITHUB_OUTPUT"
- fi
+ - name: Put dotnet on the path
+ if: always()
+ run: echo "$PWD/.dotnet" >> $GITHUB_PATH
- - name: Install binlog-mcp
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
- run: |
- mkdir -p /tmp/binlog-tool
- cat > /tmp/binlog-tool/nuget.config <<'EOF'
-
-
-
-
-
-
-
- EOF
- dotnet tool install --global Microsoft.AITools.BinlogMcp \
- --configfile /tmp/binlog-tool/nuget.config \
- --version "$BINLOG_MCP_VERSION"
- echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
+ - name: Locate binlog
+ id: find-binlog
+ if: always()
+ run: |
+ BINLOG=$(find artifacts/log -name '*.binlog' -type f -printf '%T@ %p\n' 2>/dev/null \
+ | sort -rn | head -1 | cut -d' ' -f2-)
+ if [ -n "$BINLOG" ] && [ -f "$BINLOG" ]; then
+ REL=$(realpath --relative-to="$PWD" "$BINLOG")
+ echo "found=true" >> "$GITHUB_OUTPUT"
+ echo "relative-path=$REL" >> "$GITHUB_OUTPUT"
+ else
+ echo "found=false" >> "$GITHUB_OUTPUT"
+ fi
+
+ - name: Install binlog-mcp
+ if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
+ continue-on-error: true
+ run: |
+ mkdir -p /tmp/binlog-tool
+ cat > /tmp/binlog-tool/nuget.config <<'EOF'
+
+
+
+
+
+
+
+ EOF
+ dotnet tool install --global Microsoft.AITools.BinlogMcp \
+ --configfile /tmp/binlog-tool/nuget.config \
+ --version "$BINLOG_MCP_VERSION"
+ echo "$HOME/.dotnet/tools" >> "$GITHUB_PATH"
+
+ - name: Dump binlog as JSON
+ if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
+ continue-on-error: true
+ env:
+ BINLOG_REL_PATH: ${{ steps.find-binlog.outputs.relative-path }}
+ run: |
+ mkdir -p /tmp/binlog-data
+ timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \
+ "$BINLOG_REL_PATH" \
+ /tmp/binlog-data
+
+ # Upload everything the agent needs. Always upload when the build
+ # failed (even if dump-binlog failed), so the agent gets the raw
+ # build output log and can still emit a "build failed, no binlog
+ # data" comment.
+ - name: Upload analysis artifact
+ if: always() && steps.build.outcome == 'failure'
+ continue-on-error: true
+ uses: actions/upload-artifact@v7
+ with:
+ name: build-failure-analysis-data
+ path: |
+ /tmp/binlog-data/
+ /tmp/build-output.log
+ if-no-files-found: warn
+ retention-days: 1
+
+# Steps that run in the agent job. Because the top-level `if:` gates
+# activation on `needs.build.outputs.outcome == 'failure'`, these only run
+# for failed builds — the agent never executes on a successful build and a
+# transient Copilot AI flake can no longer surface as a red workflow check
+# on a passing PR.
+steps:
+ - name: Download analysis artifact
+ uses: actions/download-artifact@v8
+ with:
+ name: build-failure-analysis-data
+ path: /tmp/
+
+ - name: Setup .NET (for NuGet MCP Server)
+ uses: actions/setup-dotnet@v5
+ with:
+ dotnet-version: '9.0.x'
- name: Install NuGet MCP Server
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
continue-on-error: true
run: dotnet tool install --global NuGet.Mcp.Server --version "$NUGET_MCP_VERSION"
- - name: Dump binlog as JSON
- if: steps.build.outcome == 'failure' && steps.find-binlog.outputs.found == 'true'
- continue-on-error: true
- env:
- BINLOG_PATH: ${{ steps.find-binlog.outputs.path }}
- run: |
- mkdir -p /tmp/binlog-data
- timeout 180 dotnet run --project .github/workflows/scripts/DumpBinlog -- \
- "$BINLOG_PATH" \
- /tmp/binlog-data
-
# On `workflow_dispatch` runs, `github.sha` is the SHA of the dispatched ref
# (usually the default branch), NOT the PR head. Look up the real PR head
# SHA via the API so permalinks and inline comment placement match the PR.
@@ -141,15 +208,24 @@ steps:
- name: Export agent context
env:
- GH_AW_STEPS_BUILD_OUTCOME: ${{ steps.build.outcome }}
- GH_AW_BINLOG_PATH_VALUE: ${{ steps.find-binlog.outputs.path }}
+ GH_AW_BUILD_OUTCOME_VALUE: ${{ needs.build.outputs.outcome }}
+ GH_AW_BINLOG_REL_VALUE: ${{ needs.build.outputs.binlog-relative-path }}
GH_AW_PR_NUMBER_VALUE: ${{ github.event.pull_request.number || github.event.issue.number || inputs.pr-number }}
GH_AW_PR_HEAD_SHA_VALUE: ${{ steps.resolve-pr-sha.outputs.sha || github.event.pull_request.head.sha || github.sha }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
run: |
+ # The binlog file itself is not transported between jobs (it is large
+ # and the agent only needs the pre-dumped JSON files). Set
+ # GH_AW_BINLOG_PATH to a synthetic workspace-relative path purely for
+ # display / permalink purposes; the agent must rely on
+ # /tmp/binlog-data/*.json for actual data (see shared body).
+ BINLOG_PATH=""
+ if [ -n "${GH_AW_BINLOG_REL_VALUE:-}" ]; then
+ BINLOG_PATH="${GH_AW_GITHUB_WORKSPACE}/${GH_AW_BINLOG_REL_VALUE}"
+ fi
{
- echo "GH_AW_BUILD_OUTCOME=${GH_AW_STEPS_BUILD_OUTCOME}"
- echo "GH_AW_BINLOG_PATH=${GH_AW_BINLOG_PATH_VALUE}"
+ echo "GH_AW_BUILD_OUTCOME=${GH_AW_BUILD_OUTCOME_VALUE}"
+ echo "GH_AW_BINLOG_PATH=${BINLOG_PATH}"
echo "GH_AW_PR_NUMBER=${GH_AW_PR_NUMBER_VALUE}"
echo "GH_AW_PR_HEAD_SHA=${GH_AW_PR_HEAD_SHA_VALUE}"
echo "GH_AW_WORKSPACE=${GH_AW_GITHUB_WORKSPACE}"
@@ -172,18 +248,29 @@ tools:
- "NuGet.Mcp.Server"
safe-outputs:
- # The agent runs on every PR (gh-aw has no agent-job-level `if:` hook), so a
- # transient AI service flake on an otherwise-successful build would
- # otherwise spam tracking issues (see issue #8685). Suppress those: the
- # workflow is advisory, and run-level failures are still visible in the
- # Actions UI for anyone who wants to investigate.
+ # The agent runs only when the build job reports failure (see top-level
+ # `if:` above). On a failed build the agent normally emits at most one
+ # `noop`, one summary comment, and a small set of inline review comments,
+ # but the Copilot CLI harness retries with `--continue` on
+ # mid-conversation AI flakes (up to 3 retries) and each retry re-emits
+ # every safe-output call it has issued so far. The caps below absorb that
+ # retry budget without spurious safe-output validation warnings:
+ # - noop max=5: covers 1 happy-path + 4 retry-amplified noops.
+ # - add-comment max=5: covers 1 summary + 4 retries (hide-older-comments
+ # auto-collapses the duplicates anyway).
+ # - create-pull-request-review-comment max=25: shared body asks the
+ # agent for "top 5 highest-priority issues" per run, so 5 × (1 + 3
+ # retries) = 20 is the worst case under flake amplification.
+ # We also disable `report-as-issue` / `report-failure-as-issue` so
+ # transient flakes never spam tracking issues (see issue #8685).
report-failure-as-issue: false
add-comment:
- max: 1
+ max: 5
hide-older-comments: true
create-pull-request-review-comment:
- max: 10
+ max: 25
noop:
+ max: 5
report-as-issue: false
---