diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index 5ace4600a..c00c619c4 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -1,6 +1,8 @@
-version: 2
updates:
- - package-ecosystem: "github-actions"
- directory: "/"
- schedule:
- interval: "weekly"
+- directory: /
+ ignore:
+ - dependency-name: "github/gh-aw-actions/**" # Managed by gh aw compile. Version-locked to the gh-aw compiler; do not bump.
+ package-ecosystem: github-actions
+ schedule:
+ interval: weekly
+version: 2
diff --git a/.github/workflows/academic-citation-tracker.md b/.github/workflows/academic-citation-tracker.md
index ef6fc7efa..f8a87499c 100644
--- a/.github/workflows/academic-citation-tracker.md
+++ b/.github/workflows/academic-citation-tracker.md
@@ -36,7 +36,7 @@ safe-outputs:
title-prefix: "[Research Trends] "
category: "Agentic Workflows"
close-older-discussions: true
- expires: 60
+ expires: 60d
missing-tool:
create-issue: true
noop:
@@ -295,4 +295,4 @@ Store for next run:
- DO NOT reproduce copyrighted paper text beyond short fair-use quotes.
- DO close older Research Trends discussions automatically (configured).
- DO always cite sources (arXiv ID, DOI, GitHub URL) so maintainers can verify.
-- DO use cache memory to track longitudinal trends across months.
+- DO use cache memory to track longitudinal trends across months.
\ No newline at end of file
diff --git a/.github/workflows/android-build.yml b/.github/workflows/android-build.yml
index f315e8384..5f63c77b8 100644
--- a/.github/workflows/android-build.yml
+++ b/.github/workflows/android-build.yml
@@ -33,7 +33,7 @@ jobs:
tar -cvf z3-build-${{ matrix.android-abi }}.tar *.jar *.so
- name: Archive production artifacts
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: android-build-${{ matrix.android-abi }}
path: build/z3-build-${{ matrix.android-abi }}.tar
diff --git a/.github/workflows/api-coherence-checker.md b/.github/workflows/api-coherence-checker.md
index 06ea22c3a..7e2ca894c 100644
--- a/.github/workflows/api-coherence-checker.md
+++ b/.github/workflows/api-coherence-checker.md
@@ -13,12 +13,10 @@ network: defaults
tools:
cache-memory: true
- serena: ["java", "python", "typescript", "csharp"]
github:
toolsets: [default]
bash: [":*"]
edit: {}
- glob: {}
web-search: {}
safe-outputs:
diff --git a/.github/workflows/build-warning-fixer.md b/.github/workflows/build-warning-fixer.md
index b4c785ad4..8caf35cd8 100644
--- a/.github/workflows/build-warning-fixer.md
+++ b/.github/workflows/build-warning-fixer.md
@@ -5,8 +5,6 @@ on:
workflow_dispatch:
permissions: read-all
tools:
- view: {}
- glob: {}
edit:
bash: true
safe-outputs:
diff --git a/.github/workflows/build-z3-cache.yml b/.github/workflows/build-z3-cache.yml
index 428f12af4..a13822ddd 100644
--- a/.github/workflows/build-z3-cache.yml
+++ b/.github/workflows/build-z3-cache.yml
@@ -45,7 +45,7 @@ jobs:
- name: Restore or create cache
id: cache-z3
- uses: actions/cache@v5.0.4
+ uses: actions/cache@v5.0.5
with:
path: |
build/z3
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cf2eeb246..c0b4070a3 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -83,8 +83,16 @@ jobs:
- name: Checkout code
uses: actions/checkout@v6.0.2
+ - name: Select Python
+ run: |
+ # Use the first available manylinux interpreter for deterministic selection.
+ PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
+ test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
+ echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
+ "$PYTHON" --version
+
- name: Setup Python virtual environment
- run: "/opt/python/cp38-cp38/bin/python -m venv $PWD/env"
+ run: "$PYTHON -m venv $PWD/env"
- name: Install build dependencies
run: |
@@ -123,8 +131,16 @@ jobs:
mkdir -p /tmp/arm-toolchain/
tar xf /tmp/arm-toolchain.tar.xz -C /tmp/arm-toolchain/ --strip-components=1
+ - name: Select Python
+ run: |
+ # Use the first available manylinux interpreter for deterministic selection.
+ PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
+ test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
+ echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
+ "$PYTHON" --version
+
- name: Setup Python virtual environment
- run: "/opt/python/cp38-cp38/bin/python -m venv $PWD/env"
+ run: "$PYTHON -m venv $PWD/env"
- name: Install build dependencies
run: |
@@ -315,7 +331,7 @@ jobs:
- name: Setup Julia (if needed)
if: matrix.name == 'debugClang'
- uses: julia-actions/setup-julia@v2
+ uses: julia-actions/setup-julia@v3
with:
version: '1'
diff --git a/.github/workflows/code-conventions-analyzer.md b/.github/workflows/code-conventions-analyzer.md
index 8264296c3..5ec27678e 100644
--- a/.github/workflows/code-conventions-analyzer.md
+++ b/.github/workflows/code-conventions-analyzer.md
@@ -8,8 +8,6 @@ tools:
cache-memory: true
github:
toolsets: [default]
- view: {}
- glob: {}
edit: {}
bash:
- "clang-format --version"
diff --git a/.github/workflows/code-simplifier.lock.yml b/.github/workflows/code-simplifier.lock.yml
index 3bc57a0a0..3c67f2c46 100644
--- a/.github/workflows/code-simplifier.lock.yml
+++ b/.github/workflows/code-simplifier.lock.yml
@@ -1,4 +1,5 @@
-#
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"a50e58358dfcf5d6e6e0b92707925040a7b1ad4f29c08cfce11cfa8c9756f876","compiler_version":"v0.71.5","strict":true,"agent_id":"copilot"}
+# gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_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"},{"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":"v0.71.5","version":"v0.71.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40","digest":"sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40","digest":"sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40","digest":"sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]}
# ___ _ _
# / _ \ | | (_)
# | |_| | __ _ ___ _ __ | |_ _ ___
@@ -13,9 +14,9 @@
# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
#
-# This file was automatically generated by gh-aw (v0.57.2). DO NOT EDIT.
+# This file was automatically generated by gh-aw (v0.71.5). DO NOT EDIT.
#
-# To update this file, edit github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b and run:
+# To update this file, edit github/gh-aw/.github/workflows/code-simplifier.md@6762bfba6ae426a03aac46e8f68701461c667404 and run:
# gh aw compile
# Not all edits will cause changes to this file.
#
@@ -23,17 +24,45 @@
#
# Analyzes recently modified code and creates pull requests with simplifications that improve clarity, consistency, and maintainability while preserving functionality
#
-# Source: github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b
+# Source: github/gh-aw/.github/workflows/code-simplifier.md@6762bfba6ae426a03aac46e8f68701461c667404
#
-# gh-aw-metadata: {"schema_version":"v2","frontmatter_hash":"6f3bad47dff7f3f86460672a86abd84130d8a7dee19358ef3391e3faf65f4857","compiler_version":"v0.57.2","strict":true}
+# Secrets used:
+# - COPILOT_GITHUB_TOKEN
+# - GH_AW_CI_TRIGGER_TOKEN
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@v0.77.0
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51
+# - ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c
+# - ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959
+# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
name: "Code Simplifier"
"on":
schedule:
- - cron: "27 13 * * *"
+ - cron: "10 4 * * *"
# Friendly format: daily (scattered)
# skip-if-match: is:pr is:open in:title "[code-simplifier]" # Skip-if-match processed as search check in pre-activation job
workflow_dispatch:
+ inputs:
+ aw_context:
+ default: ""
+ description: Agent caller context (used internally by Agentic Workflows).
+ required: false
+ type: string
permissions: {}
@@ -48,44 +77,58 @@ jobs:
if: needs.pre_activation.outputs.activated == 'true'
runs-on: ubuntu-slim
permissions:
+ actions: read
contents: read
outputs:
comment_id: ""
comment_repo: ""
+ engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
model: ${{ steps.generate_aw_info.outputs.model }}
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' }}
steps:
- name: Setup Scripts
- uses: github/gh-aw/actions/setup@48d8fdfddc8cad854ac0c70ceb573f09fb8f9c9b # v0.62.5
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.pre_activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-simplifier.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
- name: Generate agentic run info
id: generate_aw_info
env:
GH_AW_INFO_ENGINE_ID: "copilot"
GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
- GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }}
- GH_AW_INFO_VERSION: ""
- GH_AW_INFO_AGENT_VERSION: "latest"
- GH_AW_INFO_CLI_VERSION: "v0.57.2"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ GH_AW_INFO_AGENT_VERSION: "1.0.40"
+ GH_AW_INFO_CLI_VERSION: "v0.71.5"
GH_AW_INFO_WORKFLOW_NAME: "Code Simplifier"
GH_AW_INFO_EXPERIMENTAL: "false"
GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
GH_AW_INFO_STAGED: "false"
- GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
+ GH_AW_INFO_ALLOWED_DOMAINS: '["go"]'
GH_AW_INFO_FIREWALL_ENABLED: "true"
- GH_AW_INFO_AWF_VERSION: "v0.23.0"
+ GH_AW_INFO_AWF_VERSION: "v0.25.40"
GH_AW_INFO_AWMG_VERSION: ""
GH_AW_INFO_FIREWALL_TYPE: "squid"
GH_AW_COMPILED_STRICT: "true"
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
- const { main } = require('/opt/gh-aw/actions/generate_aw_info.cjs');
+ 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/generate_aw_info.cjs');
await main(core, context);
- 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
+ run: bash "${RUNNER_TEMP}/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: Checkout .github and .agents folders
@@ -95,22 +138,46 @@ jobs:
sparse-checkout: |
.github
.agents
+ .claude
+ .codex
+ .crush
+ .gemini
+ .opencode
+ .pi
sparse-checkout-cone-mode: true
fetch-depth: 1
- - name: Check workflow file timestamps
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ - name: Save agent config folders for base branch restoration
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_WORKFLOW_FILE: "code-simplifier.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
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');
+ 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/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_COMPILED_VERSION: "v0.71.5"
+ 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/check_version_updates.cjs');
await main();
- name: Create prompt with built-in context
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
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 }}
@@ -119,20 +186,27 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ # poutine:ignore untrusted_checkout_exec
run: |
- bash /opt/gh-aw/actions/create_prompt_first.sh
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
{
- cat << 'GH_AW_PROMPT_EOF'
+ cat << 'GH_AW_PROMPT_b6435d392c5b00fb_EOF'
- GH_AW_PROMPT_EOF
- cat "/opt/gh-aw/prompts/xpia.md"
- cat "/opt/gh-aw/prompts/temp_folder_prompt.md"
- cat "/opt/gh-aw/prompts/markdown.md"
- cat "/opt/gh-aw/prompts/safe_outputs_prompt.md"
- cat << 'GH_AW_PROMPT_EOF'
+ GH_AW_PROMPT_b6435d392c5b00fb_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_b6435d392c5b00fb_EOF'
- Tools: create_issue, missing_tool, missing_data, noop
+ Tools: create_pull_request, missing_tool, missing_data, noop
+ GH_AW_PROMPT_b6435d392c5b00fb_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_create_pull_request.md"
+ cat << 'GH_AW_PROMPT_b6435d392c5b00fb_EOF'
+ GH_AW_PROMPT_b6435d392c5b00fb_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
+ cat << 'GH_AW_PROMPT_b6435d392c5b00fb_EOF'
The following GitHub context information is available for this workflow:
{{#if __GH_AW_GITHUB_ACTOR__ }}
@@ -161,28 +235,28 @@ jobs:
{{/if}}
- GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF'
+ GH_AW_PROMPT_b6435d392c5b00fb_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_b6435d392c5b00fb_EOF'
- GH_AW_PROMPT_EOF
- cat << 'GH_AW_PROMPT_EOF'
{{#runtime-import .github/workflows/code-simplifier.md}}
- GH_AW_PROMPT_EOF
+ GH_AW_PROMPT_b6435d392c5b00fb_EOF
} > "$GH_AW_PROMPT"
- name: Interpolate variables and render templates
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ENGINE_ID: "copilot"
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
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');
+ 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/interpolate_prompt.cjs');
await main();
- name: Substitute placeholders
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
GH_AW_GITHUB_ACTOR: ${{ github.actor }}
@@ -193,13 +267,14 @@ jobs:
GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: ${{ needs.pre_activation.outputs.activated }}
with:
script: |
- const { setupGlobals } = require('/opt/gh-aw/actions/setup_globals.cjs');
- setupGlobals(core, github, context, exec, io);
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
- const substitutePlaceholders = require('/opt/gh-aw/actions/substitute_placeholders.cjs');
+ const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
// Call the substitution function
return await substitutePlaceholders({
@@ -213,25 +288,32 @@ jobs:
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,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST,
GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED: process.env.GH_AW_NEEDS_PRE_ACTIVATION_OUTPUTS_ACTIVATED
}
});
- 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
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/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
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
- name: Upload activation artifact
if: success()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: activation
+ include-hidden-files: true
path: |
/tmp/gh-aw/aw_info.json
/tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/base
+ if-no-files-found: ignore
retention-days: 1
agent:
@@ -249,320 +331,247 @@ jobs:
GH_AW_ASSETS_BRANCH: ""
GH_AW_ASSETS_MAX_SIZE_KB: 0
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
- GH_AW_SAFE_OUTPUTS: /opt/gh-aw/safeoutputs/outputs.jsonl
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
GH_AW_WORKFLOW_ID_SANITIZED: codesimplifier
outputs:
+ agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }}
checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
- detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
- detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
has_patch: ${{ steps.collect_output.outputs.has_patch }}
- inference_access_error: ${{ steps.detect-inference-error.outputs.inference_access_error || 'false' }}
+ inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }}
model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }}
output: ${{ steps.collect_output.outputs.output }}
output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
steps:
- name: Setup Scripts
- uses: github/gh-aw/actions/setup@48d8fdfddc8cad854ac0c70ceb573f09fb8f9c9b # v0.62.5
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-simplifier.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Create gh-aw temp directory
- run: bash /opt/gh-aw/actions/create_gh_aw_tmp_dir.sh
+ 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: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
git config --global am.keepcr true
# 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"
+ 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) || (github.event.issue.pull_request)
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ github.event.pull_request || github.event.issue.pull_request
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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');
+ 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/checkout_pr_branch.cjs');
await main();
- name: Install GitHub Copilot CLI
- run: /opt/gh-aw/actions/install_copilot_cli.sh latest
- - name: Install awf binary
- run: bash /opt/gh-aw/actions/install_awf_binary.sh v0.23.0
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.40
- name: Determine automatic lockdown mode for GitHub MCP Server
id: determine-automatic-lockdown
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
env:
GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
with:
script: |
- const determineAutomaticLockdown = require('/opt/gh-aw/actions/determine_automatic_lockdown.cjs');
+ const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
await determineAutomaticLockdown(github, context, core);
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: activation
+ path: /tmp/gh-aw
+ - name: Restore agent config folders from base branch
+ if: steps.checkout-pr.outcome == 'success'
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
- name: Download container images
- run: bash /opt/gh-aw/actions/download_docker_images.sh ghcr.io/github/gh-aw-firewall/agent:0.23.0 ghcr.io/github/gh-aw-firewall/api-proxy:0.23.0 ghcr.io/github/gh-aw-firewall/squid:0.23.0 ghcr.io/github/gh-aw-mcpg:v0.1.8 ghcr.io/github/github-mcp-server:v0.32.0 node:lts-alpine
- - name: Write Safe Outputs Config
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280 ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51 ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
+ - name: Generate Safe Outputs Config
run: |
- mkdir -p /opt/gh-aw/safeoutputs
+ mkdir -p "${RUNNER_TEMP}/gh-aw/safeoutputs"
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
- cat > /opt/gh-aw/safeoutputs/config.json << 'GH_AW_SAFE_OUTPUTS_CONFIG_EOF'
- {"create_issue":{"max":1},"missing_data":{},"missing_tool":{},"noop":{"max":1}}
- GH_AW_SAFE_OUTPUTS_CONFIG_EOF
- cat > /opt/gh-aw/safeoutputs/tools.json << 'GH_AW_SAFE_OUTPUTS_TOOLS_EOF'
- [
+ cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_9974579f3008b6e7_EOF'
+ {"create_pull_request":{"expires":24,"labels":["refactoring","code-quality","automation"],"max":1,"max_patch_files":100,"max_patch_size":1024,"protect_top_level_dot_folders":true,"protected_files":["package.json","bun.lockb","bunfig.toml","deno.json","deno.jsonc","deno.lock","global.json","NuGet.Config","Directory.Packages.props","mix.exs","mix.lock","go.mod","go.sum","stack.yaml","stack.yaml.lock","pom.xml","build.gradle","build.gradle.kts","settings.gradle","settings.gradle.kts","gradle.properties","package-lock.json","yarn.lock","pnpm-lock.yaml","npm-shrinkwrap.json","requirements.txt","Pipfile","Pipfile.lock","pyproject.toml","setup.py","setup.cfg","Gemfile","Gemfile.lock","uv.lock","CODEOWNERS","DESIGN.md","README.md","CONTRIBUTING.md","CHANGELOG.md","SECURITY.md","CODE_OF_CONDUCT.md","AGENTS.md","CLAUDE.md","GEMINI.md"],"reviewers":["copilot"],"title_prefix":"[code-simplifier] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_9974579f3008b6e7_EOF
+ - name: Generate Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
{
- "description": "Create a new GitHub issue for tracking bugs, feature requests, or tasks. Use this for actionable work items that need assignment, labeling, and status tracking. For reports, announcements, or status updates that don't require task tracking, use create_discussion instead. CONSTRAINTS: Maximum 1 issue(s) can be created. Title will be prefixed with \"[code-simplifier] \". Labels [\"refactoring\" \"code-quality\" \"automation\"] will be automatically added.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "body": {
- "description": "Detailed issue description in Markdown. Do NOT repeat the title as a heading since it already appears as the issue's h1. Include context, reproduction steps, or acceptance criteria as appropriate.",
- "type": "string"
+ "description_suffixes": {
+ "create_pull_request": " CONSTRAINTS: Maximum 1 pull request(s) can be created. Title will be prefixed with \"[code-simplifier] \". Labels [\"refactoring\" \"code-quality\" \"automation\"] will be automatically added. Reviewers [\"copilot\"] will be assigned."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "create_pull_request": {
+ "defaultMax": 1,
+ "fields": {
+ "base": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
},
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "branch": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "draft": {
+ "type": "boolean"
},
"labels": {
- "description": "Labels to categorize the issue (e.g., 'bug', 'enhancement'). Labels must exist in the repository.",
- "items": {
- "type": "string"
- },
- "type": "array"
+ "type": "array",
+ "itemType": "string",
+ "itemSanitize": true,
+ "itemMaxLength": 128
},
- "parent": {
- "description": "Parent issue number for creating sub-issues. This is the numeric ID from the GitHub URL (e.g., 42 in github.com/owner/repo/issues/42). Can also be a temporary_id (e.g., 'aw_abc123', 'aw_Test123') from a previously created issue in the same workflow run.",
- "type": [
- "number",
- "string"
- ]
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "temporary_id": {
- "description": "Unique temporary identifier for referencing this issue before it's created. Format: 'aw_' followed by 3 to 12 alphanumeric characters (e.g., 'aw_abc1', 'aw_Test123'). Use '#aw_ID' in body text to reference other issues by their temporary_id; these are replaced with actual issue numbers after creation.",
- "pattern": "^aw_[A-Za-z0-9]{3,12}$",
- "type": "string"
+ "repo": {
+ "type": "string",
+ "maxLength": 256
},
"title": {
- "description": "Concise issue title summarizing the bug, feature, or task. The title appears as the main heading, so keep it brief and descriptive.",
- "type": "string"
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
}
- },
- "required": [
- "title",
- "body"
- ],
- "type": "object"
+ }
},
- "name": "create_issue"
- },
- {
- "description": "Report that a tool or capability needed to complete the task is not available, or share any information you deem important about missing functionality or limitations. Use this when you cannot accomplish what was requested because the required functionality is missing or access is restricted.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
"alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "reason": {
- "description": "Explanation of why this tool is needed or what information you want to share about the limitation (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- },
- "tool": {
- "description": "Optional: Name or description of the missing tool or capability (max 128 characters). Be specific about what functionality is needed.",
- "type": "string"
- }
- },
- "required": [
- "reason"
- ],
- "type": "object"
- },
- "name": "missing_tool"
- },
- {
- "description": "Log a transparency message when no significant actions are needed. Use this to confirm workflow completion and provide visibility when analysis is complete but no changes or outputs are required (e.g., 'No issues found', 'All checks passed'). This ensures the workflow produces human-visible output even when no other actions are taken.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
- },
- "message": {
- "description": "Status or completion message to log. Should explain what was analyzed and the outcome (e.g., 'Code review complete - no issues found', 'Analysis complete - all tests passing').",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
- }
- },
- "required": [
- "message"
- ],
- "type": "object"
- },
- "name": "noop"
- },
- {
- "description": "Report that data or information needed to complete the task is not available. Use this when you cannot accomplish what was requested because required data, context, or information is missing.",
- "inputSchema": {
- "additionalProperties": false,
- "properties": {
- "alternatives": {
- "description": "Any workarounds, manual steps, or alternative approaches the user could take (max 256 characters).",
- "type": "string"
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
},
"context": {
- "description": "Additional context about the missing data or where it should come from (max 256 characters).",
- "type": "string"
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
},
"data_type": {
- "description": "Type or description of the missing data or information (max 128 characters). Be specific about what data is needed.",
- "type": "string"
- },
- "integrity": {
- "description": "Trustworthiness level of the message source (e.g., \"low\", \"medium\", \"high\").",
- "type": "string"
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
},
"reason": {
- "description": "Explanation of why this data is needed to complete the task (max 256 characters).",
- "type": "string"
- },
- "secrecy": {
- "description": "Confidentiality level of the message content (e.g., \"public\", \"internal\", \"private\").",
- "type": "string"
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
}
- },
- "required": [],
- "type": "object"
+ }
},
- "name": "missing_data"
- }
- ]
- GH_AW_SAFE_OUTPUTS_TOOLS_EOF
- cat > /opt/gh-aw/safeoutputs/validation.json << 'GH_AW_SAFE_OUTPUTS_VALIDATION_EOF'
- {
- "create_issue": {
- "defaultMax": 1,
- "fields": {
- "body": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
- },
- "labels": {
- "type": "array",
- "itemType": "string",
- "itemSanitize": true,
- "itemMaxLength": 128
- },
- "parent": {
- "issueOrPRNumber": true
- },
- "repo": {
- "type": "string",
- "maxLength": 256
- },
- "temporary_id": {
- "type": "string"
- },
- "title": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 128
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
}
- }
- },
- "missing_data": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "context": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "data_type": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- },
- "reason": {
- "type": "string",
- "sanitize": true,
- "maxLength": 256
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
}
- }
- },
- "missing_tool": {
- "defaultMax": 20,
- "fields": {
- "alternatives": {
- "type": "string",
- "sanitize": true,
- "maxLength": 512
- },
- "reason": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 256
- },
- "tool": {
- "type": "string",
- "sanitize": true,
- "maxLength": 128
- }
- }
- },
- "noop": {
- "defaultMax": 1,
- "fields": {
- "message": {
- "required": true,
- "type": "string",
- "sanitize": true,
- "maxLength": 65000
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 1024
+ }
}
}
}
- }
- GH_AW_SAFE_OUTPUTS_VALIDATION_EOF
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/generate_safe_outputs_tools.cjs');
+ await main();
- name: Generate Safe Outputs MCP Server Config
id: safe-outputs-config
run: |
@@ -585,37 +594,41 @@ jobs:
id: safe-outputs-start
env:
DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
- GH_AW_SAFE_OUTPUTS_TOOLS_PATH: /opt/gh-aw/safeoutputs/tools.json
- GH_AW_SAFE_OUTPUTS_CONFIG_PATH: /opt/gh-aw/safeoutputs/config.json
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
run: |
# Environment variables are set above to prevent template injection
export DEBUG
+ export GH_AW_SAFE_OUTPUTS
export GH_AW_SAFE_OUTPUTS_PORT
export GH_AW_SAFE_OUTPUTS_API_KEY
export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
export GH_AW_MCP_LOG_DIR
- bash /opt/gh-aw/actions/start_safe_outputs_server.sh
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
- name: Start MCP Gateway
id: start-mcp-gateway
env:
- GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
- GITHUB_MCP_LOCKDOWN: ${{ steps.determine-automatic-lockdown.outputs.lockdown == 'true' && '1' || '0' }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
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
+ mkdir -p "${RUNNER_TEMP}/gh-aw/mcp-config"
# Export gateway environment variables for MCP config and gateway script
- export MCP_GATEWAY_PORT="80"
+ export MCP_GATEWAY_PORT="8080"
export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ export MCP_GATEWAY_HOST_DOMAIN="localhost"
MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
echo "::add-mask::${MCP_GATEWAY_API_KEY}"
export MCP_GATEWAY_API_KEY
@@ -625,20 +638,30 @@ jobs:
export DEBUG="*"
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 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_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 -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.1.8'
+ MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
+ MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
+ DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0')
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -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.3.6'
mkdir -p /home/runner/.copilot
- cat << GH_AW_MCP_CONFIG_EOF | bash /opt/gh-aw/actions/start_mcp_gateway.sh
+ GH_AW_NODE=$(which node 2>/dev/null || command -v node 2>/dev/null || echo node)
+ cat << GH_AW_MCP_CONFIG_14204ee419df0c42_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
{
"mcpServers": {
"github": {
"type": "stdio",
- "container": "ghcr.io/github/github-mcp-server:v0.32.0",
+ "container": "ghcr.io/github/github-mcp-server:v1.0.3",
"env": {
- "GITHUB_LOCKDOWN_MODE": "$GITHUB_MCP_LOCKDOWN",
+ "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
"GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
"GITHUB_READ_ONLY": "1",
"GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
}
},
"safeoutputs": {
@@ -646,6 +669,13 @@ jobs:
"url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
"headers": {
"Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
}
}
},
@@ -656,14 +686,28 @@ jobs:
"payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
}
}
- GH_AW_MCP_CONFIG_EOF
- - name: Download activation artifact
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
+ GH_AW_MCP_CONFIG_14204ee419df0c42_EOF
+ - name: Mount MCP servers as CLIs
+ id: mount-mcp-clis
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
- name: activation
- path: /tmp/gh-aw
- - name: Clean git credentials
- run: bash /opt/gh-aw/actions/clean_git_credentials.sh
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
+ await main();
+ - name: Clean credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Audit pre-agent workspace
+ id: pre_agent_audit
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
- name: Execute GitHub Copilot CLI
id: agentic_execution
# Copilot CLI tool arguments (sorted):
@@ -671,20 +715,26 @@ jobs:
run: |
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.40/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","go.dev","golang.org","goproxy.io","host.docker.internal","pkg.go.dev","proxy.golang.org","raw.githubusercontent.com","registry.npmjs.org","storage.googleapis.com","sum.golang.org","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"models":{"auto":["large"],"deep-research":["copilot/deep-research*","google/deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"]}},"container":{"imageTag":"0.25.40,squid=sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51,agent=sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504,api-proxy=sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280,cli-proxy=sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
# shellcheck disable=SC1003
- sudo -E awf --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.23.0 --skip-pull --enable-api-proxy \
- -- /bin/bash -c '/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 --allow-all-tools --allow-all-paths --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_API_KEY: dummy-byok-key-for-offline-mode
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || '' }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
GH_AW_PHASE: agent
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_VERSION: v0.57.2
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_VERSION: v0.71.5
GITHUB_API_URL: ${{ github.api_url }}
GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
GITHUB_HEAD_REF: ${{ github.head_ref }}
GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
GITHUB_REF_NAME: ${{ github.ref_name }}
@@ -696,40 +746,28 @@ jobs:
GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
XDG_CONFIG_HOME: /home/runner
- - name: Detect inference access error
- id: detect-inference-error
+ - name: Detect Copilot errors
+ id: detect-copilot-errors
if: always()
continue-on-error: true
- run: bash /opt/gh-aw/actions/detect_inference_access_error.sh
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs"
- name: Configure Git credentials
env:
REPO_NAME: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
git config --global am.keepcr true
# 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"
+ 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: 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
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
- name: Stop MCP Gateway
if: always()
continue-on-error: true
@@ -738,15 +776,15 @@ jobs:
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"
+ bash "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
- name: Redact secrets in logs
if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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');
+ 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/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'
@@ -756,63 +794,49 @@ jobs:
SECRET_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Append agent step summary
if: always()
- run: bash /opt/gh-aw/actions/append_agent_step_summary.sh
- - name: Upload Safe Outputs
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
if: always()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- with:
- name: safe-output
- path: ${{ env.GH_AW_SAFE_OUTPUTS }}
- if-no-files-found: warn
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
- name: Ingest agent output
id: collect_output
if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
- GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
- GH_AW_ALLOWED_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"
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,pkg.go.dev,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
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/collect_ndjson_output.cjs');
+ 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/collect_ndjson_output.cjs');
await main();
- - name: Upload sanitized agent output
- if: always() && env.GH_AW_AGENT_OUTPUT
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- with:
- name: agent-output
- path: ${{ env.GH_AW_AGENT_OUTPUT }}
- if-no-files-found: warn
- - name: Upload engine output files
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
- 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
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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');
+ 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/parse_copilot_log.cjs');
await main();
- name: Parse MCP Gateway logs for step summary
if: always()
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ id: parse-mcp-gateway
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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');
+ 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/parse_mcp_gateway_log.cjs');
await main();
- name: Print firewall logs
if: always()
@@ -820,35 +844,284 @@ jobs:
env:
AWF_LOGS_DIR: /tmp/gh-aw/sandbox/firewall/logs
run: |
- # Fix permissions on firewall logs so they can be uploaded as artifacts
+ # Fix permissions on firewall logs/audit dirs 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
+ sudo chmod -R a+r /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
# Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
if command -v awf &> /dev/null; then
awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
else
echo 'AWF binary not installed, skipping firewall log summary'
fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/parse_token_usage.cjs');
+ await main();
+ - name: Print AWF reflect summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/awf_reflect_summary.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
- name: Upload agent artifacts
if: always()
continue-on-error: true
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: agent-artifacts
+ name: agent
path: |
/tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
/tmp/gh-aw/mcp-logs/
- /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/agent_usage.json
/tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/pre-agent-audit.txt
/tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/aw-*.patch
+ /tmp/gh-aw/aw-*.bundle
+ /tmp/gh-aw/awf-config.json
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ /tmp/gh-aw/sandbox/firewall/awf-reflect.json
if-no-files-found: ignore
- # --- Threat Detection (inline) ---
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - detection
+ - safe_outputs
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: write
+ issues: write
+ pull-requests: write
+ concurrency:
+ group: "gh-aw-conclusion-code-simplifier"
+ cancel-in-progress: false
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-simplifier.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ 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_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@6762bfba6ae426a03aac46e8f68701461c667404"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/blob/6762bfba6ae426a03aac46e8f68701461c667404/.github/workflows/code-simplifier.md"
+ GH_AW_TRACKER_ID: "code-simplifier"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "false"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_noop_message.cjs');
+ await main();
+ - name: Log detection run
+ id: detection_runs
+ 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_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@6762bfba6ae426a03aac46e8f68701461c667404"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/blob/6762bfba6ae426a03aac46e8f68701461c667404/.github/workflows/code-simplifier.md"
+ GH_AW_TRACKER_ID: "code-simplifier"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_detection_runs.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ 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_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@6762bfba6ae426a03aac46e8f68701461c667404"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/blob/6762bfba6ae426a03aac46e8f68701461c667404/.github/workflows/code-simplifier.md"
+ GH_AW_TRACKER_ID: "code-simplifier"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ 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_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@6762bfba6ae426a03aac46e8f68701461c667404"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/blob/6762bfba6ae426a03aac46e8f68701461c667404/.github/workflows/code-simplifier.md"
+ GH_AW_TRACKER_ID: "code-simplifier"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/report_incomplete_handler.cjs');
+ await main();
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ 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_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@6762bfba6ae426a03aac46e8f68701461c667404"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/blob/6762bfba6ae426a03aac46e8f68701461c667404/.github/workflows/code-simplifier.md"
+ GH_AW_TRACKER_ID: "code-simplifier"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "code-simplifier"
+ GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
+ GH_AW_CODE_PUSH_FAILURE_ERRORS: ${{ needs.safe_outputs.outputs.code_push_failure_errors }}
+ GH_AW_CODE_PUSH_FAILURE_COUNT: ${{ needs.safe_outputs.outputs.code_push_failure_count }}
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
+ GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
+ GH_AW_TIMEOUT_MINUTES: "30"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_agent_failure.cjs');
+ await main();
+
+ detection:
+ needs:
+ - activation
+ - agent
+ if: >
+ always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-simplifier.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Checkout repository for patch context
+ if: needs.agent.outputs.has_patch == 'true'
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ # --- Threat Detection ---
+ - name: Clean stale firewall files from agent artifact
+ run: |
+ rm -rf /tmp/gh-aw/sandbox/firewall/logs
+ rm -rf /tmp/gh-aw/sandbox/firewall/audit
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280 ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51
- name: Check if detection needed
id: detection_guard
if: always()
env:
- OUTPUT_TYPES: ${{ steps.collect_output.outputs.output_types }}
- HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
+ OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
run: |
if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
echo "run_detection=true" >> "$GITHUB_OUTPUT"
@@ -857,10 +1130,10 @@ jobs:
echo "run_detection=false" >> "$GITHUB_OUTPUT"
echo "Detection skipped: no agent outputs or patches to analyze"
fi
- - name: Clear MCP configuration for detection
+ - name: Clear MCP Config for detection
if: always() && steps.detection_guard.outputs.run_detection == 'true'
run: |
- rm -f /tmp/gh-aw/mcp-config/mcp-servers.json
+ rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
rm -f /home/runner/.copilot/mcp-config.json
rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
- name: Prepare threat detection files
@@ -872,53 +1145,67 @@ jobs:
for f in /tmp/gh-aw/aw-*.patch; do
[ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
done
+ for f in /tmp/gh-aw/aw-*.bundle; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
echo "Prepared threat detection files:"
ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
- name: Setup threat detection
if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
WORKFLOW_NAME: "Code Simplifier"
WORKFLOW_DESCRIPTION: "Analyzes recently modified code and creates pull requests with simplifications that improve clarity, consistency, and maintainability while preserving functionality"
- HAS_PATCH: ${{ steps.collect_output.outputs.has_patch }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
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/setup_threat_detection.cjs');
+ 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/setup_threat_detection.cjs');
await main();
- name: Ensure threat-detection directory and log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
run: |
mkdir -p /tmp/gh-aw/threat-detection
touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Setup Node.js
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: '24'
+ package-manager-cache: false
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.40
- name: Execute GitHub Copilot CLI
if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ continue-on-error: true
id: detection_agentic_execution
# Copilot CLI tool arguments (sorted):
- # --allow-tool shell(cat)
- # --allow-tool shell(grep)
- # --allow-tool shell(head)
- # --allow-tool shell(jq)
- # --allow-tool shell(ls)
- # --allow-tool shell(tail)
- # --allow-tool shell(wc)
timeout-minutes: 20
run: |
set -o pipefail
touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.40/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.40,squid=sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51,agent=sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504,api-proxy=sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280,cli-proxy=sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
# shellcheck disable=SC1003
- sudo -E awf --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,github.com,host.docker.internal,raw.githubusercontent.com,registry.npmjs.org,telemetry.enterprise.githubcopilot.com" --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.23.0 --skip-pull --enable-api-proxy \
- -- /bin/bash -c '/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 --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(jq)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(wc)'\'' --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
env:
COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_API_KEY: dummy-byok-key-for-offline-mode
COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
- COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || '' }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.6' }}
GH_AW_PHASE: detection
GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
- GH_AW_VERSION: v0.57.2
+ GH_AW_VERSION: v0.71.5
GITHUB_API_URL: ${{ github.api_url }}
GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
GITHUB_HEAD_REF: ${{ github.head_ref }}
GITHUB_REF_NAME: ${{ github.ref_name }}
GITHUB_SERVER_URL: ${{ github.server_url }}
@@ -929,258 +1216,208 @@ jobs:
GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
GIT_COMMITTER_NAME: github-actions[bot]
XDG_CONFIG_HOME: /home/runner
- - name: Parse threat detection results
- id: parse_detection_results
- if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- 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_threat_detection_results.cjs');
- await main();
- name: Upload threat detection log
if: always() && steps.detection_guard.outputs.run_detection == 'true'
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: threat-detection.log
+ name: detection
path: /tmp/gh-aw/threat-detection/detection.log
if-no-files-found: ignore
- - name: Set detection conclusion
+ - name: Parse and conclude threat detection
id: detection_conclusion
if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
- DETECTION_SUCCESS: ${{ steps.parse_detection_results.outputs.success }}
- run: |
- if [[ "$RUN_DETECTION" != "true" ]]; then
- echo "conclusion=skipped" >> "$GITHUB_OUTPUT"
- echo "success=true" >> "$GITHUB_OUTPUT"
- echo "Detection was not needed, marking as skipped"
- elif [[ "$DETECTION_SUCCESS" == "true" ]]; then
- echo "conclusion=success" >> "$GITHUB_OUTPUT"
- echo "success=true" >> "$GITHUB_OUTPUT"
- echo "Detection passed successfully"
- else
- echo "conclusion=failure" >> "$GITHUB_OUTPUT"
- echo "success=false" >> "$GITHUB_OUTPUT"
- echo "Detection found issues"
- fi
-
- conclusion:
- needs:
- - activation
- - agent
- - safe_outputs
- if: (always()) && (needs.agent.result != 'skipped')
- runs-on: ubuntu-slim
- permissions:
- contents: read
- issues: write
- concurrency:
- group: "gh-aw-conclusion-code-simplifier"
- cancel-in-progress: false
- outputs:
- noop_message: ${{ steps.noop.outputs.noop_message }}
- tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
- total_count: ${{ steps.missing_tool.outputs.total_count }}
- steps:
- - name: Setup Scripts
- uses: github/gh-aw/actions/setup@48d8fdfddc8cad854ac0c70ceb573f09fb8f9c9b # v0.62.5
+ GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
with:
- destination: /opt/gh-aw/actions
- - name: Download agent output artifact
- id: download-agent-output
- continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
- with:
- name: agent-output
- path: /tmp/gh-aw/safeoutputs/
- - name: Setup agent output environment variable
- if: steps.download-agent-output.outcome == 'success'
- run: |
- mkdir -p /tmp/gh-aw/safeoutputs/
- find "/tmp/gh-aw/safeoutputs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
- - name: Process No-Op Messages
- id: noop
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_NOOP_MAX: "1"
- GH_AW_WORKFLOW_NAME: "Code Simplifier"
- GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/76d37d925abd44fee97379206f105b74b91a285b/.github/workflows/code-simplifier.md"
- GH_AW_TRACKER_ID: "code-simplifier"
- with:
- github-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/noop.cjs');
- await main();
- - name: Record Missing Tool
- id: missing_tool
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Code Simplifier"
- GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/76d37d925abd44fee97379206f105b74b91a285b/.github/workflows/code-simplifier.md"
- GH_AW_TRACKER_ID: "code-simplifier"
- with:
- github-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/missing_tool.cjs');
- await main();
- - name: Handle Agent Failure
- id: handle_agent_failure
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Code Simplifier"
- GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/76d37d925abd44fee97379206f105b74b91a285b/.github/workflows/code-simplifier.md"
- GH_AW_TRACKER_ID: "code-simplifier"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_WORKFLOW_ID: "code-simplifier"
- GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
- GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
- GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
- GH_AW_GROUP_REPORTS: "false"
- GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
- GH_AW_TIMEOUT_MINUTES: "30"
- with:
- github-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/handle_agent_failure.cjs');
- await main();
- - name: Handle No-Op Message
- id: handle_noop_message
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
- env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_WORKFLOW_NAME: "Code Simplifier"
- GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/76d37d925abd44fee97379206f105b74b91a285b/.github/workflows/code-simplifier.md"
- GH_AW_TRACKER_ID: "code-simplifier"
- GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
- GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
- GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }}
- GH_AW_NOOP_REPORT_AS_ISSUE: "false"
- with:
- github-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/handle_noop_message.cjs');
- await main();
+ try {
+ 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/parse_threat_detection_results.cjs');
+ await main();
+ } catch (loadErr) {
+ const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
+ const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
+ core.error(msg);
+ core.setOutput('reason', 'parse_error');
+ if (continueOnError) {
+ core.warning('\u26A0\uFE0F ' + msg);
+ core.setOutput('conclusion', 'warning');
+ core.setOutput('success', 'false');
+ } else {
+ core.setOutput('conclusion', 'failure');
+ core.setOutput('success', 'false');
+ core.setFailed(msg);
+ }
+ }
pre_activation:
runs-on: ubuntu-slim
outputs:
- activated: ${{ (steps.check_membership.outputs.is_team_member == 'true') && (steps.check_skip_if_match.outputs.skip_check_ok == 'true') }}
+ activated: ${{ steps.check_membership.outputs.is_team_member == 'true' && steps.check_skip_if_match.outputs.skip_check_ok == 'true' }}
matched_command: ''
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
steps:
- name: Setup Scripts
- uses: github/gh-aw/actions/setup@48d8fdfddc8cad854ac0c70ceb573f09fb8f9c9b # v0.62.5
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-simplifier.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
- name: Check team membership for workflow
id: check_membership
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
- GH_AW_REQUIRED_ROLES: admin,maintainer,write
+ GH_AW_REQUIRED_ROLES: "admin,maintainer,write"
with:
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/check_membership.cjs');
+ 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/check_membership.cjs');
await main();
- name: Check skip-if-match query
id: check_skip_if_match
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_AW_SKIP_QUERY: "is:pr is:open in:title \"[code-simplifier]\""
GH_AW_WORKFLOW_NAME: "Code Simplifier"
GH_AW_SKIP_MAX_MATCHES: "1"
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_skip_if_match.cjs');
+ 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/check_skip_if_match.cjs');
await main();
safe_outputs:
- needs: agent
- if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.agent.outputs.detection_success == 'true')
+ needs:
+ - activation
+ - agent
+ - detection
+ if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
runs-on: ubuntu-slim
permissions:
- contents: read
+ contents: write
issues: write
+ pull-requests: write
timeout-minutes: 15
env:
GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/code-simplifier"
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ GH_AW_ENGINE_VERSION: "1.0.40"
GH_AW_TRACKER_ID: "code-simplifier"
GH_AW_WORKFLOW_ID: "code-simplifier"
GH_AW_WORKFLOW_NAME: "Code Simplifier"
- GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b"
- GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/tree/76d37d925abd44fee97379206f105b74b91a285b/.github/workflows/code-simplifier.md"
+ GH_AW_WORKFLOW_SOURCE: "github/gh-aw/.github/workflows/code-simplifier.md@6762bfba6ae426a03aac46e8f68701461c667404"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/github/gh-aw/blob/6762bfba6ae426a03aac46e8f68701461c667404/.github/workflows/code-simplifier.md"
outputs:
code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
- created_issue_number: ${{ steps.process_safe_outputs.outputs.created_issue_number }}
- created_issue_url: ${{ steps.process_safe_outputs.outputs.created_issue_url }}
+ created_pr_number: ${{ steps.process_safe_outputs.outputs.created_pr_number }}
+ created_pr_url: ${{ steps.process_safe_outputs.outputs.created_pr_url }}
process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
steps:
- name: Setup Scripts
- uses: github/gh-aw/actions/setup@48d8fdfddc8cad854ac0c70ceb573f09fb8f9c9b # v0.62.5
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
with:
- destination: /opt/gh-aw/actions
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Code Simplifier"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/code-simplifier.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
- name: Download agent output artifact
id: download-agent-output
continue-on-error: true
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
- name: agent-output
- path: /tmp/gh-aw/safeoutputs/
+ name: agent
+ path: /tmp/gh-aw/
- name: Setup agent output environment variable
+ id: setup-agent-output-env
if: steps.download-agent-output.outcome == 'success'
run: |
- mkdir -p /tmp/gh-aw/safeoutputs/
- find "/tmp/gh-aw/safeoutputs/" -type f -print
- echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/safeoutputs/agent_output.json" >> "$GITHUB_ENV"
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Download patch artifact
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Checkout repository
+ if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request')
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ ref: ${{ github.base_ref || github.event.pull_request.base.ref || github.ref_name || github.event.repository.default_branch }}
+ token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ persist-credentials: false
+ fetch-depth: 1
+ - name: Configure Git credentials
+ if: (!cancelled()) && needs.agent.result != 'skipped' && contains(needs.agent.outputs.output_types, 'create_pull_request')
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GIT_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # Re-authenticate git with GitHub token
+ SERVER_URL_STRIPPED="${SERVER_URL#https://}"
+ git remote set-url origin "https://x-access-token:${GIT_TOKEN}@${SERVER_URL_STRIPPED}/${REPO_NAME}.git"
+ echo "Git configured with standard GitHub Actions identity"
+ - 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"
- name: Process Safe Outputs
id: process_safe_outputs
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
- GH_AW_AGENT_OUTPUT: ${{ env.GH_AW_AGENT_OUTPUT }}
- GH_AW_ALLOWED_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"
+ GH_AW_AGENT_OUTPUT: ${{ steps.setup-agent-output-env.outputs.GH_AW_AGENT_OUTPUT }}
+ GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,github.com,go.dev,golang.org,goproxy.io,host.docker.internal,pkg.go.dev,proxy.golang.org,raw.githubusercontent.com,registry.npmjs.org,storage.googleapis.com,sum.golang.org,telemetry.enterprise.githubcopilot.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_issue\":{\"labels\":[\"refactoring\",\"code-quality\",\"automation\"],\"max\":1,\"title_prefix\":\"[code-simplifier] \"},\"missing_data\":{},\"missing_tool\":{}}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_pull_request\":{\"expires\":24,\"labels\":[\"refactoring\",\"code-quality\",\"automation\"],\"max\":1,\"max_patch_files\":100,\"max_patch_size\":1024,\"protect_top_level_dot_folders\":true,\"protected_files\":[\"package.json\",\"bun.lockb\",\"bunfig.toml\",\"deno.json\",\"deno.jsonc\",\"deno.lock\",\"global.json\",\"NuGet.Config\",\"Directory.Packages.props\",\"mix.exs\",\"mix.lock\",\"go.mod\",\"go.sum\",\"stack.yaml\",\"stack.yaml.lock\",\"pom.xml\",\"build.gradle\",\"build.gradle.kts\",\"settings.gradle\",\"settings.gradle.kts\",\"gradle.properties\",\"package-lock.json\",\"yarn.lock\",\"pnpm-lock.yaml\",\"npm-shrinkwrap.json\",\"requirements.txt\",\"Pipfile\",\"Pipfile.lock\",\"pyproject.toml\",\"setup.py\",\"setup.cfg\",\"Gemfile\",\"Gemfile.lock\",\"uv.lock\",\"CODEOWNERS\",\"DESIGN.md\",\"README.md\",\"CONTRIBUTING.md\",\"CHANGELOG.md\",\"SECURITY.md\",\"CODE_OF_CONDUCT.md\",\"AGENTS.md\",\"CLAUDE.md\",\"GEMINI.md\"],\"reviewers\":[\"copilot\"],\"title_prefix\":\"[code-simplifier] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
+ GH_AW_CI_TRIGGER_TOKEN: ${{ secrets.GH_AW_CI_TRIGGER_TOKEN }}
with:
github-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/safe_output_handler_manager.cjs');
+ 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/safe_output_handler_manager.cjs');
await main();
- - name: Upload safe output items manifest
+ - name: Upload Safe Outputs Items
if: always()
- uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
- name: safe-output-items
- path: /tmp/safe-output-items.jsonl
- if-no-files-found: warn
+ name: safe-outputs-items
+ path: |
+ /tmp/gh-aw/safe-output-items.jsonl
+ /tmp/gh-aw/temporary-id-map.json
+ if-no-files-found: ignore
diff --git a/.github/workflows/code-simplifier.md b/.github/workflows/code-simplifier.md
index f0d512276..ac4481869 100644
--- a/.github/workflows/code-simplifier.md
+++ b/.github/workflows/code-simplifier.md
@@ -1,434 +1,3 @@
-<<<<<<< current (local changes)
----
-on:
- schedule: daily
- skip-if-match: is:pr is:open in:title "[code-simplifier]"
-permissions:
- contents: read
- issues: read
- pull-requests: read
-safe-outputs:
- create-issue:
- labels:
- - refactoring
- - code-quality
- - automation
- title-prefix: "[code-simplifier] "
- noop:
- report-as-issue: false
-description: Analyzes recently modified code and creates pull requests with simplifications that improve clarity, consistency, and maintainability while preserving functionality
-name: Code Simplifier
-source: github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b
-strict: true
-timeout-minutes: 30
-tools:
- github:
- toolsets:
- - default
-tracker-id: code-simplifier
----
-
-
-
-# Code Simplifier Agent
-
-You are an expert code simplification specialist focused on enhancing code clarity, consistency, and maintainability while preserving exact functionality. Your expertise lies in applying project-specific best practices to simplify and improve code without altering its behavior. You prioritize readable, explicit code over overly compact solutions. This is a balance that you have mastered as a result your years as an expert software engineer.
-
-## Your Mission
-
-Analyze recently modified code from the last 24 hours and apply refinements that improve code quality while preserving all functionality. Create a GitHub issue with a properly formatted diff if improvements are found.
-
-## Current Context
-
-- **Repository**: ${{ github.repository }}
-- **Workspace**: ${{ github.workspace }}
-
-## Phase 1: Identify Recently Modified Code
-
-### 1.1 Find Recent Changes
-
-Search for merged pull requests and commits from the last 24 hours:
-
-```bash
-# Get yesterday's date in ISO format
-YESTERDAY=$(date -d '1 day ago' '+%Y-%m-%d' 2>/dev/null || date -v-1d '+%Y-%m-%d')
-
-# List recent commits
-git log --since="24 hours ago" --pretty=format:"%H %s" --no-merges
-```
-
-Use GitHub tools to:
-- Search for pull requests merged in the last 24 hours: `repo:${{ github.repository }} is:pr is:merged merged:>=${YESTERDAY}`
-- Get details of merged PRs to understand what files were changed
-- List commits from the last 24 hours to identify modified files
-
-### 1.2 Extract Changed Files
-
-For each merged PR or recent commit:
-- Use `pull_request_read` with `method: get_files` to list changed files
-- Use `get_commit` to see file changes in recent commits
-- Focus on source code files (`.go`, `.js`, `.ts`, `.tsx`, `.cjs`, `.py`, etc.)
-- Exclude test files, lock files, and generated files
-
-### 1.3 Determine Scope
-
-If **no files were changed in the last 24 hours**, exit gracefully without creating a PR:
-
-```
-✅ No code changes detected in the last 24 hours.
-Code simplifier has nothing to process today.
-```
-
-If **files were changed**, proceed to Phase 2.
-
-## Phase 2: Analyze and Simplify Code
-
-### 2.1 Review Project Standards
-
-Before simplifying, review the project's coding standards from relevant documentation:
-
-- For Go projects: Check `AGENTS.md`, `DEVGUIDE.md`, or similar files
-- For JavaScript/TypeScript: Look for `CLAUDE.md`, style guides, or coding conventions
-- For Python: Check for style guides, PEP 8 adherence, or project-specific conventions
-
-**Key Standards to Apply:**
-
-For **JavaScript/TypeScript** projects:
-- Use ES modules with proper import sorting and extensions
-- Prefer `function` keyword over arrow functions for top-level functions
-- Use explicit return type annotations for top-level functions
-- Follow proper React component patterns with explicit Props types
-- Use proper error handling patterns (avoid try/catch when possible)
-- Maintain consistent naming conventions
-
-For **Go** projects:
-- Use `any` instead of `interface{}`
-- Follow console formatting for CLI output
-- Use semantic type aliases for domain concepts
-- Prefer small, focused files (200-500 lines ideal)
-- Use table-driven tests with descriptive names
-
-For **Python** projects:
-- Follow PEP 8 style guide
-- Use type hints for function signatures
-- Prefer explicit over implicit code
-- Use list/dict comprehensions where they improve clarity (not complexity)
-
-### 2.2 Simplification Principles
-
-Apply these refinements to the recently modified code:
-
-#### 1. Preserve Functionality
-- **NEVER** change what the code does - only how it does it
-- All original features, outputs, and behaviors must remain intact
-- Run tests before and after to ensure no behavioral changes
-
-#### 2. Enhance Clarity
-- Reduce unnecessary complexity and nesting
-- Eliminate redundant code and abstractions
-- Improve readability through clear variable and function names
-- Consolidate related logic
-- Remove unnecessary comments that describe obvious code
-- **IMPORTANT**: Avoid nested ternary operators - prefer switch statements or if/else chains
-- Choose clarity over brevity - explicit code is often better than compact code
-
-#### 3. Apply Project Standards
-- Use project-specific conventions and patterns
-- Follow established naming conventions
-- Apply consistent formatting
-- Use appropriate language features (modern syntax where beneficial)
-
-#### 4. Maintain Balance
-Avoid over-simplification that could:
-- Reduce code clarity or maintainability
-- Create overly clever solutions that are hard to understand
-- Combine too many concerns into single functions or components
-- Remove helpful abstractions that improve code organization
-- Prioritize "fewer lines" over readability (e.g., nested ternaries, dense one-liners)
-- Make the code harder to debug or extend
-
-### 2.3 Perform Code Analysis
-
-For each changed file:
-
-1. **Read the file contents** using the edit or view tool
-2. **Identify refactoring opportunities**:
- - Long functions that could be split
- - Duplicate code patterns
- - Complex conditionals that could be simplified
- - Unclear variable names
- - Missing or excessive comments
- - Non-standard patterns
-3. **Design the simplification**:
- - What specific changes will improve clarity?
- - How can complexity be reduced?
- - What patterns should be applied?
- - Will this maintain all functionality?
-
-### 2.4 Apply Simplifications
-
-Use the **edit** tool to modify files:
-
-```bash
-# For each file with improvements:
-# 1. Read the current content
-# 2. Apply targeted edits to simplify code
-# 3. Ensure all functionality is preserved
-```
-
-**Guidelines for edits:**
-- Make surgical, targeted changes
-- One logical improvement per edit (but batch multiple edits in a single response)
-- Preserve all original behavior
-- Keep changes focused on recently modified code
-- Don't refactor unrelated code unless it improves understanding of the changes
-
-## Phase 3: Validate Changes
-
-### 3.1 Run Tests
-
-After making simplifications, run the project's test suite to ensure no functionality was broken:
-
-```bash
-# For Go projects
-make test-unit
-
-# For JavaScript/TypeScript projects
-npm test
-
-# For Python projects
-pytest
-```
-
-If tests fail:
-- Review the failures carefully
-- Revert changes that broke functionality
-- Adjust simplifications to preserve behavior
-- Re-run tests until they pass
-
-### 3.2 Run Linters
-
-Ensure code style is consistent:
-
-```bash
-# For Go projects
-make lint
-
-# For JavaScript/TypeScript projects
-npm run lint
-
-# For Python projects
-flake8 . || pylint .
-```
-
-Fix any linting issues introduced by the simplifications.
-
-### 3.3 Check Build
-
-Verify the project still builds successfully:
-
-```bash
-# For Go projects
-make build
-
-# For JavaScript/TypeScript projects
-npm run build
-
-# For Python projects
-# (typically no build step, but check imports)
-python -m py_compile changed_files.py
-```
-
-## Phase 4: Create GitHub Issue with Diff
-
-### 4.1 Determine If Issue Is Needed
-
-Only create an issue if:
-- ✅ You made actual code simplifications
-- ✅ All tests pass
-- ✅ Linting is clean
-- ✅ Build succeeds
-- ✅ Changes improve code quality without breaking functionality
-
-If no improvements were made or changes broke tests, exit gracefully:
-
-```
-✅ Code analyzed from last 24 hours.
-No simplifications needed - code already meets quality standards.
-```
-
-### 4.2 Generate Git Diff
-
-Before creating the issue, generate a properly formatted git diff that can be used to create a pull request:
-
-```bash
-# Stage all changes if not already staged
-git add .
-
-# Generate a complete unified diff of all staged changes
-git diff --cached > /tmp/code-simplification.diff
-
-# Read the diff to include in the discussion
-cat /tmp/code-simplification.diff
-```
-
-**Important**: The diff must be in standard unified diff format (git unified diff) that includes:
-- File headers with `diff --git a/path b/path`
-- Index lines with git hashes
-- `---` and `+++` lines showing old and new file paths
-- `@@` lines showing line numbers
-- Actual code changes with `-` for removed lines and `+` for added lines
-
-This format is compatible with:
-- `git apply` command for direct application
-- GitHub's "Create PR from diff" functionality
-- GitHub Copilot for suggesting PR creation
-- Manual copy-paste into PR creation interface
-
-### 4.3 Generate Issue Description
-
-If creating an issue, use this structure:
-
-```markdown
-## Code Simplification - [Date]
-
-This discussion presents code simplifications that improve clarity, consistency, and maintainability while preserving all functionality.
-
-### Files Simplified
-
-- `path/to/file1.go` - [Brief description of improvements]
-- `path/to/file2.js` - [Brief description of improvements]
-
-### Improvements Made
-
-1. **Reduced Complexity**
- - Simplified nested conditionals in `file1.go`
- - Extracted helper function for repeated logic
-
-2. **Enhanced Clarity**
- - Renamed variables for better readability
- - Removed redundant comments
- - Applied consistent naming conventions
-
-3. **Applied Project Standards**
- - Used `function` keyword instead of arrow functions
- - Added explicit type annotations
- - Followed established patterns
-
-### Changes Based On
-
-Recent changes from:
-- #[PR_NUMBER] - [PR title]
-- Commit [SHORT_SHA] - [Commit message]
-
-### Testing
-
-- ✅ All tests pass
-- ✅ Linting passes
-- ✅ Build succeeds
-- ✅ No functional changes - behavior is identical
-
-### Git Diff
-
-Below is the complete diff that can be used to create a pull request. You can copy this diff and:
-- Use it with GitHub Copilot to create a PR
-- Apply it directly with `git apply`
-- Create a PR manually by copying the changes
-
-```diff
-[PASTE THE COMPLETE GIT DIFF HERE]
-```
-
-To apply this diff:
-
-```bash
-# Save the diff to a file
-cat > /tmp/code-simplification.diff << 'EOF'
-[PASTE DIFF CONTENT]
-EOF
-
-# Apply the diff
-git apply /tmp/code-simplification.diff
-
-# Or create a PR from the current branch
-gh pr create --title "[code-simplifier] Code Simplification" --body "See discussion #[NUMBER]"
-```
-
-### Review Focus
-
-Please verify:
-- Functionality is preserved
-- Simplifications improve code quality
-- Changes align with project conventions
-- No unintended side effects
-
----
-
-*Automated by Code Simplifier Agent - analyzing code from the last 24 hours*
-```
-
-### 4.4 Use Safe Outputs
-
-Create the issue using the safe-outputs configuration:
-
-- Title will be prefixed with `[code-simplifier]`
-- Labeled with `refactoring`, `code-quality`, `automation`
-- Contains complete git diff for easy PR creation
-
-## Important Guidelines
-
-### Scope Control
-- **Focus on recent changes**: Only refine code modified in the last 24 hours
-- **Don't over-refactor**: Avoid touching unrelated code
-- **Preserve interfaces**: Don't change public APIs or exported functions
-- **Incremental improvements**: Make targeted, surgical changes
-
-### Quality Standards
-- **Test first**: Always run tests after simplifications
-- **Preserve behavior**: Functionality must remain identical
-- **Follow conventions**: Apply project-specific patterns consistently
-- **Clear over clever**: Prioritize readability and maintainability
-
-### Exit Conditions
-Exit gracefully without creating an issue if:
-- No code was changed in the last 24 hours
-- No simplifications are beneficial
-- Tests fail after changes
-- Build fails after changes
-- Changes are too risky or complex
-
-### Success Metrics
-A successful simplification:
-- ✅ Improves code clarity without changing behavior
-- ✅ Passes all tests and linting
-- ✅ Applies project-specific conventions
-- ✅ Makes code easier to understand and maintain
-- ✅ Focuses on recently modified code
-- ✅ Provides clear documentation of changes
-
-## Output Requirements
-
-Your output MUST either:
-
-1. **If no changes in last 24 hours**:
- ```
- ✅ No code changes detected in the last 24 hours.
- Code simplifier has nothing to process today.
- ```
-
-2. **If no simplifications beneficial**:
- ```
- ✅ Code analyzed from last 24 hours.
- No simplifications needed - code already meets quality standards.
- ```
-
-3. **If simplifications made**: Create an issue with the changes using safe-outputs, including:
- - Clear description of improvements
- - Complete git diff in proper format
- - Instructions for applying the diff or creating a PR
-
-Begin your code simplification analysis now. Find recently modified code, assess simplification opportunities, apply improvements while preserving functionality, validate changes, and create an issue with a git diff if beneficial.
-||||||| base (original)
---
name: Code Simplifier
description: Analyzes recently modified code and creates pull requests with simplifications that improve clarity, consistency, and maintainability while preserving functionality
@@ -443,389 +12,6 @@ permissions:
tracker-id: code-simplifier
-imports:
- - shared/reporting.md
-
-safe-outputs:
- create-pull-request:
- title-prefix: "[code-simplifier] "
- labels: [refactoring, code-quality, automation]
- reviewers: [copilot]
- expires: 7d
-
-tools:
- github:
- toolsets: [default]
-
-timeout-minutes: 30
-strict: true
-source: github/gh-aw/.github/workflows/code-simplifier.md@76d37d925abd44fee97379206f105b74b91a285b
----
-
-
-
-
-# Code Simplifier Agent
-
-You are an expert code simplification specialist focused on enhancing code clarity, consistency, and maintainability while preserving exact functionality. Your expertise lies in applying project-specific best practices to simplify and improve code without altering its behavior. You prioritize readable, explicit code over overly compact solutions. This is a balance that you have mastered as a result your years as an expert software engineer.
-
-## Your Mission
-
-Analyze recently modified code from the last 24 hours and apply refinements that improve code quality while preserving all functionality. Create a pull request with the simplified code if improvements are found.
-
-## Current Context
-
-- **Repository**: ${{ github.repository }}
-- **Analysis Date**: $(date +%Y-%m-%d)
-- **Workspace**: ${{ github.workspace }}
-
-## Phase 1: Identify Recently Modified Code
-
-### 1.1 Find Recent Changes
-
-Search for merged pull requests and commits from the last 24 hours:
-
-```bash
-# Get yesterday's date in ISO format
-YESTERDAY=$(date -d '1 day ago' '+%Y-%m-%d' 2>/dev/null || date -v-1d '+%Y-%m-%d')
-
-# List recent commits
-git log --since="24 hours ago" --pretty=format:"%H %s" --no-merges
-```
-
-Use GitHub tools to:
-- Search for pull requests merged in the last 24 hours: `repo:${{ github.repository }} is:pr is:merged merged:>=${YESTERDAY}`
-- Get details of merged PRs to understand what files were changed
-- List commits from the last 24 hours to identify modified files
-
-### 1.2 Extract Changed Files
-
-For each merged PR or recent commit:
-- Use `pull_request_read` with `method: get_files` to list changed files
-- Use `get_commit` to see file changes in recent commits
-- Focus on source code files (`.go`, `.js`, `.ts`, `.tsx`, `.cjs`, `.py`, etc.)
-- Exclude test files, lock files, and generated files
-
-### 1.3 Determine Scope
-
-If **no files were changed in the last 24 hours**, exit gracefully without creating a PR:
-
-```
-✅ No code changes detected in the last 24 hours.
-Code simplifier has nothing to process today.
-```
-
-If **files were changed**, proceed to Phase 2.
-
-## Phase 2: Analyze and Simplify Code
-
-### 2.1 Review Project Standards
-
-Before simplifying, review the project's coding standards from relevant documentation:
-
-- For Go projects: Check `AGENTS.md`, `DEVGUIDE.md`, or similar files
-- For JavaScript/TypeScript: Look for `CLAUDE.md`, style guides, or coding conventions
-- For Python: Check for style guides, PEP 8 adherence, or project-specific conventions
-
-**Key Standards to Apply:**
-
-For **JavaScript/TypeScript** projects:
-- Use ES modules with proper import sorting and extensions
-- Prefer `function` keyword over arrow functions for top-level functions
-- Use explicit return type annotations for top-level functions
-- Follow proper React component patterns with explicit Props types
-- Use proper error handling patterns (avoid try/catch when possible)
-- Maintain consistent naming conventions
-
-For **Go** projects:
-- Use `any` instead of `interface{}`
-- Follow console formatting for CLI output
-- Use semantic type aliases for domain concepts
-- Prefer small, focused files (200-500 lines ideal)
-- Use table-driven tests with descriptive names
-
-For **Python** projects:
-- Follow PEP 8 style guide
-- Use type hints for function signatures
-- Prefer explicit over implicit code
-- Use list/dict comprehensions where they improve clarity (not complexity)
-
-### 2.2 Simplification Principles
-
-Apply these refinements to the recently modified code:
-
-#### 1. Preserve Functionality
-- **NEVER** change what the code does - only how it does it
-- All original features, outputs, and behaviors must remain intact
-- Run tests before and after to ensure no behavioral changes
-
-#### 2. Enhance Clarity
-- Reduce unnecessary complexity and nesting
-- Eliminate redundant code and abstractions
-- Improve readability through clear variable and function names
-- Consolidate related logic
-- Remove unnecessary comments that describe obvious code
-- **IMPORTANT**: Avoid nested ternary operators - prefer switch statements or if/else chains
-- Choose clarity over brevity - explicit code is often better than compact code
-
-#### 3. Apply Project Standards
-- Use project-specific conventions and patterns
-- Follow established naming conventions
-- Apply consistent formatting
-- Use appropriate language features (modern syntax where beneficial)
-
-#### 4. Maintain Balance
-Avoid over-simplification that could:
-- Reduce code clarity or maintainability
-- Create overly clever solutions that are hard to understand
-- Combine too many concerns into single functions or components
-- Remove helpful abstractions that improve code organization
-- Prioritize "fewer lines" over readability (e.g., nested ternaries, dense one-liners)
-- Make the code harder to debug or extend
-
-### 2.3 Perform Code Analysis
-
-For each changed file:
-
-1. **Read the file contents** using the edit or view tool
-2. **Identify refactoring opportunities**:
- - Long functions that could be split
- - Duplicate code patterns
- - Complex conditionals that could be simplified
- - Unclear variable names
- - Missing or excessive comments
- - Non-standard patterns
-3. **Design the simplification**:
- - What specific changes will improve clarity?
- - How can complexity be reduced?
- - What patterns should be applied?
- - Will this maintain all functionality?
-
-### 2.4 Apply Simplifications
-
-Use the **edit** tool to modify files:
-
-```bash
-# For each file with improvements:
-# 1. Read the current content
-# 2. Apply targeted edits to simplify code
-# 3. Ensure all functionality is preserved
-```
-
-**Guidelines for edits:**
-- Make surgical, targeted changes
-- One logical improvement per edit (but batch multiple edits in a single response)
-- Preserve all original behavior
-- Keep changes focused on recently modified code
-- Don't refactor unrelated code unless it improves understanding of the changes
-
-## Phase 3: Validate Changes
-
-### 3.1 Run Tests
-
-After making simplifications, run the project's test suite to ensure no functionality was broken:
-
-```bash
-# For Go projects
-make test-unit
-
-# For JavaScript/TypeScript projects
-npm test
-
-# For Python projects
-pytest
-```
-
-If tests fail:
-- Review the failures carefully
-- Revert changes that broke functionality
-- Adjust simplifications to preserve behavior
-- Re-run tests until they pass
-
-### 3.2 Run Linters
-
-Ensure code style is consistent:
-
-```bash
-# For Go projects
-make lint
-
-# For JavaScript/TypeScript projects
-npm run lint
-
-# For Python projects
-flake8 . || pylint .
-```
-
-Fix any linting issues introduced by the simplifications.
-
-### 3.3 Check Build
-
-Verify the project still builds successfully:
-
-```bash
-# For Go projects
-make build
-
-# For JavaScript/TypeScript projects
-npm run build
-
-# For Python projects
-# (typically no build step, but check imports)
-python -m py_compile changed_files.py
-```
-
-## Phase 4: Create Pull Request
-
-### 4.1 Determine If PR Is Needed
-
-Only create a PR if:
-- ✅ You made actual code simplifications
-- ✅ All tests pass
-- ✅ Linting is clean
-- ✅ Build succeeds
-- ✅ Changes improve code quality without breaking functionality
-
-If no improvements were made or changes broke tests, exit gracefully:
-
-```
-✅ Code analyzed from last 24 hours.
-No simplifications needed - code already meets quality standards.
-```
-
-### 4.2 Generate PR Description
-
-If creating a PR, use this structure:
-
-```markdown
-## Code Simplification - [Date]
-
-This PR simplifies recently modified code to improve clarity, consistency, and maintainability while preserving all functionality.
-
-### Files Simplified
-
-- `path/to/file1.go` - [Brief description of improvements]
-- `path/to/file2.js` - [Brief description of improvements]
-
-### Improvements Made
-
-1. **Reduced Complexity**
- - Simplified nested conditionals in `file1.go`
- - Extracted helper function for repeated logic
-
-2. **Enhanced Clarity**
- - Renamed variables for better readability
- - Removed redundant comments
- - Applied consistent naming conventions
-
-3. **Applied Project Standards**
- - Used `function` keyword instead of arrow functions
- - Added explicit type annotations
- - Followed established patterns
-
-### Changes Based On
-
-Recent changes from:
-- #[PR_NUMBER] - [PR title]
-- Commit [SHORT_SHA] - [Commit message]
-
-### Testing
-
-- ✅ All tests pass (`make test-unit`)
-- ✅ Linting passes (`make lint`)
-- ✅ Build succeeds (`make build`)
-- ✅ No functional changes - behavior is identical
-
-### Review Focus
-
-Please verify:
-- Functionality is preserved
-- Simplifications improve code quality
-- Changes align with project conventions
-- No unintended side effects
-
----
-
-*Automated by Code Simplifier Agent - analyzing code from the last 24 hours*
-```
-
-### 4.3 Use Safe Outputs
-
-Create the pull request using the safe-outputs configuration:
-
-- Title will be prefixed with `[code-simplifier]`
-- Labeled with `refactoring`, `code-quality`, `automation`
-- Assigned to `copilot` for review
-- Set as ready for review (not draft)
-
-## Important Guidelines
-
-### Scope Control
-- **Focus on recent changes**: Only refine code modified in the last 24 hours
-- **Don't over-refactor**: Avoid touching unrelated code
-- **Preserve interfaces**: Don't change public APIs or exported functions
-- **Incremental improvements**: Make targeted, surgical changes
-
-### Quality Standards
-- **Test first**: Always run tests after simplifications
-- **Preserve behavior**: Functionality must remain identical
-- **Follow conventions**: Apply project-specific patterns consistently
-- **Clear over clever**: Prioritize readability and maintainability
-
-### Exit Conditions
-Exit gracefully without creating a PR if:
-- No code was changed in the last 24 hours
-- No simplifications are beneficial
-- Tests fail after changes
-- Build fails after changes
-- Changes are too risky or complex
-
-### Success Metrics
-A successful simplification:
-- ✅ Improves code clarity without changing behavior
-- ✅ Passes all tests and linting
-- ✅ Applies project-specific conventions
-- ✅ Makes code easier to understand and maintain
-- ✅ Focuses on recently modified code
-- ✅ Provides clear documentation of changes
-
-## Output Requirements
-
-Your output MUST either:
-
-1. **If no changes in last 24 hours**:
- ```
- ✅ No code changes detected in the last 24 hours.
- Code simplifier has nothing to process today.
- ```
-
-2. **If no simplifications beneficial**:
- ```
- ✅ Code analyzed from last 24 hours.
- No simplifications needed - code already meets quality standards.
- ```
-
-3. **If simplifications made**: Create a PR with the changes using safe-outputs
-
-Begin your code simplification analysis now. Find recently modified code, assess simplification opportunities, apply improvements while preserving functionality, validate changes, and create a PR if beneficial.
-=======
----
-name: Code Simplifier
-description: Analyzes recently modified code and creates pull requests with simplifications that improve clarity, consistency, and maintainability while preserving functionality
-on:
- schedule: daily
- skip-if-match: 'is:pr is:open in:title "[code-simplifier]"'
-
-permissions:
- contents: read
- issues: read
- pull-requests: read
-
-tracker-id: code-simplifier
-
-imports:
- - shared/activation-app.md
- - shared/reporting.md
safe-outputs:
create-pull-request:
@@ -833,6 +19,8 @@ safe-outputs:
labels: [refactoring, code-quality, automation]
reviewers: [copilot]
expires: 1d
+ noop:
+ report-as-issue: false
network:
allowed:
@@ -1216,5 +404,4 @@ Begin your code simplification analysis now. Find recently modified code, assess
```json
{"noop": {"message": "No action needed: [brief explanation of what was analyzed and why]"}}
-```
->>>>>>> new (upstream)
+```
\ No newline at end of file
diff --git a/.github/workflows/compare-stats-anomaly-reporter.lock.yml b/.github/workflows/compare-stats-anomaly-reporter.lock.yml
new file mode 100644
index 000000000..54c1f7b18
--- /dev/null
+++ b/.github/workflows/compare-stats-anomaly-reporter.lock.yml
@@ -0,0 +1,1373 @@
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"ba7cc0883e671716a71c8e76bcbdff5a80ec7bea87f96a19f5419c019ab96fdd","compiler_version":"v0.76.1","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-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"v0.76.1","version":"v0.76.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.55"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.55"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.19"},{"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"}]}
+# ___ _ _
+# / _ \ | | (_)
+# | |_| | __ _ ___ _ __ | |_ _ ___
+# | _ |/ _` |/ _ \ '_ \| __| |/ __|
+# | | | | (_| | __/ | | | |_| | (__
+# \_| |_/\__, |\___|_| |_|\__|_|\___|
+# __/ |
+# _ _ |___/
+# | | | | / _| |
+# | | | | ___ _ __ _ __| |_| | _____ ____
+# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
+# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
+# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
+#
+# This file was automatically generated by gh-aw (v0.76.1). DO NOT EDIT.
+#
+# To update this file, edit the corresponding .md file and run:
+# gh aw compile
+# Not all edits will cause changes to this file.
+#
+# For more information: https://github.github.com/gh-aw/introduction/overview/
+#
+# Analyze benchmark statistics from the latest 30 hours and publish bug/crash/anomaly summary as a GitHub Discussion
+#
+# Secrets used:
+# - COPILOT_GITHUB_TOKEN
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
+# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@v0.77.0
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.55
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.55
+# - ghcr.io/github/gh-aw-mcpg:v0.3.19
+# - ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4
+# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
+
+name: "Compare Stats Bug/Crash/Anomaly Reporter"
+on:
+ schedule:
+ - cron: "0 */12 * * *"
+ workflow_dispatch:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+
+permissions: {}
+
+concurrency:
+ group: "gh-aw-${{ github.workflow }}"
+
+run-name: "Compare Stats Bug/Crash/Anomaly Reporter"
+
+jobs:
+ activation:
+ runs-on: ubuntu-slim
+ permissions:
+ actions: read
+ contents: read
+ outputs:
+ comment_id: ""
+ comment_repo: ""
+ engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
+ model: ${{ steps.generate_aw_info.outputs.model }}
+ secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/compare-stats-anomaly-reporter.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AGENT_VERSION: "1.0.52"
+ GH_AW_INFO_CLI_VERSION: "v0.76.1"
+ GH_AW_INFO_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "false"
+ GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","mtzguido.tplinkdns.com"]'
+ GH_AW_INFO_FIREWALL_ENABLED: "true"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_AWMG_VERSION: ""
+ GH_AW_INFO_FIREWALL_TYPE: "squid"
+ GH_AW_COMPILED_STRICT: "false"
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/generate_aw_info.cjs');
+ await main(core, context);
+ - name: Validate COPILOT_GITHUB_TOKEN secret
+ id: validate-secret
+ run: bash "${RUNNER_TEMP}/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: Checkout .github and .agents folders
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ sparse-checkout: |
+ .github
+ .agents
+ .antigravity
+ .claude
+ .codex
+ .crush
+ .gemini
+ .opencode
+ .pi
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Save agent config folders for base branch restoration
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_WORKFLOW_FILE: "compare-stats-anomaly-reporter.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
+ 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/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_COMPILED_VERSION: "v0.76.1"
+ 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/check_version_updates.cjs');
+ await main();
+ - name: Create prompt with built-in context
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ # poutine:ignore untrusted_checkout_exec
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
+ {
+ cat << 'GH_AW_PROMPT_fa9bfce3ee233090_EOF'
+
+ GH_AW_PROMPT_fa9bfce3ee233090_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_fa9bfce3ee233090_EOF'
+
+ Tools: create_discussion, missing_tool, missing_data, noop
+
+ GH_AW_PROMPT_fa9bfce3ee233090_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
+ cat << 'GH_AW_PROMPT_fa9bfce3ee233090_EOF'
+
+ The following GitHub context information is available for this workflow:
+ {{#if github.actor}}
+ - **actor**: __GH_AW_GITHUB_ACTOR__
+ {{/if}}
+ {{#if github.repository}}
+ - **repository**: __GH_AW_GITHUB_REPOSITORY__
+ {{/if}}
+ {{#if github.workspace}}
+ - **workspace**: __GH_AW_GITHUB_WORKSPACE__
+ {{/if}}
+ {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
+ - **issue-number**: #__GH_AW_EXPR_802A9F6A__
+ {{/if}}
+ {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
+ - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
+ {{/if}}
+ {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
+ - **pull-request-number**: #__GH_AW_EXPR_463A214A__
+ {{/if}}
+ {{#if github.event.comment.id || github.aw.context.comment_id}}
+ - **comment-id**: __GH_AW_EXPR_FF1D34CE__
+ {{/if}}
+ {{#if github.run_id}}
+ - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
+ {{/if}}
+
+
+ GH_AW_PROMPT_fa9bfce3ee233090_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_fa9bfce3ee233090_EOF'
+
+ {{#runtime-import .github/workflows/compare-stats-anomaly-reporter.md}}
+ GH_AW_PROMPT_fa9bfce3ee233090_EOF
+ } > "$GH_AW_PROMPT"
+ - name: Interpolate variables and render templates
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
+ 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/interpolate_prompt.cjs');
+ await main();
+ - name: Substitute placeholders
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+
+ const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
+
+ // Call the substitution function
+ return await substitutePlaceholders({
+ file: process.env.GH_AW_PROMPT,
+ substitutions: {
+ GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
+ GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
+ GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
+ GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
+ GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
+ 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_WORKFLOW: process.env.GH_AW_GITHUB_WORKFLOW,
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ }
+ });
+ - name: Validate prompt placeholders
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
+ - name: Print prompt
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
+ - name: Upload activation artifact
+ if: success()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: activation
+ include-hidden-files: true
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/aw-prompts/prompt-template.txt
+ /tmp/gh-aw/aw-prompts/prompt-import-tree.json
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/base
+ /tmp/gh-aw/.github/agents
+ /tmp/gh-aw/.github/skills
+ if-no-files-found: ignore
+ retention-days: 1
+
+ agent:
+ needs: activation
+ runs-on: ubuntu-latest
+ permissions: read-all
+ concurrency:
+ group: "gh-aw-copilot-${{ github.workflow }}"
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ""
+ GH_AW_ASSETS_BRANCH: ""
+ GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ GH_AW_WORKFLOW_ID_SANITIZED: comparestatsanomalyreporter
+ outputs:
+ agentic_engine_timeout: ${{ steps.detect-agent-errors.outputs.agentic_engine_timeout || 'false' }}
+ checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
+ effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
+ has_patch: ${{ steps.collect_output.outputs.has_patch }}
+ inference_access_error: ${{ steps.detect-agent-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-agent-errors.outputs.mcp_policy_error || 'false' }}
+ model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-agent-errors.outputs.model_not_supported_error || 'false' }}
+ output: ${{ steps.collect_output.outputs.output }}
+ output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/compare-stats-anomaly-reporter.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - 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: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # 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 || github.event.issue.pull_request
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
+ await main();
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: activation
+ path: /tmp/gh-aw
+ - name: Restore agent config folders from base branch
+ if: steps.checkout-pr.outcome == 'success'
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .antigravity .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md ANTIGRAVITY.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
+ - name: Restore inline sub-agents from activation artifact
+ env:
+ GH_AW_SUB_AGENT_DIR: ".github/agents"
+ GH_AW_SUB_AGENT_EXT: ".agent.md"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
+ - name: Restore inline skills from activation artifact
+ env:
+ GH_AW_SKILL_DIR: ".github/skills"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_skills.sh"
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55 ghcr.io/github/gh-aw-firewall/squid:0.25.55 ghcr.io/github/gh-aw-mcpg:v0.3.19 ghcr.io/github/github-mcp-server:v1.0.4@sha256:e3816a476a977cfb836e7d221510011436c654d11861db66ecfd826601aba6a4 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
+ - name: Generate Safe Outputs Config
+ run: |
+ 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_4d0c9ad86b64b83a_EOF'
+ {"create_discussion":{"category":"agentic workflows","close_older_discussions":true,"expires":168,"fallback_to_issue":true,"max":1,"title_prefix":"[Compare Stats] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_4d0c9ad86b64b83a_EOF
+ - name: Generate Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Compare Stats] \". Discussions will be created in category \"agentic workflows\"."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "create_discussion": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000,
+ "minLength": 64
+ },
+ "category": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "title": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 1024
+ }
+ }
+ }
+ }
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/generate_safe_outputs_tools.cjs');
+ await main();
+ - name: Generate Safe Outputs MCP Server Config
+ id: safe-outputs-config
+ run: |
+ # Generate a secure random API key (360 bits of entropy, 40+ chars)
+ # Mask immediately to prevent timing vulnerabilities
+ API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${API_KEY}"
+
+ PORT=3001
+
+ # Set outputs for next steps
+ {
+ echo "safe_outputs_api_key=${API_KEY}"
+ echo "safe_outputs_port=${PORT}"
+ } >> "$GITHUB_OUTPUT"
+
+ echo "Safe Outputs MCP server will run on port ${PORT}"
+
+ - name: Start Safe Outputs MCP HTTP Server
+ id: safe-outputs-start
+ env:
+ DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ run: |
+ # Environment variables are set above to prevent template injection
+ export DEBUG
+ export GH_AW_SAFE_OUTPUTS
+ export GH_AW_SAFE_OUTPUTS_PORT
+ export GH_AW_SAFE_OUTPUTS_API_KEY
+ export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
+ export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
+ export GH_AW_MCP_LOG_DIR
+
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
+
+ - name: Start MCP Gateway
+ id: start-mcp-gateway
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
+ 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 "${RUNNER_TEMP}/gh-aw/mcp-config"
+
+ # Export gateway environment variables for MCP config and gateway script
+ export MCP_GATEWAY_PORT="8080"
+ export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ export MCP_GATEWAY_HOST_DOMAIN="localhost"
+ MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${MCP_GATEWAY_API_KEY}"
+ export MCP_GATEWAY_API_KEY
+ export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
+ mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
+ export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
+ export DEBUG="*"
+
+ export GH_AW_ENGINE="copilot"
+ MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
+ MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
+ case "${DOCKER_HOST:-}" in
+ unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
+ /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
+ * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
+ esac
+ DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0')
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/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 DOCKER_HOST=unix:///var/run/docker.sock -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.3.19'
+
+ 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_1d647de00ba486c1_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ {
+ "mcpServers": {
+ "github": {
+ "type": "stdio",
+ "container": "ghcr.io/github/github-mcp-server:v1.0.4",
+ "env": {
+ "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
+ "GITHUB_READ_ONLY": "1",
+ "GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
+ }
+ },
+ "safeoutputs": {
+ "type": "http",
+ "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
+ "headers": {
+ "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
+ }
+ },
+ "gateway": {
+ "port": $MCP_GATEWAY_PORT,
+ "domain": "${MCP_GATEWAY_DOMAIN}",
+ "apiKey": "${MCP_GATEWAY_API_KEY}",
+ "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
+ }
+ }
+ GH_AW_MCP_CONFIG_1d647de00ba486c1_EOF
+ - name: Mount MCP servers as CLIs
+ id: mount-mcp-clis
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
+ await main();
+ - name: Clean credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Audit pre-agent workspace
+ id: pre_agent_audit
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
+ - name: Execute GitHub Copilot CLI
+ id: agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 45
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["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","mtzguido.tplinkdns.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","www.googleapis.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5.3","gemini-pro","any"],"antigravity":["copilot/antigravity*","google/antigravity*","gemini/antigravity*"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"claude":["agent"],"codex":["agent"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"computer-use":["copilot/*computer-use*","google/*computer-use*","gemini/*computer-use*","openai/*computer-use*"],"copilot":["agent"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent"],"gemini-3-flash":["copilot/gemini-3*flash*","google/gemini-3*flash*","gemini/gemini-3*flash*"],"gemini-3-pro":["copilot/gemini-3*pro*","google/gemini-3*pro*","gemini/gemini-3*pro*"],"gemini-3.1-flash":["copilot/gemini-3.1*flash*","google/gemini-3.1*flash*","gemini/gemini-3.1*flash*"],"gemini-3.1-pro":["copilot/gemini-3.1*pro*","google/gemini-3.1*pro*","gemini/gemini-3.1*pro*"],"gemini-3.5-flash":["copilot/gemini-3.5*flash*","google/gemini-3.5*flash*","gemini/gemini-3.5*flash*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"gpt-5.2":["copilot/gpt-5.2*","openai/gpt-5.2*"],"gpt-5.3":["copilot/gpt-5.3*","openai/gpt-5.3*"],"gpt-5.4":["copilot/gpt-5.4*","openai/gpt-5.4*"],"gpt-5.5":["copilot/gpt-5.5*","openai/gpt-5.5*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite"],"opus":["copilot/*opus*","anthropic/*opus*"],"opusplan":["opus?effort=high"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"robotics":["copilot/*robotics*","google/*robotics*","gemini/*robotics*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4-5-*","anthropic/*sonnet-4-5-*","copilot/*sonnet-4-6*","anthropic/*sonnet-4-6*"],"summarization":["haiku","gpt-5-mini","gemini-flash-lite","mini"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
+ GH_AW_PHASE: agent
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_VERSION: v0.76.1
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Detect agent errors
+ if: always()
+ id: detect-agent-errors
+ continue-on-error: true
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_agent_errors.cjs"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # 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: Copy Copilot session state files to logs
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
+ - 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 "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
+ - name: Redact secrets in logs
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/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: Append agent step summary
+ if: always()
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
+ if: always()
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
+ - name: Ingest agent output
+ id: collect_output
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_ALLOWED_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,mtzguido.tplinkdns.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,www.googleapis.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ 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/collect_ndjson_output.cjs');
+ await main();
+ - name: Parse agent logs for step summary
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
+ 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/parse_copilot_log.cjs');
+ await main();
+ - name: Parse MCP Gateway logs for step summary
+ if: always()
+ id: parse-mcp-gateway
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/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/audit dirs so they can be uploaded as artifacts
+ # AWF runs with sudo, creating files owned by root
+ sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
+ # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
+ if command -v awf &> /dev/null; then
+ awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
+ else
+ echo 'AWF binary not installed, skipping firewall log summary'
+ fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/parse_token_usage.cjs');
+ await main();
+ - name: Print AWF reflect summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/awf_reflect_summary.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
+ - name: Upload agent artifacts
+ if: always()
+ continue-on-error: true
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: agent
+ path: |
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
+ /tmp/gh-aw/mcp-logs/
+ /tmp/gh-aw/agent_usage.json
+ /tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/pre-agent-audit.txt
+ /tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/aw-*.patch
+ /tmp/gh-aw/aw-*.bundle
+ /tmp/gh-aw/awf-config.json
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ /tmp/gh-aw/sandbox/firewall/awf-reflect.json
+ if-no-files-found: ignore
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - detection
+ - safe_outputs
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ concurrency:
+ group: "gh-aw-conclusion-compare-stats-anomaly-reporter"
+ cancel-in-progress: false
+ queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/compare-stats-anomaly-reporter.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ 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_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/compare-stats-anomaly-reporter.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "false"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_noop_message.cjs');
+ await main();
+ - name: Log detection run
+ id: detection_runs
+ 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_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/compare-stats-anomaly-reporter.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_detection_runs.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ 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_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_MISSING_TOOL_TITLE_PREFIX: "[missing tool]"
+ GH_AW_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/compare-stats-anomaly-reporter.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ 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_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/compare-stats-anomaly-reporter.md"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/report_incomplete_handler.cjs');
+ await main();
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ 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_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/compare-stats-anomaly-reporter.md"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "compare-stats-anomaly-reporter"
+ GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
+ GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
+ GH_AW_CREATE_DISCUSSION_ERRORS: ${{ needs.safe_outputs.outputs.create_discussion_errors }}
+ GH_AW_CREATE_DISCUSSION_ERROR_COUNT: ${{ needs.safe_outputs.outputs.create_discussion_error_count }}
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
+ GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
+ GH_AW_TIMEOUT_MINUTES: "45"
+ GH_AW_MAX_EFFECTIVE_TOKENS: "25000000"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_agent_failure.cjs');
+ await main();
+
+ detection:
+ needs:
+ - activation
+ - agent
+ if: >
+ always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/compare-stats-anomaly-reporter.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Checkout repository for patch context
+ if: needs.agent.outputs.has_patch == 'true'
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ # --- Threat Detection ---
+ - name: Clean stale firewall files from agent artifact
+ run: |
+ rm -rf /tmp/gh-aw/sandbox/firewall/logs
+ rm -rf /tmp/gh-aw/sandbox/firewall/audit
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.55 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.55 ghcr.io/github/gh-aw-firewall/squid:0.25.55
+ - name: Check if detection needed
+ id: detection_guard
+ if: always()
+ env:
+ OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ run: |
+ if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
+ echo "run_detection=true" >> "$GITHUB_OUTPUT"
+ echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
+ else
+ echo "run_detection=false" >> "$GITHUB_OUTPUT"
+ echo "Detection skipped: no agent outputs or patches to analyze"
+ fi
+ - name: Clear MCP Config for detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
+ rm -f /home/runner/.copilot/mcp-config.json
+ rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
+ - name: Prepare threat detection files
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
+ cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
+ cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
+ for f in /tmp/gh-aw/aw-*.patch; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ for f in /tmp/gh-aw/aw-*.bundle; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ echo "Prepared threat detection files:"
+ ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ - name: Setup threat detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ WORKFLOW_DESCRIPTION: "Analyze benchmark statistics from the latest 30 hours and publish bug/crash/anomaly summary as a GitHub Discussion"
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ 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/setup_threat_detection.cjs');
+ await main();
+ - name: Ensure threat-detection directory and log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection
+ touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Setup Node.js
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: '24'
+ package-manager-cache: false
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.52
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.55
+ - name: Execute GitHub Copilot CLI
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ continue-on-error: true
+ id: detection_agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.55/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.55"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_PHASE: detection
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_VERSION: v0.76.1
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Upload threat detection log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: detection
+ path: /tmp/gh-aw/threat-detection/detection.log
+ if-no-files-found: ignore
+ - name: Parse and conclude threat detection
+ id: detection_conclusion
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
+ DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }}
+ GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
+ with:
+ script: |
+ try {
+ 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/parse_threat_detection_results.cjs');
+ await main();
+ } catch (loadErr) {
+ const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
+ const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure';
+ const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
+ core.error(msg);
+ core.setOutput('reason', 'parse_error');
+ if (continueOnError && !detectionExecutionFailed) {
+ core.warning('\u26A0\uFE0F ' + msg);
+ core.setOutput('conclusion', 'warning');
+ core.setOutput('success', 'false');
+ } else {
+ core.setOutput('conclusion', 'failure');
+ core.setOutput('success', 'false');
+ core.setFailed(msg);
+ }
+ }
+
+ safe_outputs:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/compare-stats-anomaly-reporter"
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ GH_AW_ENGINE_VERSION: "1.0.52"
+ GH_AW_WORKFLOW_ID: "compare-stats-anomaly-reporter"
+ GH_AW_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_WORKFLOW_SOURCE_URL: "${{ github.server_url }}/${{ github.repository }}/blob/${{ github.ref_name }}/.github/workflows/compare-stats-anomaly-reporter.md"
+ outputs:
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Compare Stats Bug/Crash/Anomaly Reporter"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/compare-stats-anomaly-reporter.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.52"
+ GH_AW_INFO_AWF_VERSION: "v0.25.55"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - 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"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ 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_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
+ GH_AW_ALLOWED_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,mtzguido.tplinkdns.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,www.googleapis.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"agentic workflows\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[Compare Stats] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/safe_output_handler_manager.cjs');
+ await main();
+ - name: Upload Safe Outputs Items
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: safe-outputs-items
+ path: |
+ /tmp/gh-aw/safe-output-items.jsonl
+ /tmp/gh-aw/temporary-id-map.json
+ if-no-files-found: ignore
+
diff --git a/.github/workflows/compare-stats-anomaly-reporter.md b/.github/workflows/compare-stats-anomaly-reporter.md
new file mode 100644
index 000000000..8f450991b
--- /dev/null
+++ b/.github/workflows/compare-stats-anomaly-reporter.md
@@ -0,0 +1,191 @@
+---
+description: Analyze benchmark statistics from the latest 30 hours and publish bug/crash/anomaly summary as a GitHub Discussion
+
+on:
+ schedule:
+ - cron: "0 */12 * * *"
+ workflow_dispatch:
+
+permissions: read-all
+
+strict: false
+timeout-minutes: 45
+
+network:
+ allowed:
+ - defaults
+ - mtzguido.tplinkdns.com
+
+tools:
+ bash: [":*"]
+ github:
+ toolsets: [default]
+
+safe-outputs:
+ create-discussion:
+ title-prefix: "[Compare Stats] "
+ category: "agentic workflows"
+ close-older-discussions: true
+ missing-tool:
+ create-issue: true
+ noop:
+ report-as-issue: false
+---
+
+# Compare Stats Bug/Crash/Anomaly Reporter
+
+Your name is ${{ github.workflow }}. You are a Z3 benchmarking analysis agent for `${{ github.repository }}`.
+
+Analyze the benchmark statistics page below, focusing on results from the last 30 hours, then create a GitHub Discussion with a concise but actionable summary of:
+
+- Bugs
+- Crashes
+- Anomalies
+
+Source URL:
+`http://mtzguido.tplinkdns.com:8081/z3/`
+
+Note: this endpoint is currently HTTP-only. Treat fetched data as non-sensitive benchmark telemetry and do not include secrets in requests or reports.
+Note: the workflow runs every 12 hours but analyzes 30 hours intentionally to provide overlap and avoid missing transient failures between runs.
+Overlapping windows are expected; `close-older-discussions: true` keeps only the latest report thread active.
+
+## Requirements
+
+### 1) Fetch and save the source page
+
+Use bash to fetch the page into `/tmp/gh-aw/agent/benchmark_stats.html`.
+
+Try this first:
+```bash
+curl -fsSL --max-time 60 "http://mtzguido.tplinkdns.com:8081/z3/" -o /tmp/gh-aw/agent/benchmark_stats.html
+```
+
+If that fails, retry once with:
+```bash
+wget -q -T 60 -O /tmp/gh-aw/agent/benchmark_stats.html "http://mtzguido.tplinkdns.com:8081/z3/"
+```
+
+If both fail, still create a discussion that explains the fetch failure, includes stderr output, and marks the report as incomplete.
+After a successful fetch, perform basic integrity checks before parsing:
+- file is non-empty
+- content includes `= 4`, `unknown_count / total_rows <= 0.4`, and `(sat_count + unsat_count + timeout_count) / total_rows >= 0.6`.
+ - If set/suite/group columns are missing, fallback grouping order is: directory prefix of benchmark path/name, then benchmark name prefix before first separator (`/`, `:`, `::`), then a single global group.
+
+2. **Status divergence anomaly**:
+ - Same benchmark name appears multiple times with conflicting non-timeout statuses (for example `sat` vs `unsat`).
+ - Ignore timeout-only disagreements here; timeout behavior is covered under the repeated hard-failure anomaly section to reduce noise from transient runtime variance.
+
+3. **Repeated hard-failure anomaly**:
+ - Same benchmark appears repeatedly with crash/error-like status in the time window.
+
+### 5) Generate discussion report
+
+Create a GitHub Discussion using `create-discussion` safe output.
+
+Use this structure:
+
+```markdown
+### Compare Stats Analysis Report
+
+**Source**: [benchmark statistics](http://mtzguido.tplinkdns.com:8081/z3/)
+**Workflow Run**: [#${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
+**Analysis Time (UTC)**:
+**Window**: last 30 hours (or fallback mode)
+
+### Executive Summary
+
+- Rows analyzed: N
+- Rows in 30h window: M (or "timestamp unavailable")
+- Bugs/crashes: B
+- Anomalies: A
+
+### Bugs and Crashes
+
+| Benchmark Set | Benchmark | Status | Details | Timestamp |
+|---|---|---|---|---|
+| ... |
+
+### Anomalies
+
+#### Unknown-Outlier Cases
+| Benchmark Set | Benchmark | Status | Peer Status Distribution | Timestamp |
+|---|---|---|---|---|
+| ... |
+
+#### Status Divergences
+| Benchmark | Observed Statuses | Benchmark Set(s) | Timestamp(s) |
+|---|---|---|---|
+| ... |
+
+#### Repeated Hard Failures
+| Benchmark | Failure Count | Representative Status/Details | Benchmark Set(s) |
+|---|---|---|---|
+| ... |
+
+### Notes and Limitations
+- Mention parsing assumptions
+- Mention missing columns/timestamps if any
+
+
+Raw Extraction Summary
+
+- Table count
+- Candidate columns used
+- Top status distribution
+- Up to 30 representative raw rows (sanitized)
+
+
+```
+
+## Reporting Rules
+
+- Be factual and concise.
+- Do not claim certainty when column mapping is heuristic.
+- If no bugs/crashes/anomalies are found, still create the discussion and explicitly state "No issues detected in analyzed window."
+- Do not open PRs or modify repository files.
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index 08ae99656..5c965e1cb 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -89,13 +89,13 @@ jobs:
id: date
run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- - uses: actions/upload-artifact@v7
+ - uses: actions/upload-artifact@v7.0.1
with:
name: coverage-${{steps.date.outputs.date}}
path: ${{github.workspace}}/coverage.html
retention-days: 4
- - uses: actions/upload-artifact@v7
+ - uses: actions/upload-artifact@v7.0.1
with:
name: coverage-details-${{steps.date.outputs.date}}
path: ${{env.COV_DETAILS_PATH}}
diff --git a/.github/workflows/csa-analysis.md b/.github/workflows/csa-analysis.md
index a4e0fb236..dc5abe0fa 100644
--- a/.github/workflows/csa-analysis.md
+++ b/.github/workflows/csa-analysis.md
@@ -16,8 +16,6 @@ tools:
github:
toolsets: [default]
bash: [":*"]
- glob: {}
- view: {}
safe-outputs:
create-discussion:
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 2b4fa1769..a043a47b0 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -34,7 +34,7 @@ jobs:
python3 mk_go_doc.py --output-dir=api/html/go --go-api-path=../src/api/go
- name: Upload Go Documentation
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: go-docs
path: doc/api/html/go/
@@ -94,7 +94,7 @@ jobs:
ocamldoc -html -d api/html/ml -sort -hide Z3 -I $( ocamlfind query zarith ) -I ../build-x64/api/ml ../build-x64/api/ml/z3enums.mli ../build-x64/api/ml/z3.mli
- name: Setup emscripten
- uses: mymindstorm/setup-emsdk@v14
+ uses: mymindstorm/setup-emsdk@v16
with:
no-install: true
version: ${{env.EM_VERSION}}
diff --git a/.github/workflows/issue-backlog-processor.md b/.github/workflows/issue-backlog-processor.md
index c7831cd62..0feeeaf6b 100644
--- a/.github/workflows/issue-backlog-processor.md
+++ b/.github/workflows/issue-backlog-processor.md
@@ -32,6 +32,8 @@ timeout-minutes: 60
Your name is ${{ github.workflow }}. You are an expert AI agent tasked with processing the backlog of open issues in the Z3 theorem prover repository `${{ github.repository }}`. Your mission is to analyze open issues systematically and help maintainers manage the backlog effectively by surfacing actionable insights and providing helpful comments.
+> **CRITICAL**: You MUST call either `create-discussion` or `noop` before finishing, under all circumstances. Even if you only analyzed a small number of issues, always produce output. Never exit without calling one of these tools.
+
## Your Task
### 1. Initialize or Resume Progress (Cache Memory)
@@ -40,25 +42,28 @@ Check your cache memory for:
- List of issue numbers already processed and commented on in previous runs
- Issues previously flagged for closure, duplication, or merge
- Date of last run
+- The batch cursor: the last issue number processed (used for pagination across runs)
If cache data exists:
- Skip re-commenting on issues already commented in a recent run (within the last 4 days)
- Re-evaluate previously flagged issues to see if their status has changed
- Note any new issues that opened since the last run
+- Resume from where the previous run left off (use the stored batch cursor)
If this is the first run or memory is empty, initialize a fresh tracking structure.
-### 2. Fetch Open Issues
+### 2. Fetch Open Issues (Batched)
-Use the GitHub API to list all open issues in the repository:
-- Retrieve all open issues (paginate through all pages to get the full list)
+Use the GitHub API to list open issues in the repository. **Process at most 30 issues per run** to stay within context limits (this limit is based on the average size of Z3 issues including body text and inline code snippets; larger issues may require processing fewer):
+- Retrieve one page (30 issues) of open issues
- Exclude pull requests (filter where `pull_request` is not present)
- Sort by last updated date (most recently updated first)
+- If cache has a batch cursor from the last run, fetch the next page after that cursor; otherwise start from the most recently updated issues
- For each issue, collect:
- Issue number, title, body, labels, author
- Date created and last updated
- Number of comments
- - All comments (for issues with comments)
+ - **Do NOT fetch comments for every issue up front.** Only fetch comments for a specific issue when at least one of the following is true: the body mentions a version number (potential closure), the title contains words like "duplicate", "same as", or "related to" (potential duplicate), or the issue has labels such as "question", "help wanted", or "wontfix" (potential closure/status change). Fetch comments lazily, one issue at a time, only when one of these criteria is met.
- Any referenced pull requests, commits, or other issues
### 3. Analyze Each Issue
@@ -110,6 +115,8 @@ Add a comment to an issue if you have **genuinely useful and specific informatio
### 4. Create a Discussion with Findings
+**MANDATORY**: You MUST call `create-discussion` now, even if you only analyzed a few issues or found nothing actionable. If there is genuinely nothing to report, call `noop` instead. Do not skip this step.
+
Create a GitHub Discussion summarizing the analysis results.
**Title:** "[Issue Backlog] Backlog Analysis - [Date]"
@@ -224,9 +231,13 @@ After completing the analysis, update cache memory with:
- Issues flagged for closure, duplication, or merge
- Date and timestamp of this run
- Count of total issues analyzed
+- Batch cursor: the issue number of the last issue processed in this run, so the next run can continue from where this one left off
## Guidelines
+- **Always produce output**: You MUST call `create-discussion` or `noop` before finishing — never exit silently. If in doubt about whether there is enough to report, call `create-discussion` with a brief summary.
+- **Batch processing**: Only analyze up to 30 issues per run. Store a cursor in cache memory so subsequent runs pick up where you left off.
+- **Lazy comment fetching**: Do NOT bulk-fetch all comments for all issues. Only fetch comments for a specific issue when one of these criteria is met: the body mentions a version number, the title contains duplicate/related keywords, or the issue has status-relevant labels (e.g., "question", "help wanted", "wontfix").
- **Prioritize accuracy over coverage**: It is better to analyze 20 issues well than 200 issues poorly
- **Be conservative on closures**: Incorrectly closing a valid issue is harmful; when in doubt, keep it open
- **Respect the community**: Z3 is used by researchers, security engineers, and developers — treat all issues respectfully
diff --git a/.github/workflows/mark-prs-ready-for-review.yml b/.github/workflows/mark-prs-ready-for-review.yml
index 4e19bdafa..90b0d668c 100644
--- a/.github/workflows/mark-prs-ready-for-review.yml
+++ b/.github/workflows/mark-prs-ready-for-review.yml
@@ -16,7 +16,7 @@ jobs:
pull-requests: write
steps:
- name: Mark all draft pull requests ready for review
- uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
diff --git a/.github/workflows/memory-safety-report.md b/.github/workflows/memory-safety-report.md
index a2b245f3a..56ffe514f 100644
--- a/.github/workflows/memory-safety-report.md
+++ b/.github/workflows/memory-safety-report.md
@@ -30,8 +30,6 @@ tools:
github:
toolsets: [default, actions]
bash: [":*"]
- glob: {}
- view: {}
safe-outputs:
mentions: false
@@ -41,7 +39,7 @@ safe-outputs:
title-prefix: "[Memory Safety] "
category: "Agentic Workflows"
close-older-discussions: true
- expires: 7
+ expires: 7d
missing-tool:
create-issue: true
noop:
diff --git a/.github/workflows/memory-safety.yml b/.github/workflows/memory-safety.yml
index 7c1fd16f0..1c7fc7f0b 100644
--- a/.github/workflows/memory-safety.yml
+++ b/.github/workflows/memory-safety.yml
@@ -104,7 +104,7 @@ jobs:
- name: Upload ASan reports
if: always()
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: asan-reports
path: /tmp/asan-reports/
@@ -194,7 +194,7 @@ jobs:
- name: Upload UBSan reports
if: always()
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: ubsan-reports
path: /tmp/ubsan-reports/
diff --git a/.github/workflows/nightly-validation.yml b/.github/workflows/nightly-validation.yml
index 3f29e1024..5d560677b 100644
--- a/.github/workflows/nightly-validation.yml
+++ b/.github/workflows/nightly-validation.yml
@@ -665,6 +665,57 @@ jobs:
pip install $wheel.FullName
python -c "import z3; x = z3.Int('x'); s = z3.Solver(); s.add(x > 0); print('Result:', s.check()); print('Model:', s.model())"
+ validate-python-wheel-riscv64:
+ name: "Validate Python wheel for RISC-V 64"
+ runs-on: ubuntu-latest
+ if: ${{ github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' }}
+ timeout-minutes: 30
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6.0.2
+
+ - name: Setup Python
+ uses: actions/setup-python@v6
+ with:
+ python-version: '3.x'
+
+ - name: Download RISC-V 64 Python wheel from release
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ TAG="${{ github.event.inputs.release_tag }}"
+ if [ -z "$TAG" ]; then
+ TAG="Nightly"
+ fi
+ gh release download $TAG --pattern "*riscv64.whl" --dir wheels
+
+ - name: Verify wheel platform tag and contents
+ run: |
+ pip install wheel
+ WHEEL_FILE=$(ls wheels/*.whl | head -n 1)
+ echo "Wheel file: $WHEEL_FILE"
+
+ # Check that the wheel has a riscv64 platform tag
+ WHEEL_NAME=$(basename $WHEEL_FILE)
+ echo "Wheel name: $WHEEL_NAME"
+ if echo "$WHEEL_NAME" | grep -q "riscv64"; then
+ echo "riscv64 platform tag found"
+ else
+ echo "ERROR: riscv64 platform tag not found in wheel name"
+ exit 1
+ fi
+
+ # Inspect wheel contents
+ python -m zipfile -l $WHEEL_FILE
+
+ # Verify wheel contains z3 library
+ if python -m zipfile -l $WHEEL_FILE | grep -q "libz3"; then
+ echo "libz3 found in wheel"
+ else
+ echo "ERROR: libz3 not found in wheel"
+ exit 1
+ fi
+
# ============================================================================
# MACOS DYLIB HEADERPAD VALIDATION
# ============================================================================
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index 0decaffad..548fca2eb 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -45,8 +45,21 @@ jobs:
- name: Build
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=x64
+ - name: Validate libz3.dylib and z3 architecture (must be x86_64)
+ run: |
+ set -e
+ for f in build-dist/libz3.dylib build-dist/z3; do
+ ARCH=$(lipo -archs "$f")
+ echo "$f architecture: $ARCH"
+ if [ "$ARCH" != "x86_64" ]; then
+ echo "ERROR: $f has arch '$ARCH', expected 'x86_64' (see issue #9662)"
+ exit 1
+ fi
+ done
+ echo "OK: macOS x64 artifacts are x86_64"
+
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: macOsBuild
path: dist/*.zip
@@ -68,8 +81,21 @@ jobs:
- name: Build
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64
+ - name: Validate libz3.dylib and z3 architecture (must be arm64)
+ run: |
+ set -e
+ for f in build-dist/libz3.dylib build-dist/z3; do
+ ARCH=$(lipo -archs "$f")
+ echo "$f architecture: $ARCH"
+ if [ "$ARCH" != "arm64" ]; then
+ echo "ERROR: $f has arch '$ARCH', expected 'arm64' (see issue #9662)"
+ exit 1
+ fi
+ done
+ echo "OK: macOS arm64 artifacts are arm64"
+
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: MacArm64
path: dist/*.zip
@@ -101,6 +127,17 @@ jobs:
Z3_DIR=$(find . -maxdepth 1 -type d -name "z3-*" | head -n 1)
echo "Z3_DIR=$Z3_DIR" >> $GITHUB_ENV
+ - name: Validate shipped libz3.dylib architecture (must be x86_64)
+ run: |
+ set -e
+ DYLIB="artifacts/$Z3_DIR/bin/libz3.dylib"
+ ARCH=$(lipo -archs "$DYLIB")
+ echo "Shipped $DYLIB architecture: $ARCH"
+ if [ "$ARCH" != "x86_64" ]; then
+ echo "ERROR: x64 nightly zip contains '$ARCH' libz3.dylib (see issue #9662)"
+ exit 1
+ fi
+
- name: Test install_name_tool with headerpad
run: |
cd artifacts/$Z3_DIR/bin
@@ -149,6 +186,17 @@ jobs:
Z3_DIR=$(find . -maxdepth 1 -type d -name "z3-*" | head -n 1)
echo "Z3_DIR=$Z3_DIR" >> $GITHUB_ENV
+ - name: Validate shipped libz3.dylib architecture (must be arm64)
+ run: |
+ set -e
+ DYLIB="artifacts/$Z3_DIR/bin/libz3.dylib"
+ ARCH=$(lipo -archs "$DYLIB")
+ echo "Shipped $DYLIB architecture: $ARCH"
+ if [ "$ARCH" != "arm64" ]; then
+ echo "ERROR: arm64 nightly zip contains '$ARCH' libz3.dylib (see issue #9662)"
+ exit 1
+ fi
+
- name: Test install_name_tool with headerpad
run: |
cd artifacts/$Z3_DIR/bin
@@ -198,7 +246,7 @@ jobs:
run: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: UbuntuBuild
path: dist/*.zip
@@ -233,7 +281,7 @@ jobs:
python scripts/mk_unix_dist.py --nodotnet --arch=arm64
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: UbuntuArm64
path: dist/*.zip
@@ -288,7 +336,7 @@ jobs:
run: zip -r z3doc.zip doc/api
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: UbuntuDoc
path: z3doc.zip
@@ -303,9 +351,17 @@ jobs:
- name: Checkout code
uses: actions/checkout@v6.0.2
+ - name: Select Python
+ run: |
+ # Use the first available manylinux interpreter for deterministic selection.
+ PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
+ test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
+ echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
+ "$PYTHON" --version
+
- name: Setup Python environment
run: |
- /opt/python/cp38-cp38/bin/python -m venv $PWD/env
+ "$PYTHON" -m venv $PWD/env
echo "$PWD/env/bin" >> $GITHUB_PATH
- name: Install build tools
@@ -318,7 +374,7 @@ jobs:
run: pip install ./src/api/python/wheelhouse/*.whl && python - > "$GITHUB_ENV"
+ "$PYTHON" --version
+
- name: Setup Python environment
run: |
- /opt/python/cp38-cp38/bin/python -m venv $PWD/env
+ "$PYTHON" -m venv $PWD/env
echo "$PWD/env/bin" >> $GITHUB_PATH
echo "/tmp/arm-toolchain/bin" >> $GITHUB_PATH
echo "/tmp/arm-toolchain/aarch64-none-linux-gnu/libc/usr/bin" >> $GITHUB_PATH
@@ -358,12 +422,120 @@ jobs:
run: cd src/api/python && CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ AR=aarch64-none-linux-gnu-ar LD=aarch64-none-linux-gnu-ld Z3_CROSS_COMPILING=aarch64 python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../..
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: ManyLinuxPythonBuildArm64
path: src/api/python/wheelhouse/*.whl
retention-days: 2
+ manylinux-python-riscv64:
+ name: "Python bindings (manylinux RISC-V 64 cross)"
+ runs-on: ubuntu-latest
+ timeout-minutes: 90
+ container: quay.io/pypa/manylinux_2_28_x86_64:latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6.0.2
+
+ - name: Download RISC-V toolchain
+ run: curl -L -o /tmp/riscv-toolchain.tar.gz 'https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2024.09.03/riscv64-glibc-ubuntu-20.04-gcc-nightly-2024.09.03-nightly.tar.gz'
+
+ - name: Extract RISC-V toolchain
+ run: |
+ mkdir -p /tmp/riscv-toolchain/
+ tar xf /tmp/riscv-toolchain.tar.gz -C /tmp/riscv-toolchain/ --strip-components=1
+
+ - name: Install MPFR 4 (required by RISC-V toolchain host binaries)
+ run: |
+ dnf install -y gmp-devel
+ curl -L -o /tmp/mpfr.tar.xz https://ftp.gnu.org/gnu/mpfr/mpfr-4.2.1.tar.xz
+ tar xf /tmp/mpfr.tar.xz -C /tmp/
+ cd /tmp/mpfr-4.2.1 && ./configure --prefix=/usr/local --disable-static && make -j$(nproc) && make install
+ ldconfig
+
+ - name: Select Python
+ run: |
+ # Use the first available manylinux interpreter for deterministic selection.
+ PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
+ test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
+ echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
+ "$PYTHON" --version
+
+ - name: Setup Python environment
+ run: |
+ "$PYTHON" -m venv $PWD/env
+ echo "$PWD/env/bin" >> $GITHUB_PATH
+ echo "/tmp/riscv-toolchain/bin" >> $GITHUB_PATH
+
+ - name: Install build tools
+ run: |
+ echo $PATH
+ stat $(which riscv64-unknown-linux-gnu-gcc)
+ pip install build git+https://github.com/rhelmot/auditwheel
+
+ - name: Build wheels
+ run: cd src/api/python && CC=riscv64-unknown-linux-gnu-gcc CXX=riscv64-unknown-linux-gnu-g++ AR=riscv64-unknown-linux-gnu-ar LD=riscv64-unknown-linux-gnu-ld Z3_CROSS_COMPILING=riscv64 python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../..
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v7.0.1
+ with:
+ name: ManyLinuxPythonBuildRiscv64
+ path: src/api/python/wheelhouse/*.whl
+ retention-days: 2
+
+ pyodide-python:
+ name: "Python bindings (Pyodide)"
+ runs-on: ubuntu-24.04
+ timeout-minutes: 90
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6.0.2
+
+ - name: Setup packages
+ run: sudo apt-get update && sudo apt-get install -y python3-dev python3-pip python3-venv
+
+ - name: Create venv
+ run: python3 -m venv ~/env
+
+ - name: Install pyodide
+ run: ~/env/bin/pip install pyodide-build pyodide-cli
+
+ - name: Configure Emscripten
+ run: |
+ git clone https://github.com/emscripten-core/emsdk.git ~/emsdk
+ cd ~/emsdk
+ PYODIDE_EMSCRIPTEN_VERSION=$(~/env/bin/pyodide config get emscripten_version)
+ ./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
+ ./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}
+
+ - name: Build wheel
+ run: |
+ source ~/emsdk/emsdk_env.sh
+ cd src/api/python
+ CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" CXXFLAGS="${CXXFLAGS}" ~/env/bin/pyodide build --exports whole_archive
+ env:
+ CFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0 -g2"
+ LDFLAGS: "-fexceptions -s WASM_BIGINT"
+ CXXFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0"
+
+ - name: Setup env-pyodide
+ run: |
+ source ~/env/bin/activate
+ source ~/emsdk/emsdk_env.sh
+ pyodide venv ~/env-pyodide
+
+ - name: Test wheel
+ run: |
+ ~/env-pyodide/bin/pip install src/api/python/dist/*.whl
+ ~/env-pyodide/bin/python src/api/python/z3test.py z3
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v7.0.1
+ with:
+ name: PyodidePythonBuild
+ path: src/api/python/dist/*.whl
+ retention-days: 2
+
windows-build-x64:
name: "Windows x64 build"
runs-on: windows-latest
@@ -384,7 +556,7 @@ jobs:
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: WindowsBuild-x64
path: dist/*.zip
@@ -410,7 +582,7 @@ jobs:
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: WindowsBuild-x86
path: dist/*.zip
@@ -436,7 +608,7 @@ jobs:
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.PATCH }} --zip
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: WindowsBuild-arm64
path: dist/arm64/*.zip
@@ -496,7 +668,7 @@ jobs:
path: package
- name: Setup NuGet
- uses: nuget/setup-nuget@v2
+ uses: nuget/setup-nuget@v4
with:
nuget-version: 'latest'
@@ -513,7 +685,7 @@ jobs:
nuget pack out\Microsoft.Z3.sym.nuspec -Version ${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.PATCH }}.${{ github.run_number }} -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: NuGet
path: |
@@ -541,7 +713,7 @@ jobs:
path: package
- name: Setup NuGet
- uses: nuget/setup-nuget@v2
+ uses: nuget/setup-nuget@v4
with:
nuget-version: 'latest'
@@ -558,7 +730,7 @@ jobs:
nuget pack out\Microsoft.Z3.x86.sym.nuspec -Version ${{ env.MAJOR }}.${{ env.MINOR }}.${{ env.PATCH }}.${{ github.run_number }} -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: NuGet32
path: |
@@ -568,7 +740,7 @@ jobs:
python-package:
name: "Python packaging"
- needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64]
+ needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64, manylinux-python-riscv64, pyodide-python]
runs-on: ubuntu-24.04
steps:
- name: Checkout code
@@ -621,6 +793,18 @@ jobs:
name: ManyLinuxPythonBuildArm64
path: artifacts
+ - name: Download ManyLinux RISC-V 64 Build
+ uses: actions/download-artifact@v8.0.1
+ with:
+ name: ManyLinuxPythonBuildRiscv64
+ path: artifacts
+
+ - name: Download Pyodide Build
+ uses: actions/download-artifact@v8.0.1
+ with:
+ name: PyodidePythonBuild
+ path: artifacts
+
- name: Extract builds
run: |
cd artifacts
@@ -651,7 +835,7 @@ jobs:
cp artifacts/*.whl src/api/python/dist/.
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: PythonPackages
path: src/api/python/dist/*
@@ -759,4 +943,3 @@ jobs:
with:
packages-dir: dist
repository-url: https://test.pypi.org/legacy/
-
diff --git a/.github/workflows/nuget-build.yml b/.github/workflows/nuget-build.yml
index 06e1af741..bc4c8c5cb 100644
--- a/.github/workflows/nuget-build.yml
+++ b/.github/workflows/nuget-build.yml
@@ -34,7 +34,7 @@ jobs:
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.17.0' }} --zip
- name: Upload Windows x64 artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: windows-x64
path: dist/*.zip
@@ -58,7 +58,7 @@ jobs:
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.17.0' }} --zip
- name: Upload Windows x86 artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: windows-x86
path: dist/*.zip
@@ -82,7 +82,7 @@ jobs:
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ github.event.inputs.version || '4.17.0' }} --zip
- name: Upload Windows ARM64 artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: windows-arm64
path: build-dist\arm64\dist\*.zip
@@ -103,7 +103,7 @@ jobs:
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk
- name: Upload Ubuntu artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: ubuntu
path: dist/*.zip
@@ -124,7 +124,7 @@ jobs:
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk
- name: Upload macOS x64 artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: macos-x64
path: dist/*.zip
@@ -145,7 +145,7 @@ jobs:
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64
- name: Upload macOS ARM64 artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: macos-arm64
path: dist/*.zip
@@ -181,7 +181,7 @@ jobs:
ls -la package-files/
- name: Setup NuGet
- uses: nuget/setup-nuget@v2
+ uses: nuget/setup-nuget@v4
with:
nuget-version: 'latest'
@@ -198,7 +198,7 @@ jobs:
nuget pack out\Microsoft.Z3.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
- name: Upload NuGet package
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: nuget-x64
path: |
@@ -230,7 +230,7 @@ jobs:
run: find packages -type f
- name: Setup NuGet
- uses: nuget/setup-nuget@v2
+ uses: nuget/setup-nuget@v4
with:
nuget-version: 'latest'
@@ -247,7 +247,7 @@ jobs:
nuget pack out\Microsoft.Z3.x86.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
- name: Upload NuGet package
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: nuget-x86
path: |
diff --git a/.github/workflows/ocaml.yaml b/.github/workflows/ocaml.yaml
index 7ed972b65..e2ed53d54 100644
--- a/.github/workflows/ocaml.yaml
+++ b/.github/workflows/ocaml.yaml
@@ -21,7 +21,7 @@ jobs:
# Cache ccache (shared across runs)
- name: Cache ccache
- uses: actions/cache@v5.0.4
+ uses: actions/cache@v5.0.5
with:
path: ~/.ccache
key: ${{ runner.os }}-ccache-${{ github.sha }}
@@ -30,7 +30,7 @@ jobs:
# Cache opam (compiler + packages)
- name: Cache opam
- uses: actions/cache@v5.0.4
+ uses: actions/cache@v5.0.5
with:
path: ~/.opam
key: ${{ runner.os }}-opam-${{ matrix.ocaml-version }}-${{ github.sha }}
diff --git a/.github/workflows/ostrich-benchmark.md b/.github/workflows/ostrich-benchmark.md
index 6e7c450e4..ef298d065 100644
--- a/.github/workflows/ostrich-benchmark.md
+++ b/.github/workflows/ostrich-benchmark.md
@@ -8,7 +8,10 @@ on:
permissions: read-all
-network: defaults
+network:
+ allowed:
+ - defaults
+ - api.nuget.org
tools:
bash: true
@@ -85,6 +88,8 @@ echo "Found Microsoft.Z3.dll at: $Z3_DOTNET_DLL"
If the build fails, report the error clearly and exit without proceeding.
+Once the binary is confirmed working, call the `noop` safe-output tool with the message `"Z3 built successfully from the c3 branch. Starting ZIPT build and benchmark — results will be posted as a GitHub Discussion once complete."` This keepalive call refreshes the safe-output MCP session before the long build and benchmark phases begin, preventing a session timeout.
+
## Phase 2a: Clone and Build ZIPT
Clone the ZIPT solver from the `parikh` branch and compile it against the Z3 .NET bindings built in Phase 1.
@@ -150,6 +155,8 @@ if [ "$TOTAL_FILES" -eq 0 ]; then
fi
```
+Once the benchmark files are confirmed, call the `noop` safe-output tool with the message `"Benchmark files ready: Ostrich .smt2 files extracted. Starting benchmark run — this may take over an hour."` This second keepalive refreshes the safe-output MCP session immediately before the long per-file benchmark loop begins.
+
## Phase 3: Run Benchmarks
Run every file from `/tmp/all_ostrich_files.txt` with both Z3 string solvers and ZIPT. Use a **5-second timeout** per run.
@@ -402,3 +409,14 @@ Post the Markdown report as a new GitHub Discussion using the `create-discussion
- **Handle build failures gracefully**: If Z3 fails to build, report the error and create a brief discussion noting the build failure. If ZIPT fails to build, continue with only the seq/nseq columns and note `n/a` for ZIPT results.
- **Large report**: Always put the per-file table in a `` collapsible section since there may be many files.
- **Progress logging**: Print a line per file as you run it (e.g., `[N] [filename] seq=...`) so the workflow log shows progress even for large benchmark sets.
+
+## Safe Output Guarantee
+
+You **MUST** call either `create_discussion` or `noop` before the workflow ends, regardless of what happened during execution:
+
+- **Build succeeded, benchmarks ran**: Call `create_discussion` with the full report.
+- **Build succeeded, benchmarks partially ran**: Call `create_discussion` with whatever results were collected and a note about what could not be completed.
+- **Z3 build failed**: Call `noop` with a brief message describing the build error.
+- **No benchmarks could be run**: Call `noop` with a summary of what failed and why.
+
+Failing to produce any safe output triggers an automatic workflow-failure issue that clutters the repository.
diff --git a/.github/workflows/qf-s-benchmark.md b/.github/workflows/qf-s-benchmark.md
index fce83f8a9..653380fd6 100644
--- a/.github/workflows/qf-s-benchmark.md
+++ b/.github/workflows/qf-s-benchmark.md
@@ -1,5 +1,5 @@
---
-description: Run Z3 string solver benchmarks (seq vs nseq) on QF_S test suite from the c3 branch and post results as a GitHub discussion
+description: Benchmark Z3 seq vs nseq string solvers on QF_S test suite from the c3 branch and post results as a GitHub discussion
on:
schedule:
@@ -17,7 +17,7 @@ tools:
safe-outputs:
create-discussion:
- title-prefix: "[ZIPT Benchmark] "
+ title-prefix: "[QF_S Benchmark] "
category: "Agentic Workflows"
close-older-discussions: true
missing-tool:
@@ -25,7 +25,7 @@ safe-outputs:
noop:
report-as-issue: false
-timeout-minutes: 90
+timeout-minutes: 120
steps:
- name: Checkout c3 branch
@@ -37,437 +37,368 @@ steps:
---
+# QF_S String Solver Benchmark
-# ZIPT String Solver Benchmark
+## Job Description
-You are an AI agent that benchmarks Z3 string solvers (`seq` and `nseq`) and the standalone ZIPT solver on QF_S SMT-LIB2 benchmarks from the `c3` branch, and publishes a summary report as a GitHub discussion.
+Your name is ${{ github.workflow }}. You are an expert performance analyst for the Z3 theorem prover, specializing in the string/sequence theory. Your task is to benchmark the `seq` solver (classical string theory) against the `nseq` solver (ZIPT-based string theory) on the QF_S test suite from the `c3` branch, and post a structured report as a GitHub Discussion.
-## Context
+The workspace already contains the `c3` branch (checked out by the preceding workflow step).
-- **Repository**: ${{ github.repository }}
-- **Workspace**: ${{ github.workspace }}
-- **Branch**: c3 (already checked out by the workflow setup step)
+## Phase 1: Set Up the Build Environment
-## Phase 1: Build Z3
-
-Build Z3 from the checked-out `c3` branch using CMake + Ninja, including the .NET bindings required by ZIPT.
+Install required build tools:
```bash
-cd ${{ github.workspace }}
-
-# Install build dependencies if missing
-sudo apt-get install -y ninja-build cmake python3 zstd dotnet-sdk-8.0 2>/dev/null || true
-
-# Configure the build in Debug mode to enable assertions and tracing
-# (Debug mode is required for -tr: trace flags to produce meaningful output)
-mkdir -p build
-cd build
-cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Debug -DZ3_BUILD_DOTNET_BINDINGS=ON 2>&1 | tail -20
-
-# Build z3 binary and .NET bindings (this takes ~15-17 minutes)
-ninja z3 2>&1 | tail -30
-ninja build_z3_dotnet_bindings 2>&1 | tail -20
-
-# Verify the build succeeded
-./z3 --version
-
-# Locate the Microsoft.Z3.dll produced by the build
-Z3_DOTNET_DLL=$(find . -name "Microsoft.Z3.dll" -not -path "*/obj/*" | head -1)
-if [ -z "$Z3_DOTNET_DLL" ]; then
- echo "ERROR: Microsoft.Z3.dll not found after build"
- exit 1
-fi
-echo "Found Microsoft.Z3.dll at: $Z3_DOTNET_DLL"
+sudo apt-get update -y
+sudo apt-get install -y cmake ninja-build python3 python3-pip time
```
-If the build fails, report the error clearly and exit without proceeding.
-
-## Phase 2a: Clone and Build ZIPT
-
-Clone the ZIPT solver from the `parikh` branch and compile it against the Z3 .NET bindings built in Phase 1.
+Verify tools:
```bash
-cd ${{ github.workspace }}
+cmake --version
+ninja --version
+python3 --version
+```
-# Re-locate the Microsoft.Z3.dll if needed
-Z3_DOTNET_DLL=$(find build -name "Microsoft.Z3.dll" -not -path "*/obj/*" | head -1)
-Z3_LIB_DIR=${{ github.workspace }}/build
+## Phase 2: Build Z3 in Release Mode
-# Clone ZIPT (parikh branch)
-git clone --depth=1 --branch parikh https://github.com/CEisenhofer/ZIPT.git /tmp/zipt
+Build Z3 in Release mode for accurate benchmark performance numbers and lower memory usage. Running `ninja` in the background with `&` is not allowed — concurrent C++ compilation and LLM inference can exhaust available RAM and kill the agent process.
-# Patch ZIPT.csproj to point at the freshly built Microsoft.Z3.dll
-# (the repo has a Windows-relative hardcoded path that won't exist here)
-sed -i "s|.*|$Z3_DOTNET_DLL|" /tmp/zipt/ZIPT/ZIPT.csproj
+```bash
+mkdir -p /tmp/z3-build
+cd /tmp/z3-build
+cmake "$GITHUB_WORKSPACE" \
+ -G Ninja \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DZ3_BUILD_TEST_EXECUTABLES=OFF \
+ 2>&1 | tee /tmp/z3-cmake.log
+ninja -j2 z3 2>&1 | tee /tmp/z3-build.log
+```
-# Build ZIPT in Release mode
-cd /tmp/zipt/ZIPT
-dotnet build --configuration Release 2>&1 | tail -20
+Verify the binary was built:
-# Locate the built ZIPT.dll
-ZIPT_DLL=$(find /tmp/zipt/ZIPT/bin/Release -name "ZIPT.dll" | head -1)
-if [ -z "$ZIPT_DLL" ]; then
- echo "ERROR: ZIPT.dll not found after build"
- exit 1
+```bash
+/tmp/z3-build/z3 --version
+```
+
+If the build fails, report it immediately and stop.
+
+Once the binary is confirmed working, call the `noop` safe-output tool with the message `"Z3 built successfully from the c3 branch. Benchmark starting — results will be posted as a GitHub Discussion once complete."` This keepalive call refreshes the safe-output MCP session before the long benchmark run begins, preventing a session timeout.
+
+## Phase 3: Discover QF_S Benchmark Files
+
+Find all `.smt2` benchmark files in the workspace that belong to the QF_S logic:
+
+```bash
+# Search for explicit QF_S logic declarations
+grep -rl 'QF_S' "$GITHUB_WORKSPACE" --include='*.smt2' 2>/dev/null > /tmp/qf_s_files.txt
+
+# Also look in dedicated benchmark directories
+find "$GITHUB_WORKSPACE" \
+ \( -path "*/QF_S/*" -o -path "*/qf_s/*" -o -path "*/benchmarks/*" \) \
+ -name '*.smt2' 2>/dev/null >> /tmp/qf_s_files.txt
+
+# Deduplicate
+sort -u /tmp/qf_s_files.txt -o /tmp/qf_s_files.txt
+
+TOTAL=$(wc -l < /tmp/qf_s_files.txt)
+echo "Found $TOTAL QF_S benchmark files"
+head -20 /tmp/qf_s_files.txt
+```
+
+If fewer than 5 files are found, also scan the entire workspace for any `.smt2` file that exercises string constraints:
+
+```bash
+if [ "$TOTAL" -lt 5 ]; then
+ grep -rl 'declare.*String\|str\.\|seq\.' "$GITHUB_WORKSPACE" \
+ --include='*.smt2' 2>/dev/null >> /tmp/qf_s_files.txt
+ sort -u /tmp/qf_s_files.txt -o /tmp/qf_s_files.txt
+ TOTAL=$(wc -l < /tmp/qf_s_files.txt)
+ echo "After extended search: $TOTAL files"
fi
-echo "ZIPT binary: $ZIPT_DLL"
+```
-# Make libz3.so visible to the .NET runtime at ZIPT startup
-ZIPT_OUT_DIR=$(dirname "$ZIPT_DLL")
-if cp "$Z3_LIB_DIR/libz3.so" "$ZIPT_OUT_DIR/" 2>/dev/null; then
- echo "Copied libz3.so to $ZIPT_OUT_DIR"
+Cap the benchmark set to keep total runtime under 60 minutes:
+
+```bash
+# Use at most 300 files; take a random sample if more are available
+if [ "$TOTAL" -gt 300 ]; then
+ shuf -n 300 /tmp/qf_s_files.txt > /tmp/qf_s_sample.txt
else
- echo "WARNING: could not copy libz3.so to $ZIPT_OUT_DIR — setting LD_LIBRARY_PATH fallback"
+ cp /tmp/qf_s_files.txt /tmp/qf_s_sample.txt
fi
-export LD_LIBRARY_PATH="$Z3_LIB_DIR${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
-echo "ZIPT build complete."
+SAMPLE=$(wc -l < /tmp/qf_s_sample.txt)
+echo "Running benchmarks on $SAMPLE files"
```
-If the ZIPT build fails, note the error in the report but continue with the Z3-only benchmark columns.
+## Phase 4: Run Benchmarks — seq vs nseq
-## Phase 2b: Extract and Select Benchmark Files
-
-Extract the QF_S benchmark archive and randomly select 50 files.
+Run each benchmark with both solvers. Use a per-file timeout of 5 seconds. Set Z3's internal timeout to 4 seconds so it exits cleanly before the shell timeout fires.
```bash
-cd ${{ github.workspace }}
+Z3=/tmp/z3-build/z3
+TIMEOUT_SEC=5
+Z3_TIMEOUT_SEC=4
+RESULTS=/tmp/benchmark-results.csv
-# Extract the archive
-mkdir -p /tmp/qfs_benchmarks
-tar --zstd -xf tests/QF_S.tar.zst -C /tmp/qfs_benchmarks
+echo "file,seq_result,seq_time_ms,nseq_result,nseq_time_ms" > "$RESULTS"
-# List all .smt2 files
-find /tmp/qfs_benchmarks -name "*.smt2" -type f > /tmp/all_qfs_files.txt
-TOTAL_FILES=$(wc -l < /tmp/all_qfs_files.txt)
-echo "Total QF_S files: $TOTAL_FILES"
+total=0
+done_count=0
+while IFS= read -r smt_file; do
+ total=$((total + 1))
-# Randomly select 200 files
-shuf -n 200 /tmp/all_qfs_files.txt > /tmp/selected_files.txt
-echo "Selected 200 files for benchmarking"
-cat /tmp/selected_files.txt
+ # Run with seq solver; capture both stdout (z3 output) and stderr (time output)
+ SEQ_OUT=$({ time timeout "$TIMEOUT_SEC" "$Z3" \
+ smt.string_solver=seq \
+ -T:"$Z3_TIMEOUT_SEC" \
+ "$smt_file" 2>/dev/null; } 2>&1)
+ SEQ_RESULT=$(echo "$SEQ_OUT" | grep -E '^(sat|unsat|unknown)' | head -1)
+ SEQ_MS=$(echo "$SEQ_OUT" | grep real | awk '{split($2,a,"m"); split(a[2],b,"s"); printf "%d", (a[1]*60+b[1])*1000}')
+ [ -z "$SEQ_RESULT" ] && SEQ_RESULT="timeout"
+ [ -z "$SEQ_MS" ] && SEQ_MS=$((TIMEOUT_SEC * 1000))
+
+ # Run with nseq solver; same structure
+ NSEQ_OUT=$({ time timeout "$TIMEOUT_SEC" "$Z3" \
+ smt.string_solver=nseq \
+ -T:"$Z3_TIMEOUT_SEC" \
+ "$smt_file" 2>/dev/null; } 2>&1)
+ NSEQ_RESULT=$(echo "$NSEQ_OUT" | grep -E '^(sat|unsat|unknown)' | head -1)
+ NSEQ_MS=$(echo "$NSEQ_OUT" | grep real | awk '{split($2,a,"m"); split(a[2],b,"s"); printf "%d", (a[1]*60+b[1])*1000}')
+ [ -z "$NSEQ_RESULT" ] && NSEQ_RESULT="timeout"
+ [ -z "$NSEQ_MS" ] && NSEQ_MS=$((TIMEOUT_SEC * 1000))
+
+ SHORT=$(basename "$smt_file")
+ echo "$SHORT,$SEQ_RESULT,$SEQ_MS,$NSEQ_RESULT,$NSEQ_MS" >> "$RESULTS"
+
+ done_count=$((done_count + 1))
+ if [ $((done_count % 50)) -eq 0 ]; then
+ echo "Progress: $done_count / $SAMPLE files completed"
+ fi
+done < /tmp/qf_s_sample.txt
+
+echo "Benchmark run complete: $done_count files"
```
-## Phase 3: Run Benchmarks
+## Phase 5: Collect Seq Traces for Interesting Cases
-Run each of the 200 selected files with both Z3 string solvers and ZIPT. Use a 5-second timeout for seq and a 10-second timeout for nseq and ZIPT.
-
-For each file, run:
-1. `z3 smt.string_solver=seq -tr:seq -T:5 ` — seq solver with sequence-solver tracing enabled; rename the `.z3-trace` output after each run so it is not overwritten. Use `-T:5` when tracing to cap trace size.
-2. `z3 smt.string_solver=nseq -T:5 ` — nseq solver without tracing (timing only).
-3. `dotnet -t:5000 ` — ZIPT solver (milliseconds).
-
-Capture:
-- **Verdict**: `sat`, `unsat`, `unknown`, `timeout` (if exit code indicates timeout or process is killed), or `bug` (if a solver crashes / produces a non-standard result)
-- **Time** (seconds): wall-clock time for the run
-- A row is flagged `SOUNDNESS_DISAGREEMENT` when any two solvers that both produced a definitive answer (sat/unsat) disagree
-
-Use a bash script to automate this:
+For benchmarks where `seq` solves in under 2 s but `nseq` times out (seq-fast/nseq-slow cases), collect a brief `seq` trace to understand what algorithm is used:
```bash
-#!/usr/bin/env bash
-set -euo pipefail
+Z3=/tmp/z3-build/z3
+mkdir -p /tmp/traces
-Z3=${{ github.workspace }}/build/z3
-ZIPT_DLL=$(find /tmp/zipt/ZIPT/bin/Release -name "ZIPT.dll" 2>/dev/null | head -1)
-ZIPT_AVAILABLE=false
-[ -n "$ZIPT_DLL" ] && ZIPT_AVAILABLE=true
+# Find seq-fast / nseq-slow files: seq solved (sat/unsat) in <2000ms AND nseq timed out
+awk -F, 'NR>1 && ($2=="sat"||$2=="unsat") && $3<2000 && $4=="timeout" {print $1}' \
+ /tmp/benchmark-results.csv > /tmp/seq_fast_nseq_slow.txt
+echo "seq-fast / nseq-slow files: $(wc -l < /tmp/seq_fast_nseq_slow.txt)"
-# Ensure libz3.so is on the dynamic-linker path for the .NET runtime
-export LD_LIBRARY_PATH=${{ github.workspace }}/build${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}
-
-RESULTS=/tmp/benchmark_results.tsv
-TRACES_DIR=/tmp/seq_traces
-mkdir -p "$TRACES_DIR"
-
-echo -e "file\tseq_verdict\tseq_time\tnseq_verdict\tnseq_time\tzipt_verdict\tzipt_time\tnotes" > "$RESULTS"
-
-run_z3_seq_traced() {
- # Run seq solver with -tr:seq tracing. Cap at 5 s so trace files stay manageable.
- local file="$1"
- local trace_dest="$2"
- local start end elapsed verdict output exit_code
-
- # Remove any leftover trace from a prior run so we can detect whether one was produced.
- rm -f .z3-trace
-
- start=$(date +%s%3N)
- output=$(timeout 7 "$Z3" "smt.string_solver=seq" -tr:seq -T:5 "$file" 2>&1)
- exit_code=$?
- end=$(date +%s%3N)
- elapsed=$(echo "scale=3; ($end - $start) / 1000" | bc)
-
- # Rename the trace file immediately so the next run does not overwrite it.
- if [ -f .z3-trace ]; then
- mv .z3-trace "$trace_dest"
- else
- # Write a sentinel so Phase 4 can detect the absence of a trace.
- echo "(no trace produced)" > "$trace_dest"
- fi
-
- if echo "$output" | grep -q "^unsat"; then
- verdict="unsat"
- elif echo "$output" | grep -q "^sat"; then
- verdict="sat"
- elif echo "$output" | grep -q "^unknown"; then
- verdict="unknown"
- elif [ "$exit_code" -eq 124 ]; then
- verdict="timeout"
- elif echo "$output" | grep -qi "error\|assertion\|segfault\|SIGABRT\|exception"; then
- verdict="bug"
- else
- verdict="unknown"
- fi
-
- echo "$verdict $elapsed"
-}
-
-run_z3_nseq() {
- local file="$1"
- local start end elapsed verdict output exit_code
-
- start=$(date +%s%3N)
- output=$(timeout 12 "$Z3" "smt.string_solver=nseq" -T:5 "$file" 2>&1)
- exit_code=$?
- end=$(date +%s%3N)
- elapsed=$(echo "scale=3; ($end - $start) / 1000" | bc)
-
- if echo "$output" | grep -q "^unsat"; then
- verdict="unsat"
- elif echo "$output" | grep -q "^sat"; then
- verdict="sat"
- elif echo "$output" | grep -q "^unknown"; then
- verdict="unknown"
- elif [ "$exit_code" -eq 124 ]; then
- verdict="timeout"
- elif echo "$output" | grep -qi "error\|assertion\|segfault\|SIGABRT\|exception"; then
- verdict="bug"
- else
- verdict="unknown"
- fi
-
- echo "$verdict $elapsed"
-}
-
-run_zipt() {
- local file="$1"
- local start end elapsed verdict output exit_code
-
- if [ "$ZIPT_AVAILABLE" != "true" ]; then
- echo "n/a 0.000"
- return
- fi
-
- start=$(date +%s%3N)
- # ZIPT prints the filename on the first line, then SAT/UNSAT/UNKNOWN on subsequent lines
- output=$(timeout 12 dotnet "$ZIPT_DLL" -t:5000 "$file" 2>&1)
- exit_code=$?
- end=$(date +%s%3N)
- elapsed=$(echo "scale=3; ($end - $start) / 1000" | bc)
-
- if echo "$output" | grep -qi "^UNSAT$"; then
- verdict="unsat"
- elif echo "$output" | grep -qi "^SAT$"; then
- verdict="sat"
- elif echo "$output" | grep -qi "^UNKNOWN$"; then
- verdict="unknown"
- elif [ "$exit_code" -eq 124 ]; then
- verdict="timeout"
- elif echo "$output" | grep -qi "error\|crash\|exception\|Unsupported"; then
- verdict="bug"
- else
- verdict="unknown"
- fi
-
- echo "$verdict $elapsed"
-}
-
-while IFS= read -r file; do
- fname=$(basename "$file")
- # Use a sanitised filename (replace non-alphanumeric with _) for the trace path.
- safe_name=$(echo "$fname" | tr -cs 'A-Za-z0-9._-' '_')
- trace_path="$TRACES_DIR/${safe_name}.z3-trace"
-
- seq_result=$(run_z3_seq_traced "$file" "$trace_path")
- nseq_result=$(run_z3_nseq "$file")
- zipt_result=$(run_zipt "$file")
-
- seq_verdict=$(echo "$seq_result" | cut -d' ' -f1)
- seq_time=$(echo "$seq_result" | cut -d' ' -f2)
- nseq_verdict=$(echo "$nseq_result" | cut -d' ' -f1)
- nseq_time=$(echo "$nseq_result" | cut -d' ' -f2)
- zipt_verdict=$(echo "$zipt_result" | cut -d' ' -f1)
- zipt_time=$(echo "$zipt_result" | cut -d' ' -f2)
-
- # Flag soundness disagreement when any two definitive verdicts disagree
- notes=""
- # Build list of (solver, verdict) pairs for definitive answers only
- declare -A definitive_map
- [ "$seq_verdict" = "sat" ] || [ "$seq_verdict" = "unsat" ] && definitive_map[seq]="$seq_verdict"
- [ "$nseq_verdict" = "sat" ] || [ "$nseq_verdict" = "unsat" ] && definitive_map[nseq]="$nseq_verdict"
- [ "$zipt_verdict" = "sat" ] || [ "$zipt_verdict" = "unsat" ] && definitive_map[zipt]="$zipt_verdict"
- # Check every pair for conflict
- has_sat=false; has_unsat=false
- for v in "${definitive_map[@]}"; do
- [ "$v" = "sat" ] && has_sat=true
- [ "$v" = "unsat" ] && has_unsat=true
- done
- if $has_sat && $has_unsat; then
- notes="SOUNDNESS_DISAGREEMENT"
- fi
-
- echo -e "$fname\t$seq_verdict\t$seq_time\t$nseq_verdict\t$nseq_time\t$zipt_verdict\t$zipt_time\t$notes" >> "$RESULTS"
- echo "[$fname] seq=$seq_verdict(${seq_time}s) nseq=$nseq_verdict(${nseq_time}s) zipt=$zipt_verdict(${zipt_time}s) $notes"
-done < /tmp/selected_files.txt
-
-echo "Benchmark run complete. Results saved to $RESULTS"
-echo "Trace files saved to $TRACES_DIR"
-```
-
-Save this script to `/tmp/run_benchmarks.sh`, make it executable, and run it.
-
-## Phase 3.5: Identify seq-fast / nseq-slow Cases and Analyse Traces
-
-After the benchmark loop completes, identify files where seq solved the instance quickly but nseq was significantly slower (or timed out). For each such file, read its saved seq trace and produce a hypothesis for why nseq is slower.
-
-**Definition of "seq-fast / nseq-slow"**: seq_time < 1.0 s AND nseq_time > 3 × seq_time (and nseq_time > 0.5 s).
-
-For each matching file:
-1. Read the corresponding trace file from `/tmp/seq_traces/`.
-2. Look for the sequence of lemmas, reductions, or decisions that led seq to a fast conclusion.
-3. Identify patterns absent or less exploited in nseq: e.g., length-based propagation early in the trace, Parikh constraints eliminating possibilities, Nielsen graph pruning, equation splitting, or overlap resolution.
-4. Write a 3–5 sentence hypothesis explaining the likely reason for the nseq slowdown, referencing specific trace entries where possible.
-
-Use a script to collect the candidates:
-
-```bash
-#!/usr/bin/env bash
-RESULTS=/tmp/benchmark_results.tsv
-TRACES_DIR=/tmp/seq_traces
-ANALYSIS=/tmp/trace_analysis.md
-
-echo "# Trace Analysis: seq-fast / nseq-slow Candidates" > "$ANALYSIS"
-echo "" >> "$ANALYSIS"
-
-# Skip header line; columns: file seq_verdict seq_time nseq_verdict nseq_time ...
-tail -n +2 "$RESULTS" | while IFS=$'\t' read -r fname seq_verdict seq_time nseq_verdict nseq_time _rest; do
- # Use bc for floating-point comparison; bc does not support && so split into separate tests.
- is_fast=$(echo "$seq_time < 1.0" | bc -l 2>/dev/null || echo 0)
- threshold=$(echo "$seq_time * 3" | bc -l 2>/dev/null || echo 99999)
- is_slow_threshold=$(echo "$nseq_time > $threshold" | bc -l 2>/dev/null || echo 0)
- # Extra guard: exclude trivially fast seq cases where 3× is still < 0.5 s
- is_over_half=$(echo "$nseq_time > 0.5" | bc -l 2>/dev/null || echo 0)
-
- if [ "$is_fast" = "1" ] && [ "$is_slow_threshold" = "1" ] && [ "$is_over_half" = "1" ]; then
- safe_name=$(echo "$fname" | tr -cs 'A-Za-z0-9._-' '_')
- trace_path="$TRACES_DIR/${safe_name}.z3-trace"
- echo "## $fname" >> "$ANALYSIS"
- echo "" >> "$ANALYSIS"
- echo "seq: ${seq_time}s (${seq_verdict}), nseq: ${nseq_time}s (${nseq_verdict})" >> "$ANALYSIS"
- echo "" >> "$ANALYSIS"
- echo "### Trace excerpt (first 200 lines)" >> "$ANALYSIS"
- echo '```' >> "$ANALYSIS"
- head -200 "$trace_path" 2>/dev/null >> "$ANALYSIS" || echo "(trace file not found on disk)" >> "$ANALYSIS"
- echo '```' >> "$ANALYSIS"
- echo "" >> "$ANALYSIS"
- echo "---" >> "$ANALYSIS"
- echo "" >> "$ANALYSIS"
- fi
+# Collect traces for at most 5 such cases
+head -5 /tmp/seq_fast_nseq_slow.txt | while IFS= read -r short; do
+ # Find the full path
+ full=$(grep "/$short$" /tmp/qf_s_sample.txt | head -1)
+ [ -z "$full" ] && continue
+ timeout 5 "$Z3" \
+ smt.string_solver=seq \
+ -tr:seq \
+ -T:5 \
+ "$full" > "/tmp/traces/${short%.smt2}.seq.trace" 2>&1 || true
done
-
-echo "Candidate list written to $ANALYSIS"
-cat "$ANALYSIS"
```
-Save this to `/tmp/analyse_traces.sh`, make it executable, and run it. Then read the trace excerpts collected in `/tmp/trace_analysis.md` and — for each candidate — write your hypothesis in the Phase 4 summary report under a **"Trace Analysis"** section.
+## Phase 6: Analyze Results
-## Phase 4: Generate Summary Report
+Compute summary statistics from the CSV. Save the analysis script to a file and run it:
-Read `/tmp/benchmark_results.tsv` and compute statistics. Then generate a Markdown report.
+```bash
+cat > /tmp/analyze_benchmark.py << 'PYEOF'
+import csv, sys
-Compute:
-- **Total benchmarks**: 200
-- **Per solver (seq, nseq, and ZIPT)**: count of sat / unsat / unknown / timeout / bug verdicts
-- **Total time used**: sum of all times for each solver
-- **Average time per benchmark**: total_time / 200
-- **Soundness disagreements**: files where any two solvers that both returned a definitive answer disagree (these are the most critical bugs)
-- **Bugs / crashes**: files with error/crash verdicts
+results = []
+with open('/tmp/benchmark-results.csv') as f:
+ reader = csv.DictReader(f)
+ for row in reader:
+ results.append(row)
-Format the report as a GitHub Discussion post (GitHub-flavored Markdown):
+total = len(results)
+if total == 0:
+ print("No results found.")
+ sys.exit(0)
+
+def is_correct(r, solver):
+ prefix = 'seq' if solver == 'seq' else 'nseq'
+ return r[f'{prefix}_result'] in ('sat', 'unsat')
+
+def timed_out(r, solver):
+ prefix = 'seq' if solver == 'seq' else 'nseq'
+ return r[f'{prefix}_result'] == 'timeout'
+
+seq_solved = sum(1 for r in results if is_correct(r, 'seq'))
+nseq_solved = sum(1 for r in results if is_correct(r, 'nseq'))
+seq_to = sum(1 for r in results if timed_out(r, 'seq'))
+nseq_to = sum(1 for r in results if timed_out(r, 'nseq'))
+
+seq_times = [int(r['seq_time_ms']) for r in results if is_correct(r, 'seq')]
+nseq_times = [int(r['nseq_time_ms']) for r in results if is_correct(r, 'nseq')]
+
+def median(lst):
+ s = sorted(lst)
+ n = len(s)
+ return s[n//2] if n else 0
+
+def mean(lst):
+ return sum(lst)//len(lst) if lst else 0
+
+# Disagreements (sat vs unsat or vice-versa)
+disagreements = [
+ r for r in results
+ if r['seq_result'] in ('sat','unsat')
+ and r['nseq_result'] in ('sat','unsat')
+ and r['seq_result'] != r['nseq_result']
+]
+
+# seq-fast / nseq-slow: seq solved in <2s, nseq timed out
+seq_fast_nseq_slow = [
+ r for r in results
+ if is_correct(r, 'seq') and int(r['seq_time_ms']) < 2000 and timed_out(r, 'nseq')
+]
+# nseq-fast / seq-slow: nseq solved in <2s, seq timed out
+nseq_fast_seq_slow = [
+ r for r in results
+ if is_correct(r, 'nseq') and int(r['nseq_time_ms']) < 2000 and timed_out(r, 'seq')
+]
+
+print(f"TOTAL={total}")
+print(f"SEQ_SOLVED={seq_solved}")
+print(f"NSEQ_SOLVED={nseq_solved}")
+print(f"SEQ_TIMEOUTS={seq_to}")
+print(f"NSEQ_TIMEOUTS={nseq_to}")
+print(f"SEQ_MEDIAN_MS={median(seq_times)}")
+print(f"NSEQ_MEDIAN_MS={median(nseq_times)}")
+print(f"SEQ_MEAN_MS={mean(seq_times)}")
+print(f"NSEQ_MEAN_MS={mean(nseq_times)}")
+print(f"DISAGREEMENTS={len(disagreements)}")
+print(f"SEQ_FAST_NSEQ_SLOW={len(seq_fast_nseq_slow)}")
+print(f"NSEQ_FAST_SEQ_SLOW={len(nseq_fast_seq_slow)}")
+
+# Print top-10 slowest for nseq that seq handles fast
+print("\nTOP_SEQ_FAST_NSEQ_SLOW:")
+for r in sorted(seq_fast_nseq_slow, key=lambda x: -int(x['nseq_time_ms']))[:10]:
+ print(f" {r['file']} seq={r['seq_time_ms']}ms nseq={r['nseq_time_ms']}ms seq_result={r['seq_result']} nseq_result={r['nseq_result']}")
+
+print("\nTOP_NSEQ_FAST_SEQ_SLOW:")
+for r in sorted(nseq_fast_seq_slow, key=lambda x: -int(x['seq_time_ms']))[:10]:
+ print(f" {r['file']} seq={r['seq_time_ms']}ms nseq={r['nseq_time_ms']}ms seq_result={r['seq_result']} nseq_result={r['nseq_result']}")
+
+if disagreements:
+ print(f"\nDISAGREEMENTS ({len(disagreements)}):")
+ for r in disagreements[:10]:
+ print(f" {r['file']} seq={r['seq_result']} nseq={r['nseq_result']}")
+PYEOF
+
+python3 /tmp/analyze_benchmark.py
+```
+
+## Phase 7: Create GitHub Discussion
+
+Use the `create_discussion` safe-output tool to post a structured benchmark report.
+
+The discussion body should be formatted as follows (fill in real numbers from Phase 6):
```markdown
-### ZIPT Benchmark Report — Z3 c3 branch
+# QF_S Benchmark: seq vs nseq
-**Date**:
+**Date**: YYYY-MM-DD
**Branch**: c3
-**Benchmark set**: QF_S (200 randomly selected files from tests/QF_S.tar.zst)
-**Timeout**: 5 seconds for seq (`-T:5`); 5 seconds for nseq (`-T:5`) and ZIPT (`-t:5000`)
+**Commit**: ``
+**Workflow Run**: [#](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
+**Files benchmarked**: N (capped at 300, timeout 5 s per file)
---
-### Summary
+## Summary
-| Metric | seq solver | nseq solver | ZIPT solver |
-|--------|-----------|-------------|-------------|
-| sat | X | X | X |
-| unsat | X | X | X |
-| unknown | X | X | X |
-| timeout | X | X | X |
-| bug/crash | X | X | X |
-| **Total time (s)** | X.XXX | X.XXX | X.XXX |
-| **Avg time/benchmark (s)** | X.XXX | X.XXX | X.XXX |
-
-**Soundness disagreements** (any two solvers return conflicting sat/unsat): N
+| Metric | seq | nseq |
+|--------|-----|------|
+| Files solved (sat/unsat) | SEQ_SOLVED | NSEQ_SOLVED |
+| Timeouts | SEQ_TO | NSEQ_TO |
+| Median solve time (solved files) | X ms | Y ms |
+| Mean solve time (solved files) | X ms | Y ms |
+| **Disagreements (sat≠unsat)** | — | N |
---
-### Per-File Results
+## Performance Comparison
-| # | File | seq verdict | seq time (s) | nseq verdict | nseq time (s) | ZIPT verdict | ZIPT time (s) | Notes |
-|---|------|-------------|-------------|--------------|--------------|--------------|--------------|-------|
-| 1 | benchmark_0001.smt2 | sat | 0.123 | sat | 0.456 | sat | 0.789 | |
-| ... | ... | ... | ... | ... | ... | ... | ... | ... |
+### seq-fast / nseq-slow (seq < 2 s, nseq timed out)
+
+These are benchmarks where the classical `seq` solver is significantly faster. These represent regression risk for `nseq`.
+
+| File | seq (ms) | nseq (ms) | seq result | nseq result |
+|------|----------|-----------|------------|-------------|
+[TOP 10 ENTRIES]
+
+### nseq-fast / seq-slow (nseq < 2 s, seq timed out)
+
+These are benchmarks where `nseq` shows a performance advantage.
+
+| File | seq (ms) | nseq (ms) | seq result | nseq result |
+|------|----------|-----------|------------|-------------|
+[TOP 10 ENTRIES]
---
-### Notable Issues
+## Correctness
-#### Soundness Disagreements (Critical)
-
+**Disagreements** (files where seq says `sat` but nseq says `unsat` or vice versa): N
-#### Crashes / Bugs
-
-
-#### Slow Benchmarks (> 8s)
-
-
-#### Trace Analysis: seq-fast / nseq-slow Hypotheses
- 3× longer, write a 3–5 sentence hypothesis based on the trace excerpt, referencing specific trace entries where possible. If no such files were found, state "No seq-fast / nseq-slow cases were observed in this run.">
+[If disagreements exist, list all of them here with file paths and both results]
---
-*Generated automatically by the ZIPT Benchmark workflow on the c3 branch.*
+## seq Trace Analysis (seq-fast / nseq-slow cases)
+
+
+Click to expand trace snippets for top seq-fast/nseq-slow cases
+
+[Insert trace snippet for each traced file, or "No traces collected" if section was skipped]
+
+
+
+---
+
+## Raw Data
+
+
+Full results CSV (click to expand)
+
+```csv
+[PASTE FIRST 200 LINES OF /tmp/benchmark-results.csv]
```
-## Phase 5: Post to GitHub Discussion
+
-Post the Markdown report as a new GitHub Discussion using the `create-discussion` safe output.
+---
-- **Category**: "Agentic Workflows"
-- **Title**: `[ZIPT Benchmark] Z3 c3 branch — `
-- Close older discussions with the same title prefix to avoid clutter.
+*Generated by the QF_S Benchmark workflow. To reproduce: build Z3 from the `c3` branch and run `z3 smt.string_solver=seq|nseq -T:10 `.*
+```
-## Guidelines
+## Edge Cases
-- **Always build from c3 branch**: The workspace is already checked out on c3; don't change branches.
-- **Debug build required**: The build must use `CMAKE_BUILD_TYPE=Debug` so that Z3's internal assertions and trace infrastructure are active; `-tr:` trace flags have no effect in Release builds.
-- **Tracing time cap**: Always pass `-T:5` when running with `-tr:seq` to limit solver runtime and keep trace files a manageable size. The nseq and ZIPT runs use `-T:5` / `-t:5000` as before.
-- **Rename trace files immediately**: After each seq run, rename `.z3-trace` to a per-benchmark path before starting the next run, or the next invocation will overwrite it.
-- **Handle build failures gracefully**: If Z3 fails to build, report the error and create a brief discussion noting the build failure. If ZIPT fails to build, continue with only the seq/nseq columns and note `n/a` for ZIPT results.
-- **Handle missing zstd**: If `tar --zstd` fails, try `zstd -d tests/QF_S.tar.zst --stdout | tar -x -C /tmp/qfs_benchmarks`.
-- **Be precise with timing**: Use millisecond-precision timestamps and report times in seconds with 3 decimal places.
-- **Distinguish timeout from unknown**: A timeout (process killed after 7s outer / 5s Z3-internal for seq, or 12s/10s for nseq) is different from `(unknown)` returned by a solver.
-- **ZIPT timeout unit**: ZIPT's `-t` flag takes **milliseconds**, so pass `-t:5000` for a 5-second limit.
-- **ZIPT output format**: ZIPT prints the input filename on the first line, then `SAT`, `UNSAT`, or `UNKNOWN` on subsequent lines. Parse accordingly.
-- **Report soundness bugs prominently**: If any benchmark shows a conflict between any two solvers that both returned a definitive sat/unsat answer, highlight it as a critical finding and name which pair disagrees.
-- **Don't skip any file**: Run all 200 files even if some fail.
-- **Large report**: If the per-file table is very long, put it in a `` collapsible section.
+- If the build fails, call `missing_data` explaining the build error and stop.
+- If no benchmark files are found at all, call `missing_data` explaining that no QF_S `.smt2` files were found in the `c3` branch.
+- If Z3 crashes (segfault) on a file with either solver, record the result as `crash` and continue.
+- If the total benchmark set is very small (< 5 files), note this prominently in the discussion and suggest adding more QF_S benchmarks to the `c3` branch.
+- If zero disagreements and both solvers time out on the same files, note that the solvers are in agreement.
+- If `create_discussion` fails (e.g., MCP session error), call `report_incomplete` with the reason and include the top-line statistics (files solved, timeouts, disagreement count) in the `details` field.
+
+## Important Notes
+
+- **DO NOT** modify any source files or create pull requests.
+- **DO NOT** run `ninja` or any build command in the background with `&` — concurrent C++ compilation and LLM inference can exhaust available RAM and kill the agent process. Always wait for build commands to complete before proceeding.
+- **DO NOT** run benchmarks for longer than 100 minutes total (leave buffer for posting).
+- **DO** always report the commit SHA so results can be correlated with specific code versions.
+- **DO** close older QF_S Benchmark discussions automatically (configured via `close-older-discussions: true`).
+- **DO** highlight disagreements prominently — these are potential correctness bugs.
diff --git a/.github/workflows/release-notes-updater.md b/.github/workflows/release-notes-updater.md
index b77f47099..b0c6860ad 100644
--- a/.github/workflows/release-notes-updater.md
+++ b/.github/workflows/release-notes-updater.md
@@ -16,8 +16,6 @@ tools:
toolsets: [default]
bash: [":*"]
edit: {}
- glob: {}
- view: {}
safe-outputs:
create-discussion:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 712490003..275207b28 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -46,6 +46,19 @@ jobs:
- name: Build
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=x64
+ - name: Validate libz3.dylib and z3 architecture (must be x86_64)
+ run: |
+ set -e
+ for f in build-dist/libz3.dylib build-dist/z3; do
+ ARCH=$(lipo -archs "$f")
+ echo "$f architecture: $ARCH"
+ if [ "$ARCH" != "x86_64" ]; then
+ echo "ERROR: $f has arch '$ARCH', expected 'x86_64' (see issue #9662)"
+ exit 1
+ fi
+ done
+ echo "OK: macOS x64 artifacts are x86_64"
+
- name: Clone z3test
run: git clone https://github.com/z3prover/z3test z3test
@@ -53,7 +66,7 @@ jobs:
run: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: macOsBuild
path: dist/*.zip
@@ -75,11 +88,24 @@ jobs:
- name: Build
run: python scripts/mk_unix_dist.py --dotnet-key=$GITHUB_WORKSPACE/resources/z3.snk --arch=arm64
+ - name: Validate libz3.dylib and z3 architecture (must be arm64)
+ run: |
+ set -e
+ for f in build-dist/libz3.dylib build-dist/z3; do
+ ARCH=$(lipo -archs "$f")
+ echo "$f architecture: $ARCH"
+ if [ "$ARCH" != "arm64" ]; then
+ echo "ERROR: $f has arch '$ARCH', expected 'arm64' (see issue #9662)"
+ exit 1
+ fi
+ done
+ echo "OK: macOS arm64 artifacts are arm64"
+
- name: Clone z3test
run: git clone https://github.com/z3prover/z3test z3test
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: MacArm64
path: dist/*.zip
@@ -111,6 +137,17 @@ jobs:
Z3_DIR=$(find . -maxdepth 1 -type d -name "z3-*" | head -n 1)
echo "Z3_DIR=$Z3_DIR" >> $GITHUB_ENV
+ - name: Validate shipped libz3.dylib architecture (must be x86_64)
+ run: |
+ set -e
+ DYLIB="artifacts/$Z3_DIR/bin/libz3.dylib"
+ ARCH=$(lipo -archs "$DYLIB")
+ echo "Shipped $DYLIB architecture: $ARCH"
+ if [ "$ARCH" != "x86_64" ]; then
+ echo "ERROR: x64 release zip contains '$ARCH' libz3.dylib (see issue #9662)"
+ exit 1
+ fi
+
- name: Test install_name_tool with headerpad
run: |
cd artifacts/$Z3_DIR/bin
@@ -159,6 +196,17 @@ jobs:
Z3_DIR=$(find . -maxdepth 1 -type d -name "z3-*" | head -n 1)
echo "Z3_DIR=$Z3_DIR" >> $GITHUB_ENV
+ - name: Validate shipped libz3.dylib architecture (must be arm64)
+ run: |
+ set -e
+ DYLIB="artifacts/$Z3_DIR/bin/libz3.dylib"
+ ARCH=$(lipo -archs "$DYLIB")
+ echo "Shipped $DYLIB architecture: $ARCH"
+ if [ "$ARCH" != "arm64" ]; then
+ echo "ERROR: arm64 release zip contains '$ARCH' libz3.dylib (see issue #9662)"
+ exit 1
+ fi
+
- name: Test install_name_tool with headerpad
run: |
cd artifacts/$Z3_DIR/bin
@@ -208,7 +256,7 @@ jobs:
run: python z3test/scripts/test_benchmarks.py build-dist/z3 z3test/regressions/smt2
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: UbuntuBuild
path: dist/*.zip
@@ -243,7 +291,7 @@ jobs:
python scripts/mk_unix_dist.py --nodotnet --arch=arm64
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: UbuntuArm64
path: dist/*.zip
@@ -298,7 +346,7 @@ jobs:
run: zip -r z3doc.zip doc/api
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: UbuntuDoc
path: z3doc.zip
@@ -313,9 +361,17 @@ jobs:
- name: Checkout code
uses: actions/checkout@v6.0.2
+ - name: Select Python
+ run: |
+ # Use the first available manylinux interpreter for deterministic selection.
+ PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
+ test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
+ echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
+ "$PYTHON" --version
+
- name: Setup Python environment
run: |
- /opt/python/cp38-cp38/bin/python -m venv $PWD/env
+ "$PYTHON" -m venv $PWD/env
echo "$PWD/env/bin" >> $GITHUB_PATH
- name: Install build tools
@@ -328,7 +384,7 @@ jobs:
run: pip install ./src/api/python/wheelhouse/*.whl && python - > "$GITHUB_ENV"
+ "$PYTHON" --version
+
- name: Setup Python environment
run: |
- /opt/python/cp38-cp38/bin/python -m venv $PWD/env
+ "$PYTHON" -m venv $PWD/env
echo "$PWD/env/bin" >> $GITHUB_PATH
echo "/tmp/arm-toolchain/bin" >> $GITHUB_PATH
echo "/tmp/arm-toolchain/aarch64-none-linux-gnu/libc/usr/bin" >> $GITHUB_PATH
@@ -368,12 +432,120 @@ jobs:
run: cd src/api/python && CC=aarch64-none-linux-gnu-gcc CXX=aarch64-none-linux-gnu-g++ AR=aarch64-none-linux-gnu-ar LD=aarch64-none-linux-gnu-ld Z3_CROSS_COMPILING=aarch64 python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../..
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: ManyLinuxPythonBuildArm64
path: src/api/python/wheelhouse/*.whl
retention-days: 7
+ manylinux-python-riscv64:
+ name: "Python bindings (manylinux RISC-V 64 cross)"
+ runs-on: ubuntu-latest
+ timeout-minutes: 90
+ container: quay.io/pypa/manylinux_2_28_x86_64:latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6.0.2
+
+ - name: Download RISC-V toolchain
+ run: curl -L -o /tmp/riscv-toolchain.tar.gz 'https://github.com/riscv-collab/riscv-gnu-toolchain/releases/download/2024.09.03/riscv64-glibc-ubuntu-20.04-gcc-nightly-2024.09.03-nightly.tar.gz'
+
+ - name: Extract RISC-V toolchain
+ run: |
+ mkdir -p /tmp/riscv-toolchain/
+ tar xf /tmp/riscv-toolchain.tar.gz -C /tmp/riscv-toolchain/ --strip-components=1
+
+ - name: Install MPFR 4 (required by RISC-V toolchain host binaries)
+ run: |
+ dnf install -y gmp-devel
+ curl -L -o /tmp/mpfr.tar.xz https://ftp.gnu.org/gnu/mpfr/mpfr-4.2.1.tar.xz
+ tar xf /tmp/mpfr.tar.xz -C /tmp/
+ cd /tmp/mpfr-4.2.1 && ./configure --prefix=/usr/local --disable-static && make -j$(nproc) && make install
+ ldconfig
+
+ - name: Select Python
+ run: |
+ # Use the first available manylinux interpreter for deterministic selection.
+ PYTHON=$(printf '%s\n' /opt/python/*/bin/python | sort -V | head -n1)
+ test -x "$PYTHON" || { echo "Error: no interpreter found under /opt/python/*/bin/python"; exit 1; }
+ echo "PYTHON=$PYTHON" >> "$GITHUB_ENV"
+ "$PYTHON" --version
+
+ - name: Setup Python environment
+ run: |
+ "$PYTHON" -m venv $PWD/env
+ echo "$PWD/env/bin" >> $GITHUB_PATH
+ echo "/tmp/riscv-toolchain/bin" >> $GITHUB_PATH
+
+ - name: Install build tools
+ run: |
+ echo $PATH
+ stat $(which riscv64-unknown-linux-gnu-gcc)
+ pip install build git+https://github.com/rhelmot/auditwheel
+
+ - name: Build wheels
+ run: cd src/api/python && CC=riscv64-unknown-linux-gnu-gcc CXX=riscv64-unknown-linux-gnu-g++ AR=riscv64-unknown-linux-gnu-ar LD=riscv64-unknown-linux-gnu-ld Z3_CROSS_COMPILING=riscv64 python -m build && AUDITWHEEL_PLAT= auditwheel repair --best-plat dist/*.whl && cd ../../..
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v7.0.1
+ with:
+ name: ManyLinuxPythonBuildRiscv64
+ path: src/api/python/wheelhouse/*.whl
+ retention-days: 7
+
+ pyodide-python:
+ name: "Python bindings (Pyodide)"
+ runs-on: ubuntu-24.04
+ timeout-minutes: 90
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6.0.2
+
+ - name: Setup packages
+ run: sudo apt-get update && sudo apt-get install -y python3-dev python3-pip python3-venv
+
+ - name: Create venv
+ run: python3 -m venv ~/env
+
+ - name: Install pyodide
+ run: ~/env/bin/pip install pyodide-build pyodide-cli
+
+ - name: Configure Emscripten
+ run: |
+ git clone https://github.com/emscripten-core/emsdk.git ~/emsdk
+ cd ~/emsdk
+ PYODIDE_EMSCRIPTEN_VERSION=$(~/env/bin/pyodide config get emscripten_version)
+ ./emsdk install ${PYODIDE_EMSCRIPTEN_VERSION}
+ ./emsdk activate ${PYODIDE_EMSCRIPTEN_VERSION}
+
+ - name: Build wheel
+ run: |
+ source ~/emsdk/emsdk_env.sh
+ cd src/api/python
+ CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" CXXFLAGS="${CXXFLAGS}" ~/env/bin/pyodide build --exports whole_archive
+ env:
+ CFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0 -g2"
+ LDFLAGS: "-fexceptions -s WASM_BIGINT"
+ CXXFLAGS: "-fexceptions -s DISABLE_EXCEPTION_CATCHING=0"
+
+ - name: Setup env-pyodide
+ run: |
+ source ~/env/bin/activate
+ source ~/emsdk/emsdk_env.sh
+ pyodide venv ~/env-pyodide
+
+ - name: Test wheel
+ run: |
+ ~/env-pyodide/bin/pip install src/api/python/dist/*.whl
+ ~/env-pyodide/bin/python src/api/python/z3test.py z3
+
+ - name: Upload artifact
+ uses: actions/upload-artifact@v7.0.1
+ with:
+ name: PyodidePythonBuild
+ path: src/api/python/dist/*.whl
+ retention-days: 7
+
windows-build-x64:
name: "Windows x64 build"
runs-on: windows-latest
@@ -394,7 +566,7 @@ jobs:
python scripts\mk_win_dist.py --x64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: WindowsBuild-x64
path: dist/*.zip
@@ -420,7 +592,7 @@ jobs:
python scripts\mk_win_dist.py --x86-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --zip
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: WindowsBuild-x86
path: dist/*.zip
@@ -446,7 +618,7 @@ jobs:
python scripts\mk_win_dist_cmake.py --arm64-only --dotnet-key=%GITHUB_WORKSPACE%\resources\z3.snk --assembly-version=${{ env.RELEASE_VERSION }} --zip
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: WindowsBuild-arm64
path: dist/arm64/*.zip
@@ -506,7 +678,7 @@ jobs:
path: package
- name: Setup NuGet
- uses: nuget/setup-nuget@v2
+ uses: nuget/setup-nuget@v4
with:
nuget-version: 'latest'
@@ -523,7 +695,7 @@ jobs:
nuget pack out\Microsoft.Z3.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: NuGet
path: |
@@ -551,7 +723,7 @@ jobs:
path: package
- name: Setup NuGet
- uses: nuget/setup-nuget@v2
+ uses: nuget/setup-nuget@v4
with:
nuget-version: 'latest'
@@ -568,7 +740,7 @@ jobs:
nuget pack out\Microsoft.Z3.x86.sym.nuspec -OutputDirectory . -Verbosity detailed -Symbols -SymbolPackageFormat snupkg -BasePath out
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: NuGet32
path: |
@@ -578,7 +750,7 @@ jobs:
python-package:
name: "Python packaging"
- needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64]
+ needs: [mac-build-x64, mac-build-arm64, windows-build-x64, windows-build-x86, windows-build-arm64, manylinux-python-amd64, manylinux-python-arm64, manylinux-python-riscv64, pyodide-python]
runs-on: ubuntu-24.04
steps:
- name: Checkout code
@@ -631,6 +803,18 @@ jobs:
name: ManyLinuxPythonBuildArm64
path: artifacts
+ - name: Download ManyLinux RISC-V 64 Build
+ uses: actions/download-artifact@v8.0.1
+ with:
+ name: ManyLinuxPythonBuildRiscv64
+ path: artifacts
+
+ - name: Download Pyodide Build
+ uses: actions/download-artifact@v8.0.1
+ with:
+ name: PyodidePythonBuild
+ path: artifacts
+
- name: Extract builds
run: |
cd artifacts
@@ -658,7 +842,7 @@ jobs:
cp artifacts/*.whl src/api/python/dist/.
- name: Upload artifact
- uses: actions/upload-artifact@v7
+ uses: actions/upload-artifact@v7.0.1
with:
name: PythonPackage
path: src/api/python/dist/*
@@ -760,7 +944,7 @@ jobs:
path: packages
- name: Setup NuGet
- uses: nuget/setup-nuget@v2
+ uses: nuget/setup-nuget@v4
with:
nuget-version: 'latest'
diff --git a/.github/workflows/smtlib-benchmark-finder.lock.yml b/.github/workflows/smtlib-benchmark-finder.lock.yml
new file mode 100644
index 000000000..2b397404b
--- /dev/null
+++ b/.github/workflows/smtlib-benchmark-finder.lock.yml
@@ -0,0 +1,1442 @@
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"7fe62b76e1ec94b221dbb20fe5cd392a7a47a296f9da44c7405c343a2ca3c3e9","compiler_version":"v0.74.8","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/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"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-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"v0.74.8","version":"v0.74.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.49"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.9","digest":"sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388"},{"image":"ghcr.io/github/github-mcp-server:v1.0.4"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]}
+# ___ _ _
+# / _ \ | | (_)
+# | |_| | __ _ ___ _ __ | |_ _ ___
+# | _ |/ _` |/ _ \ '_ \| __| |/ __|
+# | | | | (_| | __/ | | | |_| | (__
+# \_| |_/\__, |\___|_| |_|\__|_|\___|
+# __/ |
+# _ _ |___/
+# | | | | / _| |
+# | | | | ___ _ __ _ __| |_| | _____ ____
+# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
+# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
+# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
+#
+# This file was automatically generated by gh-aw (v0.74.8). DO NOT EDIT.
+#
+# To update this file, edit the corresponding .md file and run:
+# gh aw compile
+# Not all edits will cause changes to this file.
+#
+# For more information: https://github.github.com/gh-aw/introduction/overview/
+#
+# Monthly SMTLIB Benchmark Finder. Searches GitHub for repositories containing SMT-LIB benchmarks (.smt2 files), excludes repositories that belong to the official SMT-LIB benchmark sets (linked from smtlib.org and hosted on Zenodo), and posts a curated summary of community-contributed benchmark links as a GitHub Discussion.
+#
+# Secrets used:
+# - COPILOT_GITHUB_TOKEN
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
+# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@v0.77.0
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.49
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.49
+# - ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388
+# - ghcr.io/github/github-mcp-server:v1.0.4
+# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
+
+name: "SMTLIB Benchmark Finder"
+on:
+ schedule:
+ - cron: "0 8 1 * *"
+ workflow_dispatch:
+ inputs:
+ aw_context:
+ default: ""
+ description: "Agent caller context (used internally by Agentic Workflows)."
+ required: false
+ type: string
+
+permissions: {}
+
+concurrency:
+ group: "gh-aw-${{ github.workflow }}"
+
+run-name: "SMTLIB Benchmark Finder"
+
+jobs:
+ activation:
+ runs-on: ubuntu-slim
+ permissions:
+ actions: read
+ contents: read
+ outputs:
+ comment_id: ""
+ comment_repo: ""
+ engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
+ model: ${{ steps.generate_aw_info.outputs.model }}
+ secret_verification_result: ${{ steps.validate-secret.outputs.verification_result }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ stale_lock_file_failed: ${{ steps.check-lock-file.outputs.stale_lock_file_failed == 'true' }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smtlib-benchmark-finder.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.48"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.5' }}
+ GH_AW_INFO_VERSION: "1.0.48"
+ GH_AW_INFO_AGENT_VERSION: "1.0.48"
+ GH_AW_INFO_CLI_VERSION: "v0.74.8"
+ GH_AW_INFO_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "false"
+ GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","github","smtlib.cs.uiowa.edu","zenodo.org"]'
+ GH_AW_INFO_FIREWALL_ENABLED: "true"
+ GH_AW_INFO_AWF_VERSION: "v0.25.49"
+ GH_AW_INFO_AWMG_VERSION: ""
+ GH_AW_INFO_FIREWALL_TYPE: "squid"
+ GH_AW_COMPILED_STRICT: "true"
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/generate_aw_info.cjs');
+ await main(core, context);
+ - name: Validate COPILOT_GITHUB_TOKEN secret
+ id: validate-secret
+ run: bash "${RUNNER_TEMP}/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: Checkout .github and .agents folders
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ sparse-checkout: |
+ .github
+ .agents
+ .claude
+ .codex
+ .crush
+ .gemini
+ .opencode
+ .pi
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Save agent config folders for base branch restoration
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_WORKFLOW_FILE: "smtlib-benchmark-finder.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
+ 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/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_COMPILED_VERSION: "v0.74.8"
+ 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/check_version_updates.cjs');
+ await main();
+ - name: Create prompt with built-in context
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ # poutine:ignore untrusted_checkout_exec
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
+ {
+ cat << 'GH_AW_PROMPT_04598545a7d250b1_EOF'
+
+ GH_AW_PROMPT_04598545a7d250b1_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/cache_memory_prompt.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
+ cat << 'GH_AW_PROMPT_04598545a7d250b1_EOF'
+
+ Tools: create_discussion, missing_tool, missing_data, noop
+
+ GH_AW_PROMPT_04598545a7d250b1_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
+ cat << 'GH_AW_PROMPT_04598545a7d250b1_EOF'
+
+ The following GitHub context information is available for this workflow:
+ {{#if github.actor}}
+ - **actor**: __GH_AW_GITHUB_ACTOR__
+ {{/if}}
+ {{#if github.repository}}
+ - **repository**: __GH_AW_GITHUB_REPOSITORY__
+ {{/if}}
+ {{#if github.workspace}}
+ - **workspace**: __GH_AW_GITHUB_WORKSPACE__
+ {{/if}}
+ {{#if github.event.issue.number || (github.aw.context.item_type == 'issue' && github.aw.context.item_number)}}
+ - **issue-number**: #__GH_AW_EXPR_802A9F6A__
+ {{/if}}
+ {{#if github.event.discussion.number || (github.aw.context.item_type == 'discussion' && github.aw.context.item_number)}}
+ - **discussion-number**: #__GH_AW_EXPR_1A3A194A__
+ {{/if}}
+ {{#if github.event.pull_request.number || (github.aw.context.item_type == 'pull_request' && github.aw.context.item_number)}}
+ - **pull-request-number**: #__GH_AW_EXPR_463A214A__
+ {{/if}}
+ {{#if github.event.comment.id || github.aw.context.comment_id}}
+ - **comment-id**: __GH_AW_EXPR_FF1D34CE__
+ {{/if}}
+ {{#if github.run_id}}
+ - **workflow-run-id**: __GH_AW_GITHUB_RUN_ID__
+ {{/if}}
+
+
+ GH_AW_PROMPT_04598545a7d250b1_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_04598545a7d250b1_EOF'
+
+ {{#runtime-import .github/workflows/smtlib-benchmark-finder.md}}
+ GH_AW_PROMPT_04598545a7d250b1_EOF
+ } > "$GH_AW_PROMPT"
+ - name: Interpolate variables and render templates
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
+ 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/interpolate_prompt.cjs');
+ await main();
+ - name: Substitute placeholders
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ALLOWED_EXTENSIONS: ''
+ GH_AW_CACHE_DESCRIPTION: ''
+ GH_AW_CACHE_DIR: '/tmp/gh-aw/cache-memory/'
+ GH_AW_EXPR_1A3A194A: ${{ github.event.discussion.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'discussion' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_463A214A: ${{ github.event.pull_request.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'pull_request' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_802A9F6A: ${{ github.event.issue.number || (fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_type == 'issue' && fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').item_number) }}
+ GH_AW_EXPR_FF1D34CE: ${{ github.event.comment.id || fromJSON(github.event.inputs.aw_context || github.event.client_payload.aw_context || '{}').comment_id }}
+ GH_AW_GITHUB_ACTOR: ${{ github.actor }}
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+
+ const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
+
+ // Call the substitution function
+ return await substitutePlaceholders({
+ file: process.env.GH_AW_PROMPT,
+ substitutions: {
+ GH_AW_ALLOWED_EXTENSIONS: process.env.GH_AW_ALLOWED_EXTENSIONS,
+ GH_AW_CACHE_DESCRIPTION: process.env.GH_AW_CACHE_DESCRIPTION,
+ GH_AW_CACHE_DIR: process.env.GH_AW_CACHE_DIR,
+ GH_AW_EXPR_1A3A194A: process.env.GH_AW_EXPR_1A3A194A,
+ GH_AW_EXPR_463A214A: process.env.GH_AW_EXPR_463A214A,
+ GH_AW_EXPR_802A9F6A: process.env.GH_AW_EXPR_802A9F6A,
+ GH_AW_EXPR_FF1D34CE: process.env.GH_AW_EXPR_FF1D34CE,
+ GH_AW_GITHUB_ACTOR: process.env.GH_AW_GITHUB_ACTOR,
+ 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_WORKFLOW: process.env.GH_AW_GITHUB_WORKFLOW,
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ }
+ });
+ - name: Validate prompt placeholders
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
+ - name: Print prompt
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
+ - name: Upload activation artifact
+ if: success()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: activation
+ include-hidden-files: true
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/aw-prompts/prompt-template.txt
+ /tmp/gh-aw/aw-prompts/prompt-import-tree.json
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/base
+ /tmp/gh-aw/.github/agents
+ if-no-files-found: ignore
+ retention-days: 1
+
+ agent:
+ needs: activation
+ runs-on: ubuntu-latest
+ permissions: read-all
+ concurrency:
+ group: "gh-aw-copilot-${{ github.workflow }}"
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ""
+ GH_AW_ASSETS_BRANCH: ""
+ GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ GH_AW_WORKFLOW_ID_SANITIZED: smtlibbenchmarkfinder
+ outputs:
+ agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }}
+ checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
+ effective_tokens_rate_limit_error: ${{ steps.parse-mcp-gateway.outputs.effective_tokens_rate_limit_error || 'false' }}
+ has_patch: ${{ steps.collect_output.outputs.has_patch }}
+ inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }}
+ model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }}
+ output: ${{ steps.collect_output.outputs.output }}
+ output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-parent-span-id: ${{ steps.setup.outputs.parent-span-id || steps.setup.outputs.span-id }}
+ setup-span-id: ${{ steps.setup.outputs.span-id }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smtlib-benchmark-finder.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.48"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
+ - name: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - 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 }}
+ # Cache memory file share configuration from frontmatter processed below
+ - name: Create cache-memory directory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
+ - name: Restore cache-memory file share data
+ uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+ with:
+ key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
+ path: /tmp/gh-aw/cache-memory
+ restore-keys: |
+ memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-
+ - name: Setup cache-memory git repository
+ env:
+ GH_AW_CACHE_DIR: /tmp/gh-aw/cache-memory
+ GH_AW_MIN_INTEGRITY: none
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/setup_cache_memory_git.sh"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # 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 || github.event.issue.pull_request
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
+ await main();
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 (source v9)
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: activation
+ path: /tmp/gh-aw
+ - name: Restore agent config folders from base branch
+ if: steps.checkout-pr.outcome == 'success'
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
+ - name: Restore inline sub-agents from activation artifact
+ env:
+ GH_AW_SUB_AGENT_DIR: ".github/agents"
+ GH_AW_SUB_AGENT_EXT: ".agent.md"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49 ghcr.io/github/gh-aw-mcpg:v0.3.9@sha256:64828b42a4482f58fab16509d7f8f495a6d97c972a98a68aff20543531ac0388 ghcr.io/github/github-mcp-server:v1.0.4 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
+ - name: Generate Safe Outputs Config
+ run: |
+ 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_1c2429a4603946af_EOF'
+ {"create_discussion":{"category":"agentic workflows","close_older_discussions":true,"expires":2160,"fallback_to_issue":true,"max":1,"title_prefix":"[SMT-LIB Benchmarks] "},"create_report_incomplete_issue":{},"max_bot_mentions":1,"mentions":{"enabled":false},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_1c2429a4603946af_EOF
+ - name: Generate Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[SMT-LIB Benchmarks] \". Discussions will be created in category \"agentic workflows\"."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "create_discussion": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "category": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "title": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 1024
+ }
+ }
+ }
+ }
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/generate_safe_outputs_tools.cjs');
+ await main();
+ - name: Generate Safe Outputs MCP Server Config
+ id: safe-outputs-config
+ run: |
+ # Generate a secure random API key (360 bits of entropy, 40+ chars)
+ # Mask immediately to prevent timing vulnerabilities
+ API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${API_KEY}"
+
+ PORT=3001
+
+ # Set outputs for next steps
+ {
+ echo "safe_outputs_api_key=${API_KEY}"
+ echo "safe_outputs_port=${PORT}"
+ } >> "$GITHUB_OUTPUT"
+
+ echo "Safe Outputs MCP server will run on port ${PORT}"
+
+ - name: Start Safe Outputs MCP HTTP Server
+ id: safe-outputs-start
+ env:
+ DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ run: |
+ # Environment variables are set above to prevent template injection
+ export DEBUG
+ export GH_AW_SAFE_OUTPUTS
+ export GH_AW_SAFE_OUTPUTS_PORT
+ export GH_AW_SAFE_OUTPUTS_API_KEY
+ export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
+ export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
+ export GH_AW_MCP_LOG_DIR
+
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
+
+ - name: Start MCP Gateway
+ id: start-mcp-gateway
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
+ 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 "${RUNNER_TEMP}/gh-aw/mcp-config"
+
+ # Export gateway environment variables for MCP config and gateway script
+ export MCP_GATEWAY_PORT="8080"
+ export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ export MCP_GATEWAY_HOST_DOMAIN="localhost"
+ MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${MCP_GATEWAY_API_KEY}"
+ export MCP_GATEWAY_API_KEY
+ export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
+ mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
+ export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
+ export DEBUG="*"
+
+ export GH_AW_ENGINE="copilot"
+ MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
+ MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
+ case "${DOCKER_HOST:-}" in
+ unix://* ) DOCKER_SOCK_PATH="${DOCKER_HOST#unix://}" ;;
+ /* ) DOCKER_SOCK_PATH="$DOCKER_HOST" ;;
+ * ) DOCKER_SOCK_PATH=/var/run/docker.sock ;;
+ esac
+ DOCKER_SOCK_GID=$(stat -c '%g' "$DOCKER_SOCK_PATH" 2>/dev/null || echo '0')
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -v '"${DOCKER_SOCK_PATH}"':/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 DOCKER_HOST=unix:///var/run/docker.sock -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.3.9'
+
+ 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_07d3b8ec96f0806f_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ {
+ "mcpServers": {
+ "github": {
+ "type": "stdio",
+ "container": "ghcr.io/github/github-mcp-server:v1.0.4",
+ "env": {
+ "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
+ "GITHUB_READ_ONLY": "1",
+ "GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
+ }
+ },
+ "safeoutputs": {
+ "type": "http",
+ "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
+ "headers": {
+ "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
+ }
+ },
+ "gateway": {
+ "port": $MCP_GATEWAY_PORT,
+ "domain": "${MCP_GATEWAY_DOMAIN}",
+ "apiKey": "${MCP_GATEWAY_API_KEY}",
+ "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
+ }
+ }
+ GH_AW_MCP_CONFIG_07d3b8ec96f0806f_EOF
+ - name: Mount MCP servers as CLIs
+ id: mount-mcp-clis
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
+ await main();
+ - name: Clean credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Audit pre-agent workspace
+ id: pre_agent_audit
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
+ - name: Execute GitHub Copilot CLI
+ id: agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 60
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["*.githubusercontent.com","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","codeload.github.com","crl.geotrust.com","crl.globalsign.com","crl.identrust.com","crl.sectigo.com","crl.thawte.com","crl.usertrust.com","crl.verisign.com","crl3.digicert.com","crl4.digicert.com","crls.ssl.com","docs.github.com","github-cloud.githubusercontent.com","github-cloud.s3.amazonaws.com","github.blog","github.com","github.githubassets.com","host.docker.internal","json-schema.org","json.schemastore.org","keyserver.ubuntu.com","lfs.github.com","objects.githubusercontent.com","ocsp.digicert.com","ocsp.geotrust.com","ocsp.globalsign.com","ocsp.identrust.com","ocsp.sectigo.com","ocsp.ssl.com","ocsp.thawte.com","ocsp.usertrust.com","ocsp.verisign.com","packagecloud.io","packages.cloud.google.com","packages.microsoft.com","patch-diff.githubusercontent.com","ppa.launchpad.net","raw.githubusercontent.com","registry.npmjs.org","s.symcb.com","s.symcd.com","security.ubuntu.com","smtlib.cs.uiowa.edu","telemetry.enterprise.githubcopilot.com","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com","zenodo.org"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000,"models":{"agent":["sonnet-6x","gpt-5.4","gpt-5","gemini-pro","haiku","any"],"any":["copilot/*","anthropic/*","openai/*","google/*","gemini/*"],"auto":["large"],"claude":["agent","sonnet-6x","haiku","any"],"codex":["agent","gpt-5-codex","gpt-5","any"],"coding":["copilot/gpt-5*codex*","openai/gpt-5*codex*","gpt-5-codex"],"copilot":["agent","gpt-5.4","sonnet","gpt-5","any"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","gemini/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini":["agent","gemini-pro","gemini-flash","any"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*","gemini/gemini-*flash*"],"gemini-flash-lite":["copilot/gemini-*flash*lite*","google/gemini-*flash*lite*","gemini/gemini-*flash*lite*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*","gemini/gemini-*pro*"],"gemma":["copilot/gemma*","google/gemma*","gemini/gemma*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash-lite","copilot/raptor*mini*"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"],"sonnet-6x":["copilot/*sonnet-4.5*","copilot/*sonnet-4-5*","anthropic/*sonnet-4.5*","anthropic/*sonnet-4-5*","copilot/*sonnet-3.7*","copilot/*sonnet-3-7*","anthropic/*sonnet-3.7*","anthropic/*sonnet-3-7*","copilot/*sonnet-3.5*","copilot/*sonnet-3-5*","anthropic/*sonnet-3.5*","anthropic/*sonnet-3-5*"],"vision":["copilot/gemini-*image*","gemini/gemini-*image*","copilot/gemini-*flash*","gemini/gemini-*flash*"]}},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.5' }}
+ GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
+ GH_AW_PHASE: agent
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_VERSION: v0.74.8
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Detect Copilot errors
+ id: detect-copilot-errors
+ if: always()
+ continue-on-error: true
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # 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: Copy Copilot session state files to logs
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
+ - 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 "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
+ - name: Redact secrets in logs
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/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: Append agent step summary
+ if: always()
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
+ if: always()
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
+ - name: Ingest agent output
+ id: collect_output
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,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,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,smtlib.cs.uiowa.edu,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,zenodo.org"
+ GH_AW_ALLOWED_GITHUB_REFS: ""
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ 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/collect_ndjson_output.cjs');
+ await main();
+ - name: Parse agent logs for step summary
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
+ 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/parse_copilot_log.cjs');
+ await main();
+ - name: Parse MCP Gateway logs for step summary
+ if: always()
+ id: parse-mcp-gateway
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/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/audit dirs so they can be uploaded as artifacts
+ # AWF runs with sudo, creating files owned by root
+ sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
+ # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
+ if command -v awf &> /dev/null; then
+ awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
+ else
+ echo 'AWF binary not installed, skipping firewall log summary'
+ fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/parse_token_usage.cjs');
+ await main();
+ - name: Print AWF reflect summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/awf_reflect_summary.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
+ - name: Commit cache-memory changes
+ if: always()
+ env:
+ GH_AW_CACHE_DIR: /tmp/gh-aw/cache-memory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/commit_cache_memory_git.sh"
+ - name: Upload cache-memory data as artifact
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ if: always()
+ with:
+ name: cache-memory
+ include-hidden-files: true
+ path: /tmp/gh-aw/cache-memory
+ - name: Upload agent artifacts
+ if: always()
+ continue-on-error: true
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: agent
+ path: |
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
+ /tmp/gh-aw/mcp-logs/
+ /tmp/gh-aw/agent_usage.json
+ /tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/pre-agent-audit.txt
+ /tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/aw-*.patch
+ /tmp/gh-aw/aw-*.bundle
+ /tmp/gh-aw/awf-config.json
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ /tmp/gh-aw/sandbox/firewall/awf-reflect.json
+ if-no-files-found: ignore
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - detection
+ - safe_outputs
+ - update_cache_memory
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ concurrency:
+ group: "gh-aw-conclusion-smtlib-benchmark-finder"
+ cancel-in-progress: false
+ queue: max
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smtlib-benchmark-finder.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.48"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ 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_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "false"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_noop_message.cjs');
+ await main();
+ - name: Log detection run
+ id: detection_runs
+ 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_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_detection_runs.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ 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_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_MISSING_TOOL_TITLE_PREFIX: "[missing tool]"
+ GH_AW_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ 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_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/report_incomplete_handler.cjs');
+ await main();
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ 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_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "smtlib-benchmark-finder"
+ GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens || '' }}
+ GH_AW_EFFECTIVE_TOKENS_RATE_LIMIT_ERROR: ${{ needs.agent.outputs.effective_tokens_rate_limit_error || 'false' }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
+ GH_AW_CREATE_DISCUSSION_ERRORS: ${{ needs.safe_outputs.outputs.create_discussion_errors }}
+ GH_AW_CREATE_DISCUSSION_ERROR_COUNT: ${{ needs.safe_outputs.outputs.create_discussion_error_count }}
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
+ GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
+ GH_AW_TIMEOUT_MINUTES: "60"
+ GH_AW_MAX_EFFECTIVE_TOKENS: "25000000"
+ GH_AW_CACHE_MEMORY_ENABLED: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_agent_failure.cjs');
+ await main();
+
+ detection:
+ needs:
+ - activation
+ - agent
+ if: >
+ always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smtlib-benchmark-finder.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.48"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Checkout repository for patch context
+ if: needs.agent.outputs.has_patch == 'true'
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ # --- Threat Detection ---
+ - name: Clean stale firewall files from agent artifact
+ run: |
+ rm -rf /tmp/gh-aw/sandbox/firewall/logs
+ rm -rf /tmp/gh-aw/sandbox/firewall/audit
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.49 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.49 ghcr.io/github/gh-aw-firewall/squid:0.25.49
+ - name: Check if detection needed
+ id: detection_guard
+ if: always()
+ env:
+ OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ run: |
+ if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
+ echo "run_detection=true" >> "$GITHUB_OUTPUT"
+ echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
+ else
+ echo "run_detection=false" >> "$GITHUB_OUTPUT"
+ echo "Detection skipped: no agent outputs or patches to analyze"
+ fi
+ - name: Clear MCP Config for detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
+ rm -f /home/runner/.copilot/mcp-config.json
+ rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
+ - name: Prepare threat detection files
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
+ cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
+ cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
+ for f in /tmp/gh-aw/aw-*.patch; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ for f in /tmp/gh-aw/aw-*.bundle; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ echo "Prepared threat detection files:"
+ ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ - name: Setup threat detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ WORKFLOW_DESCRIPTION: "Monthly SMTLIB Benchmark Finder. Searches GitHub for repositories containing SMT-LIB benchmarks (.smt2 files), excludes repositories that belong to the official SMT-LIB benchmark sets (linked from smtlib.org and hosted on Zenodo), and posts a curated summary of community-contributed benchmark links as a GitHub Discussion."
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ 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/setup_threat_detection.cjs');
+ await main();
+ - name: Ensure threat-detection directory and log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection
+ touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Setup Node.js
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: '24'
+ package-manager-cache: false
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.48
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.49
+ - name: Execute GitHub Copilot CLI
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ continue-on-error: true
+ id: detection_agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ printf '%s' "$(date +%s%3N)" > /tmp/gh-aw/agent_cli_start_ms.txt
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ export COPILOT_API_KEY="$COPILOT_DUMMY_BYOK"
+ (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.49/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true,"enableTokenSteering":true,"maxRuns":500,"maxEffectiveTokens":25000000},"container":{"imageTag":"0.25.49"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json"
+ cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS=""
+ if [[ "${DOCKER_HOST:-}" =~ ^tcp:// ]]; then
+ GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS="--docker-host-path-prefix /tmp/gh-aw"
+ fi
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" ${GH_AW_DOCKER_HOST_PATH_PREFIX_ARGS} --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 5 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || true)"; fi; if [ -z "$GH_AW_NODE_EXEC" ]; then echo "node runtime missing on this runner — check runtimes.node in workflow YAML" >&2; exit 127; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_DUMMY_BYOK: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.5' }}
+ GH_AW_PHASE: detection
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_VERSION: v0.74.8
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Upload threat detection log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: detection
+ path: /tmp/gh-aw/threat-detection/detection.log
+ if-no-files-found: ignore
+ - name: Parse and conclude threat detection
+ id: detection_conclusion
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
+ DETECTION_AGENTIC_EXECUTION_OUTCOME: ${{ steps.detection_agentic_execution.outcome }}
+ GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
+ with:
+ script: |
+ try {
+ 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/parse_threat_detection_results.cjs');
+ await main();
+ } catch (loadErr) {
+ const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
+ const detectionExecutionFailed = process.env.DETECTION_AGENTIC_EXECUTION_OUTCOME === 'failure';
+ const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
+ core.error(msg);
+ core.setOutput('reason', 'parse_error');
+ if (continueOnError && !detectionExecutionFailed) {
+ core.warning('\u26A0\uFE0F ' + msg);
+ core.setOutput('conclusion', 'warning');
+ core.setOutput('success', 'false');
+ } else {
+ core.setOutput('conclusion', 'failure');
+ core.setOutput('success', 'false');
+ core.setFailed(msg);
+ }
+ }
+
+ safe_outputs:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/smtlib-benchmark-finder"
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ GH_AW_ENGINE_VERSION: "1.0.48"
+ GH_AW_WORKFLOW_ID: "smtlib-benchmark-finder"
+ GH_AW_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ outputs:
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smtlib-benchmark-finder.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.48"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - 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"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ 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_COMMENT_ID: ${{ needs.activation.outputs.comment_id }}
+ GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,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,codeload.github.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,docs.github.com,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.blog,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,patch-diff.githubusercontent.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,smtlib.cs.uiowa.edu,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com,zenodo.org"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"agentic workflows\",\"close_older_discussions\":true,\"expires\":2160,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[SMT-LIB Benchmarks] \"},\"create_report_incomplete_issue\":{},\"mentions\":{\"enabled\":false},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/safe_output_handler_manager.cjs');
+ await main();
+ - name: Upload Safe Outputs Items
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: safe-outputs-items
+ path: |
+ /tmp/gh-aw/safe-output-items.jsonl
+ /tmp/gh-aw/temporary-id-map.json
+ if-no-files-found: ignore
+
+ update_cache_memory:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: >
+ always() && (needs.detection.result == 'success' || needs.detection.result == 'skipped') &&
+ needs.agent.result == 'success'
+ runs-on: ubuntu-slim
+ permissions: {}
+ env:
+ GH_AW_WORKFLOW_ID_SANITIZED: smtlibbenchmarkfinder
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ parent-span-id: ${{ needs.activation.outputs.setup-parent-span-id || needs.activation.outputs.setup-span-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "SMTLIB Benchmark Finder"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/smtlib-benchmark-finder.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.48"
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ - name: Download cache-memory artifact (default)
+ id: download_cache_default
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ continue-on-error: true
+ with:
+ name: cache-memory
+ path: /tmp/gh-aw/cache-memory
+ - name: Check if cache-memory folder has content (default)
+ id: check_cache_default
+ shell: bash
+ run: |
+ if [ -d "/tmp/gh-aw/cache-memory" ] && [ "$(ls -A /tmp/gh-aw/cache-memory 2>/dev/null)" ]; then
+ echo "has_content=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "has_content=false" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Save cache-memory to cache (default)
+ if: steps.check_cache_default.outputs.has_content == 'true'
+ uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+ with:
+ key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
+ path: /tmp/gh-aw/cache-memory
+
diff --git a/.github/workflows/smtlib-benchmark-finder.md b/.github/workflows/smtlib-benchmark-finder.md
new file mode 100644
index 000000000..19c615493
--- /dev/null
+++ b/.github/workflows/smtlib-benchmark-finder.md
@@ -0,0 +1,342 @@
+---
+description: >
+ Monthly SMTLIB Benchmark Finder.
+ Searches GitHub for repositories containing SMT-LIB benchmarks (.smt2 files),
+ excludes repositories that belong to the official SMT-LIB benchmark sets
+ (linked from smtlib.org and hosted on Zenodo), and posts a curated summary
+ of community-contributed benchmark links as a GitHub Discussion.
+
+on:
+ schedule:
+ - cron: "0 8 1 * *"
+ workflow_dispatch:
+
+timeout-minutes: 60
+
+permissions: read-all
+
+network:
+ allowed:
+ - defaults
+ - github
+ - smtlib.cs.uiowa.edu
+ - zenodo.org
+
+tools:
+ cache-memory: true
+ web-fetch: {}
+ github:
+ toolsets: [default, repos]
+ bash: [":*"]
+
+safe-outputs:
+ mentions: false
+ allowed-github-references: []
+ max-bot-mentions: 1
+ create-discussion:
+ title-prefix: "[SMT-LIB Benchmarks] "
+ category: "Agentic Workflows"
+ close-older-discussions: true
+ expires: 90d
+ missing-tool:
+ create-issue: true
+ noop:
+ report-as-issue: false
+
+---
+
+# SMTLIB Benchmark Finder
+
+## Job Description
+
+Your name is ${{ github.workflow }}. You are a research analyst for the Z3 theorem
+prover repository `${{ github.repository }}`. Your mission is to discover GitHub
+repositories that host SMT-LIB benchmarks, exclude the ones that are already part
+of the official SMT-LIB benchmark distribution (linked from smtlib.org and published
+on Zenodo), and post a curated summary of community-contributed benchmark links as a
+GitHub Discussion.
+
+## Step 1: Load Cache and Determine Run Mode
+
+Check cache memory for:
+- `official_repos`: set of GitHub repository full names (`owner/repo`) and Zenodo
+ record IDs already identified as official SMT-LIB benchmark sets
+- `known_community_repos`: set of repo full names already listed in a previous report
+- `last_run_date`: ISO-8601 date string of the previous run
+
+Use the cache to skip repos already classified. On the very first run (no cache),
+perform a full discovery pass. On subsequent runs focus on repos pushed or created
+since `last_run_date`.
+
+## Step 2: Collect Official SMT-LIB Benchmark Sets to Exclude
+
+### 2.1 Scrape smtlib.org
+
+Fetch the SMT-LIB benchmarks page and extract all linked Zenodo DOIs and GitHub URLs:
+
+```bash
+curl -s "https://smtlib.cs.uiowa.edu/benchmarks.shtml" -o /tmp/smtlib-benchmarks.html
+# Also try the main page and any mirror
+curl -s "https://smtlib.cs.uiowa.edu/" -o /tmp/smtlib-home.html
+```
+
+Parse both files for:
+- Zenodo DOI links (`doi.org/10.5281/zenodo.*` or `zenodo.org/record/*`)
+- GitHub repository URLs (`github.com/...`)
+- Any other hosted benchmark archive links
+
+### 2.2 Enumerate Zenodo SMT-LIB Community Records
+
+Query the Zenodo API for all records in the SMT-LIB community:
+
+```bash
+curl -s "https://zenodo.org/api/records?communities=smt-lib&size=100&page=1" \
+ -o /tmp/zenodo-smtlib-page1.json
+
+# Check if there are more pages (paginate until empty)
+curl -s "https://zenodo.org/api/records?communities=smt-lib&size=100&page=2" \
+ -o /tmp/zenodo-smtlib-page2.json 2>/dev/null || true
+
+curl -s "https://zenodo.org/api/records?communities=smt-lib&size=100&page=3" \
+ -o /tmp/zenodo-smtlib-page3.json 2>/dev/null || true
+```
+
+For each Zenodo record extract:
+- Record ID (e.g. `5827900`)
+- Title
+- Any GitHub repository URLs listed in the description or related identifiers
+
+```bash
+python3 - <<'PYEOF'
+import json, re
+
+def extract_github_repos(text):
+ pattern = r'github\.com/([A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+)'
+ return set(re.findall(pattern, text or ''))
+
+official_repos = set()
+official_zenodo_ids = set()
+
+for fname in ['/tmp/zenodo-smtlib-page1.json', '/tmp/zenodo-smtlib-page2.json',
+ '/tmp/zenodo-smtlib-page3.json']:
+ try:
+ data = json.load(open(fname))
+ except Exception:
+ continue
+ for hit in data.get('hits', {}).get('hits', []):
+ rid = str(hit.get('id', ''))
+ official_zenodo_ids.add(rid)
+ metadata = hit.get('metadata', {})
+ description = metadata.get('description', '')
+ related = ' '.join(
+ r.get('identifier', '')
+ for r in metadata.get('related_identifiers', [])
+ )
+ title = metadata.get('title', '')
+ for repo in extract_github_repos(description + ' ' + related + ' ' + title):
+ official_repos.add(repo.lower().rstrip('.'))
+
+with open('/tmp/smtlib-benchmarks.html') as f:
+ html = f.read()
+for repo in extract_github_repos(html):
+ official_repos.add(repo.lower().rstrip('.'))
+
+print("OFFICIAL_ZENODO_IDS:", ','.join(sorted(official_zenodo_ids)) or '(none)')
+print("OFFICIAL_GITHUB_REPOS:", ','.join(sorted(official_repos)) or '(none)')
+PYEOF
+```
+
+### 2.3 Well-Known Official Repository Patterns
+
+Regardless of the above scrape, always exclude:
+- Any repo under the `SMT-LIB` GitHub organization (`SMT-LIB/*`)
+- Any repo whose name matches `smt-comp-*` that is under `SMT-Competition` org
+- The Z3 repo itself (`Z3Prover/z3`) and its forks
+
+Combine all official sources into a single exclusion set stored at
+`/tmp/official_exclusions.txt` (one `owner/repo` pattern per line, lowercase).
+
+## Step 3: Search GitHub for Community SMT-LIB Benchmark Repositories
+
+Use multiple GitHub search strategies to find repos containing `.smt2` benchmark
+files that are NOT part of the official distribution. Search for repos updated
+since the last run date (or the last 90 days for the initial run).
+
+Compute the cutoff date first:
+```bash
+CUTOFF=$(date -d "90 days ago" +%Y-%m-%d 2>/dev/null || date -v-90d +%Y-%m-%d)
+echo "Using cutoff date: $CUTOFF"
+```
+
+### Search Strategies
+
+Use the GitHub MCP server tools to run these searches:
+
+1. **Topic search**: `topic:smtlib pushed:>$CUTOFF`
+2. **Topic search variant**: `topic:smt-lib pushed:>$CUTOFF`
+3. **Topic search variant**: `topic:smt2 pushed:>$CUTOFF`
+4. **Benchmark filename pattern**: `filename:*.smt2 pushed:>$CUTOFF` (limit to top results)
+5. **Benchmarks directory pattern**: `path:benchmarks *.smt2 in:path pushed:>$CUTOFF`
+6. **README mention**: `SMT-LIB benchmarks in:readme stars:>2 pushed:>$CUTOFF`
+7. **Organization-level search**: repos under `SMT-Competition` org, if any are not already excluded
+
+For each search, collect: `full_name`, `html_url`, `description`, `stargazers_count`,
+`updated_at`, `pushed_at`, `default_branch`, `topics`.
+
+Deduplicate by `full_name`. Limit to 200 total candidates before filtering.
+
+## Step 4: Filter Out Official Benchmark Sets
+
+For each candidate repo:
+
+1. Check if `full_name.lower()` is in the exclusion set from Step 2.
+2. Check if the repo is owned by `SMT-LIB` or `SMT-Competition` org (case-insensitive).
+3. Check if the repo is a fork of a known official repo (if `fork: true` and parent is
+ in the exclusion set).
+
+Discard repos that match any of the above. Keep the rest as community benchmarks.
+
+Also apply quality filters to reduce noise — skip repos that:
+- Have 0 stars and fewer than 3 `.smt2` files (likely a student homework or test repo
+ with minimal public value); use your judgement — if the repo description clearly
+ describes a research benchmark, keep it regardless of star count.
+- Were created but never pushed after creation (empty repos).
+- Have names that are clearly course assignment repositories
+ (e.g. contain `homework`, `assignment`, `hw[0-9]`, `cs[0-9]{3}`).
+
+## Step 5: Classify Remaining Repos
+
+For each repo that survives filtering, classify it into one of these categories:
+
+| Category | Description |
+|----------|-------------|
+| **Solver evaluation** | Benchmarks used to evaluate or compare SMT solvers |
+| **Verification** | Benchmarks from program verification or model checking |
+| **Security / CTF** | Benchmarks from security research or CTF challenges |
+| **Theory / logic** | Benchmarks exploring specific SMT theories or logics |
+| **Tool output** | Benchmarks generated by another tool (e.g. a compiler, fuzzer) |
+| **Education** | Course materials or tutorials with benchmark examples |
+| **Other / unknown** | Does not fit another category |
+
+Base the classification on: repo description, README (fetch if web-fetch is available),
+topics, and directory structure. A single brief `web-fetch` of the repo's README is
+sufficient; do not fetch individual `.smt2` files.
+
+Note the dominant SMT logic(s) present, if discernible from the description or topics
+(e.g. QF_BV, QF_LIA, QF_S, NIA, …).
+
+## Step 6: Generate the Discussion Report
+
+Create a GitHub Discussion. Use heading level 3 or deeper (`###`, `####`, …) for all
+section headers; never use `##` or `#` in the body.
+Wrap long tables in `` tags to keep the report scannable.
+
+Title: `[SMT-LIB Benchmarks] Community Benchmark Repository Survey — [Month YYYY]`
+
+Structure the report as follows:
+
+```markdown
+**Period covered**: [cutoff date] – [today's date]
+**Repositories found**: N community repos (after excluding M official sets)
+**New this run**: N (not listed in previous report)
+
+### Overview
+
+1–2 sentences summarising the breadth of community SMT-LIB benchmarks found.
+
+### Community Benchmark Repositories
+
+Use `###` for category headers. Within each category, list repos as a markdown table.
+For each repo include:
+- Repo link (`[owner/repo](html_url)`)
+- Stars
+- Last pushed date
+- Dominant logic(s) (if known)
+- Brief description (from repo description or README, max 120 chars)
+
+#### Solver Evaluation
+
+| Repository | ⭐ | Last pushed | Logic(s) | Description |
+|------------|-----|------------|---------|-------------|
+| [owner/repo](url) | N | YYYY-MM-DD | QF_BV, QF_LIA | … |
+
+#### Verification
+
+[same table structure]
+
+#### Security / CTF
+
+[same table structure]
+
+#### Theory / Logic
+
+[same table structure]
+
+#### Tool Output
+
+[same table structure]
+
+#### Education
+
+[same table structure]
+
+#### Other / Unknown
+
+[same table structure]
+
+---
+
+### Exclusions Applied
+
+
+Official SMT-LIB sets excluded from this report
+
+List Zenodo record IDs and GitHub repos identified as official distributions.
+
+| Source | Identifier | Notes |
+|--------|-----------|-------|
+| Zenodo | [10.5281/zenodo.XXXXXXX](https://zenodo.org/record/XXXXXXX) | Official QF_BV benchmark set |
+| GitHub | [SMT-LIB/benchmarks-non-incremental](https://github.com/SMT-LIB/benchmarks-non-incremental) | |
+
+
+
+---
+
+### Methodology
+
+Brief note on search queries used, cutoff date, and any quality filters applied.
+```
+
+## Step 7: Update Cache Memory
+
+After posting the discussion, update cache memory with:
+- `official_repos`: updated exclusion set (union of previous + newly found)
+- `known_community_repos`: union of previous + repos listed in this report
+- `last_run_date`: today's ISO-8601 date
+- `report_url`: URL of the GitHub Discussion created
+
+## Guidelines
+
+- **Be conservative with exclusions**: when in doubt whether a repo is "official",
+ keep it in the community list rather than silently dropping it.
+- **Be accurate**: only include repos that genuinely contain SMT-LIB `.smt2` files
+ or clearly describe themselves as SMT-LIB benchmark collections.
+- **Avoid noise**: student homework repos and trivially small repos add clutter;
+ apply the quality filters from Step 4 judiciously.
+- **No source code changes**: DO NOT create pull requests or modify any source files.
+- **No copyrighted content**: DO NOT reproduce benchmark file contents; only post
+ links and metadata.
+- **Always cite sources**: include the full GitHub URL for every listed repository.
+- **Use cache**: skip repos already classified in a previous run to keep runtime short.
+- **Fail gracefully**: if GitHub search rate-limits the workflow, post whatever was
+ collected so far with a note that the search was incomplete.
+
+## Important Notes
+
+- DO NOT create pull requests or modify source files.
+- DO close older SMT-LIB Benchmarks discussions automatically (configured).
+- DO always call `create_discussion` or `noop` before the workflow ends.
+ Failing to produce any safe output triggers an automatic failure issue.
+- DO use cache memory to avoid re-processing repos already surveyed.
+- DO limit individual `web-fetch` calls (README fetches) to repos where the
+ description alone is insufficient for classification.
diff --git a/.github/workflows/specbot-crash-analyzer.lock.yml b/.github/workflows/specbot-crash-analyzer.lock.yml
new file mode 100644
index 000000000..58fa451d5
--- /dev/null
+++ b/.github/workflows/specbot-crash-analyzer.lock.yml
@@ -0,0 +1,1429 @@
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"6ca984b05852b2ec7d6d294a8f22caa4fb6bba8af84d8c39be110bbd512d2db0","compiler_version":"v0.71.5","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/cache/restore","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"repo":"actions/cache/save","sha":"27d5ce7f107fe9357f9df03efb73ab90386fccae","version":"v5.0.5"},{"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"},{"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":"v0.71.5","version":"v0.71.5"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40","digest":"sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40","digest":"sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40","digest":"sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]}
+# ___ _ _
+# / _ \ | | (_)
+# | |_| | __ _ ___ _ __ | |_ _ ___
+# | _ |/ _` |/ _ \ '_ \| __| |/ __|
+# | | | | (_| | __/ | | | |_| | (__
+# \_| |_/\__, |\___|_| |_|\__|_|\___|
+# __/ |
+# _ _ |___/
+# | | | | / _| |
+# | | | | ___ _ __ _ __| |_| | _____ ____
+# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
+# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
+# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
+#
+# This file was automatically generated by gh-aw (v0.71.5). DO NOT EDIT.
+#
+# To update this file, edit the corresponding .md file and run:
+# gh aw compile
+# Not all edits will cause changes to this file.
+#
+# For more information: https://github.github.com/gh-aw/introduction/overview/
+#
+# Build Z3 in debug mode from the c3 branch, compile and run the specbot tests, identify root causes for any crashes, and post findings as a GitHub Discussion.
+#
+# Secrets used:
+# - COPILOT_GITHUB_TOKEN
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+# - actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@v0.77.0
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51
+# - ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c
+# - ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959
+# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
+
+name: "Specbot Crash Analyzer"
+"on":
+ workflow_dispatch:
+ inputs:
+ aw_context:
+ default: ""
+ description: Agent caller context (used internally by Agentic Workflows).
+ required: false
+ type: string
+
+permissions: {}
+
+concurrency:
+ group: "gh-aw-${{ github.workflow }}"
+
+run-name: "Specbot Crash Analyzer"
+
+jobs:
+ activation:
+ runs-on: ubuntu-slim
+ permissions:
+ actions: read
+ contents: read
+ outputs:
+ comment_id: ""
+ comment_repo: ""
+ engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
+ model: ${{ steps.generate_aw_info.outputs.model }}
+ 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' }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/specbot-crash-analyzer.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ GH_AW_INFO_AGENT_VERSION: "1.0.40"
+ GH_AW_INFO_CLI_VERSION: "v0.71.5"
+ GH_AW_INFO_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "false"
+ GH_AW_INFO_ALLOWED_DOMAINS: '["defaults"]'
+ GH_AW_INFO_FIREWALL_ENABLED: "true"
+ GH_AW_INFO_AWF_VERSION: "v0.25.40"
+ GH_AW_INFO_AWMG_VERSION: ""
+ GH_AW_INFO_FIREWALL_TYPE: "squid"
+ GH_AW_COMPILED_STRICT: "true"
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/generate_aw_info.cjs');
+ await main(core, context);
+ - name: Validate COPILOT_GITHUB_TOKEN secret
+ id: validate-secret
+ run: bash "${RUNNER_TEMP}/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: Checkout .github and .agents folders
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ sparse-checkout: |
+ .github
+ .agents
+ .claude
+ .codex
+ .crush
+ .gemini
+ .opencode
+ .pi
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Save agent config folders for base branch restoration
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_WORKFLOW_FILE: "specbot-crash-analyzer.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
+ 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/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_COMPILED_VERSION: "v0.71.5"
+ 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/check_version_updates.cjs');
+ await main();
+ - name: Create prompt with built-in context
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
+ 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_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ # poutine:ignore untrusted_checkout_exec
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
+ {
+ cat << 'GH_AW_PROMPT_3c664e92078139cb_EOF'
+
+ GH_AW_PROMPT_3c664e92078139cb_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/cache_memory_prompt.md"
+ cat "${RUNNER_TEMP}/gh-aw/prompts/safe_outputs_prompt.md"
+ cat << 'GH_AW_PROMPT_3c664e92078139cb_EOF'
+
+ Tools: create_discussion, missing_tool, missing_data, noop
+
+ GH_AW_PROMPT_3c664e92078139cb_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
+ cat << 'GH_AW_PROMPT_3c664e92078139cb_EOF'
+
+ 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}}
+
+
+ GH_AW_PROMPT_3c664e92078139cb_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_3c664e92078139cb_EOF'
+
+ {{#runtime-import .github/workflows/specbot-crash-analyzer.md}}
+ GH_AW_PROMPT_3c664e92078139cb_EOF
+ } > "$GH_AW_PROMPT"
+ - name: Interpolate variables and render templates
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ 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/interpolate_prompt.cjs');
+ await main();
+ - name: Substitute placeholders
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ALLOWED_EXTENSIONS: ''
+ GH_AW_CACHE_DESCRIPTION: ''
+ GH_AW_CACHE_DIR: '/tmp/gh-aw/cache-memory/'
+ 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_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+
+ const substitutePlaceholders = require('${{ runner.temp }}/gh-aw/actions/substitute_placeholders.cjs');
+
+ // Call the substitution function
+ return await substitutePlaceholders({
+ file: process.env.GH_AW_PROMPT,
+ substitutions: {
+ GH_AW_ALLOWED_EXTENSIONS: process.env.GH_AW_ALLOWED_EXTENSIONS,
+ GH_AW_CACHE_DESCRIPTION: process.env.GH_AW_CACHE_DESCRIPTION,
+ GH_AW_CACHE_DIR: process.env.GH_AW_CACHE_DIR,
+ 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_WORKFLOW: process.env.GH_AW_GITHUB_WORKFLOW,
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ }
+ });
+ - name: Validate prompt placeholders
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
+ - name: Print prompt
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
+ - name: Upload activation artifact
+ if: success()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: activation
+ include-hidden-files: true
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/base
+ if-no-files-found: ignore
+ retention-days: 1
+
+ agent:
+ needs: activation
+ runs-on: ubuntu-latest
+ permissions: read-all
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ""
+ GH_AW_ASSETS_BRANCH: ""
+ GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ GH_AW_WORKFLOW_ID_SANITIZED: specbotcrashanalyzer
+ outputs:
+ agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }}
+ checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
+ has_patch: ${{ steps.collect_output.outputs.has_patch }}
+ inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }}
+ model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }}
+ output: ${{ steps.collect_output.outputs.output }}
+ output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/specbot-crash-analyzer.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
+ - 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: Checkout c3 branch
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ ref: c3
+ - name: Install build dependencies
+ run: |
+ sudo apt-get update -y
+ sudo apt-get install -y cmake ninja-build python3 gcc g++ 2>&1 | tail -5
+ - continue-on-error: true
+ id: build-z3
+ name: Build Z3 in debug mode
+ run: |
+ mkdir -p build/debug specbot-results
+ cd build/debug
+ cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ../.. 2>&1 | tee ../../specbot-results/cmake.log
+ ninja 2>&1 | tee ../../specbot-results/build.log
+ BUILD_EXIT=$?
+ cd ../..
+ echo "build_exit=${BUILD_EXIT}" >> specbot-results/build-status.txt
+ ls -la build/debug/libz3* build/debug/*.so* 2>/dev/null >> specbot-results/build-status.txt || echo "Library not found" >> specbot-results/build-status.txt
+ exit $BUILD_EXIT
+ - continue-on-error: true
+ name: Compile specbot tests
+ run: "mkdir -p specbot-results\ngcc -g -O0 \\\n -I src/api \\\n specbot/test_specbot_seq.c \\\n -L build/debug \\\n -lz3 \\\n -Wl,-rpath,\"${GITHUB_WORKSPACE}/build/debug\" \\\n -o specbot-results/test_specbot_seq \\\n 2>&1 | tee specbot-results/compile_specbot_seq.log\necho \"compile_specbot_seq_exit=$?\" >> specbot-results/compile-status.txt\n\ngcc -g -O0 \\\n -I src/api \\\n specbot/test_deeptest_seq.c \\\n -L build/debug \\\n -lz3 \\\n -Wl,-rpath,\"${GITHUB_WORKSPACE}/build/debug\" \\\n -o specbot-results/test_deeptest_seq \\\n 2>&1 | tee specbot-results/compile_deeptest_seq.log\necho \"compile_deeptest_seq_exit=$?\" >> specbot-results/compile-status.txt\n"
+ - continue-on-error: true
+ name: Run specbot tests
+ run: |-
+ mkdir -p specbot-results
+ if [ -f specbot-results/test_specbot_seq ]; then
+ LD_LIBRARY_PATH="${GITHUB_WORKSPACE}/build/debug" timeout 120 specbot-results/test_specbot_seq > specbot-results/test_specbot_seq.log 2>&1
+ SPECBOT_EXIT=$?
+ echo "specbot_seq_exit=${SPECBOT_EXIT}" >> specbot-results/test-status.txt
+ else
+ echo "Binary not compiled" > specbot-results/test_specbot_seq.log
+ echo "specbot_seq_exit=127" >> specbot-results/test-status.txt
+ fi
+
+ if [ -f specbot-results/test_deeptest_seq ]; then
+ LD_LIBRARY_PATH="${GITHUB_WORKSPACE}/build/debug" timeout 120 specbot-results/test_deeptest_seq > specbot-results/test_deeptest_seq.log 2>&1
+ DEEPTEST_EXIT=$?
+ echo "deeptest_seq_exit=${DEEPTEST_EXIT}" >> specbot-results/test-status.txt
+ else
+ echo "Binary not compiled" > specbot-results/test_deeptest_seq.log
+ echo "deeptest_seq_exit=127" >> specbot-results/test-status.txt
+ fi
+
+ # Cache memory file share configuration from frontmatter processed below
+ - name: Create cache-memory directory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/create_cache_memory_dir.sh"
+ - name: Restore cache-memory file share data
+ uses: actions/cache/restore@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+ with:
+ key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
+ path: /tmp/gh-aw/cache-memory
+ restore-keys: |
+ memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-
+ - name: Setup cache-memory git repository
+ env:
+ GH_AW_CACHE_DIR: /tmp/gh-aw/cache-memory
+ GH_AW_MIN_INTEGRITY: none
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/setup_cache_memory_git.sh"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # 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 || github.event.issue.pull_request
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
+ await main();
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.40
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: activation
+ path: /tmp/gh-aw
+ - name: Restore agent config folders from base branch
+ if: steps.checkout-pr.outcome == 'success'
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280 ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51 ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
+ - name: Generate Safe Outputs Config
+ run: |
+ 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_466e88200fc203e0_EOF'
+ {"create_discussion":{"category":"agentic workflows","close_older_discussions":true,"expires":168,"fallback_to_issue":true,"max":1,"title_prefix":"[Specbot] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_466e88200fc203e0_EOF
+ - name: Generate Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[Specbot] \". Discussions will be created in category \"agentic workflows\"."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "create_discussion": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "category": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "title": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 1024
+ }
+ }
+ }
+ }
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/generate_safe_outputs_tools.cjs');
+ await main();
+ - name: Generate Safe Outputs MCP Server Config
+ id: safe-outputs-config
+ run: |
+ # Generate a secure random API key (360 bits of entropy, 40+ chars)
+ # Mask immediately to prevent timing vulnerabilities
+ API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${API_KEY}"
+
+ PORT=3001
+
+ # Set outputs for next steps
+ {
+ echo "safe_outputs_api_key=${API_KEY}"
+ echo "safe_outputs_port=${PORT}"
+ } >> "$GITHUB_OUTPUT"
+
+ echo "Safe Outputs MCP server will run on port ${PORT}"
+
+ - name: Start Safe Outputs MCP HTTP Server
+ id: safe-outputs-start
+ env:
+ DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ run: |
+ # Environment variables are set above to prevent template injection
+ export DEBUG
+ export GH_AW_SAFE_OUTPUTS
+ export GH_AW_SAFE_OUTPUTS_PORT
+ export GH_AW_SAFE_OUTPUTS_API_KEY
+ export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
+ export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
+ export GH_AW_MCP_LOG_DIR
+
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
+
+ - name: Start MCP Gateway
+ id: start-mcp-gateway
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
+ 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 "${RUNNER_TEMP}/gh-aw/mcp-config"
+
+ # Export gateway environment variables for MCP config and gateway script
+ export MCP_GATEWAY_PORT="8080"
+ export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ export MCP_GATEWAY_HOST_DOMAIN="localhost"
+ MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${MCP_GATEWAY_API_KEY}"
+ export MCP_GATEWAY_API_KEY
+ export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
+ mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
+ export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
+ export DEBUG="*"
+
+ export GH_AW_ENGINE="copilot"
+ MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
+ MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
+ DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0')
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -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.3.6'
+
+ 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_310ecb016ae7c835_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ {
+ "mcpServers": {
+ "github": {
+ "type": "stdio",
+ "container": "ghcr.io/github/github-mcp-server:v1.0.3",
+ "env": {
+ "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
+ "GITHUB_READ_ONLY": "1",
+ "GITHUB_TOOLSETS": "context,repos,issues,pull_requests,discussions"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
+ }
+ },
+ "safeoutputs": {
+ "type": "http",
+ "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
+ "headers": {
+ "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
+ }
+ },
+ "gateway": {
+ "port": $MCP_GATEWAY_PORT,
+ "domain": "${MCP_GATEWAY_DOMAIN}",
+ "apiKey": "${MCP_GATEWAY_API_KEY}",
+ "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
+ }
+ }
+ GH_AW_MCP_CONFIG_310ecb016ae7c835_EOF
+ - name: Mount MCP servers as CLIs
+ id: mount-mcp-clis
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
+ await main();
+ - name: Clean credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Audit pre-agent workspace
+ id: pre_agent_audit
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
+ - name: Execute GitHub Copilot CLI
+ id: agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 120
+ run: |
+ set -o pipefail
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.40/awf-config.schema.json","network":{"allowDomains":["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","www.googleapis.com"]},"apiProxy":{"enabled":true,"models":{"auto":["large"],"deep-research":["copilot/deep-research*","google/deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"]}},"container":{"imageTag":"0.25.40,squid=sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51,agent=sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504,api-proxy=sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280,cli-proxy=sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ env:
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_API_KEY: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
+ GH_AW_PHASE: agent
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_VERSION: v0.71.5
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Detect Copilot errors
+ id: detect-copilot-errors
+ if: always()
+ continue-on-error: true
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # 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: Copy Copilot session state files to logs
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
+ - 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 "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
+ - name: Redact secrets in logs
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/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: Append agent step summary
+ if: always()
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
+ if: always()
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
+ - name: Ingest agent output
+ id: collect_output
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_ALLOWED_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,www.googleapis.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ 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/collect_ndjson_output.cjs');
+ await main();
+ - name: Parse agent logs for step summary
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
+ 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/parse_copilot_log.cjs');
+ await main();
+ - name: Parse MCP Gateway logs for step summary
+ if: always()
+ id: parse-mcp-gateway
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/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/audit dirs 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 2>/dev/null || true
+ # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
+ if command -v awf &> /dev/null; then
+ awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
+ else
+ echo 'AWF binary not installed, skipping firewall log summary'
+ fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/parse_token_usage.cjs');
+ await main();
+ - name: Print AWF reflect summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/awf_reflect_summary.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
+ - name: Commit cache-memory changes
+ if: always()
+ env:
+ GH_AW_CACHE_DIR: /tmp/gh-aw/cache-memory
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/commit_cache_memory_git.sh"
+ - name: Upload cache-memory data as artifact
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ if: always()
+ with:
+ name: cache-memory
+ path: /tmp/gh-aw/cache-memory
+ - name: Upload agent artifacts
+ if: always()
+ continue-on-error: true
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: agent
+ path: |
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
+ /tmp/gh-aw/mcp-logs/
+ /tmp/gh-aw/agent_usage.json
+ /tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/pre-agent-audit.txt
+ /tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/aw-*.patch
+ /tmp/gh-aw/aw-*.bundle
+ /tmp/gh-aw/awf-config.json
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ /tmp/gh-aw/sandbox/firewall/awf-reflect.json
+ if-no-files-found: ignore
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - detection
+ - safe_outputs
+ - update_cache_memory
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ concurrency:
+ group: "gh-aw-conclusion-specbot-crash-analyzer"
+ cancel-in-progress: false
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/specbot-crash-analyzer.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ 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_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "false"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_noop_message.cjs');
+ await main();
+ - name: Log detection run
+ id: detection_runs
+ 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_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_detection_runs.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ 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_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_MISSING_TOOL_TITLE_PREFIX: "[missing tool]"
+ GH_AW_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ 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_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/report_incomplete_handler.cjs');
+ await main();
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ 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_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "specbot-crash-analyzer"
+ GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
+ GH_AW_CREATE_DISCUSSION_ERRORS: ${{ needs.safe_outputs.outputs.create_discussion_errors }}
+ GH_AW_CREATE_DISCUSSION_ERROR_COUNT: ${{ needs.safe_outputs.outputs.create_discussion_error_count }}
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
+ GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
+ GH_AW_TIMEOUT_MINUTES: "120"
+ GH_AW_CACHE_MEMORY_ENABLED: "true"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_agent_failure.cjs');
+ await main();
+
+ detection:
+ needs:
+ - activation
+ - agent
+ if: >
+ always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/specbot-crash-analyzer.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Checkout repository for patch context
+ if: needs.agent.outputs.has_patch == 'true'
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ # --- Threat Detection ---
+ - name: Clean stale firewall files from agent artifact
+ run: |
+ rm -rf /tmp/gh-aw/sandbox/firewall/logs
+ rm -rf /tmp/gh-aw/sandbox/firewall/audit
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.40@sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.40@sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280 ghcr.io/github/gh-aw-firewall/squid:0.25.40@sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51
+ - name: Check if detection needed
+ id: detection_guard
+ if: always()
+ env:
+ OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ run: |
+ if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
+ echo "run_detection=true" >> "$GITHUB_OUTPUT"
+ echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
+ else
+ echo "run_detection=false" >> "$GITHUB_OUTPUT"
+ echo "Detection skipped: no agent outputs or patches to analyze"
+ fi
+ - name: Clear MCP Config for detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
+ rm -f /home/runner/.copilot/mcp-config.json
+ rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
+ - name: Prepare threat detection files
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
+ cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
+ cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
+ for f in /tmp/gh-aw/aw-*.patch; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ for f in /tmp/gh-aw/aw-*.bundle; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ echo "Prepared threat detection files:"
+ ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ - name: Setup threat detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ WORKFLOW_NAME: "Specbot Crash Analyzer"
+ WORKFLOW_DESCRIPTION: "Build Z3 in debug mode from the c3 branch, compile and run the specbot tests, identify root causes for any crashes, and post findings as a GitHub Discussion."
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ 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/setup_threat_detection.cjs');
+ await main();
+ - name: Ensure threat-detection directory and log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection
+ touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Setup Node.js
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: '24'
+ package-manager-cache: false
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.40
+ - name: Execute GitHub Copilot CLI
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ continue-on-error: true
+ id: detection_agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.40/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.40,squid=sha256:b084f4a2c771f584ee68084ced52fa6b3245197a1889645d817462d307d3ac51,agent=sha256:14ff567e8d9d4c2fbc5e55c973488381c71d7e0fdbe72d30ee7b8a738fd86504,api-proxy=sha256:2883ca3e5ae9f330cafdd9345bfd4ae17fc8da36c96d4c9a1f76e922b4c45280,cli-proxy=sha256:3e7152911d4b4b7b97beef9d3d7d924ff7902227e86001ef3838fb728d5d514c"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
+ env:
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_API_KEY: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_PHASE: detection
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_VERSION: v0.71.5
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Upload threat detection log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: detection
+ path: /tmp/gh-aw/threat-detection/detection.log
+ if-no-files-found: ignore
+ - name: Parse and conclude threat detection
+ id: detection_conclusion
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
+ GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
+ with:
+ script: |
+ try {
+ 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/parse_threat_detection_results.cjs');
+ await main();
+ } catch (loadErr) {
+ const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
+ const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
+ core.error(msg);
+ core.setOutput('reason', 'parse_error');
+ if (continueOnError) {
+ core.warning('\u26A0\uFE0F ' + msg);
+ core.setOutput('conclusion', 'warning');
+ core.setOutput('success', 'false');
+ } else {
+ core.setOutput('conclusion', 'failure');
+ core.setOutput('success', 'false');
+ core.setFailed(msg);
+ }
+ }
+
+ safe_outputs:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/specbot-crash-analyzer"
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ GH_AW_ENGINE_VERSION: "1.0.40"
+ GH_AW_WORKFLOW_ID: "specbot-crash-analyzer"
+ GH_AW_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ outputs:
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/specbot-crash-analyzer.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - 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"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ 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_ALLOWED_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,www.googleapis.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"agentic workflows\",\"close_older_discussions\":true,\"expires\":168,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[Specbot] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/safe_output_handler_manager.cjs');
+ await main();
+ - name: Upload Safe Outputs Items
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: safe-outputs-items
+ path: |
+ /tmp/gh-aw/safe-output-items.jsonl
+ /tmp/gh-aw/temporary-id-map.json
+ if-no-files-found: ignore
+
+ update_cache_memory:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: >
+ always() && (needs.detection.result == 'success' || needs.detection.result == 'skipped') &&
+ needs.agent.result == 'success'
+ runs-on: ubuntu-slim
+ permissions: {}
+ env:
+ GH_AW_WORKFLOW_ID_SANITIZED: specbotcrashanalyzer
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "Specbot Crash Analyzer"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/specbot-crash-analyzer.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Download cache-memory artifact (default)
+ id: download_cache_default
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ continue-on-error: true
+ with:
+ name: cache-memory
+ path: /tmp/gh-aw/cache-memory
+ - name: Check if cache-memory folder has content (default)
+ id: check_cache_default
+ shell: bash
+ run: |
+ if [ -d "/tmp/gh-aw/cache-memory" ] && [ "$(ls -A /tmp/gh-aw/cache-memory 2>/dev/null)" ]; then
+ echo "has_content=true" >> "$GITHUB_OUTPUT"
+ else
+ echo "has_content=false" >> "$GITHUB_OUTPUT"
+ fi
+ - name: Save cache-memory to cache (default)
+ if: steps.check_cache_default.outputs.has_content == 'true'
+ uses: actions/cache/save@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
+ with:
+ key: memory-none-nopolicy-${{ env.GH_AW_WORKFLOW_ID_SANITIZED }}-${{ github.run_id }}
+ path: /tmp/gh-aw/cache-memory
+
diff --git a/.github/workflows/specbot-crash-analyzer.md b/.github/workflows/specbot-crash-analyzer.md
new file mode 100644
index 000000000..38d9af7ad
--- /dev/null
+++ b/.github/workflows/specbot-crash-analyzer.md
@@ -0,0 +1,247 @@
+---
+description: >
+ Build Z3 in debug mode from the c3 branch, compile and run the specbot tests,
+ identify root causes for any crashes, and post findings as a GitHub Discussion.
+
+on:
+ workflow_dispatch:
+
+timeout-minutes: 120
+
+permissions: read-all
+
+network: defaults
+
+tools:
+ cache-memory: true
+ github:
+ toolsets: [default, discussions]
+ bash: [":*"]
+ edit: {}
+
+safe-outputs:
+ create-discussion:
+ title-prefix: "[Specbot] "
+ category: "Agentic Workflows"
+ close-older-discussions: true
+ missing-tool:
+ create-issue: true
+ noop:
+ report-as-issue: false
+
+steps:
+ - name: Checkout c3 branch
+ uses: actions/checkout@v6.0.2
+ with:
+ ref: c3
+ persist-credentials: false
+
+ - name: Install build dependencies
+ run: |
+ sudo apt-get update -y
+ sudo apt-get install -y cmake ninja-build python3 gcc g++ 2>&1 | tail -5
+
+ - name: Build Z3 in debug mode
+ id: build-z3
+ continue-on-error: true
+ run: |
+ mkdir -p build/debug specbot-results
+ cd build/debug
+ cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ../.. 2>&1 | tee ../../specbot-results/cmake.log
+ ninja 2>&1 | tee ../../specbot-results/build.log
+ BUILD_EXIT=$?
+ cd ../..
+ echo "build_exit=${BUILD_EXIT}" >> specbot-results/build-status.txt
+ ls -la build/debug/libz3* build/debug/*.so* 2>/dev/null >> specbot-results/build-status.txt || echo "Library not found" >> specbot-results/build-status.txt
+ exit $BUILD_EXIT
+
+ - name: Compile specbot tests
+ continue-on-error: true
+ run: |
+ mkdir -p specbot-results
+ gcc -g -O0 \
+ -I src/api \
+ specbot/test_specbot_seq.c \
+ -L build/debug \
+ -lz3 \
+ -Wl,-rpath,"${GITHUB_WORKSPACE}/build/debug" \
+ -o specbot-results/test_specbot_seq \
+ 2>&1 | tee specbot-results/compile_specbot_seq.log
+ echo "compile_specbot_seq_exit=$?" >> specbot-results/compile-status.txt
+
+ gcc -g -O0 \
+ -I src/api \
+ specbot/test_deeptest_seq.c \
+ -L build/debug \
+ -lz3 \
+ -Wl,-rpath,"${GITHUB_WORKSPACE}/build/debug" \
+ -o specbot-results/test_deeptest_seq \
+ 2>&1 | tee specbot-results/compile_deeptest_seq.log
+ echo "compile_deeptest_seq_exit=$?" >> specbot-results/compile-status.txt
+
+ - name: Run specbot tests
+ continue-on-error: true
+ run: |
+ mkdir -p specbot-results
+ if [ -f specbot-results/test_specbot_seq ]; then
+ LD_LIBRARY_PATH="${GITHUB_WORKSPACE}/build/debug" timeout 120 specbot-results/test_specbot_seq > specbot-results/test_specbot_seq.log 2>&1
+ SPECBOT_EXIT=$?
+ echo "specbot_seq_exit=${SPECBOT_EXIT}" >> specbot-results/test-status.txt
+ else
+ echo "Binary not compiled" > specbot-results/test_specbot_seq.log
+ echo "specbot_seq_exit=127" >> specbot-results/test-status.txt
+ fi
+
+ if [ -f specbot-results/test_deeptest_seq ]; then
+ LD_LIBRARY_PATH="${GITHUB_WORKSPACE}/build/debug" timeout 120 specbot-results/test_deeptest_seq > specbot-results/test_deeptest_seq.log 2>&1
+ DEEPTEST_EXIT=$?
+ echo "deeptest_seq_exit=${DEEPTEST_EXIT}" >> specbot-results/test-status.txt
+ else
+ echo "Binary not compiled" > specbot-results/test_deeptest_seq.log
+ echo "deeptest_seq_exit=127" >> specbot-results/test-status.txt
+ fi
+
+---
+
+# Specbot Crash Analyzer
+
+## Job Description
+
+Your name is ${{ github.workflow }}. You are an expert C/C++ and SMT solver analyst for the Z3 theorem prover
+repository `${{ github.repository }}`. The pre-steps above have already built Z3 in debug mode from the `c3`
+branch, compiled and run the specbot test suite, and saved all output to the `specbot-results/` directory in
+the workspace (`${{ github.workspace }}/specbot-results/`). Your task is to analyze those results, diagnose
+any crash root causes by reading the relevant source files, and publish a structured findings report as a
+GitHub Discussion.
+
+**Do not try to build Z3 or run tests yourself.** All build and test output is already in `specbot-results/`.
+
+## Your Task
+
+### 1. Read the Pre-Generated Results
+
+All build and test outputs are in `specbot-results/` (relative to the workspace root). Read each file:
+
+```bash
+# Build status
+cat specbot-results/build-status.txt 2>/dev/null || echo "No build status"
+
+# Compile status
+cat specbot-results/compile-status.txt 2>/dev/null || echo "No compile status"
+
+# Test status
+cat specbot-results/test-status.txt 2>/dev/null || echo "No test status"
+
+# Test output from test_specbot_seq
+cat specbot-results/test_specbot_seq.log 2>/dev/null || echo "No test_specbot_seq output"
+
+# Test output from test_deeptest_seq
+cat specbot-results/test_deeptest_seq.log 2>/dev/null || echo "No test_deeptest_seq output"
+
+# Last 30 lines of the build log
+tail -30 specbot-results/build.log 2>/dev/null || echo "No build log"
+```
+
+If `specbot-results/build-status.txt` shows `build_exit=0`, the build succeeded.
+If it shows a non-zero exit, include the last 50 lines of `specbot-results/build.log` in the report
+under a "Build Failure" section.
+
+If `specbot-results/compile-status.txt` shows a non-zero exit for a test, include the compile error
+from `specbot-results/compile_specbot_seq.log` or `specbot-results/compile_deeptest_seq.log`.
+
+Collect every line containing `CRASH` or `ABORT` from the test log files — these are the crashes to analyze.
+
+### 2. Diagnose Each Crash
+
+For each crashed test function, perform the following analysis:
+
+1. **Identify the test body**: read `specbot/test_specbot_seq.c` or `specbot/test_deeptest_seq.c`
+ to understand what Z3 API calls the test makes and what invariants it exercises.
+
+2. **Find the likely crash site**: the test exercises the Z3 Nielsen/nseq string solver. Relevant source files are:
+ - `src/smt/seq_solver.h` and `src/smt/seq_solver.cpp` (or nearby files)
+ - `src/smt/seq_axioms.cpp`, `src/smt/seq_eq_solver.cpp`, `src/smt/seq_regex.cpp`
+ - `src/math/lp/` for length-arithmetic paths
+ - `src/api/z3_api.h` for the public API entry points
+
+ Use `grep` and `view` to locate assertion macros, `UNREACHABLE()`, `SASSERT`, or `throw` statements
+ in the code paths exercised by the failing test. Example:
+ ```bash
+ grep -rn "SASSERT\|UNREACHABLE\|Z3_CATCH" src/smt/seq_solver.cpp 2>/dev/null | head -30
+ ```
+
+3. **Hypothesize root cause**: based on the Z3 API calls in the test and the assertion/throw sites in
+ the solver source, state the most likely root cause. Common categories include:
+ - Violated invariant (SASSERT/UNREACHABLE hit due to unexpected solver state)
+ - Use-after-free or dangling reference during push/pop
+ - Unhandled edge case in Nielsen graph construction
+ - Missing theory-combination lemma between string length and integer arithmetic
+
+4. **Suggest a fix**: propose a minimal, concrete fix — e.g., a guard condition, an additional lemma,
+ a missing reference-count increment, or a missing case in a switch/match.
+
+### 3. Generate the Report
+
+After analyzing all crashes, produce a structured GitHub Discussion in the "Agentic Workflows" category
+using `create-discussion`.
+
+The discussion body must follow this structure (use `###` and lower for headers):
+
+```
+### Summary
+
+- Build: Debug (CMake + Ninja, c3 branch)
+- Tests compiled: N
+- Tests run: N
+- Tests passed: N
+- Tests crashed: N
+- Tests timed out: N
+
+### Crash Findings
+
+For each crash, one subsection:
+
+####
+
+**Test file**: `specbot/test_specbot_seq.c` or `specbot/test_deeptest_seq.c`
+
+**Observed failure**: ABORT/CRASH — one-line description of what was caught
+
+**Root cause hypothesis**: explanation of which assertion or code path was hit and why
+
+**Suggested fix**: concrete proposed change (file, function, what to add/change)
+
+---
+
+### Tests Passed
+
+List of test names that passed.
+
+
+Full Test Output
+
+Raw stdout/stderr from both test binaries.
+
+
+
+
+Build Log
+
+Last 30 lines of the ninja build output.
+
+
+```
+
+If there are no crashes at all, write a "No Crashes Found" summary celebrating that all tests passed,
+and include the full test output in a collapsible section.
+
+Use `mentions: false` behavior — do not mention any GitHub usernames in the report.
+
+Format workflow run references as: `[§${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})`.
+
+## Usage
+
+Trigger via **Actions → Specbot Crash Analyzer → Run workflow** on any branch. The pre-steps
+always check out the `c3` branch where `specbot/test_specbot_seq.c` and
+`specbot/test_deeptest_seq.c` live, build Z3, run the tests, and save results to `specbot-results/`.
+The agent then analyzes the results and posts a discussion to the "Agentic Workflows" category.
diff --git a/.github/workflows/tactic-to-simplifier.md b/.github/workflows/tactic-to-simplifier.md
index 56f698a0b..9c6faf8aa 100644
--- a/.github/workflows/tactic-to-simplifier.md
+++ b/.github/workflows/tactic-to-simplifier.md
@@ -19,8 +19,6 @@ tools:
github:
toolsets: [default]
bash: [":*"]
- glob: {}
- view: {}
safe-outputs:
create-issue:
diff --git a/.github/workflows/tptp-benchmark.lock.yml b/.github/workflows/tptp-benchmark.lock.yml
new file mode 100644
index 000000000..3b8a2c903
--- /dev/null
+++ b/.github/workflows/tptp-benchmark.lock.yml
@@ -0,0 +1,1323 @@
+# gh-aw-metadata: {"schema_version":"v3","frontmatter_hash":"f76e98e3954f6892f78728b22721e87d255ca21f122ab7076306b1ed8b5679b8","compiler_version":"v0.72.1","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"},{"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":"v0.72.1","version":"v0.72.1"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.25.41"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.25.41"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.6","digest":"sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c"},{"image":"ghcr.io/github/github-mcp-server:v1.0.3","digest":"sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959","pinned_image":"ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959"},{"image":"node:lts-alpine","digest":"sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f","pinned_image":"node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f"}]}
+# ___ _ _
+# / _ \ | | (_)
+# | |_| | __ _ ___ _ __ | |_ _ ___
+# | _ |/ _` |/ _ \ '_ \| __| |/ __|
+# | | | | (_| | __/ | | | |_| | (__
+# \_| |_/\__, |\___|_| |_|\__|_|\___|
+# __/ |
+# _ _ |___/
+# | | | | / _| |
+# | | | | ___ _ __ _ __| |_| | _____ ____
+# | |/\| |/ _ \ '__| |/ /| _| |/ _ \ \ /\ / / ___|
+# \ /\ / (_) | | | | ( | | | | (_) \ V V /\__ \
+# \/ \/ \___/|_| |_|\_\|_| |_|\___/ \_/\_/ |___/
+#
+# This file was automatically generated by gh-aw (v0.72.1). DO NOT EDIT.
+#
+# To update this file, edit the corresponding .md file and run:
+# gh aw compile
+# Not all edits will cause changes to this file.
+#
+# For more information: https://github.github.com/gh-aw/introduction/overview/
+#
+# Weekly benchmark of Z3's TPTP front-end against 500 random TPTP problems. Downloads TPTP benchmarks from tptp.org, resolves axiom dependencies, skips large problems, runs each with a 5-second timeout, and posts a discrepancy/crash report as a GitHub discussion.
+#
+# Secrets used:
+# - COPILOT_GITHUB_TOKEN
+# - GH_AW_GITHUB_MCP_SERVER_TOKEN
+# - GH_AW_GITHUB_TOKEN
+# - GITHUB_TOKEN
+#
+# Custom actions used:
+# - actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+# - actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
+# - actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+# - actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+# - actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+# - github/gh-aw-actions/setup@v0.77.0
+#
+# Container images used:
+# - ghcr.io/github/gh-aw-firewall/agent:0.25.41
+# - ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41
+# - ghcr.io/github/gh-aw-firewall/squid:0.25.41
+# - ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c
+# - ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959
+# - node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
+
+name: "TPTP Front-End Benchmark"
+"on":
+ schedule:
+ - cron: "0 6 * * 1"
+ workflow_dispatch:
+ inputs:
+ aw_context:
+ default: ""
+ description: Agent caller context (used internally by Agentic Workflows).
+ required: false
+ type: string
+
+permissions: {}
+
+concurrency:
+ group: "gh-aw-${{ github.workflow }}"
+
+run-name: "TPTP Front-End Benchmark"
+
+jobs:
+ activation:
+ runs-on: ubuntu-slim
+ permissions:
+ actions: read
+ contents: read
+ outputs:
+ comment_id: ""
+ comment_repo: ""
+ engine_id: ${{ steps.generate_aw_info.outputs.engine_id }}
+ lockdown_check_failed: ${{ steps.generate_aw_info.outputs.lockdown_check_failed == 'true' }}
+ model: ${{ steps.generate_aw_info.outputs.model }}
+ 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' }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/tptp-benchmark.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Generate agentic run info
+ id: generate_aw_info
+ env:
+ GH_AW_INFO_ENGINE_ID: "copilot"
+ GH_AW_INFO_ENGINE_NAME: "GitHub Copilot CLI"
+ GH_AW_INFO_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ GH_AW_INFO_AGENT_VERSION: "1.0.40"
+ GH_AW_INFO_CLI_VERSION: "v0.72.1"
+ GH_AW_INFO_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ GH_AW_INFO_EXPERIMENTAL: "false"
+ GH_AW_INFO_SUPPORTS_TOOLS_ALLOWLIST: "true"
+ GH_AW_INFO_STAGED: "false"
+ GH_AW_INFO_ALLOWED_DOMAINS: '["defaults","tptp.org"]'
+ GH_AW_INFO_FIREWALL_ENABLED: "true"
+ GH_AW_INFO_AWF_VERSION: "v0.25.41"
+ GH_AW_INFO_AWMG_VERSION: ""
+ GH_AW_INFO_FIREWALL_TYPE: "squid"
+ GH_AW_COMPILED_STRICT: "true"
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/generate_aw_info.cjs');
+ await main(core, context);
+ - name: Validate COPILOT_GITHUB_TOKEN secret
+ id: validate-secret
+ run: bash "${RUNNER_TEMP}/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: Checkout .github and .agents folders
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ sparse-checkout: |
+ .github
+ .agents
+ .claude
+ .codex
+ .crush
+ .gemini
+ .opencode
+ .pi
+ sparse-checkout-cone-mode: true
+ fetch-depth: 1
+ - name: Save agent config folders for base branch restoration
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/save_base_github_folders.sh"
+ - name: Check workflow lock file
+ id: check-lock-file
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_WORKFLOW_FILE: "tptp-benchmark.lock.yml"
+ GH_AW_CONTEXT_WORKFLOW_REF: "${{ github.workflow_ref }}"
+ 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/check_workflow_timestamp_api.cjs');
+ await main();
+ - name: Check compile-agentic version
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_COMPILED_VERSION: "v0.72.1"
+ 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/check_version_updates.cjs');
+ await main();
+ - name: Create prompt with built-in context
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ runner.temp }}/gh-aw/safeoutputs/outputs.jsonl
+ 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_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ # poutine:ignore untrusted_checkout_exec
+ run: |
+ bash "${RUNNER_TEMP}/gh-aw/actions/create_prompt_first.sh"
+ {
+ cat << 'GH_AW_PROMPT_0f8b33477e7f4791_EOF'
+
+ GH_AW_PROMPT_0f8b33477e7f4791_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_0f8b33477e7f4791_EOF'
+
+ Tools: create_discussion, missing_tool, missing_data, noop
+
+ GH_AW_PROMPT_0f8b33477e7f4791_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/mcp_cli_tools_prompt.md"
+ cat << 'GH_AW_PROMPT_0f8b33477e7f4791_EOF'
+
+ 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}}
+
+
+ GH_AW_PROMPT_0f8b33477e7f4791_EOF
+ cat "${RUNNER_TEMP}/gh-aw/prompts/github_mcp_tools_with_safeoutputs_prompt.md"
+ cat << 'GH_AW_PROMPT_0f8b33477e7f4791_EOF'
+
+ {{#runtime-import .github/workflows/tptp-benchmark.md}}
+ GH_AW_PROMPT_0f8b33477e7f4791_EOF
+ } > "$GH_AW_PROMPT"
+ - name: Interpolate variables and render templates
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_GITHUB_REPOSITORY: ${{ github.repository }}
+ GH_AW_GITHUB_RUN_ID: ${{ github.run_id }}
+ GH_AW_GITHUB_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ 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/interpolate_prompt.cjs');
+ await main();
+ - name: Substitute placeholders
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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_WORKFLOW: ${{ github.workflow }}
+ GH_AW_GITHUB_WORKSPACE: ${{ github.workspace }}
+ GH_AW_MCP_CLI_SERVERS_LIST: '- `safeoutputs` — run `safeoutputs --help` to see available tools'
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+
+ const substitutePlaceholders = require('${{ runner.temp }}/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_WORKFLOW: process.env.GH_AW_GITHUB_WORKFLOW,
+ GH_AW_GITHUB_WORKSPACE: process.env.GH_AW_GITHUB_WORKSPACE,
+ GH_AW_MCP_CLI_SERVERS_LIST: process.env.GH_AW_MCP_CLI_SERVERS_LIST
+ }
+ });
+ - name: Validate prompt placeholders
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/validate_prompt_placeholders.sh"
+ - name: Print prompt
+ env:
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ # poutine:ignore untrusted_checkout_exec
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/print_prompt_summary.sh"
+ - name: Upload activation artifact
+ if: success()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: activation
+ include-hidden-files: true
+ path: |
+ /tmp/gh-aw/aw_info.json
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/aw-prompts/prompt-template.txt
+ /tmp/gh-aw/aw-prompts/prompt-import-tree.json
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/base
+ /tmp/gh-aw/.github/agents
+ if-no-files-found: ignore
+ retention-days: 1
+
+ agent:
+ needs: activation
+ runs-on: ubuntu-latest
+ permissions: read-all
+ concurrency:
+ group: "gh-aw-copilot-${{ github.workflow }}"
+ env:
+ DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
+ GH_AW_ASSETS_ALLOWED_EXTS: ""
+ GH_AW_ASSETS_BRANCH: ""
+ GH_AW_ASSETS_MAX_SIZE_KB: 0
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ GH_AW_WORKFLOW_ID_SANITIZED: tptpbenchmark
+ outputs:
+ agentic_engine_timeout: ${{ steps.detect-copilot-errors.outputs.agentic_engine_timeout || 'false' }}
+ checkout_pr_success: ${{ steps.checkout-pr.outputs.checkout_pr_success || 'true' }}
+ effective_tokens: ${{ steps.parse-mcp-gateway.outputs.effective_tokens }}
+ has_patch: ${{ steps.collect_output.outputs.has_patch }}
+ inference_access_error: ${{ steps.detect-copilot-errors.outputs.inference_access_error || 'false' }}
+ mcp_policy_error: ${{ steps.detect-copilot-errors.outputs.mcp_policy_error || 'false' }}
+ model: ${{ needs.activation.outputs.model }}
+ model_not_supported_error: ${{ steps.detect-copilot-errors.outputs.model_not_supported_error || 'false' }}
+ output: ${{ steps.collect_output.outputs.output }}
+ output_types: ${{ steps.collect_output.outputs.output_types }}
+ setup-trace-id: ${{ steps.setup.outputs.trace-id }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/tptp-benchmark.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Set runtime paths
+ id: set-runtime-paths
+ run: |
+ {
+ echo "GH_AW_SAFE_OUTPUTS=${RUNNER_TEMP}/gh-aw/safeoutputs/outputs.jsonl"
+ echo "GH_AW_SAFE_OUTPUTS_CONFIG_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/config.json"
+ echo "GH_AW_SAFE_OUTPUTS_TOOLS_PATH=${RUNNER_TEMP}/gh-aw/safeoutputs/tools.json"
+ } >> "$GITHUB_OUTPUT"
+ - 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: Checkout repository
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ - name: Install build dependencies
+ run: |
+ sudo apt-get update -y -q
+ sudo apt-get install -y cmake ninja-build python3 wget curl bc
+ - name: Build Z3
+ run: "mkdir -p /tmp/z3-build\ncd /tmp/z3-build\ncmake \"$GITHUB_WORKSPACE\" \\\n -G Ninja \\\n -DCMAKE_BUILD_TYPE=Release \\\n -DZ3_BUILD_TEST_EXECUTABLES=OFF\nninja -j$(nproc) z3\n./z3 --version\n"
+
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # 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 || github.event.issue.pull_request
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.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('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io, getOctokit);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/checkout_pr_branch.cjs');
+ await main();
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.41
+ - name: Determine automatic lockdown mode for GitHub MCP Server
+ id: determine-automatic-lockdown
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9
+ env:
+ GH_AW_GITHUB_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN }}
+ GH_AW_GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN }}
+ with:
+ script: |
+ const determineAutomaticLockdown = require('${{ runner.temp }}/gh-aw/actions/determine_automatic_lockdown.cjs');
+ await determineAutomaticLockdown(github, context, core);
+ - name: Download activation artifact
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: activation
+ path: /tmp/gh-aw
+ - name: Restore agent config folders from base branch
+ if: steps.checkout-pr.outcome == 'success'
+ env:
+ GH_AW_AGENT_FOLDERS: ".agents .claude .codex .crush .gemini .github .opencode .pi"
+ GH_AW_AGENT_FILES: ".crush.json AGENTS.md CLAUDE.md GEMINI.md PI.md opencode.jsonc"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_base_github_folders.sh"
+ - name: Restore inline sub-agents from activation artifact
+ env:
+ GH_AW_SUB_AGENT_DIR: ".github/agents"
+ GH_AW_SUB_AGENT_EXT: ".agent.md"
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/restore_inline_sub_agents.sh"
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.41 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41 ghcr.io/github/gh-aw-firewall/squid:0.25.41 ghcr.io/github/gh-aw-mcpg:v0.3.6@sha256:2bb8eef86006a4c5963c55616a9c51c32f27bfdecb023b8aa6f91f6718d9171c ghcr.io/github/github-mcp-server:v1.0.3@sha256:2ac27ef03461ef2b877031b838a7d1fd7f12b12d4ace7796d8cad91446d55959 node:lts-alpine@sha256:d1b3b4da11eefd5941e7f0b9cf17783fc99d9c6fc34884a665f40a06dbdfc94f
+ - name: Generate Safe Outputs Config
+ run: |
+ 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_6d18c7040963ed61_EOF'
+ {"create_discussion":{"category":"agentic workflows","close_older_discussions":true,"expires":336,"fallback_to_issue":true,"max":1,"title_prefix":"[TPTP Benchmark] "},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"false"},"report_incomplete":{}}
+ GH_AW_SAFE_OUTPUTS_CONFIG_6d18c7040963ed61_EOF
+ - name: Generate Safe Outputs Tools
+ env:
+ GH_AW_TOOLS_META_JSON: |
+ {
+ "description_suffixes": {
+ "create_discussion": " CONSTRAINTS: Maximum 1 discussion(s) can be created. Title will be prefixed with \"[TPTP Benchmark] \". Discussions will be created in category \"agentic workflows\"."
+ },
+ "repo_params": {},
+ "dynamic_tools": []
+ }
+ GH_AW_VALIDATION_JSON: |
+ {
+ "create_discussion": {
+ "defaultMax": 1,
+ "fields": {
+ "body": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "category": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "repo": {
+ "type": "string",
+ "maxLength": 256
+ },
+ "title": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "missing_data": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "context": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "data_type": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ },
+ "reason": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ }
+ }
+ },
+ "missing_tool": {
+ "defaultMax": 20,
+ "fields": {
+ "alternatives": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 512
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 256
+ },
+ "tool": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 128
+ }
+ }
+ },
+ "noop": {
+ "defaultMax": 1,
+ "fields": {
+ "message": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ }
+ }
+ },
+ "report_incomplete": {
+ "defaultMax": 5,
+ "fields": {
+ "details": {
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 65000
+ },
+ "reason": {
+ "required": true,
+ "type": "string",
+ "sanitize": true,
+ "maxLength": 1024
+ }
+ }
+ }
+ }
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/generate_safe_outputs_tools.cjs');
+ await main();
+ - name: Generate Safe Outputs MCP Server Config
+ id: safe-outputs-config
+ run: |
+ # Generate a secure random API key (360 bits of entropy, 40+ chars)
+ # Mask immediately to prevent timing vulnerabilities
+ API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${API_KEY}"
+
+ PORT=3001
+
+ # Set outputs for next steps
+ {
+ echo "safe_outputs_api_key=${API_KEY}"
+ echo "safe_outputs_port=${PORT}"
+ } >> "$GITHUB_OUTPUT"
+
+ echo "Safe Outputs MCP server will run on port ${PORT}"
+
+ - name: Start Safe Outputs MCP HTTP Server
+ id: safe-outputs-start
+ env:
+ DEBUG: '*'
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-config.outputs.safe_outputs_port }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-config.outputs.safe_outputs_api_key }}
+ GH_AW_SAFE_OUTPUTS_TOOLS_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/tools.json
+ GH_AW_SAFE_OUTPUTS_CONFIG_PATH: ${{ runner.temp }}/gh-aw/safeoutputs/config.json
+ GH_AW_MCP_LOG_DIR: /tmp/gh-aw/mcp-logs/safeoutputs
+ run: |
+ # Environment variables are set above to prevent template injection
+ export DEBUG
+ export GH_AW_SAFE_OUTPUTS
+ export GH_AW_SAFE_OUTPUTS_PORT
+ export GH_AW_SAFE_OUTPUTS_API_KEY
+ export GH_AW_SAFE_OUTPUTS_TOOLS_PATH
+ export GH_AW_SAFE_OUTPUTS_CONFIG_PATH
+ export GH_AW_MCP_LOG_DIR
+
+ bash "${RUNNER_TEMP}/gh-aw/actions/start_safe_outputs_server.sh"
+
+ - name: Start MCP Gateway
+ id: start-mcp-gateway
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_SAFE_OUTPUTS_API_KEY: ${{ steps.safe-outputs-start.outputs.api_key }}
+ GH_AW_SAFE_OUTPUTS_PORT: ${{ steps.safe-outputs-start.outputs.port }}
+ GITHUB_MCP_GUARD_MIN_INTEGRITY: ${{ steps.determine-automatic-lockdown.outputs.min_integrity }}
+ GITHUB_MCP_GUARD_REPOS: ${{ steps.determine-automatic-lockdown.outputs.repos }}
+ 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 "${RUNNER_TEMP}/gh-aw/mcp-config"
+
+ # Export gateway environment variables for MCP config and gateway script
+ export MCP_GATEWAY_PORT="8080"
+ export MCP_GATEWAY_DOMAIN="host.docker.internal"
+ export MCP_GATEWAY_HOST_DOMAIN="localhost"
+ MCP_GATEWAY_API_KEY=$(openssl rand -base64 45 | tr -d '/+=')
+ echo "::add-mask::${MCP_GATEWAY_API_KEY}"
+ export MCP_GATEWAY_API_KEY
+ export MCP_GATEWAY_PAYLOAD_DIR="/tmp/gh-aw/mcp-payloads"
+ mkdir -p "${MCP_GATEWAY_PAYLOAD_DIR}"
+ export MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD="524288"
+ export DEBUG="*"
+
+ export GH_AW_ENGINE="copilot"
+ MCP_GATEWAY_UID=$(id -u 2>/dev/null || echo '0')
+ MCP_GATEWAY_GID=$(id -g 2>/dev/null || echo '0')
+ DOCKER_SOCK_GID=$(stat -c '%g' /var/run/docker.sock 2>/dev/null || echo '0')
+ export MCP_GATEWAY_DOCKER_COMMAND='docker run -i --rm --network host --add-host host.docker.internal:127.0.0.1 --user '"${MCP_GATEWAY_UID}"':'"${MCP_GATEWAY_GID}"' --group-add '"${DOCKER_SOCK_GID}"' -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.3.6'
+
+ 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_9ec215538233c65c_EOF | "$GH_AW_NODE" "${RUNNER_TEMP}/gh-aw/actions/start_mcp_gateway.cjs"
+ {
+ "mcpServers": {
+ "github": {
+ "type": "stdio",
+ "container": "ghcr.io/github/github-mcp-server:v1.0.3",
+ "env": {
+ "GITHUB_HOST": "\${GITHUB_SERVER_URL}",
+ "GITHUB_PERSONAL_ACCESS_TOKEN": "\${GITHUB_MCP_SERVER_TOKEN}",
+ "GITHUB_READ_ONLY": "1",
+ "GITHUB_TOOLSETS": "context,repos,issues,pull_requests"
+ },
+ "guard-policies": {
+ "allow-only": {
+ "min-integrity": "$GITHUB_MCP_GUARD_MIN_INTEGRITY",
+ "repos": "$GITHUB_MCP_GUARD_REPOS"
+ }
+ }
+ },
+ "safeoutputs": {
+ "type": "http",
+ "url": "http://host.docker.internal:$GH_AW_SAFE_OUTPUTS_PORT",
+ "headers": {
+ "Authorization": "\${GH_AW_SAFE_OUTPUTS_API_KEY}"
+ },
+ "guard-policies": {
+ "write-sink": {
+ "accept": [
+ "*"
+ ]
+ }
+ }
+ }
+ },
+ "gateway": {
+ "port": $MCP_GATEWAY_PORT,
+ "domain": "${MCP_GATEWAY_DOMAIN}",
+ "apiKey": "${MCP_GATEWAY_API_KEY}",
+ "payloadDir": "${MCP_GATEWAY_PAYLOAD_DIR}"
+ }
+ }
+ GH_AW_MCP_CONFIG_9ec215538233c65c_EOF
+ - name: Mount MCP servers as CLIs
+ id: mount-mcp-clis
+ continue-on-error: true
+ env:
+ MCP_GATEWAY_API_KEY: ${{ steps.start-mcp-gateway.outputs.gateway-api-key }}
+ MCP_GATEWAY_DOMAIN: ${{ steps.start-mcp-gateway.outputs.gateway-domain }}
+ MCP_GATEWAY_PORT: ${{ steps.start-mcp-gateway.outputs.gateway-port }}
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ with:
+ script: |
+ const { setupGlobals } = require('${{ runner.temp }}/gh-aw/actions/setup_globals.cjs');
+ setupGlobals(core, github, context, exec, io);
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/mount_mcp_as_cli.cjs');
+ await main();
+ - name: Clean credentials
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/clean_git_credentials.sh"
+ - name: Audit pre-agent workspace
+ id: pre_agent_audit
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/audit_pre_agent_workspace.sh"
+ - name: Execute GitHub Copilot CLI
+ id: agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 300
+ run: |
+ set -o pipefail
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ (umask 177 && touch /tmp/gh-aw/agent-stdio.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.41/awf-config.schema.json","network":{"allowDomains":["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","tptp.org","ts-crl.ws.symantec.com","ts-ocsp.ws.symantec.com","www.googleapis.com"]},"apiProxy":{"enabled":true,"models":{"auto":["large"],"deep-research":["copilot/deep-research*","copilot/o3-deep-research*","copilot/o4-mini-deep-research*","google/deep-research*","openai/o3-deep-research*","openai/o4-mini-deep-research*"],"gemini-flash":["copilot/gemini-*flash*","google/gemini-*flash*"],"gemini-pro":["copilot/gemini-*pro*","google/gemini-*pro*"],"gpt-4.1":["copilot/gpt-4.1*","openai/gpt-4.1*"],"gpt-5":["copilot/gpt-5*","openai/gpt-5*"],"gpt-5-codex":["copilot/gpt-5*codex*","openai/gpt-5*codex*"],"gpt-5-mini":["copilot/gpt-5*mini*","openai/gpt-5*mini*"],"gpt-5-nano":["copilot/gpt-5*nano*","openai/gpt-5*nano*"],"gpt-5-pro":["copilot/gpt-5*pro*","openai/gpt-5*pro*"],"haiku":["copilot/*haiku*","anthropic/*haiku*"],"large":["sonnet","gpt-5-pro","gpt-5","gemini-pro"],"mini":["haiku","gpt-5-mini","gpt-5-nano","gemini-flash"],"opus":["copilot/*opus*","anthropic/*opus*"],"reasoning":["copilot/o1*","copilot/o3*","copilot/o4*","openai/o1*","openai/o3*","openai/o4*"],"small":["mini"],"sonnet":["copilot/*sonnet*","anthropic/*sonnet*"]}},"container":{"imageTag":"0.25.41"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --exclude-env GITHUB_MCP_SERVER_TOKEN --exclude-env MCP_GATEWAY_API_KEY --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="${RUNNER_TEMP}/gh-aw/mcp-cli/bin:$PATH" && export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --allow-all-paths --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/agent-stdio.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_API_KEY: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_AGENT_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_MCP_CONFIG: /home/runner/.copilot/mcp-config.json
+ GH_AW_PHASE: agent
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_VERSION: v0.72.1
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_MCP_SERVER_TOKEN: ${{ secrets.GH_AW_GITHUB_MCP_SERVER_TOKEN || secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Detect Copilot errors
+ id: detect-copilot-errors
+ if: always()
+ continue-on-error: true
+ run: node "${RUNNER_TEMP}/gh-aw/actions/detect_copilot_errors.cjs"
+ - name: Configure Git credentials
+ env:
+ REPO_NAME: ${{ github.repository }}
+ SERVER_URL: ${{ github.server_url }}
+ GITHUB_TOKEN: ${{ github.token }}
+ run: |
+ git config --global user.email "github-actions[bot]@users.noreply.github.com"
+ git config --global user.name "github-actions[bot]"
+ git config --global am.keepcr true
+ # 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: Copy Copilot session state files to logs
+ if: always()
+ continue-on-error: true
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/copy_copilot_session_state.sh"
+ - 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 "${RUNNER_TEMP}/gh-aw/actions/stop_mcp_gateway.sh" "$GATEWAY_PID"
+ - name: Redact secrets in logs
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/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: Append agent step summary
+ if: always()
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/append_agent_step_summary.sh"
+ - name: Copy Safe Outputs
+ if: always()
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ run: |
+ mkdir -p /tmp/gh-aw
+ cp "$GH_AW_SAFE_OUTPUTS" /tmp/gh-aw/safeoutputs.jsonl 2>/dev/null || true
+ - name: Ingest agent output
+ id: collect_output
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_SAFE_OUTPUTS: ${{ steps.set-runtime-paths.outputs.GH_AW_SAFE_OUTPUTS }}
+ GH_AW_ALLOWED_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,tptp.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ 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/collect_ndjson_output.cjs');
+ await main();
+ - name: Parse agent logs for step summary
+ if: always()
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ GH_AW_AGENT_OUTPUT: /tmp/gh-aw/sandbox/agent/logs/
+ 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/parse_copilot_log.cjs');
+ await main();
+ - name: Parse MCP Gateway logs for step summary
+ if: always()
+ id: parse-mcp-gateway
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/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/audit dirs so they can be uploaded as artifacts
+ # AWF runs with sudo, creating files owned by root
+ sudo chmod -R a+rX /tmp/gh-aw/sandbox/firewall 2>/dev/null || true
+ # Only run awf logs summary if awf command exists (it may not be installed if workflow failed before install step)
+ if command -v awf &> /dev/null; then
+ awf logs summary | tee -a "$GITHUB_STEP_SUMMARY"
+ else
+ echo 'AWF binary not installed, skipping firewall log summary'
+ fi
+ - name: Parse token usage for step summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/parse_token_usage.cjs');
+ await main();
+ - name: Print AWF reflect summary
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ 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/awf_reflect_summary.cjs');
+ await main();
+ - name: Write agent output placeholder if missing
+ if: always()
+ run: |
+ if [ ! -f /tmp/gh-aw/agent_output.json ]; then
+ echo '{"items":[]}' > /tmp/gh-aw/agent_output.json
+ fi
+ - name: Upload agent artifacts
+ if: always()
+ continue-on-error: true
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: agent
+ path: |
+ /tmp/gh-aw/aw-prompts/prompt.txt
+ /tmp/gh-aw/sandbox/agent/logs/
+ /tmp/gh-aw/redacted-urls.log
+ /tmp/gh-aw/mcp-logs/
+ /tmp/gh-aw/agent_usage.json
+ /tmp/gh-aw/agent-stdio.log
+ /tmp/gh-aw/pre-agent-audit.txt
+ /tmp/gh-aw/agent/
+ /tmp/gh-aw/github_rate_limits.jsonl
+ /tmp/gh-aw/safeoutputs.jsonl
+ /tmp/gh-aw/agent_output.json
+ /tmp/gh-aw/aw-*.patch
+ /tmp/gh-aw/aw-*.bundle
+ /tmp/gh-aw/awf-config.json
+ /tmp/gh-aw/sandbox/firewall/logs/
+ /tmp/gh-aw/sandbox/firewall/audit/
+ /tmp/gh-aw/sandbox/firewall/awf-reflect.json
+ if-no-files-found: ignore
+
+ conclusion:
+ needs:
+ - activation
+ - agent
+ - detection
+ - safe_outputs
+ if: >
+ always() && (needs.agent.result != 'skipped' || needs.activation.outputs.lockdown_check_failed == 'true' ||
+ needs.activation.outputs.stale_lock_file_failed == 'true')
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ concurrency:
+ group: "gh-aw-conclusion-tptp-benchmark"
+ cancel-in-progress: false
+ outputs:
+ incomplete_count: ${{ steps.report_incomplete.outputs.incomplete_count }}
+ noop_message: ${{ steps.noop.outputs.noop_message }}
+ tools_reported: ${{ steps.missing_tool.outputs.tools_reported }}
+ total_count: ${{ steps.missing_tool.outputs.total_count }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/tptp-benchmark.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Process no-op messages
+ id: noop
+ 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_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_NOOP_REPORT_AS_ISSUE: "false"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_noop_message.cjs');
+ await main();
+ - name: Log detection run
+ id: detection_runs
+ 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_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_detection_runs.cjs');
+ await main();
+ - name: Record missing tool
+ id: missing_tool
+ 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_MISSING_TOOL_CREATE_ISSUE: "true"
+ GH_AW_MISSING_TOOL_TITLE_PREFIX: "[missing tool]"
+ GH_AW_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/missing_tool.cjs');
+ await main();
+ - name: Record incomplete
+ id: report_incomplete
+ 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_REPORT_INCOMPLETE_CREATE_ISSUE: "true"
+ GH_AW_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/report_incomplete_handler.cjs');
+ await main();
+ - name: Handle agent failure
+ id: handle_agent_failure
+ if: always()
+ 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_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ GH_AW_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+ GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
+ GH_AW_WORKFLOW_ID: "tptp-benchmark"
+ GH_AW_ACTION_FAILURE_ISSUE_EXPIRES_HOURS: "168"
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_SECRET_VERIFICATION_RESULT: ${{ needs.activation.outputs.secret_verification_result }}
+ GH_AW_CHECKOUT_PR_SUCCESS: ${{ needs.agent.outputs.checkout_pr_success }}
+ GH_AW_INFERENCE_ACCESS_ERROR: ${{ needs.agent.outputs.inference_access_error }}
+ GH_AW_MCP_POLICY_ERROR: ${{ needs.agent.outputs.mcp_policy_error }}
+ GH_AW_AGENTIC_ENGINE_TIMEOUT: ${{ needs.agent.outputs.agentic_engine_timeout }}
+ GH_AW_MODEL_NOT_SUPPORTED_ERROR: ${{ needs.agent.outputs.model_not_supported_error }}
+ GH_AW_ENGINE_API_HOSTS: "api.enterprise.githubcopilot.com,api.githubcopilot.com,api.business.githubcopilot.com,api.individual.githubcopilot.com"
+ GH_AW_CREATE_DISCUSSION_ERRORS: ${{ needs.safe_outputs.outputs.create_discussion_errors }}
+ GH_AW_CREATE_DISCUSSION_ERROR_COUNT: ${{ needs.safe_outputs.outputs.create_discussion_error_count }}
+ GH_AW_LOCKDOWN_CHECK_FAILED: ${{ needs.activation.outputs.lockdown_check_failed }}
+ GH_AW_STALE_LOCK_FILE_FAILED: ${{ needs.activation.outputs.stale_lock_file_failed }}
+ GH_AW_GROUP_REPORTS: "false"
+ GH_AW_FAILURE_REPORT_AS_ISSUE: "true"
+ GH_AW_MISSING_TOOL_REPORT_AS_FAILURE: "true"
+ GH_AW_MISSING_DATA_REPORT_AS_FAILURE: "true"
+ GH_AW_TIMEOUT_MINUTES: "300"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/handle_agent_failure.cjs');
+ await main();
+
+ detection:
+ needs:
+ - activation
+ - agent
+ if: >
+ always() && needs.agent.result != 'skipped' && (needs.agent.outputs.output_types != '' || needs.agent.outputs.has_patch == 'true')
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ outputs:
+ detection_conclusion: ${{ steps.detection_conclusion.outputs.conclusion }}
+ detection_reason: ${{ steps.detection_conclusion.outputs.reason }}
+ detection_success: ${{ steps.detection_conclusion.outputs.success }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/tptp-benchmark.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - name: Checkout repository for patch context
+ if: needs.agent.outputs.has_patch == 'true'
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ with:
+ persist-credentials: false
+ # --- Threat Detection ---
+ - name: Clean stale firewall files from agent artifact
+ run: |
+ rm -rf /tmp/gh-aw/sandbox/firewall/logs
+ rm -rf /tmp/gh-aw/sandbox/firewall/audit
+ - name: Download container images
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/download_docker_images.sh" ghcr.io/github/gh-aw-firewall/agent:0.25.41 ghcr.io/github/gh-aw-firewall/api-proxy:0.25.41 ghcr.io/github/gh-aw-firewall/squid:0.25.41
+ - name: Check if detection needed
+ id: detection_guard
+ if: always()
+ env:
+ OUTPUT_TYPES: ${{ needs.agent.outputs.output_types }}
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ run: |
+ if [[ -n "$OUTPUT_TYPES" || "$HAS_PATCH" == "true" ]]; then
+ echo "run_detection=true" >> "$GITHUB_OUTPUT"
+ echo "Detection will run: output_types=$OUTPUT_TYPES, has_patch=$HAS_PATCH"
+ else
+ echo "run_detection=false" >> "$GITHUB_OUTPUT"
+ echo "Detection skipped: no agent outputs or patches to analyze"
+ fi
+ - name: Clear MCP Config for detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ rm -f "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"
+ rm -f /home/runner/.copilot/mcp-config.json
+ rm -f "$GITHUB_WORKSPACE/.gemini/settings.json"
+ - name: Prepare threat detection files
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection/aw-prompts
+ cp /tmp/gh-aw/aw-prompts/prompt.txt /tmp/gh-aw/threat-detection/aw-prompts/prompt.txt 2>/dev/null || true
+ cp /tmp/gh-aw/agent_output.json /tmp/gh-aw/threat-detection/agent_output.json 2>/dev/null || true
+ for f in /tmp/gh-aw/aw-*.patch; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ for f in /tmp/gh-aw/aw-*.bundle; do
+ [ -f "$f" ] && cp "$f" /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ done
+ echo "Prepared threat detection files:"
+ ls -la /tmp/gh-aw/threat-detection/ 2>/dev/null || true
+ - name: Setup threat detection
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ WORKFLOW_DESCRIPTION: "Weekly benchmark of Z3's TPTP front-end against 500 random TPTP problems. Downloads TPTP benchmarks from tptp.org, resolves axiom dependencies, skips large problems, runs each with a 5-second timeout, and posts a discrepancy/crash report as a GitHub discussion."
+ HAS_PATCH: ${{ needs.agent.outputs.has_patch }}
+ 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/setup_threat_detection.cjs');
+ await main();
+ - name: Ensure threat-detection directory and log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ run: |
+ mkdir -p /tmp/gh-aw/threat-detection
+ touch /tmp/gh-aw/threat-detection/detection.log
+ - name: Setup Node.js
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ with:
+ node-version: '24'
+ package-manager-cache: false
+ - name: Install GitHub Copilot CLI
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_copilot_cli.sh" 1.0.40
+ env:
+ GH_HOST: github.com
+ - name: Install AWF binary
+ run: bash "${RUNNER_TEMP}/gh-aw/actions/install_awf_binary.sh" v0.25.41
+ - name: Execute GitHub Copilot CLI
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ continue-on-error: true
+ id: detection_agentic_execution
+ # Copilot CLI tool arguments (sorted):
+ timeout-minutes: 20
+ run: |
+ set -o pipefail
+ touch /tmp/gh-aw/agent-step-summary.md
+ GH_AW_NODE_BIN=$(command -v node 2>/dev/null || true)
+ export GH_AW_NODE_BIN
+ (umask 177 && touch /tmp/gh-aw/threat-detection/detection.log)
+ printf '%s\n' '{"$schema":"https://github.com/github/gh-aw-firewall/releases/download/v0.25.41/awf-config.schema.json","network":{"allowDomains":["api.business.githubcopilot.com","api.enterprise.githubcopilot.com","api.github.com","api.githubcopilot.com","api.individual.githubcopilot.com","github.com","host.docker.internal","telemetry.enterprise.githubcopilot.com"]},"apiProxy":{"enabled":true},"container":{"imageTag":"0.25.41"}}' > "${RUNNER_TEMP}/gh-aw/awf-config.json" && cp "${RUNNER_TEMP}/gh-aw/awf-config.json" /tmp/gh-aw/awf-config.json
+ # shellcheck disable=SC1003
+ sudo -E awf --config "${RUNNER_TEMP}/gh-aw/awf-config.json" --container-workdir "${GITHUB_WORKSPACE}" --mount "${RUNNER_TEMP}/gh-aw:${RUNNER_TEMP}/gh-aw:ro" --mount "${RUNNER_TEMP}/gh-aw:/host${RUNNER_TEMP}/gh-aw:ro" --env-all --exclude-env COPILOT_GITHUB_TOKEN --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --audit-dir /tmp/gh-aw/sandbox/firewall/audit --enable-host-access --allow-host-ports 80,443,8080 --skip-pull \
+ -- /bin/bash -c 'export PATH="$(find /opt/hostedtoolcache /home/runner/work/_tool -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH"; [ -n "$GOROOT" ] && export PATH="$GOROOT/bin:$PATH" || true && GH_AW_NODE_EXEC="${GH_AW_NODE_BIN:-}"; if [ -z "$GH_AW_NODE_EXEC" ] || [ ! -x "$GH_AW_NODE_EXEC" ]; then GH_AW_NODE_EXEC="$(command -v node 2>/dev/null || echo node)"; fi; "$GH_AW_NODE_EXEC" ${RUNNER_TEMP}/gh-aw/actions/copilot_harness.cjs /usr/local/bin/copilot --add-dir /tmp/gh-aw/ --log-level all --log-dir /tmp/gh-aw/sandbox/agent/logs/ --disable-builtin-mcps --no-ask-user --allow-all-tools --add-dir "${GITHUB_WORKSPACE}" --prompt-file /tmp/gh-aw/aw-prompts/prompt.txt' 2>&1 | tee -a /tmp/gh-aw/threat-detection/detection.log
+ env:
+ AWF_REFLECT_ENABLED: 1
+ COPILOT_AGENT_RUNNER_TYPE: STANDALONE
+ COPILOT_API_KEY: dummy-byok-key-for-offline-mode
+ COPILOT_GITHUB_TOKEN: ${{ secrets.COPILOT_GITHUB_TOKEN }}
+ COPILOT_MODEL: ${{ vars.GH_AW_MODEL_DETECTION_COPILOT || 'claude-sonnet-4.6' }}
+ GH_AW_PHASE: detection
+ GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt
+ GH_AW_VERSION: v0.72.1
+ GITHUB_API_URL: ${{ github.api_url }}
+ GITHUB_AW: true
+ GITHUB_COPILOT_INTEGRATION_ID: agentic-workflows
+ GITHUB_HEAD_REF: ${{ github.head_ref }}
+ GITHUB_REF_NAME: ${{ github.ref_name }}
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_STEP_SUMMARY: /tmp/gh-aw/agent-step-summary.md
+ GITHUB_WORKSPACE: ${{ github.workspace }}
+ GIT_AUTHOR_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_AUTHOR_NAME: github-actions[bot]
+ GIT_COMMITTER_EMAIL: github-actions[bot]@users.noreply.github.com
+ GIT_COMMITTER_NAME: github-actions[bot]
+ XDG_CONFIG_HOME: /home/runner
+ - name: Upload threat detection log
+ if: always() && steps.detection_guard.outputs.run_detection == 'true'
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: detection
+ path: /tmp/gh-aw/threat-detection/detection.log
+ if-no-files-found: ignore
+ - name: Parse and conclude threat detection
+ id: detection_conclusion
+ if: always()
+ continue-on-error: true
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
+ env:
+ RUN_DETECTION: ${{ steps.detection_guard.outputs.run_detection }}
+ GH_AW_DETECTION_CONTINUE_ON_ERROR: "true"
+ with:
+ script: |
+ try {
+ 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/parse_threat_detection_results.cjs');
+ await main();
+ } catch (loadErr) {
+ const continueOnError = process.env.GH_AW_DETECTION_CONTINUE_ON_ERROR !== 'false';
+ const msg = 'ERR_SYSTEM: \u274C Unexpected error loading threat detection module: ' + (loadErr && loadErr.message ? loadErr.message : String(loadErr));
+ core.error(msg);
+ core.setOutput('reason', 'parse_error');
+ if (continueOnError) {
+ core.warning('\u26A0\uFE0F ' + msg);
+ core.setOutput('conclusion', 'warning');
+ core.setOutput('success', 'false');
+ } else {
+ core.setOutput('conclusion', 'failure');
+ core.setOutput('success', 'false');
+ core.setFailed(msg);
+ }
+ }
+
+ safe_outputs:
+ needs:
+ - activation
+ - agent
+ - detection
+ if: (!cancelled()) && needs.agent.result != 'skipped' && needs.detection.result == 'success'
+ runs-on: ubuntu-slim
+ permissions:
+ contents: read
+ discussions: write
+ issues: write
+ timeout-minutes: 15
+ env:
+ GH_AW_CALLER_WORKFLOW_ID: "${{ github.repository }}/tptp-benchmark"
+ GH_AW_DETECTION_CONCLUSION: ${{ needs.detection.outputs.detection_conclusion }}
+ GH_AW_DETECTION_REASON: ${{ needs.detection.outputs.detection_reason }}
+ GH_AW_EFFECTIVE_TOKENS: ${{ needs.agent.outputs.effective_tokens }}
+ GH_AW_ENGINE_ID: "copilot"
+ GH_AW_ENGINE_MODEL: ${{ needs.agent.outputs.model }}
+ GH_AW_ENGINE_VERSION: "1.0.40"
+ GH_AW_WORKFLOW_ID: "tptp-benchmark"
+ GH_AW_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ outputs:
+ code_push_failure_count: ${{ steps.process_safe_outputs.outputs.code_push_failure_count }}
+ code_push_failure_errors: ${{ steps.process_safe_outputs.outputs.code_push_failure_errors }}
+ create_discussion_error_count: ${{ steps.process_safe_outputs.outputs.create_discussion_error_count }}
+ create_discussion_errors: ${{ steps.process_safe_outputs.outputs.create_discussion_errors }}
+ process_safe_outputs_processed_count: ${{ steps.process_safe_outputs.outputs.processed_count }}
+ process_safe_outputs_temporary_id_map: ${{ steps.process_safe_outputs.outputs.temporary_id_map }}
+ steps:
+ - name: Setup Scripts
+ id: setup
+ uses: github/gh-aw-actions/setup@v0.77.0
+ with:
+ destination: ${{ runner.temp }}/gh-aw/actions
+ job-name: ${{ github.job }}
+ trace-id: ${{ needs.activation.outputs.setup-trace-id }}
+ env:
+ GH_AW_SETUP_WORKFLOW_NAME: "TPTP Front-End Benchmark"
+ GH_AW_CURRENT_WORKFLOW_REF: ${{ github.repository }}/.github/workflows/tptp-benchmark.lock.yml@${{ github.ref }}
+ GH_AW_INFO_VERSION: "1.0.40"
+ - name: Download agent output artifact
+ id: download-agent-output
+ continue-on-error: true
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
+ with:
+ name: agent
+ path: /tmp/gh-aw/
+ - name: Setup agent output environment variable
+ id: setup-agent-output-env
+ if: steps.download-agent-output.outcome == 'success'
+ run: |
+ mkdir -p /tmp/gh-aw/
+ find "/tmp/gh-aw/" -type f -print
+ echo "GH_AW_AGENT_OUTPUT=/tmp/gh-aw/agent_output.json" >> "$GITHUB_OUTPUT"
+ - 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"
+ - name: Process Safe Outputs
+ id: process_safe_outputs
+ 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_ALLOWED_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,tptp.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
+ GITHUB_SERVER_URL: ${{ github.server_url }}
+ GITHUB_API_URL: ${{ github.api_url }}
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"create_discussion\":{\"category\":\"agentic workflows\",\"close_older_discussions\":true,\"expires\":336,\"fallback_to_issue\":true,\"max\":1,\"title_prefix\":\"[TPTP Benchmark] \"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"false\"},\"report_incomplete\":{}}"
+ with:
+ github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
+ 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/safe_output_handler_manager.cjs');
+ await main();
+ - name: Upload Safe Outputs Items
+ if: always()
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
+ with:
+ name: safe-outputs-items
+ path: |
+ /tmp/gh-aw/safe-output-items.jsonl
+ /tmp/gh-aw/temporary-id-map.json
+ if-no-files-found: ignore
+
diff --git a/.github/workflows/tptp-benchmark.md b/.github/workflows/tptp-benchmark.md
new file mode 100644
index 000000000..fe3f7d5b2
--- /dev/null
+++ b/.github/workflows/tptp-benchmark.md
@@ -0,0 +1,546 @@
+---
+description: >
+ Weekly benchmark of Z3's TPTP front-end against 500 random TPTP problems.
+ Downloads TPTP benchmarks from tptp.org, resolves axiom dependencies,
+ skips large problems, runs each with a 5-second timeout, and posts a
+ discrepancy/crash report as a GitHub discussion.
+
+on:
+ schedule:
+ - cron: "0 6 * * 1"
+ workflow_dispatch:
+
+permissions: read-all
+
+network:
+ allowed:
+ - defaults
+ - tptp.org
+
+tools:
+ bash: true
+ github:
+ toolsets: [default]
+
+safe-outputs:
+ create-discussion:
+ title-prefix: "[TPTP Benchmark] "
+ category: "Agentic Workflows"
+ close-older-discussions: true
+ expires: 14d
+ missing-tool:
+ create-issue: true
+ noop:
+ report-as-issue: false
+
+timeout-minutes: 300
+
+steps:
+ - name: Checkout repository
+ uses: actions/checkout@v6.0.2
+ with:
+ persist-credentials: false
+
+ - name: Install build dependencies
+ run: |
+ sudo apt-get update -y -q
+ sudo apt-get install -y cmake ninja-build python3 wget curl bc
+
+ - name: Build Z3
+ run: |
+ mkdir -p /tmp/z3-build
+ cd /tmp/z3-build
+ cmake "$GITHUB_WORKSPACE" \
+ -G Ninja \
+ -DCMAKE_BUILD_TYPE=Release \
+ -DZ3_BUILD_TEST_EXECUTABLES=OFF
+ ninja -j$(nproc) z3
+ ./z3 --version
+
+---
+
+# TPTP Front-End Benchmark
+
+## Job Description
+
+Your name is ${{ github.workflow }}. You are an expert testing engineer for the Z3 theorem prover. Your task is to:
+
+1. Verify the Z3 binary built by the pre-flight step is available
+2. Download the TPTP benchmark library from tptp.org
+3. Select 500 random small-to-medium problems (with their axiom dependencies)
+4. Run each problem through Z3's TPTP front-end with a 5-second timeout
+5. Compare Z3's output against the expected SZS status declared in each problem file
+6. Post a detailed report as a GitHub Discussion summarising discrepancies and crashes
+
+**Repository**: ${{ github.repository }}
+**Workspace**: ${{ github.workspace }}
+
+## Phase 1: Verify Z3 Binary
+
+Z3 was built by the workflow pre-flight step and is available at `/tmp/z3-build/z3`.
+Confirm the binary is present and functional:
+
+```bash
+/tmp/z3-build/z3 --version
+```
+
+If the binary is missing or returns an error, call the `noop` safe-output with a message describing the problem and stop.
+
+Once confirmed, call `noop` with `"Z3 binary verified. Downloading TPTP benchmark library — this may take a few minutes."` to keep the safe-output session alive.
+
+## Phase 2: Download the TPTP Problem Library
+
+Find the latest TPTP release and download the full archive.
+
+```bash
+# Find the latest TPTP distribution version by fetching the directory listing
+TPTP_DIST_URL="https://tptp.org/TPTP/Distribution/"
+LATEST_TGZ=$(curl -sL "$TPTP_DIST_URL" \
+ | grep -oP 'TPTP-v[0-9]+\.[0-9]+\.[0-9]+\.tgz' \
+ | sort -V | tail -1)
+
+if [ -z "$LATEST_TGZ" ]; then
+ echo "ERROR: Could not determine latest TPTP version from $TPTP_DIST_URL"
+ # Fall back to a known stable version
+ LATEST_TGZ="TPTP-v9.0.0.tgz"
+fi
+
+echo "Downloading $LATEST_TGZ ..."
+mkdir -p /tmp/tptp_download
+wget -q --show-progress \
+ "${TPTP_DIST_URL}${LATEST_TGZ}" \
+ -O /tmp/tptp_download/tptp.tgz
+
+echo "Extracting TPTP library..."
+mkdir -p /tmp/tptp
+tar -xzf /tmp/tptp_download/tptp.tgz -C /tmp/tptp --strip-components=1 2>&1 | tail -5
+
+# Verify extraction
+if [ ! -d /tmp/tptp/Problems ] || [ ! -d /tmp/tptp/Axioms ]; then
+ echo "ERROR: TPTP extraction failed — Problems/ or Axioms/ directory not found"
+ ls /tmp/tptp/
+ exit 1
+fi
+
+TPTP_ROOT=/tmp/tptp
+echo "TPTP library extracted to $TPTP_ROOT"
+echo "Problem domains available:"
+ls "$TPTP_ROOT/Problems/" | wc -l
+echo "Axiom files available:"
+ls "$TPTP_ROOT/Axioms/" | wc -l
+```
+
+If the download or extraction fails, call `noop` with the error details and stop.
+
+Call `noop` with `"TPTP library downloaded and extracted. Selecting 500 benchmark problems — filtering by size."` to keep the session alive.
+
+## Phase 3: Select 500 Benchmark Problems
+
+Filter out large problems and problems that depend on large axiom files, then take a random sample of 500.
+
+Save this script to `/tmp/select_benchmarks.py` and run it:
+
+```python
+#!/usr/bin/env python3
+"""
+Select 500 random TPTP problems that:
+ - Have a known, conclusive expected status (Theorem, Unsatisfiable,
+ CounterSatisfiable, Satisfiable) OR Unknown/Open status.
+ - Are not "large" (problem file <= 50 KB).
+ - Do not include any axiom file larger than 100 KB.
+"""
+import os
+import re
+import random
+import sys
+
+TPTP_ROOT = "/tmp/tptp"
+PROBLEMS_DIR = os.path.join(TPTP_ROOT, "Problems")
+AXIOMS_DIR = os.path.join(TPTP_ROOT, "Axioms")
+MAX_PROBLEM_SIZE = 50 * 1024 # 50 KB
+MAX_AXIOM_SIZE = 100 * 1024 # 100 KB
+SAMPLE_SIZE = 500
+OUTPUT_FILE = "/tmp/selected_benchmarks.txt"
+
+include_re = re.compile(r"include\s*\(\s*['\"]([^'\"]+)['\"]", re.IGNORECASE)
+status_re = re.compile(r"%\s*Status\s*:\s*(\S+)", re.IGNORECASE)
+
+def axiom_sizes_ok(problem_path):
+ """Return True if all included axiom files exist and are <= MAX_AXIOM_SIZE."""
+ try:
+ with open(problem_path, encoding="utf-8", errors="replace") as f:
+ content = f.read(4096) # header is in first few KB
+ except OSError:
+ return False
+ for m in include_re.finditer(content):
+ axiom_rel = m.group(1) # e.g. "Axioms/AGT001+0.ax"
+ axiom_path = os.path.join(TPTP_ROOT, axiom_rel)
+ if not os.path.exists(axiom_path):
+ return False # axiom missing — skip
+ if os.path.getsize(axiom_path) > MAX_AXIOM_SIZE:
+ return False # axiom too large — skip
+ return True
+
+candidates = []
+skipped_size = 0
+skipped_axiom = 0
+
+for domain in sorted(os.listdir(PROBLEMS_DIR)):
+ domain_dir = os.path.join(PROBLEMS_DIR, domain)
+ if not os.path.isdir(domain_dir):
+ continue
+ for fname in os.listdir(domain_dir):
+ if not fname.endswith(".p"):
+ continue
+ fpath = os.path.join(domain_dir, fname)
+ size = os.path.getsize(fpath)
+ if size > MAX_PROBLEM_SIZE:
+ skipped_size += 1
+ continue
+ if not axiom_sizes_ok(fpath):
+ skipped_axiom += 1
+ continue
+ candidates.append(fpath)
+
+print(f"Total candidates (after filtering): {len(candidates)}", flush=True)
+print(f" Skipped — problem too large : {skipped_size}", flush=True)
+print(f" Skipped — axiom too large : {skipped_axiom}", flush=True)
+
+if len(candidates) == 0:
+ print("ERROR: No suitable benchmark problems found.", file=sys.stderr)
+ sys.exit(1)
+
+if len(candidates) > SAMPLE_SIZE:
+ random.seed(42)
+ selected = random.sample(candidates, SAMPLE_SIZE)
+else:
+ selected = candidates
+
+selected.sort()
+with open(OUTPUT_FILE, "w") as f:
+ f.write("\n".join(selected) + "\n")
+
+print(f"Selected {len(selected)} problems → {OUTPUT_FILE}", flush=True)
+```
+
+Run the script:
+
+```bash
+python3 /tmp/select_benchmarks.py
+SELECTED=$(wc -l < /tmp/selected_benchmarks.txt)
+echo "Benchmark set: $SELECTED problems"
+```
+
+If no problems are found, call `noop` with an error message and stop.
+
+Call `noop` with `"$SELECTED problems selected. Starting benchmark run with 5-second timeout per problem — this will take approximately $(( SELECTED * 7 / 60 )) minutes."` to keep the session alive.
+
+## Phase 4: Run Benchmarks
+
+Save the following script to `/tmp/run_tptp_benchmarks.sh`, make it executable, and run it.
+
+```bash
+#!/usr/bin/env bash
+set -euo pipefail
+
+Z3=/tmp/z3-build/z3
+TPTP_ROOT=/tmp/tptp
+TIMEOUT_HARD=8 # outer OS-level guard (seconds; 3 s beyond Z3's -T:5)
+Z3_TIMEOUT=5 # Z3 internal timeout: -T:N sets N-second limit (uppercase -T is seconds)
+
+RESULTS=/tmp/tptp_results.tsv
+PROBLEM_LIST=/tmp/selected_benchmarks.txt
+
+echo -e "file\texpected\tactual\ttime_s\tnotes" > "$RESULTS"
+
+# Helper: extract the expected SZS status from the TPTP problem header.
+get_expected_status() {
+ local file="$1"
+ # Look for lines like: "% Status : Theorem"
+ grep -m1 -iP '%\s*Status\s*:\s*\K\S+' "$file" 2>/dev/null || echo "Unknown"
+}
+
+# Helper: run z3 on a single TPTP problem with timeout.
+run_benchmark() {
+ local file="$1"
+ local start end elapsed output exit_code verdict
+
+ start=$(date +%s%3N) # milliseconds since epoch
+ output=$(TPTP="$TPTP_ROOT" timeout "$TIMEOUT_HARD" \
+ "$Z3" -tptp -T:"$Z3_TIMEOUT" "$file" 2>&1) || exit_code=$?
+ exit_code=${exit_code:-0}
+ end=$(date +%s%3N)
+ elapsed=$(echo "scale=3; ($end - $start) / 1000" | bc)
+
+ # Extract SZS status line from output
+ szs_line=$(echo "$output" | grep -m1 "% SZS status" || true)
+
+ if [ -n "$szs_line" ]; then
+ # Parse the status keyword (e.g. "Theorem", "CounterSatisfiable", "GaveUp")
+ verdict=$(echo "$szs_line" | grep -oP '% SZS status \K\S+' || echo "Unknown")
+ elif [ "$exit_code" -eq 124 ]; then
+ verdict="Timeout"
+ elif [ "$exit_code" -ne 0 ]; then
+ verdict="Crash"
+ else
+ verdict="NoOutput"
+ fi
+
+ echo "$verdict $elapsed"
+}
+
+COUNTER=0
+TOTAL=$(wc -l < "$PROBLEM_LIST")
+
+while IFS= read -r problem_file; do
+ COUNTER=$((COUNTER + 1))
+
+ expected=$(get_expected_status "$problem_file")
+ result_line=$(run_benchmark "$problem_file")
+ actual=$(echo "$result_line" | cut -d' ' -f1)
+ elapsed=$(echo "$result_line" | cut -d' ' -f2)
+ fname=$(basename "$problem_file")
+
+ # Classify notes
+ notes=""
+ # Soundness discrepancy: both answers are conclusive but conflict
+ conclusive_expected=false
+ conclusive_actual=false
+ case "$expected" in
+ Theorem|Unsatisfiable) conclusive_expected=true ;;
+ Satisfiable|CounterSatisfiable) conclusive_expected=true ;;
+ esac
+ case "$actual" in
+ Theorem|Unsatisfiable) conclusive_actual=true ;;
+ Satisfiable|CounterSatisfiable) conclusive_actual=true ;;
+ esac
+
+ if $conclusive_expected && $conclusive_actual; then
+ # Map expected to the Z3 output equivalents for comparison
+ # Theorem (has-conjecture unsat) matches "Theorem"
+ # Unsatisfiable (no-conjecture unsat) matches "Unsatisfiable"
+ # Satisfiable (no-conjecture sat) matches "Satisfiable"
+ # CounterSatisfiable (has-conjecture sat) matches "CounterSatisfiable"
+ if [ "$expected" != "$actual" ]; then
+ # Check for sat/unsat polarity conflict
+ sat_expected=false; sat_actual=false
+ case "$expected" in Satisfiable|CounterSatisfiable) sat_expected=true ;; esac
+ case "$actual" in Satisfiable|CounterSatisfiable) sat_actual=true ;; esac
+ if [ "$sat_expected" != "$sat_actual" ]; then
+ notes="SOUNDNESS_ERROR"
+ else
+ notes="STATUS_MISMATCH"
+ fi
+ fi
+ fi
+
+ if [ "$actual" = "Crash" ]; then
+ notes="CRASH"
+ fi
+
+ echo -e "$fname\t$expected\t$actual\t$elapsed\t$notes" >> "$RESULTS"
+
+ if [ -n "$notes" ]; then
+ echo "[$COUNTER/$TOTAL] $fname expected=$expected actual=$actual time=${elapsed}s *** $notes ***"
+ elif [ $((COUNTER % 50)) -eq 0 ]; then
+ echo "[$COUNTER/$TOTAL] Progress checkpoint last=$fname actual=$actual time=${elapsed}s"
+ fi
+
+done < "$PROBLEM_LIST"
+
+echo "Benchmark run complete: $COUNTER problems processed. Results → $RESULTS"
+```
+
+Run it:
+
+```bash
+chmod +x /tmp/run_tptp_benchmarks.sh
+/tmp/run_tptp_benchmarks.sh
+```
+
+Do not skip any file in the list.
+
+## Phase 5: Analyze Results
+
+Save the following script to `/tmp/analyze_tptp.py` and run it:
+
+```python
+#!/usr/bin/env python3
+"""Compute summary statistics from the TPTP benchmark TSV."""
+import csv
+
+RESULTS_FILE = "/tmp/tptp_results.tsv"
+
+rows = []
+with open(RESULTS_FILE, newline="") as f:
+ reader = csv.DictReader(f, delimiter="\t")
+ for row in reader:
+ rows.append(row)
+
+total = len(rows)
+
+# Verdict counts
+from collections import Counter, defaultdict
+actual_counts = Counter(r["actual"] for r in rows)
+expected_counts = Counter(r["expected"] for r in rows)
+
+# Flagged rows
+soundness_errors = [r for r in rows if r["notes"] == "SOUNDNESS_ERROR"]
+status_mismatches = [r for r in rows if r["notes"] == "STATUS_MISMATCH"]
+crashes = [r for r in rows if r["notes"] == "CRASH"]
+timeouts = [r for r in rows if r["actual"] == "Timeout"]
+gave_up = [r for r in rows if r["actual"] == "GaveUp"]
+
+# Solved correctly (expected matches actual for conclusive verdicts)
+conclusive_expected = {"Theorem", "Unsatisfiable", "Satisfiable", "CounterSatisfiable"}
+correct = [r for r in rows
+ if r["expected"] in conclusive_expected
+ and r["actual"] == r["expected"]]
+
+print(f"TOTAL={total}")
+print(f"CORRECT={len(correct)}")
+print(f"TIMEOUTS={len(timeouts)}")
+print(f"GAVE_UP={len(gave_up)}")
+print(f"CRASHES={len(crashes)}")
+print(f"SOUNDNESS_ERRORS={len(soundness_errors)}")
+print(f"STATUS_MISMATCHES={len(status_mismatches)}")
+
+print("\n--- Actual verdict breakdown ---")
+for v, c in sorted(actual_counts.items()):
+ print(f" {v}: {c}")
+
+print("\n--- Expected status breakdown ---")
+for v, c in sorted(expected_counts.items()):
+ print(f" {v}: {c}")
+
+if soundness_errors:
+ print(f"\n--- SOUNDNESS ERRORS ({len(soundness_errors)}) ---")
+ for r in soundness_errors:
+ print(f" {r['file']} expected={r['expected']} actual={r['actual']}")
+
+if crashes:
+ print(f"\n--- CRASHES ({len(crashes)}) ---")
+ for r in crashes:
+ print(f" {r['file']} expected={r['expected']}")
+
+if status_mismatches:
+ print(f"\n--- STATUS MISMATCHES ({len(status_mismatches)}) ---")
+ for r in status_mismatches[:20]:
+ print(f" {r['file']} expected={r['expected']} actual={r['actual']}")
+```
+
+Run the analysis:
+
+```bash
+python3 /tmp/analyze_tptp.py
+```
+
+## Phase 6: Generate and Post the Discussion Report
+
+Read the TSV at `/tmp/tptp_results.tsv` and the analysis output, then compose a Markdown report and call `create_discussion`.
+
+The report should use `###` or lower for all headers (never `#` or `##`). Use collapsible `` sections for large tables.
+
+Use this structure:
+
+```markdown
+**Date**:
+**Branch**: master
+**Commit**: `` (run `git rev-parse --short HEAD` in ${{ github.workspace }} to get the SHA)
+**Workflow Run**: [${{ github.run_id }}](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
+**TPTP version**:
+**Problems benchmarked**: (random sample, timeout 5 s per problem)
+
+---
+
+### Summary
+
+| Metric | Count |
+|--------|-------|
+| Total problems run | N |
+| Correct (expected = actual) | N |
+| Timeouts | N |
+| GaveUp (within time budget) | N |
+| Crashes / errors | N |
+| Soundness errors (sat↔unsat conflict) | N |
+| Status mismatches (Theorem vs Unsatisfiable etc.) | N |
+
+### Expected Status Distribution
+
+| Expected Status | Count |
+|----------------|-------|
+| Theorem | N |
+| Unsatisfiable | N |
+| Satisfiable | N |
+| CounterSatisfiable | N |
+| Unknown / Open | N |
+
+---
+
+### ⚠️ Critical: Soundness Errors
+
+[List ALL files where Z3 returned a conclusive answer that contradicts the expected answer
+(e.g., expected Theorem but got CounterSatisfiable). If none, write "None detected."]
+
+### 💥 Crashes
+
+[List ALL files where Z3 crashed (non-zero exit, no SZS output, not a timeout).
+Include filename and expected status. If none, write "None detected."]
+
+### Status Mismatches
+
+[Files where both answers are conclusive but differ in Theorem vs Unsatisfiable polarity
+(e.g., expected Theorem but actual Unsatisfiable). These may indicate conjecture-handling
+differences rather than soundness bugs. If none, write "None detected."]
+
+---
+
+
+View all Timeouts (problems where Z3 exceeded the 5-second limit)
+
+| # | File | Expected Status |
+|---|------|----------------|
+[First 100 timeout rows]
+
+
+
+
+View full per-problem results table
+
+| # | File | Expected | Actual | Time (s) | Notes |
+|---|------|----------|--------|----------|-------|
+[All rows, or first 500 if over limit]
+
+
+
+---
+
+### Recommendations
+
+[Based on the findings, list actionable items. E.g.: investigate soundness errors,
+file crash bugs, note domains where Z3 consistently times out.]
+```
+
+Post the discussion using the `create_discussion` safe output. The title should be
+`[TPTP Benchmark] master — `.
+
+## Safe Output Guarantee
+
+You **MUST** call either `create_discussion` or `noop` before the workflow ends:
+
+- **Full success**: Call `create_discussion` with the complete report.
+- **Partial results** (some problems ran): Call `create_discussion` with whatever results are available and a note about incomplete execution.
+- **Download failure**: Call `noop` with the download error details.
+- **No problems selected**: Call `noop` explaining why no problems were found.
+- **Binary missing**: If `/tmp/z3-build/z3` is unexpectedly absent, call `noop` with that detail and stop.
+
+## Important Notes
+
+- **Build failure handling**: Z3 was built before the agent loaded. If the binary is missing or non-functional, call `noop` with the error and stop.
+- **TPTP environment variable**: Set `TPTP=/tmp/tptp` when invoking `z3 -tptp` so that `include()` directives in problem files resolve correctly against the downloaded Axioms directory.
+- **Timeout detection**: Use `timeout 8` as the outer OS-level guard (3 seconds beyond Z3's `-T:5`) to allow Z3 to exit cleanly before the shell kills it. If the exit code from `timeout` is 124, record the verdict as `Timeout`.
+- **Crash detection**: A crash is a non-zero exit code with no `% SZS status` line in the output and no timeout. Record it separately from `GaveUp`.
+- **SZS status semantics**: Z3 outputs `Theorem` (not `Unsatisfiable`) when it proves a conjecture; `CounterSatisfiable` (not `Satisfiable`) when it finds a counterexample to a conjecture. A status mismatch between `Theorem` and `Unsatisfiable` for the same problem may be innocuous and depends on whether the problem file uses a conjecture formula.
+- **Report soundness bugs prominently**: Any case where the polarity of the answer conflicts (expected Theorem/Unsatisfiable but got CounterSatisfiable/Satisfiable, or vice versa) is a potential soundness bug and must be highlighted as critical.
+- **Keep progress log**: Print a line for every flagged result and every 50th problem so the workflow log shows progress.
+- **Close older discussions**: Configured via `close-older-discussions: true`. Only the latest weekly report remains open.
diff --git a/.github/workflows/wasm-release.yml b/.github/workflows/wasm-release.yml
index 2fb04d49f..624f80502 100644
--- a/.github/workflows/wasm-release.yml
+++ b/.github/workflows/wasm-release.yml
@@ -36,7 +36,7 @@ jobs:
cp ../../../LICENSE.txt .
- name: Setup emscripten
- uses: mymindstorm/setup-emsdk@v14
+ uses: mymindstorm/setup-emsdk@v16
with:
no-install: true
version: ${{env.EM_VERSION}}
diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml
index 0eaa8f863..c6e118ad5 100644
--- a/.github/workflows/wasm.yml
+++ b/.github/workflows/wasm.yml
@@ -29,7 +29,7 @@ jobs:
node-version: "lts/*"
- name: Setup emscripten
- uses: mymindstorm/setup-emsdk@v14
+ uses: mymindstorm/setup-emsdk@v16
with:
no-install: true
version: ${{env.EM_VERSION}}
diff --git a/.github/workflows/workflow-suggestion-agent.md b/.github/workflows/workflow-suggestion-agent.md
index 948f01079..6886f6505 100644
--- a/.github/workflows/workflow-suggestion-agent.md
+++ b/.github/workflows/workflow-suggestion-agent.md
@@ -12,11 +12,9 @@ network: defaults
tools:
cache-memory: true
- serena: ["python", "java", "csharp"]
github:
toolsets: [default]
bash: [":*"]
- glob: {}
safe-outputs:
create-discussion:
diff --git a/.github/workflows/zipt-code-reviewer.md b/.github/workflows/zipt-code-reviewer.md
index bc1e7285c..e09d7fb12 100644
--- a/.github/workflows/zipt-code-reviewer.md
+++ b/.github/workflows/zipt-code-reviewer.md
@@ -17,8 +17,6 @@ tools:
cache-memory: true
github:
toolsets: [default]
- view: {}
- glob: {}
edit: {}
web-fetch: {}
bash:
@@ -169,12 +167,10 @@ git diff > /tmp/zipt-improvements.diff
cat /tmp/zipt-improvements.diff
```
-If no changes were made because no improvements were found or all were too risky, exit gracefully:
+If no changes were made because no improvements were found or all were too risky, call the `noop` safe-output tool:
```
-✅ ZIPT code review complete. No concrete improvements found in this run.
-Files examined: [list files]
-ZIPT files compared: [list files]
+noop: "ZIPT code review complete. No concrete improvements found in this run. Files examined: [list files]. ZIPT files compared: [list files]."
```
## Phase 6: Create GitHub Issue
@@ -235,7 +231,12 @@ make test-z3
*Generated by ZIPT Code Reviewer agent — comparing Z3 implementation with CEisenhofer/ZIPT@parikh*
```
-## Important Guidelines
+## Important: Always Call a Safe Output Tool
+
+**You MUST always call at least one safe-output tool before finishing.** Failing to do so is reported as a workflow failure.
+
+- If you found and applied improvements → call `create_issue`
+- If ZIPT is unreachable, no improvements were found, or all improvements are out of scope → call `noop` with a brief explanation
### Scope
- **Only** examine the files listed in Phase 1
@@ -249,7 +250,12 @@ make test-z3
- Prefer small, surgical changes over large refactors
### Exit Conditions
-Exit without creating an issue if:
+Call `noop` (instead of creating an issue) if:
- ZIPT repository is unreachable
- No concrete, safe improvements can be identified
- All identified improvements require architectural changes beyond the scope of a single diff
+
+Example noop call:
+```
+noop: "ZIPT code review complete. No improvements applied: [brief reason, e.g. ZIPT unreachable / no safe changes identified]. Files reviewed: [list]."
+```
diff --git a/.gitignore b/.gitignore
index cfa4ed939..9cad945c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -120,3 +120,9 @@ bazel-*
.beads
build/
.z3-agent/
+.playwright*/
+.atomic/
+.deepscan/
+.deeptest/
+tptp_test/
+tptp_benchmarks/
diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md
index d5d487c77..4544fec27 100644
--- a/RELEASE_NOTES.md
+++ b/RELEASE_NOTES.md
@@ -60,6 +60,77 @@ Version 4.17.0
https://github.com/Z3Prover/z3/pull/8983
- Fix deep API bugs in Z3 C API (null pointer handling, error propagation).
https://github.com/Z3Prover/z3/pull/8972
+- Implement multivariate polynomial factorization via Hensel lifting. Replaces the prior stub
+ implementation (factor_n_sqf_pp) with a working algorithm: evaluate away extra variables to
+ reduce to bivariate, factor the univariate specialization, lift via linear Hensel lifting in
+ Zp[x], and verify the result over Z[x,y]. For more than two variables, bivariate factors are
+ checked against the original polynomial. Thanks to Lev Nachmanson.
+- Add riscv64 Python wheel builds to nightly and release PyPI publishing.
+ https://github.com/Z3Prover/z3/pull/9153
+- Fix nlsat clear() crash: reset polynomial cache and root-atom assignments during solver
+ destruction to prevent use-after-free heap corruption. Also fix scoped_numeral_vector copy
+ constructor to read from the source operand instead of uninitialized self.
+ https://github.com/Z3Prover/z3/pull/9150
+- Fix #9030: in box mode optimization (opt.priority=box), each objective is now optimized
+ independently using push/pop scopes, so adding or removing one objective no longer changes
+ the optimal values of others.
+- Fix assertion violation in isolate_roots for nested nlsat calls. Fixes #6871.
+- Fix #9036: expand bounded integer quantifiers in qe-light when Fourier-Motzkin elimination
+ fails due to non-unit coefficients. When all remaining quantified integers have explicit
+ finite bounds and the product of domain sizes is at most 10000, the quantifier is unrolled
+ into an explicit disjunction.
+- Fix #8023: only skip adding an axiom clause when its satisfying literal is assigned at base
+ level (scope 0). The previous optimization was unsound: literals can be retracted by
+ backtracking, causing the string solver to miss propagations such as indexof(a,s) = -1 when
+ contains(a,s) becomes false after backtracking.
+- Fix lock contention in theory_diff_logic and theory_dense_diff_logic when using multi-threaded
+ solving (smt.threads > 1). A diagnostic IF_VERBOSE(0,...) call was always acquiring the global
+ verbose mutex, causing catastrophic contention when multiple threads internalized atoms.
+ Fixes #8019.
+- Fix string solver: move m_fixed insertion after check_long_strings guard to prevent premature
+ marking of string variables with length > 20 as processed.
+- Fix documentation for Z3_solver_to_dimacs_string: corrected the function name in the API
+ comment. Thanks to Mark DenHoed, https://github.com/Z3Prover/z3/pull/9053
+- Add global backbones to parallel architecture for smt.threads > 1. Backbone literals learned
+ by any worker thread are broadcast to all others, improving search pruning in the shared search
+ tree. Thanks to Ilana Shapiro.
+ https://github.com/Z3Prover/z3/pull/9343
+- Terminate on Demand and algorithmic bugfixes in the parallel search tree, including improved
+ worker termination signaling and fixes to node-state management. Thanks to Ilana Shapiro.
+ https://github.com/Z3Prover/z3/pull/9336
+- Add adaptive growth knobs for Gröbner basis computation under arith.nl.grobner_adaptive.
+ Allows tuning of Gröbner basis expansion rate for better NLA performance. Thanks to Arie.
+ https://github.com/Z3Prover/z3/pull/9390
+- Improvements to NLA lemmas for better nonlinear arithmetic solving. Thanks to Arie.
+ https://github.com/Z3Prover/z3/pull/9391
+- Throttle lia2card tactic in QF_NIA preamble to avoid combinatorial explosion on large instances.
+ Thanks to Arie, https://github.com/Z3Prover/z3/pull/9362
+- Fix smt: reset give-up state when escalating final_check level to prevent solver from
+ incorrectly abandoning solvable instances. Thanks to Lev Nachmanson.
+ https://github.com/Z3Prover/z3/pull/9408
+- Fix double-free crash in anum by giving anum move semantics to prevent sort-triggered
+ double-free. Thanks to Arie, https://github.com/Z3Prover/z3/pull/9320
+- Fix lar_term equality operator to correctly compare terms. Thanks to Arie.
+ https://github.com/Z3Prover/z3/pull/9284
+- Prevent unsound solve-eqs elimination across recursive-function definitions.
+ https://github.com/Z3Prover/z3/pull/9358
+- Fix inverted logic of is-linear check in solve-eqs, #9311.
+- Fix #9293: disable elim-uncnstr simplification under quantifiers to prevent unsound
+ eliminations. Also fix #9234, #9309.
+- Add exception protection for nlsat_tactic and try_for tactic to correctly handle cancellation
+ and ensure robust exception propagation.
+- Add smt.solve_eqs.linear parameter (default false). When set to true, restricts variable
+ eliminations in solve-eqs to only use linear substitutions, avoiding cross-multiplication
+ of nested substitutions.
+- Fix null dereference in linearise_multi_pattern: reorder null check before side effect.
+ https://github.com/Z3Prover/z3/pull/9427
+- Add Go and OCaml API coverage: substitution, AST introspection, Spacer, and Goal completion
+ APIs. https://github.com/Z3Prover/z3/pull/9277
+- Fix two bugs in Python examples. Thanks to Guangyu (Gary) HU.
+ https://github.com/Z3Prover/z3/pull/9303
+- Add fold-unfold tactic as an alternative to solve-eqs for variable elimination using
+ fold-unfold transformations. Also exposed as a simplifier.
+- Handle SIGXCPU (OS timeout) like a regular `-T` timeout. Users should make sure to set the soft limit below the hard one, as in `ulimit -S -t 30 -H -t 31` for a 30s soft limit, so SIGXCPU is delivered before SIGKILL.
Version 4.16.0
==============
diff --git a/cmake/modules/FindDotnet.cmake b/cmake/modules/FindDotnet.cmake
index c73cbd605..ccaf24eb8 100644
--- a/cmake/modules/FindDotnet.cmake
+++ b/cmake/modules/FindDotnet.cmake
@@ -262,8 +262,8 @@ FUNCTION(DOTNET_GET_DEPS _DN_PROJECT arguments)
ENDIF()
IF(_DN_NETCOREAPP)
- SET(_DN_BUILD_OPTIONS -f netcoreapp2.0)
- SET(_DN_PACK_OPTIONS /p:TargetFrameworks=netcoreapp2.0)
+ SET(_DN_BUILD_OPTIONS -f net8.0)
+ SET(_DN_PACK_OPTIONS /p:TargetFrameworks=net8.0)
ELSEIF(UNIX)
# Unix builds default to netstandard2.0
SET(_DN_BUILD_OPTIONS -f netstandard2.0)
@@ -384,7 +384,7 @@ FUNCTION(RUN_DOTNET DOTNET_PROJECT)
COMMAND ${DOTNET_EXE} clean ${DOTNET_PROJPATH} ${DOTNET_BUILD_PROPERTIES}
COMMAND ${DOTNET_EXE} build --no-restore ${DOTNET_PROJPATH} -c ${DOTNET_CONFIG} ${DOTNET_BUILD_PROPERTIES} ${DOTNET_BUILD_OPTIONS}
# XXX tfm
- COMMAND ${DOTNET_EXE} ${DOTNET_OUTPUT_PATH}/netcoreapp2.0/${DOTNET_PROJNAME}.dll ${DOTNET_ARGUMENTS}
+ COMMAND ${DOTNET_EXE} ${DOTNET_OUTPUT_PATH}/net8.0/${DOTNET_PROJNAME}.dll ${DOTNET_ARGUMENTS}
COMMAND ${CMAKE_COMMAND} -E touch ${CMAKE_CURRENT_BINARY_DIR}/${DOTNET_PROJNAME}.runtimestamp
WORKING_DIRECTORY ${DOTNET_OUTPUT_PATH})
ADD_CUSTOM_TARGET(
@@ -399,7 +399,7 @@ FUNCTION(TEST_DOTNET DOTNET_PROJECT)
IF(WIN32)
SET(test_framework_args "")
ELSE()
- SET(test_framework_args -f netcoreapp2.0)
+ SET(test_framework_args -f net8.0)
ENDIF()
ADD_TEST(NAME ${DOTNET_PROJNAME}
diff --git a/examples/dotnet/CMakeLists.txt b/examples/dotnet/CMakeLists.txt
index b07ae4219..98398f945 100644
--- a/examples/dotnet/CMakeLists.txt
+++ b/examples/dotnet/CMakeLists.txt
@@ -24,9 +24,9 @@ if(UNIX AND NOT APPLE)
add_custom_target(
z3_dotnet_test_manual_copy_assembly_hack ALL
- COMMAND ${CMAKE_COMMAND} -E copy ${z3_dotnet_test_manual_copy_deps} ${PROJECT_BINARY_DIR}/dotnet/netcoreapp2.0/
+ COMMAND ${CMAKE_COMMAND} -E copy ${z3_dotnet_test_manual_copy_deps} ${PROJECT_BINARY_DIR}/dotnet/net8.0/
# hack the libz3 entry in deps so it's easy enough for dotnet to reach it...
- COMMAND sed \"s/runtimes\\/.*libz3\\.so/libz3.so/\" -i ${PROJECT_BINARY_DIR}/dotnet/netcoreapp2.0/dotnet.deps.json
+ COMMAND sed \"s/runtimes\\/.*libz3\\.so/libz3.so/\" -i ${PROJECT_BINARY_DIR}/dotnet/net8.0/dotnet.deps.json
)
add_dependencies(z3_dotnet_test_manual_copy_assembly_hack BUILD_dotnet)
diff --git a/examples/dotnet/dotnet.csproj b/examples/dotnet/dotnet.csproj
index 7776259ea..2084e3678 100644
--- a/examples/dotnet/dotnet.csproj
+++ b/examples/dotnet/dotnet.csproj
@@ -2,7 +2,7 @@
Exe
- netcoreapp2.0
+ net8.0
diff --git a/examples/python/bincover.py b/examples/python/bincover.py
index d8a81c25a..72b769982 100644
--- a/examples/python/bincover.py
+++ b/examples/python/bincover.py
@@ -195,7 +195,7 @@ class BinCoverSolver(UserPropagateBase):
assert isinstance(value, BitVecNumRef)
bin_index = value.as_long()
if bin_index >= len(self.bins):
- return NOne
+ return None
return self.bins[bin_index]
def _add_item2bin(self, item, bin):
diff --git a/examples/python/complex/complex.py b/examples/python/complex/complex.py
index aa9adeef8..051641808 100644
--- a/examples/python/complex/complex.py
+++ b/examples/python/complex/complex.py
@@ -81,7 +81,7 @@ class ComplexExpr:
other = _to_complex(other)
return And(self.r == other.r, self.i == other.i)
- def __neq__(self, other):
+ def __ne__(self, other):
return Not(self.__eq__(other))
def simplify(self):
diff --git a/examples/python/mini_ic3.py b/examples/python/mini_ic3.py
index 31d3c595b..13056d385 100644
--- a/examples/python/mini_ic3.py
+++ b/examples/python/mini_ic3.py
@@ -74,6 +74,8 @@ class Horn2Transitions:
pred, inv0 = self.is_body(body)
if pred is None:
return False
+ if inv0 is None:
+ return False
inv1 = self.is_inv(head)
if inv1 is None:
return False
@@ -335,7 +337,7 @@ class MiniIC3:
s = self.states[f - 1].solver
if unsat == s.check(cube):
core = s.unsat_core()
- if not check_disjoint(self.init, self.prev(And(core))):
+ if len(core) > 0 and check_disjoint(self.init, self.prev(And(core))):
return core, f
return cube, f
diff --git a/examples/python/mini_quip.py b/examples/python/mini_quip.py
index a10d5a334..d25abe9af 100644
--- a/examples/python/mini_quip.py
+++ b/examples/python/mini_quip.py
@@ -3,6 +3,8 @@ import heapq
import numpy
import time
import random
+import sys
+import copy
verbose = True
@@ -78,6 +80,8 @@ class Horn2Transitions:
pred, inv0 = self.is_body(body)
if pred is None:
return False
+ if inv0 is None:
+ return False
inv1 = self.is_inv(head)
if inv1 is None:
return False
@@ -349,12 +353,12 @@ class Quip:
def next(self, f):
if is_seq(f):
return [self.next(f1) for f1 in f]
- return substitute(f, zip(self.x0, self.xn))
+ return substitute(f, list(zip(self.x0, self.xn)))
def prev(self, f):
if is_seq(f):
return [self.prev(f1) for f1 in f]
- return substitute(f, zip(self.xn, self.x0))
+ return substitute(f, list(zip(self.xn, self.x0)))
def add_solver(self):
s = fd_solver()
@@ -423,7 +427,8 @@ class Quip:
s.push()
r = self.reachable.state2cube(state)
s.add(And(self.prev(r)))
- s.add(self.prev(cube))
+ if len(cube) > 0:
+ s.add(And(self.prev(list(cube))))
is_sat = s.check()
s.pop()
if is_sat == sat:
@@ -441,7 +446,7 @@ class Quip:
s = self.states[f - 1].solver
if unsat == s.check(cube):
core = s.unsat_core()
- if self.check_reachable(core):
+ if len(core) > 0 and self.check_reachable(core):
return core, f
return cube, f
@@ -454,8 +459,8 @@ class Quip:
for state in self.reachable.states:
s.push()
s.add(And(self.next(self.reachable.state2cube(state))))
- print self.reachable.state2cube(state)
- print s.check()
+ print(self.reachable.state2cube(state))
+ print(s.check())
s.pop()
def lemmas(self, level):
@@ -553,7 +558,7 @@ class Quip:
s.add(self.init)
s.add(self.prev(g.cube))
# since init is a complete assignment, so g.cube must equal to init in sat solver
- assert is_sat == s.check()
+ assert sat == s.check()
if verbose:
print("")
return g
@@ -564,7 +569,7 @@ class Quip:
if r0 is not None:
if g.must:
if verbose:
- print ""
+ print("")
s = fd_solver()
s.add(self.trans)
# make it as a concrete reachable state
@@ -573,9 +578,16 @@ class Quip:
while True:
is_sat = s.check(self.next(g.cube))
assert is_sat == sat
- r = self.next(self.project0(s.model()))
+ m = s.model()
+ r = self.next(self.project0(m))
r = self.reachable.intersect(self.prev(r))
- child = QGoal(self.next(r.children()), g, 0, g.must, 0)
+ if r is None:
+ # reachable intersect failed: fall back to the raw
+ # model projection so we still get a concrete
+ # predecessor and avoid crashing on r.children()
+ child = QGoal(self.next(self.project0(m)), g, 0, g.must, 0)
+ else:
+ child = QGoal(self.next(r.children()), g, 0, g.must, 0)
g = child
if not check_disjoint(self.init, self.prev(g.cube)):
# g is init, break the loop
@@ -596,7 +608,7 @@ class Quip:
for l in self.frames[f_1]:
if not l.bad and len(l.cube) > 0 and set(l.cube).issubset(g.cube):
cube = l.cube
- is_sat == unsat
+ is_sat = unsat
break
f_1 -= 1
if cube is None:
@@ -707,7 +719,7 @@ def test(file):
h2t = Horn2Transitions()
h2t.parse(file)
if verbose:
- print("Test file: %s") % file
+ print("Test file: %s" % file)
mp = Quip(h2t.init, h2t.trans, h2t.goal, h2t.xs, h2t.inputs, h2t.xns)
start_time = time.time()
result = mp.run()
@@ -744,7 +756,7 @@ def validate(var, result, trans):
s.pop()
g = g.parent
if verbose:
- print "--- validation succeed ----"
+ print("--- validation succeed ----")
return
if isinstance(result, ExprRef):
inv = result
@@ -762,7 +774,7 @@ def validate(var, result, trans):
# too many steps to reach invariant
if step > 1000:
if verbose:
- print "--- validation failed --"
+ print("--- validation failed --")
return
if not check_disjoint(var.prev(cube), var.prev(inv)):
# reach invariant
@@ -773,7 +785,7 @@ def validate(var, result, trans):
cube = var.projectN(s.model())
s.pop()
if verbose:
- print "--- validation succeed ----"
+ print("--- validation succeed ----")
return
diff --git a/scripts/mk_project.py b/scripts/mk_project.py
index fef34d036..e62b2e159 100644
--- a/scripts/mk_project.py
+++ b/scripts/mk_project.py
@@ -126,5 +126,3 @@ def init_project_def():
add_ml_example('ml_example', 'ml')
add_z3py_example('py_example', 'python')
return API_files
-
-
diff --git a/scripts/mk_util.py b/scripts/mk_util.py
index 67a2fca63..8b862fec2 100644
--- a/scripts/mk_util.py
+++ b/scripts/mk_util.py
@@ -2314,7 +2314,7 @@ class DotNetExampleComponent(ExampleComponent):
dotnet_proj_str = r"""
Exe
- netcoreapp2.0
+ net8.0
%s
diff --git a/src/api/api_ast.cpp b/src/api/api_ast.cpp
index c0dd3c837..d7ea3d3c8 100644
--- a/src/api/api_ast.cpp
+++ b/src/api/api_ast.cpp
@@ -129,7 +129,6 @@ extern "C" {
Z3_TRY;
LOG_Z3_mk_rec_func_decl(c, s, domain_size, domain, range);
RESET_ERROR_CODE();
- //
recfun::promise_def def =
mk_c(c)->recfun().get_plugin().mk_def(
to_symbol(s), domain_size, to_sorts(domain), to_sort(range), false);
@@ -1088,407 +1087,425 @@ extern "C" {
Z3_CATCH_RETURN("");
}
+ // Helper functions to reduce instruction cache pressure in Z3_get_decl_kind.
+ // Each theory gets its own function to avoid loading the entire switch table.
+
+ static Z3_decl_kind get_decl_kind_basic(decl_kind k) {
+ switch(k) {
+ case OP_TRUE: return Z3_OP_TRUE;
+ case OP_FALSE: return Z3_OP_FALSE;
+ case OP_EQ: return Z3_OP_EQ;
+ case OP_DISTINCT: return Z3_OP_DISTINCT;
+ case OP_ITE: return Z3_OP_ITE;
+ case OP_AND: return Z3_OP_AND;
+ case OP_OR: return Z3_OP_OR;
+ case OP_XOR: return Z3_OP_XOR;
+ case OP_NOT: return Z3_OP_NOT;
+ case OP_IMPLIES: return Z3_OP_IMPLIES;
+ case OP_OEQ: return Z3_OP_OEQ;
+ case PR_UNDEF: return Z3_OP_PR_UNDEF;
+ case PR_TRUE: return Z3_OP_PR_TRUE;
+ case PR_ASSERTED: return Z3_OP_PR_ASSERTED;
+ case PR_GOAL: return Z3_OP_PR_GOAL;
+ case PR_MODUS_PONENS: return Z3_OP_PR_MODUS_PONENS;
+ case PR_REFLEXIVITY: return Z3_OP_PR_REFLEXIVITY;
+ case PR_SYMMETRY: return Z3_OP_PR_SYMMETRY;
+ case PR_TRANSITIVITY: return Z3_OP_PR_TRANSITIVITY;
+ case PR_TRANSITIVITY_STAR: return Z3_OP_PR_TRANSITIVITY_STAR;
+ case PR_MONOTONICITY: return Z3_OP_PR_MONOTONICITY;
+ case PR_QUANT_INTRO: return Z3_OP_PR_QUANT_INTRO;
+ case PR_BIND: return Z3_OP_PR_BIND;
+ case PR_DISTRIBUTIVITY: return Z3_OP_PR_DISTRIBUTIVITY;
+ case PR_AND_ELIM: return Z3_OP_PR_AND_ELIM;
+ case PR_NOT_OR_ELIM: return Z3_OP_PR_NOT_OR_ELIM;
+ case PR_REWRITE: return Z3_OP_PR_REWRITE;
+ case PR_REWRITE_STAR: return Z3_OP_PR_REWRITE_STAR;
+ case PR_PULL_QUANT: return Z3_OP_PR_PULL_QUANT;
+ case PR_PUSH_QUANT: return Z3_OP_PR_PUSH_QUANT;
+ case PR_ELIM_UNUSED_VARS: return Z3_OP_PR_ELIM_UNUSED_VARS;
+ case PR_DER: return Z3_OP_PR_DER;
+ case PR_QUANT_INST: return Z3_OP_PR_QUANT_INST;
+ case PR_HYPOTHESIS: return Z3_OP_PR_HYPOTHESIS;
+ case PR_LEMMA: return Z3_OP_PR_LEMMA;
+ case PR_UNIT_RESOLUTION: return Z3_OP_PR_UNIT_RESOLUTION;
+ case PR_IFF_TRUE: return Z3_OP_PR_IFF_TRUE;
+ case PR_IFF_FALSE: return Z3_OP_PR_IFF_FALSE;
+ case PR_COMMUTATIVITY: return Z3_OP_PR_COMMUTATIVITY;
+ case PR_DEF_AXIOM: return Z3_OP_PR_DEF_AXIOM;
+ case PR_ASSUMPTION_ADD: return Z3_OP_PR_ASSUMPTION_ADD;
+ case PR_LEMMA_ADD: return Z3_OP_PR_LEMMA_ADD;
+ case PR_REDUNDANT_DEL: return Z3_OP_PR_REDUNDANT_DEL;
+ case PR_CLAUSE_TRAIL: return Z3_OP_PR_CLAUSE_TRAIL;
+ case PR_DEF_INTRO: return Z3_OP_PR_DEF_INTRO;
+ case PR_APPLY_DEF: return Z3_OP_PR_APPLY_DEF;
+ case PR_IFF_OEQ: return Z3_OP_PR_IFF_OEQ;
+ case PR_NNF_POS: return Z3_OP_PR_NNF_POS;
+ case PR_NNF_NEG: return Z3_OP_PR_NNF_NEG;
+ case PR_SKOLEMIZE: return Z3_OP_PR_SKOLEMIZE;
+ case PR_MODUS_PONENS_OEQ: return Z3_OP_PR_MODUS_PONENS_OEQ;
+ case PR_TH_LEMMA: return Z3_OP_PR_TH_LEMMA;
+ case PR_HYPER_RESOLVE: return Z3_OP_PR_HYPER_RESOLVE;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_arith(decl_kind k) {
+ switch(k) {
+ case OP_NUM: return Z3_OP_ANUM;
+ case OP_IRRATIONAL_ALGEBRAIC_NUM: return Z3_OP_AGNUM;
+ case OP_LE: return Z3_OP_LE;
+ case OP_GE: return Z3_OP_GE;
+ case OP_LT: return Z3_OP_LT;
+ case OP_GT: return Z3_OP_GT;
+ case OP_ADD: return Z3_OP_ADD;
+ case OP_SUB: return Z3_OP_SUB;
+ case OP_UMINUS: return Z3_OP_UMINUS;
+ case OP_MUL: return Z3_OP_MUL;
+ case OP_DIV: return Z3_OP_DIV;
+ case OP_IDIV: return Z3_OP_IDIV;
+ case OP_REM: return Z3_OP_REM;
+ case OP_MOD: return Z3_OP_MOD;
+ case OP_POWER: return Z3_OP_POWER;
+ case OP_ABS: return Z3_OP_ABS;
+ case OP_TO_REAL: return Z3_OP_TO_REAL;
+ case OP_TO_INT: return Z3_OP_TO_INT;
+ case OP_IS_INT: return Z3_OP_IS_INT;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_array(decl_kind k) {
+ switch(k) {
+ case OP_STORE: return Z3_OP_STORE;
+ case OP_SELECT: return Z3_OP_SELECT;
+ case OP_CONST_ARRAY: return Z3_OP_CONST_ARRAY;
+ case OP_ARRAY_DEFAULT: return Z3_OP_ARRAY_DEFAULT;
+ case OP_ARRAY_MAP: return Z3_OP_ARRAY_MAP;
+ case OP_SET_UNION: return Z3_OP_SET_UNION;
+ case OP_SET_INTERSECT: return Z3_OP_SET_INTERSECT;
+ case OP_SET_DIFFERENCE: return Z3_OP_SET_DIFFERENCE;
+ case OP_SET_COMPLEMENT: return Z3_OP_SET_COMPLEMENT;
+ case OP_SET_SUBSET: return Z3_OP_SET_SUBSET;
+ case OP_AS_ARRAY: return Z3_OP_AS_ARRAY;
+ case OP_ARRAY_EXT: return Z3_OP_ARRAY_EXT;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_special_relations(decl_kind k) {
+ switch(k) {
+ case OP_SPECIAL_RELATION_LO : return Z3_OP_SPECIAL_RELATION_LO;
+ case OP_SPECIAL_RELATION_PO : return Z3_OP_SPECIAL_RELATION_PO;
+ case OP_SPECIAL_RELATION_PLO: return Z3_OP_SPECIAL_RELATION_PLO;
+ case OP_SPECIAL_RELATION_TO : return Z3_OP_SPECIAL_RELATION_TO;
+ case OP_SPECIAL_RELATION_TC : return Z3_OP_SPECIAL_RELATION_TC;
+ default: UNREACHABLE(); return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_bv(decl_kind k) {
+ switch(k) {
+ case OP_BV_NUM: return Z3_OP_BNUM;
+ case OP_BIT1: return Z3_OP_BIT1;
+ case OP_BIT0: return Z3_OP_BIT0;
+ case OP_BNEG: return Z3_OP_BNEG;
+ case OP_BADD: return Z3_OP_BADD;
+ case OP_BSUB: return Z3_OP_BSUB;
+ case OP_BMUL: return Z3_OP_BMUL;
+ case OP_BSDIV: return Z3_OP_BSDIV;
+ case OP_BUDIV: return Z3_OP_BUDIV;
+ case OP_BSREM: return Z3_OP_BSREM;
+ case OP_BUREM: return Z3_OP_BUREM;
+ case OP_BSMOD: return Z3_OP_BSMOD;
+ case OP_BSDIV0: return Z3_OP_BSDIV0;
+ case OP_BUDIV0: return Z3_OP_BUDIV0;
+ case OP_BSREM0: return Z3_OP_BSREM0;
+ case OP_BUREM0: return Z3_OP_BUREM0;
+ case OP_BSMOD0: return Z3_OP_BSMOD0;
+ case OP_ULEQ: return Z3_OP_ULEQ;
+ case OP_SLEQ: return Z3_OP_SLEQ;
+ case OP_UGEQ: return Z3_OP_UGEQ;
+ case OP_SGEQ: return Z3_OP_SGEQ;
+ case OP_ULT: return Z3_OP_ULT;
+ case OP_SLT: return Z3_OP_SLT;
+ case OP_UGT: return Z3_OP_UGT;
+ case OP_SGT: return Z3_OP_SGT;
+ case OP_BAND: return Z3_OP_BAND;
+ case OP_BOR: return Z3_OP_BOR;
+ case OP_BNOT: return Z3_OP_BNOT;
+ case OP_BXOR: return Z3_OP_BXOR;
+ case OP_BNAND: return Z3_OP_BNAND;
+ case OP_BNOR: return Z3_OP_BNOR;
+ case OP_BXNOR: return Z3_OP_BXNOR;
+ case OP_CONCAT: return Z3_OP_CONCAT;
+ case OP_SIGN_EXT: return Z3_OP_SIGN_EXT;
+ case OP_ZERO_EXT: return Z3_OP_ZERO_EXT;
+ case OP_EXTRACT: return Z3_OP_EXTRACT;
+ case OP_REPEAT: return Z3_OP_REPEAT;
+ case OP_BREDOR: return Z3_OP_BREDOR;
+ case OP_BREDAND: return Z3_OP_BREDAND;
+ case OP_BCOMP: return Z3_OP_BCOMP;
+ case OP_BSHL: return Z3_OP_BSHL;
+ case OP_BLSHR: return Z3_OP_BLSHR;
+ case OP_BASHR: return Z3_OP_BASHR;
+ case OP_ROTATE_LEFT: return Z3_OP_ROTATE_LEFT;
+ case OP_ROTATE_RIGHT: return Z3_OP_ROTATE_RIGHT;
+ case OP_EXT_ROTATE_LEFT: return Z3_OP_EXT_ROTATE_LEFT;
+ case OP_EXT_ROTATE_RIGHT: return Z3_OP_EXT_ROTATE_RIGHT;
+ case OP_INT2BV: return Z3_OP_INT2BV;
+ case OP_UBV2INT: return Z3_OP_BV2INT;
+ case OP_SBV2INT: return Z3_OP_SBV2INT;
+ case OP_CARRY: return Z3_OP_CARRY;
+ case OP_XOR3: return Z3_OP_XOR3;
+ case OP_BIT2BOOL: return Z3_OP_BIT2BOOL;
+ case OP_BSMUL_NO_OVFL: return Z3_OP_BSMUL_NO_OVFL;
+ case OP_BUMUL_NO_OVFL: return Z3_OP_BUMUL_NO_OVFL;
+ case OP_BSMUL_NO_UDFL: return Z3_OP_BSMUL_NO_UDFL;
+ case OP_BSDIV_I: return Z3_OP_BSDIV_I;
+ case OP_BUDIV_I: return Z3_OP_BUDIV_I;
+ case OP_BSREM_I: return Z3_OP_BSREM_I;
+ case OP_BUREM_I: return Z3_OP_BUREM_I;
+ case OP_BSMOD_I: return Z3_OP_BSMOD_I;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_dt(decl_kind k) {
+ switch(k) {
+ case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR;
+ case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER;
+ case OP_DT_IS: return Z3_OP_DT_IS;
+ case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR;
+ case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_datalog(decl_kind k) {
+ switch(k) {
+ case datalog::OP_RA_STORE: return Z3_OP_RA_STORE;
+ case datalog::OP_RA_EMPTY: return Z3_OP_RA_EMPTY;
+ case datalog::OP_RA_IS_EMPTY: return Z3_OP_RA_IS_EMPTY;
+ case datalog::OP_RA_JOIN: return Z3_OP_RA_JOIN;
+ case datalog::OP_RA_UNION: return Z3_OP_RA_UNION;
+ case datalog::OP_RA_WIDEN: return Z3_OP_RA_WIDEN;
+ case datalog::OP_RA_PROJECT: return Z3_OP_RA_PROJECT;
+ case datalog::OP_RA_FILTER: return Z3_OP_RA_FILTER;
+ case datalog::OP_RA_NEGATION_FILTER: return Z3_OP_RA_NEGATION_FILTER;
+ case datalog::OP_RA_RENAME: return Z3_OP_RA_RENAME;
+ case datalog::OP_RA_COMPLEMENT: return Z3_OP_RA_COMPLEMENT;
+ case datalog::OP_RA_SELECT: return Z3_OP_RA_SELECT;
+ case datalog::OP_RA_CLONE: return Z3_OP_RA_CLONE;
+ case datalog::OP_DL_CONSTANT: return Z3_OP_FD_CONSTANT;
+ case datalog::OP_DL_LT: return Z3_OP_FD_LT;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_seq(decl_kind k) {
+ switch (k) {
+ case OP_SEQ_UNIT: return Z3_OP_SEQ_UNIT;
+ case OP_SEQ_EMPTY: return Z3_OP_SEQ_EMPTY;
+ case OP_SEQ_CONCAT: return Z3_OP_SEQ_CONCAT;
+ case OP_SEQ_PREFIX: return Z3_OP_SEQ_PREFIX;
+ case OP_SEQ_SUFFIX: return Z3_OP_SEQ_SUFFIX;
+ case OP_SEQ_CONTAINS: return Z3_OP_SEQ_CONTAINS;
+ case OP_SEQ_EXTRACT: return Z3_OP_SEQ_EXTRACT;
+ case OP_SEQ_REPLACE: return Z3_OP_SEQ_REPLACE;
+ case OP_SEQ_REPLACE_RE: return Z3_OP_SEQ_REPLACE_RE;
+ case OP_SEQ_REPLACE_RE_ALL: return Z3_OP_SEQ_REPLACE_RE_ALL;
+ case OP_SEQ_REPLACE_ALL: return Z3_OP_SEQ_REPLACE_ALL;
+ case OP_SEQ_AT: return Z3_OP_SEQ_AT;
+ case OP_SEQ_NTH: return Z3_OP_SEQ_NTH;
+ case OP_SEQ_LENGTH: return Z3_OP_SEQ_LENGTH;
+ case OP_SEQ_INDEX: return Z3_OP_SEQ_INDEX;
+ case OP_SEQ_TO_RE: return Z3_OP_SEQ_TO_RE;
+ case OP_SEQ_IN_RE: return Z3_OP_SEQ_IN_RE;
+ case OP_SEQ_MAP: return Z3_OP_SEQ_MAP;
+ case OP_SEQ_MAPI: return Z3_OP_SEQ_MAPI;
+ case OP_SEQ_FOLDL: return Z3_OP_SEQ_FOLDL;
+ case OP_SEQ_FOLDLI: return Z3_OP_SEQ_FOLDLI;
+ case _OP_STRING_STRREPL: return Z3_OP_SEQ_REPLACE;
+ case _OP_STRING_CONCAT: return Z3_OP_SEQ_CONCAT;
+ case _OP_STRING_LENGTH: return Z3_OP_SEQ_LENGTH;
+ case _OP_STRING_STRCTN: return Z3_OP_SEQ_CONTAINS;
+ case _OP_STRING_PREFIX: return Z3_OP_SEQ_PREFIX;
+ case _OP_STRING_SUFFIX: return Z3_OP_SEQ_SUFFIX;
+ case _OP_STRING_IN_REGEXP: return Z3_OP_SEQ_IN_RE;
+ case _OP_STRING_TO_REGEXP: return Z3_OP_SEQ_TO_RE;
+ case _OP_STRING_CHARAT: return Z3_OP_SEQ_AT;
+ case _OP_STRING_SUBSTR: return Z3_OP_SEQ_EXTRACT;
+ case _OP_STRING_STRIDOF: return Z3_OP_SEQ_INDEX;
+ case _OP_REGEXP_EMPTY: return Z3_OP_RE_EMPTY_SET;
+ case _OP_REGEXP_FULL_CHAR: return Z3_OP_RE_FULL_SET;
+ case OP_STRING_STOI: return Z3_OP_STR_TO_INT;
+ case OP_STRING_ITOS: return Z3_OP_INT_TO_STR;
+ case OP_STRING_TO_CODE: return Z3_OP_STR_TO_CODE;
+ case OP_STRING_FROM_CODE: return Z3_OP_STR_FROM_CODE;
+ case OP_STRING_UBVTOS: return Z3_OP_UBV_TO_STR;
+ case OP_STRING_SBVTOS: return Z3_OP_SBV_TO_STR;
+ case OP_STRING_LT: return Z3_OP_STRING_LT;
+ case OP_STRING_LE: return Z3_OP_STRING_LE;
+ case OP_RE_PLUS: return Z3_OP_RE_PLUS;
+ case OP_RE_STAR: return Z3_OP_RE_STAR;
+ case OP_RE_OPTION: return Z3_OP_RE_OPTION;
+ case OP_RE_RANGE: return Z3_OP_RE_RANGE;
+ case OP_RE_CONCAT: return Z3_OP_RE_CONCAT;
+ case OP_RE_UNION: return Z3_OP_RE_UNION;
+ case OP_RE_DIFF: return Z3_OP_RE_DIFF;
+ case OP_RE_INTERSECT: return Z3_OP_RE_INTERSECT;
+ case OP_RE_LOOP: return Z3_OP_RE_LOOP;
+ case OP_RE_POWER: return Z3_OP_RE_POWER;
+ case OP_RE_COMPLEMENT: return Z3_OP_RE_COMPLEMENT;
+ case OP_RE_EMPTY_SET: return Z3_OP_RE_EMPTY_SET;
+ case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET;
+ case OP_RE_FULL_CHAR_SET: return Z3_OP_RE_FULL_CHAR_SET;
+ case OP_RE_OF_PRED: return Z3_OP_RE_OF_PRED;
+ case OP_RE_REVERSE: return Z3_OP_RE_REVERSE;
+ case OP_RE_DERIVATIVE: return Z3_OP_RE_DERIVATIVE;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_char(decl_kind k) {
+ switch (k) {
+ case OP_CHAR_CONST: return Z3_OP_CHAR_CONST;
+ case OP_CHAR_LE: return Z3_OP_CHAR_LE;
+ case OP_CHAR_TO_INT: return Z3_OP_CHAR_TO_INT;
+ case OP_CHAR_TO_BV: return Z3_OP_CHAR_TO_BV;
+ case OP_CHAR_FROM_BV: return Z3_OP_CHAR_FROM_BV;
+ case OP_CHAR_IS_DIGIT: return Z3_OP_CHAR_IS_DIGIT;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_fpa(decl_kind k) {
+ switch (k) {
+ case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN;
+ case OP_FPA_RM_NEAREST_TIES_TO_AWAY: return Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY;
+ case OP_FPA_RM_TOWARD_POSITIVE: return Z3_OP_FPA_RM_TOWARD_POSITIVE;
+ case OP_FPA_RM_TOWARD_NEGATIVE: return Z3_OP_FPA_RM_TOWARD_NEGATIVE;
+ case OP_FPA_RM_TOWARD_ZERO: return Z3_OP_FPA_RM_TOWARD_ZERO;
+ case OP_FPA_NUM: return Z3_OP_FPA_NUM;
+ case OP_FPA_PLUS_INF: return Z3_OP_FPA_PLUS_INF;
+ case OP_FPA_MINUS_INF: return Z3_OP_FPA_MINUS_INF;
+ case OP_FPA_NAN: return Z3_OP_FPA_NAN;
+ case OP_FPA_MINUS_ZERO: return Z3_OP_FPA_MINUS_ZERO;
+ case OP_FPA_PLUS_ZERO: return Z3_OP_FPA_PLUS_ZERO;
+ case OP_FPA_ADD: return Z3_OP_FPA_ADD;
+ case OP_FPA_SUB: return Z3_OP_FPA_SUB;
+ case OP_FPA_NEG: return Z3_OP_FPA_NEG;
+ case OP_FPA_MUL: return Z3_OP_FPA_MUL;
+ case OP_FPA_DIV: return Z3_OP_FPA_DIV;
+ case OP_FPA_REM: return Z3_OP_FPA_REM;
+ case OP_FPA_ABS: return Z3_OP_FPA_ABS;
+ case OP_FPA_MIN: return Z3_OP_FPA_MIN;
+ case OP_FPA_MAX: return Z3_OP_FPA_MAX;
+ case OP_FPA_FMA: return Z3_OP_FPA_FMA;
+ case OP_FPA_SQRT: return Z3_OP_FPA_SQRT;
+ case OP_FPA_EQ: return Z3_OP_FPA_EQ;
+ case OP_FPA_ROUND_TO_INTEGRAL: return Z3_OP_FPA_ROUND_TO_INTEGRAL;
+ case OP_FPA_LT: return Z3_OP_FPA_LT;
+ case OP_FPA_GT: return Z3_OP_FPA_GT;
+ case OP_FPA_LE: return Z3_OP_FPA_LE;
+ case OP_FPA_GE: return Z3_OP_FPA_GE;
+ case OP_FPA_IS_NAN: return Z3_OP_FPA_IS_NAN;
+ case OP_FPA_IS_INF: return Z3_OP_FPA_IS_INF;
+ case OP_FPA_IS_ZERO: return Z3_OP_FPA_IS_ZERO;
+ case OP_FPA_IS_NORMAL: return Z3_OP_FPA_IS_NORMAL;
+ case OP_FPA_IS_SUBNORMAL: return Z3_OP_FPA_IS_SUBNORMAL;
+ case OP_FPA_IS_NEGATIVE: return Z3_OP_FPA_IS_NEGATIVE;
+ case OP_FPA_IS_POSITIVE: return Z3_OP_FPA_IS_POSITIVE;
+ case OP_FPA_FP: return Z3_OP_FPA_FP;
+ case OP_FPA_TO_FP: return Z3_OP_FPA_TO_FP;
+ case OP_FPA_TO_FP_UNSIGNED: return Z3_OP_FPA_TO_FP_UNSIGNED;
+ case OP_FPA_TO_UBV: return Z3_OP_FPA_TO_UBV;
+ case OP_FPA_TO_SBV: return Z3_OP_FPA_TO_SBV;
+ case OP_FPA_TO_REAL: return Z3_OP_FPA_TO_REAL;
+ case OP_FPA_TO_IEEE_BV: return Z3_OP_FPA_TO_IEEE_BV;
+ case OP_FPA_BVWRAP: return Z3_OP_FPA_BVWRAP;
+ case OP_FPA_BV2RM: return Z3_OP_FPA_BV2RM;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_label(decl_kind k) {
+ switch(k) {
+ case OP_LABEL: return Z3_OP_LABEL;
+ case OP_LABEL_LIT: return Z3_OP_LABEL_LIT;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_pb(decl_kind k) {
+ switch(k) {
+ case OP_PB_LE: return Z3_OP_PB_LE;
+ case OP_PB_GE: return Z3_OP_PB_GE;
+ case OP_PB_EQ: return Z3_OP_PB_EQ;
+ case OP_AT_MOST_K: return Z3_OP_PB_AT_MOST;
+ case OP_AT_LEAST_K: return Z3_OP_PB_AT_LEAST;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
+ static Z3_decl_kind get_decl_kind_finite_set(decl_kind k) {
+ switch(k) {
+ case OP_FINITE_SET_EMPTY: return Z3_OP_FINITE_SET_EMPTY;
+ case OP_FINITE_SET_SINGLETON: return Z3_OP_FINITE_SET_SINGLETON;
+ case OP_FINITE_SET_UNION: return Z3_OP_FINITE_SET_UNION;
+ case OP_FINITE_SET_INTERSECT: return Z3_OP_FINITE_SET_INTERSECT;
+ case OP_FINITE_SET_DIFFERENCE: return Z3_OP_FINITE_SET_DIFFERENCE;
+ case OP_FINITE_SET_IN: return Z3_OP_FINITE_SET_IN;
+ case OP_FINITE_SET_SIZE: return Z3_OP_FINITE_SET_SIZE;
+ case OP_FINITE_SET_SUBSET: return Z3_OP_FINITE_SET_SUBSET;
+ case OP_FINITE_SET_MAP: return Z3_OP_FINITE_SET_MAP;
+ case OP_FINITE_SET_FILTER: return Z3_OP_FINITE_SET_FILTER;
+ case OP_FINITE_SET_RANGE: return Z3_OP_FINITE_SET_RANGE;
+ case OP_FINITE_SET_EXT: return Z3_OP_FINITE_SET_EXT;
+ case OP_FINITE_SET_MAP_INVERSE: return Z3_OP_FINITE_SET_MAP_INVERSE;
+ default: return Z3_OP_INTERNAL;
+ }
+ }
+
Z3_decl_kind Z3_API Z3_get_decl_kind(Z3_context c, Z3_func_decl d) {
Z3_TRY;
LOG_Z3_get_decl_kind(c, d);
RESET_ERROR_CODE();
func_decl* _d = to_func_decl(d);
- if (d == nullptr || null_family_id == _d->get_family_id()) {
+ if (d == nullptr || null_family_id == _d->get_family_id())
return Z3_OP_UNINTERPRETED;
- }
- if (mk_c(c)->get_basic_fid() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case OP_TRUE: return Z3_OP_TRUE;
- case OP_FALSE: return Z3_OP_FALSE;
- case OP_EQ: return Z3_OP_EQ;
- case OP_DISTINCT: return Z3_OP_DISTINCT;
- case OP_ITE: return Z3_OP_ITE;
- case OP_AND: return Z3_OP_AND;
- case OP_OR: return Z3_OP_OR;
- case OP_XOR: return Z3_OP_XOR;
- case OP_NOT: return Z3_OP_NOT;
- case OP_IMPLIES: return Z3_OP_IMPLIES;
- case OP_OEQ: return Z3_OP_OEQ;
- case PR_UNDEF: return Z3_OP_PR_UNDEF;
- case PR_TRUE: return Z3_OP_PR_TRUE;
- case PR_ASSERTED: return Z3_OP_PR_ASSERTED;
- case PR_GOAL: return Z3_OP_PR_GOAL;
- case PR_MODUS_PONENS: return Z3_OP_PR_MODUS_PONENS;
- case PR_REFLEXIVITY: return Z3_OP_PR_REFLEXIVITY;
- case PR_SYMMETRY: return Z3_OP_PR_SYMMETRY;
- case PR_TRANSITIVITY: return Z3_OP_PR_TRANSITIVITY;
- case PR_TRANSITIVITY_STAR: return Z3_OP_PR_TRANSITIVITY_STAR;
- case PR_MONOTONICITY: return Z3_OP_PR_MONOTONICITY;
- case PR_QUANT_INTRO: return Z3_OP_PR_QUANT_INTRO;
- case PR_BIND: return Z3_OP_PR_BIND;
- case PR_DISTRIBUTIVITY: return Z3_OP_PR_DISTRIBUTIVITY;
- case PR_AND_ELIM: return Z3_OP_PR_AND_ELIM;
- case PR_NOT_OR_ELIM: return Z3_OP_PR_NOT_OR_ELIM;
- case PR_REWRITE: return Z3_OP_PR_REWRITE;
- case PR_REWRITE_STAR: return Z3_OP_PR_REWRITE_STAR;
- case PR_PULL_QUANT: return Z3_OP_PR_PULL_QUANT;
- case PR_PUSH_QUANT: return Z3_OP_PR_PUSH_QUANT;
- case PR_ELIM_UNUSED_VARS: return Z3_OP_PR_ELIM_UNUSED_VARS;
- case PR_DER: return Z3_OP_PR_DER;
- case PR_QUANT_INST: return Z3_OP_PR_QUANT_INST;
- case PR_HYPOTHESIS: return Z3_OP_PR_HYPOTHESIS;
- case PR_LEMMA: return Z3_OP_PR_LEMMA;
- case PR_UNIT_RESOLUTION: return Z3_OP_PR_UNIT_RESOLUTION;
- case PR_IFF_TRUE: return Z3_OP_PR_IFF_TRUE;
- case PR_IFF_FALSE: return Z3_OP_PR_IFF_FALSE;
- case PR_COMMUTATIVITY: return Z3_OP_PR_COMMUTATIVITY;
- case PR_DEF_AXIOM: return Z3_OP_PR_DEF_AXIOM;
- case PR_ASSUMPTION_ADD: return Z3_OP_PR_ASSUMPTION_ADD;
- case PR_LEMMA_ADD: return Z3_OP_PR_LEMMA_ADD;
- case PR_REDUNDANT_DEL: return Z3_OP_PR_REDUNDANT_DEL;
- case PR_CLAUSE_TRAIL: return Z3_OP_PR_CLAUSE_TRAIL;
- case PR_DEF_INTRO: return Z3_OP_PR_DEF_INTRO;
- case PR_APPLY_DEF: return Z3_OP_PR_APPLY_DEF;
- case PR_IFF_OEQ: return Z3_OP_PR_IFF_OEQ;
- case PR_NNF_POS: return Z3_OP_PR_NNF_POS;
- case PR_NNF_NEG: return Z3_OP_PR_NNF_NEG;
- case PR_SKOLEMIZE: return Z3_OP_PR_SKOLEMIZE;
- case PR_MODUS_PONENS_OEQ: return Z3_OP_PR_MODUS_PONENS_OEQ;
- case PR_TH_LEMMA: return Z3_OP_PR_TH_LEMMA;
- case PR_HYPER_RESOLVE: return Z3_OP_PR_HYPER_RESOLVE;
- default:
- return Z3_OP_INTERNAL;
- }
- }
- if (mk_c(c)->get_arith_fid() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case OP_NUM: return Z3_OP_ANUM;
- case OP_IRRATIONAL_ALGEBRAIC_NUM: return Z3_OP_AGNUM;
- case OP_LE: return Z3_OP_LE;
- case OP_GE: return Z3_OP_GE;
- case OP_LT: return Z3_OP_LT;
- case OP_GT: return Z3_OP_GT;
- case OP_ADD: return Z3_OP_ADD;
- case OP_SUB: return Z3_OP_SUB;
- case OP_UMINUS: return Z3_OP_UMINUS;
- case OP_MUL: return Z3_OP_MUL;
- case OP_DIV: return Z3_OP_DIV;
- case OP_IDIV: return Z3_OP_IDIV;
- case OP_REM: return Z3_OP_REM;
- case OP_MOD: return Z3_OP_MOD;
- case OP_POWER: return Z3_OP_POWER;
- case OP_ABS: return Z3_OP_ABS;
- case OP_TO_REAL: return Z3_OP_TO_REAL;
- case OP_TO_INT: return Z3_OP_TO_INT;
- case OP_IS_INT: return Z3_OP_IS_INT;
- default:
- return Z3_OP_INTERNAL;
- }
- }
- if (mk_c(c)->get_array_fid() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case OP_STORE: return Z3_OP_STORE;
- case OP_SELECT: return Z3_OP_SELECT;
- case OP_CONST_ARRAY: return Z3_OP_CONST_ARRAY;
- case OP_ARRAY_DEFAULT: return Z3_OP_ARRAY_DEFAULT;
- case OP_ARRAY_MAP: return Z3_OP_ARRAY_MAP;
- case OP_SET_UNION: return Z3_OP_SET_UNION;
- case OP_SET_INTERSECT: return Z3_OP_SET_INTERSECT;
- case OP_SET_DIFFERENCE: return Z3_OP_SET_DIFFERENCE;
- case OP_SET_COMPLEMENT: return Z3_OP_SET_COMPLEMENT;
- case OP_SET_SUBSET: return Z3_OP_SET_SUBSET;
- case OP_AS_ARRAY: return Z3_OP_AS_ARRAY;
- case OP_ARRAY_EXT: return Z3_OP_ARRAY_EXT;
- default:
- return Z3_OP_INTERNAL;
- }
- }
+ family_id fid = _d->get_family_id();
+ decl_kind k = _d->get_decl_kind();
- if (mk_c(c)->get_special_relations_fid() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case OP_SPECIAL_RELATION_LO : return Z3_OP_SPECIAL_RELATION_LO;
- case OP_SPECIAL_RELATION_PO : return Z3_OP_SPECIAL_RELATION_PO;
- case OP_SPECIAL_RELATION_PLO: return Z3_OP_SPECIAL_RELATION_PLO;
- case OP_SPECIAL_RELATION_TO : return Z3_OP_SPECIAL_RELATION_TO;
- case OP_SPECIAL_RELATION_TC : return Z3_OP_SPECIAL_RELATION_TC;
- default: UNREACHABLE();
- }
- }
-
-
- if (mk_c(c)->get_bv_fid() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case OP_BV_NUM: return Z3_OP_BNUM;
- case OP_BIT1: return Z3_OP_BIT1;
- case OP_BIT0: return Z3_OP_BIT0;
- case OP_BNEG: return Z3_OP_BNEG;
- case OP_BADD: return Z3_OP_BADD;
- case OP_BSUB: return Z3_OP_BSUB;
- case OP_BMUL: return Z3_OP_BMUL;
- case OP_BSDIV: return Z3_OP_BSDIV;
- case OP_BUDIV: return Z3_OP_BUDIV;
- case OP_BSREM: return Z3_OP_BSREM;
- case OP_BUREM: return Z3_OP_BUREM;
- case OP_BSMOD: return Z3_OP_BSMOD;
- case OP_BSDIV0: return Z3_OP_BSDIV0;
- case OP_BUDIV0: return Z3_OP_BUDIV0;
- case OP_BSREM0: return Z3_OP_BSREM0;
- case OP_BUREM0: return Z3_OP_BUREM0;
- case OP_BSMOD0: return Z3_OP_BSMOD0;
- case OP_ULEQ: return Z3_OP_ULEQ;
- case OP_SLEQ: return Z3_OP_SLEQ;
- case OP_UGEQ: return Z3_OP_UGEQ;
- case OP_SGEQ: return Z3_OP_SGEQ;
- case OP_ULT: return Z3_OP_ULT;
- case OP_SLT: return Z3_OP_SLT;
- case OP_UGT: return Z3_OP_UGT;
- case OP_SGT: return Z3_OP_SGT;
- case OP_BAND: return Z3_OP_BAND;
- case OP_BOR: return Z3_OP_BOR;
- case OP_BNOT: return Z3_OP_BNOT;
- case OP_BXOR: return Z3_OP_BXOR;
- case OP_BNAND: return Z3_OP_BNAND;
- case OP_BNOR: return Z3_OP_BNOR;
- case OP_BXNOR: return Z3_OP_BXNOR;
- case OP_CONCAT: return Z3_OP_CONCAT;
- case OP_SIGN_EXT: return Z3_OP_SIGN_EXT;
- case OP_ZERO_EXT: return Z3_OP_ZERO_EXT;
- case OP_EXTRACT: return Z3_OP_EXTRACT;
- case OP_REPEAT: return Z3_OP_REPEAT;
- case OP_BREDOR: return Z3_OP_BREDOR;
- case OP_BREDAND: return Z3_OP_BREDAND;
- case OP_BCOMP: return Z3_OP_BCOMP;
- case OP_BSHL: return Z3_OP_BSHL;
- case OP_BLSHR: return Z3_OP_BLSHR;
- case OP_BASHR: return Z3_OP_BASHR;
- case OP_ROTATE_LEFT: return Z3_OP_ROTATE_LEFT;
- case OP_ROTATE_RIGHT: return Z3_OP_ROTATE_RIGHT;
- case OP_EXT_ROTATE_LEFT: return Z3_OP_EXT_ROTATE_LEFT;
- case OP_EXT_ROTATE_RIGHT: return Z3_OP_EXT_ROTATE_RIGHT;
- case OP_INT2BV: return Z3_OP_INT2BV;
- case OP_UBV2INT: return Z3_OP_BV2INT;
- case OP_SBV2INT: return Z3_OP_SBV2INT;
- case OP_CARRY: return Z3_OP_CARRY;
- case OP_XOR3: return Z3_OP_XOR3;
- case OP_BIT2BOOL: return Z3_OP_BIT2BOOL;
- case OP_BSMUL_NO_OVFL: return Z3_OP_BSMUL_NO_OVFL;
- case OP_BUMUL_NO_OVFL: return Z3_OP_BUMUL_NO_OVFL;
- case OP_BSMUL_NO_UDFL: return Z3_OP_BSMUL_NO_UDFL;
- case OP_BSDIV_I: return Z3_OP_BSDIV_I;
- case OP_BUDIV_I: return Z3_OP_BUDIV_I;
- case OP_BSREM_I: return Z3_OP_BSREM_I;
- case OP_BUREM_I: return Z3_OP_BUREM_I;
- case OP_BSMOD_I: return Z3_OP_BSMOD_I;
- default:
- return Z3_OP_INTERNAL;
- }
- }
- if (mk_c(c)->get_dt_fid() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR;
- case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER;
- case OP_DT_IS: return Z3_OP_DT_IS;
- case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR;
- case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD;
- default:
- return Z3_OP_INTERNAL;
- }
- }
- if (mk_c(c)->get_datalog_fid() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case datalog::OP_RA_STORE: return Z3_OP_RA_STORE;
- case datalog::OP_RA_EMPTY: return Z3_OP_RA_EMPTY;
- case datalog::OP_RA_IS_EMPTY: return Z3_OP_RA_IS_EMPTY;
- case datalog::OP_RA_JOIN: return Z3_OP_RA_JOIN;
- case datalog::OP_RA_UNION: return Z3_OP_RA_UNION;
- case datalog::OP_RA_WIDEN: return Z3_OP_RA_WIDEN;
- case datalog::OP_RA_PROJECT: return Z3_OP_RA_PROJECT;
- case datalog::OP_RA_FILTER: return Z3_OP_RA_FILTER;
- case datalog::OP_RA_NEGATION_FILTER: return Z3_OP_RA_NEGATION_FILTER;
- case datalog::OP_RA_RENAME: return Z3_OP_RA_RENAME;
- case datalog::OP_RA_COMPLEMENT: return Z3_OP_RA_COMPLEMENT;
- case datalog::OP_RA_SELECT: return Z3_OP_RA_SELECT;
- case datalog::OP_RA_CLONE: return Z3_OP_RA_CLONE;
- case datalog::OP_DL_CONSTANT: return Z3_OP_FD_CONSTANT;
- case datalog::OP_DL_LT: return Z3_OP_FD_LT;
- default:
- return Z3_OP_INTERNAL;
- }
- }
-
- if (mk_c(c)->get_seq_fid() == _d->get_family_id()) {
- switch (_d->get_decl_kind()) {
- case OP_SEQ_UNIT: return Z3_OP_SEQ_UNIT;
- case OP_SEQ_EMPTY: return Z3_OP_SEQ_EMPTY;
- case OP_SEQ_CONCAT: return Z3_OP_SEQ_CONCAT;
- case OP_SEQ_PREFIX: return Z3_OP_SEQ_PREFIX;
- case OP_SEQ_SUFFIX: return Z3_OP_SEQ_SUFFIX;
- case OP_SEQ_CONTAINS: return Z3_OP_SEQ_CONTAINS;
- case OP_SEQ_EXTRACT: return Z3_OP_SEQ_EXTRACT;
- case OP_SEQ_REPLACE: return Z3_OP_SEQ_REPLACE;
- case OP_SEQ_REPLACE_RE: return Z3_OP_SEQ_REPLACE_RE;
- case OP_SEQ_REPLACE_RE_ALL: return Z3_OP_SEQ_REPLACE_RE_ALL;
- case OP_SEQ_REPLACE_ALL: return Z3_OP_SEQ_REPLACE_ALL;
- case OP_SEQ_AT: return Z3_OP_SEQ_AT;
- case OP_SEQ_NTH: return Z3_OP_SEQ_NTH;
- case OP_SEQ_LENGTH: return Z3_OP_SEQ_LENGTH;
- case OP_SEQ_INDEX: return Z3_OP_SEQ_INDEX;
- case OP_SEQ_TO_RE: return Z3_OP_SEQ_TO_RE;
- case OP_SEQ_IN_RE: return Z3_OP_SEQ_IN_RE;
- case OP_SEQ_MAP: return Z3_OP_SEQ_MAP;
- case OP_SEQ_MAPI: return Z3_OP_SEQ_MAPI;
- case OP_SEQ_FOLDL: return Z3_OP_SEQ_FOLDL;
- case OP_SEQ_FOLDLI: return Z3_OP_SEQ_FOLDLI;
-
- case _OP_STRING_STRREPL: return Z3_OP_SEQ_REPLACE;
- case _OP_STRING_CONCAT: return Z3_OP_SEQ_CONCAT;
- case _OP_STRING_LENGTH: return Z3_OP_SEQ_LENGTH;
- case _OP_STRING_STRCTN: return Z3_OP_SEQ_CONTAINS;
- case _OP_STRING_PREFIX: return Z3_OP_SEQ_PREFIX;
- case _OP_STRING_SUFFIX: return Z3_OP_SEQ_SUFFIX;
- case _OP_STRING_IN_REGEXP: return Z3_OP_SEQ_IN_RE;
- case _OP_STRING_TO_REGEXP: return Z3_OP_SEQ_TO_RE;
- case _OP_STRING_CHARAT: return Z3_OP_SEQ_AT;
- case _OP_STRING_SUBSTR: return Z3_OP_SEQ_EXTRACT;
- case _OP_STRING_STRIDOF: return Z3_OP_SEQ_INDEX;
- case _OP_REGEXP_EMPTY: return Z3_OP_RE_EMPTY_SET;
- case _OP_REGEXP_FULL_CHAR: return Z3_OP_RE_FULL_SET;
-
- case OP_STRING_STOI: return Z3_OP_STR_TO_INT;
- case OP_STRING_ITOS: return Z3_OP_INT_TO_STR;
- case OP_STRING_TO_CODE: return Z3_OP_STR_TO_CODE;
- case OP_STRING_FROM_CODE: return Z3_OP_STR_FROM_CODE;
-
- case OP_STRING_UBVTOS: return Z3_OP_UBV_TO_STR;
- case OP_STRING_SBVTOS: return Z3_OP_SBV_TO_STR;
- case OP_STRING_LT: return Z3_OP_STRING_LT;
- case OP_STRING_LE: return Z3_OP_STRING_LE;
-
- case OP_RE_PLUS: return Z3_OP_RE_PLUS;
- case OP_RE_STAR: return Z3_OP_RE_STAR;
- case OP_RE_OPTION: return Z3_OP_RE_OPTION;
- case OP_RE_RANGE: return Z3_OP_RE_RANGE;
- case OP_RE_CONCAT: return Z3_OP_RE_CONCAT;
- case OP_RE_UNION: return Z3_OP_RE_UNION;
- case OP_RE_DIFF: return Z3_OP_RE_DIFF;
- case OP_RE_INTERSECT: return Z3_OP_RE_INTERSECT;
- case OP_RE_LOOP: return Z3_OP_RE_LOOP;
- case OP_RE_POWER: return Z3_OP_RE_POWER;
- case OP_RE_COMPLEMENT: return Z3_OP_RE_COMPLEMENT;
- case OP_RE_EMPTY_SET: return Z3_OP_RE_EMPTY_SET;
-
- case OP_RE_FULL_SEQ_SET: return Z3_OP_RE_FULL_SET;
- case OP_RE_FULL_CHAR_SET: return Z3_OP_RE_FULL_CHAR_SET;
- case OP_RE_OF_PRED: return Z3_OP_RE_OF_PRED;
- case OP_RE_REVERSE: return Z3_OP_RE_REVERSE;
- case OP_RE_DERIVATIVE: return Z3_OP_RE_DERIVATIVE;
- default:
- return Z3_OP_INTERNAL;
- }
- }
-
- if (mk_c(c)->get_char_fid() == _d->get_family_id()) {
- switch (_d->get_decl_kind()) {
- case OP_CHAR_CONST: return Z3_OP_CHAR_CONST;
- case OP_CHAR_LE: return Z3_OP_CHAR_LE;
- case OP_CHAR_TO_INT: return Z3_OP_CHAR_TO_INT;
- case OP_CHAR_TO_BV: return Z3_OP_CHAR_TO_BV;
- case OP_CHAR_FROM_BV: return Z3_OP_CHAR_FROM_BV;
- case OP_CHAR_IS_DIGIT: return Z3_OP_CHAR_IS_DIGIT;
- default:
- return Z3_OP_INTERNAL;
- }
- }
-
- if (mk_c(c)->get_fpa_fid() == _d->get_family_id()) {
- switch (_d->get_decl_kind()) {
- case OP_FPA_RM_NEAREST_TIES_TO_EVEN: return Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN;
- case OP_FPA_RM_NEAREST_TIES_TO_AWAY: return Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY;
- case OP_FPA_RM_TOWARD_POSITIVE: return Z3_OP_FPA_RM_TOWARD_POSITIVE;
- case OP_FPA_RM_TOWARD_NEGATIVE: return Z3_OP_FPA_RM_TOWARD_NEGATIVE;
- case OP_FPA_RM_TOWARD_ZERO: return Z3_OP_FPA_RM_TOWARD_ZERO;
- case OP_FPA_NUM: return Z3_OP_FPA_NUM;
- case OP_FPA_PLUS_INF: return Z3_OP_FPA_PLUS_INF;
- case OP_FPA_MINUS_INF: return Z3_OP_FPA_MINUS_INF;
- case OP_FPA_NAN: return Z3_OP_FPA_NAN;
- case OP_FPA_MINUS_ZERO: return Z3_OP_FPA_MINUS_ZERO;
- case OP_FPA_PLUS_ZERO: return Z3_OP_FPA_PLUS_ZERO;
- case OP_FPA_ADD: return Z3_OP_FPA_ADD;
- case OP_FPA_SUB: return Z3_OP_FPA_SUB;
- case OP_FPA_NEG: return Z3_OP_FPA_NEG;
- case OP_FPA_MUL: return Z3_OP_FPA_MUL;
- case OP_FPA_DIV: return Z3_OP_FPA_DIV;
- case OP_FPA_REM: return Z3_OP_FPA_REM;
- case OP_FPA_ABS: return Z3_OP_FPA_ABS;
- case OP_FPA_MIN: return Z3_OP_FPA_MIN;
- case OP_FPA_MAX: return Z3_OP_FPA_MAX;
- case OP_FPA_FMA: return Z3_OP_FPA_FMA;
- case OP_FPA_SQRT: return Z3_OP_FPA_SQRT;
- case OP_FPA_EQ: return Z3_OP_FPA_EQ;
- case OP_FPA_ROUND_TO_INTEGRAL: return Z3_OP_FPA_ROUND_TO_INTEGRAL;
- case OP_FPA_LT: return Z3_OP_FPA_LT;
- case OP_FPA_GT: return Z3_OP_FPA_GT;
- case OP_FPA_LE: return Z3_OP_FPA_LE;
- case OP_FPA_GE: return Z3_OP_FPA_GE;
- case OP_FPA_IS_NAN: return Z3_OP_FPA_IS_NAN;
- case OP_FPA_IS_INF: return Z3_OP_FPA_IS_INF;
- case OP_FPA_IS_ZERO: return Z3_OP_FPA_IS_ZERO;
- case OP_FPA_IS_NORMAL: return Z3_OP_FPA_IS_NORMAL;
- case OP_FPA_IS_SUBNORMAL: return Z3_OP_FPA_IS_SUBNORMAL;
- case OP_FPA_IS_NEGATIVE: return Z3_OP_FPA_IS_NEGATIVE;
- case OP_FPA_IS_POSITIVE: return Z3_OP_FPA_IS_POSITIVE;
- case OP_FPA_FP: return Z3_OP_FPA_FP;
- case OP_FPA_TO_FP: return Z3_OP_FPA_TO_FP;
- case OP_FPA_TO_FP_UNSIGNED: return Z3_OP_FPA_TO_FP_UNSIGNED;
- case OP_FPA_TO_UBV: return Z3_OP_FPA_TO_UBV;
- case OP_FPA_TO_SBV: return Z3_OP_FPA_TO_SBV;
- case OP_FPA_TO_REAL: return Z3_OP_FPA_TO_REAL;
- case OP_FPA_TO_IEEE_BV: return Z3_OP_FPA_TO_IEEE_BV;
- case OP_FPA_BVWRAP: return Z3_OP_FPA_BVWRAP;
- case OP_FPA_BV2RM: return Z3_OP_FPA_BV2RM;
- return Z3_OP_UNINTERPRETED;
- default:
- return Z3_OP_INTERNAL;
- }
- }
-
- if (mk_c(c)->m().get_label_family_id() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case OP_LABEL: return Z3_OP_LABEL;
- case OP_LABEL_LIT: return Z3_OP_LABEL_LIT;
- default:
- return Z3_OP_INTERNAL;
- }
- }
-
- if (mk_c(c)->get_pb_fid() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case OP_PB_LE: return Z3_OP_PB_LE;
- case OP_PB_GE: return Z3_OP_PB_GE;
- case OP_PB_EQ: return Z3_OP_PB_EQ;
- case OP_AT_MOST_K: return Z3_OP_PB_AT_MOST;
- case OP_AT_LEAST_K: return Z3_OP_PB_AT_LEAST;
- default: return Z3_OP_INTERNAL;
- }
- }
-
- if (mk_c(c)->fsutil().get_family_id() == _d->get_family_id()) {
- switch(_d->get_decl_kind()) {
- case OP_FINITE_SET_EMPTY: return Z3_OP_FINITE_SET_EMPTY;
- case OP_FINITE_SET_SINGLETON: return Z3_OP_FINITE_SET_SINGLETON;
- case OP_FINITE_SET_UNION: return Z3_OP_FINITE_SET_UNION;
- case OP_FINITE_SET_INTERSECT: return Z3_OP_FINITE_SET_INTERSECT;
- case OP_FINITE_SET_DIFFERENCE: return Z3_OP_FINITE_SET_DIFFERENCE;
- case OP_FINITE_SET_IN: return Z3_OP_FINITE_SET_IN;
- case OP_FINITE_SET_SIZE: return Z3_OP_FINITE_SET_SIZE;
- case OP_FINITE_SET_SUBSET: return Z3_OP_FINITE_SET_SUBSET;
- case OP_FINITE_SET_MAP: return Z3_OP_FINITE_SET_MAP;
- case OP_FINITE_SET_FILTER: return Z3_OP_FINITE_SET_FILTER;
- case OP_FINITE_SET_RANGE: return Z3_OP_FINITE_SET_RANGE;
- case OP_FINITE_SET_EXT: return Z3_OP_FINITE_SET_EXT;
- case OP_FINITE_SET_MAP_INVERSE: return Z3_OP_FINITE_SET_MAP_INVERSE;
- default: return Z3_OP_INTERNAL;
- }
- }
-
- if (mk_c(c)->recfun().get_family_id() == _d->get_family_id())
+ if (mk_c(c)->get_basic_fid() == fid)
+ return get_decl_kind_basic(k);
+ if (mk_c(c)->get_arith_fid() == fid)
+ return get_decl_kind_arith(k);
+ if (mk_c(c)->get_bv_fid() == fid)
+ return get_decl_kind_bv(k);
+ if (mk_c(c)->get_array_fid() == fid)
+ return get_decl_kind_array(k);
+ if (mk_c(c)->get_dt_fid() == fid)
+ return get_decl_kind_dt(k);
+ if (mk_c(c)->get_seq_fid() == fid)
+ return get_decl_kind_seq(k);
+ if (mk_c(c)->get_fpa_fid() == fid)
+ return get_decl_kind_fpa(k);
+ if (mk_c(c)->get_datalog_fid() == fid)
+ return get_decl_kind_datalog(k);
+ if (mk_c(c)->get_pb_fid() == fid)
+ return get_decl_kind_pb(k);
+ if (mk_c(c)->get_special_relations_fid() == fid)
+ return get_decl_kind_special_relations(k);
+ if (mk_c(c)->get_char_fid() == fid)
+ return get_decl_kind_char(k);
+ if (mk_c(c)->m().get_label_family_id() == fid)
+ return get_decl_kind_label(k);
+ if (mk_c(c)->fsutil().get_family_id() == fid)
+ return get_decl_kind_finite_set(k);
+ if (mk_c(c)->recfun().get_family_id() == fid)
return Z3_OP_RECURSIVE;
return Z3_OP_UNINTERPRETED;
@@ -1505,11 +1522,7 @@ extern "C" {
return 0;
}
var* va = to_var(_a);
- if (va) {
- return va->get_idx();
- }
- SET_ERROR_CODE(Z3_INVALID_ARG, nullptr);
- return 0;
+ return va->get_idx();
Z3_CATCH_RETURN(0);
}
diff --git a/src/api/dotnet/CMakeLists.txt b/src/api/dotnet/CMakeLists.txt
index c309f4027..3d3864139 100644
--- a/src/api/dotnet/CMakeLists.txt
+++ b/src/api/dotnet/CMakeLists.txt
@@ -147,8 +147,13 @@ endforeach()
set(Z3_DOTNET_NUPKG_VERSION "${VER_MAJOR}.${VER_MINOR}.${VER_BUILD}")
if(TARGET_ARCHITECTURE STREQUAL "i686")
set(Z3_DOTNET_PLATFORM "x86")
+ set(Z3_DOTNET_WIN_RID "win-x86")
+elseif(TARGET_ARCHITECTURE STREQUAL "arm64")
+ set(Z3_DOTNET_PLATFORM "AnyCPU")
+ set(Z3_DOTNET_WIN_RID "win-arm64")
else()
set(Z3_DOTNET_PLATFORM "AnyCPU")
+ set(Z3_DOTNET_WIN_RID "win-x64")
endif()
# TODO conditional for signing. we can then enable the ``Release_delaysign`` configuration
diff --git a/src/api/dotnet/Context.cs b/src/api/dotnet/Context.cs
index 52c9af8f6..cba223f14 100644
--- a/src/api/dotnet/Context.cs
+++ b/src/api/dotnet/Context.cs
@@ -562,6 +562,63 @@ namespace Microsoft.Z3
}
}
+ ///
+ /// Create a type variable sort for use as a parameter in polymorphic datatypes.
+ ///
+ /// name of the type variable
+ public Sort MkTypeVariable(Symbol name)
+ {
+ Debug.Assert(name != null);
+ CheckContextMatch(name);
+ return new Sort(this, Native.Z3_mk_type_variable(nCtx, name.NativeObject));
+ }
+
+ ///
+ /// Create a type variable sort for use as a parameter in polymorphic datatypes.
+ ///
+ /// name of the type variable
+ public Sort MkTypeVariable(string name)
+ {
+ using var symbol = MkSymbol(name);
+ return MkTypeVariable(symbol);
+ }
+
+ ///
+ /// Create a polymorphic datatype sort with explicit type parameters.
+ /// Type parameters should be sorts created with .
+ ///
+ /// name of the datatype sort
+ /// array of type variable sorts
+ /// array of constructors
+ public DatatypeSort MkPolymorphicDatatypeSort(Symbol name, Sort[] typeParams, Constructor[] constructors)
+ {
+ Debug.Assert(name != null);
+ Debug.Assert(typeParams != null);
+ Debug.Assert(constructors != null);
+ Debug.Assert(constructors.All(c => c != null));
+
+ CheckContextMatch(name);
+ CheckContextMatch(typeParams);
+ CheckContextMatch(constructors);
+ return new DatatypeSort(this,
+ Native.Z3_mk_polymorphic_datatype(nCtx, name.NativeObject,
+ (uint)typeParams.Length, AST.ArrayToNative(typeParams),
+ (uint)constructors.Length, Z3Object.ArrayToNative(constructors)));
+ }
+
+ ///
+ /// Create a polymorphic datatype sort with explicit type parameters.
+ /// Type parameters should be sorts created with .
+ ///
+ /// name of the datatype sort
+ /// array of type variable sorts
+ /// array of constructors
+ public DatatypeSort MkPolymorphicDatatypeSort(string name, Sort[] typeParams, Constructor[] constructors)
+ {
+ using var symbol = MkSymbol(name);
+ return MkPolymorphicDatatypeSort(symbol, typeParams, constructors);
+ }
+
///
/// Update a datatype field at expression t with value v.
/// The function performs a record update at t. The field
diff --git a/src/api/dotnet/Microsoft.Z3.csproj.in b/src/api/dotnet/Microsoft.Z3.csproj.in
index ec136809d..cf5aacf46 100644
--- a/src/api/dotnet/Microsoft.Z3.csproj.in
+++ b/src/api/dotnet/Microsoft.Z3.csproj.in
@@ -84,10 +84,10 @@ ${Z3_DOTNET_COMPILE_ITEMS}
-
+
-
- runtimes\win-x64\native
+
+ runtimes\${Z3_DOTNET_WIN_RID}\native
runtimes\linux-x64\native
@@ -99,7 +99,7 @@ ${Z3_DOTNET_COMPILE_ITEMS}
-
+
runtimes\win-x86\native
diff --git a/src/api/dotnet/Microsoft.Z3.props b/src/api/dotnet/Microsoft.Z3.props
index a5db71359..4625fdd18 100644
--- a/src/api/dotnet/Microsoft.Z3.props
+++ b/src/api/dotnet/Microsoft.Z3.props
@@ -9,7 +9,8 @@
$(MSBuildThisFileDirectory)..\
- $(Z3_PACKAGE_PATH)runtimes\win-x64\native\libz3.dll
+ $(Z3_PACKAGE_PATH)runtimes\win-arm64\native\libz3.dll
+ $(Z3_PACKAGE_PATH)runtimes\win-x64\native\libz3.dll
$(Z3_PACKAGE_PATH)runtimes\win-x86\native\libz3.dll
$(Z3_PACKAGE_PATH)runtimes\linux-x64\native\libz3.so
diff --git a/src/api/dotnet/Microsoft.Z3.targets b/src/api/dotnet/Microsoft.Z3.targets
index 38e56b350..a1436242c 100644
--- a/src/api/dotnet/Microsoft.Z3.targets
+++ b/src/api/dotnet/Microsoft.Z3.targets
@@ -1,7 +1,7 @@
-
+
%(RecursiveDir)%(FileName)%(Extension)
PreserveNewest
diff --git a/src/api/go/char.go b/src/api/go/char.go
new file mode 100644
index 000000000..846101f96
--- /dev/null
+++ b/src/api/go/char.go
@@ -0,0 +1,43 @@
+package z3
+
+/*
+#include "z3.h"
+*/
+import "C"
+
+// Char operations
+
+// MkCharSort creates the character sort (Unicode characters).
+func (c *Context) MkCharSort() *Sort {
+ return newSort(c, C.Z3_mk_char_sort(c.ptr))
+}
+
+// MkChar creates a character literal from a Unicode code point.
+func (c *Context) MkChar(ch uint) *Expr {
+ return newExpr(c, C.Z3_mk_char(c.ptr, C.uint(ch)))
+}
+
+// MkCharLe creates a character less-than-or-equal predicate (ch1 ≤ ch2).
+func (c *Context) MkCharLe(ch1, ch2 *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_char_le(c.ptr, ch1.ptr, ch2.ptr))
+}
+
+// MkCharToInt converts a character to its integer (Unicode code point) value.
+func (c *Context) MkCharToInt(ch *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_char_to_int(c.ptr, ch.ptr))
+}
+
+// MkCharToBV converts a character to a bit-vector.
+func (c *Context) MkCharToBV(ch *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_char_to_bv(c.ptr, ch.ptr))
+}
+
+// MkCharFromBV converts a bit-vector to a character.
+func (c *Context) MkCharFromBV(bv *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_char_from_bv(c.ptr, bv.ptr))
+}
+
+// MkCharIsDigit creates a predicate that is true if the character is a decimal digit.
+func (c *Context) MkCharIsDigit(ch *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_char_is_digit(c.ptr, ch.ptr))
+}
diff --git a/src/api/go/datatype.go b/src/api/go/datatype.go
index f4ae8a4d4..fc5e1c187 100644
--- a/src/api/go/datatype.go
+++ b/src/api/go/datatype.go
@@ -127,6 +127,41 @@ func (c *Context) MkDatatypeSort(name string, constructors []*Constructor) *Sort
return newSort(c, C.Z3_mk_datatype(c.ptr, sym.ptr, C.uint(numCons), &cons[0]))
}
+// MkPolymorphicDatatypeSort creates a polymorphic datatype sort with explicit type parameters.
+// typeParams should be sorts created with MkTypeVariable.
+// Self-recursive field sorts should be passed as nil; use the fieldSortRefs parameter in
+// MkConstructor to indicate the recursive reference by index.
+func (c *Context) MkPolymorphicDatatypeSort(name string, typeParams []*Sort, constructors []*Constructor) *Sort {
+ sym := c.MkStringSymbol(name)
+
+ numParams := len(typeParams)
+ numCons := len(constructors)
+
+ var paramPtr *C.Z3_sort
+ if numParams > 0 {
+ paramPtrs := make([]C.Z3_sort, numParams)
+ for i, p := range typeParams {
+ paramPtrs[i] = p.ptr
+ }
+ paramPtr = ¶mPtrs[0]
+ }
+
+ var consPtr *C.Z3_constructor
+ if numCons > 0 {
+ consPtrs := make([]C.Z3_constructor, numCons)
+ for i, cons := range constructors {
+ consPtrs[i] = cons.ptr
+ }
+ consPtr = &consPtrs[0]
+ }
+
+ return newSort(c, C.Z3_mk_polymorphic_datatype(
+ c.ptr, sym.ptr,
+ C.uint(numParams), paramPtr,
+ C.uint(numCons), consPtr,
+ ))
+}
+
// MkDatatypeSorts creates multiple mutually recursive datatype sorts.
func (c *Context) MkDatatypeSorts(names []string, constructorLists [][]*Constructor) []*Sort {
numTypes := uint(len(names))
diff --git a/src/api/go/fixedpoint.go b/src/api/go/fixedpoint.go
index ab28569fc..55db062f4 100644
--- a/src/api/go/fixedpoint.go
+++ b/src/api/go/fixedpoint.go
@@ -218,6 +218,59 @@ func (f *Fixedpoint) FromFile(filename string) {
C.Z3_fixedpoint_from_file(f.ctx.ptr, f.ptr, cstr)
}
+// QueryFromLvl poses a query against the asserted rules at the given level.
+// This is a Spacer-specific function.
+func (f *Fixedpoint) QueryFromLvl(query *Expr, lvl uint) Status {
+ result := C.Z3_fixedpoint_query_from_lvl(f.ctx.ptr, f.ptr, query.ptr, C.uint(lvl))
+ switch result {
+ case C.Z3_L_TRUE:
+ return Satisfiable
+ case C.Z3_L_FALSE:
+ return Unsatisfiable
+ default:
+ return Unknown
+ }
+}
+
+// GetGroundSatAnswer retrieves a bottom-up sequence of ground facts.
+// The previous call to Query or QueryFromLvl must have returned Satisfiable.
+// This is a Spacer-specific function.
+func (f *Fixedpoint) GetGroundSatAnswer() *Expr {
+ ptr := C.Z3_fixedpoint_get_ground_sat_answer(f.ctx.ptr, f.ptr)
+ if ptr == nil {
+ return nil
+ }
+ return newExpr(f.ctx, ptr)
+}
+
+// GetRulesAlongTrace returns the list of rules along the counterexample trace.
+// This is a Spacer-specific function.
+func (f *Fixedpoint) GetRulesAlongTrace() *ASTVector {
+ return newASTVector(f.ctx, C.Z3_fixedpoint_get_rules_along_trace(f.ctx.ptr, f.ptr))
+}
+
+// GetRuleNamesAlongTrace returns the list of rule names along the counterexample trace.
+// This is a Spacer-specific function.
+func (f *Fixedpoint) GetRuleNamesAlongTrace() *Symbol {
+ return newSymbol(f.ctx, C.Z3_fixedpoint_get_rule_names_along_trace(f.ctx.ptr, f.ptr))
+}
+
+// AddInvariant adds an assumed invariant for the predicate pred.
+// This is a Spacer-specific function.
+func (f *Fixedpoint) AddInvariant(pred *FuncDecl, property *Expr) {
+ C.Z3_fixedpoint_add_invariant(f.ctx.ptr, f.ptr, pred.ptr, property.ptr)
+}
+
+// GetReachable retrieves the reachable states of a predicate.
+// This is a Spacer-specific function.
+func (f *Fixedpoint) GetReachable(pred *FuncDecl) *Expr {
+ ptr := C.Z3_fixedpoint_get_reachable(f.ctx.ptr, f.ptr, pred.ptr)
+ if ptr == nil {
+ return nil
+ }
+ return newExpr(f.ctx, ptr)
+}
+
// Statistics represents statistics for Z3 solvers
type Statistics struct {
ctx *Context
diff --git a/src/api/go/relations.go b/src/api/go/relations.go
new file mode 100644
index 000000000..637c6ab5b
--- /dev/null
+++ b/src/api/go/relations.go
@@ -0,0 +1,38 @@
+package z3
+
+/*
+#include "z3.h"
+*/
+import "C"
+
+// Special relation constructors
+
+// MkLinearOrder creates a linear (total) order relation over the given sort.
+// The id parameter distinguishes multiple linear orders over the same sort.
+func (c *Context) MkLinearOrder(s *Sort, id uint) *FuncDecl {
+ return newFuncDecl(c, C.Z3_mk_linear_order(c.ptr, s.ptr, C.uint(id)))
+}
+
+// MkPartialOrder creates a partial order relation over the given sort.
+// The id parameter distinguishes multiple partial orders over the same sort.
+func (c *Context) MkPartialOrder(s *Sort, id uint) *FuncDecl {
+ return newFuncDecl(c, C.Z3_mk_partial_order(c.ptr, s.ptr, C.uint(id)))
+}
+
+// MkPiecewiseLinearOrder creates a piecewise linear order relation over the given sort.
+// The id parameter distinguishes multiple piecewise linear orders over the same sort.
+func (c *Context) MkPiecewiseLinearOrder(s *Sort, id uint) *FuncDecl {
+ return newFuncDecl(c, C.Z3_mk_piecewise_linear_order(c.ptr, s.ptr, C.uint(id)))
+}
+
+// MkTreeOrder creates a tree order relation over the given sort.
+// The id parameter distinguishes multiple tree orders over the same sort.
+func (c *Context) MkTreeOrder(s *Sort, id uint) *FuncDecl {
+ return newFuncDecl(c, C.Z3_mk_tree_order(c.ptr, s.ptr, C.uint(id)))
+}
+
+// MkTransitiveClosure creates the transitive closure of a binary relation.
+// The resulting relation is recursive.
+func (c *Context) MkTransitiveClosure(f *FuncDecl) *FuncDecl {
+ return newFuncDecl(c, C.Z3_mk_transitive_closure(c.ptr, f.ptr))
+}
diff --git a/src/api/go/set.go b/src/api/go/set.go
new file mode 100644
index 000000000..53b1be672
--- /dev/null
+++ b/src/api/go/set.go
@@ -0,0 +1,77 @@
+package z3
+
+/*
+#include "z3.h"
+*/
+import "C"
+
+// Regular (array-encoded) Set operations
+
+// MkSetSort creates a set sort with the given element sort.
+func (c *Context) MkSetSort(elemSort *Sort) *Sort {
+ return newSort(c, C.Z3_mk_set_sort(c.ptr, elemSort.ptr))
+}
+
+// MkEmptySet creates an empty set of the given element sort.
+func (c *Context) MkEmptySet(elemSort *Sort) *Expr {
+ return newExpr(c, C.Z3_mk_empty_set(c.ptr, elemSort.ptr))
+}
+
+// MkFullSet creates the full set (universe) of the given element sort.
+func (c *Context) MkFullSet(elemSort *Sort) *Expr {
+ return newExpr(c, C.Z3_mk_full_set(c.ptr, elemSort.ptr))
+}
+
+// MkSetAdd adds an element to a set.
+func (c *Context) MkSetAdd(set, elem *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_set_add(c.ptr, set.ptr, elem.ptr))
+}
+
+// MkSetDel removes an element from a set.
+func (c *Context) MkSetDel(set, elem *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_set_del(c.ptr, set.ptr, elem.ptr))
+}
+
+// MkSetUnion creates the union of two or more sets.
+func (c *Context) MkSetUnion(sets ...*Expr) *Expr {
+ if len(sets) == 0 {
+ return nil
+ }
+ cSets := make([]C.Z3_ast, len(sets))
+ for i, s := range sets {
+ cSets[i] = s.ptr
+ }
+ return newExpr(c, C.Z3_mk_set_union(c.ptr, C.uint(len(sets)), &cSets[0]))
+}
+
+// MkSetIntersect creates the intersection of two or more sets.
+func (c *Context) MkSetIntersect(sets ...*Expr) *Expr {
+ if len(sets) == 0 {
+ return nil
+ }
+ cSets := make([]C.Z3_ast, len(sets))
+ for i, s := range sets {
+ cSets[i] = s.ptr
+ }
+ return newExpr(c, C.Z3_mk_set_intersect(c.ptr, C.uint(len(sets)), &cSets[0]))
+}
+
+// MkSetDifference creates the set difference (set1 \ set2).
+func (c *Context) MkSetDifference(set1, set2 *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_set_difference(c.ptr, set1.ptr, set2.ptr))
+}
+
+// MkSetComplement creates the complement of a set.
+func (c *Context) MkSetComplement(set *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_set_complement(c.ptr, set.ptr))
+}
+
+// MkSetMember creates a membership predicate: elem ∈ set.
+func (c *Context) MkSetMember(elem, set *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_set_member(c.ptr, elem.ptr, set.ptr))
+}
+
+// MkSetSubset creates a subset predicate: set1 ⊆ set2.
+func (c *Context) MkSetSubset(set1, set2 *Expr) *Expr {
+ return newExpr(c, C.Z3_mk_set_subset(c.ptr, set1.ptr, set2.ptr))
+}
diff --git a/src/api/go/spacer.go b/src/api/go/spacer.go
new file mode 100644
index 000000000..6b79c8ce6
--- /dev/null
+++ b/src/api/go/spacer.go
@@ -0,0 +1,131 @@
+// Copyright (c) Microsoft Corporation 2025
+// Z3 Go API: Spacer quantifier elimination and model projection functions
+
+package z3
+
+/*
+#include "z3.h"
+#include
+*/
+import "C"
+import "runtime"
+
+// ASTMap represents a mapping from Z3 ASTs to Z3 ASTs.
+type ASTMap struct {
+ ctx *Context
+ ptr C.Z3_ast_map
+}
+
+// newASTMap creates a new ASTMap and manages its reference count.
+func newASTMap(ctx *Context, ptr C.Z3_ast_map) *ASTMap {
+ m := &ASTMap{ctx: ctx, ptr: ptr}
+ C.Z3_ast_map_inc_ref(ctx.ptr, ptr)
+ runtime.SetFinalizer(m, func(am *ASTMap) {
+ C.Z3_ast_map_dec_ref(am.ctx.ptr, am.ptr)
+ })
+ return m
+}
+
+// MkASTMap creates a new empty AST map.
+func (c *Context) MkASTMap() *ASTMap {
+ return newASTMap(c, C.Z3_mk_ast_map(c.ptr))
+}
+
+// Contains returns true if the map contains the key k.
+func (m *ASTMap) Contains(k *Expr) bool {
+ return bool(C.Z3_ast_map_contains(m.ctx.ptr, m.ptr, k.ptr))
+}
+
+// Find returns the value associated with key k.
+func (m *ASTMap) Find(k *Expr) *Expr {
+ return newExpr(m.ctx, C.Z3_ast_map_find(m.ctx.ptr, m.ptr, k.ptr))
+}
+
+// Insert associates key k with value v in the map.
+func (m *ASTMap) Insert(k, v *Expr) {
+ C.Z3_ast_map_insert(m.ctx.ptr, m.ptr, k.ptr, v.ptr)
+}
+
+// Erase removes the entry with key k from the map.
+func (m *ASTMap) Erase(k *Expr) {
+ C.Z3_ast_map_erase(m.ctx.ptr, m.ptr, k.ptr)
+}
+
+// Reset removes all entries from the map.
+func (m *ASTMap) Reset() {
+ C.Z3_ast_map_reset(m.ctx.ptr, m.ptr)
+}
+
+// Size returns the number of entries in the map.
+func (m *ASTMap) Size() uint {
+ return uint(C.Z3_ast_map_size(m.ctx.ptr, m.ptr))
+}
+
+// Keys returns all keys in the map as an ASTVector.
+func (m *ASTMap) Keys() *ASTVector {
+ return newASTVector(m.ctx, C.Z3_ast_map_keys(m.ctx.ptr, m.ptr))
+}
+
+// String returns the string representation of the map.
+func (m *ASTMap) String() string {
+ return C.GoString(C.Z3_ast_map_to_string(m.ctx.ptr, m.ptr))
+}
+
+// ModelExtrapolate extrapolates a model of a formula.
+// Given a model m and formula fml, returns an expression that is implied by fml
+// and is consistent with the model. This is a Spacer-specific function.
+func (c *Context) ModelExtrapolate(m *Model, fml *Expr) *Expr {
+ return newExpr(c, C.Z3_model_extrapolate(c.ptr, m.ptr, fml.ptr))
+}
+
+// QeLite performs best-effort quantifier elimination.
+// vars is a vector of variables to eliminate, body is the formula.
+func (c *Context) QeLite(vars *ASTVector, body *Expr) *Expr {
+ return newExpr(c, C.Z3_qe_lite(c.ptr, vars.ptr, body.ptr))
+}
+
+// QeModelProject projects variables given a model.
+// bound is a slice of application expressions representing the variables to project.
+func (c *Context) QeModelProject(m *Model, bound []*Expr, body *Expr) *Expr {
+ n := len(bound)
+ cBound := make([]C.Z3_app, n)
+ for i, b := range bound {
+ cBound[i] = C.Z3_to_app(c.ptr, b.ptr)
+ }
+ var boundPtr *C.Z3_app
+ if n > 0 {
+ boundPtr = &cBound[0]
+ }
+ return newExpr(c, C.Z3_qe_model_project(c.ptr, m.ptr, C.uint(n), boundPtr, body.ptr))
+}
+
+// QeModelProjectSkolem projects variables given a model, storing the skolem witnesses in map_.
+// bound is a slice of application expressions representing the variables to project.
+func (c *Context) QeModelProjectSkolem(m *Model, bound []*Expr, body *Expr, map_ *ASTMap) *Expr {
+ n := len(bound)
+ cBound := make([]C.Z3_app, n)
+ for i, b := range bound {
+ cBound[i] = C.Z3_to_app(c.ptr, b.ptr)
+ }
+ var boundPtr *C.Z3_app
+ if n > 0 {
+ boundPtr = &cBound[0]
+ }
+ return newExpr(c, C.Z3_qe_model_project_skolem(c.ptr, m.ptr, C.uint(n), boundPtr, body.ptr, map_.ptr))
+}
+
+// QeModelProjectWithWitness projects variables given a model and extracts witnesses.
+// The map_ is populated with bindings of projected variables to witness terms.
+// bound is a slice of application expressions representing the variables to project.
+func (c *Context) QeModelProjectWithWitness(m *Model, bound []*Expr, body *Expr, map_ *ASTMap) *Expr {
+ n := len(bound)
+ cBound := make([]C.Z3_app, n)
+ for i, b := range bound {
+ cBound[i] = C.Z3_to_app(c.ptr, b.ptr)
+ }
+ var boundPtr *C.Z3_app
+ if n > 0 {
+ boundPtr = &cBound[0]
+ }
+ return newExpr(c, C.Z3_qe_model_project_with_witness(c.ptr, m.ptr, C.uint(n), boundPtr, body.ptr, map_.ptr))
+}
diff --git a/src/api/go/tactic.go b/src/api/go/tactic.go
index 8961c2df8..e3b37622d 100644
--- a/src/api/go/tactic.go
+++ b/src/api/go/tactic.go
@@ -228,6 +228,17 @@ func (g *Goal) String() string {
return C.GoString(C.Z3_goal_to_string(g.ctx.ptr, g.ptr))
}
+// IsInconsistent returns true if the goal contains the formula false.
+func (g *Goal) IsInconsistent() bool {
+ return bool(C.Z3_goal_inconsistent(g.ctx.ptr, g.ptr))
+}
+
+// ToDimacsString converts the goal to a string in DIMACS format.
+// If includeNames is true, formula names are included as comments.
+func (g *Goal) ToDimacsString(includeNames bool) string {
+ return C.GoString(C.Z3_goal_to_dimacs_string(g.ctx.ptr, g.ptr, C.bool(includeNames)))
+}
+
// ApplyResult represents the result of applying a tactic to a goal.
type ApplyResult struct {
ctx *Context
diff --git a/src/api/go/z3.go b/src/api/go/z3.go
index 0d1322ea8..4e982111e 100644
--- a/src/api/go/z3.go
+++ b/src/api/go/z3.go
@@ -240,6 +240,45 @@ func newExpr(ctx *Context, ptr C.Z3_ast) *Expr {
return expr
}
+// intsToCs converts a []int slice to []C.int, returning the slice and
+// a pointer to its first element (nil if empty).
+func intsToCs(ints []int) ([]C.int, *C.int) {
+ if len(ints) == 0 {
+ return nil, nil
+ }
+ cInts := make([]C.int, len(ints))
+ for i, v := range ints {
+ cInts[i] = C.int(v)
+ }
+ return cInts, &cInts[0]
+}
+
+// exprsToASTs converts a []*Expr slice to []C.Z3_ast, returning the slice and
+// a pointer to its first element (nil if empty).
+func exprsToASTs(exprs []*Expr) ([]C.Z3_ast, *C.Z3_ast) {
+ if len(exprs) == 0 {
+ return nil, nil
+ }
+ cExprs := make([]C.Z3_ast, len(exprs))
+ for i, e := range exprs {
+ cExprs[i] = e.ptr
+ }
+ return cExprs, &cExprs[0]
+}
+
+// sortsToCSorts converts a []*Sort slice to []C.Z3_sort, returning the slice and
+// a pointer to its first element (nil if empty).
+func sortsToCSorts(sorts []*Sort) ([]C.Z3_sort, *C.Z3_sort) {
+ if len(sorts) == 0 {
+ return nil, nil
+ }
+ cSorts := make([]C.Z3_sort, len(sorts))
+ for i, s := range sorts {
+ cSorts[i] = s.ptr
+ }
+ return cSorts, &cSorts[0]
+}
+
// String returns the string representation of the expression.
func (e *Expr) String() string {
return C.GoString(C.Z3_ast_to_string(e.ctx.ptr, e.ptr))
@@ -291,6 +330,21 @@ func newASTVector(ctx *Context, ptr C.Z3_ast_vector) *ASTVector {
return v
}
+// Size returns the number of ASTs in the vector.
+func (v *ASTVector) Size() uint {
+ return uint(C.Z3_ast_vector_size(v.ctx.ptr, v.ptr))
+}
+
+// Get returns the i-th AST in the vector.
+func (v *ASTVector) Get(i uint) *Expr {
+ return newExpr(v.ctx, C.Z3_ast_vector_get(v.ctx.ptr, v.ptr, C.uint(i)))
+}
+
+// String returns the string representation of the AST vector.
+func (v *ASTVector) String() string {
+ return C.GoString(C.Z3_ast_vector_to_string(v.ctx.ptr, v.ptr))
+}
+
// ParamDescrs represents parameter descriptions for Z3 objects.
type ParamDescrs struct {
ctx *Context
@@ -353,11 +407,8 @@ func (c *Context) MkAnd(exprs ...*Expr) *Expr {
if len(exprs) == 1 {
return exprs[0]
}
- cExprs := make([]C.Z3_ast, len(exprs))
- for i, e := range exprs {
- cExprs[i] = e.ptr
- }
- return newExpr(c, C.Z3_mk_and(c.ptr, C.uint(len(exprs)), &cExprs[0]))
+ _, cExprsPtr := exprsToASTs(exprs)
+ return newExpr(c, C.Z3_mk_and(c.ptr, C.uint(len(exprs)), cExprsPtr))
}
// MkOr creates a disjunction.
@@ -368,11 +419,8 @@ func (c *Context) MkOr(exprs ...*Expr) *Expr {
if len(exprs) == 1 {
return exprs[0]
}
- cExprs := make([]C.Z3_ast, len(exprs))
- for i, e := range exprs {
- cExprs[i] = e.ptr
- }
- return newExpr(c, C.Z3_mk_or(c.ptr, C.uint(len(exprs)), &cExprs[0]))
+ _, cExprsPtr := exprsToASTs(exprs)
+ return newExpr(c, C.Z3_mk_or(c.ptr, C.uint(len(exprs)), cExprsPtr))
}
// MkNot creates a negation.
@@ -407,11 +455,52 @@ func (c *Context) MkDistinct(exprs ...*Expr) *Expr {
if len(exprs) <= 1 {
return c.MkTrue()
}
- cExprs := make([]C.Z3_ast, len(exprs))
- for i, e := range exprs {
- cExprs[i] = e.ptr
+ _, cExprsPtr := exprsToASTs(exprs)
+ return newExpr(c, C.Z3_mk_distinct(c.ptr, C.uint(len(exprs)), cExprsPtr))
+}
+
+// Pseudo-Boolean / cardinality constraints
+
+// MkAtMost encodes p1 + p2 + ... + pn <= k.
+func (c *Context) MkAtMost(args []*Expr, k uint) *Expr {
+ _, cArgsPtr := exprsToASTs(args)
+ return newExpr(c, C.Z3_mk_atmost(c.ptr, C.uint(len(args)), cArgsPtr, C.uint(k)))
+}
+
+// MkAtLeast encodes p1 + p2 + ... + pn >= k.
+func (c *Context) MkAtLeast(args []*Expr, k uint) *Expr {
+ _, cArgsPtr := exprsToASTs(args)
+ return newExpr(c, C.Z3_mk_atleast(c.ptr, C.uint(len(args)), cArgsPtr, C.uint(k)))
+}
+
+// MkPBLe encodes k1*p1 + k2*p2 + ... + kn*pn <= k.
+func (c *Context) MkPBLe(args []*Expr, coeffs []int, k int) *Expr {
+ if len(args) != len(coeffs) {
+ panic("MkPBLe: args and coeffs must have the same length")
}
- return newExpr(c, C.Z3_mk_distinct(c.ptr, C.uint(len(exprs)), &cExprs[0]))
+ _, cArgsPtr := exprsToASTs(args)
+ _, cCoeffsPtr := intsToCs(coeffs)
+ return newExpr(c, C.Z3_mk_pble(c.ptr, C.uint(len(args)), cArgsPtr, cCoeffsPtr, C.int(k)))
+}
+
+// MkPBGe encodes k1*p1 + k2*p2 + ... + kn*pn >= k.
+func (c *Context) MkPBGe(args []*Expr, coeffs []int, k int) *Expr {
+ if len(args) != len(coeffs) {
+ panic("MkPBGe: args and coeffs must have the same length")
+ }
+ _, cArgsPtr := exprsToASTs(args)
+ _, cCoeffsPtr := intsToCs(coeffs)
+ return newExpr(c, C.Z3_mk_pbge(c.ptr, C.uint(len(args)), cArgsPtr, cCoeffsPtr, C.int(k)))
+}
+
+// MkPBEq encodes k1*p1 + k2*p2 + ... + kn*pn = k.
+func (c *Context) MkPBEq(args []*Expr, coeffs []int, k int) *Expr {
+ if len(args) != len(coeffs) {
+ panic("MkPBEq: args and coeffs must have the same length")
+ }
+ _, cArgsPtr := exprsToASTs(args)
+ _, cCoeffsPtr := intsToCs(coeffs)
+ return newExpr(c, C.Z3_mk_pbeq(c.ptr, C.uint(len(args)), cArgsPtr, cCoeffsPtr, C.int(k)))
}
// FuncDecl represents a function declaration.
@@ -460,54 +549,26 @@ func (f *FuncDecl) GetRange() *Sort {
// MkFuncDecl creates a function declaration.
func (c *Context) MkFuncDecl(name *Symbol, domain []*Sort, range_ *Sort) *FuncDecl {
- cDomain := make([]C.Z3_sort, len(domain))
- for i, s := range domain {
- cDomain[i] = s.ptr
- }
- var domainPtr *C.Z3_sort
- if len(domain) > 0 {
- domainPtr = &cDomain[0]
- }
+ _, domainPtr := sortsToCSorts(domain)
return newFuncDecl(c, C.Z3_mk_func_decl(c.ptr, name.ptr, C.uint(len(domain)), domainPtr, range_.ptr))
}
// MkRecFuncDecl creates a recursive function declaration.
// After creating, use AddRecDef to provide the function body.
func (c *Context) MkRecFuncDecl(name *Symbol, domain []*Sort, range_ *Sort) *FuncDecl {
- cDomain := make([]C.Z3_sort, len(domain))
- for i, s := range domain {
- cDomain[i] = s.ptr
- }
- var domainPtr *C.Z3_sort
- if len(domain) > 0 {
- domainPtr = &cDomain[0]
- }
+ _, domainPtr := sortsToCSorts(domain)
return newFuncDecl(c, C.Z3_mk_rec_func_decl(c.ptr, name.ptr, C.uint(len(domain)), domainPtr, range_.ptr))
}
// AddRecDef adds the definition (body) for a recursive function created with MkRecFuncDecl.
func (c *Context) AddRecDef(f *FuncDecl, args []*Expr, body *Expr) {
- cArgs := make([]C.Z3_ast, len(args))
- for i, a := range args {
- cArgs[i] = a.ptr
- }
- var argsPtr *C.Z3_ast
- if len(args) > 0 {
- argsPtr = &cArgs[0]
- }
+ _, argsPtr := exprsToASTs(args)
C.Z3_add_rec_def(c.ptr, f.ptr, C.uint(len(args)), argsPtr, body.ptr)
}
// MkApp creates a function application.
func (c *Context) MkApp(decl *FuncDecl, args ...*Expr) *Expr {
- cArgs := make([]C.Z3_ast, len(args))
- for i, a := range args {
- cArgs[i] = a.ptr
- }
- var argsPtr *C.Z3_ast
- if len(args) > 0 {
- argsPtr = &cArgs[0]
- }
+ _, argsPtr := exprsToASTs(args)
return newExpr(c, C.Z3_mk_app(c.ptr, decl.ptr, C.uint(len(args)), argsPtr))
}
@@ -546,6 +607,66 @@ func (e *Expr) Simplify() *Expr {
return newExpr(e.ctx, C.Z3_simplify(e.ctx.ptr, e.ptr))
}
+// GetDecl returns the function declaration of an application expression.
+func (e *Expr) GetDecl() *FuncDecl {
+ return newFuncDecl(e.ctx, C.Z3_get_app_decl(e.ctx.ptr, C.Z3_to_app(e.ctx.ptr, e.ptr)))
+}
+
+// NumArgs returns the number of arguments of an application expression.
+func (e *Expr) NumArgs() uint {
+ return uint(C.Z3_get_app_num_args(e.ctx.ptr, C.Z3_to_app(e.ctx.ptr, e.ptr)))
+}
+
+// Arg returns the i-th argument of an application expression.
+func (e *Expr) Arg(i uint) *Expr {
+ return newExpr(e.ctx, C.Z3_get_app_arg(e.ctx.ptr, C.Z3_to_app(e.ctx.ptr, e.ptr), C.uint(i)))
+}
+
+// Substitute replaces every occurrence of from[i] in the expression with to[i].
+// The from and to slices must have the same length.
+func (e *Expr) Substitute(from, to []*Expr) *Expr {
+ n := len(from)
+ cFrom := make([]C.Z3_ast, n)
+ cTo := make([]C.Z3_ast, n)
+ for i := range from {
+ cFrom[i] = from[i].ptr
+ cTo[i] = to[i].ptr
+ }
+ var fromPtr, toPtr *C.Z3_ast
+ if n > 0 {
+ fromPtr = &cFrom[0]
+ toPtr = &cTo[0]
+ }
+ return newExpr(e.ctx, C.Z3_substitute(e.ctx.ptr, e.ptr, C.uint(n), fromPtr, toPtr))
+}
+
+// SubstituteVars replaces free variables in the expression with the expressions in to.
+// Variable with de-Bruijn index i is replaced with to[i].
+func (e *Expr) SubstituteVars(to []*Expr) *Expr {
+ _, toPtr := exprsToASTs(to)
+ return newExpr(e.ctx, C.Z3_substitute_vars(e.ctx.ptr, e.ptr, C.uint(len(to)), toPtr))
+}
+
+// SubstituteFuns replaces every occurrence of from[i] applied to arguments
+// with to[i] in the expression.
+// The from and to slices must have the same length.
+func (e *Expr) SubstituteFuns(from []*FuncDecl, to []*Expr) *Expr {
+ n := len(from)
+ cFrom := make([]C.Z3_func_decl, n)
+ cTo := make([]C.Z3_ast, n)
+ for i := range from {
+ cFrom[i] = from[i].ptr
+ cTo[i] = to[i].ptr
+ }
+ var fromPtr *C.Z3_func_decl
+ var toPtr *C.Z3_ast
+ if n > 0 {
+ fromPtr = &cFrom[0]
+ toPtr = &cTo[0]
+ }
+ return newExpr(e.ctx, C.Z3_substitute_funs(e.ctx.ptr, e.ptr, C.uint(n), fromPtr, toPtr))
+}
+
// MkTypeVariable creates a type variable sort for use in polymorphic functions and datatypes
func (c *Context) MkTypeVariable(name *Symbol) *Sort {
return newSort(c, C.Z3_mk_type_variable(c.ptr, name.ptr))
@@ -639,12 +760,7 @@ func (q *Quantifier) String() string {
// MkQuantifier creates a quantifier with patterns
func (c *Context) MkQuantifier(isForall bool, weight int, sorts []*Sort, names []*Symbol, body *Expr, patterns []*Pattern) *Quantifier {
- var forallInt C.bool
- if isForall {
- forallInt = true
- } else {
- forallInt = false
- }
+ forallInt := C.bool(isForall)
numBound := len(sorts)
if numBound != len(names) {
@@ -687,12 +803,7 @@ func (c *Context) MkQuantifier(isForall bool, weight int, sorts []*Sort, names [
// MkQuantifierConst creates a quantifier using constant bound variables
func (c *Context) MkQuantifierConst(isForall bool, weight int, bound []*Expr, body *Expr, patterns []*Pattern) *Quantifier {
- var forallInt C.bool
- if isForall {
- forallInt = true
- } else {
- forallInt = false
- }
+ forallInt := C.bool(isForall)
numBound := len(bound)
var cBound []C.Z3_app
@@ -815,6 +926,33 @@ func (c *Context) MkLambdaConst(bound []*Expr, body *Expr) *Lambda {
return newLambda(c, ptr)
}
+// SetGlobalParam sets a global Z3 parameter.
+func SetGlobalParam(id, value string) {
+ cID := C.CString(id)
+ cValue := C.CString(value)
+ defer C.free(unsafe.Pointer(cID))
+ defer C.free(unsafe.Pointer(cValue))
+ C.Z3_global_param_set(cID, cValue)
+}
+
+// GetGlobalParam retrieves the value of a global Z3 parameter.
+// Returns the value and true if the parameter exists, or empty string and false otherwise.
+func GetGlobalParam(id string) (string, bool) {
+ cID := C.CString(id)
+ defer C.free(unsafe.Pointer(cID))
+ var cValue C.Z3_string
+ ok := C.Z3_global_param_get(cID, &cValue)
+ if ok == C.bool(false) {
+ return "", false
+ }
+ return C.GoString(cValue), true
+}
+
+// ResetAllGlobalParams resets all global Z3 parameters to their default values.
+func ResetAllGlobalParams() {
+ C.Z3_global_param_reset_all()
+}
+
// astVectorToExprs converts a Z3_ast_vector to a slice of Expr.
// This function properly manages the reference count of the vector by
// incrementing it on entry and decrementing it on exit.
diff --git a/src/api/java/Context.java b/src/api/java/Context.java
index 22887729c..d02f4f287 100644
--- a/src/api/java/Context.java
+++ b/src/api/java/Context.java
@@ -4616,6 +4616,38 @@ public class Context implements AutoCloseable {
);
}
+ /**
+ * Creates a piecewise linear order.
+ * @param index The index of the order.
+ * @param sort The sort of the order.
+ */
+ public final FuncDecl mkPiecewiseLinearOrder(R sort, int index) {
+ return (FuncDecl) FuncDecl.create(
+ this,
+ Native.mkPiecewiseLinearOrder(
+ nCtx(),
+ sort.getNativeObject(),
+ index
+ )
+ );
+ }
+
+ /**
+ * Creates a tree order.
+ * @param index The index of the order.
+ * @param sort The sort of the order.
+ */
+ public final FuncDecl mkTreeOrder(R sort, int index) {
+ return (FuncDecl) FuncDecl.create(
+ this,
+ Native.mkTreeOrder(
+ nCtx(),
+ sort.getNativeObject(),
+ index
+ )
+ );
+ }
+
/**
* Return the nonzero subresultants of p and q with respect to the "variable" x.
* Note that any subterm that cannot be viewed as a polynomial is assumed to be a variable.
diff --git a/src/api/js/PUBLISHED_README.md b/src/api/js/PUBLISHED_README.md
index 42d58a916..85987ba7a 100644
--- a/src/api/js/PUBLISHED_README.md
+++ b/src/api/js/PUBLISHED_README.md
@@ -16,6 +16,17 @@ const {
This package has different initialization for browser and node. Your bundler and node should choose good version automatically, but you can import the one you need manually - `const { init } = require('z3-solver/node');` or `const { init } = require('z3-solver/browser');`.
+The `init` function also accepts an optional Emscripten module overrides object. This is useful in runtimes such as Deno where you may want to provide a wasm load path explicitly instead of relying on filesystem reads. In Deno 2.1+, `import.meta.resolve(...)` returns a string synchronously, so it can be used directly in `locateFile`. For example:
+
+```typescript
+import { init } from 'npm:z3-solver';
+
+const api = await init({
+ locateFile: (file, _prefix): string =>
+ import.meta.resolve(`npm:z3-solver/build/${file}`), // _prefix is unused here
+});
+```
+
### Limitations
The package requires threads, which means you'll need to be running in an environment which supports `SharedArrayBuffer`. In browsers, in addition to ensuring the browser has implemented `SharedArrayBuffer`, you'll need to serve your page with [special headers](https://web.dev/coop-coep/). There's a [neat trick](https://github.com/gzuidhof/coi-serviceworker) for doing that client-side on e.g. Github Pages, though you shouldn't use that trick in more complex applications.
diff --git a/src/api/js/package-lock.json b/src/api/js/package-lock.json
index a26cb3e27..9917a912c 100644
--- a/src/api/js/package-lock.json
+++ b/src/api/js/package-lock.json
@@ -5842,10 +5842,11 @@
"license": "ISC"
},
"node_modules/picomatch": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
- "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
+ "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
+ "license": "MIT",
"engines": {
"node": ">=8.6"
},
diff --git a/src/api/js/scripts/make-ts-wrapper.ts b/src/api/js/scripts/make-ts-wrapper.ts
index d19307243..560ad292e 100644
--- a/src/api/js/scripts/make-ts-wrapper.ts
+++ b/src/api/js/scripts/make-ts-wrapper.ts
@@ -444,8 +444,8 @@ ${Object.entries(primitiveTypes)
.map(e => `type ${e[0]} = ${e[1]};`)
.join('\n')}
-export async function init(initModule: any) {
- let Mod = await initModule();
+export async function init(initModule: any, moduleOverrides: Record = {}) {
+ let Mod = await initModule(moduleOverrides);
// this works for both signed and unsigned, because JS will wrap for you when constructing the Uint32Array
function intArrayToByteArr(ints: number[]) {
@@ -461,13 +461,13 @@ export async function init(initModule: any) {
}
let outAddress = Mod._malloc(${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS});
- let outUintArray = (new Uint32Array(Mod.HEAPU32.buffer, outAddress, 4));
+ let outUintArray = (new Uint32Array(Mod.HEAPU32.buffer, outAddress, ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 4}));
let getOutUint = (i: ${getValidOutArrayIndexes(4)}) => outUintArray[i];
- let outIntArray = (new Int32Array(Mod.HEAPU32.buffer, outAddress, 4));
+ let outIntArray = (new Int32Array(Mod.HEAPU32.buffer, outAddress, ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 4}));
let getOutInt = (i: ${getValidOutArrayIndexes(4)}) => outIntArray[i];
- let outUint64Array = (new BigUint64Array(Mod.HEAPU32.buffer, outAddress, 2));
+ let outUint64Array = (new BigUint64Array(Mod.HEAPU32.buffer, outAddress, ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 8}));
let getOutUint64 = (i: ${getValidOutArrayIndexes(8)}) => outUint64Array[i];
- let outInt64Array = (new BigInt64Array(Mod.HEAPU32.buffer, outAddress, 2));
+ let outInt64Array = (new BigInt64Array(Mod.HEAPU32.buffer, outAddress, ${BYTES_TO_ALLOCATE_FOR_OUT_PARAMS / 8}));
let getOutInt64 = (i: ${getValidOutArrayIndexes(8)}) => outInt64Array[i];
return {
diff --git a/src/api/js/src/browser.test.ts b/src/api/js/src/browser.test.ts
new file mode 100644
index 000000000..827f7281d
--- /dev/null
+++ b/src/api/js/src/browser.test.ts
@@ -0,0 +1,43 @@
+const mockInitWrapper = jest.fn();
+const mockCreateApi = jest.fn();
+
+jest.mock('./low-level', () => ({
+ init: mockInitWrapper,
+ Z3Core: undefined,
+ Z3LowLevel: undefined,
+}));
+jest.mock('./high-level', () => ({
+ createApi: mockCreateApi,
+}));
+
+import { init } from './browser';
+
+describe('browser init', () => {
+ beforeEach(() => {
+ delete (global as any).initZ3;
+ mockInitWrapper.mockReset();
+ mockCreateApi.mockReset();
+ });
+
+ it('passes module overrides to the browser initializer', async () => {
+ const initZ3 = jest.fn();
+ const locateFile = jest.fn((file: string) => `https://example.test/${file}`);
+ const lowLevel = { Z3: { low: true }, em: { module: true } };
+ const highLevel = { Context: jest.fn() };
+ (global as any).initZ3 = initZ3;
+ mockInitWrapper.mockResolvedValue(lowLevel);
+ mockCreateApi.mockReturnValue(highLevel);
+
+ const api = await init({ locateFile });
+
+ expect(mockInitWrapper).toHaveBeenCalledWith(initZ3, { locateFile });
+ expect(mockCreateApi).toHaveBeenCalledWith(lowLevel.Z3, lowLevel.em);
+ expect(api).toEqual({ ...lowLevel, ...highLevel });
+ });
+
+ it('throws when initZ3 is unavailable', async () => {
+ await expect(init()).rejects.toThrow(
+ 'initZ3 was not imported correctly. Please consult documentation on how to load Z3 in browser',
+ );
+ });
+});
diff --git a/src/api/js/src/browser.ts b/src/api/js/src/browser.ts
index 1a6e41f39..83acec7f6 100644
--- a/src/api/js/src/browser.ts
+++ b/src/api/js/src/browser.ts
@@ -1,16 +1,16 @@
import { createApi, Z3HighLevel } from './high-level';
-import { init as initWrapper, Z3LowLevel } from './low-level';
+import { init as initWrapper, Z3LowLevel, Z3ModuleOverrides } from './low-level';
export * from './high-level/types';
export { Z3Core, Z3LowLevel } from './low-level';
export * from './low-level/types.__GENERATED__';
-export async function init(): Promise {
+export async function init(moduleOverrides: Z3ModuleOverrides = {}): Promise {
const initZ3 = (global as any).initZ3;
if (initZ3 === undefined) {
throw new Error('initZ3 was not imported correctly. Please consult documentation on how to load Z3 in browser');
}
- const lowLevel = await initWrapper(initZ3);
+ const lowLevel = await initWrapper(initZ3, moduleOverrides);
const highLevel = createApi(lowLevel.Z3, lowLevel.em);
return { ...lowLevel, ...highLevel };
}
diff --git a/src/api/js/src/high-level/high-level.ts b/src/api/js/src/high-level/high-level.ts
index 71f156557..f336fe30b 100644
--- a/src/api/js/src/high-level/high-level.ts
+++ b/src/api/js/src/high-level/high-level.ts
@@ -1172,9 +1172,16 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
createDatatypes(...datatypes: DatatypeImpl[]): DatatypeSortImpl[] {
return createDatatypes(...datatypes);
},
+ createPolymorphicDatatype(typeParams: Sort[], datatype: DatatypeImpl): DatatypeSortImpl {
+ return createPolymorphicDatatype(typeParams, datatype);
+ },
},
);
+ function TypeVariable(name: string): Sort {
+ return new SortImpl(check(Z3.mk_type_variable(contextPtr, Z3.mk_string_symbol(contextPtr, name))));
+ }
+
////////////////
// Operations //
////////////////
@@ -1950,10 +1957,46 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
return new FuncDeclImpl(check(Z3.mk_partial_order(contextPtr, sort.ptr, index)));
}
+ function mkLinearOrder(sort: Sort, index: number): FuncDecl {
+ return new FuncDeclImpl(check(Z3.mk_linear_order(contextPtr, sort.ptr, index)));
+ }
+
+ function mkPiecewiseLinearOrder(sort: Sort, index: number): FuncDecl {
+ return new FuncDeclImpl(check(Z3.mk_piecewise_linear_order(contextPtr, sort.ptr, index)));
+ }
+
+ function mkTreeOrder(sort: Sort, index: number): FuncDecl {
+ return new FuncDeclImpl(check(Z3.mk_tree_order(contextPtr, sort.ptr, index)));
+ }
+
function mkTransitiveClosure(f: FuncDecl): FuncDecl {
return new FuncDeclImpl(check(Z3.mk_transitive_closure(contextPtr, f.ptr)));
}
+ function mkChar(ch: number): Expr {
+ return new ExprImpl(check(Z3.mk_char(contextPtr, ch)));
+ }
+
+ function mkCharLe(ch1: Expr, ch2: Expr): Bool {
+ return new BoolImpl(check(Z3.mk_char_le(contextPtr, ch1.ast, ch2.ast)));
+ }
+
+ function mkCharToInt(ch: Expr): Arith {
+ return new ArithImpl(check(Z3.mk_char_to_int(contextPtr, ch.ast)));
+ }
+
+ function mkCharToBV(ch: Expr): Expr {
+ return new ExprImpl(check(Z3.mk_char_to_bv(contextPtr, ch.ast)));
+ }
+
+ function mkCharFromBV(bv: Expr): Expr {
+ return new ExprImpl(check(Z3.mk_char_from_bv(contextPtr, bv.ast)));
+ }
+
+ function mkCharIsDigit(ch: Expr): Bool {
+ return new BoolImpl(check(Z3.mk_char_is_digit(contextPtr, ch.ast)));
+ }
+
async function polynomialSubresultants(
p: Arith,
q: Arith,
@@ -4689,6 +4732,10 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
const datatypes = createDatatypes(this);
return datatypes[0];
}
+
+ createPolymorphic(typeParams: Sort[]): DatatypeSort {
+ return createPolymorphicDatatype(typeParams, this);
+ }
}
class DatatypeSortImpl extends SortImpl implements DatatypeSort {
@@ -4845,6 +4892,84 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
}
}
+ function createPolymorphicDatatype(typeParams: Sort[], datatype: DatatypeImpl): DatatypeSortImpl {
+ if (!(datatype instanceof DatatypeImpl)) {
+ throw new Error('Datatype instance expected');
+ }
+
+ const constructors: Z3_constructor[] = [];
+
+ try {
+ for (const [constructorName, fields] of datatype.constructors) {
+ const fieldNames: string[] = [];
+ const fieldSorts: Z3_sort[] = [];
+ const fieldRefs: number[] = [];
+
+ for (const [fieldName, fieldSort] of fields) {
+ fieldNames.push(fieldName);
+
+ if (fieldSort instanceof DatatypeImpl) {
+ // Self-recursive reference
+ if (fieldSort !== datatype) {
+ throw new Error(
+ `Referenced datatype "${fieldSort.name}" is not the polymorphic datatype being created; mutual recursion is not supported in createPolymorphicDatatype`,
+ );
+ }
+ fieldSorts.push(null as any);
+ fieldRefs.push(0);
+ } else {
+ fieldSorts.push((fieldSort as Sort).ptr);
+ fieldRefs.push(0);
+ }
+ }
+
+ const constructor = Z3.mk_constructor(
+ contextPtr,
+ Z3.mk_string_symbol(contextPtr, constructorName),
+ Z3.mk_string_symbol(contextPtr, `is_${constructorName}`),
+ fieldNames.map(name => Z3.mk_string_symbol(contextPtr, name)),
+ fieldSorts,
+ fieldRefs,
+ );
+ constructors.push(constructor);
+ }
+
+ const nameSymbol = Z3.mk_string_symbol(contextPtr, datatype.name);
+ const paramPtrs = typeParams.map(p => p.ptr);
+ const resultSort = Z3.mk_polymorphic_datatype(contextPtr, nameSymbol, paramPtrs, constructors);
+
+ const sortImpl = new DatatypeSortImpl(resultSort);
+
+ // Attach constructor, recognizer, and accessor functions dynamically
+ const numConstructors = sortImpl.numConstructors();
+ for (let j = 0; j < numConstructors; j++) {
+ const constructor = sortImpl.constructorDecl(j);
+ const recognizer = sortImpl.recognizer(j);
+ const constructorName = constructor.name().toString();
+
+ if (constructor.arity() === 0) {
+ (sortImpl as any)[constructorName] = constructor.call();
+ } else {
+ (sortImpl as any)[constructorName] = constructor;
+ }
+
+ (sortImpl as any)[`is_${constructorName}`] = recognizer;
+
+ for (let k = 0; k < constructor.arity(); k++) {
+ const accessor = sortImpl.accessor(j, k);
+ const accessorName = accessor.name().toString();
+ (sortImpl as any)[accessorName] = accessor;
+ }
+ }
+
+ return sortImpl;
+ } finally {
+ for (const constructor of constructors) {
+ Z3.del_constructor(contextPtr, constructor);
+ }
+ }
+ }
+
class QuantifierImpl<
QVarSorts extends NonEmptySortArray,
QSort extends BoolSort | SMTArraySort,
@@ -5292,6 +5417,7 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
Set,
FiniteSet,
Datatype,
+ TypeVariable,
////////////////
// Operations //
@@ -5400,7 +5526,16 @@ export function createApi(Z3: Z3Core, em?: any): Z3HighLevel {
Full,
mkPartialOrder,
+ mkLinearOrder,
+ mkPiecewiseLinearOrder,
+ mkTreeOrder,
mkTransitiveClosure,
+ mkChar,
+ mkCharLe,
+ mkCharToInt,
+ mkCharToBV,
+ mkCharFromBV,
+ mkCharIsDigit,
polynomialSubresultants,
};
cleanup.register(ctx, () => Z3.del_context(contextPtr));
diff --git a/src/api/js/src/high-level/types.ts b/src/api/js/src/high-level/types.ts
index db28c8d16..6eef81044 100644
--- a/src/api/js/src/high-level/types.ts
+++ b/src/api/js/src/high-level/types.ts
@@ -479,6 +479,12 @@ export interface Context {
/** @category Expressions */
readonly Datatype: DatatypeCreation;
+ /**
+ * Create a type variable sort for use as a parameter in polymorphic datatypes.
+ * @category Sorts
+ */
+ TypeVariable(name: string): Sort;
+
////////////////
// Operations //
////////////////
@@ -927,6 +933,30 @@ export interface Context {
*/
mkPartialOrder(sort: Sort, index: number): FuncDecl;
+ /**
+ * Create a linear (total) order relation over a sort.
+ * @param sort The sort of the relation
+ * @param index The index of the relation
+ * @category Operations
+ */
+ mkLinearOrder(sort: Sort, index: number): FuncDecl;
+
+ /**
+ * Create a piecewise linear order relation over a sort.
+ * @param sort The sort of the relation
+ * @param index The index of the relation
+ * @category Operations
+ */
+ mkPiecewiseLinearOrder(sort: Sort, index: number): FuncDecl;
+
+ /**
+ * Create a tree order relation over a sort.
+ * @param sort The sort of the relation
+ * @param index The index of the relation
+ * @category Operations
+ */
+ mkTreeOrder(sort: Sort, index: number): FuncDecl;
+
/**
* Create the transitive closure of a binary relation.
* The resulting relation is recursive.
@@ -935,6 +965,49 @@ export interface Context {
*/
mkTransitiveClosure(f: FuncDecl): FuncDecl;
+ /**
+ * Create a character literal from a Unicode code point.
+ * @param ch The Unicode code point
+ * @category Characters
+ */
+ mkChar(ch: number): Expr;
+
+ /**
+ * Create a character less-than-or-equal predicate (ch1 ≤ ch2).
+ * @param ch1 First character
+ * @param ch2 Second character
+ * @category Characters
+ */
+ mkCharLe(ch1: Expr, ch2: Expr): Bool;
+
+ /**
+ * Convert a character to its integer (Unicode code point) value.
+ * @param ch The character expression
+ * @category Characters
+ */
+ mkCharToInt(ch: Expr): Arith;
+
+ /**
+ * Convert a character to a bit-vector.
+ * @param ch The character expression
+ * @category Characters
+ */
+ mkCharToBV(ch: Expr): Expr;
+
+ /**
+ * Convert a bit-vector to a character.
+ * @param bv The bit-vector expression
+ * @category Characters
+ */
+ mkCharFromBV(bv: Expr): Expr;
+
+ /**
+ * Create a predicate that is true if the character is a decimal digit.
+ * @param ch The character expression
+ * @category Characters
+ */
+ mkCharIsDigit(ch: Expr): Bool;
+
/**
* Return the nonzero subresultants of p and q with respect to the "variable" x.
* Note that any subterm that cannot be viewed as a polynomial is assumed to be a variable.
@@ -3136,6 +3209,15 @@ export interface Datatype {
* For mutually recursive datatypes, use Context.createDatatypes instead.
*/
create(): DatatypeSort;
+
+ /**
+ * Create a polymorphic datatype sort with explicit type parameters.
+ * Type parameters should be sorts created with Context.TypeVariable.
+ * Self-recursive fields may reference this Datatype object directly.
+ *
+ * @param typeParams Array of type variable sorts
+ */
+ createPolymorphic(typeParams: AnySort[]): DatatypeSort;
}
/**
@@ -3154,6 +3236,17 @@ export interface DatatypeCreation {
* @returns Array of created DatatypeSort instances
*/
createDatatypes(...datatypes: Datatype[]): DatatypeSort[];
+
+ /**
+ * Create a single polymorphic datatype sort with explicit type parameters.
+ * Type parameters should be sorts created with Context.TypeVariable.
+ * Self-recursive fields in constructors may reference the Datatype object directly.
+ *
+ * @param typeParams Array of type variable sorts
+ * @param datatype Datatype declaration with constructors
+ * @returns Created DatatypeSort instance
+ */
+ createPolymorphicDatatype(typeParams: AnySort[], datatype: Datatype): DatatypeSort;
}
/**
diff --git a/src/api/js/src/low-level/index.ts b/src/api/js/src/low-level/index.ts
index 1791eae27..f4924eee5 100644
--- a/src/api/js/src/low-level/index.ts
+++ b/src/api/js/src/low-level/index.ts
@@ -1,4 +1,8 @@
export * from './types.__GENERATED__';
export * from './wrapper.__GENERATED__';
+export type Z3ModuleOverrides = {
+ locateFile?: (path: string, prefix: string) => string;
+ [key: string]: unknown;
+};
export type Z3Core = Awaited>['Z3'];
export type Z3LowLevel = Awaited>;
diff --git a/src/api/js/src/node.test.ts b/src/api/js/src/node.test.ts
new file mode 100644
index 000000000..7ea67cb21
--- /dev/null
+++ b/src/api/js/src/node.test.ts
@@ -0,0 +1,37 @@
+const mockInitModule = jest.fn();
+const mockInitWrapper = jest.fn();
+const mockCreateApi = jest.fn();
+
+jest.mock('./z3-built', () => mockInitModule, { virtual: true });
+jest.mock('./low-level', () => ({
+ init: mockInitWrapper,
+ Z3Core: undefined,
+ Z3LowLevel: undefined,
+}));
+jest.mock('./high-level', () => ({
+ createApi: mockCreateApi,
+}));
+
+import { init } from './node';
+
+describe('node init', () => {
+ beforeEach(() => {
+ mockInitModule.mockReset();
+ mockInitWrapper.mockReset();
+ mockCreateApi.mockReset();
+ });
+
+ it('passes module overrides to the low-level initializer', async () => {
+ const locateFile = jest.fn((file: string) => `npm:z3-solver/build/${file}`);
+ const lowLevel = { Z3: { low: true }, em: { module: true } };
+ const highLevel = { Context: jest.fn() };
+ mockInitWrapper.mockResolvedValue(lowLevel);
+ mockCreateApi.mockReturnValue(highLevel);
+
+ const api = await init({ locateFile });
+
+ expect(mockInitWrapper).toHaveBeenCalledWith(mockInitModule, { locateFile });
+ expect(mockCreateApi).toHaveBeenCalledWith(lowLevel.Z3, lowLevel.em);
+ expect(api).toEqual({ ...lowLevel, ...highLevel });
+ });
+});
diff --git a/src/api/js/src/node.ts b/src/api/js/src/node.ts
index 87be038e5..a2e4f3e0c 100644
--- a/src/api/js/src/node.ts
+++ b/src/api/js/src/node.ts
@@ -2,7 +2,7 @@
import initModule = require('./z3-built');
import { createApi, Z3HighLevel } from './high-level';
-import { init as initWrapper, Z3LowLevel } from './low-level';
+import { init as initWrapper, Z3LowLevel, Z3ModuleOverrides } from './low-level';
export * from './high-level/types';
export { Z3Core, Z3LowLevel } from './low-level';
export * from './low-level/types.__GENERATED__';
@@ -29,10 +29,17 @@ export * from './low-level/types.__GENERATED__';
*
* console.log(`x=${model.get(x)}, y=${model.get(y)}`);
* // x=0, y=12
+ *
+ * // Deno users can provide an Emscripten locateFile hook to load the wasm
+ * // through npm's asset resolution instead of filesystem reads.
+ * // const api = await init({
+ * // locateFile: (file, _prefix): string =>
+ * // import.meta.resolve(`npm:z3-solver/build/${file}`), // _prefix is unused here
+ * // });
* ```
* @category Global */
-export async function init(): Promise {
- const lowLevel = await initWrapper(initModule);
+export async function init(moduleOverrides: Z3ModuleOverrides = {}): Promise {
+ const lowLevel = await initWrapper(initModule, moduleOverrides);
const highLevel = createApi(lowLevel.Z3, lowLevel.em);
return { ...lowLevel, ...highLevel };
}
diff --git a/src/api/julia/z3jl.cpp b/src/api/julia/z3jl.cpp
index ec3efa86b..3df98ac01 100644
--- a/src/api/julia/z3jl.cpp
+++ b/src/api/julia/z3jl.cpp
@@ -320,6 +320,20 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
m.method("finite_set_map", &finite_set_map);
m.method("finite_set_filter", &finite_set_filter);
m.method("finite_set_range", &finite_set_range);
+ m.method("empty_set", &empty_set);
+ m.method("full_set", &full_set);
+ m.method("set_add", &set_add);
+ m.method("set_del", &set_del);
+ m.method("set_union", &set_union);
+ m.method("set_intersect", &set_intersect);
+ m.method("set_difference", &set_difference);
+ m.method("set_complement", &set_complement);
+ m.method("set_member", &set_member);
+ m.method("set_subset", &set_subset);
+ m.method("linear_order", &linear_order);
+ m.method("partial_order", &partial_order);
+ m.method("piecewise_linear_order", &piecewise_linear_order);
+ m.method("tree_order", &tree_order);
// -------------------------------------------------------------------------
@@ -629,7 +643,13 @@ JLCXX_MODULE define_julia_module(jlcxx::Module &m)
.MM(context, string_sort)
.MM(context, seq_sort)
.MM(context, re_sort)
+ .MM(context, char_sort)
.MM(context, finite_set_sort)
+ .method("set_sort", [](context &c, sort s) {
+ Z3_sort r = Z3_mk_set_sort(c, s);
+ c.check_error();
+ return sort(c, r);
+ })
.method("array_sort", static_cast(&context::array_sort))
.method("array_sort", static_cast(&context::array_sort))
.method("fpa_sort", static_cast(&context::fpa_sort))
diff --git a/src/api/ml/z3.ml b/src/api/ml/z3.ml
index 74cf974b2..f64c4ddd8 100644
--- a/src/api/ml/z3.ml
+++ b/src/api/ml/z3.ml
@@ -475,6 +475,7 @@ sig
val substitute : expr -> expr list -> expr list -> expr
val substitute_one : expr -> expr -> expr -> expr
val substitute_vars : expr -> expr list -> expr
+ val substitute_funs : expr -> FuncDecl.func_decl list -> expr list -> expr
val translate : expr -> context -> expr
val to_string : expr -> string
val is_numeral : expr -> bool
@@ -537,6 +538,13 @@ end = struct
let substitute_vars x to_ =
Z3native.substitute_vars (gc x) x (List.length to_) to_
+ let substitute_funs x from to_ =
+ let len = List.length from in
+ if List.length to_ <> len then
+ raise (Error "Argument sizes do not match")
+ else
+ Z3native.substitute_funs (gc x) x len from to_
+
let translate (x:expr) to_ctx =
if gc x = to_ctx then
x
@@ -587,6 +595,12 @@ struct
let mk_eq = Z3native.mk_eq
let mk_distinct ctx args = Z3native.mk_distinct ctx (List.length args) args
+ let mk_atmost ctx args k = Z3native.mk_atmost ctx (List.length args) args k
+ let mk_atleast ctx args k = Z3native.mk_atleast ctx (List.length args) args k
+ let mk_pble ctx args coeffs k = Z3native.mk_pble ctx (List.length args) args coeffs k
+ let mk_pbge ctx args coeffs k = Z3native.mk_pbge ctx (List.length args) args coeffs k
+ let mk_pbeq ctx args coeffs k = Z3native.mk_pbeq ctx (List.length args) args coeffs k
+
let get_bool_value x = lbool_of_int (Z3native.get_bool_value (gc x) x)
let is_bool x =
@@ -1283,6 +1297,9 @@ struct
let mk_seq_contains = Z3native.mk_seq_contains
let mk_seq_extract = Z3native.mk_seq_extract
let mk_seq_replace = Z3native.mk_seq_replace
+ let mk_seq_replace_all = Z3native.mk_seq_replace_all
+ let mk_seq_replace_re = Z3native.mk_seq_replace_re
+ let mk_seq_replace_re_all = Z3native.mk_seq_replace_re_all
let mk_seq_at = Z3native.mk_seq_at
let mk_seq_length = Z3native.mk_seq_length
let mk_seq_nth = Z3native.mk_seq_nth
@@ -1308,6 +1325,7 @@ struct
let mk_re_loop = Z3native.mk_re_loop
let mk_re_intersect ctx args = Z3native.mk_re_intersect ctx (List.length args) args
let mk_re_complement = Z3native.mk_re_complement
+ let mk_re_diff = Z3native.mk_re_diff
let mk_re_empty = Z3native.mk_re_empty
let mk_re_full = Z3native.mk_re_full
let mk_char = Z3native.mk_char
@@ -1336,6 +1354,15 @@ struct
let mk_range = Z3native.mk_finite_set_range
end
+module SpecialRelation =
+struct
+ let mk_linear_order = Z3native.mk_linear_order
+ let mk_partial_order = Z3native.mk_partial_order
+ let mk_piecewise_linear_order = Z3native.mk_piecewise_linear_order
+ let mk_tree_order = Z3native.mk_tree_order
+ let mk_transitive_closure = Z3native.mk_transitive_closure
+end
+
module FloatingPoint =
struct
module RoundingMode =
diff --git a/src/api/ml/z3.mli b/src/api/ml/z3.mli
index 689fa088d..f5e90d845 100644
--- a/src/api/ml/z3.mli
+++ b/src/api/ml/z3.mli
@@ -531,6 +531,10 @@ sig
For every [i] smaller than [num_exprs], the variable with de-Bruijn index [i] is replaced with term [to[i]]. *)
val substitute_vars : Expr.expr -> Expr.expr list -> expr
+ (** Substitute every application of [from[i]] with [to[i]] in the expression.
+ The [from] and [to] lists must have the same length. *)
+ val substitute_funs : Expr.expr -> FuncDecl.func_decl list -> Expr.expr list -> expr
+
(** Translates (copies) the term to another context.
@return A copy of the term which is associated with the other context *)
val translate : Expr.expr -> context -> expr
@@ -632,6 +636,21 @@ sig
(** Creates a [distinct] term. *)
val mk_distinct : context -> Expr.expr list -> Expr.expr
+ (** Encodes p1 + p2 + ... + pn <= k. *)
+ val mk_atmost : context -> Expr.expr list -> int -> Expr.expr
+
+ (** Encodes p1 + p2 + ... + pn >= k. *)
+ val mk_atleast : context -> Expr.expr list -> int -> Expr.expr
+
+ (** Encodes k1*p1 + k2*p2 + ... + kn*pn <= k. *)
+ val mk_pble : context -> Expr.expr list -> int list -> int -> Expr.expr
+
+ (** Encodes k1*p1 + k2*p2 + ... + kn*pn >= k. *)
+ val mk_pbge : context -> Expr.expr list -> int list -> int -> Expr.expr
+
+ (** Encodes k1*p1 + k2*p2 + ... + kn*pn = k. *)
+ val mk_pbeq : context -> Expr.expr list -> int list -> int -> Expr.expr
+
(** Indicates whether the expression is the true or false expression
or something else (L_UNDEF). *)
val get_bool_value : Expr.expr -> Z3enums.lbool
@@ -1968,9 +1987,22 @@ sig
(** extract sub-sequence starting at index given by second argument and of length provided by third argument *)
val mk_seq_extract : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
- (** replace first occurrence of second argument by third *)
+ (** [mk_seq_replace ctx seq target replacement] replaces the first occurrence
+ of [target] within [seq] with [replacement]. *)
val mk_seq_replace : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
+ (** [mk_seq_replace_all ctx seq target replacement] replaces all occurrences
+ of [target] within [seq] with [replacement]. *)
+ val mk_seq_replace_all : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
+
+ (** [mk_seq_replace_re ctx seq re replacement] replaces the first occurrence
+ matching the regular expression [re] within [seq] with [replacement]. *)
+ val mk_seq_replace_re : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
+
+ (** [mk_seq_replace_re_all ctx seq re replacement] replaces all occurrences
+ matching the regular expression [re] within [seq] with [replacement]. *)
+ val mk_seq_replace_re_all : context -> Expr.expr -> Expr.expr -> Expr.expr -> Expr.expr
+
(** a unit sequence at index provided by second argument *)
val mk_seq_at : context -> Expr.expr -> Expr.expr -> Expr.expr
@@ -2048,6 +2080,9 @@ sig
(** the regular expression complement *)
val mk_re_complement : context -> Expr.expr -> Expr.expr
+ (** the regular expression difference *)
+ val mk_re_diff : context -> Expr.expr -> Expr.expr -> Expr.expr
+
(** the regular expression that accepts no sequences *)
val mk_re_empty : context -> Sort.sort -> Expr.expr
@@ -2121,6 +2156,31 @@ sig
end
+(** Special relation constructors *)
+module SpecialRelation :
+sig
+ (** Create a linear (total) order relation over the given sort.
+ The [id] parameter distinguishes multiple linear orders over the same sort. *)
+ val mk_linear_order : context -> Sort.sort -> int -> FuncDecl.func_decl
+
+ (** Create a partial order relation over the given sort.
+ The [id] parameter distinguishes multiple partial orders over the same sort. *)
+ val mk_partial_order : context -> Sort.sort -> int -> FuncDecl.func_decl
+
+ (** Create a piecewise linear order relation over the given sort.
+ The [id] parameter distinguishes multiple piecewise linear orders over the same sort. *)
+ val mk_piecewise_linear_order : context -> Sort.sort -> int -> FuncDecl.func_decl
+
+ (** Create a tree order relation over the given sort.
+ The [id] parameter distinguishes multiple tree orders over the same sort. *)
+ val mk_tree_order : context -> Sort.sort -> int -> FuncDecl.func_decl
+
+ (** Create the transitive closure of a binary relation.
+ The resulting relation is recursive. *)
+ val mk_transitive_closure : context -> FuncDecl.func_decl -> FuncDecl.func_decl
+
+end
+
(** Floating-Point Arithmetic *)
module FloatingPoint :
sig
diff --git a/src/api/python/setup.py b/src/api/python/setup.py
index 8f70b9c6f..116b3b570 100644
--- a/src/api/python/setup.py
+++ b/src/api/python/setup.py
@@ -313,6 +313,7 @@ class bdist_wheel(_bdist_wheel):
("linux", "x86_64"): "linux_x86_64",
("linux", "aarch64"): "linux_aarch64",
('linux', "riscv64"): "linux_riscv64",
+ ("linux", "loongarch64"): "linux_loongarch64",
# windows arm64 is not supported by pypi yet
("win", "x64"): "win_amd64",
("win", "x86"): "win32",
diff --git a/src/api/python/z3/z3.py b/src/api/python/z3/z3.py
index 02ed4f166..34eb9f5f8 100644
--- a/src/api/python/z3/z3.py
+++ b/src/api/python/z3/z3.py
@@ -5612,6 +5612,20 @@ class Datatype:
"""
return CreateDatatypes([self])[0]
+ def create_polymorphic(self, type_params):
+ """Create a polymorphic Z3 datatype with explicit type variables.
+
+ `type_params` is a list of type variables created with `DeclareTypeVar`.
+ Constructor field sorts may reference these type variables.
+ Self-recursive fields may reference this datatype directly.
+
+ >>> A = DeclareTypeVar('A')
+ >>> Pair = Datatype('Pair')
+ >>> Pair.declare('pair', ('fst', A), ('snd', A))
+ >>> Pair = Pair.create_polymorphic([A])
+ """
+ return CreatePolymorphicDatatype(self, type_params)
+
class ScopedConstructor:
"""Auxiliary object used to create Z3 datatypes."""
@@ -5733,6 +5747,76 @@ def CreateDatatypes(*ds):
return tuple(result)
+def CreatePolymorphicDatatype(d, type_params):
+ """Create a single polymorphic Z3 datatype with explicit type parameters.
+
+ `d` is a `Datatype` helper object whose constructors have been declared.
+ `type_params` is a list of type variables created with `DeclareTypeVar`.
+ Constructor field sorts may reference these type variables, and self-recursive
+ fields may reference `d` directly.
+
+ >>> A = DeclareTypeVar('A')
+ >>> Pair = Datatype('Pair')
+ >>> Pair.declare('pair', ('fst', A), ('snd', A))
+ >>> Pair = CreatePolymorphicDatatype(Pair, [A])
+ """
+ if z3_debug():
+ _z3_assert(isinstance(d, Datatype), "Datatype expected")
+ _z3_assert(d.constructors != [], "Non-empty Datatype expected")
+ ctx = d.ctx
+ name = to_symbol(d.name, ctx)
+ num_params = len(type_params)
+ params_arr = (Sort * num_params)()
+ for i, p in enumerate(type_params):
+ if z3_debug():
+ _z3_assert(is_sort(p), "Z3 sort expected for type parameter")
+ params_arr[i] = p.ast
+ num_cs = len(d.constructors)
+ cs = (Constructor * num_cs)()
+ to_delete = []
+ for j in range(num_cs):
+ c = d.constructors[j]
+ cname = to_symbol(c[0], ctx)
+ rname = to_symbol(c[1], ctx)
+ fs = c[2]
+ num_fs = len(fs)
+ fnames = (Symbol * num_fs)()
+ sorts = (Sort * num_fs)()
+ refs = (ctypes.c_uint * num_fs)()
+ for k in range(num_fs):
+ fname = fs[k][0]
+ ftype = fs[k][1]
+ fnames[k] = to_symbol(fname, ctx)
+ if isinstance(ftype, Datatype):
+ if z3_debug():
+ _z3_assert(ftype is d, "Only self-recursive references are supported in polymorphic datatypes. Use CreateDatatypes for mutually recursive datatypes.")
+ sorts[k] = None
+ refs[k] = 0
+ else:
+ if z3_debug():
+ _z3_assert(is_sort(ftype), "Z3 sort expected")
+ sorts[k] = ftype.ast
+ refs[k] = 0
+ cs[j] = Z3_mk_constructor(ctx.ref(), cname, rname, num_fs, fnames, sorts, refs)
+ to_delete.append(ScopedConstructor(cs[j], ctx))
+ out = Z3_mk_polymorphic_datatype(ctx.ref(), name, num_params, params_arr, num_cs, cs)
+ dref = DatatypeSortRef(out, ctx)
+ num_cs_actual = dref.num_constructors()
+ for j in range(num_cs_actual):
+ cref = dref.constructor(j)
+ cref_name = cref.name()
+ cref_arity = cref.arity()
+ if cref_arity == 0:
+ cref = cref()
+ setattr(dref, cref_name, cref)
+ rref = dref.recognizer(j)
+ setattr(dref, "is_" + cref_name, rref)
+ for k in range(cref_arity):
+ aref = dref.accessor(j, k)
+ setattr(dref, aref.name(), aref)
+ return dref
+
+
class DatatypeSortRef(SortRef):
"""Datatype sorts."""
diff --git a/src/ast/array_decl_plugin.cpp b/src/ast/array_decl_plugin.cpp
index 7da470221..b51972934 100644
--- a/src/ast/array_decl_plugin.cpp
+++ b/src/ast/array_decl_plugin.cpp
@@ -36,7 +36,8 @@ array_decl_plugin::array_decl_plugin():
m_set_complement_sym("complement"),
m_set_subset_sym("subset"),
m_array_ext_sym("array-ext"),
- m_as_array_sym("as-array") {
+ m_as_array_sym("as-array"),
+ m_choice_sym("choice") {
}
#define ARRAY_SORT_STR "Array"
@@ -433,6 +434,20 @@ func_decl * array_decl_plugin::mk_as_array(func_decl * f) {
return m_manager->mk_const_decl(m_as_array_sym, s, info);
}
+func_decl* array_decl_plugin::mk_choice(unsigned arity, sort* const* domain) {
+ if (arity != 1) {
+ m_manager->raise_exception("choice takes one argument");
+ return nullptr;
+ }
+ sort* s = domain[0];
+ if (!is_array_sort(s) || get_array_arity(s) != 1 || !m_manager->is_bool(get_array_range(s))) {
+ m_manager->raise_exception("choice expects an argument with sort (Array T Bool)");
+ return nullptr;
+ }
+ return m_manager->mk_func_decl(m_choice_sym, arity, domain, get_array_domain(s, 0),
+ func_decl_info(m_family_id, OP_CHOICE));
+}
+
func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
unsigned arity, sort * const * domain, sort * range) {
@@ -501,6 +516,8 @@ func_decl * array_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
func_decl * f = to_func_decl(parameters[0].get_ast());
return mk_as_array(f);
}
+ case OP_CHOICE:
+ return mk_choice(arity, domain);
default: return nullptr;
}
}
@@ -529,6 +546,7 @@ void array_decl_plugin::get_op_names(svector& op_names, symbol con
op_names.push_back(builtin_name("complement",OP_SET_COMPLEMENT));
op_names.push_back(builtin_name("subset",OP_SET_SUBSET));
op_names.push_back(builtin_name("as-array", OP_AS_ARRAY));
+ op_names.push_back(builtin_name("choice", OP_CHOICE));
op_names.push_back(builtin_name("array-ext", OP_ARRAY_EXT));
#if 0
@@ -655,4 +673,3 @@ func_decl* array_util::mk_array_ext(sort *domain, unsigned i) {
parameter p(i);
return m_manager.mk_func_decl(m_fid, OP_ARRAY_EXT, 1, &p, 2, domains);
}
-
diff --git a/src/ast/array_decl_plugin.h b/src/ast/array_decl_plugin.h
index 36403f3ca..0c4983eec 100644
--- a/src/ast/array_decl_plugin.h
+++ b/src/ast/array_decl_plugin.h
@@ -63,6 +63,7 @@ enum array_op_kind {
OP_SET_COMPLEMENT,
OP_SET_SUBSET,
OP_AS_ARRAY, // used for model construction
+ OP_CHOICE,
LAST_ARRAY_OP
};
@@ -79,6 +80,7 @@ class array_decl_plugin : public decl_plugin {
symbol m_set_subset_sym;
symbol m_array_ext_sym;
symbol m_as_array_sym;
+ symbol m_choice_sym;
bool check_set_arguments(unsigned arity, sort * const * domain);
@@ -106,6 +108,8 @@ class array_decl_plugin : public decl_plugin {
func_decl * mk_as_array(func_decl * f);
+ func_decl * mk_choice(unsigned arity, sort* const* domain);
+
bool is_array_sort(sort* s) const;
public:
array_decl_plugin();
@@ -164,6 +168,7 @@ public:
bool is_difference(expr* n) const { return is_app_of(n, m_fid, OP_SET_DIFFERENCE); }
bool is_complement(expr* n) const { return is_app_of(n, m_fid, OP_SET_COMPLEMENT); }
bool is_as_array(expr * n) const { return is_app_of(n, m_fid, OP_AS_ARRAY); }
+ bool is_choice(expr* n) const { return is_app_of(n, m_fid, OP_CHOICE); }
bool is_as_array(expr * n, func_decl*& f) const { return is_as_array(n) && (f = get_as_array_func_decl(n), true); }
bool is_select(func_decl* f) const { return is_decl_of(f, m_fid, OP_SELECT); }
bool is_store(func_decl* f) const { return is_decl_of(f, m_fid, OP_STORE); }
@@ -172,6 +177,7 @@ public:
bool is_union(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_UNION); }
bool is_intersect(func_decl* f) const { return is_decl_of(f, m_fid, OP_SET_INTERSECT); }
bool is_as_array(func_decl* f) const { return is_decl_of(f, m_fid, OP_AS_ARRAY); }
+ bool is_choice(func_decl* f) const { return is_decl_of(f, m_fid, OP_CHOICE); }
bool is_default(func_decl* f) const { return is_decl_of(f, m_fid, OP_ARRAY_DEFAULT); }
bool is_default(expr* n) const { return is_app_of(n, m_fid, OP_ARRAY_DEFAULT); }
bool is_subset(expr const* n) const { return is_app_of(n, m_fid, OP_SET_SUBSET); }
@@ -308,6 +314,10 @@ public:
return m_manager.mk_app(m_fid, OP_AS_ARRAY, 1, ¶m, 0, nullptr, nullptr);
}
+ app* mk_choice(expr* p) const {
+ return m_manager.mk_app(m_fid, OP_CHOICE, p);
+ }
+
sort* get_array_range_rec(sort* s) {
while (is_array(s)) {
s = get_array_range(s);
@@ -317,5 +327,3 @@ public:
};
-
-
diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp
index cf5e7af87..928bebc07 100644
--- a/src/ast/ast.cpp
+++ b/src/ast/ast.cpp
@@ -242,21 +242,14 @@ func_decl_info::func_decl_info(family_id family_id, decl_kind k, unsigned num_pa
m_injective(false),
m_idempotent(false),
m_skolem(false),
- m_lambda(false),
m_polymorphic(false) {
}
bool func_decl_info::operator==(func_decl_info const & info) const {
- return decl_info::operator==(info) &&
- m_left_assoc == info.m_left_assoc &&
- m_right_assoc == info.m_right_assoc &&
- m_flat_associative == info.m_flat_associative &&
- m_commutative == info.m_commutative &&
- m_chainable == info.m_chainable &&
- m_pairwise == info.m_pairwise &&
- m_injective == info.m_injective &&
- m_skolem == info.m_skolem &&
- m_lambda == info.m_lambda;
+ return decl_info::operator==(info) && m_left_assoc == info.m_left_assoc && m_right_assoc == info.m_right_assoc &&
+ m_flat_associative == info.m_flat_associative && m_commutative == info.m_commutative &&
+ m_chainable == info.m_chainable && m_pairwise == info.m_pairwise && m_injective == info.m_injective &&
+ m_skolem == info.m_skolem;
}
std::ostream & operator<<(std::ostream & out, func_decl_info const & info) {
@@ -270,7 +263,6 @@ std::ostream & operator<<(std::ostream & out, func_decl_info const & info) {
if (info.is_injective()) out << " :injective ";
if (info.is_idempotent()) out << " :idempotent ";
if (info.is_skolem()) out << " :skolem ";
- if (info.is_lambda()) out << " :lambda ";
if (info.is_polymorphic()) out << " :polymorphic ";
return out;
}
@@ -1625,19 +1617,6 @@ bool ast_manager::are_distinct(expr* a, expr* b) const {
return false;
}
-void ast_manager::add_lambda_def(func_decl* f, quantifier* q) {
- TRACE(model, tout << "add lambda def " << mk_pp(q, *this) << "\n");
- m_lambda_defs.insert(f, q);
- f->get_info()->set_lambda(true);
- inc_ref(q);
-}
-
-quantifier* ast_manager::is_lambda_def(func_decl* f) {
- if (f->get_info() && f->get_info()->is_lambda())
- return m_lambda_defs[f];
- return nullptr;
-}
-
void ast_manager::register_plugin(family_id id, decl_plugin * plugin) {
SASSERT(m_plugins.get(id, 0) == 0);
@@ -1670,7 +1649,7 @@ bool ast_manager::slow_not_contains(ast const * n) {
}
#endif
-#if 1
+#if 0
static unsigned s_count = 0;
static void track_id(ast_manager& m, ast* n, unsigned id) {
@@ -1832,10 +1811,6 @@ void ast_manager::delete_node(ast * n) {
m_poly_roots.erase(f);
if (f->m_info != nullptr) {
func_decl_info * info = f->get_info();
- if (info->is_lambda()) {
- push_dec_ref(m_lambda_defs[f]);
- m_lambda_defs.remove(f);
- }
info->del_eh(*this);
dealloc(info);
}
diff --git a/src/ast/ast.h b/src/ast/ast.h
index 7fc86070d..03713ee4b 100644
--- a/src/ast/ast.h
+++ b/src/ast/ast.h
@@ -404,7 +404,6 @@ struct func_decl_info : public decl_info {
bool m_injective:1;
bool m_idempotent:1;
bool m_skolem:1;
- bool m_lambda:1;
bool m_polymorphic:1;
func_decl_info(family_id family_id = null_family_id, decl_kind k = null_decl_kind, unsigned num_parameters = 0, parameter const * parameters = nullptr);
@@ -419,7 +418,6 @@ struct func_decl_info : public decl_info {
bool is_injective() const { return m_injective; }
bool is_idempotent() const { return m_idempotent; }
bool is_skolem() const { return m_skolem; }
- bool is_lambda() const { return m_lambda; }
bool is_polymorphic() const { return m_polymorphic; }
void set_associative(bool flag = true) { m_left_assoc = flag; m_right_assoc = flag; }
@@ -432,7 +430,6 @@ struct func_decl_info : public decl_info {
void set_injective(bool flag = true) { m_injective = flag; }
void set_idempotent(bool flag = true) { m_idempotent = flag; }
void set_skolem(bool flag = true) { m_skolem = flag; }
- void set_lambda(bool flag = true) { m_lambda = flag; }
void set_polymorphic(bool flag = true) { m_polymorphic = flag; }
bool operator==(func_decl_info const & info) const;
@@ -661,7 +658,6 @@ public:
bool is_pairwise() const { return get_info() != nullptr && get_info()->is_pairwise(); }
bool is_injective() const { return get_info() != nullptr && get_info()->is_injective(); }
bool is_skolem() const { return get_info() != nullptr && get_info()->is_skolem(); }
- bool is_lambda() const { return get_info() != nullptr && get_info()->is_lambda(); }
bool is_idempotent() const { return get_info() != nullptr && get_info()->is_idempotent(); }
bool is_polymorphic() const { return get_info() != nullptr && get_info()->is_polymorphic(); }
unsigned get_arity() const { return m_arity; }
@@ -857,7 +853,8 @@ public:
enum quantifier_kind {
forall_k,
exists_k,
- lambda_k
+ lambda_k,
+ choice_k
};
class quantifier : public expr {
@@ -1512,7 +1509,6 @@ protected:
proof_gen_mode m_proof_mode;
bool m_int_real_coercions; // If true, use hack that automatically introduces to_int/to_real when needed.
ast_table m_ast_table;
- obj_map m_lambda_defs;
id_gen m_expr_id_gen;
id_gen m_decl_id_gen;
sort * m_bool_sort;
@@ -1642,15 +1638,7 @@ public:
bool are_distinct(expr * a, expr * b) const;
bool contains(ast * a) const { return m_ast_table.contains(a); }
-
- bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; }
- void add_lambda_def(func_decl* f, quantifier* q);
- quantifier* is_lambda_def(func_decl* f);
- quantifier* is_lambda_def(app* e) { return is_lambda_def(e->get_decl()); }
- obj_map const& lambda_defs() const { return m_lambda_defs; }
-
- symbol const& lambda_def_qid() const { return m_lambda_def; }
-
+
unsigned get_num_asts() const { return m_ast_table.size(); }
void debug_ref_count() { m_debug_ref_count = true; }
diff --git a/src/ast/ast_lt.cpp b/src/ast/ast_lt.cpp
index cab7c5b53..48c60085f 100644
--- a/src/ast/ast_lt.cpp
+++ b/src/ast/ast_lt.cpp
@@ -18,12 +18,12 @@ Revision History:
--*/
#include "ast/ast.h"
-#define check_symbol(S1,S2) if (S1 != S2) return lt(S1,S2)
-#define check_value(V1,V2) if (V1 != V2) return V1 < V2
-#define check_bool(B1,B2) if (B1 != B2) return !B1 && B2
-#define check_ptr(P1,P2) if (!P1 && P2) return true; if (P1 && !P2) return false
-#define check_ast(T1,T2) if (T1 != T2) { n1 = T1; n2 = T2; goto start; }
-#define check_zstring(S1, S2) if (S1 != S2) return S1 < S2
+#define check_symbol(S1,S2) if ((S1) != (S2)) return lt((S1),(S2))
+#define check_value(V1,V2) if ((V1) != (V2)) return (V1) < (V2)
+#define check_bool(B1,B2) if ((B1) != (B2)) return !(B1) && (B2)
+#define check_ptr(P1,P2) if (!(P1) && (P2)) return true; if ((P1) && !(P2)) return false
+#define check_ast(T1,T2) if ((T1) != (T2)) { n1 = (T1); n2 = (T2); goto start; }
+#define check_zstring(S1, S2) if ((S1) != (S2)) return (S1) < (S2)
#define check_parameter(p1, p2) { \
check_value(p1.get_kind(), p2.get_kind()); \
diff --git a/src/ast/ast_pp_dot.cpp b/src/ast/ast_pp_dot.cpp
index 3dae0492c..17750a7b9 100644
--- a/src/ast/ast_pp_dot.cpp
+++ b/src/ast/ast_pp_dot.cpp
@@ -77,7 +77,7 @@ private:
void pp_atomic_step(const expr * e) {
unsigned id = get_id(e);
- m_out << "node_" << id << " [shape=box,color=\"yellow\",style=\"filled\",label=\"" << label_of_expr(e) << "\"] ;" << std::endl;
+ m_out << "node_" << id << " [shape=box,color=\"yellow\",style=\"filled\",label=\"" << label_of_expr(e) << "\"] ;" << '\n';
}
void pp_step(const proof * p) {
@@ -91,7 +91,7 @@ private:
m_first ? (m_first=false,"color=\"red\"") : num_parents==0 ? "color=\"yellow\"": "";
m_out << "node_" << id <<
" [shape=box,style=\"filled\",label=\"" << label_of_expr(p_res) << "\""
- << color << "]" << std::endl;
+ << color << "]" << '\n';
// now print edges to parents (except last one, which is the result)
std::string label = p->get_decl()->get_name().str();
for (unsigned i = 0 ; i < num_parents; ++i) {
@@ -99,7 +99,7 @@ private:
// explore parent, also print a link to it
push_term(to_app(parent));
m_out << "node_" << id << " -> " << "node_" << get_id((expr*)parent)
- << "[label=\"" << label << "\"];" << std::endl;;
+ << "[label=\"" << label << "\"];" << '\n';
}
} else {
pp_atomic_step(p);
@@ -120,11 +120,11 @@ private:
// main printer
std::ostream & ast_pp_dot::pp(std::ostream & out) const {
- out << "digraph proof { " << std::endl;
+ out << "digraph proof { " << '\n';
ast_pp_dot_st pp_st(this, out);
pp_st.push_term(m_pr);
pp_st.pp_loop();
- out << std::endl << " } " << std::endl << std::flush;
+ out << '\n' << " } " << '\n' << std::flush;
return out;
}
diff --git a/src/ast/ast_smt_pp.cpp b/src/ast/ast_smt_pp.cpp
index 23130e902..db52a583f 100644
--- a/src/ast/ast_smt_pp.cpp
+++ b/src/ast/ast_smt_pp.cpp
@@ -733,7 +733,8 @@ public:
m_AUFLIRA("AUFLIRA"),
// It's much easier to read those testcases with that.
m_no_lets(no_lets),
- m_simplify_implies(simplify_implies)
+ m_simplify_implies(simplify_implies),
+ m_top(nullptr)
{
m_basic_fid = m.get_basic_family_id();
m_label_fid = m.mk_family_id("label");
diff --git a/src/ast/ast_translation.cpp b/src/ast/ast_translation.cpp
index affe9d49d..126b52099 100644
--- a/src/ast/ast_translation.cpp
+++ b/src/ast/ast_translation.cpp
@@ -181,20 +181,12 @@ void ast_translation::mk_func_decl(func_decl * f, frame & fr) {
new_fi.set_injective(fi->is_injective());
new_fi.set_skolem(fi->is_skolem());
new_fi.set_idempotent(fi->is_idempotent());
- new_fi.set_lambda(fi->is_lambda());
new_f = m_to_manager.mk_func_decl(f->get_name(),
f->get_arity(),
new_domain,
new_range,
new_fi);
-
- if (new_fi.is_lambda()) {
- quantifier* q = from().is_lambda_def(f);
- ast_translation tr(from(), to());
- quantifier* new_q = tr(q);
- to().add_lambda_def(new_f, new_q);
- }
}
TRACE(ast_translation,
tout << f->get_name() << " "; if (fi) tout << *fi; tout << "\n";
diff --git a/src/ast/ast_util.cpp b/src/ast/ast_util.cpp
index 5d71ffda9..d29d437f2 100644
--- a/src/ast/ast_util.cpp
+++ b/src/ast/ast_util.cpp
@@ -168,7 +168,7 @@ expr * mk_and(ast_manager & m, unsigned num_args, expr * const * args) {
}
app* mk_and(ast_manager & m, unsigned num_args, app * const * args) {
- return to_app(mk_and(m, num_args, (expr* const*) args));
+ return to_app(mk_and(m, num_args, reinterpret_cast(args)));
}
expr * mk_or(ast_manager & m, unsigned num_args, expr * const * args) {
diff --git a/src/ast/bv_decl_plugin.cpp b/src/ast/bv_decl_plugin.cpp
index 76dc8fe07..bb65581cc 100644
--- a/src/ast/bv_decl_plugin.cpp
+++ b/src/ast/bv_decl_plugin.cpp
@@ -783,6 +783,9 @@ void bv_decl_plugin::get_op_names(svector & op_names, symbol const
op_names.push_back(builtin_name("rotate_left",OP_ROTATE_LEFT));
op_names.push_back(builtin_name("rotate_right",OP_ROTATE_RIGHT));
op_names.push_back(builtin_name("bit2bool", OP_BIT2BOOL));
+ op_names.push_back(builtin_name("ubv_to_int", OP_UBV2INT));
+ op_names.push_back(builtin_name("sbv_to_int", OP_SBV2INT));
+ op_names.push_back(builtin_name("int_to_bv", OP_INT2BV));
if (logic == symbol::null || logic == symbol("ALL") || logic == "QF_FD" || logic == "HORN") {
op_names.push_back(builtin_name("bvumul_noovfl",OP_BUMUL_NO_OVFL));
@@ -804,11 +807,10 @@ void bv_decl_plugin::get_op_names(svector & op_names, symbol const
op_names.push_back(builtin_name("ext_rotate_left",OP_EXT_ROTATE_LEFT));
op_names.push_back(builtin_name("ext_rotate_right",OP_EXT_ROTATE_RIGHT));
op_names.push_back(builtin_name("int2bv",OP_INT2BV));
- op_names.push_back(builtin_name("int_to_bv",OP_INT2BV));
+
op_names.push_back(builtin_name("bv2int",OP_UBV2INT));
op_names.push_back(builtin_name("bv2nat",OP_UBV2INT));
- op_names.push_back(builtin_name("ubv_to_int",OP_UBV2INT));
- op_names.push_back(builtin_name("sbv_to_int",OP_SBV2INT));
+
op_names.push_back(builtin_name("mkbv",OP_MKBV));
}
}
diff --git a/src/ast/euf/euf_mam.cpp b/src/ast/euf/euf_mam.cpp
index cce838c61..d47eb087b 100644
--- a/src/ast/euf/euf_mam.cpp
+++ b/src/ast/euf/euf_mam.cpp
@@ -133,7 +133,7 @@ namespace euf {
// Instructions
//
// ------------------------------------
- typedef enum {
+ typedef enum : uint8_t {
INIT1=0, INIT2, INIT3, INIT4, INIT5, INIT6, INITN, INITAC,
BIND1, BIND2, BIND3, BIND4, BIND5, BIND6, BINDN,
YIELD1, YIELD2, YIELD3, YIELD4, YIELD5, YIELD6, YIELDN,
@@ -239,6 +239,7 @@ namespace euf {
unsigned short m_num_args;
unsigned m_ireg;
unsigned m_oreg;
+ unsigned m_curr_generation;
};
struct get_cgr : public instruction {
@@ -1926,28 +1927,38 @@ namespace euf {
m_max_generation = std::max(m_max_generation, n->generation());
}
+ void get_f_app(func_decl* lbl, unsigned num_expected_args, enode* curr, enode*& matching_cgr, enode*& min_gen_match) {
+ if (curr->get_decl() == lbl && curr->num_args() == num_expected_args) {
+ if (curr->is_cgr() && !matching_cgr)
+ matching_cgr = curr;
+ if (!min_gen_match || min_gen_match->generation() > curr->generation())
+ min_gen_match = curr;
+ }
+ }
+
// We have to provide the number of expected arguments because we have flat-assoc applications such as +.
// Flat-assoc applications may have arbitrary number of arguments.
enode * get_first_f_app(func_decl * lbl, unsigned num_expected_args, enode * first) {
+ enode *matching_cgr = nullptr, *min_gen_match = nullptr;
for (enode* curr : euf::enode_class(first)) {
- if (curr->get_decl() == lbl && curr->is_cgr() && curr->num_args() == num_expected_args) {
- update_max_generation(curr, first);
- return curr;
- }
+ get_f_app(lbl, num_expected_args, curr, matching_cgr, min_gen_match);
+ curr = curr->get_next();
}
- return nullptr;
+ if (matching_cgr)
+ update_max_generation(min_gen_match, first);
+ return matching_cgr;
}
enode * get_next_f_app(func_decl * lbl, unsigned num_expected_args, enode * first, enode * curr) {
curr = curr->get_next();
+ enode *matching_cgr = nullptr, *min_gen_match = nullptr;
while (curr != first) {
- if (curr->get_decl() == lbl && curr->is_cgr() && curr->num_args() == num_expected_args) {
- update_max_generation(curr, first);
- return curr;
- }
+ get_f_app(lbl, num_expected_args, curr, matching_cgr, min_gen_match);
curr = curr->get_next();
}
- return nullptr;
+ if (matching_cgr)
+ update_max_generation(min_gen_match, first);
+ return matching_cgr;
}
/**
@@ -2563,6 +2574,7 @@ namespace euf {
m_backtrack_stack[m_top].m_instr = m_pc; \
m_backtrack_stack[m_top].m_old_max_generation = m_curr_max_generation; \
m_backtrack_stack[m_top].m_curr = m_app; \
+ const_cast(static_cast(m_pc))->m_curr_generation = m_max_generation; \
m_top++;
BIND_COMMON();
@@ -2829,7 +2841,8 @@ namespace euf {
goto backtrack; \
} \
bp.m_curr = m_app; \
- TRACE(mam_int, tout << "bind next candidate:\n" << mk_ll_pp(m_app->get_expr(), m);); \
+ m_max_generation = m_b->m_curr_generation; \
+ TRACE(mam_int, tout << "bind next candidate:\n" << mk_ll_pp(m_app->get_expr(), m);); \
m_oreg = m_b->m_oreg
BBIND_COMMON();
@@ -4059,4 +4072,4 @@ void euf::mam::ground_subterms(expr* e, ptr_vector& ground) {
euf::mam* euf::mam::mk(euf::mam_solver& ctx, euf::on_binding_callback& em) {
return alloc(mam_impl, ctx, em, true);
-}
\ No newline at end of file
+}
diff --git a/src/ast/euf/ho_matcher.cpp b/src/ast/euf/ho_matcher.cpp
index f86d82b64..a740167ad 100644
--- a/src/ast/euf/ho_matcher.cpp
+++ b/src/ast/euf/ho_matcher.cpp
@@ -64,7 +64,7 @@ namespace euf {
}
void ho_matcher::search() {
- IF_VERBOSE(1, display(verbose_stream()));
+ IF_VERBOSE(10, display(verbose_stream()));
while (m.inc()) {
// Q, B -> Q', B'. Push work on the backtrack stack and new work items
@@ -77,7 +77,7 @@ namespace euf {
break;
}
- IF_VERBOSE(1, display(verbose_stream() << "ho_matcher: done\n"));
+ IF_VERBOSE(10, display(verbose_stream() << "ho_matcher: done\n"));
}
void ho_matcher::backtrack() {
@@ -92,7 +92,7 @@ namespace euf {
while (!m_backtrack.empty()) {
auto& wi = *m_backtrack.back();
bool st = consume_work(wi);
- IF_VERBOSE(3, display(verbose_stream() << "ho_matcher::consume_work: " << wi.pat << " =?= " << wi.t << " -> " << (st?"true":"false") << "\n"););
+ TRACE(ho_matching, display(tout << "ho_matcher::consume_work: " << mk_bounded_pp(wi.pat, m) << " =?= " << mk_bounded_pp(wi.t, m) << " -> " << (st?"true":"false") << "\n"););
if (st) {
if (m_goals.empty())
m_on_match(m_subst);
@@ -110,7 +110,11 @@ namespace euf {
}
lbool ho_matcher::are_equal(unsigned o1, expr* p, unsigned o2, expr* t) const {
- SASSERT(p->get_sort() == t->get_sort());
+ if (p->get_sort() != t->get_sort()) {
+ TRACE(ho_matching, tout << "sort mismatch: " << mk_pp(p, m) << " : " << mk_pp(p->get_sort(), m)
+ << " vs " << mk_pp(t, m) << " : " << mk_pp(t->get_sort(), m) << "\n";);
+ return l_false;
+ }
if (o1 == o2 && p == t)
return l_true;
@@ -239,25 +243,19 @@ namespace euf {
return r;
}
- // We assume that m_rewriter should produce
- // something amounting to weak-head normal form WHNF
+ expr_ref ho_matcher::whnf_star(expr *e, unsigned offset) const {
+ expr_ref r(e, m);
+ while (true) {
+ auto q = whnf(r, offset);
+ if (q == r)
+ return r;
+ r = q;
+ }
+ }
void ho_matcher::reduce(match_goal& wi) {
- while (true) {
- expr_ref r = whnf(wi.pat, wi.pat_offset());
- if (r == wi.pat)
- break;
- IF_VERBOSE(3, verbose_stream() << "ho_matcher::reduce: " << wi.pat << " -> " << r << "\n";);
- wi.pat = r;
- }
-
- while (true) {
- expr_ref r = whnf(wi.t, wi.term_offset());
- if (r == wi.t)
- break;
- IF_VERBOSE(3, verbose_stream() << "ho_matcher::reduce: " << wi.t << " -> " << r << "\n";);
- wi.t = r;
- }
+ wi.pat = whnf_star(wi.pat, wi.pat_offset());
+ wi.t = whnf_star(wi.t, wi.term_offset());
}
bool ho_matcher::consume_work(match_goal &wi) {
@@ -288,7 +286,6 @@ namespace euf {
break;
}
-
// v >= offset
// v - offset |-> t
if (is_meta_var(p, wi.pat_offset()) && is_closed(t, 0, wi.term_offset())) {
@@ -299,7 +296,6 @@ namespace euf {
return true;
}
-
// N = \ x. T => ((shift1 N) x) = T
if (is_lambda(t) && !is_lambda(p)) {
auto q = to_quantifier(t);
@@ -318,6 +314,43 @@ namespace euf {
return true;
}
+ // \x . N = T => N = ((shift1 T) x)
+ if (is_lambda(p) && !is_lambda(t)) {
+ auto q = to_quantifier(p);
+ auto p_body = q->get_expr();
+ auto nd = q->get_num_decls();
+ var_shifter vs(m);
+ expr_ref r(m);
+ vs(t, nd, r);
+ expr_ref_vector args(m);
+ args.push_back(r);
+ for (unsigned i = 0; i < nd; ++i)
+ args.push_back(m.mk_var(nd - 1 - i, q->get_decl_sort(i)));
+ r = m_array.mk_select(args);
+ m_goals.push(wi.level, wi.term_offset() + nd, p_body, r);
+ wi.set_done();
+ return true;
+ }
+
+ //
+ // lambda x . p == lambda x . t
+ //
+ if (is_quantifier(p) && is_quantifier(t)) {
+ auto qp = to_quantifier(p);
+ auto qt = to_quantifier(t);
+ unsigned pd = qp->get_num_decls();
+ unsigned td = qt->get_num_decls();
+ if (qp->get_kind() != qt->get_kind())
+ return false;
+ if (pd != td)
+ return false;
+ for (unsigned i = 0; i < pd; ++i)
+ if (qp->get_decl_sort(i) != qt->get_decl_sort(i))
+ return false;
+ m_goals.push(wi.level, wi.term_offset() + td, qp->get_expr(), qt->get_expr());
+ return true;
+ }
+
// Flex head unitary
// H(pat) = t
@@ -457,25 +490,7 @@ namespace euf {
m_goals.push(wi.level, wi.term_offset(), tp->get_arg(i), ta->get_arg(i));
return true;
}
-
- //
- // lambda x . p == lambda x . t
- //
- if (is_quantifier(p) && is_quantifier(t)) {
- auto qp = to_quantifier(p);
- auto qt = to_quantifier(t);
- unsigned pd = qp->get_num_decls();
- unsigned td = qt->get_num_decls();
- if (qp->get_kind() != qt->get_kind())
- return false;
- if (pd != td)
- return false;
- for (unsigned i = 0; i < pd; ++i)
- if (qp->get_decl_sort(i) != qt->get_decl_sort(i))
- return false;
- m_goals.push(wi.level, wi.term_offset() + td, qp->get_expr(), qt->get_expr());
- return true;
- }
+
return false;
}
@@ -488,8 +503,7 @@ namespace euf {
uint_set vars;
while (m_array.is_select(p)) {
auto a = to_app(p);
- for (unsigned i = 1; i < a->get_num_args(); ++i) {
- auto arg = a->get_arg(i);
+ for (auto arg : *a) {
if (!is_bound_var(arg, offset))
return false;
auto idx = to_var(arg)->get_idx();
@@ -549,15 +563,12 @@ namespace euf {
}
expr_ref_vector pat2bound(m);
for (auto a : pats) {
- unsigned sz = a->get_num_args();
- for (unsigned i = 1; i < sz; ++i) {
- auto arg = a->get_arg(i);
+ for (auto arg : *a) {
SASSERT(is_bound_var(arg, offset));
auto idx = to_var(arg)->get_idx();
pat2bound.reserve(idx + 1);
pat2bound[idx] = m.mk_var(--num_bound, arg->get_sort());
- }
- p1 = a->get_arg(0);
+ }
}
var_subst sub(m, false);
expr_ref lam = sub(t, pat2bound);
@@ -575,7 +586,7 @@ namespace euf {
//
// keep track of number of internal scopes and offset to non-capture variables.
- // a variable is captured if it's index is in the interval [scopes, offset[.
+ // a variable is captured if its index is in the interval [scopes, offset[.
//
bool ho_matcher::is_closed(expr* v, unsigned scopes, unsigned offset) const {
if (is_ground(v))
@@ -630,49 +641,59 @@ namespace euf {
void ho_matcher::add_binding(var* v, unsigned offset, expr* t) {
SASSERT(v->get_idx() >= offset);
m_subst.set(v->get_idx() - offset, t);
- IF_VERBOSE(1, verbose_stream() << "ho_matcher::add_binding: v" << v->get_idx() - offset << " -> " << mk_pp(t, m) << "\n";);
+ SASSERT(v->get_sort() == t->get_sort());
+ TRACE(ho_matching, tout << "ho_matcher::add_binding: v" << v->get_idx() - offset << " -> " << mk_pp(t, m) << "\n";);
m_trail.push(undo_set(m_subst, v->get_idx() - offset));
}
std::pair ho_matcher::compile_ho_pattern(quantifier* q, app* p) {
app* p1 = nullptr;
- if (m_pat2hopat.find(p, p)) {
- q = m_q2hoq[q];
- return { q, p };
+ quantifier *q1 = nullptr;
+ if (m_pat2hopat.find(p, p1) && m_q2hoq.find(q, q1)) {
+ return { q1, p1 };
}
- auto is_ho = any_of(subterms::all(expr_ref(p, m)), [&](expr* t) { return m_unitary.is_flex(0, t); });
+ auto is_ho = any_of(subterms::all(expr_ref(p, m)), [&](expr* t) {
+ return m_unitary.is_flex(0, t) ||
+ // m.is_lambda_def(t) ||
+ is_lambda(t);
+ });
if (!is_ho)
return { q, p };
- ptr_vector todo;
+ vector> todo;
ptr_buffer bound;
expr_ref_vector cache(m);
unsigned nb = q->get_num_decls();
- todo.push_back(p);
+ bool contains_pat2abs = m_pat2abs.contains(p);
+ SASSERT(m.is_pattern(p));
+ todo.push_back({p, 0});
while (!todo.empty()) {
- auto t = todo.back();
+ auto [t, lvl] = todo.back();
if (is_var(t)) {
cache.setx(t->get_id(), t);
todo.pop_back();
continue;
}
- if (m_unitary.is_flex(0, t)) {
- m_pat2abs.insert_if_not_there(p, svector>()).push_back({ nb, t });
+ if ((m_unitary.is_flex(0, t) && lvl > 1) || // m.is_lambda_def(t) ||
+ is_lambda(t)) {
+ if (!contains_pat2abs)
+ m_pat2abs.insert_if_not_there(p, svector>()).push_back({ nb, t });
auto v = m.mk_var(nb++, t->get_sort());
bound.push_back(v);
cache.setx(t->get_id(), v);
todo.pop_back();
continue;
- }
+ }
if (is_app(t)) {
auto a = to_app(t);
+
unsigned sz = a->get_num_args();
ptr_buffer args;
for (auto arg : *a) {
cache.reserve(arg->get_id() + 1);
expr* arg1 = cache.get(arg->get_id());
if (!arg1)
- todo.push_back(arg);
+ todo.push_back({arg, lvl + 1});
else
args.push_back(arg1);
}
@@ -682,11 +703,15 @@ namespace euf {
cache.setx(t->get_id(), m.mk_app(a->get_decl(), args.size(), args.data()));
}
if (is_quantifier(t)) {
- m_pat2abs.remove(p);
+ if (!contains_pat2abs)
+ m_pat2abs.remove(p);
return { q, p };
}
}
p1 = to_app(cache.get(p->get_id()));
+
+ if (p1 == p)
+ return {q, p};
expr_free_vars free_vars;
free_vars(p1);
app_ref_vector new_ground(m);
@@ -713,6 +738,8 @@ namespace euf {
auto body = q->get_expr();
if (!new_patterns.empty()) {
ptr_vector pats;
+ CTRACE(ho_matching, !m.is_pattern(p1),
+ tout << mk_pp(p, m) << "\n" << mk_pp(p1, m) << "\n";);
VERIFY(m.is_pattern(p1, pats));
for (auto p : new_patterns) // patterns for variables that are not free in new pattern
pats.push_back(p);
@@ -721,23 +748,40 @@ namespace euf {
p1 = m.mk_pattern(pats.size(), pats.data());
}
- quantifier* q1 = m.mk_forall(sorts.size(), sorts.data(), names.data(), body);
+ q1 = m.mk_forall(sorts.size(), sorts.data(), names.data(), body);
- m_pat2hopat.insert(p, p1);
- m_hopat2pat.insert(p1, p);
- m_q2hoq.insert(q, q1);
- m_hoq2q.insert(q1, q);
- m_hopat2free_vars.insert(p1, std::move(free_vars));
m_ho_patterns.push_back(p1);
m_ho_qs.push_back(q1);
trail().push(push_back_vector(m_ho_patterns));
trail().push(push_back_vector(m_ho_qs));
- trail().push(insert_map(m_pat2hopat, p));
- trail().push(insert_map(m_hopat2pat, p1));
- trail().push(insert_map(m_pat2abs, p));
- trail().push(insert_map(m_q2hoq, q));
- trail().push(insert_map(m_hoq2q, q1));
- trail().push(insert_map(m_hopat2free_vars, p1));
+
+ if (!m_pat2hopat.contains(p)) {
+ m_pat2hopat.insert(p, p1);
+ trail().push(insert_map(m_pat2hopat, p));
+ }
+ if (!m_hopat2pat.contains(p1)) {
+ m_hopat2pat.insert(p1, p);
+ trail().push(insert_map(m_hopat2pat, p1));
+ }
+ if (!m_q2hoq.contains(q)) {
+ m_q2hoq.insert(q, q1);
+ trail().push(insert_map(m_q2hoq, q));
+ }
+ if (!m_hoq2q.contains(q1)) {
+ m_hoq2q.insert(q1, q);
+ trail().push(insert_map(m_hoq2q, q1));
+ }
+ if (!m_hopat2free_vars.contains(p1)) {
+ m_hopat2free_vars.insert(p1, std::move(free_vars));
+ trail().push(insert_map(m_hopat2free_vars, p1));
+ }
+ if (!contains_pat2abs)
+ trail().push(insert_map(m_pat2abs, p));
+
+ TRACE(ho_matching, tout << mk_pp(q, m) << "\n"
+ << mk_pp(p, m) << "\n->\n"
+ << mk_pp(q1, m) << "\n"
+ << mk_pp(p1, m) << "\n");
return { q1, p1 };
}
@@ -745,28 +789,46 @@ namespace euf {
return m_hopat2pat.contains(p);
}
+ void ho_matcher::register_ho_pattern(app* alias_p, app* full_p) {
+ if (alias_p == full_p) return;
+ auto orig_p = m_hopat2pat[full_p];
+ m_hopat2pat.insert(alias_p, orig_p);
+ m_hopat2free_vars.insert(alias_p, m_hopat2free_vars[full_p]);
+ m_ho_patterns.push_back(alias_p);
+ trail().push(push_back_vector(m_ho_patterns));
+ trail().push(insert_map(m_hopat2pat, alias_p));
+ trail().push(insert_map(m_hopat2free_vars, alias_p));
+ }
+
void ho_matcher::refine_ho_match(app* p, expr_ref_vector& s) {
auto fo_pat = m_hopat2pat[p];
+ IF_VERBOSE(10, verbose_stream() << "refine_ho_match: p=" << mk_pp(p, m) << "\n fo_pat=" << mk_pp(fo_pat, m) << "\n";
+ verbose_stream() << " m_pat2abs has fo_pat: " << m_pat2abs.contains(fo_pat) << "\n";
+ auto& abs = m_pat2abs[fo_pat];
+ verbose_stream() << " m_pat2abs size: " << abs.size() << "\n";
+ for (auto [v, pat] : abs) verbose_stream() << " v=" << v << " pat=" << mk_pp(pat, m) << "\n";);
m_trail.push_scope();
m_subst.resize(0);
m_subst.resize(s.size());
m_goals.reset();
+ // MAM bindings are reversed: s[i] = binding for var idx = s.size()-1-i
+ // m_subst is indexed by var index directly
for (unsigned i = 0; i < s.size(); ++i) {
auto idx = s.size() - i - 1;
if (!m_hopat2free_vars[p].contains(idx))
s[i] = m.mk_var(idx, s[i]->get_sort());
else if (s.get(i))
- m_subst.set(i, s.get(i));
+ m_subst.set(idx, s.get(i));
}
- IF_VERBOSE(1, verbose_stream() << "refine " << mk_pp(p, m) << "\n" << s << "\n");
+ TRACE(ho_matching, tout << "refine " << mk_pp(p, m) << "\n" << s << "\n");
unsigned num_bound = 0, level = 0;
for (auto [v, pat] : m_pat2abs[fo_pat]) {
var_subst sub(m, true);
auto pat_refined = sub(pat, s);
- IF_VERBOSE(1, verbose_stream() << mk_pp(pat, m) << " -> " << pat_refined << "\n");
- m_goals.push(level, num_bound, pat_refined, s.get(s.size() - v - 1));
+ TRACE(ho_matching, tout << mk_pp(pat, m) << " -> " << pat_refined << "\n");
+ m_goals.push(level, num_bound, pat_refined, m_subst.get(v));
}
search();
diff --git a/src/ast/euf/ho_matcher.h b/src/ast/euf/ho_matcher.h
index 65477078c..023555926 100644
--- a/src/ast/euf/ho_matcher.h
+++ b/src/ast/euf/ho_matcher.h
@@ -25,6 +25,7 @@ Author:
#include "ast/for_each_expr.h"
#include "ast/reg_decl_plugins.h"
#include "ast/ast_pp.h"
+#include "ast/ast_ll_pp.h"
#include "ast/rewriter/array_rewriter.h"
#include "ast/rewriter/var_subst.h"
@@ -88,13 +89,15 @@ namespace euf {
}
match_goal(unsigned level, unsigned offset, expr_ref const& pat, expr_ref const& t) noexcept :
- base_offset(offset), pat(pat), t(t), level(level) {}
+ base_offset(offset), pat(pat), t(t), level(level) {
+ SASSERT(pat->get_sort() == t->get_sort());
+ }
unsigned term_offset() const { return base_offset + delta_offset; }
unsigned pat_offset() const { return base_offset + delta_offset; }
std::ostream& display(std::ostream& out) const {
- return out << "[" << level << ":" << base_offset + delta_offset << "] " << pat << " ~ " << t << "\n";
+ return out << "[" << level << ":" << base_offset + delta_offset << "] " << mk_bounded_pp(pat, pat.m()) << " ~ " << mk_bounded_pp(t, t.m()) << "\n";
}
};
@@ -329,6 +332,8 @@ namespace euf {
bool consume_work(match_goal& wi);
expr_ref whnf(expr* e, unsigned offset) const;
+
+ expr_ref whnf_star(expr *e, unsigned offset) const;
bool is_bound_var(expr* v, unsigned offset) const { return is_var(v) && to_var(v)->get_idx() < offset; }
@@ -389,11 +394,23 @@ namespace euf {
bool is_ho_pattern(app* p);
+ // Register an alias pattern (e.g., after stripping ground elements)
+ // that maps to the same original pattern as full_p
+ void register_ho_pattern(app* alias_p, app* full_p);
+
void refine_ho_match(app* p, expr_ref_vector& s);
bool is_free(app* p, unsigned i) const { return m_hopat2free_vars[p].contains(i); }
quantifier* hoq2q(quantifier* q) const { return m_hoq2q[q]; }
+
+ svector> const* get_flex_subterms(app* p) const {
+ auto orig_p = m_hopat2pat.find_core(p);
+ if (!orig_p) return nullptr;
+ auto abs = m_pat2abs.find_core(orig_p->get_data().get_value());
+ return abs ? &abs->get_data().get_value() : nullptr;
+ }
+
};
}
diff --git a/src/ast/fpa/fpa2bv_converter.cpp b/src/ast/fpa/fpa2bv_converter.cpp
index 5e02a101e..d2431174e 100644
--- a/src/ast/fpa/fpa2bv_converter.cpp
+++ b/src/ast/fpa/fpa2bv_converter.cpp
@@ -2976,13 +2976,12 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar
prev_bit = bit;
}
- expr_ref one_div_exp2(m);
- one_div_exp2 = m_arith_util.mk_div(one, exp2);
- exp2 = m.mk_ite(exp_is_neg, one_div_exp2, exp2);
- dbg_decouple("fpa2bv_to_real_exp2", exp2);
-
- expr_ref res(m), two_exp2(m), minus_res(m), sgn_is_1(m);
+ expr_ref two_exp2(m), one_div_two_exp2(m);
two_exp2 = m_arith_util.mk_power(two, exp2);
+ one_div_two_exp2 = m_arith_util.mk_div(one, two_exp2);
+ two_exp2 = m.mk_ite(exp_is_neg, one_div_two_exp2, two_exp2);
+ dbg_decouple("fpa2bv_to_real_exp2", two_exp2);
+ expr_ref res(m), minus_res(m), sgn_is_1(m);
res = m_arith_util.mk_mul(rsig, two_exp2);
minus_res = m_arith_util.mk_uminus(res);
sgn_is_1 = m.mk_eq(sgn, bv1);
@@ -2990,7 +2989,7 @@ void fpa2bv_converter::mk_to_real(func_decl * f, unsigned num, expr * const * ar
dbg_decouple("fpa2bv_to_real_sig_times_exp2", res);
TRACE(fpa2bv_to_real, tout << "rsig = " << mk_ismt2_pp(rsig, m) << std::endl;
- tout << "exp2 = " << mk_ismt2_pp(exp2, m) << std::endl;);
+ tout << "two_exp2 = " << mk_ismt2_pp(two_exp2, m) << std::endl;);
expr_ref unspec(m);
mk_to_real_unspecified(f, num, args, unspec);
diff --git a/src/ast/normal_forms/defined_names.cpp b/src/ast/normal_forms/defined_names.cpp
index 997022c69..9bb2b89c2 100644
--- a/src/ast/normal_forms/defined_names.cpp
+++ b/src/ast/normal_forms/defined_names.cpp
@@ -121,9 +121,6 @@ app * defined_names::impl::gen_name(expr * e, sort_ref_buffer & var_sorts, buffe
sort * range = e->get_sort();
func_decl * new_skolem_decl = m.mk_fresh_func_decl(m_z3name, symbol::null, domain.size(), domain.data(), range);
app * n = m.mk_app(new_skolem_decl, new_args.size(), new_args.data());
- if (is_lambda(e)) {
- m.add_lambda_def(new_skolem_decl, to_quantifier(e));
- }
return n;
}
@@ -193,43 +190,7 @@ void defined_names::impl::mk_definition(expr * e, app * n, sort_ref_buffer & var
else if (m.is_term_ite(e)) {
bound_vars(var_sorts, var_names, MK_OR(MK_NOT(to_app(e)->get_arg(0)), MK_EQ(n, to_app(e)->get_arg(1))), n, defs);
bound_vars(var_sorts, var_names, MK_OR(to_app(e)->get_arg(0), MK_EQ(n, to_app(e)->get_arg(2))), n, defs);
- }
- else if (is_lambda(e)) {
- // n(y) = \x . M[x,y]
- // =>
- // n(y)[x] = M, forall x y
- //
- // NB. The pattern is incomplete.
- // consider store(a, i, v) == \lambda j . if i = j then v else a[j]
- // the instantiation rules for store(a, i, v) are:
- // store(a, i, v)[j] = if i = j then v else a[j] with patterns {a[j], store(a, i, v)} { store(a, i, v)[j] }
- // The first pattern is not included.
- // TBD use a model-based scheme for extracting instantiations instead of
- // using multi-patterns.
- //
-
- quantifier* q = to_quantifier(e);
- expr_ref_vector args(m);
- expr_ref n2(m), n3(m);
- var_shifter vs(m);
- vs(n, q->get_num_decls(), n2);
- args.push_back(n2);
- var_sorts.append(q->get_num_decls(), q->get_decl_sorts());
- var_names.append(q->get_num_decls(), q->get_decl_names());
- for (unsigned i = 0; i < q->get_num_decls(); ++i) {
- args.push_back(m.mk_var(q->get_num_decls() - i - 1, q->get_decl_sort(i)));
- }
- array_util autil(m);
- func_decl * f = nullptr;
- if (autil.is_as_array(n2, f)) {
- n3 = m.mk_app(f, args.size()-1, args.data() + 1);
- }
- else {
- n3 = autil.mk_select(args.size(), args.data());
- }
- bound_vars(var_sorts, var_names, MK_EQ(q->get_expr(), n3), to_app(n3), defs, m.lambda_def_qid());
-
- }
+ }
else {
bound_vars(var_sorts, var_names, MK_EQ(e, n), n, defs);
}
diff --git a/src/ast/normal_forms/name_exprs.cpp b/src/ast/normal_forms/name_exprs.cpp
index 9cf1ab08f..577c3d51a 100644
--- a/src/ast/normal_forms/name_exprs.cpp
+++ b/src/ast/normal_forms/name_exprs.cpp
@@ -100,7 +100,7 @@ class name_quantifier_labels : public name_exprs_core {
public:
pred(ast_manager & m):m(m) {}
bool operator()(expr * t) override {
- return is_quantifier(t) || m.is_label(t);
+ return (is_quantifier(t) && !is_lambda(t)) || m.is_label(t);
}
};
@@ -127,7 +127,7 @@ class name_nested_formulas : public name_exprs_core {
TRACE(name_exprs, tout << "name_nested_formulas::pred:\n" << mk_ismt2_pp(t, m) << "\n";);
if (is_app(t))
return to_app(t)->get_family_id() == m.get_basic_family_id() && to_app(t)->get_num_args() > 0 && t != m_root;
- return m.is_label(t) || is_quantifier(t);
+ return m.is_label(t) || (is_quantifier(t) && !is_lambda(t));
}
};
diff --git a/src/ast/normal_forms/pull_quant.cpp b/src/ast/normal_forms/pull_quant.cpp
index a37580b86..be84afcc9 100644
--- a/src/ast/normal_forms/pull_quant.cpp
+++ b/src/ast/normal_forms/pull_quant.cpp
@@ -188,7 +188,7 @@ struct pull_quant::imp {
var_names.data(),
nested_q->get_expr(),
std::min(q->get_weight(), nested_q->get_weight()),
- m.is_lambda_def(q) ? symbol("pulled-lambda") : q->get_qid());
+ q->get_qid());
}
void pull_quant1(quantifier * q, expr * new_expr, expr_ref & result) {
diff --git a/src/ast/pattern/pattern_inference.cpp b/src/ast/pattern/pattern_inference.cpp
index 6d3518de0..6fd8c684d 100644
--- a/src/ast/pattern/pattern_inference.cpp
+++ b/src/ast/pattern/pattern_inference.cpp
@@ -254,6 +254,27 @@ void pattern_inference_cfg::collect::save_candidate(expr * n, unsigned delta) {
}
return;
}
+ case AST_QUANTIFIER: {
+ quantifier * q = to_quantifier(n);
+ unsigned num_decls = q->get_num_decls();
+ info * body_info = nullptr;
+ m_cache.find(entry(q->get_expr(), delta + num_decls), body_info);
+ if (body_info == nullptr) {
+ save(n, delta, nullptr);
+ return;
+ }
+ // The lambda/quantifier itself is a valid sub-term in a pattern.
+ // Propagate the free variables from the body (they already refer
+ // to the outer quantifier's bindings) and keep the node as-is.
+ expr * new_body = body_info->m_node.get();
+ quantifier_ref new_q(m);
+ if (new_body != q->get_expr())
+ new_q = m.update_quantifier(q, new_body);
+ else
+ new_q = q;
+ save(n, delta, alloc(info, m, new_q, body_info->m_free_vars, body_info->m_size + 1));
+ return;
+ }
default:
save(n, delta, nullptr);
return;
@@ -363,6 +384,8 @@ bool pattern_inference_cfg::contains_subpattern::operator()(expr * n) {
break;
case AST_VAR:
break;
+ case AST_QUANTIFIER:
+ break;
default:
UNREACHABLE();
}
@@ -525,7 +548,7 @@ void pattern_inference_cfg::reset_pre_patterns() {
bool pattern_inference_cfg::is_forbidden(app * n) const {
- func_decl const * decl = n->get_decl();
+ func_decl * decl = n->get_decl();
if (is_ground(n))
return false;
// Remark: skolem constants should not be used in patterns, since they do not
diff --git a/src/ast/recfun_decl_plugin.cpp b/src/ast/recfun_decl_plugin.cpp
index 660dcab28..57c1d4d12 100644
--- a/src/ast/recfun_decl_plugin.cpp
+++ b/src/ast/recfun_decl_plugin.cpp
@@ -444,7 +444,8 @@ namespace recfun {
promise_def plugin::mk_def(symbol const& name, unsigned n, sort *const * params, sort * range, bool is_generated) {
def* d = u().decl_fun(name, n, params, range, is_generated);
- SASSERT(!m_defs.contains(d->get_decl()));
+ if (m_defs.contains(d->get_decl()))
+ throw default_exception(std::string("recursive function ") + name.str() + " already defined");
m_defs.insert(d->get_decl(), d);
return promise_def(&u(), d);
}
diff --git a/src/ast/rewriter/array_rewriter.cpp b/src/ast/rewriter/array_rewriter.cpp
index 67f969197..5d3ddf1dd 100644
--- a/src/ast/rewriter/array_rewriter.cpp
+++ b/src/ast/rewriter/array_rewriter.cpp
@@ -750,7 +750,10 @@ bool array_rewriter::add_store(expr_ref_vector& args, unsigned num_idxs, expr* e
}
if (is_var(e1) && is_ground(e2)) {
unsigned idx = to_var(e1)->get_idx();
- args[num_idxs - idx - 1] = e2;
+ unsigned nidx = num_idxs - idx - 1;
+ if (args.get(nidx) && args.get(nidx) != e2)
+ return false;
+ args[nidx] = e2;
}
else {
return false;
@@ -858,19 +861,45 @@ br_status array_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result)
return false;
};
+ auto domain_is_larger_than = [&](sort* s, unsigned num_stores) {
+ unsigned sz = get_array_arity(s);
+ rational dsz(1);
+ for (unsigned i = 0; i < sz; ++i) {
+ sort* d = get_array_domain(s, i);
+ if (d->is_infinite())
+ return true;
+ if (d->is_very_big())
+ return false;
+ dsz *= rational(d->get_num_elements().size(), rational::ui64());
+ if (dsz > rational(num_stores, rational::ui64()))
+ return true;
+ }
+ return false;
+ };
+
+ expr* lhs1 = lhs;
+ expr* rhs1 = rhs;
+ unsigned num_lhs = 0, num_rhs = 0;
+ while (m_util.is_store(lhs1)) {
+ lhs1 = to_app(lhs1)->get_arg(0);
+ ++num_lhs;
+ }
+ while (m_util.is_store(rhs1)) {
+ rhs1 = to_app(rhs1)->get_arg(0);
+ ++num_rhs;
+ }
+
+ if (m_util.is_const(lhs1, v) && m_util.is_const(rhs1, w) &&
+ domain_is_larger_than(lhs->get_sort(), num_lhs + num_rhs)) {
+ mk_eq(lhs, lhs, rhs, fmls);
+ mk_eq(rhs, lhs, rhs, fmls);
+ fmls.push_back(m().mk_eq(v, w));
+ result = m().mk_and(fmls);
+ return BR_REWRITE_FULL;
+ }
+
if (m_expand_store_eq) {
- expr* lhs1 = lhs;
- expr* rhs1 = rhs;
- unsigned num_lhs = 0, num_rhs = 0;
- while (m_util.is_store(lhs1)) {
- lhs1 = to_app(lhs1)->get_arg(0);
- ++num_lhs;
- }
- while (m_util.is_store(rhs1)) {
- rhs1 = to_app(rhs1)->get_arg(0);
- ++num_rhs;
- }
if (lhs1 == rhs1) {
mk_eq(lhs, lhs, rhs, fmls);
mk_eq(rhs, lhs, rhs, fmls);
diff --git a/src/ast/rewriter/enum2bv_rewriter.cpp b/src/ast/rewriter/enum2bv_rewriter.cpp
index d2c5fd122..8210cdc93 100644
--- a/src/ast/rewriter/enum2bv_rewriter.cpp
+++ b/src/ast/rewriter/enum2bv_rewriter.cpp
@@ -225,6 +225,7 @@ struct enum2bv_rewriter::imp {
new_body_ref = mk_and(bounds);
break;
case lambda_k:
+ case choice_k:
UNREACHABLE();
break;
}
diff --git a/src/ast/rewriter/rewriter_def.h b/src/ast/rewriter/rewriter_def.h
index ebfc71482..ad4702de5 100644
--- a/src/ast/rewriter/rewriter_def.h
+++ b/src/ast/rewriter/rewriter_def.h
@@ -561,9 +561,13 @@ void rewriter_tpl::process_quantifier(quantifier * q, frame & fr) {
expr * const * np = it + 1;
expr * const * nnp = np + num_pats;
unsigned j = 0;
- for (unsigned i = 0; i < num_pats; ++i)
+ for (unsigned i = 0; i < num_pats; ++i) {
if (m_manager.is_pattern(np[i]))
new_pats[j++] = np[i];
+ else {
+ IF_VERBOSE(10, verbose_stream() << "[rewriter] dropping pattern (is_pattern check failed) for qid=" << q->get_qid() << " pattern[" << i << "]: " << mk_ismt2_pp(np[i], m_manager, 3) << "\n";);
+ }
+ }
new_pats.shrink(j);
num_pats = j;
j = 0;
@@ -664,7 +668,7 @@ template
void rewriter_tpl::display_bindings(std::ostream& out) {
for (unsigned i = 0; i < m_bindings.size(); ++i) {
if (m_bindings[i])
- out << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << ";\n";
+ out << i << ": " << mk_ismt2_pp(m_bindings[i], m()) << " : " << mk_pp(m_bindings[i]->get_sort(), m()) << ";\n";
}
}
diff --git a/src/ast/simplifiers/CMakeLists.txt b/src/ast/simplifiers/CMakeLists.txt
index d43bbe203..aae8227e4 100644
--- a/src/ast/simplifiers/CMakeLists.txt
+++ b/src/ast/simplifiers/CMakeLists.txt
@@ -17,6 +17,7 @@ z3_add_component(simplifiers
euf_completion.cpp
extract_eqs.cpp
factor_simplifier.cpp
+ fold_unfold.cpp
linear_equation.cpp
max_bv_sharing.cpp
model_reconstruction_trail.cpp
diff --git a/src/ast/simplifiers/bound_propagator.cpp b/src/ast/simplifiers/bound_propagator.cpp
index 240ba7a98..a73d81fb8 100644
--- a/src/ast/simplifiers/bound_propagator.cpp
+++ b/src/ast/simplifiers/bound_propagator.cpp
@@ -381,7 +381,7 @@ bool bound_propagator::relevant_bound(var x, double new_k) const {
if (b == nullptr)
return true; // variable did not have a bound
- double interval_size;
+ double interval_size = 0.0;
bool bounded = get_interval_size(x, interval_size);
if (!is_int(x)) {
@@ -939,4 +939,3 @@ void bound_propagator::display(std::ostream & out) const {
}
-
diff --git a/src/ast/simplifiers/dependent_expr_state.cpp b/src/ast/simplifiers/dependent_expr_state.cpp
index 9cfd8d1a0..1b9315466 100644
--- a/src/ast/simplifiers/dependent_expr_state.cpp
+++ b/src/ast/simplifiers/dependent_expr_state.cpp
@@ -88,22 +88,6 @@ void dependent_expr_state::freeze_recfun() {
m_num_recfun = sz;
}
-/**
-* Freeze all functions used in lambda defined declarations
-*/
-void dependent_expr_state::freeze_lambda() {
- auto& m = m_frozen_trail.get_manager();
- unsigned sz = m.lambda_defs().size();
- if (m_num_lambdas >= sz)
- return;
-
- ast_mark visited;
- for (auto const& [f, body] : m.lambda_defs())
- freeze_terms(body, false, visited);
- m_trail.push(value_trail(m_num_lambdas));
- m_num_lambdas = sz;
-}
-
/**
* The current qhead is to be updated to qtail.
@@ -122,8 +106,7 @@ void dependent_expr_state::freeze_suffix() {
if (m_suffix_frozen)
return;
m_suffix_frozen = true;
- freeze_recfun();
- freeze_lambda();
+ freeze_recfun();
auto& m = m_frozen_trail.get_manager();
ast_mark visited;
ptr_vector es;
diff --git a/src/ast/simplifiers/dependent_expr_state.h b/src/ast/simplifiers/dependent_expr_state.h
index f30671bef..504f67ad0 100644
--- a/src/ast/simplifiers/dependent_expr_state.h
+++ b/src/ast/simplifiers/dependent_expr_state.h
@@ -51,7 +51,6 @@ class dependent_expr_state {
func_decl_ref_vector m_frozen_trail;
void freeze_prefix();
void freeze_recfun();
- void freeze_lambda();
void freeze_terms(expr* term, bool only_as_array, ast_mark& visited);
void freeze(func_decl* f);
struct thaw : public trail {
diff --git a/src/ast/simplifiers/elim_unconstrained.cpp b/src/ast/simplifiers/elim_unconstrained.cpp
index 974b37a00..6b53882cd 100644
--- a/src/ast/simplifiers/elim_unconstrained.cpp
+++ b/src/ast/simplifiers/elim_unconstrained.cpp
@@ -121,7 +121,7 @@ eliminate:
elim_unconstrained::elim_unconstrained(ast_manager& m, dependent_expr_state& fmls) :
dependent_expr_simplifier(m, fmls), m_inverter(m), m_lt(*this), m_heap(1024, m_lt), m_trail(m), m_args(m) {
std::function is_var = [&](expr* e) {
- return is_uninterp_const(e) && !m_fmls.frozen(e) && get_node(e).is_root() && get_node(e).num_parents() <= 1;
+ return is_uninterp_const(e) && !m_fmls.frozen(e) && !m_disabled.is_marked(e) && get_node(e).is_root() && get_node(e).num_parents() <= 1;
};
m_inverter.set_is_var(is_var);
}
@@ -247,10 +247,12 @@ elim_unconstrained::node& elim_unconstrained::get_node(expr* t) {
m_heap.increased(arg->get_id());
}
}
- else if (is_quantifier(t)) {
- node& ch = get_node(to_quantifier(t)->get_expr());
+ else if (is_quantifier(t)) {
+ auto body = to_quantifier(t)->get_expr();
+ node& ch = get_node(body);
SASSERT(ch.is_root());
ch.add_parent(*n);
+ disable(body);
}
}
return *n;
@@ -411,10 +413,9 @@ void elim_unconstrained::update_model_trail(generic_model_converter& mc, vector<
case generic_model_converter::instruction::HIDE:
break;
case generic_model_converter::instruction::ADD:
- // new_def = entry.m_def;
- // (*rp)(new_def);
- new_def = m.mk_const(entry.m_f);
- sub->insert(new_def, new_def, nullptr, nullptr);
+ new_def = entry.m_def;
+ (*rp)(new_def);
+ sub->insert(m.mk_const(entry.m_f), new_def, nullptr, nullptr);
break;
}
}
@@ -436,6 +437,7 @@ void elim_unconstrained::reduce() {
assert_normalized(old_fmls);
update_model_trail(*mc, old_fmls);
mc->reset();
+ m_disabled.reset();
}
}
@@ -443,3 +445,21 @@ void elim_unconstrained::updt_params(params_ref const& p) {
smt_params_helper sp(p);
m_config.m_enabled = sp.elim_unconstrained();
}
+
+void elim_unconstrained::disable(expr* e) {
+ if (m_disabled.is_marked(e))
+ return;
+
+ ptr_buffer todo;
+ todo.push_back(e);
+ while (!todo.empty()) {
+ e = todo.back();
+ todo.pop_back();
+ if (m_disabled.is_marked(e))
+ continue;
+ m_disabled.mark(e);
+ if (is_app(e))
+ for (auto arg : *to_app(e))
+ todo.push_back(arg);
+ }
+}
diff --git a/src/ast/simplifiers/elim_unconstrained.h b/src/ast/simplifiers/elim_unconstrained.h
index 4a248b44f..4a946c191 100644
--- a/src/ast/simplifiers/elim_unconstrained.h
+++ b/src/ast/simplifiers/elim_unconstrained.h
@@ -92,6 +92,7 @@ class elim_unconstrained : public dependent_expr_simplifier {
stats m_stats;
config m_config;
bool m_created_compound = false;
+ expr_mark m_disabled;
bool is_var_lt(int v1, int v2) const;
node& get_node(unsigned n) const { return *m_nodes[n]; }
@@ -108,6 +109,7 @@ class elim_unconstrained : public dependent_expr_simplifier {
expr* reconstruct_term(node& n);
void assert_normalized(vector& old_fmls);
void update_model_trail(generic_model_converter& mc, vector const& old_fmls);
+ void disable(expr *e);
public:
diff --git a/src/ast/simplifiers/fold_unfold.cpp b/src/ast/simplifiers/fold_unfold.cpp
new file mode 100644
index 000000000..346f96683
--- /dev/null
+++ b/src/ast/simplifiers/fold_unfold.cpp
@@ -0,0 +1,396 @@
+/*++
+Copyright (c) 2022 Microsoft Corporation
+
+Module Name:
+
+ fold_unfold.h
+
+Abstract:
+
+ fold-unfold simplifier
+
+Author:
+
+ Nikolaj Bjorner (nbjorner) 2025-11-5.
+
+- remove alias x = y
+- remove alias with const x = k
+- fold-unfold simplification x = f(y), y = g(z), f(g(z)) = u -> x |-> u
+
+- assign levels to E-nodes:
+ - dfs over roots.
+ - visit children, assign level
+ -
+- remove alias with linear x = f(y) -> x |-> f(y) if level y < level x
+--*/
+
+#include "ast/ast_pp.h"
+#include "ast/simplifiers/fold_unfold.h"
+#include "ast/rewriter/expr_replacer.h"
+#include "util/union_find.h"
+#include "params/smt_params_helper.hpp"
+
+namespace euf {
+
+ fold_unfold::fold_unfold(ast_manager& m, dependent_expr_state& fmls)
+ : dependent_expr_simplifier(m, fmls),
+ m_rewriter(m),
+ m_egraph(m) {
+ register_extract_eqs(m, m_extract_plugins);
+ m_rewriter.set_flat_and_or(false);
+ // flat sum/prod := false
+ }
+
+ void fold_unfold::reduce() {
+ if (!m_config.m_enabled)
+ return;
+
+ m_fmls.freeze_suffix();
+
+ for (extract_eq* ex : m_extract_plugins)
+ ex->pre_process(m_fmls);
+
+ reduce_alias(true);
+ reduce_linear();
+ reduce_alias(false);
+ }
+
+ void fold_unfold::reduce_alias(bool fuf) {
+ m_subst = nullptr;
+ dep_eq_vector eqs;
+ get_eqs(eqs);
+ extract_subst(fuf, eqs);
+ vector old_fmls;
+ apply_subst(old_fmls);
+ }
+
+ void fold_unfold::get_eqs(dep_eq_vector& eqs) {
+ for (extract_eq* ex : m_extract_plugins)
+ for (unsigned i : indices())
+ ex->get_eqs(m_fmls[i], eqs);
+ }
+
+ void fold_unfold::extract_subst(bool fuf, dep_eq_vector const& eqs) {
+ m_find.reset();
+ for (auto const& [orig, v, t, d] : eqs) {
+ auto a = mk_enode(v);
+ auto b = mk_enode(t);
+ // verbose_stream() << mk_bounded_pp(v, m) << " == " << mk_bounded_pp(t, m) << "\n";
+ proof_ref pr(m);
+ auto j = to_ptr(push_pr_dep(pr, d));
+ m_egraph.merge(a, b, j);
+ }
+
+ // choose uninterpreted or value representative
+ auto find_rep = [&](enode *a, ptr_buffer& vars) {
+ enode *rep = nullptr;
+ for (auto b : euf::enode_class(a)) {
+ expr *t = b->get_expr();
+ if (is_uninterp_const(t))
+ vars.push_back(b);
+ if (m.is_value(t))
+ rep = b;
+ }
+ if (!rep) {
+ for (auto v : vars)
+ if (!rep || v->get_id() < rep->get_id())
+ rep = v;
+ }
+ return rep;
+ };
+
+ for (auto a : m_egraph.nodes()) {
+ if (!a->is_root())
+ continue;
+ ptr_buffer vars;
+ enode *rep = find_rep(a, vars);
+ if (!rep)
+ continue;
+ for (auto w : vars) {
+ if (w != rep)
+ m_find.setx(w->get_id(), rep, nullptr);
+ }
+ }
+ if (fuf) {
+ // find new equalities by performing fold-unfold
+ vector> new_eqs;
+ for (auto n : m_egraph.nodes()) {
+ if (!n->is_root())
+ continue;
+ auto ne = n->get_expr();
+ unsigned depth = 3;
+ vector> es;
+ unfold(depth, n, nullptr, es);
+ // verbose_stream() << "unfolds " << es.size() << "\n";
+ for (auto [e, d] : es) {
+ expr_ref r(m);
+ proof_ref pr(m);
+ fold(e, r, pr);
+ if (ne == r)
+ continue;
+ new_eqs.push_back({n, r, pr, d});
+ }
+ }
+ for (auto const &[a, t, pr, d] : new_eqs) {
+ auto b = mk_enode(t);
+ auto j = to_ptr(push_pr_dep(pr, d));
+ m_egraph.merge(a, b, j);
+ }
+ }
+
+ for (auto a : m_egraph.nodes()) {
+ if (!a->is_root())
+ continue;
+ ptr_buffer vars;
+ enode *rep = find_rep(a, vars);
+ if (!rep)
+ continue;
+ for (auto v : vars) {
+ if (v == rep)
+ continue;
+ m_find.setx(v->get_id(), rep, nullptr);
+ // verbose_stream() << "insert " << mk_pp(v->get_expr(), m) << " " << mk_pp(rep->get_expr(), m) << "\n";
+ insert_subst(v->get_expr(), rep->get_expr(), explain_eq(v, rep));
+ m_stats.m_num_elim_vars++;
+ }
+ }
+ }
+
+ expr_dependency *fold_unfold::explain_eq(enode *a, enode *b) {
+ if (a == b)
+ return nullptr;
+ ptr_vector just;
+ m_egraph.begin_explain();
+ m_egraph.explain_eq(just, nullptr, a, b);
+ m_egraph.end_explain();
+ expr_dependency *d = nullptr;
+ for (size_t *j : just)
+ d = m.mk_join(d, m_pr_dep[from_ptr(j)].second);
+ return d;
+ }
+
+ unsigned fold_unfold::push_pr_dep(proof *pr, expr_dependency *d) {
+ unsigned sz = m_pr_dep.size();
+ SASSERT(!m.proofs_enabled() || pr);
+ m_pr_dep.push_back({proof_ref(pr, m), d});
+ m_trail.push(push_back_vector(m_pr_dep));
+ return sz;
+ }
+
+ enode *fold_unfold::mk_enode(expr *e) {
+ m_todo.push_back(e);
+ enode *n;
+ while (!m_todo.empty()) {
+ e = m_todo.back();
+ if (m_egraph.find(e)) {
+ m_todo.pop_back();
+ continue;
+ }
+ if (!is_app(e)) {
+ m_egraph.mk(e, m_generation, 0, nullptr);
+ m_todo.pop_back();
+ continue;
+ }
+ m_args.reset();
+ unsigned sz = m_todo.size();
+ for (expr *arg : *to_app(e)) {
+ n = m_egraph.find(arg);
+ if (n)
+ m_args.push_back(n);
+ else
+ m_todo.push_back(arg);
+ }
+ if (sz == m_todo.size()) {
+ n = m_egraph.mk(e, m_generation, m_args.size(), m_args.data());
+ if (m_egraph.get_plugin(e->get_sort()->get_family_id()))
+ m_egraph.add_th_var(n, m_th_var++, e->get_sort()->get_family_id());
+ if (!m.is_eq(e)) {
+ for (auto ch : m_args)
+ for (auto idv : euf::enode_th_vars(*ch))
+ m_egraph.register_shared(n, idv.get_id());
+ }
+ m_todo.pop_back();
+ }
+ }
+ return m_egraph.find(e);
+ }
+
+
+ void fold_unfold::fold(expr *e, expr_ref &result, proof_ref &pr) {
+ m_rewriter(e, result, pr);
+ }
+
+ void fold_unfold::unfold(unsigned n, enode *e, expr_dependency* d, vector>& es) {
+ if (n == 0) {
+ es.push_back({expr_ref(e->get_expr(), m), d});
+ return;
+ }
+ if (es.size() > 10)
+ return;
+ unsigned count = 0;
+ for (auto sib : euf::enode_class(e)) {
+ auto sib_e = sib->get_expr();
+ if (!is_app(sib_e))
+ continue;
+ if (is_uninterp_const(sib_e)) {
+ auto f = m_find.get(sib->get_id(), nullptr);
+ if (f && f != sib)
+ continue;
+ }
+ ++count;
+ expr_ref_vector args(m);
+ expr_dependency *d1 = m.mk_join(d, explain_eq(sib, e));
+ unfold_arg(n, 0, sib, args, d1, es);
+ if (count > 2)
+ break;
+ }
+ // verbose_stream() << "count " << count << "\n";
+ }
+
+ void fold_unfold::unfold_arg(unsigned n, unsigned i, enode* e, expr_ref_vector& args, expr_dependency* d,
+ vector>& es) {
+ if (i == e->num_args()) {
+ es.push_back({expr_ref(m.mk_app(e->get_decl(), args), m), d});
+ return;
+ }
+ vector> es_arg;
+ unfold(n - 1, e->get_arg(i), d, es_arg);
+ for (auto [arg, dep] : es_arg) {
+ args.push_back(arg);
+ unfold_arg(n, i + 1, e, args, dep, es);
+ args.pop_back();
+ if (es.size() > 10)
+ return;
+ }
+ }
+
+ void fold_unfold::insert_subst(expr * v, expr * t, expr_dependency* d) {
+ if (!m_subst)
+ m_subst = alloc(expr_substitution, m, true, false);
+ m_subst->insert(v, t, d);
+ }
+
+ void fold_unfold::apply_subst(vector &old_fmls) {
+ if (!m.inc())
+ return;
+ if (!m_subst)
+ return;
+
+ scoped_ptr rp = mk_default_expr_replacer(m, false);
+ rp->set_substitution(m_subst.get());
+
+ for (unsigned i : indices()) {
+ auto [f, p, d] = m_fmls[i]();
+ auto [new_f, new_dep] = rp->replace_with_dep(f);
+ proof_ref new_pr(m);
+ expr_ref tmp(m);
+ m_rewriter(new_f, tmp, new_pr);
+ if (tmp == f)
+ continue;
+ new_dep = m.mk_join(d, new_dep);
+ old_fmls.push_back(m_fmls[i]);
+ m_fmls.update(i, dependent_expr(m, tmp, mp(p, new_pr), new_dep));
+ }
+ m_fmls.model_trail().push(m_subst.detach(), old_fmls, false);
+ }
+
+ void fold_unfold::set_levels() {
+ m_node2level.reset();
+ m_level2node.reset();
+ m_level_count = 0;
+ for (auto n : m_egraph.nodes())
+ if (n->is_root())
+ set_level(n);
+ for (auto n : m_egraph.nodes())
+ if (n->is_root())
+ n->unmark1();
+ }
+
+ void fold_unfold::set_level(enode* n) {
+ SASSERT(n->is_root());
+
+ if (m_node2level.get(n->get_id(), UINT_MAX) != UINT_MAX)
+ return;
+
+ if (!n->is_marked1()) {
+ n->mark1();
+ for (auto b : enode_class(n)) {
+ for (auto arg : enode_args(b))
+ set_level(arg->get_root());
+ }
+ }
+ if (m_node2level.get(n->get_id(), UINT_MAX) != UINT_MAX)
+ return;
+ for (auto a : enode_class(n)) {
+ m_node2level.setx(a->get_id(), m_level_count, UINT_MAX);
+ m_level2node.setx(m_level_count, a, nullptr);
+ }
+ ++m_level_count;
+ }
+
+ void fold_unfold::reduce_linear() {
+ set_levels();
+ m_subst = alloc(expr_substitution, m, true, false);
+ scoped_ptr rp = mk_default_expr_replacer(m, false);
+ rp->set_substitution(m_subst.get());
+ for (auto n : m_level2node) {
+ SASSERT(n);
+ SASSERT(n->is_root());
+ // if a is uninterpreted and is not eliminated,
+ // n is equal to a linear term with lower level argument
+ // back-substitute the linear term using existing subst.
+ // update subst with a -> linear term
+ enode *var = nullptr;
+ enode *term = nullptr;
+ for (auto a : enode_class(n)) {
+ if (m_find.get(a->get_id(), nullptr) != nullptr) // already substituted
+ continue;
+ if (is_uninterp_const(a->get_expr()))
+ var = a;
+ else if (is_linear_term(a))
+ term = a;
+ }
+ if (var && term) {
+ m_find.setx(var->get_id(), term, nullptr); // record that var was replaced
+ auto dep = explain_eq(var, term);
+ auto [new_term, new_dep] = rp->replace_with_dep(term->get_expr());
+ expr_ref r(m);
+ proof_ref pr(m);
+ m_rewriter(new_term, r, pr);
+ m_subst->insert(var->get_expr(), r, m.mk_join(dep, new_dep));
+ }
+ }
+ vector old_fmls;
+ apply_subst(old_fmls);
+ }
+
+ bool fold_unfold::is_linear_term(enode *n) {
+ unsigned num_vars = 0;
+ unsigned level = m_node2level[n->get_root_id()];
+ for (auto arg : enode_args(n))
+ if (!m.is_value(arg->get_expr())) {
+ if (m_node2level[arg->get_root_id()] >= level)
+ return false;
+ ++num_vars;
+ }
+ return num_vars <= 1;
+ }
+
+ void fold_unfold::updt_params(params_ref const &p) {
+ m_config.m_enabled = true;
+ params_ref p1;
+ p1.set_bool("eliminate_mod", false);
+ for (auto ex : m_extract_plugins) {
+ ex->updt_params(p);
+ ex->updt_params(p1);
+ }
+ }
+
+ void fold_unfold::collect_param_descrs(param_descrs &r) {}
+
+ void fold_unfold::collect_statistics(statistics &st) const {
+ st.update("fold-unfold-steps", m_stats.m_num_steps);
+ st.update("fold-unfold-elim-vars", m_stats.m_num_elim_vars);
+ }
+
+}
diff --git a/src/ast/simplifiers/fold_unfold.h b/src/ast/simplifiers/fold_unfold.h
new file mode 100644
index 000000000..577801f2d
--- /dev/null
+++ b/src/ast/simplifiers/fold_unfold.h
@@ -0,0 +1,108 @@
+
+/*++
+Copyright (c) 2022 Microsoft Corporation
+
+Module Name:
+
+ fold_unfold.h
+
+Abstract:
+
+ fold-unfold simplifier
+
+Author:
+
+ Nikolaj Bjorner (nbjorner) 2025-11-5.
+
+--*/
+
+#pragma once
+
+#include "util/scoped_ptr_vector.h"
+#include "ast/expr_substitution.h"
+#include "ast/rewriter/th_rewriter.h"
+#include "ast/simplifiers/extract_eqs.h"
+#include "ast/euf/euf_egraph.h"
+
+namespace euf {
+
+ class fold_unfold : public dependent_expr_simplifier {
+ friend class solve_context_eqs;
+
+ struct stats {
+ unsigned m_num_steps = 0;
+ unsigned m_num_elim_vars = 0;
+ void reset() {
+ m_num_steps = 0;
+ m_num_elim_vars = 0;
+ }
+ };
+
+ struct config {
+ bool m_enabled = true;
+ };
+
+ stats m_stats;
+ config m_config;
+ th_rewriter m_rewriter;
+ egraph m_egraph;
+ scoped_ptr_vector m_extract_plugins;
+ unsigned_vector m_var2id; // app->get_id() |-> small numeral
+ scoped_ptr m_subst; // current substitution
+ vector> m_pr_dep;
+
+ void get_eqs(dep_eq_vector &eqs);
+ void extract_subst(bool fuf, dep_eq_vector const &eqs);
+ void insert_subst(expr *v, expr *t, expr_dependency* d);
+ void apply_subst(vector &old_fmls);
+ void reduce_alias(bool fuf);
+ void reduce_linear();
+
+ size_t *to_ptr(size_t i) const {
+ return reinterpret_cast(i);
+ }
+ unsigned from_ptr(size_t *s) const {
+ return (unsigned)reinterpret_cast(s);
+ }
+ unsigned push_pr_dep(proof *pr, expr_dependency *d);
+ expr_dependency *explain_eq(enode *a, enode *b);
+
+ ptr_vector m_todo;
+ enode_vector m_args, m_find;
+ unsigned_vector m_node2level;
+ enode_vector m_level2node;
+ unsigned m_level_count = 0;
+
+ void set_levels();
+ void set_level(enode *n);
+ bool is_linear_term(enode *n);
+
+ unsigned m_generation = 0;
+ unsigned m_th_var = 0;
+ enode *mk_enode(expr *e);
+
+ void fold(expr *e, expr_ref &result, proof_ref &pr);
+ void unfold(unsigned n, enode *e, expr_dependency* d, vector> &es);
+ void unfold_arg(unsigned n, unsigned i, enode *e, expr_ref_vector &args, expr_dependency *d,
+ vector> &es);
+
+ public:
+ fold_unfold(ast_manager &m, dependent_expr_state &fmls);
+
+ char const *name() const override {
+ return "fold-unfold";
+ }
+
+ void reduce() override;
+
+ void updt_params(params_ref const &p) override;
+
+ void collect_param_descrs(param_descrs &r) override;
+
+ void collect_statistics(statistics &st) const override;
+
+ void reset_statistics() override {
+ m_stats.reset();
+ }
+ };
+} // namespace euf
diff --git a/src/ast/simplifiers/solve_eqs.cpp b/src/ast/simplifiers/solve_eqs.cpp
index 9022f0c8d..0cdb5de69 100644
--- a/src/ast/simplifiers/solve_eqs.cpp
+++ b/src/ast/simplifiers/solve_eqs.cpp
@@ -121,7 +121,10 @@ namespace euf {
continue;
if (!m_config.m_enable_non_ground && has_quantifiers(t))
- continue;
+ continue;
+
+ if (!m_config.m_enable_non_linear && !is_linear(t))
+ continue;
bool is_safe = true;
unsigned todo_sz = todo.size();
@@ -241,10 +244,12 @@ namespace euf {
unsigned count = 0;
vector old_fmls;
dep_eq_vector eqs;
+ auto _reset_unsafe = on_scope_exit([&]() { m_unsafe_vars.reset(); });
do {
old_fmls.reset();
m_subst_ids.reset();
eqs.reset();
+ filter_unsafe_vars();
get_eqs(eqs);
extract_dep_graph(eqs);
extract_subst();
@@ -262,6 +267,7 @@ namespace euf {
old_fmls.reset();
m_subst_ids.reset();
eqs.reset();
+ filter_unsafe_vars();
solve_context_eqs context_solve(*this);
context_solve.collect_nested_equalities(eqs);
extract_dep_graph(eqs);
@@ -313,6 +319,15 @@ namespace euf {
return num <= m_config.m_max_occs;
}
+ bool solve_eqs::is_linear(expr* t) const {
+ unsigned num_values = 0;
+ if (!is_app(t))
+ return false;
+ for (auto arg : *to_app(t))
+ num_values += m.is_value(arg) ? 0 : 1;
+ return num_values <= 1;
+ }
+
void solve_eqs::save_subst(vector const& old_fmls) {
if (!m_subst->empty())
m_fmls.model_trail().push(m_subst.detach(), old_fmls, false);
@@ -322,7 +337,7 @@ namespace euf {
m_unsafe_vars.reset();
recfun::util rec(m);
for (func_decl* f : rec.get_rec_funs())
- for (expr* term : subterms::all(expr_ref(rec.get_def(f).get_rhs(), m), &m_todo, &m_visited))
+ for (expr* term : subterms::all(expr_ref(rec.get_def(f).get_rhs(), m)))
m_unsafe_vars.mark(term);
}
@@ -342,6 +357,7 @@ namespace euf {
smt_params_helper sp(p);
m_config.m_enabled = sp.solve_eqs();
m_config.m_enable_non_ground = sp.solve_eqs_non_ground();
+ m_config.m_enable_non_linear = !sp.solve_eqs_linear();
}
void solve_eqs::collect_param_descrs(param_descrs& r) {
diff --git a/src/ast/simplifiers/solve_eqs.h b/src/ast/simplifiers/solve_eqs.h
index 5f9a993aa..7a2f49111 100644
--- a/src/ast/simplifiers/solve_eqs.h
+++ b/src/ast/simplifiers/solve_eqs.h
@@ -43,6 +43,7 @@ namespace euf {
unsigned m_max_occs = UINT_MAX;
bool m_enabled = true;
bool m_enable_non_ground = true;
+ bool m_enable_non_linear = true;
};
stats m_stats;
@@ -74,6 +75,7 @@ namespace euf {
void collect_num_occs(expr * t, expr_fast_mark1 & visited);
void collect_num_occs();
bool check_occs(expr* t) const;
+ bool is_linear(expr *t) const;
public:
diff --git a/src/cmd_context/CMakeLists.txt b/src/cmd_context/CMakeLists.txt
index f3cdb3c03..0b75a8526 100644
--- a/src/cmd_context/CMakeLists.txt
+++ b/src/cmd_context/CMakeLists.txt
@@ -12,6 +12,7 @@ z3_add_component(cmd_context
simplifier_cmds.cpp
tactic_cmds.cpp
tactic_manager.cpp
+ tptp_frontend.cpp
COMPONENT_DEPENDENCIES
rewriter
solver
diff --git a/src/cmd_context/tptp_frontend.cpp b/src/cmd_context/tptp_frontend.cpp
new file mode 100644
index 000000000..c2b2c4fc3
--- /dev/null
+++ b/src/cmd_context/tptp_frontend.cpp
@@ -0,0 +1,2258 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "ast/arith_decl_plugin.h"
+#include "ast/array_decl_plugin.h"
+#include "ast/expr_abstract.h"
+#include "ast/ast_util.h"
+#include "ast/rewriter/expr_safe_replace.h"
+#include "cmd_context/cmd_context.h"
+#include "cmd_context/tptp_frontend.h"
+#include "solver/solver.h"
+#include "util/error_codes.h"
+#include "util/rational.h"
+#include "util/timeout.h"
+#include "util/z3_exception.h"
+
+bool g_display_statistics = false;
+bool g_display_model = false;
+
+static void on_timeout() {
+ std::cout << "% SZS status Timeout\n";
+ std::cout.flush();
+ _Exit(0);
+}
+
+namespace {
+
+enum class token_kind {
+ eof_tok,
+ id,
+ str,
+ lparen,
+ rparen,
+ lbrack,
+ rbrack,
+ comma,
+ dot,
+ colon,
+ and_tok,
+ or_tok,
+ not_tok,
+ forall_tok,
+ exists_tok,
+ type_forall_tok, // !>
+ type_exists_tok, // ?*
+ equal_tok,
+ neq_tok,
+ iff_tok,
+ implies_tok,
+ implied_tok,
+ xor_tok,
+ nor_tok,
+ nand_tok,
+ gt_tok,
+ lt_tok,
+ star_tok,
+ slash_tok,
+ minus_tok,
+ at_tok,
+ lambda_tok
+};
+
+struct parse_error : public std::exception {
+ std::string m_msg;
+ parse_error(std::string const& msg): m_msg(msg) {}
+ char const* what() const noexcept override { return m_msg.c_str(); }
+};
+
+class scoped_regular_stream {
+ cmd_context& m_ctx;
+ std::string m_prev;
+public:
+ scoped_regular_stream(cmd_context& ctx, std::ostream& out): m_ctx(ctx), m_prev(ctx.get_regular_stream_name()) { m_ctx.set_regular_stream(out); }
+ ~scoped_regular_stream() { m_ctx.set_regular_stream(m_prev.c_str()); }
+};
+
+struct token {
+ token_kind kind = token_kind::eof_tok;
+ std::string text;
+ unsigned line = 1;
+ unsigned col = 1;
+};
+
+class lexer {
+ std::string const& m_input;
+ size_t m_pos = 0;
+ unsigned m_line = 1;
+ unsigned m_col = 1;
+
+ bool eof() const { return m_pos >= m_input.size(); }
+ char peek(unsigned k = 0) const { return m_pos + k < m_input.size() ? m_input[m_pos + k] : '\0'; }
+ char get() {
+ char c = peek();
+ if (!eof()) {
+ ++m_pos;
+ if (c == '\n') {
+ ++m_line;
+ m_col = 1;
+ }
+ else {
+ ++m_col;
+ }
+ }
+ return c;
+ }
+
+ static bool is_symbol_start(char c) {
+ return std::isalnum(static_cast(c)) || c == '$' || c == '_';
+ }
+
+ static bool is_id_char(char c) {
+ return std::isalnum(static_cast(c)) || c == '$' || c == '_' || c == '\'' || c == '-';
+ }
+
+ void skip_ws_comments() {
+ while (!eof()) {
+ if (std::isspace(static_cast(peek()))) {
+ get();
+ continue;
+ }
+ if (peek() == '%') {
+ while (!eof() && get() != '\n') {}
+ continue;
+ }
+ if (peek() == '/' && peek(1) == '*') {
+ get();
+ get();
+ while (!eof()) {
+ if (peek() == '*' && peek(1) == '/') {
+ get();
+ get();
+ break;
+ }
+ get();
+ }
+ continue;
+ }
+ break;
+ }
+ }
+
+public:
+ lexer(std::string const& input): m_input(input) {}
+
+ token next() {
+ skip_ws_comments();
+ token t;
+ t.line = m_line;
+ t.col = m_col;
+ if (eof()) {
+ t.kind = token_kind::eof_tok;
+ return t;
+ }
+
+ if (peek() == '\'' || peek() == '"') {
+ char q = get();
+ t.kind = token_kind::str;
+ while (!eof()) {
+ char c = get();
+ if (c == '\\' && !eof()) {
+ t.text.push_back(c);
+ t.text.push_back(get());
+ continue;
+ }
+ if (c == q) return t;
+ t.text.push_back(c);
+ }
+ throw parse_error("unterminated string literal");
+ }
+
+ if (peek() == '<' && peek(1) == '=' && peek(2) == '>') {
+ get(); get(); get();
+ t.kind = token_kind::iff_tok;
+ t.text = "<=>";
+ return t;
+ }
+ if (peek() == '<' && peek(1) == '~' && peek(2) == '>') {
+ get(); get(); get();
+ t.kind = token_kind::xor_tok;
+ t.text = "<~>";
+ return t;
+ }
+ if (peek() == '=' && peek(1) == '>') {
+ get(); get();
+ t.kind = token_kind::implies_tok;
+ t.text = "=>";
+ return t;
+ }
+ if (peek() == '<' && peek(1) == '=') {
+ get(); get();
+ t.kind = token_kind::implied_tok;
+ t.text = "<=";
+ return t;
+ }
+ if (peek() == '~' && peek(1) == '|') {
+ get(); get();
+ t.kind = token_kind::nor_tok;
+ t.text = "~|";
+ return t;
+ }
+ if (peek() == '~' && peek(1) == '&') {
+ get(); get();
+ t.kind = token_kind::nand_tok;
+ t.text = "~&";
+ return t;
+ }
+ if (peek() == '!' && peek(1) == '=') {
+ get(); get();
+ t.kind = token_kind::neq_tok;
+ t.text = "!=";
+ return t;
+ }
+
+ char c = get();
+ switch (c) {
+ case '(': t.kind = token_kind::lparen; return t;
+ case ')': t.kind = token_kind::rparen; return t;
+ case '[': t.kind = token_kind::lbrack; return t;
+ case ']': t.kind = token_kind::rbrack; return t;
+ case ',': t.kind = token_kind::comma; return t;
+ case '.': t.kind = token_kind::dot; return t;
+ case ':': t.kind = token_kind::colon; return t;
+ case '&': t.kind = token_kind::and_tok; return t;
+ case '|': t.kind = token_kind::or_tok; return t;
+ case '~': t.kind = token_kind::not_tok; return t;
+ case '!':
+ if (peek() == '>') { get(); t.kind = token_kind::type_forall_tok; return t; }
+ if (peek() == '!') { get(); t.kind = token_kind::id; t.text = "!!"; return t; }
+ t.kind = token_kind::forall_tok; return t;
+ case '?':
+ if (peek() == '*') { get(); t.kind = token_kind::type_exists_tok; return t; }
+ if (peek() == '?') { get(); t.kind = token_kind::id; t.text = "??"; return t; }
+ t.kind = token_kind::exists_tok; return t;
+ case '=': t.kind = token_kind::equal_tok; return t;
+ case '>': t.kind = token_kind::gt_tok; return t;
+ case '<': t.kind = token_kind::lt_tok; return t;
+ case '*': t.kind = token_kind::star_tok; return t;
+ case '/': t.kind = token_kind::slash_tok; return t;
+ case '-': t.kind = token_kind::minus_tok; return t;
+ case '@':
+ if (peek() == '+') { get(); t.kind = token_kind::id; t.text = "@+"; return t; }
+ if (peek() == '-') { get(); t.kind = token_kind::id; t.text = "@-"; return t; }
+ t.kind = token_kind::at_tok; return t;
+ case '^': t.kind = token_kind::lambda_tok; return t;
+ case '{':
+ // Modal operators: {$box}, {$dia}, etc. — lex as identifier including braces
+ t.kind = token_kind::id;
+ t.text.push_back(c);
+ while (!eof() && peek() != '}')
+ t.text.push_back(get());
+ if (!eof()) t.text.push_back(get()); // consume '}'
+ return t;
+ default:
+ break;
+ }
+
+ if (is_symbol_start(c)) {
+ t.kind = token_kind::id;
+ t.text.push_back(c);
+ while (!eof() && is_id_char(peek()))
+ t.text.push_back(get());
+ return t;
+ }
+
+ std::ostringstream out;
+ out << "unexpected character '" << c << "' at " << t.line << ":" << t.col;
+ throw parse_error(out.str());
+ }
+};
+
+struct parsed_type {
+ std::vector domain;
+ sort* range = nullptr;
+ parsed_type(sort* s): range(s) {}
+ parsed_type(std::vector const& d, sort* r): domain(d), range(r) {}
+};
+
+class tptp_parser {
+ cmd_context& m_cmd;
+ ast_manager& m;
+ arith_util m_arith;
+ array_util m_array;
+ sort* m_univ;
+ bool m_has_conjecture = false;
+ bool m_last_name_quoted = false;
+ std::unordered_map m_sorts;
+ sort_ref_vector m_pinned_sorts; // prevents cached sorts from being freed
+ std::unordered_map m_decls;
+ func_decl_ref_vector m_pinned_decls; // prevents cached func_decls from being freed
+ expr_ref_vector m_pinned_exprs; // prevents bound variable apps from being freed
+ std::unordered_map, sort*>> m_typed_decls;
+ std::vector> m_bound;
+ bool m_in_at_arg = false; // true when parsing inside @ argument (lambda body stops consuming @)
+ struct implicit_var_scope {
+ std::unordered_map vars;
+ ptr_vector order;
+ };
+ implicit_var_scope* m_implicit_scope = nullptr;
+ std::unordered_set m_seen_files;
+
+ // Table-driven operator dispatch
+ using op_builder = std::function;
+ struct op_entry {
+ bool is_infix;
+ unsigned precedence; // only meaningful for infix; higher = tighter binding
+ bool right_assoc;
+ op_builder builder;
+ };
+ std::unordered_map m_ops;
+
+ // Infix precedence levels:
+ static constexpr unsigned PREC_IFF = 1; // <=> <~>
+ static constexpr unsigned PREC_IMPLIES = 2; // => <=
+ static constexpr unsigned PREC_OR = 3; // | ~|
+ static constexpr unsigned PREC_AND = 4; // & ~&
+ static constexpr unsigned PREC_EQ = 5; // = !=
+
+ std::string m_input;
+ std::unique_ptr m_lex;
+ token m_curr;
+
+ // Helper: check arity for arithmetic operators
+ void check_arith_arity(expr_ref_vector const& args, unsigned expected, char const* name) {
+ if (args.size() != expected) {
+ std::ostringstream out;
+ out << "'" << name << "' expects arity " << expected;
+ throw parse_error(out.str());
+ }
+ }
+
+ // Helper: coerce two arithmetic args to same sort (promote int to real if needed)
+ std::pair coerce_arith2(expr_ref_vector const& args) {
+ expr_ref a(args[0], m), b(args[1], m);
+ // Coerce U-sorted args to Int (from HO encoding / $let bindings)
+ if (!m_arith.is_int_real(a) && !m_arith.is_int_real(b)) {
+ a = coerce_arg(a, m_arith.mk_int());
+ b = coerce_arg(b, m_arith.mk_int());
+ } else if (!m_arith.is_int_real(a)) {
+ a = coerce_arg(a, b->get_sort());
+ } else if (!m_arith.is_int_real(b)) {
+ b = coerce_arg(b, a->get_sort());
+ }
+ if (m_arith.is_real(a) || m_arith.is_real(b)) {
+ if (m_arith.is_int(a)) a = expr_ref(m_arith.mk_to_real(a), m);
+ if (m_arith.is_int(b)) b = expr_ref(m_arith.mk_to_real(b), m);
+ }
+ return { a, b };
+ }
+
+ // Helper: quotient dispatch (integer division for int/int, real division otherwise)
+ expr_ref mk_quotient(expr_ref_vector const& args) {
+ expr_ref a(args[0], m), b(args[1], m);
+ if (m_arith.is_int(a) && m_arith.is_int(b))
+ return expr_ref(m_arith.mk_idiv(a, b), m);
+ if (m_arith.is_int(a)) a = expr_ref(m_arith.mk_to_real(a), m);
+ if (m_arith.is_int(b)) b = expr_ref(m_arith.mk_to_real(b), m);
+ return expr_ref(m_arith.mk_div(a, b), m);
+ }
+
+ // Map infix token to operator name (returns nullptr if not an infix op token)
+ char const* token_to_op_name() const {
+ switch (m_curr.kind) {
+ case token_kind::iff_tok: return "<=>";
+ case token_kind::xor_tok: return "<~>";
+ case token_kind::implies_tok: return "=>";
+ case token_kind::implied_tok: return "<=";
+ case token_kind::or_tok: return "|";
+ case token_kind::nor_tok: return "~|";
+ case token_kind::and_tok: return "&";
+ case token_kind::nand_tok: return "~&";
+ case token_kind::equal_tok: return "=";
+ case token_kind::neq_tok: return "!=";
+ default: return nullptr;
+ }
+ }
+ static std::string to_lower(std::string s) {
+ for (char& c : s) c = static_cast(std::tolower(static_cast(c)));
+ return s;
+ }
+
+ static bool is_var_name(std::string const& s) {
+ if (s.empty()) return false;
+ unsigned char c = static_cast(s[0]);
+ return std::isupper(c) || s[0] == '_';
+ }
+
+ std::string loc() const {
+ std::ostringstream out;
+ out << m_curr.line << ":" << m_curr.col;
+ return out.str();
+ }
+
+ void next() { m_curr = m_lex->next(); }
+
+ bool is(token_kind k) const { return m_curr.kind == k; }
+
+ bool accept(token_kind k) {
+ if (is(k)) {
+ next();
+ return true;
+ }
+ return false;
+ }
+
+ void expect(token_kind k, char const* msg) {
+ if (!accept(k)) {
+ std::ostringstream out;
+ out << "expected " << msg << " at " << loc();
+ throw parse_error(out.str());
+ }
+ }
+
+ // Grammar: ::= |
+ // ::= |
+ // Used universally for parsing identifiers, keywords, and quoted names.
+ std::string parse_name() {
+ if (is(token_kind::id) || is(token_kind::str)) {
+ m_last_name_quoted = is(token_kind::str);
+ std::string r = m_curr.text;
+ next();
+ return r;
+ }
+ std::ostringstream out;
+ out << "expected identifier at " << loc();
+ throw parse_error(out.str());
+ }
+
+ sort* get_sort(std::string const& n) {
+ if (n == "$i") return m_univ;
+ if (n == "$o") return m.mk_bool_sort();
+ if (n == "$int") return m_arith.mk_int();
+ if (n == "$rat" || n == "$real") return m_arith.mk_real();
+ auto it = m_sorts.find(n);
+ if (it != m_sorts.end()) return it->second;
+ sort* s = m.mk_uninterpreted_sort(symbol(n));
+ m_sorts.emplace(n, s);
+ m_pinned_sorts.push_back(s);
+ return s;
+ }
+
+ // For higher-order types like ($i > $o), create an uninterpreted sort
+ // Function type A > B is represented as Array(A, B).
+ // Multi-argument A * B > C is represented as Array(A, Array(B, C)) (curried).
+ sort* get_ho_sort(std::vector const& domain, sort* range) {
+ sort* s = range;
+ for (int i = (int)domain.size() - 1; i >= 0; --i)
+ s = m_array.mk_array_sort(domain[i], s);
+ return s;
+ }
+
+ static bool is_ttype(sort* s) {
+ return s->get_name() == symbol("$tType");
+ }
+
+ static bool is_nonempty_digit_string(std::string const& s) {
+ if (s.empty()) return false;
+ for (char c : s) {
+ if (!std::isdigit(static_cast(c)))
+ return false;
+ }
+ return true;
+ }
+
+ // Grammar: ::= | |
+ // ::= |
+ // ::= /
+ // ::= | ...
+ // Parses integer, rational (N/D), and real (N.D or N.DeE) numeric literals.
+ expr_ref parse_numeral_from_name(std::string const& n) {
+ SASSERT(is_nonempty_digit_string(n));
+ rational num(n.c_str());
+ if (accept(token_kind::dot)) {
+ std::string frac = parse_name();
+ if (!is_nonempty_digit_string(frac))
+ throw parse_error("fractional part of decimal literal must be a sequence of digits");
+ rational den(1);
+ for (unsigned i = 0; i < frac.size(); ++i) {
+ den *= rational(10);
+ }
+ rational frac_num(frac.c_str());
+ return expr_ref(m_arith.mk_numeral(num + frac_num / den, false), m);
+ }
+ if (accept(token_kind::slash_tok)) {
+ std::string d = parse_name();
+ if (!is_nonempty_digit_string(d))
+ throw parse_error("denominator of rational literal must be a sequence of digits");
+ rational den(d.c_str());
+ if (den.is_zero())
+ throw parse_error("denominator of rational literal cannot be zero");
+ return expr_ref(m_arith.mk_numeral(num / den, false), m);
+ }
+ return expr_ref(m_arith.mk_numeral(num, true), m);
+ }
+
+ static std::string mk_decl_key(std::string const& name, unsigned arity, char tag) {
+ return std::to_string(name.size()) + ":" + name + "\x1f" + std::to_string(arity) + "\x1f" + tag;
+ }
+
+ static std::string mk_typed_key(std::string const& name, unsigned arity) {
+ return mk_decl_key(name, arity, 't');
+ }
+
+ func_decl* mk_decl(std::string const& name, unsigned arity, bool pred) {
+ auto itt = m_typed_decls.find(mk_typed_key(name, arity));
+ if (itt != m_typed_decls.end()) {
+ std::string typed_decl_key = mk_decl_key(name, arity, 'd');
+ auto itd = m_decls.find(typed_decl_key);
+ if (itd != m_decls.end()) return itd->second;
+ auto const& sig = itt->second;
+ func_decl* f = m.mk_func_decl(symbol(name), sig.first.size(), sig.first.data(), sig.second);
+ m_pinned_decls.push_back(f);
+ m_decls.emplace(typed_decl_key, f);
+ return f;
+ }
+
+ std::string key = mk_decl_key(name, arity, pred ? 'p' : 'f');
+ auto itd = m_decls.find(key);
+ if (itd != m_decls.end()) return itd->second;
+
+ std::vector dom(arity, m_univ);
+ func_decl* f = m.mk_func_decl(symbol(name), arity, dom.data(), pred ? m.mk_bool_sort() : m_univ);
+ m_pinned_decls.push_back(f);
+ m_decls.emplace(key, f);
+ return f;
+ }
+
+ // Create a modal operator declaration: Bool → Bool
+ func_decl* mk_modal_op(std::string const& name) {
+ std::string key = mk_decl_key(name, 1, 'm');
+ auto it = m_decls.find(key);
+ if (it != m_decls.end()) return it->second;
+ sort* bool_sort = m.mk_bool_sort();
+ func_decl* f = m.mk_func_decl(symbol(name), 1, &bool_sort, bool_sort);
+ m_pinned_decls.push_back(f);
+ m_decls.emplace(key, f);
+ return f;
+ }
+
+ // When a symbol is used with 0 args but has a typed decl with arity > 0,
+ // create a 0-arity constant with the function type sort (for THF function-as-value).
+ func_decl* mk_decl_or_ho_const(std::string const& name, unsigned arity, bool pred) {
+ if (arity == 0) {
+ // Check if there's a typed decl at any arity > 0 for this name
+ for (unsigned try_arity = 1; try_arity <= 30; ++try_arity) {
+ auto itt = m_typed_decls.find(mk_typed_key(name, try_arity));
+ if (itt != m_typed_decls.end()) {
+ auto const& sig = itt->second;
+ sort* ho = get_ho_sort(sig.first, sig.second);
+ std::string dkey = mk_decl_key(name, 0, 'h');
+ auto itd = m_decls.find(dkey);
+ if (itd != m_decls.end()) return itd->second;
+ func_decl* f = m.mk_func_decl(symbol(name), 0, static_cast(nullptr), ho);
+ m_pinned_decls.push_back(f);
+ m_decls.emplace(dkey, f);
+ return f;
+ }
+ }
+ }
+ return mk_decl(name, arity, pred);
+ }
+
+ // Coerce an expression to a target sort using boxing/unboxing functions
+ expr_ref coerce_arg(expr_ref const& e, sort* target) {
+ sort* actual = e->get_sort();
+ if (actual == target) return e;
+ // Create a boxing function from actual sort to target sort
+ std::string box_name = std::string("$box_") + actual->get_name().str() + "_to_" + target->get_name().str();
+ std::string key = mk_decl_key(box_name, 1, 'f');
+ auto it = m_decls.find(key);
+ func_decl* f;
+ if (it != m_decls.end()) {
+ f = it->second;
+ } else {
+ f = m.mk_func_decl(symbol(box_name), 1, &actual, target);
+ m_pinned_decls.push_back(f);
+ m_decls.emplace(key, f);
+ }
+ return expr_ref(m.mk_app(f, e.get()), m);
+ }
+
+ // Coerce expression to Bool sort — if U-sorted, wrap with an uninterpreted predicate
+ expr_ref ensure_bool(expr* e) {
+ if (m.is_bool(e->get_sort())) return expr_ref(e, m);
+ return coerce_arg(expr_ref(e, m), m.mk_bool_sort());
+ }
+
+ // Coerce arguments of a function application to match declared sorts
+ void coerce_args(func_decl* f, expr_ref_vector& args) {
+ for (unsigned i = 0; i < args.size() && i < f->get_arity(); ++i) {
+ sort* expected = f->get_domain(i);
+ sort* actual = args.get(i)->get_sort();
+ if (expected != actual) {
+ args[i] = coerce_arg(expr_ref(args.get(i), m), expected);
+ }
+ }
+ }
+
+ // Coerce result to expected sort if needed
+ expr_ref coerce_result(expr_ref const& e, sort* expected) {
+ if (!expected || e->get_sort() == expected) return e;
+ return coerce_arg(e, expected);
+ }
+
+ bool find_bound(std::string const& n, expr_ref& e) const {
+ for (auto it = m_bound.rbegin(); it != m_bound.rend(); ++it) {
+ auto jt = it->find(n);
+ if (jt != it->end()) {
+ e = jt->second;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool is_bound_var(app* a) const {
+ std::string name = a->get_decl()->get_name().str();
+ for (auto it = m_bound.rbegin(); it != m_bound.rend(); ++it) {
+ auto jt = it->find(name);
+ if (jt != it->end() && jt->second == a)
+ return true;
+ }
+ return false;
+ }
+
+ bool should_create_implicit_var(std::string const& n) const {
+ return is_var_name(n) && m_implicit_scope;
+ }
+
+ app* get_or_create_implicit_var(std::string const& n) {
+ if (!m_implicit_scope)
+ throw parse_error("unexpected parser state: missing implicit variable scope");
+ auto it = m_implicit_scope->vars.find(n);
+ if (it != m_implicit_scope->vars.end()) return it->second;
+ app* c = m.mk_const(symbol(n), m_univ);
+ m_pinned_exprs.push_back(c);
+ m_implicit_scope->vars.emplace(n, c);
+ m_implicit_scope->order.push_back(c);
+ return c;
+ }
+
+ class scoped_implicit_vars {
+ tptp_parser& m_p;
+ implicit_var_scope* m_prev_scope;
+ public:
+ scoped_implicit_vars(tptp_parser& p, implicit_var_scope& scope):
+ m_p(p),
+ m_prev_scope(p.m_implicit_scope) {
+ m_p.m_implicit_scope = &scope;
+ }
+ scoped_implicit_vars(scoped_implicit_vars const&) = delete;
+ scoped_implicit_vars& operator=(scoped_implicit_vars const&) = delete;
+ scoped_implicit_vars(scoped_implicit_vars&&) = delete;
+ scoped_implicit_vars& operator=(scoped_implicit_vars&&) = delete;
+ ~scoped_implicit_vars() {
+ m_p.m_implicit_scope = m_prev_scope;
+ }
+ };
+
+ expr_ref mk_quantifier(bool is_forall, ptr_vector const& bound, expr_ref const& body) {
+ SASSERT(body);
+ if (bound.empty()) return body;
+ expr_ref b = ensure_bool(body);
+ return is_forall ? ::mk_forall(m, bound.size(), bound.data(), b.get()) : ::mk_exists(m, bound.size(), bound.data(), b.get());
+ }
+
+ // $is_rat(x) ≡ exists a:Int, b:Int. b != 0 && x = a/b
+ expr_ref mk_is_rat(expr_ref const& x) {
+ sort* int_sort = m_arith.mk_int();
+ app* a = m.mk_fresh_const("a", int_sort);
+ app* b = m.mk_fresh_const("b", int_sort);
+ expr_ref ar(m_arith.mk_to_real(a), m);
+ expr_ref br(m_arith.mk_to_real(b), m);
+ expr_ref xr(x);
+ if (m_arith.is_int(x))
+ xr = expr_ref(m_arith.mk_to_real(x), m);
+ expr_ref b_ne_zero(m.mk_not(m.mk_eq(b, m_arith.mk_int(0))), m);
+ expr_ref x_eq_div(m.mk_eq(xr, m_arith.mk_div(ar, br)), m);
+ expr_ref body(m.mk_and(b_ne_zero, x_eq_div), m);
+ ptr_vector bound;
+ bound.push_back(a);
+ bound.push_back(b);
+ return expr_ref(::mk_exists(m, bound.size(), bound.data(), body.get()), m);
+ }
+
+ // Grammar: ::= | | |
+ // | ()
+ // ::= | | |
+ // ()
+ // ::= $oType | $o | $iType | $i | $tType | $real | $rat | $int
+ parsed_type parse_type_atom() {
+ if (accept(token_kind::lparen)) {
+ std::vector prod = parse_type_product_raw();
+ if (accept(token_kind::gt_tok)) {
+ // Full function type inside parens: (A * B > C) or (A > B > C)
+ parsed_type rhs = parse_type_expr();
+ std::vector full_domain = prod;
+ if (!rhs.domain.empty()) {
+ // Nested higher-order: (A > B > C) → flatten
+ full_domain.insert(full_domain.end(), rhs.domain.begin(), rhs.domain.end());
+ }
+ expect(token_kind::rparen, "')'");
+ // Return with domain/range preserved for proper flattening
+ return parsed_type(full_domain, rhs.range);
+ }
+ expect(token_kind::rparen, "')'");
+ if (prod.size() == 1)
+ return parsed_type(prod[0]);
+ // Parenthesized product: (A * B) — used as domain in outer context
+ return parsed_type(prod, nullptr);
+ }
+ std::string n = parse_name();
+ // Handle parameterized type constructors: fun(A, B), product_prod(A, B), etc.
+ if (accept(token_kind::lparen)) {
+ // Consume type arguments — for monomorphization, we ignore them
+ // and return the base sort (or m_univ if the constructor result is $tType)
+ if (!accept(token_kind::rparen)) {
+ do { parse_type_expr(); } while (accept(token_kind::comma));
+ expect(token_kind::rparen, "')'");
+ }
+ // Return m_univ as the monomorphized result of any type constructor application
+ return parsed_type(m_univ);
+ }
+ sort* s = get_sort(n);
+ // Handle type-level application with @: list @ nat, pair @ A @ B, etc.
+ // Monomorphize by consuming all @ arguments and returning m_univ.
+ if (is(token_kind::at_tok)) {
+ while (accept(token_kind::at_tok)) {
+ parse_type_atom(); // consume the argument type
+ }
+ return parsed_type(m_univ);
+ }
+ return parsed_type(s);
+ }
+
+ // Grammar: ::= *
+ // | *
+ // Product types form the domain in mapping types: (A * B) > C
+ std::vector parse_type_product_raw() {
+ parsed_type first = parse_type_atom();
+ if (!first.domain.empty() && first.range == nullptr) {
+ // Already a parenthesized product from nested parens
+ std::vector args = first.domain;
+ while (accept(token_kind::star_tok)) {
+ parsed_type t = parse_type_atom();
+ if (!t.domain.empty()) {
+ args.insert(args.end(), t.domain.begin(), t.domain.end());
+ } else {
+ args.push_back(t.range);
+ }
+ }
+ return args;
+ }
+ if (!first.domain.empty()) {
+ // Function type as first element of product — use ho_sort
+ sort* ho = get_ho_sort(first.domain, first.range);
+ std::vector args;
+ args.push_back(ho);
+ while (accept(token_kind::star_tok)) {
+ parsed_type t = parse_type_atom();
+ if (!t.domain.empty() && t.range != nullptr) {
+ args.push_back(get_ho_sort(t.domain, t.range));
+ } else if (!t.domain.empty()) {
+ args.insert(args.end(), t.domain.begin(), t.domain.end());
+ } else {
+ args.push_back(t.range);
+ }
+ }
+ return args;
+ }
+ std::vector