From 8538ea8deb51f003613ea63830e45c6020f7c7fd Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 29 Jan 2026 23:12:40 +0000
Subject: [PATCH 1/7] Initial plan


From 0ec5f9499f239071d3707aa0b556b0f310f21dcf Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 29 Jan 2026 23:17:23 +0000
Subject: [PATCH 2/7] Initial plan for removing campaign command and related
 code

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .github/workflows/artifacts-summary.lock.yml              | 4 ++--
 .github/workflows/changeset.lock.yml                      | 4 ++--
 .github/workflows/cli-consistency-checker.lock.yml        | 4 ++--
 .github/workflows/cli-version-checker.lock.yml            | 4 ++--
 .github/workflows/copilot-pr-nlp-analysis.lock.yml        | 4 ++--
 .github/workflows/copilot-pr-prompt-analysis.lock.yml     | 4 ++--
 .github/workflows/daily-multi-device-docs-tester.lock.yml | 4 ++--
 .github/workflows/daily-news.lock.yml                     | 4 ++--
 .github/workflows/daily-repo-chronicle.lock.yml           | 4 ++--
 .github/workflows/deep-report.lock.yml                    | 4 ++--
 .github/workflows/docs-noob-tester.lock.yml               | 4 ++--
 .github/workflows/firewall-escape.lock.yml                | 4 ++--
 .github/workflows/firewall.lock.yml                       | 2 +-
 .github/workflows/jsweep.lock.yml                         | 2 +-
 .github/workflows/mcp-inspector.lock.yml                  | 4 ++--
 .github/workflows/release.lock.yml                        | 4 ++--
 .github/workflows/research.lock.yml                       | 4 ++--
 .github/workflows/slide-deck-maintainer.lock.yml          | 4 ++--
 .github/workflows/smoke-copilot.lock.yml                  | 4 ++--
 .github/workflows/smoke-test-tools.lock.yml               | 4 ++--
 .github/workflows/weekly-issue-summary.lock.yml           | 4 ++--
 .github/workflows/workflow-normalizer.lock.yml            | 4 ++--
 22 files changed, 42 insertions(+), 42 deletions(-)

diff --git a/.github/workflows/artifacts-summary.lock.yml b/.github/workflows/artifacts-summary.lock.yml
index dcf551ce3b4..b6d4f05cad4 100644
--- a/.github/workflows/artifacts-summary.lock.yml
+++ b/.github/workflows/artifacts-summary.lock.yml
@@ -732,7 +732,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -800,7 +800,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/changeset.lock.yml b/.github/workflows/changeset.lock.yml
index aea1188310a..3c20e052a32 100644
--- a/.github/workflows/changeset.lock.yml
+++ b/.github/workflows/changeset.lock.yml
@@ -935,7 +935,7 @@ jobs:
           mkdir -p "$HOME/.cache"
           INSTRUCTION="$(cat "$GH_AW_PROMPT")"
           mkdir -p "$CODEX_HOME/logs"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\n' ':')$PATH" && codex -c model=gpt-5.1-codex-mini exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION" \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -984,7 +984,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml
index 086c96e32e8..ddca5f9797c 100644
--- a/.github/workflows/cli-consistency-checker.lock.yml
+++ b/.github/workflows/cli-consistency-checker.lock.yml
@@ -798,7 +798,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -866,7 +866,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml
index e152e11354a..cdd0f660f3b 100644
--- a/.github/workflows/cli-version-checker.lock.yml
+++ b/.github/workflows/cli-version-checker.lock.yml
@@ -1239,7 +1239,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --tty --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --tty --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.githubusercontent.com,*.jsr.io,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- /bin/bash -c 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && claude --print --disable-slash-commands --no-chrome --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash,BashOutput,Edit,Edit(/tmp/gh-aw/cache-memory/*),ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,MultiEdit(/tmp/gh-aw/cache-memory/*),NotebookEdit,NotebookRead,Read,Read(/tmp/gh-aw/cache-memory/*),Task,TodoWrite,WebFetch,Write,Write(/tmp/gh-aw/cache-memory/*),mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}'
         env:
           ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1293,7 +1293,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
index 85cc1dc6647..5fd26b1dbfa 100644
--- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml
@@ -1527,7 +1527,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -1598,7 +1598,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
index 2b56728247e..d5d9438ed38 100644
--- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml
+++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml
@@ -1070,7 +1070,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -1138,7 +1138,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml
index ec73f9ba225..a113cc2e136 100644
--- a/.github/workflows/daily-multi-device-docs-tester.lock.yml
+++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml
@@ -1036,7 +1036,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --tty --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --tty --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.githubusercontent.com,*.jsr.io,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- /bin/bash -c 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && claude --print --disable-slash-commands --no-chrome --max-turns 30 --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools '\''Bash(cat),Bash(cd*),Bash(curl*),Bash(date),Bash(echo),Bash(grep),Bash(head),Bash(kill*),Bash(ls),Bash(ls*),Bash(lsof*),Bash(npm install*),Bash(npm run build*),Bash(npm run preview*),Bash(npx playwright*),Bash(pwd),Bash(pwd*),Bash(sort),Bash(tail),Bash(uniq),Bash(wc),Bash(yq),BashOutput,Edit,ExitPlanMode,Glob,Grep,KillBash,LS,MultiEdit,NotebookEdit,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__issue_read,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__playwright__browser_click,mcp__playwright__browser_close,mcp__playwright__browser_console_messages,mcp__playwright__browser_drag,mcp__playwright__browser_evaluate,mcp__playwright__browser_file_upload,mcp__playwright__browser_fill_form,mcp__playwright__browser_handle_dialog,mcp__playwright__browser_hover,mcp__playwright__browser_install,mcp__playwright__browser_navigate,mcp__playwright__browser_navigate_back,mcp__playwright__browser_network_requests,mcp__playwright__browser_press_key,mcp__playwright__browser_resize,mcp__playwright__browser_select_option,mcp__playwright__browser_snapshot,mcp__playwright__browser_tabs,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_type,mcp__playwright__browser_wait_for'\'' --debug-file /tmp/gh-aw/agent-stdio.log --verbose --permission-mode bypassPermissions --output-format json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_CLAUDE:+ --model "$GH_AW_MODEL_AGENT_CLAUDE"}'
         env:
           ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
@@ -1094,7 +1094,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,anthropic.com,api.anthropic.com,api.github.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,ghcr.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,pypi.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,sentry.io,skimdb.npmjs.com,statsig.anthropic.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml
index b24a7f1c653..759dd4d9994 100644
--- a/.github/workflows/daily-news.lock.yml
+++ b/.github/workflows/daily-news.lock.yml
@@ -1604,7 +1604,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,mcp.tavily.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,mcp.tavily.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -1677,7 +1677,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml
index 45801c061fa..59e98388b12 100644
--- a/.github/workflows/daily-repo-chronicle.lock.yml
+++ b/.github/workflows/daily-repo-chronicle.lock.yml
@@ -1385,7 +1385,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -1456,7 +1456,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml
index 40e9b7c228a..14af7a5d153 100644
--- a/.github/workflows/deep-report.lock.yml
+++ b/.github/workflows/deep-report.lock.yml
@@ -1345,7 +1345,7 @@ jobs:
           mkdir -p "$HOME/.cache"
           INSTRUCTION="$(cat "$GH_AW_PROMPT")"
           mkdir -p "$CODEX_HOME/logs"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.pythonhosted.org,anaconda.org,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,*.pythonhosted.org,anaconda.org,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\n' ':')$PATH" && codex ${GH_AW_MODEL_AGENT_CODEX:+-c model="$GH_AW_MODEL_AGENT_CODEX" }exec --dangerously-bypass-approvals-and-sandbox --skip-git-repo-check "$INSTRUCTION" \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -1398,7 +1398,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,*.pythonhosted.org,anaconda.org,api.npms.io,api.openai.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,openai.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/docs-noob-tester.lock.yml b/.github/workflows/docs-noob-tester.lock.yml
index 071f9fe4a1f..ceeacf5b2da 100644
--- a/.github/workflows/docs-noob-tester.lock.yml
+++ b/.github/workflows/docs-noob-tester.lock.yml
@@ -832,7 +832,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -903,7 +903,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/firewall-escape.lock.yml b/.github/workflows/firewall-escape.lock.yml
index c7d75c7858e..c926413543f 100644
--- a/.github/workflows/firewall-escape.lock.yml
+++ b/.github/workflows/firewall-escape.lock.yml
@@ -946,7 +946,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -1014,7 +1014,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/firewall.lock.yml b/.github/workflows/firewall.lock.yml
index ddae15a9c97..4c85c8a0f37 100644
--- a/.github/workflows/firewall.lock.yml
+++ b/.github/workflows/firewall.lock.yml
@@ -363,7 +363,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_DETECTION_COPILOT:+ --model "$GH_AW_MODEL_DETECTION_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml
index 68bd3991563..e7d5cd61478 100644
--- a/.github/workflows/jsweep.lock.yml
+++ b/.github/workflows/jsweep.lock.yml
@@ -874,7 +874,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml
index 775d0f80069..9d877966a41 100644
--- a/.github/workflows/mcp-inspector.lock.yml
+++ b/.github/workflows/mcp-inspector.lock.yml
@@ -1220,7 +1220,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.docker.com,*.docker.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,auth.docker.io,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,dl.k8s.io,fonts.googleapis.com,fonts.gstatic.com,gcr.io,get.pnpm.io,ghcr.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,learn.microsoft.com,localhost,mcp.datadoghq.com,mcp.deepwiki.com,mcp.tavily.com,mcr.microsoft.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pkgs.k8s.io,ppa.launchpad.net,production.cloudflare.docker.com,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.docker.com,*.docker.io,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,auth.docker.io,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,dl.k8s.io,fonts.googleapis.com,fonts.gstatic.com,gcr.io,get.pnpm.io,ghcr.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,learn.microsoft.com,localhost,mcp.datadoghq.com,mcp.deepwiki.com,mcp.tavily.com,mcr.microsoft.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pkgs.k8s.io,ppa.launchpad.net,production.cloudflare.docker.com,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 arxiv --allow-tool '\''arxiv(get_paper_details)'\'' --allow-tool '\''arxiv(get_paper_pdf)'\'' --allow-tool '\''arxiv(search_arxiv)'\'' --allow-tool ast-grep --allow-tool '\''ast-grep(*)'\'' --allow-tool brave-search --allow-tool '\''brave-search(*)'\'' --allow-tool context7 --allow-tool '\''context7(query-docs)'\'' --allow-tool '\''context7(resolve-library-id)'\'' --allow-tool datadog --allow-tool '\''datadog(get_datadog_metric)'\'' --allow-tool '\''datadog(search_datadog_dashboards)'\'' --allow-tool '\''datadog(search_datadog_metrics)'\'' --allow-tool '\''datadog(search_datadog_slos)'\'' --allow-tool deepwiki --allow-tool '\''deepwiki(ask_question)'\'' --allow-tool '\''deepwiki(read_wiki_contents)'\'' --allow-tool '\''deepwiki(read_wiki_structure)'\'' --allow-tool fabric-rti --allow-tool '\''fabric-rti(get_eventstream)'\'' --allow-tool '\''fabric-rti(get_eventstream_definition)'\'' --allow-tool '\''fabric-rti(kusto_get_entities_schema)'\'' --allow-tool '\''fabric-rti(kusto_get_function_schema)'\'' --allow-tool '\''fabric-rti(kusto_get_shots)'\'' --allow-tool '\''fabric-rti(kusto_get_table_schema)'\'' --allow-tool '\''fabric-rti(kusto_known_services)'\'' --allow-tool '\''fabric-rti(kusto_list_databases)'\'' --allow-tool '\''fabric-rti(kusto_list_tables)'\'' --allow-tool '\''fabric-rti(kusto_query)'\'' --allow-tool '\''fabric-rti(kusto_sample_function_data)'\'' --allow-tool '\''fabric-rti(kusto_sample_table_data)'\'' --allow-tool '\''fabric-rti(list_eventstreams)'\'' --allow-tool gh-aw --allow-tool github --allow-tool markitdown --allow-tool '\''markitdown(*)'\'' --allow-tool memory --allow-tool '\''memory(delete_memory)'\'' --allow-tool '\''memory(list_memories)'\'' --allow-tool '\''memory(retrieve_memory)'\'' --allow-tool '\''memory(store_memory)'\'' --allow-tool microsoftdocs --allow-tool '\''microsoftdocs(*)'\'' --allow-tool notion --allow-tool '\''notion(get_database)'\'' --allow-tool '\''notion(get_page)'\'' --allow-tool '\''notion(query_database)'\'' --allow-tool '\''notion(search_pages)'\'' --allow-tool safeoutputs --allow-tool sentry --allow-tool '\''sentry(analyze_issue_with_seer)'\'' --allow-tool '\''sentry(find_dsns)'\'' --allow-tool '\''sentry(find_organizations)'\'' --allow-tool '\''sentry(find_projects)'\'' --allow-tool '\''sentry(find_releases)'\'' --allow-tool '\''sentry(find_teams)'\'' --allow-tool '\''sentry(get_doc)'\'' --allow-tool '\''sentry(get_event_attachment)'\'' --allow-tool '\''sentry(get_issue_details)'\'' --allow-tool '\''sentry(get_trace_details)'\'' --allow-tool '\''sentry(search_docs requires SENTRY_OPENAI_API_KEY)'\'' --allow-tool '\''sentry(search_events)'\'' --allow-tool '\''sentry(search_issues)'\'' --allow-tool '\''sentry(whoami)'\'' --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool tavily --allow-tool '\''tavily(*)'\'' --allow-tool write --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -1304,7 +1304,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "*.docker.com,*.docker.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,auth.docker.io,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,dl.k8s.io,fonts.googleapis.com,fonts.gstatic.com,gcr.io,get.pnpm.io,ghcr.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,mcr.microsoft.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pkgs.k8s.io,ppa.launchpad.net,production.cloudflare.docker.com,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.docker.com,*.docker.io,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,auth.docker.io,azure.archive.ubuntu.com,bun.sh,cdn.jsdelivr.net,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,deb.nodesource.com,deno.land,dl.k8s.io,fonts.googleapis.com,fonts.gstatic.com,gcr.io,get.pnpm.io,ghcr.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,mcr.microsoft.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pkgs.k8s.io,ppa.launchpad.net,production.cloudflare.docker.com,quay.io,raw.githubusercontent.com,registry.bower.io,registry.hub.docker.com,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/release.lock.yml b/.github/workflows/release.lock.yml
index 7825906629f..354230ea54e 100644
--- a/.github/workflows/release.lock.yml
+++ b/.github/workflows/release.lock.yml
@@ -753,7 +753,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,githubnext.github.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,githubnext.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -821,7 +821,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,githubnext.github.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,githubnext.github.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/research.lock.yml b/.github/workflows/research.lock.yml
index 99646c78f80..cd5eb240c4a 100644
--- a/.github/workflows/research.lock.yml
+++ b/.github/workflows/research.lock.yml
@@ -709,7 +709,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,mcp.tavily.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,mcp.tavily.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -779,7 +779,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/slide-deck-maintainer.lock.yml b/.github/workflows/slide-deck-maintainer.lock.yml
index 3ae67752dd1..9f5477f865d 100644
--- a/.github/workflows/slide-deck-maintainer.lock.yml
+++ b/.github/workflows/slide-deck-maintainer.lock.yml
@@ -889,7 +889,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,bun.sh,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,skimdb.npmjs.com,www.npmjs.com,www.npmjs.org,yarnpkg.com --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,bun.sh,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,jsr.io,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,skimdb.npmjs.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 github --allow-tool safeoutputs --allow-tool '\''shell(cat)'\'' --allow-tool '\''shell(cat*)'\'' --allow-tool '\''shell(cd*)'\'' --allow-tool '\''shell(curl*)'\'' --allow-tool '\''shell(date)'\'' --allow-tool '\''shell(echo)'\'' --allow-tool '\''shell(find*)'\'' --allow-tool '\''shell(git add:*)'\'' --allow-tool '\''shell(git branch:*)'\'' --allow-tool '\''shell(git checkout:*)'\'' --allow-tool '\''shell(git commit:*)'\'' --allow-tool '\''shell(git merge:*)'\'' --allow-tool '\''shell(git rm:*)'\'' --allow-tool '\''shell(git status)'\'' --allow-tool '\''shell(git switch:*)'\'' --allow-tool '\''shell(grep)'\'' --allow-tool '\''shell(grep*)'\'' --allow-tool '\''shell(head)'\'' --allow-tool '\''shell(head*)'\'' --allow-tool '\''shell(kill*)'\'' --allow-tool '\''shell(ls)'\'' --allow-tool '\''shell(ls*)'\'' --allow-tool '\''shell(lsof*)'\'' --allow-tool '\''shell(npm ci*)'\'' --allow-tool '\''shell(npm install*)'\'' --allow-tool '\''shell(npm run*)'\'' --allow-tool '\''shell(npx @marp-team/marp-cli*)'\'' --allow-tool '\''shell(npx http-server*)'\'' --allow-tool '\''shell(pwd)'\'' --allow-tool '\''shell(pwd*)'\'' --allow-tool '\''shell(sort)'\'' --allow-tool '\''shell(tail)'\'' --allow-tool '\''shell(tail*)'\'' --allow-tool '\''shell(uniq)'\'' --allow-tool '\''shell(wc)'\'' --allow-tool '\''shell(yq)'\'' --allow-tool write --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -957,7 +957,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.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.npms.io,bun.sh,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,skimdb.npmjs.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,bun.sh,deb.nodesource.com,deno.land,get.pnpm.io,github.com,host.docker.internal,jsr.io,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,skimdb.npmjs.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml
index 31cd27ce084..367720910f5 100644
--- a/.github/workflows/smoke-copilot.lock.yml
+++ b/.github/workflows/smoke-copilot.lock.yml
@@ -1444,7 +1444,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.githubusercontent.com,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.githubusercontent.com,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pkg.go.dev,playwright.download.prss.microsoft.com,ppa.launchpad.net,proxy.golang.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -1513,7 +1513,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.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.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,cdn.playwright.dev,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,deb.nodesource.com,deno.land,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,objects.githubusercontent.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,playwright.download.prss.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/smoke-test-tools.lock.yml b/.github/workflows/smoke-test-tools.lock.yml
index 00731db057b..9766ed37876 100644
--- a/.github/workflows/smoke-test-tools.lock.yml
+++ b/.github/workflows/smoke-test-tools.lock.yml
@@ -668,7 +668,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.githubusercontent.com,*.pythonhosted.org,adoptium.net,anaconda.org,api.adoptium.net,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.nuget.org,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,bun.sh,ci.dot.net,codeload.github.com,conda.anaconda.org,conda.binstar.org,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,deb.nodesource.com,deno.land,dist.nuget.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,download.eclipse.org,download.oracle.com,files.pythonhosted.org,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,gradle.org,host.docker.internal,jcenter.bintray.com,jdk.java.net,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,maven.apache.org,maven.oracle.com,maven.pkg.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,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,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,pkgs.dev.azure.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,proxy.golang.org,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.grails.org,repo.maven.apache.org,repo.spring.io,repo.yarnpkg.com,repo1.maven.org,s.symcb.com,s.symcd.com,security.ubuntu.com,services.gradle.org,skimdb.npmjs.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.java.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.githubusercontent.com,*.jsr.io,*.pythonhosted.org,adoptium.net,anaconda.org,api.adoptium.net,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.foojay.io,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.nuget.org,api.snapcraft.io,archive.apache.org,archive.ubuntu.com,azure.archive.ubuntu.com,azuresearch-usnc.nuget.org,azuresearch-ussc.nuget.org,binstar.org,bootstrap.pypa.io,builds.dotnet.microsoft.com,bun.sh,cdn.azul.com,ci.dot.net,codeload.github.com,conda.anaconda.org,conda.binstar.org,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,dc.services.visualstudio.com,deb.nodesource.com,deno.land,dist.nuget.org,dlcdn.apache.org,dot.net,dotnet.microsoft.com,dotnetcli.blob.core.windows.net,download.eclipse.org,download.java.net,download.oracle.com,files.pythonhosted.org,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,go.dev,golang.org,goproxy.io,gradle.org,host.docker.internal,jcenter.bintray.com,jdk.java.net,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,maven.apache.org,maven.oracle.com,maven.pkg.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,nuget.org,nuget.pkg.github.com,nugetregistryv2prod.blob.core.windows.net,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,oneocsp.microsoft.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,pip.pypa.io,pkg.go.dev,pkgs.dev.azure.com,plugins-artifacts.gradle.org,plugins.gradle.org,ppa.launchpad.net,proxy.golang.org,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.grails.org,repo.maven.apache.org,repo.spring.io,repo.yarnpkg.com,repo1.maven.org,s.symcb.com,s.symcd.com,security.ubuntu.com,services.gradle.org,skimdb.npmjs.com,sum.golang.org,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.java.com,www.microsoft.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -736,7 +736,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.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.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.githubusercontent.com,*.jsr.io,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,bun.sh,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,deb.nodesource.com,deno.land,get.pnpm.io,github-cloud.githubusercontent.com,github-cloud.s3.amazonaws.com,github.com,github.githubassets.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,lfs.github.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,ppa.launchpad.net,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml
index 8c2b7de3028..54e04f2e389 100644
--- a/.github/workflows/weekly-issue-summary.lock.yml
+++ b/.github/workflows/weekly-issue-summary.lock.yml
@@ -1300,7 +1300,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --add-dir /tmp/gh-aw/cache-memory/ --allow-all-paths --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -1371,7 +1371,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:
diff --git a/.github/workflows/workflow-normalizer.lock.yml b/.github/workflows/workflow-normalizer.lock.yml
index dbd383cd37c..c874c130bb4 100644
--- a/.github/workflows/workflow-normalizer.lock.yml
+++ b/.github/workflows/workflow-normalizer.lock.yml
@@ -908,7 +908,7 @@ jobs:
           set -o pipefail
           GH_AW_TOOL_BINS=""; command -v go >/dev/null 2>&1 && GH_AW_TOOL_BINS="$(go env GOROOT)/bin:$GH_AW_TOOL_BINS"; [ -n "$JAVA_HOME" ] && GH_AW_TOOL_BINS="$JAVA_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CARGO_HOME" ] && GH_AW_TOOL_BINS="$CARGO_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$GEM_HOME" ] && GH_AW_TOOL_BINS="$GEM_HOME/bin:$GH_AW_TOOL_BINS"; [ -n "$CONDA" ] && GH_AW_TOOL_BINS="$CONDA/bin:$GH_AW_TOOL_BINS"; [ -n "$PIPX_BIN_DIR" ] && GH_AW_TOOL_BINS="$PIPX_BIN_DIR:$GH_AW_TOOL_BINS"; [ -n "$SWIFT_PATH" ] && GH_AW_TOOL_BINS="$SWIFT_PATH:$GH_AW_TOOL_BINS"; [ -n "$DOTNET_ROOT" ] && GH_AW_TOOL_BINS="$DOTNET_ROOT:$GH_AW_TOOL_BINS"; export GH_AW_TOOL_BINS
           mkdir -p "$HOME/.cache"
-          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
+          sudo -E awf --env-all --env "ANDROID_HOME=${ANDROID_HOME}" --env "ANDROID_NDK=${ANDROID_NDK}" --env "ANDROID_NDK_HOME=${ANDROID_NDK_HOME}" --env "ANDROID_NDK_LATEST_HOME=${ANDROID_NDK_LATEST_HOME}" --env "ANDROID_NDK_ROOT=${ANDROID_NDK_ROOT}" --env "ANDROID_SDK_ROOT=${ANDROID_SDK_ROOT}" --env "AZURE_EXTENSION_DIR=${AZURE_EXTENSION_DIR}" --env "CARGO_HOME=${CARGO_HOME}" --env "CHROMEWEBDRIVER=${CHROMEWEBDRIVER}" --env "CONDA=${CONDA}" --env "DOTNET_ROOT=${DOTNET_ROOT}" --env "EDGEWEBDRIVER=${EDGEWEBDRIVER}" --env "GECKOWEBDRIVER=${GECKOWEBDRIVER}" --env "GEM_HOME=${GEM_HOME}" --env "GEM_PATH=${GEM_PATH}" --env "GOPATH=${GOPATH}" --env "GOROOT=${GOROOT}" --env "HOMEBREW_CELLAR=${HOMEBREW_CELLAR}" --env "HOMEBREW_PREFIX=${HOMEBREW_PREFIX}" --env "HOMEBREW_REPOSITORY=${HOMEBREW_REPOSITORY}" --env "JAVA_HOME=${JAVA_HOME}" --env "JAVA_HOME_11_X64=${JAVA_HOME_11_X64}" --env "JAVA_HOME_17_X64=${JAVA_HOME_17_X64}" --env "JAVA_HOME_21_X64=${JAVA_HOME_21_X64}" --env "JAVA_HOME_25_X64=${JAVA_HOME_25_X64}" --env "JAVA_HOME_8_X64=${JAVA_HOME_8_X64}" --env "NVM_DIR=${NVM_DIR}" --env "PIPX_BIN_DIR=${PIPX_BIN_DIR}" --env "PIPX_HOME=${PIPX_HOME}" --env "RUSTUP_HOME=${RUSTUP_HOME}" --env "SELENIUM_JAR_PATH=${SELENIUM_JAR_PATH}" --env "SWIFT_PATH=${SWIFT_PATH}" --env "VCPKG_INSTALLATION_ROOT=${VCPKG_INSTALLATION_ROOT}" --env "GH_AW_TOOL_BINS=$GH_AW_TOOL_BINS" --container-workdir "${GITHUB_WORKSPACE}" --mount /tmp:/tmp:rw --mount "${HOME}/.cache:${HOME}/.cache:rw" --mount "${GITHUB_WORKSPACE}:${GITHUB_WORKSPACE}:rw" --mount /usr/bin/cat:/usr/bin/cat:ro --mount /usr/bin/curl:/usr/bin/curl:ro --mount /usr/bin/date:/usr/bin/date:ro --mount /usr/bin/find:/usr/bin/find:ro --mount /usr/bin/gh:/usr/bin/gh:ro --mount /usr/bin/grep:/usr/bin/grep:ro --mount /usr/bin/jq:/usr/bin/jq:ro --mount /usr/bin/yq:/usr/bin/yq:ro --mount /usr/bin/cp:/usr/bin/cp:ro --mount /usr/bin/cut:/usr/bin/cut:ro --mount /usr/bin/diff:/usr/bin/diff:ro --mount /usr/bin/head:/usr/bin/head:ro --mount /usr/bin/ls:/usr/bin/ls:ro --mount /usr/bin/mkdir:/usr/bin/mkdir:ro --mount /usr/bin/rm:/usr/bin/rm:ro --mount /usr/bin/sed:/usr/bin/sed:ro --mount /usr/bin/sort:/usr/bin/sort:ro --mount /usr/bin/tail:/usr/bin/tail:ro --mount /usr/bin/wc:/usr/bin/wc:ro --mount /usr/bin/which:/usr/bin/which:ro --mount /usr/local/bin/copilot:/usr/local/bin/copilot:ro --mount /home/runner/.copilot:/home/runner/.copilot:rw --mount /opt/hostedtoolcache:/opt/hostedtoolcache:ro --mount /opt/gh-aw:/opt/gh-aw:ro --allow-domains '*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,localhost,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com' --log-level info --proxy-logs-dir /tmp/gh-aw/sandbox/firewall/logs --enable-host-access --image-tag 0.11.2 --agent-image act \
             -- 'source /opt/gh-aw/actions/sanitize_path.sh "$GH_AW_TOOL_BINS$(find /opt/hostedtoolcache -maxdepth 4 -type d -name bin 2>/dev/null | tr '\''\n'\'' '\'':'\'')$PATH" && /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 --share /tmp/gh-aw/sandbox/agent/logs/conversation.md --prompt "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)"${GH_AW_MODEL_AGENT_COPILOT:+ --model "$GH_AW_MODEL_AGENT_COPILOT"}' \
             2>&1 | tee /tmp/gh-aw/agent-stdio.log
         env:
@@ -976,7 +976,7 @@ jobs:
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
         env:
           GH_AW_SAFE_OUTPUTS: ${{ env.GH_AW_SAFE_OUTPUTS }}
-          GH_AW_ALLOWED_DOMAINS: "*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
+          GH_AW_ALLOWED_DOMAINS: "*.jsr.io,*.pythonhosted.org,anaconda.org,api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.npms.io,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,binstar.org,bootstrap.pypa.io,bun.sh,conda.anaconda.org,conda.binstar.org,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,deb.nodesource.com,deno.land,files.pythonhosted.org,get.pnpm.io,github.com,host.docker.internal,json-schema.org,json.schemastore.org,jsr.io,keyserver.ubuntu.com,nodejs.org,npm.pkg.github.com,npmjs.com,npmjs.org,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,pip.pypa.io,ppa.launchpad.net,pypi.org,pypi.python.org,raw.githubusercontent.com,registry.bower.io,registry.npmjs.com,registry.npmjs.org,registry.yarnpkg.com,repo.anaconda.com,repo.continuum.io,repo.yarnpkg.com,s.symcb.com,s.symcd.com,security.ubuntu.com,skimdb.npmjs.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.npmjs.com,www.npmjs.org,yarnpkg.com"
           GITHUB_SERVER_URL: ${{ github.server_url }}
           GITHUB_API_URL: ${{ github.api_url }}
         with:

From 1787379a2b59a815b6b85b44bff74a17bb2f4bf7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 29 Jan 2026 23:25:43 +0000
Subject: [PATCH 3/7] Remove campaign command and related code

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 cmd/gh-aw/main.go                             |   4 -
 pkg/campaign/bootstrap_test.go                | 458 ----------
 pkg/campaign/campaign_test.go                 | 222 -----
 pkg/campaign/command.go                       | 435 ----------
 pkg/campaign/create_test.go                   | 237 ------
 pkg/campaign/filter_test.go                   | 141 ---
 pkg/campaign/injection.go                     | 264 ------
 pkg/campaign/injection_test.go                | 462 ----------
 pkg/campaign/interactive.go                   | 511 -----------
 pkg/campaign/interactive_test.go              |  61 --
 pkg/campaign/loader.go                        | 282 ------
 pkg/campaign/loader_test.go                   | 242 ------
 pkg/campaign/metrics_snapshot_test.go         | 224 -----
 pkg/campaign/orchestrator.go                  | 376 --------
 pkg/campaign/orchestrator_test.go             | 452 ----------
 pkg/campaign/project.go                       | 800 ------------------
 .../project_owner_normalization_test.go       |  48 --
 pkg/campaign/project_repo_parsing_test.go     |  72 --
 pkg/campaign/project_status_options_test.go   |  91 --
 pkg/campaign/project_test.go                  | 163 ----
 .../project_update_contract_validator.go      | 144 ----
 pkg/campaign/project_views_test.go            |  54 --
 pkg/campaign/prompt_sections.go               |  19 -
 .../schemas/campaign_spec_schema.json         | 386 ---------
 .../schemas/project_update_schema.json        |  90 --
 pkg/campaign/scope.go                         | 100 ---
 pkg/campaign/spec.go                          | 333 --------
 pkg/campaign/status.go                        | 226 -----
 pkg/campaign/template.go                      | 200 -----
 pkg/campaign/template_test.go                 | 174 ----
 pkg/campaign/test_helpers_test.go             |  64 --
 pkg/campaign/validation.go                    | 416 ---------
 pkg/campaign/validation_test.go               | 566 -------------
 pkg/campaign/workflow_discovery.go            | 170 ----
 pkg/campaign/workflow_discovery_test.go       | 199 -----
 pkg/cli/commands.go                           |  15 -
 pkg/cli/compile_campaign.go                   | 167 ----
 pkg/cli/compile_campaign_orchestrator_test.go | 268 ------
 pkg/cli/compile_campaign_validation_test.go   |  90 --
 pkg/cli/compile_helpers.go                    |  30 +
 pkg/cli/compile_orchestration.go              |  71 +-
 pkg/cli/compile_orchestrator.go               | 217 -----
 pkg/cli/compile_post_processing.go            |  20 +-
 pkg/cli/compile_workflow_processor.go         | 112 +--
 pkg/cli/copilot-agents.go                     |  60 --
 pkg/cli/init.go                               |  26 -
 pkg/workflow/compiler.go                      |   9 -
 pkg/workflow/tools.go                         |  66 +-
 48 files changed, 43 insertions(+), 9794 deletions(-)
 delete mode 100644 pkg/campaign/bootstrap_test.go
 delete mode 100644 pkg/campaign/campaign_test.go
 delete mode 100644 pkg/campaign/command.go
 delete mode 100644 pkg/campaign/create_test.go
 delete mode 100644 pkg/campaign/filter_test.go
 delete mode 100644 pkg/campaign/injection.go
 delete mode 100644 pkg/campaign/injection_test.go
 delete mode 100644 pkg/campaign/interactive.go
 delete mode 100644 pkg/campaign/interactive_test.go
 delete mode 100644 pkg/campaign/loader.go
 delete mode 100644 pkg/campaign/loader_test.go
 delete mode 100644 pkg/campaign/metrics_snapshot_test.go
 delete mode 100644 pkg/campaign/orchestrator.go
 delete mode 100644 pkg/campaign/orchestrator_test.go
 delete mode 100644 pkg/campaign/project.go
 delete mode 100644 pkg/campaign/project_owner_normalization_test.go
 delete mode 100644 pkg/campaign/project_repo_parsing_test.go
 delete mode 100644 pkg/campaign/project_status_options_test.go
 delete mode 100644 pkg/campaign/project_test.go
 delete mode 100644 pkg/campaign/project_update_contract_validator.go
 delete mode 100644 pkg/campaign/project_views_test.go
 delete mode 100644 pkg/campaign/prompt_sections.go
 delete mode 100644 pkg/campaign/schemas/campaign_spec_schema.json
 delete mode 100644 pkg/campaign/schemas/project_update_schema.json
 delete mode 100644 pkg/campaign/scope.go
 delete mode 100644 pkg/campaign/spec.go
 delete mode 100644 pkg/campaign/status.go
 delete mode 100644 pkg/campaign/template.go
 delete mode 100644 pkg/campaign/template_test.go
 delete mode 100644 pkg/campaign/test_helpers_test.go
 delete mode 100644 pkg/campaign/validation.go
 delete mode 100644 pkg/campaign/validation_test.go
 delete mode 100644 pkg/campaign/workflow_discovery.go
 delete mode 100644 pkg/campaign/workflow_discovery_test.go
 delete mode 100644 pkg/cli/compile_campaign.go
 delete mode 100644 pkg/cli/compile_campaign_orchestrator_test.go
 delete mode 100644 pkg/cli/compile_campaign_validation_test.go

diff --git a/cmd/gh-aw/main.go b/cmd/gh-aw/main.go
index 434755dcad6..fd2bead3994 100644
--- a/cmd/gh-aw/main.go
+++ b/cmd/gh-aw/main.go
@@ -5,7 +5,6 @@ import (
 	"os"
 	"strings"
 
-	"github.com/githubnext/gh-aw/pkg/campaign"
 	"github.com/githubnext/gh-aw/pkg/cli"
 	"github.com/githubnext/gh-aw/pkg/console"
 	"github.com/githubnext/gh-aw/pkg/constants"
@@ -550,7 +549,6 @@ Use "` + string(constants.CLIExtensionPrefix) + ` help all" to show help for all
 	auditCmd := cli.NewAuditCommand()
 	mcpServerCmd := cli.NewMCPServerCommand()
 	prCmd := cli.NewPRCommand()
-	campaignCmd := campaign.NewCommand()
 	secretsCmd := cli.NewSecretsCommand()
 	fixCmd := cli.NewFixCommand()
 	upgradeCmd := cli.NewUpgradeCommand()
@@ -582,7 +580,6 @@ Use "` + string(constants.CLIExtensionPrefix) + ` help all" to show help for all
 	// Analysis Commands
 	logsCmd.GroupID = "analysis"
 	auditCmd.GroupID = "analysis"
-	campaignCmd.GroupID = "analysis"
 
 	// Utilities
 	mcpServerCmd.GroupID = "utilities"
@@ -611,7 +608,6 @@ Use "` + string(constants.CLIExtensionPrefix) + ` help all" to show help for all
 	rootCmd.AddCommand(mcpServerCmd)
 	rootCmd.AddCommand(prCmd)
 	rootCmd.AddCommand(versionCmd)
-	rootCmd.AddCommand(campaignCmd)
 	rootCmd.AddCommand(secretsCmd)
 	rootCmd.AddCommand(fixCmd)
 	rootCmd.AddCommand(completionCmd)
diff --git a/pkg/campaign/bootstrap_test.go b/pkg/campaign/bootstrap_test.go
deleted file mode 100644
index 4eecbc6eee6..00000000000
--- a/pkg/campaign/bootstrap_test.go
+++ /dev/null
@@ -1,458 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"encoding/json"
-	"testing"
-
-	"go.yaml.in/yaml/v3"
-)
-
-// TestBootstrapConfig_SeederWorker tests parsing bootstrap config with seeder-worker mode
-func TestBootstrapConfig_SeederWorker(t *testing.T) {
-	yamlContent := `---
-id: test-bootstrap-seeder
-name: Test Bootstrap Seeder
-project-url: https://github.com/orgs/test/projects/1
-bootstrap:
-  mode: seeder-worker
-  seeder-worker:
-    workflow-id: security-scanner
-    payload:
-      scan-type: full
-      max-findings: 100
-    max-items: 50
----
-
-# Test Campaign`
-
-	var spec CampaignSpec
-	err := yaml.Unmarshal([]byte(yamlContent), &spec)
-	if err != nil {
-		t.Fatalf("Failed to parse bootstrap seeder config: %v", err)
-	}
-
-	if spec.Bootstrap == nil {
-		t.Fatal("Bootstrap config should not be nil")
-	}
-	if spec.Bootstrap.Mode != "seeder-worker" {
-		t.Errorf("Expected mode 'seeder-worker', got '%s'", spec.Bootstrap.Mode)
-	}
-	if spec.Bootstrap.SeederWorker == nil {
-		t.Fatal("SeederWorker config should not be nil")
-	}
-	if spec.Bootstrap.SeederWorker.WorkflowID != "security-scanner" {
-		t.Errorf("Expected workflow-id 'security-scanner', got '%s'", spec.Bootstrap.SeederWorker.WorkflowID)
-	}
-	if spec.Bootstrap.SeederWorker.MaxItems != 50 {
-		t.Errorf("Expected max-items 50, got %d", spec.Bootstrap.SeederWorker.MaxItems)
-	}
-	if len(spec.Bootstrap.SeederWorker.Payload) == 0 {
-		t.Error("Payload should not be empty")
-	}
-}
-
-// TestBootstrapConfig_ProjectTodos tests parsing bootstrap config with project-todos mode
-func TestBootstrapConfig_ProjectTodos(t *testing.T) {
-	yamlContent := `---
-id: test-bootstrap-todos
-name: Test Bootstrap Todos
-project-url: https://github.com/orgs/test/projects/1
-bootstrap:
-  mode: project-todos
-  project-todos:
-    status-field: Status
-    todo-value: Backlog
-    max-items: 10
-    require-fields:
-      - Priority
-      - Assignee
----
-
-# Test Campaign`
-
-	var spec CampaignSpec
-	err := yaml.Unmarshal([]byte(yamlContent), &spec)
-	if err != nil {
-		t.Fatalf("Failed to parse bootstrap todos config: %v", err)
-	}
-
-	if spec.Bootstrap == nil {
-		t.Fatal("Bootstrap config should not be nil")
-	}
-	if spec.Bootstrap.Mode != "project-todos" {
-		t.Errorf("Expected mode 'project-todos', got '%s'", spec.Bootstrap.Mode)
-	}
-	if spec.Bootstrap.ProjectTodos == nil {
-		t.Fatal("ProjectTodos config should not be nil")
-	}
-	if spec.Bootstrap.ProjectTodos.StatusField != "Status" {
-		t.Errorf("Expected status-field 'Status', got '%s'", spec.Bootstrap.ProjectTodos.StatusField)
-	}
-	if spec.Bootstrap.ProjectTodos.TodoValue != "Backlog" {
-		t.Errorf("Expected todo-value 'Backlog', got '%s'", spec.Bootstrap.ProjectTodos.TodoValue)
-	}
-	if spec.Bootstrap.ProjectTodos.MaxItems != 10 {
-		t.Errorf("Expected max-items 10, got %d", spec.Bootstrap.ProjectTodos.MaxItems)
-	}
-	if len(spec.Bootstrap.ProjectTodos.RequireFields) != 2 {
-		t.Errorf("Expected 2 require-fields, got %d", len(spec.Bootstrap.ProjectTodos.RequireFields))
-	}
-}
-
-// TestBootstrapConfig_Manual tests parsing bootstrap config with manual mode
-func TestBootstrapConfig_Manual(t *testing.T) {
-	yamlContent := `---
-id: test-bootstrap-manual
-name: Test Bootstrap Manual
-project-url: https://github.com/orgs/test/projects/1
-bootstrap:
-  mode: manual
----
-
-# Test Campaign`
-
-	var spec CampaignSpec
-	err := yaml.Unmarshal([]byte(yamlContent), &spec)
-	if err != nil {
-		t.Fatalf("Failed to parse bootstrap manual config: %v", err)
-	}
-
-	if spec.Bootstrap == nil {
-		t.Fatal("Bootstrap config should not be nil")
-	}
-	if spec.Bootstrap.Mode != "manual" {
-		t.Errorf("Expected mode 'manual', got '%s'", spec.Bootstrap.Mode)
-	}
-}
-
-// TestWorkerMetadata_Basic tests parsing basic worker metadata
-func TestWorkerMetadata_Basic(t *testing.T) {
-	yamlContent := `---
-id: test-workers
-name: Test Workers
-project-url: https://github.com/orgs/test/projects/1
-workers:
-  - id: security-fixer
-    name: Security Fix Worker
-    description: Fixes security vulnerabilities
-    capabilities:
-      - fix-security-alerts
-      - create-pull-requests
-    payload-schema:
-      repository:
-        type: string
-        description: Target repository in owner/repo format
-        required: true
-        example: owner/repo
-      work_item_id:
-        type: string
-        description: Unique work item identifier
-        required: true
-        example: alert-123
-      severity:
-        type: string
-        description: Alert severity level
-        required: false
-        example: high
-    output-labeling:
-      labels:
-        - security
-        - automated
-      key-in-title: true
-      key-format: "campaign-{campaign_id}-{repository}-{work_item_id}"
-      metadata-fields:
-        - Campaign Id
-        - Worker Workflow
-    idempotency-strategy: pr-title-based
-    priority: 10
----
-
-# Test Campaign`
-
-	var spec CampaignSpec
-	err := yaml.Unmarshal([]byte(yamlContent), &spec)
-	if err != nil {
-		t.Fatalf("Failed to parse worker metadata: %v", err)
-	}
-
-	if len(spec.Workers) == 0 {
-		t.Fatal("Workers should not be empty")
-	}
-
-	worker := spec.Workers[0]
-	if worker.ID != "security-fixer" {
-		t.Errorf("Expected worker ID 'security-fixer', got '%s'", worker.ID)
-	}
-	if worker.Name != "Security Fix Worker" {
-		t.Errorf("Expected worker name 'Security Fix Worker', got '%s'", worker.Name)
-	}
-	if len(worker.Capabilities) != 2 {
-		t.Errorf("Expected 2 capabilities, got %d", len(worker.Capabilities))
-	}
-	if len(worker.PayloadSchema) != 3 {
-		t.Errorf("Expected 3 payload schema fields, got %d", len(worker.PayloadSchema))
-	}
-
-	// Check payload schema field
-	repoField, exists := worker.PayloadSchema["repository"]
-	if !exists {
-		t.Error("Expected 'repository' field in payload schema")
-	}
-	if repoField.Type != "string" {
-		t.Errorf("Expected repository type 'string', got '%s'", repoField.Type)
-	}
-	if !repoField.Required {
-		t.Error("Expected repository field to be required")
-	}
-
-	// Check output labeling
-	if !worker.OutputLabeling.KeyInTitle {
-		t.Error("Expected key-in-title to be true")
-	}
-	if len(worker.OutputLabeling.Labels) != 2 {
-		t.Errorf("Expected 2 labels, got %d", len(worker.OutputLabeling.Labels))
-	}
-	if len(worker.OutputLabeling.MetadataFields) != 2 {
-		t.Errorf("Expected 2 metadata fields, got %d", len(worker.OutputLabeling.MetadataFields))
-	}
-
-	// Check idempotency strategy
-	if worker.IdempotencyStrategy != "pr-title-based" {
-		t.Errorf("Expected idempotency strategy 'pr-title-based', got '%s'", worker.IdempotencyStrategy)
-	}
-
-	// Check priority
-	if worker.Priority != 10 {
-		t.Errorf("Expected priority 10, got %d", worker.Priority)
-	}
-}
-
-// TestWorkerMetadata_Multiple tests parsing multiple workers
-func TestWorkerMetadata_Multiple(t *testing.T) {
-	yamlContent := `---
-id: test-multi-workers
-name: Test Multiple Workers
-project-url: https://github.com/orgs/test/projects/1
-workers:
-  - id: worker-one
-    capabilities: [scan]
-    payload-schema:
-      target:
-        type: string
-        description: Target to scan
-        required: true
-    output-labeling:
-      key-in-title: false
-    idempotency-strategy: branch-based
-  - id: worker-two
-    capabilities: [fix]
-    payload-schema:
-      issue:
-        type: number
-        description: Issue number
-        required: true
-    output-labeling:
-      key-in-title: true
-      key-format: "fix-{issue}"
-    idempotency-strategy: issue-title-based
-    priority: 5
----
-
-# Test Campaign`
-
-	var spec CampaignSpec
-	err := yaml.Unmarshal([]byte(yamlContent), &spec)
-	if err != nil {
-		t.Fatalf("Failed to parse multiple workers: %v", err)
-	}
-
-	if len(spec.Workers) != 2 {
-		t.Fatalf("Expected 2 workers, got %d", len(spec.Workers))
-	}
-
-	// Check first worker
-	if spec.Workers[0].ID != "worker-one" {
-		t.Errorf("Expected first worker ID 'worker-one', got '%s'", spec.Workers[0].ID)
-	}
-	if spec.Workers[0].IdempotencyStrategy != "branch-based" {
-		t.Errorf("Expected first worker idempotency 'branch-based', got '%s'", spec.Workers[0].IdempotencyStrategy)
-	}
-
-	// Check second worker
-	if spec.Workers[1].ID != "worker-two" {
-		t.Errorf("Expected second worker ID 'worker-two', got '%s'", spec.Workers[1].ID)
-	}
-	if spec.Workers[1].Priority != 5 {
-		t.Errorf("Expected second worker priority 5, got %d", spec.Workers[1].Priority)
-	}
-}
-
-// TestBootstrapAndWorkers_Combined tests campaign with both bootstrap and workers
-func TestBootstrapAndWorkers_Combined(t *testing.T) {
-	yamlContent := `---
-id: test-combined
-name: Test Combined
-project-url: https://github.com/orgs/test/projects/1
-bootstrap:
-  mode: seeder-worker
-  seeder-worker:
-    workflow-id: scanner
-    payload:
-      mode: discovery
-workers:
-  - id: scanner
-    capabilities: [scan]
-    payload-schema:
-      mode:
-        type: string
-        description: Scan mode
-        required: true
-    output-labeling:
-      key-in-title: true
-    idempotency-strategy: cursor-based
-  - id: fixer
-    capabilities: [fix]
-    payload-schema:
-      alert_id:
-        type: string
-        description: Alert ID
-        required: true
-    output-labeling:
-      key-in-title: true
-    idempotency-strategy: pr-title-based
-    priority: 10
----
-
-# Test Campaign`
-
-	var spec CampaignSpec
-	err := yaml.Unmarshal([]byte(yamlContent), &spec)
-	if err != nil {
-		t.Fatalf("Failed to parse combined config: %v", err)
-	}
-
-	// Verify bootstrap
-	if spec.Bootstrap == nil {
-		t.Fatal("Bootstrap should not be nil")
-	}
-	if spec.Bootstrap.Mode != "seeder-worker" {
-		t.Errorf("Expected bootstrap mode 'seeder-worker', got '%s'", spec.Bootstrap.Mode)
-	}
-	if spec.Bootstrap.SeederWorker.WorkflowID != "scanner" {
-		t.Errorf("Expected seeder workflow-id 'scanner', got '%s'", spec.Bootstrap.SeederWorker.WorkflowID)
-	}
-
-	// Verify workers
-	if len(spec.Workers) != 2 {
-		t.Fatalf("Expected 2 workers, got %d", len(spec.Workers))
-	}
-
-	// Check that seeder worker exists
-	var scannerFound bool
-	for _, worker := range spec.Workers {
-		if worker.ID == "scanner" {
-			scannerFound = true
-			if len(worker.Capabilities) != 1 || worker.Capabilities[0] != "scan" {
-				t.Error("Scanner worker should have 'scan' capability")
-			}
-		}
-	}
-	if !scannerFound {
-		t.Error("Scanner worker not found in workers list")
-	}
-}
-
-// TestBootstrapConfig_JSONSerialization tests JSON marshaling/unmarshaling
-func TestBootstrapConfig_JSONSerialization(t *testing.T) {
-	original := CampaignSpec{
-		ID:         "test-json",
-		Name:       "Test JSON",
-		ProjectURL: "https://github.com/orgs/test/projects/1",
-		Bootstrap: &CampaignBootstrapConfig{
-			Mode: "seeder-worker",
-			SeederWorker: &SeederWorkerConfig{
-				WorkflowID: "scanner",
-				Payload: map[string]any{
-					"type": "full-scan",
-					"max":  100,
-				},
-				MaxItems: 50,
-			},
-		},
-		Workers: []WorkerMetadata{
-			{
-				ID:           "test-worker",
-				Capabilities: []string{"scan"},
-				PayloadSchema: map[string]WorkerPayloadField{
-					"target": {
-						Type:        "string",
-						Description: "Target to scan",
-						Required:    true,
-					},
-				},
-				OutputLabeling: WorkerOutputLabeling{
-					KeyInTitle: true,
-				},
-				IdempotencyStrategy: "branch-based",
-			},
-		},
-	}
-
-	// Marshal to JSON
-	jsonData, err := json.Marshal(original)
-	if err != nil {
-		t.Fatalf("Failed to marshal to JSON: %v", err)
-	}
-
-	// Unmarshal from JSON
-	var restored CampaignSpec
-	err = json.Unmarshal(jsonData, &restored)
-	if err != nil {
-		t.Fatalf("Failed to unmarshal from JSON: %v", err)
-	}
-
-	// Verify fields
-	if restored.Bootstrap == nil {
-		t.Fatal("Restored bootstrap should not be nil")
-	}
-	if restored.Bootstrap.Mode != original.Bootstrap.Mode {
-		t.Errorf("Bootstrap mode mismatch: got '%s', want '%s'", restored.Bootstrap.Mode, original.Bootstrap.Mode)
-	}
-	if len(restored.Workers) != len(original.Workers) {
-		t.Errorf("Worker count mismatch: got %d, want %d", len(restored.Workers), len(original.Workers))
-	}
-}
-
-// TestWorkerPayloadField_RequiredDefaultsFalse tests that Required defaults to false
-func TestWorkerPayloadField_RequiredDefaultsFalse(t *testing.T) {
-	yamlContent := `---
-id: test-required
-name: Test Required Default
-project-url: https://github.com/orgs/test/projects/1
-workers:
-  - id: test
-    capabilities: [test]
-    payload-schema:
-      optional_field:
-        type: string
-        description: Optional field without explicit required
-    output-labeling:
-      key-in-title: false
-    idempotency-strategy: branch-based
----
-
-# Test`
-
-	var spec CampaignSpec
-	err := yaml.Unmarshal([]byte(yamlContent), &spec)
-	if err != nil {
-		t.Fatalf("Failed to parse: %v", err)
-	}
-
-	field := spec.Workers[0].PayloadSchema["optional_field"]
-	if field.Required {
-		t.Error("Expected Required to default to false")
-	}
-}
diff --git a/pkg/campaign/campaign_test.go b/pkg/campaign/campaign_test.go
deleted file mode 100644
index 9a392fe4e00..00000000000
--- a/pkg/campaign/campaign_test.go
+++ /dev/null
@@ -1,222 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"encoding/json"
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-)
-
-// TestLoadCampaignSpecs_Basic verifies that campaign specs can be loaded
-// from the default campaigns directory without errors.
-func TestLoadCampaignSpecs_Basic(t *testing.T) {
-	// Save current directory
-	originalDir, err := os.Getwd()
-	if err != nil {
-		t.Fatalf("Failed to get current directory: %v", err)
-	}
-	defer os.Chdir(originalDir)
-
-	// Change to repository root
-	repoRoot := filepath.Join(originalDir, "..", "..")
-	if err := os.Chdir(repoRoot); err != nil {
-		t.Fatalf("Failed to change to repository root: %v", err)
-	}
-
-	specs, err := LoadSpecs(repoRoot)
-	if err != nil {
-		t.Fatalf("LoadSpecs failed: %v", err)
-	}
-
-	// The repository may or may not have campaign specs, but LoadSpecs should succeed
-	t.Logf("Found %d campaign specs in repository", len(specs))
-
-	// If campaign specs exist, verify they have required fields
-	for _, spec := range specs {
-		if spec.ID == "" {
-			t.Errorf("Campaign spec has empty ID: %+v", spec)
-		}
-		if spec.Name == "" {
-			t.Errorf("Campaign spec %s has empty Name", spec.ID)
-		}
-		if spec.ConfigPath == "" {
-			t.Errorf("Campaign spec %s has empty ConfigPath", spec.ID)
-		}
-	}
-}
-
-// TestComputeCompiledStateForCampaign_UsesLockFiles checks that compiled
-// state reflects presence and freshness of .lock.yml files.
-func TestComputeCompiledStateForCampaign_UsesLockFiles(t *testing.T) {
-	originalDir, err := os.Getwd()
-	if err != nil {
-		t.Fatalf("Failed to get current directory: %v", err)
-	}
-	defer os.Chdir(originalDir)
-
-	repoRoot := filepath.Join(originalDir, "..", "..")
-	if err := os.Chdir(repoRoot); err != nil {
-		t.Fatalf("Failed to change to repository root: %v", err)
-	}
-
-	specs, err := LoadSpecs(repoRoot)
-	if err != nil {
-		t.Fatalf("LoadSpecs failed: %v", err)
-	}
-
-	var incident CampaignSpec
-	found := false
-	for _, spec := range specs {
-		if spec.ID == "go-file-size-reduction-project64" {
-			incident = spec
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Skip("go-file-size-reduction-project64 campaign spec not found; skipping compiled-state test")
-	}
-
-	state := ComputeCompiledState(incident, ".github/workflows")
-	if state == "Missing workflow" {
-		t.Fatalf("Expected go-file-size-reduction-project64 workflows to exist, got compiled state: %s", state)
-	}
-}
-
-// TestRunCampaignStatus_JSON ensures the campaign list view returns valid JSON
-// and that at least one campaign is present.
-func TestRunCampaignStatus_JSON(t *testing.T) {
-	originalDir, err := os.Getwd()
-	if err != nil {
-		t.Fatalf("Failed to get current directory: %v", err)
-	}
-	defer os.Chdir(originalDir)
-
-	repoRoot := filepath.Join(originalDir, "..", "..")
-	if err := os.Chdir(repoRoot); err != nil {
-		t.Fatalf("Failed to change to repository root: %v", err)
-	}
-
-	// Capture stdout via a pipe; simpler is to call runCampaignStatus and
-	// re-marshal the result, so instead we directly call the loader and
-	// verify JSON marshaling there.
-	specs, err := LoadSpecs(repoRoot)
-	if err != nil {
-		t.Fatalf("LoadSpecs failed: %v", err)
-	}
-
-	data, err := json.Marshal(specs)
-	if err != nil {
-		t.Fatalf("Failed to marshal specs to JSON: %v", err)
-	}
-
-	if len(data) == 0 {
-		t.Fatalf("Expected non-empty JSON for specs")
-	}
-}
-
-// TestValidateCampaignSpec_Basic ensures that a minimal but well-formed
-// spec passes validation without problems and that defaulting behavior
-// (like version) is applied.
-func TestValidateCampaignSpec_Basic(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "go-file-size-reduction",
-		Name:       "Go File Size Reduction",
-		ProjectURL: "https://github.com/orgs/githubnext/projects/1",
-		Scope:      []string{"org/repo1"},
-		Workflows:  []string{"daily-file-diet"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) != 0 {
-		t.Fatalf("Expected no validation problems for basic spec, got: %v", problems)
-	}
-
-	if spec.Version != "v1" {
-		t.Errorf("Expected default version 'v1', got %q", spec.Version)
-	}
-}
-
-// TestValidateCampaignSpec_InvalidState verifies that invalid state
-// values are reported by validation.
-func TestValidateCampaignSpec_InvalidState(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "rollout-q1-2025",
-		Name:       "Rollout",
-		ProjectURL: "https://github.com/orgs/githubnext/projects/1",
-		Scope:      []string{"org/repo1"},
-		Workflows:  []string{"daily-file-diet"},
-		State:      "launching", // invalid
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatalf("Expected validation problems for invalid state, got none")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "state must be one of") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected state validation problem, got: %v", problems)
-	}
-}
-
-// TestComputeCompiledState_LockFilePath verifies that lock file paths are
-// correctly constructed (workflow.lock.yml, not workflow.md.lock.yml).
-func TestComputeCompiledState_LockFilePath(t *testing.T) {
-	// Create a temporary directory for test workflows
-	tmpDir := t.TempDir()
-
-	// Create a workflow .md file and its .lock.yml companion
-	workflowID := "test-workflow"
-	mdPath := filepath.Join(tmpDir, workflowID+".md")
-	lockPath := filepath.Join(tmpDir, workflowID+".lock.yml")
-
-	if err := os.WriteFile(mdPath, []byte("test content"), 0o644); err != nil {
-		t.Fatalf("Failed to create test workflow: %v", err)
-	}
-	if err := os.WriteFile(lockPath, []byte("test lock"), 0o644); err != nil {
-		t.Fatalf("Failed to create test lock file: %v", err)
-	}
-
-	spec := CampaignSpec{
-		ID:        "test-campaign",
-		Scope:     []string{"org/repo1"},
-		Workflows: []string{workflowID},
-	}
-
-	// This should find the lock file and return "Yes"
-	state := ComputeCompiledState(spec, tmpDir)
-	if state != "Yes" {
-		t.Errorf("Expected compiled state 'Yes' when both .md and .lock.yml exist, got %q", state)
-	}
-
-	// Now test with only the .md file (remove lock file)
-	if err := os.Remove(lockPath); err != nil {
-		t.Fatalf("Failed to remove lock file: %v", err)
-	}
-
-	state = ComputeCompiledState(spec, tmpDir)
-	if state != "No" {
-		t.Errorf("Expected compiled state 'No' when .lock.yml is missing, got %q", state)
-	}
-
-	// Test that we don't look for the wrong path (workflow.md.lock.yml)
-	wrongLockPath := mdPath + ".lock.yml" // This would be workflow.md.lock.yml
-	if err := os.WriteFile(wrongLockPath, []byte("wrong lock"), 0o644); err != nil {
-		t.Fatalf("Failed to create wrong lock file: %v", err)
-	}
-
-	state = ComputeCompiledState(spec, tmpDir)
-	if state != "No" {
-		t.Errorf("Expected compiled state 'No' because correct lock file doesn't exist (only wrong path exists), got %q", state)
-	}
-}
diff --git a/pkg/campaign/command.go b/pkg/campaign/command.go
deleted file mode 100644
index 0a4965cfb58..00000000000
--- a/pkg/campaign/command.go
+++ /dev/null
@@ -1,435 +0,0 @@
-package campaign
-
-import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/githubnext/gh-aw/pkg/console"
-	"github.com/githubnext/gh-aw/pkg/constants"
-	"github.com/githubnext/gh-aw/pkg/logger"
-	"github.com/spf13/cobra"
-)
-
-var campaignLog = logger.New("campaign:command")
-
-// getWorkflowsDir returns the .github/workflows directory path.
-// This is a helper to avoid circular dependencies with cli package.
-func getWorkflowsDir() string {
-	return ".github/workflows"
-}
-
-// NewCommand creates the `gh aw campaign` command that surfaces
-// first-class campaign definitions from YAML files.
-func NewCommand() *cobra.Command {
-	cmd := &cobra.Command{
-		Use:   "campaign [filter]",
-		Short: "Inspect first-class campaign definitions from .github/workflows/*.campaign.md",
-		Long: `List and inspect first-class campaign definitions declared in YAML files.
-
-Campaigns are defined using Markdown files with YAML frontmatter under the local repository:
-
-	.github/workflows/*.campaign.md
-
-Each file describes a campaign pattern (ID, name, owners, associated
-workflows, repo-memory paths, and risk level). This command provides a
-single place to see all campaigns configured for the repo.
-
-Available subcommands:
-	• status   - Show live status for campaigns (compiled workflows, repo-memory)
-  • new      - Create a new campaign spec file
-  • validate - Validate campaign spec files for common issues
-
-Examples:
-  ` + string(constants.CLIExtensionPrefix) + ` campaign                      # List all campaigns
-  ` + string(constants.CLIExtensionPrefix) + ` campaign security             # Filter campaigns by ID or name
-  ` + string(constants.CLIExtensionPrefix) + ` campaign --json               # Output campaign definitions as JSON
-  ` + string(constants.CLIExtensionPrefix) + ` campaign status               # Show live campaign status with issue/PR counts
-  ` + string(constants.CLIExtensionPrefix) + ` campaign new security-q1-2025 # Create new campaign spec
-`,
-		Args: cobra.MaximumNArgs(1),
-		RunE: func(cmd *cobra.Command, args []string) error {
-			var pattern string
-			if len(args) > 0 {
-				pattern = args[0]
-			}
-
-			jsonOutput, _ := cmd.Flags().GetBool("json")
-			return runStatus(pattern, jsonOutput)
-		},
-	}
-
-	cmd.Flags().Bool("json", false, "Output campaign definitions in JSON format")
-
-	// Subcommand: campaign status
-	statusCmd := &cobra.Command{
-		Use:   "status [filter]",
-		Short: "Show live status for campaigns (compiled workflows, repo-memory)",
-		Long: `Show live status for campaigns, including whether referenced workflows
-are compiled and best-effort campaign metrics derived from repo-memory.
-
-Examples:
-  ` + string(constants.CLIExtensionPrefix) + ` campaign status              # Status for all campaigns
-  ` + string(constants.CLIExtensionPrefix) + ` campaign status security     # Filter by ID or name
-  ` + string(constants.CLIExtensionPrefix) + ` campaign status --json       # JSON status output
-`,
-		Args: cobra.MaximumNArgs(1),
-		RunE: func(cmd *cobra.Command, args []string) error {
-			var pattern string
-			if len(args) > 0 {
-				pattern = args[0]
-			}
-
-			jsonOutput, _ := cmd.Flags().GetBool("json")
-			return runRuntimeStatus(pattern, jsonOutput)
-		},
-	}
-
-	statusCmd.Flags().Bool("json", false, "Output campaign status in JSON format")
-	cmd.AddCommand(statusCmd)
-
-	// Subcommand: campaign new
-	newCmd := &cobra.Command{
-		Use:   "new <campaign-id>",
-		Short: "Create a new Markdown campaign spec under .github/workflows/",
-		Long: `Create a new campaign spec Markdown file under .github/workflows/.
-
-The file will be created as .github/workflows/<id>.campaign.md with YAML
-frontmatter (id, name, version, state, project-url) followed by a
-Markdown body. You can then
-update owners, workflows, memory paths, metrics-glob, and governance
-fields to match your initiative.
-
-With --interactive flag, enter an interactive wizard to create a comprehensive
-campaign spec with guided prompts for:
-- Campaign objective and description
-- Workflow discovery (optional: scan additional repos/orgs for worker workflows)
-- Repository scope (current, multiple repos, or org-wide)
-- Workflow selection
-- Owners and stakeholders
-- Risk level assessment
-- Project board creation
-
-With --project flag, a GitHub Project will be created with:
-- Required fields: Campaign Id, Worker Workflow, Priority, Size, Start Date, End Date
-- Views: Progress Board (board), Task Tracker (table), Campaign Roadmap (roadmap)
-- Linked to a repository (best-effort): defaults to current repo; override with --repo; disable with --no-link-repo
-- The project URL will be automatically added to the campaign spec
-
-Examples:
-  ` + string(constants.CLIExtensionPrefix) + ` campaign new security-q1-2025
-  ` + string(constants.CLIExtensionPrefix) + ` campaign new modernization-winter2025 --force
-  ` + string(constants.CLIExtensionPrefix) + ` campaign new --interactive                    # Interactive wizard
-  ` + string(constants.CLIExtensionPrefix) + ` campaign new security-q1-2025 --project --owner @me
-	` + string(constants.CLIExtensionPrefix) + ` campaign new modernization --project --owner myorg
-	` + string(constants.CLIExtensionPrefix) + ` campaign new modernization --project --owner myorg --no-link-repo
-	` + string(constants.CLIExtensionPrefix) + ` campaign new modernization --project --owner myorg --repo myorg/myrepo`,
-		Args: cobra.MaximumNArgs(1),
-		RunE: func(cmd *cobra.Command, args []string) error {
-			interactive, _ := cmd.Flags().GetBool("interactive")
-
-			// Interactive mode doesn't require campaign ID as argument
-			if interactive {
-				force, _ := cmd.Flags().GetBool("force")
-				verbose, _ := cmd.Flags().GetBool("verbose")
-
-				cwd, err := os.Getwd()
-				if err != nil {
-					return fmt.Errorf("failed to get current working directory: %w", err)
-				}
-
-				return RunInteractiveCampaignCreation(cwd, force, verbose)
-			}
-
-			// Non-interactive mode requires campaign ID
-			if len(args) == 0 {
-				// Build an error message with suggestions but without the leading
-				// error prefix icon; the main CLI handler will add that.
-				var b strings.Builder
-				b.WriteString("missing campaign id argument")
-				b.WriteString("\n\nSuggestions:\n")
-				suggestions := []string{
-					"Provide an ID: '" + string(constants.CLIExtensionPrefix) + " campaign new security-q1-2025'",
-					"Use '" + string(constants.CLIExtensionPrefix) + " campaign' to see existing campaigns",
-					"Run '" + string(constants.CLIExtensionPrefix) + " help campaign new' for full usage",
-				}
-				for _, s := range suggestions {
-					b.WriteString("  • ")
-					b.WriteString(s)
-					b.WriteString("\n")
-				}
-
-				return errors.New(b.String())
-			}
-
-			id := args[0]
-			force, _ := cmd.Flags().GetBool("force")
-			createProject, _ := cmd.Flags().GetBool("project")
-			owner, _ := cmd.Flags().GetString("owner")
-			repo, _ := cmd.Flags().GetString("repo")
-			noLinkRepo, _ := cmd.Flags().GetBool("no-link-repo")
-			verbose, _ := cmd.Flags().GetBool("verbose")
-
-			cwd, err := os.Getwd()
-			if err != nil {
-				return fmt.Errorf("failed to get current working directory: %w", err)
-			}
-
-			path, err := CreateSpecSkeleton(cwd, id, force)
-			if err != nil {
-				return err
-			}
-
-			fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(
-				"Created campaign spec at "+path,
-			))
-
-			// Create project if requested
-			if createProject {
-				if owner == "" {
-					return fmt.Errorf("--owner is required when using --project flag. Use '@me' for your personal projects or specify an organization name")
-				}
-
-				// Load the spec to get the campaign name
-				specs, err := LoadSpecs(cwd)
-				if err != nil {
-					return fmt.Errorf("failed to load campaign spec: %w", err)
-				}
-
-				// Find the newly created spec
-				var campaignName string
-				for _, spec := range specs {
-					if spec.ID == id {
-						campaignName = spec.Name
-						break
-					}
-				}
-
-				if campaignName == "" {
-					campaignName = id
-				}
-
-				fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Creating GitHub Project..."))
-
-				projectConfig := ProjectCreationConfig{
-					CampaignID:   id,
-					CampaignName: campaignName,
-					Owner:        owner,
-					LinkRepo:     repo,
-					NoLinkRepo:   noLinkRepo,
-					Verbose:      verbose,
-				}
-
-				result, err := CreateCampaignProject(projectConfig)
-				if err != nil {
-					return fmt.Errorf("failed to create project: %w", err)
-				}
-
-				fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(
-					fmt.Sprintf("Created project: %s", result.ProjectURL),
-				))
-
-				// Update the spec file with the project URL
-				fullPath := filepath.Join(cwd, path)
-				if err := UpdateSpecWithProjectURL(fullPath, result.ProjectURL); err != nil {
-					return fmt.Errorf("failed to update spec with project URL: %w", err)
-				}
-
-				fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(
-					"Updated campaign spec with project URL",
-				))
-			} else {
-				fmt.Fprintln(os.Stderr, console.FormatInfoMessage(
-					"Open the file and fill in owners, workflows, memory-paths, and other details.",
-				))
-			}
-
-			return nil
-		},
-	}
-
-	newCmd.Flags().Bool("force", false, "Overwrite existing spec file if it already exists")
-	newCmd.Flags().BoolP("interactive", "i", false, "Enter interactive mode to create campaign with guided prompts")
-	newCmd.Flags().Bool("project", false, "Create a GitHub Project with required views and fields")
-	newCmd.Flags().String("owner", "", "GitHub organization or user for the project (required with --project). Use '@me' for personal projects")
-	newCmd.Flags().StringP("repo", "r", "", "Repository to link the created project to (owner/name). Defaults to current repo")
-	newCmd.Flags().Bool("no-link-repo", false, "Disable best-effort project-to-repo linking")
-	newCmd.Flags().Bool("verbose", false, "Enable verbose output")
-	cmd.AddCommand(newCmd)
-
-	// Subcommand: campaign validate
-	validateCmd := &cobra.Command{
-		Use:   "validate [filter]",
-		Short: "Validate campaign spec files for common issues",
-		Long: `Validate campaign spec files under .github/workflows/*.campaign.md.
-
-This command performs lightweight semantic validation of campaign
-definitions (IDs, workflows, lifecycle state, and
-other key fields). By default it exits with a non-zero status when
-problems are found.
-
-Examples:
-  ` + string(constants.CLIExtensionPrefix) + ` campaign validate              # Validate all campaigns
-  ` + string(constants.CLIExtensionPrefix) + ` campaign validate security     # Filter by ID or name
-  ` + string(constants.CLIExtensionPrefix) + ` campaign validate --json       # JSON validation report
-  ` + string(constants.CLIExtensionPrefix) + ` campaign validate --no-strict  # Report problems without failing`,
-		Args: cobra.MaximumNArgs(1),
-		RunE: func(cmd *cobra.Command, args []string) error {
-			var pattern string
-			if len(args) > 0 {
-				pattern = args[0]
-			}
-
-			jsonOutput, _ := cmd.Flags().GetBool("json")
-			strict, _ := cmd.Flags().GetBool("strict")
-			return runValidate(pattern, jsonOutput, strict)
-		},
-	}
-
-	validateCmd.Flags().Bool("json", false, "Output campaign validation results in JSON format")
-	validateCmd.Flags().Bool("strict", true, "Exit with non-zero status if any problems are found")
-	cmd.AddCommand(validateCmd)
-
-	return cmd
-}
-
-// runStatus is the implementation for the `gh aw campaign` command.
-// It loads campaign specs from the local repository and renders them either
-// as a console table or JSON.
-func runStatus(pattern string, jsonOutput bool) error {
-	campaignLog.Printf("Running campaign status with pattern: %s, jsonOutput: %v", pattern, jsonOutput)
-	cwd, err := os.Getwd()
-	if err != nil {
-		return fmt.Errorf("failed to get current working directory: %w", err)
-	}
-
-	specs, err := LoadSpecs(cwd)
-	if err != nil {
-		campaignLog.Printf("Failed to load campaign specs: %s", err)
-		return err
-	}
-	campaignLog.Printf("Loaded %d campaign specs", len(specs))
-
-	specs = FilterSpecs(specs, pattern)
-	campaignLog.Printf("Filtered to %d campaign specs", len(specs))
-
-	if jsonOutput {
-		jsonBytes, err := json.MarshalIndent(specs, "", "  ")
-		if err != nil {
-			return fmt.Errorf("failed to marshal campaigns as JSON: %w", err)
-		}
-		fmt.Println(string(jsonBytes))
-		return nil
-	}
-
-	if len(specs) == 0 {
-		campaignLog.Print("No campaign specs found matching criteria")
-		fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No campaign specs found. Add files under '.github/workflows/*.campaign.md' to define campaigns."))
-		return nil
-	}
-
-	// Render the actual campaign specs (thin, declarative config) as a table.
-	output := console.RenderStruct(specs)
-	fmt.Print(output)
-	return nil
-}
-
-// runRuntimeStatus builds a higher-level view of campaign specs with
-// live information derived from GitHub (issue/PR counts) and compiled
-// workflow state.
-func runRuntimeStatus(pattern string, jsonOutput bool) error {
-	cwd, err := os.Getwd()
-	if err != nil {
-		return fmt.Errorf("failed to get current working directory: %w", err)
-	}
-
-	specs, err := LoadSpecs(cwd)
-	if err != nil {
-		return err
-	}
-
-	specs = FilterSpecs(specs, pattern)
-	if len(specs) == 0 {
-		fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No campaign specs found. Add files under '.github/workflows/*.campaign.md' to define campaigns."))
-		return nil
-	}
-
-	workflowsDir := getWorkflowsDir()
-	var statuses []CampaignRuntimeStatus
-	for _, spec := range specs {
-		status := BuildRuntimeStatus(spec, workflowsDir)
-		statuses = append(statuses, status)
-	}
-
-	if jsonOutput {
-		jsonBytes, err := json.MarshalIndent(statuses, "", "  ")
-		if err != nil {
-			return fmt.Errorf("failed to marshal campaign status as JSON: %w", err)
-		}
-		fmt.Println(string(jsonBytes))
-		return nil
-	}
-
-	output := console.RenderStruct(statuses)
-	fmt.Print(output)
-	return nil
-}
-
-// runValidate loads campaign specs and validates them, returning
-// a structured report. When strict is true, the command will exit with
-// a non-zero status if any problems are found.
-func runValidate(pattern string, jsonOutput bool, strict bool) error {
-	cwd, err := os.Getwd()
-	if err != nil {
-		return fmt.Errorf("failed to get current working directory: %w", err)
-	}
-
-	specs, err := LoadSpecs(cwd)
-	if err != nil {
-		return err
-	}
-
-	specs = FilterSpecs(specs, pattern)
-	if len(specs) == 0 {
-		fmt.Fprintln(os.Stderr, console.FormatInfoMessage("No campaign specs found. Add files under '.github/workflows/*.campaign.md' to define campaigns."))
-		return nil
-	}
-
-	var results []CampaignValidationResult
-	var totalProblems int
-
-	for i := range specs {
-		problems := ValidateSpec(&specs[i])
-		if len(problems) > 0 {
-			log.Printf("Validation problems for campaign '%s' (%s): %v", specs[i].ID, specs[i].ConfigPath, problems)
-		}
-
-		results = append(results, CampaignValidationResult{
-			ID:         specs[i].ID,
-			Name:       specs[i].Name,
-			ConfigPath: specs[i].ConfigPath,
-			Problems:   problems,
-		})
-		totalProblems += len(problems)
-	}
-
-	if jsonOutput {
-		jsonBytes, err := json.MarshalIndent(results, "", "  ")
-		if err != nil {
-			return fmt.Errorf("failed to marshal campaign validation results as JSON: %w", err)
-		}
-		fmt.Println(string(jsonBytes))
-	} else {
-		output := console.RenderStruct(results)
-		fmt.Print(output)
-	}
-
-	if strict && totalProblems > 0 {
-		return fmt.Errorf("campaign validation failed: %d problem(s) found across %d campaign(s)", totalProblems, len(results))
-	}
-
-	return nil
-}
diff --git a/pkg/campaign/create_test.go b/pkg/campaign/create_test.go
deleted file mode 100644
index d4610f85850..00000000000
--- a/pkg/campaign/create_test.go
+++ /dev/null
@@ -1,237 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-)
-
-func TestCreateSpecSkeleton_Basic(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	path, err := CreateSpecSkeleton(tmpDir, "test-campaign", false)
-	if err != nil {
-		t.Fatalf("CreateSpecSkeleton failed: %v", err)
-	}
-
-	expectedPath := ".github/workflows/test-campaign.campaign.md"
-	if path != expectedPath {
-		t.Errorf("Expected path '%s', got '%s'", expectedPath, path)
-	}
-
-	// Verify file was created
-	fullPath := filepath.Join(tmpDir, ".github", "workflows", "test-campaign.campaign.md")
-	if _, err := os.Stat(fullPath); os.IsNotExist(err) {
-		t.Errorf("Expected file to be created at %s", fullPath)
-	}
-
-	// Read and verify content
-	content, err := os.ReadFile(fullPath)
-	if err != nil {
-		t.Fatalf("Failed to read created file: %v", err)
-	}
-
-	contentStr := string(content)
-	if !strings.Contains(contentStr, "id: test-campaign") {
-		t.Error("Expected file to contain 'id: test-campaign'")
-	}
-	if !strings.Contains(contentStr, "name: Test campaign") {
-		t.Error("Expected file to contain 'name: Test campaign'")
-	}
-	if !strings.Contains(contentStr, "version: v1") {
-		t.Error("Expected file to contain 'version: v1'")
-	}
-	if !strings.Contains(contentStr, "state: planned") {
-		t.Error("Expected file to contain 'state: planned'")
-	}
-	if strings.Contains(contentStr, "tracker-label:") {
-		t.Error("Did not expect file to contain legacy 'tracker-label' frontmatter")
-	}
-	if !strings.Contains(contentStr, "project-url: https://github.com/orgs/ORG/projects/1") {
-		t.Error("Expected file to contain 'project-url: https://github.com/orgs/ORG/projects/1'")
-	}
-	if !strings.Contains(contentStr, "governance:") {
-		t.Error("Expected file to contain 'governance:'")
-	}
-	if !strings.Contains(contentStr, "max-new-items-per-run: 25") {
-		t.Error("Expected file to contain 'max-new-items-per-run: 25'")
-	}
-	if !strings.Contains(contentStr, "max-discovery-items-per-run: 200") {
-		t.Error("Expected file to contain 'max-discovery-items-per-run: 200'")
-	}
-	if !strings.Contains(contentStr, "max-discovery-pages-per-run: 10") {
-		t.Error("Expected file to contain 'max-discovery-pages-per-run: 10'")
-	}
-	if !strings.Contains(contentStr, "max-project-updates-per-run: 10") {
-		t.Error("Expected file to contain 'max-project-updates-per-run: 10'")
-	}
-	if !strings.Contains(contentStr, "max-comments-per-run: 10") {
-		t.Error("Expected file to contain 'max-comments-per-run: 10'")
-	}
-	if !strings.Contains(contentStr, "cursor-glob: memory/campaigns/test-campaign/cursor.json") {
-		t.Error("Expected file to contain 'cursor-glob: memory/campaigns/test-campaign/cursor.json'")
-	}
-}
-
-func TestCreateSpecSkeleton_InvalidID_Empty(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	_, err := CreateSpecSkeleton(tmpDir, "", false)
-	if err == nil {
-		t.Fatal("Expected error for empty ID")
-	}
-
-	if !strings.Contains(err.Error(), "id is required") {
-		t.Errorf("Expected 'id is required' error, got: %v", err)
-	}
-}
-
-func TestCreateSpecSkeleton_InvalidID_Uppercase(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	_, err := CreateSpecSkeleton(tmpDir, "Test-Campaign", false)
-	if err == nil {
-		t.Fatal("Expected error for uppercase in ID")
-	}
-
-	if !strings.Contains(err.Error(), "lowercase letters, digits, and hyphens") {
-		t.Errorf("Expected character restriction error, got: %v", err)
-	}
-}
-
-func TestCreateSpecSkeleton_InvalidID_Underscore(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	_, err := CreateSpecSkeleton(tmpDir, "test_campaign", false)
-	if err == nil {
-		t.Fatal("Expected error for underscore in ID")
-	}
-
-	if !strings.Contains(err.Error(), "lowercase letters, digits, and hyphens") {
-		t.Errorf("Expected character restriction error, got: %v", err)
-	}
-}
-
-func TestCreateSpecSkeleton_InvalidID_Space(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	_, err := CreateSpecSkeleton(tmpDir, "test campaign", false)
-	if err == nil {
-		t.Fatal("Expected error for space in ID")
-	}
-
-	if !strings.Contains(err.Error(), "lowercase letters, digits, and hyphens") {
-		t.Errorf("Expected character restriction error, got: %v", err)
-	}
-}
-
-func TestCreateSpecSkeleton_FileExists_NoForce(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	// Create first time
-	_, err := CreateSpecSkeleton(tmpDir, "test-campaign", false)
-	if err != nil {
-		t.Fatalf("First CreateSpecSkeleton failed: %v", err)
-	}
-
-	// Try to create again without force
-	_, err = CreateSpecSkeleton(tmpDir, "test-campaign", false)
-	if err == nil {
-		t.Fatal("Expected error when file exists without force flag")
-	}
-
-	if !strings.Contains(err.Error(), "already exists") {
-		t.Errorf("Expected 'already exists' error, got: %v", err)
-	}
-}
-
-func TestCreateSpecSkeleton_FileExists_WithForce(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	// Create first time
-	_, err := CreateSpecSkeleton(tmpDir, "test-campaign", false)
-	if err != nil {
-		t.Fatalf("First CreateSpecSkeleton failed: %v", err)
-	}
-
-	// Try to create again with force
-	_, err = CreateSpecSkeleton(tmpDir, "test-campaign", true)
-	if err != nil {
-		t.Errorf("CreateSpecSkeleton with force should succeed: %v", err)
-	}
-}
-
-func TestCreateSpecSkeleton_NameFormatting(t *testing.T) {
-	tests := []struct {
-		id           string
-		expectedName string
-	}{
-		{"test", "Test"},
-		{"test-campaign", "Test campaign"},
-		{"security-q1-2025", "Security q1 2025"},
-		{"org-modernization", "Org modernization"},
-	}
-
-	for _, tt := range tests {
-		tmpDir := t.TempDir()
-
-		_, err := CreateSpecSkeleton(tmpDir, tt.id, false)
-		if err != nil {
-			t.Fatalf("CreateSpecSkeleton failed for ID '%s': %v", tt.id, err)
-		}
-
-		// Load the created spec
-		specs, err := LoadSpecs(tmpDir)
-		if err != nil {
-			t.Fatalf("LoadSpecs failed: %v", err)
-		}
-
-		if len(specs) != 1 {
-			t.Fatalf("Expected 1 spec, got %d", len(specs))
-		}
-
-		if specs[0].Name != tt.expectedName {
-			t.Errorf("For ID '%s', expected name '%s', got '%s'", tt.id, tt.expectedName, specs[0].Name)
-		}
-	}
-}
-
-func TestCreateSpecSkeleton_CreatesDirectory(t *testing.T) {
-	tmpDir := t.TempDir()
-	// Don't create .github/workflows directory beforehand
-
-	_, err := CreateSpecSkeleton(tmpDir, "test-campaign", false)
-	if err != nil {
-		t.Fatalf("CreateSpecSkeleton failed: %v", err)
-	}
-
-	// Verify .github/workflows directory was created
-	workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
-	if _, err := os.Stat(workflowsDir); os.IsNotExist(err) {
-		t.Error("Expected .github/workflows directory to be created")
-	}
-}
-
-func TestCreateSpecSkeleton_ValidIDs(t *testing.T) {
-	validIDs := []string{
-		"test",
-		"test-campaign",
-		"test123",
-		"test-123-campaign",
-		"123-test",
-		"a",
-		"1",
-	}
-
-	for _, id := range validIDs {
-		tmpDir := t.TempDir()
-
-		_, err := CreateSpecSkeleton(tmpDir, id, false)
-		if err != nil {
-			t.Errorf("Expected ID '%s' to be valid, got error: %v", id, err)
-		}
-	}
-}
diff --git a/pkg/campaign/filter_test.go b/pkg/campaign/filter_test.go
deleted file mode 100644
index c06be810448..00000000000
--- a/pkg/campaign/filter_test.go
+++ /dev/null
@@ -1,141 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"testing"
-)
-
-func TestFilterSpecs_EmptyPattern(t *testing.T) {
-	specs := []CampaignSpec{
-		{ID: "campaign1", Name: "Campaign One"},
-		{ID: "campaign2", Name: "Campaign Two"},
-		{ID: "campaign3", Name: "Campaign Three"},
-	}
-
-	filtered := FilterSpecs(specs, "")
-
-	if len(filtered) != len(specs) {
-		t.Errorf("Expected %d specs with empty pattern, got %d", len(specs), len(filtered))
-	}
-}
-
-func TestFilterSpecs_MatchByID(t *testing.T) {
-	specs := []CampaignSpec{
-		{ID: "security-alpha", Name: "Security Alpha"},
-		{ID: "incident-beta", Name: "Incident Beta"},
-		{ID: "modernization-gamma", Name: "Modernization Gamma"},
-	}
-
-	filtered := FilterSpecs(specs, "security")
-
-	if len(filtered) != 1 {
-		t.Fatalf("Expected 1 spec matching 'security', got %d", len(filtered))
-	}
-
-	if filtered[0].ID != "security-alpha" {
-		t.Errorf("Expected to find 'security-alpha', got '%s'", filtered[0].ID)
-	}
-}
-
-func TestFilterSpecs_MatchByName(t *testing.T) {
-	specs := []CampaignSpec{
-		{ID: "sec-comp", Name: "Security Compliance"},
-		{ID: "incident", Name: "Incident Response"},
-		{ID: "modernization", Name: "Org Modernization"},
-	}
-
-	filtered := FilterSpecs(specs, "Response")
-
-	if len(filtered) != 1 {
-		t.Fatalf("Expected 1 spec matching 'Response', got %d", len(filtered))
-	}
-
-	if filtered[0].ID != "incident" {
-		t.Errorf("Expected to find 'incident', got '%s'", filtered[0].ID)
-	}
-}
-
-func TestFilterSpecs_CaseInsensitive(t *testing.T) {
-	specs := []CampaignSpec{
-		{ID: "security-alpha", Name: "Security Alpha"},
-		{ID: "incident-beta", Name: "Incident Beta"},
-	}
-
-	tests := []struct {
-		pattern string
-		wantLen int
-	}{
-		{"SECURITY", 1},
-		{"Security", 1},
-		{"security", 1},
-		{"INCIDENT", 1},
-		{"incident", 1},
-	}
-
-	for _, tt := range tests {
-		filtered := FilterSpecs(specs, tt.pattern)
-		if len(filtered) != tt.wantLen {
-			t.Errorf("Pattern '%s': expected %d matches, got %d", tt.pattern, tt.wantLen, len(filtered))
-		}
-	}
-}
-
-func TestFilterSpecs_MultipleMatches(t *testing.T) {
-	specs := []CampaignSpec{
-		{ID: "security-q1", Name: "Security Q1"},
-		{ID: "security-q2", Name: "Security Q2"},
-		{ID: "incident-beta", Name: "Incident Beta"},
-	}
-
-	filtered := FilterSpecs(specs, "security")
-
-	if len(filtered) != 2 {
-		t.Fatalf("Expected 2 specs matching 'security', got %d", len(filtered))
-	}
-
-	foundQ1, foundQ2 := false, false
-	for _, spec := range filtered {
-		if spec.ID == "security-q1" {
-			foundQ1 = true
-		}
-		if spec.ID == "security-q2" {
-			foundQ2 = true
-		}
-	}
-
-	if !foundQ1 || !foundQ2 {
-		t.Error("Expected to find both security-q1 and security-q2")
-	}
-}
-
-func TestFilterSpecs_NoMatches(t *testing.T) {
-	specs := []CampaignSpec{
-		{ID: "security-alpha", Name: "Security Alpha"},
-		{ID: "incident-beta", Name: "Incident Beta"},
-	}
-
-	filtered := FilterSpecs(specs, "nonexistent")
-
-	if len(filtered) != 0 {
-		t.Errorf("Expected 0 specs matching 'nonexistent', got %d", len(filtered))
-	}
-}
-
-func TestFilterSpecs_PartialMatch(t *testing.T) {
-	specs := []CampaignSpec{
-		{ID: "compliance-alpha", Name: "Compliance Alpha"},
-		{ID: "incident-beta", Name: "Incident Beta"},
-		{ID: "modernization-gamma", Name: "Modernization Gamma"},
-	}
-
-	filtered := FilterSpecs(specs, "comp")
-
-	if len(filtered) != 1 {
-		t.Fatalf("Expected 1 spec matching 'comp', got %d", len(filtered))
-	}
-
-	if filtered[0].ID != "compliance-alpha" {
-		t.Errorf("Expected to find 'compliance-alpha', got '%s'", filtered[0].ID)
-	}
-}
diff --git a/pkg/campaign/injection.go b/pkg/campaign/injection.go
deleted file mode 100644
index 37276d6b50b..00000000000
--- a/pkg/campaign/injection.go
+++ /dev/null
@@ -1,264 +0,0 @@
-package campaign
-
-import (
-	"fmt"
-	"regexp"
-	"strings"
-
-	"github.com/githubnext/gh-aw/pkg/logger"
-	"github.com/githubnext/gh-aw/pkg/workflow"
-)
-
-var injectionLog = logger.New("campaign:injection")
-
-var campaignIDSanitizer = regexp.MustCompile(`[^a-z0-9-]+`)
-
-func normalizeCampaignID(id string) string {
-	// Keep IDs stable and safe for labels/paths.
-	id = strings.ToLower(strings.TrimSpace(id))
-	id = strings.ReplaceAll(id, "_", "-")
-	id = strings.ReplaceAll(id, " ", "-")
-	id = campaignIDSanitizer.ReplaceAllString(id, "-")
-	// Collapse multiple hyphens into single hyphen
-	for strings.Contains(id, "--") {
-		id = strings.ReplaceAll(id, "--", "-")
-	}
-	id = strings.Trim(id, "-")
-	return id
-}
-
-// InjectOrchestratorFeatures detects if a workflow has project field with campaign
-// configuration and injects orchestrator features directly into the workflow during compilation.
-// This transforms the workflow into a campaign orchestrator without generating separate files.
-func InjectOrchestratorFeatures(workflowData *workflow.WorkflowData) error {
-	injectionLog.Print("Checking workflow for campaign orchestrator features")
-
-	// Check if this workflow has project configuration with campaign fields
-	if workflowData.ParsedFrontmatter == nil || workflowData.ParsedFrontmatter.Project == nil {
-		injectionLog.Print("No project field detected, skipping campaign injection")
-		return nil
-	}
-
-	project := workflowData.ParsedFrontmatter.Project
-
-	// Determine whether the project config looks like "project tracking" only.
-	// A minimal campaign can specify only the project URL (either short or long form) and omit
-	// campaign fields like id/workflows; in that case we infer the campaign ID from the workflow filename.
-	hasTrackingOnlySettings := len(project.Scope) > 0 ||
-		project.MaxUpdates > 0 ||
-		project.MaxStatusUpdates > 0 ||
-		strings.TrimSpace(project.GitHubToken) != "" ||
-		project.DoNotDowngradeDoneItems != nil
-
-	// Check if project has any campaign orchestration fields to determine if this is a campaign
-	// Campaign indicators (any of these present means it's a campaign orchestrator):
-	// - explicit campaign ID
-	// - workflows list (predefined workers)
-	// - governance policies (campaign-specific constraints)
-	// - bootstrap configuration (initial work item generation)
-	// - memory-paths, metrics-glob, cursor-glob (campaign state tracking)
-	// If only URL and scope are present, it's simple project tracking, not a campaign
-	hasCampaignIndicators := strings.TrimSpace(project.ID) != "" ||
-		len(project.Workflows) > 0 ||
-		project.Governance != nil ||
-		project.Bootstrap != nil ||
-		len(project.MemoryPaths) > 0 ||
-		project.MetricsGlob != "" ||
-		project.CursorGlob != ""
-
-	// If the user used the object form with only a URL (no tracking-only knobs), treat it as a campaign
-	// and infer the campaign ID from the workflow filename (minus .md).
-	if !hasCampaignIndicators && !hasTrackingOnlySettings {
-		if workflowData.WorkflowID != "" {
-			project.ID = workflowData.WorkflowID
-			hasCampaignIndicators = true
-		}
-	}
-
-	isCampaign := hasCampaignIndicators
-
-	if !isCampaign {
-		injectionLog.Print("Project field present but no campaign indicators, treating as simple project tracking")
-		return nil
-	}
-
-	injectionLog.Printf("Detected campaign orchestrator: workflows=%d, has_governance=%v, has_bootstrap=%v",
-		len(project.Workflows), project.Governance != nil, project.Bootstrap != nil)
-
-	// Derive campaign ID (prefer explicit id, then workflow filename, then workflow name).
-	// Note: workflowData.FrontmatterName is the *frontmatter name field* (display name), not the file basename.
-	campaignID := ""
-	if strings.TrimSpace(project.ID) != "" {
-		campaignID = project.ID
-	} else if strings.TrimSpace(workflowData.WorkflowID) != "" {
-		campaignID = workflowData.WorkflowID
-	} else if strings.TrimSpace(workflowData.Name) != "" {
-		campaignID = workflowData.Name
-	} else {
-		campaignID = "campaign"
-	}
-	campaignID = normalizeCampaignID(campaignID)
-	if campaignID == "" {
-		campaignID = "campaign"
-	}
-
-	// Apply campaign defaults (matching the historical .campaign.md defaults) when omitted.
-	// This keeps project-based campaigns minimal: users can specify just url + id.
-	project.ID = campaignID
-	if strings.TrimSpace(project.TrackerLabel) == "" {
-		project.TrackerLabel = fmt.Sprintf("z_campaign_%s", campaignID)
-	}
-	if len(project.MemoryPaths) == 0 {
-		project.MemoryPaths = []string{fmt.Sprintf("memory/campaigns/%s/**", campaignID)}
-	}
-	if strings.TrimSpace(project.MetricsGlob) == "" {
-		project.MetricsGlob = fmt.Sprintf("memory/campaigns/%s/metrics/*.json", campaignID)
-	}
-	if strings.TrimSpace(project.CursorGlob) == "" {
-		project.CursorGlob = fmt.Sprintf("memory/campaigns/%s/cursor.json", campaignID)
-	}
-
-	// Build campaign prompt data from project configuration
-	promptData := CampaignPromptData{
-		CampaignID:   campaignID,
-		CampaignName: workflowData.Name,
-		ProjectURL:   project.URL,
-		CursorGlob:   project.CursorGlob,
-		MetricsGlob:  project.MetricsGlob,
-		Workflows:    project.Workflows,
-	}
-
-	if project.Governance != nil {
-		promptData.MaxDiscoveryItemsPerRun = project.Governance.MaxDiscoveryItemsPerRun
-		promptData.MaxDiscoveryPagesPerRun = project.Governance.MaxDiscoveryPagesPerRun
-		promptData.MaxProjectUpdatesPerRun = project.Governance.MaxProjectUpdatesPerRun
-		promptData.MaxProjectCommentsPerRun = project.Governance.MaxCommentsPerRun
-	}
-
-	if project.Bootstrap != nil {
-		promptData.BootstrapMode = project.Bootstrap.Mode
-		if project.Bootstrap.SeederWorker != nil {
-			promptData.SeederWorkerID = project.Bootstrap.SeederWorker.WorkflowID
-			promptData.SeederMaxItems = project.Bootstrap.SeederWorker.MaxItems
-		}
-		if project.Bootstrap.ProjectTodos != nil {
-			promptData.StatusField = project.Bootstrap.ProjectTodos.StatusField
-			if promptData.StatusField == "" {
-				promptData.StatusField = "Status"
-			}
-			promptData.TodoValue = project.Bootstrap.ProjectTodos.TodoValue
-			if promptData.TodoValue == "" {
-				promptData.TodoValue = "Todo"
-			}
-			promptData.TodoMaxItems = project.Bootstrap.ProjectTodos.MaxItems
-			promptData.RequireFields = project.Bootstrap.ProjectTodos.RequireFields
-		}
-	}
-
-	if len(project.Workers) > 0 {
-		promptData.WorkerMetadata = make([]WorkerMetadata, len(project.Workers))
-		for i, w := range project.Workers {
-			promptData.WorkerMetadata[i] = WorkerMetadata{
-				ID:                  w.ID,
-				Name:                w.Name,
-				Description:         w.Description,
-				Capabilities:        w.Capabilities,
-				IdempotencyStrategy: w.IdempotencyStrategy,
-				Priority:            w.Priority,
-			}
-			// Convert payload schema
-			if len(w.PayloadSchema) > 0 {
-				promptData.WorkerMetadata[i].PayloadSchema = make(map[string]WorkerPayloadField)
-				for key, field := range w.PayloadSchema {
-					promptData.WorkerMetadata[i].PayloadSchema[key] = WorkerPayloadField{
-						Type:        field.Type,
-						Description: field.Description,
-						Required:    field.Required,
-						Example:     field.Example,
-					}
-				}
-			}
-			// Convert output labeling
-			promptData.WorkerMetadata[i].OutputLabeling = WorkerOutputLabeling{
-				Labels:         w.OutputLabeling.Labels,
-				KeyInTitle:     w.OutputLabeling.KeyInTitle,
-				KeyFormat:      w.OutputLabeling.KeyFormat,
-				MetadataFields: w.OutputLabeling.MetadataFields,
-			}
-		}
-	}
-
-	// Append orchestrator instructions to markdown content
-	markdownBuilder := &strings.Builder{}
-	markdownBuilder.WriteString(workflowData.MarkdownContent)
-	markdownBuilder.WriteString("\n\n")
-
-	// Add bootstrap instructions if configured
-	if project.Bootstrap != nil && project.Bootstrap.Mode != "" {
-		bootstrapInstructions := RenderBootstrapInstructions(promptData)
-		if bootstrapInstructions != "" {
-			AppendPromptSection(markdownBuilder, "BOOTSTRAP INSTRUCTIONS (PHASE 0)", bootstrapInstructions)
-		}
-	}
-
-	// Add workflow execution instructions
-	workflowExecution := RenderWorkflowExecution(promptData)
-	if workflowExecution != "" {
-		AppendPromptSection(markdownBuilder, "WORKFLOW EXECUTION (PHASE 0)", workflowExecution)
-	}
-
-	// Add orchestrator instructions
-	orchestratorInstructions := RenderOrchestratorInstructions(promptData)
-	if orchestratorInstructions != "" {
-		AppendPromptSection(markdownBuilder, "ORCHESTRATOR INSTRUCTIONS", orchestratorInstructions)
-	}
-
-	// Add project update instructions
-	projectInstructions := RenderProjectUpdateInstructions(promptData)
-	if projectInstructions != "" {
-		AppendPromptSection(markdownBuilder, "PROJECT UPDATE INSTRUCTIONS (AUTHORITATIVE FOR WRITES)", projectInstructions)
-	}
-
-	// Add closing instructions
-	closingInstructions := RenderClosingInstructions()
-	if closingInstructions != "" {
-		AppendPromptSection(markdownBuilder, "CLOSING INSTRUCTIONS (HIGHEST PRIORITY)", closingInstructions)
-	}
-
-	// Update the workflow markdown content with injected instructions
-	workflowData.MarkdownContent = markdownBuilder.String()
-	injectionLog.Printf("Injected campaign orchestrator instructions into workflow markdown")
-
-	// Configure safe-outputs for campaign orchestration
-	if workflowData.SafeOutputs == nil {
-		workflowData.SafeOutputs = &workflow.SafeOutputsConfig{}
-	}
-
-	// Configure dispatch-workflow for worker coordination (optional - only if workflows are specified)
-	if len(project.Workflows) > 0 && workflowData.SafeOutputs.DispatchWorkflow == nil {
-		workflowData.SafeOutputs.DispatchWorkflow = &workflow.DispatchWorkflowConfig{
-			BaseSafeOutputConfig: workflow.BaseSafeOutputConfig{Max: 3},
-			Workflows:            project.Workflows,
-		}
-		injectionLog.Printf("Configured dispatch-workflow safe-output for %d workflows", len(project.Workflows))
-	} else if len(project.Workflows) == 0 {
-		injectionLog.Print("No workflows specified - campaign will use custom discovery and dispatch logic")
-	}
-
-	// Configure update-project (already handled by applyProjectSafeOutputs, but ensure governance max is applied)
-	if project.Governance != nil && project.Governance.MaxProjectUpdatesPerRun > 0 {
-		if workflowData.SafeOutputs.UpdateProjects != nil {
-			workflowData.SafeOutputs.UpdateProjects.Max = project.Governance.MaxProjectUpdatesPerRun
-			injectionLog.Printf("Applied governance max-project-updates-per-run: %d", project.Governance.MaxProjectUpdatesPerRun)
-		}
-	}
-
-	// Add concurrency control for campaigns if not already set
-	if workflowData.Concurrency == "" {
-		workflowData.Concurrency = fmt.Sprintf("concurrency:\n  group: \"campaign-%s-orchestrator-${{ github.ref }}\"\n  cancel-in-progress: false", campaignID)
-		injectionLog.Printf("Added campaign concurrency control")
-	}
-
-	injectionLog.Printf("Successfully injected campaign orchestrator features for: %s", campaignID)
-	return nil
-}
diff --git a/pkg/campaign/injection_test.go b/pkg/campaign/injection_test.go
deleted file mode 100644
index 1fd4d2256a3..00000000000
--- a/pkg/campaign/injection_test.go
+++ /dev/null
@@ -1,462 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"testing"
-
-	"github.com/githubnext/gh-aw/pkg/workflow"
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func TestInjectOrchestratorFeatures_ProjectIDTriggersCampaignAndDefaults(t *testing.T) {
-	data := &workflow.WorkflowData{
-		Name:            "Security Alert Burndown",
-		WorkflowID:      "security-alert-burndown",
-		MarkdownContent: "# Test",
-		ParsedFrontmatter: &workflow.FrontmatterConfig{
-			Project: &workflow.ProjectConfig{
-				URL: "https://github.com/orgs/githubnext/projects/144",
-			},
-		},
-	}
-
-	err := InjectOrchestratorFeatures(data)
-	require.NoError(t, err, "campaign injection should succeed")
-	require.NotNil(t, data.ParsedFrontmatter, "ParsedFrontmatter should remain")
-	require.NotNil(t, data.ParsedFrontmatter.Project, "Project should remain")
-
-	project := data.ParsedFrontmatter.Project
-	assert.Equal(t, "security-alert-burndown", project.ID, "Campaign ID should be inferred and normalized")
-	assert.Equal(t, "z_campaign_security-alert-burndown", project.TrackerLabel, "TrackerLabel should be defaulted")
-	assert.Equal(t, []string{"memory/campaigns/security-alert-burndown/**"}, project.MemoryPaths, "MemoryPaths should be defaulted")
-	assert.Equal(t, "memory/campaigns/security-alert-burndown/metrics/*.json", project.MetricsGlob, "MetricsGlob should be defaulted")
-	assert.Equal(t, "memory/campaigns/security-alert-burndown/cursor.json", project.CursorGlob, "CursorGlob should be defaulted")
-
-	assert.Contains(t, data.MarkdownContent, "ORCHESTRATOR INSTRUCTIONS", "Markdown should have orchestrator instructions injected")
-}
-
-func TestInjectOrchestratorFeatures_ProjectTrackingOnly_DoesNotInject(t *testing.T) {
-	data := &workflow.WorkflowData{
-		Name:            "Project Tracking Only",
-		FrontmatterName: "project-tracking-only",
-		MarkdownContent: "# Test",
-		ParsedFrontmatter: &workflow.FrontmatterConfig{
-			Project: &workflow.ProjectConfig{
-				URL:   "https://github.com/orgs/githubnext/projects/144",
-				Scope: []string{"githubnext/gh-aw"},
-			},
-		},
-	}
-
-	err := InjectOrchestratorFeatures(data)
-	require.NoError(t, err, "non-campaign project tracking should be a no-op")
-	assert.NotContains(t, data.MarkdownContent, "ORCHESTRATOR INSTRUCTIONS", "Should not inject campaign sections")
-}
-
-func TestInjectOrchestratorFeatures_EdgeCases(t *testing.T) {
-	tests := []struct {
-		name               string
-		workflowData       *workflow.WorkflowData
-		shouldErr          bool
-		expectInjection    bool
-		expectedCampaignID string
-	}{
-		{
-			name: "nil frontmatter - should skip",
-			workflowData: &workflow.WorkflowData{
-				Name:              "Test",
-				WorkflowID:        "test",
-				MarkdownContent:   "# Test",
-				ParsedFrontmatter: nil,
-			},
-			shouldErr:       false,
-			expectInjection: false,
-		},
-		{
-			name: "nil project - should skip",
-			workflowData: &workflow.WorkflowData{
-				Name:            "Test",
-				WorkflowID:      "test",
-				MarkdownContent: "# Test",
-				ParsedFrontmatter: &workflow.FrontmatterConfig{
-					Project: nil,
-				},
-			},
-			shouldErr:       false,
-			expectInjection: false,
-		},
-		{
-			name: "infer campaign ID from WorkflowID",
-			workflowData: &workflow.WorkflowData{
-				Name:            "Security Alert Burndown",
-				WorkflowID:      "security-alert-burndown",
-				MarkdownContent: "# Test",
-				ParsedFrontmatter: &workflow.FrontmatterConfig{
-					Project: &workflow.ProjectConfig{
-						URL: "https://github.com/orgs/githubnext/projects/144",
-					},
-				},
-			},
-			shouldErr:          false,
-			expectInjection:    true,
-			expectedCampaignID: "security-alert-burndown",
-		},
-		{
-			name: "explicit campaign ID with WorkflowID",
-			workflowData: &workflow.WorkflowData{
-				Name:            "Security Alert Burndown",
-				WorkflowID:      "security-alert-burndown",
-				MarkdownContent: "# Test",
-				ParsedFrontmatter: &workflow.FrontmatterConfig{
-					Project: &workflow.ProjectConfig{
-						URL: "https://github.com/orgs/githubnext/projects/144",
-						ID:  "custom-campaign-id",
-					},
-				},
-			},
-			shouldErr:          false,
-			expectInjection:    true,
-			expectedCampaignID: "custom-campaign-id",
-		},
-		{
-			name: "campaign ID inferred from Name when WorkflowID empty but campaign indicator present",
-			workflowData: &workflow.WorkflowData{
-				Name:            "Security Alert Burndown",
-				WorkflowID:      "",
-				MarkdownContent: "# Test",
-				ParsedFrontmatter: &workflow.FrontmatterConfig{
-					Project: &workflow.ProjectConfig{
-						URL:       "https://github.com/orgs/githubnext/projects/144",
-						Workflows: []string{"worker.md"}, // Campaign indicator
-					},
-				},
-			},
-			shouldErr:          false,
-			expectInjection:    true,
-			expectedCampaignID: "security-alert-burndown",
-		},
-		{
-			name: "fallback to default campaign ID when all identifiers empty",
-			workflowData: &workflow.WorkflowData{
-				Name:            "",
-				WorkflowID:      "",
-				MarkdownContent: "# Test",
-				ParsedFrontmatter: &workflow.FrontmatterConfig{
-					Project: &workflow.ProjectConfig{
-						URL:       "https://github.com/orgs/githubnext/projects/144",
-						Workflows: []string{"worker.md"}, // Campaign indicator
-					},
-				},
-			},
-			shouldErr:          false,
-			expectInjection:    true,
-			expectedCampaignID: "campaign",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			err := InjectOrchestratorFeatures(tt.workflowData)
-
-			if tt.shouldErr {
-				require.Error(t, err)
-			} else {
-				require.NoError(t, err)
-
-				if tt.expectInjection {
-					assert.Contains(t, tt.workflowData.MarkdownContent, "ORCHESTRATOR INSTRUCTIONS", "should inject campaign sections")
-					if tt.expectedCampaignID != "" {
-						assert.Equal(t, tt.expectedCampaignID, tt.workflowData.ParsedFrontmatter.Project.ID, "campaign ID should match expected")
-					}
-				} else {
-					assert.NotContains(t, tt.workflowData.MarkdownContent, "ORCHESTRATOR INSTRUCTIONS", "should not inject campaign sections")
-				}
-			}
-		})
-	}
-}
-
-func TestInjectOrchestratorFeatures_GovernanceConfiguration(t *testing.T) {
-	data := &workflow.WorkflowData{
-		Name:            "Campaign with Governance",
-		WorkflowID:      "test-campaign",
-		MarkdownContent: "# Test",
-		ParsedFrontmatter: &workflow.FrontmatterConfig{
-			Project: &workflow.ProjectConfig{
-				URL: "https://github.com/orgs/githubnext/projects/144",
-				Governance: &workflow.CampaignGovernanceConfig{
-					MaxDiscoveryItemsPerRun: 10,
-					MaxDiscoveryPagesPerRun: 5,
-					MaxProjectUpdatesPerRun: 20,
-					MaxCommentsPerRun:       15,
-				},
-			},
-		},
-		SafeOutputs: &workflow.SafeOutputsConfig{
-			UpdateProjects: &workflow.UpdateProjectConfig{
-				BaseSafeOutputConfig: workflow.BaseSafeOutputConfig{Max: 5},
-			},
-		},
-	}
-
-	err := InjectOrchestratorFeatures(data)
-	require.NoError(t, err, "should handle governance configuration")
-
-	// Verify governance max is applied to safe-outputs
-	assert.Equal(t, 20, data.SafeOutputs.UpdateProjects.Max, "governance max should override safe-outputs max")
-}
-
-func TestInjectOrchestratorFeatures_BootstrapConfiguration(t *testing.T) {
-	data := &workflow.WorkflowData{
-		Name:            "Campaign with Bootstrap",
-		WorkflowID:      "test-campaign",
-		MarkdownContent: "# Test",
-		ParsedFrontmatter: &workflow.FrontmatterConfig{
-			Project: &workflow.ProjectConfig{
-				URL: "https://github.com/orgs/githubnext/projects/144",
-				Bootstrap: &workflow.CampaignBootstrapConfig{
-					Mode: "seeder",
-					SeederWorker: &workflow.SeederWorkerConfig{
-						WorkflowID: "seeder-workflow",
-						MaxItems:   50,
-					},
-				},
-			},
-		},
-	}
-
-	err := InjectOrchestratorFeatures(data)
-	require.NoError(t, err, "should handle bootstrap configuration")
-	assert.Contains(t, data.MarkdownContent, "BOOTSTRAP INSTRUCTIONS", "should include bootstrap instructions")
-}
-
-func TestInjectOrchestratorFeatures_WorkersConfiguration(t *testing.T) {
-	data := &workflow.WorkflowData{
-		Name:            "Campaign with Workers",
-		WorkflowID:      "test-campaign",
-		MarkdownContent: "# Test",
-		ParsedFrontmatter: &workflow.FrontmatterConfig{
-			Project: &workflow.ProjectConfig{
-				URL: "https://github.com/orgs/githubnext/projects/144",
-				Workers: []workflow.WorkerMetadata{
-					{
-						ID:          "worker-1",
-						Name:        "Test Worker",
-						Description: "A test worker",
-						PayloadSchema: map[string]workflow.WorkerPayloadField{
-							"field1": {
-								Type:        "string",
-								Description: "Test field",
-								Required:    true,
-								Example:     "example",
-							},
-						},
-						OutputLabeling: workflow.WorkerOutputLabeling{
-							Labels:     []string{"test-label"},
-							KeyInTitle: true,
-						},
-					},
-				},
-			},
-		},
-	}
-
-	err := InjectOrchestratorFeatures(data)
-	require.NoError(t, err, "should handle workers configuration")
-	assert.Contains(t, data.MarkdownContent, "ORCHESTRATOR INSTRUCTIONS", "should inject orchestrator instructions")
-}
-
-func TestInjectOrchestratorFeatures_SafeOutputsDispatchWorkflow(t *testing.T) {
-	data := &workflow.WorkflowData{
-		Name:            "Campaign with Workflows",
-		WorkflowID:      "test-campaign",
-		MarkdownContent: "# Test",
-		ParsedFrontmatter: &workflow.FrontmatterConfig{
-			Project: &workflow.ProjectConfig{
-				URL:       "https://github.com/orgs/githubnext/projects/144",
-				Workflows: []string{"worker-1.md", "worker-2.md"},
-			},
-		},
-	}
-
-	err := InjectOrchestratorFeatures(data)
-	require.NoError(t, err, "should configure dispatch-workflow")
-	require.NotNil(t, data.SafeOutputs, "SafeOutputs should be initialized")
-	require.NotNil(t, data.SafeOutputs.DispatchWorkflow, "DispatchWorkflow should be configured")
-	assert.Equal(t, 3, data.SafeOutputs.DispatchWorkflow.Max, "should have max of 3")
-	assert.Equal(t, []string{"worker-1.md", "worker-2.md"}, data.SafeOutputs.DispatchWorkflow.Workflows, "should have configured workflows")
-}
-
-func TestInjectOrchestratorFeatures_ConcurrencyControl(t *testing.T) {
-	data := &workflow.WorkflowData{
-		Name:            "Campaign",
-		WorkflowID:      "test-campaign",
-		MarkdownContent: "# Test",
-		Concurrency:     "", // Empty - should be injected
-		ParsedFrontmatter: &workflow.FrontmatterConfig{
-			Project: &workflow.ProjectConfig{
-				URL: "https://github.com/orgs/githubnext/projects/144",
-			},
-		},
-	}
-
-	err := InjectOrchestratorFeatures(data)
-	require.NoError(t, err, "should add concurrency control")
-	assert.Contains(t, data.Concurrency, "campaign-test-campaign-orchestrator", "should include campaign ID in concurrency group")
-	assert.Contains(t, data.Concurrency, "cancel-in-progress: false", "should not cancel in progress")
-}
-
-func TestInjectOrchestratorFeatures_ConcurrencyAlreadySet(t *testing.T) {
-	existingConcurrency := "concurrency:\n  group: custom-group\n  cancel-in-progress: true"
-	data := &workflow.WorkflowData{
-		Name:            "Campaign",
-		WorkflowID:      "test-campaign",
-		MarkdownContent: "# Test",
-		Concurrency:     existingConcurrency,
-		ParsedFrontmatter: &workflow.FrontmatterConfig{
-			Project: &workflow.ProjectConfig{
-				URL: "https://github.com/orgs/githubnext/projects/144",
-			},
-		},
-	}
-
-	err := InjectOrchestratorFeatures(data)
-	require.NoError(t, err, "should not override existing concurrency")
-	assert.Equal(t, existingConcurrency, data.Concurrency, "should preserve existing concurrency")
-}
-
-func TestInjectOrchestratorFeatures_DefaultsApplication(t *testing.T) {
-	tests := []struct {
-		name                 string
-		campaignID           string
-		initialTrackerLabel  string
-		initialMemoryPaths   []string
-		initialMetricsGlob   string
-		initialCursorGlob    string
-		expectedTrackerLabel string
-		expectedMemoryPaths  []string
-		expectedMetricsGlob  string
-		expectedCursorGlob   string
-	}{
-		{
-			name:                 "all defaults applied",
-			campaignID:           "test-campaign",
-			initialTrackerLabel:  "",
-			initialMemoryPaths:   nil,
-			initialMetricsGlob:   "",
-			initialCursorGlob:    "",
-			expectedTrackerLabel: "z_campaign_test-campaign",
-			expectedMemoryPaths:  []string{"memory/campaigns/test-campaign/**"},
-			expectedMetricsGlob:  "memory/campaigns/test-campaign/metrics/*.json",
-			expectedCursorGlob:   "memory/campaigns/test-campaign/cursor.json",
-		},
-		{
-			name:                 "explicit values preserved",
-			campaignID:           "test-campaign",
-			initialTrackerLabel:  "custom-label",
-			initialMemoryPaths:   []string{"custom/path/**"},
-			initialMetricsGlob:   "custom/metrics/*.json",
-			initialCursorGlob:    "custom/cursor.json",
-			expectedTrackerLabel: "custom-label",
-			expectedMemoryPaths:  []string{"custom/path/**"},
-			expectedMetricsGlob:  "custom/metrics/*.json",
-			expectedCursorGlob:   "custom/cursor.json",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			data := &workflow.WorkflowData{
-				Name:            "Test Campaign",
-				WorkflowID:      tt.campaignID,
-				MarkdownContent: "# Test",
-				ParsedFrontmatter: &workflow.FrontmatterConfig{
-					Project: &workflow.ProjectConfig{
-						URL:          "https://github.com/orgs/githubnext/projects/144",
-						TrackerLabel: tt.initialTrackerLabel,
-						MemoryPaths:  tt.initialMemoryPaths,
-						MetricsGlob:  tt.initialMetricsGlob,
-						CursorGlob:   tt.initialCursorGlob,
-					},
-				},
-			}
-
-			err := InjectOrchestratorFeatures(data)
-			require.NoError(t, err, "should apply defaults")
-
-			project := data.ParsedFrontmatter.Project
-			assert.Equal(t, tt.expectedTrackerLabel, project.TrackerLabel, "TrackerLabel should match")
-			assert.Equal(t, tt.expectedMemoryPaths, project.MemoryPaths, "MemoryPaths should match")
-			assert.Equal(t, tt.expectedMetricsGlob, project.MetricsGlob, "MetricsGlob should match")
-			assert.Equal(t, tt.expectedCursorGlob, project.CursorGlob, "CursorGlob should match")
-		})
-	}
-}
-
-func TestNormalizeCampaignID(t *testing.T) {
-	tests := []struct {
-		name     string
-		input    string
-		expected string
-	}{
-		{
-			name:     "mixed case with spaces",
-			input:    "Security Alert Burndown",
-			expected: "security-alert-burndown",
-		},
-		{
-			name:     "underscores to hyphens",
-			input:    "security_alert_burndown",
-			expected: "security-alert-burndown",
-		},
-		{
-			name:     "special characters and extra hyphens",
-			input:    " security---alert@@burndown ",
-			expected: "security-alert-burndown",
-		},
-		{
-			name:     "empty string",
-			input:    "",
-			expected: "",
-		},
-		{
-			name:     "only whitespace",
-			input:    "   ",
-			expected: "",
-		},
-		{
-			name:     "only special characters",
-			input:    "@@##$$",
-			expected: "",
-		},
-		{
-			name:     "already normalized",
-			input:    "campaign-name",
-			expected: "campaign-name",
-		},
-		{
-			name:     "numbers preserved",
-			input:    "Campaign 123 Test",
-			expected: "campaign-123-test",
-		},
-		{
-			name:     "leading and trailing hyphens",
-			input:    "---campaign---",
-			expected: "campaign",
-		},
-		{
-			name:     "unicode characters",
-			input:    "Campaña Tëst",
-			expected: "campa-a-t-st",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result := normalizeCampaignID(tt.input)
-			assert.Equal(t, tt.expected, result, "normalization should match expected output")
-		})
-	}
-}
diff --git a/pkg/campaign/interactive.go b/pkg/campaign/interactive.go
deleted file mode 100644
index 7d25fbac35a..00000000000
--- a/pkg/campaign/interactive.go
+++ /dev/null
@@ -1,511 +0,0 @@
-package campaign
-
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/charmbracelet/huh"
-	"github.com/githubnext/gh-aw/pkg/console"
-	"github.com/githubnext/gh-aw/pkg/logger"
-	"github.com/goccy/go-yaml"
-)
-
-var interactiveLog = logger.New("campaign:interactive")
-
-// InteractiveCampaignConfig holds the configuration for interactive campaign creation
-type InteractiveCampaignConfig struct {
-	ID             string
-	Name           string
-	Description    string
-	Scope          string // "current", "multiple", "org-wide"
-	ScopeSelectors []string
-	Workflows      []string
-	Owners         []string
-	RiskLevel      string
-	CreateProject  bool
-	ProjectOwner   string
-	LinkRepo       string
-	Force          bool
-}
-
-// RunInteractiveCampaignCreation runs an interactive wizard to create a campaign spec
-func RunInteractiveCampaignCreation(rootDir string, force bool, verbose bool) error {
-	interactiveLog.Print("Starting interactive campaign creation")
-
-	// Assert this function is not running in automated unit tests
-	if os.Getenv("GO_TEST_MODE") == "true" || os.Getenv("CI") != "" {
-		return fmt.Errorf("interactive mode cannot be used in automated tests or CI environments")
-	}
-
-	fmt.Fprintln(os.Stderr, "")
-	fmt.Fprintln(os.Stderr, console.FormatInfoMessage("🎯 Let's create your agentic campaign!"))
-	fmt.Fprintln(os.Stderr, "")
-
-	config := &InteractiveCampaignConfig{
-		Force: force,
-	}
-
-	// Step 1: Campaign ID
-	if err := promptForCampaignID(config); err != nil {
-		return err
-	}
-
-	// Step 2: Campaign objective/description
-	if err := promptForObjective(config); err != nil {
-		return err
-	}
-
-	// Step 3: Repository scope
-	if err := promptForRepositoryScope(config); err != nil {
-		return err
-	}
-
-	// Step 4: Workflow selection
-	if err := promptForWorkflows(config); err != nil {
-		return err
-	}
-
-	// Step 5: Owners/stakeholders
-	if err := promptForOwners(config); err != nil {
-		return err
-	}
-
-	// Step 6: Risk level
-	if err := promptForRiskLevel(config); err != nil {
-		return err
-	}
-
-	// Step 7: Project board creation
-	if err := promptForProjectCreation(config); err != nil {
-		return err
-	}
-
-	// Generate the campaign spec
-	if err := generateCampaignFromConfig(rootDir, config, verbose); err != nil {
-		return err
-	}
-
-	fmt.Fprintln(os.Stderr, "")
-	fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("✅ Campaign spec created successfully!"))
-	fmt.Fprintln(os.Stderr, "")
-	fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Next steps:"))
-	fmt.Fprintln(os.Stderr, "  1. Review and edit: .github/workflows/"+config.ID+".campaign.md")
-	fmt.Fprintln(os.Stderr, "  2. Compile the orchestrator: gh aw compile")
-	if config.CreateProject {
-		fmt.Fprintln(os.Stderr, "  3. Project board will be created during compilation")
-	}
-	fmt.Fprintln(os.Stderr, "")
-
-	return nil
-}
-
-func promptForCampaignID(config *InteractiveCampaignConfig) error {
-	var id string
-	form := huh.NewForm(
-		huh.NewGroup(
-			huh.NewInput().
-				Title("Campaign ID").
-				Description("Use lowercase letters, digits, and hyphens (e.g., security-q1-2025)").
-				Placeholder("my-campaign").
-				Value(&id).
-				Validate(func(s string) error {
-					s = strings.TrimSpace(s)
-					if s == "" {
-						return fmt.Errorf("campaign ID is required")
-					}
-					for _, ch := range s {
-						if (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' {
-							continue
-						}
-						return fmt.Errorf("campaign ID must use only lowercase letters, digits, and hyphens")
-					}
-					return nil
-				}),
-		),
-	)
-
-	if err := form.Run(); err != nil {
-		return fmt.Errorf("campaign ID input failed: %w", err)
-	}
-
-	config.ID = strings.TrimSpace(id)
-	config.Name = formatCampaignName(config.ID)
-	interactiveLog.Printf("Campaign ID: %s", config.ID)
-	return nil
-}
-
-func promptForObjective(config *InteractiveCampaignConfig) error {
-	var objective string
-	form := huh.NewForm(
-		huh.NewGroup(
-			huh.NewText().
-				Title("What is the main objective of this campaign?").
-				Description("Describe what you want to achieve (e.g., 'Reduce critical vulnerabilities across all repositories')").
-				Placeholder("Enter campaign objective...").
-				Value(&objective).
-				Validate(func(s string) error {
-					if strings.TrimSpace(s) == "" {
-						return fmt.Errorf("objective is required")
-					}
-					return nil
-				}),
-		),
-	)
-
-	if err := form.Run(); err != nil {
-		return fmt.Errorf("objective input failed: %w", err)
-	}
-
-	config.Description = strings.TrimSpace(objective)
-	interactiveLog.Printf("Campaign description: %s", config.Description)
-	return nil
-}
-
-func promptForRepositoryScope(config *InteractiveCampaignConfig) error {
-	var scopeType string
-	form := huh.NewForm(
-		huh.NewGroup(
-			huh.NewSelect[string]().
-				Title("What is the repository scope for this campaign?").
-				Options(
-					huh.NewOption("Current repository only", "current"),
-					huh.NewOption("Specific repositories", "multiple"),
-					huh.NewOption("Organization-wide", "org-wide"),
-				).
-				Value(&scopeType),
-		),
-	)
-
-	if err := form.Run(); err != nil {
-		return fmt.Errorf("scope selection failed: %w", err)
-	}
-
-	config.Scope = scopeType
-
-	switch scopeType {
-	case "current":
-		currentRepo, err := getCurrentRepository()
-		if err != nil {
-			interactiveLog.Printf("Warning: could not determine current repository for scope: %v", err)
-			break
-		}
-		config.ScopeSelectors = []string{currentRepo}
-	case "multiple":
-		var reposInput string
-		reposForm := huh.NewForm(
-			huh.NewGroup(
-				huh.NewText().
-					Title("Allowed repositories").
-					Description("Enter repositories this campaign can operate on (comma-separated, e.g., 'owner/repo1, owner/repo2')").
-					Placeholder("owner/repo1, owner/repo2").
-					Value(&reposInput).
-					Validate(func(s string) error {
-						if strings.TrimSpace(s) == "" {
-							return fmt.Errorf("at least one repository is required")
-						}
-						return nil
-					}),
-			),
-		)
-
-		if err := reposForm.Run(); err != nil {
-			return fmt.Errorf("allowed repos input failed: %w", err)
-		}
-
-		repos := strings.Split(reposInput, ",")
-		for _, repo := range repos {
-			repo = strings.TrimSpace(repo)
-			if repo != "" {
-				config.ScopeSelectors = append(config.ScopeSelectors, repo)
-			}
-		}
-	case "org-wide":
-		var orgsInput string
-		orgsForm := huh.NewForm(
-			huh.NewGroup(
-				huh.NewInput().
-					Title("Organization name").
-					Description("Enter the organization name (e.g., 'myorg')").
-					Placeholder("myorg").
-					Value(&orgsInput).
-					Validate(func(s string) error {
-						if strings.TrimSpace(s) == "" {
-							return fmt.Errorf("organization name is required")
-						}
-						return nil
-					}),
-			),
-		)
-
-		if err := orgsForm.Run(); err != nil {
-			return fmt.Errorf("allowed org input failed: %w", err)
-		}
-
-		org := strings.TrimSpace(orgsInput)
-		if org != "" {
-			config.ScopeSelectors = append(config.ScopeSelectors, "org:"+org)
-		}
-	}
-
-	interactiveLog.Printf("Scope: %s, selectors: %v", config.Scope, config.ScopeSelectors)
-	return nil
-}
-
-func promptForWorkflows(config *InteractiveCampaignConfig) error {
-	var workflowsInput string
-	form := huh.NewForm(
-		huh.NewGroup(
-			huh.NewText().
-				Title("Which workflows should this campaign use?").
-				Description("Enter workflow names (comma-separated, e.g., 'vulnerability-scanner, dependency-updater'). Leave empty to configure later.").
-				Placeholder("workflow-1, workflow-2").
-				Value(&workflowsInput),
-		),
-	)
-
-	if err := form.Run(); err != nil {
-		return fmt.Errorf("workflows input failed: %w", err)
-	}
-
-	if strings.TrimSpace(workflowsInput) != "" {
-		workflows := strings.Split(workflowsInput, ",")
-		for _, workflow := range workflows {
-			workflow = strings.TrimSpace(workflow)
-			if workflow != "" {
-				config.Workflows = append(config.Workflows, workflow)
-			}
-		}
-	}
-
-	interactiveLog.Printf("Workflows: %v", config.Workflows)
-	return nil
-}
-
-func promptForOwners(config *InteractiveCampaignConfig) error {
-	var ownersInput string
-	form := huh.NewForm(
-		huh.NewGroup(
-			huh.NewInput().
-				Title("Who are the campaign owners?").
-				Description("Enter GitHub usernames (comma-separated, with @ prefix, e.g., '@alice, @bob'). Leave empty to configure later.").
-				Placeholder("@username").
-				Value(&ownersInput),
-		),
-	)
-
-	if err := form.Run(); err != nil {
-		return fmt.Errorf("owners input failed: %w", err)
-	}
-
-	if strings.TrimSpace(ownersInput) != "" {
-		owners := strings.Split(ownersInput, ",")
-		for _, owner := range owners {
-			trimmedOwner := strings.TrimSpace(owner)
-			if trimmedOwner != "" {
-				if !strings.HasPrefix(trimmedOwner, "@") {
-					trimmedOwner = "@" + trimmedOwner
-				}
-				config.Owners = append(config.Owners, trimmedOwner)
-			}
-		}
-	}
-
-	interactiveLog.Printf("Owners: %v", config.Owners)
-	return nil
-}
-
-func promptForRiskLevel(config *InteractiveCampaignConfig) error {
-	var riskLevel string
-	form := huh.NewForm(
-		huh.NewGroup(
-			huh.NewSelect[string]().
-				Title("What is the risk level for this campaign?").
-				Description("Risk level determines approval requirements").
-				Options(
-					huh.NewOption("Low - Read-only, single repo", "low"),
-					huh.NewOption("Medium - Cross-repo, automated changes", "medium"),
-					huh.NewOption("High - Multi-repo, sensitive data, breaking changes", "high"),
-				).
-				Value(&riskLevel),
-		),
-	)
-
-	if err := form.Run(); err != nil {
-		return fmt.Errorf("risk level selection failed: %w", err)
-	}
-
-	config.RiskLevel = riskLevel
-	interactiveLog.Printf("Risk level: %s", config.RiskLevel)
-	return nil
-}
-
-func promptForProjectCreation(config *InteractiveCampaignConfig) error {
-	var createProject bool
-	form := huh.NewForm(
-		huh.NewGroup(
-			huh.NewConfirm().
-				Title("Create a GitHub Project board for this campaign?").
-				Description("This will set up project views and fields for tracking").
-				Value(&createProject),
-		),
-	)
-
-	if err := form.Run(); err != nil {
-		return fmt.Errorf("project creation prompt failed: %w", err)
-	}
-
-	config.CreateProject = createProject
-
-	if createProject {
-		var projectOwner string
-		ownerForm := huh.NewForm(
-			huh.NewGroup(
-				huh.NewInput().
-					Title("Project owner").
-					Description("GitHub organization or user (use '@me' for personal projects)").
-					Placeholder("@me or org-name").
-					Value(&projectOwner).
-					Validate(func(s string) error {
-						if strings.TrimSpace(s) == "" {
-							return fmt.Errorf("project owner is required")
-						}
-						return nil
-					}),
-			),
-		)
-
-		if err := ownerForm.Run(); err != nil {
-			return fmt.Errorf("project owner input failed: %w", err)
-		}
-
-		config.ProjectOwner = strings.TrimSpace(projectOwner)
-	}
-
-	interactiveLog.Printf("Create project: %v, owner: %s", config.CreateProject, config.ProjectOwner)
-	return nil
-}
-
-func generateCampaignFromConfig(rootDir string, config *InteractiveCampaignConfig, verbose bool) error {
-	interactiveLog.Print("Generating campaign spec from interactive config")
-
-	workflowsDir := filepath.Join(rootDir, ".github", "workflows")
-	if err := os.MkdirAll(workflowsDir, 0o755); err != nil {
-		return fmt.Errorf("failed to create .github/workflows directory: %w", err)
-	}
-
-	fileName := config.ID + ".campaign.md"
-	fullPath := filepath.Join(workflowsDir, fileName)
-	relPath := filepath.ToSlash(filepath.Join(".github", "workflows", fileName))
-
-	if _, err := os.Stat(fullPath); err == nil && !config.Force {
-		return fmt.Errorf("campaign spec already exists at %s (use --force to overwrite)", relPath)
-	}
-
-	// Build the spec
-	spec := CampaignSpec{
-		ID:          config.ID,
-		Name:        config.Name,
-		Description: config.Description,
-		ProjectURL:  "https://github.com/orgs/ORG/projects/1", // Placeholder
-		Version:     "v1",
-		State:       "planned",
-		Workflows:   config.Workflows,
-		Scope:       config.ScopeSelectors,
-		Owners:      config.Owners,
-		RiskLevel:   config.RiskLevel,
-		MemoryPaths: []string{"memory/campaigns/" + config.ID + "/**"},
-		MetricsGlob: "memory/campaigns/" + config.ID + "/metrics/*.json",
-		CursorGlob:  "memory/campaigns/" + config.ID + "/cursor.json",
-		Governance: &CampaignGovernancePolicy{
-			MaxNewItemsPerRun:       25,
-			MaxDiscoveryItemsPerRun: 200,
-			MaxDiscoveryPagesPerRun: 10,
-			OptOutLabels:            []string{"no-campaign", "no-bot"},
-			DoNotDowngradeDoneItems: boolPtr(true),
-			MaxProjectUpdatesPerRun: 10,
-			MaxCommentsPerRun:       10,
-		},
-	}
-
-	data, err := yaml.Marshal(&spec)
-	if err != nil {
-		return fmt.Errorf("failed to marshal campaign spec: %w", err)
-	}
-
-	var buf strings.Builder
-	buf.WriteString("---\n")
-	buf.Write(data)
-	buf.WriteString("---\n\n")
-	buf.WriteString("# " + config.Name + "\n\n")
-	buf.WriteString(config.Description + "\n\n")
-
-	buf.WriteString("## Workflows\n\n")
-	if len(config.Workflows) > 0 {
-		for _, workflow := range config.Workflows {
-			buf.WriteString("### " + workflow + "\n")
-			buf.WriteString("Description of what this workflow does in the context of this campaign.\n\n")
-		}
-	} else {
-		buf.WriteString("Add workflow descriptions here.\n\n")
-	}
-
-	buf.WriteString("## Timeline\n\n")
-	buf.WriteString("- **Start**: TBD\n")
-	buf.WriteString("- **Target**: Ongoing\n\n")
-
-	buf.WriteString("## Governance\n\n")
-	buf.WriteString("Describe risk mitigation, approval process, and stakeholder communication.\n")
-
-	// Use restrictive permissions (0644) for proper git tracking
-	if err := os.WriteFile(fullPath, []byte(buf.String()), 0o644); err != nil {
-		return fmt.Errorf("failed to write campaign spec file '%s': %w", relPath, err)
-	}
-
-	fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Created campaign spec at %s", relPath)))
-
-	// Create project if requested
-	if config.CreateProject {
-		fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Creating GitHub Project..."))
-
-		projectConfig := ProjectCreationConfig{
-			CampaignID:   config.ID,
-			CampaignName: config.Name,
-			Owner:        config.ProjectOwner,
-			LinkRepo:     config.LinkRepo,
-			NoLinkRepo:   false,
-			Verbose:      verbose,
-		}
-
-		result, err := CreateCampaignProject(projectConfig)
-		if err != nil {
-			return fmt.Errorf("failed to create project: %w", err)
-		}
-
-		fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Created project: %s", result.ProjectURL)))
-
-		// Update the spec file with the project URL
-		if err := UpdateSpecWithProjectURL(fullPath, result.ProjectURL); err != nil {
-			return fmt.Errorf("failed to update spec with project URL: %w", err)
-		}
-
-		fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Updated campaign spec with project URL"))
-	}
-
-	return nil
-}
-
-func formatCampaignName(id string) string {
-	name := strings.ReplaceAll(id, "-", " ")
-	if name != "" {
-		words := strings.Fields(name)
-		for i, word := range words {
-			if len(word) > 0 {
-				words[i] = strings.ToUpper(word[:1]) + word[1:]
-			}
-		}
-		name = strings.Join(words, " ")
-	}
-	return name
-}
diff --git a/pkg/campaign/interactive_test.go b/pkg/campaign/interactive_test.go
deleted file mode 100644
index 738c65ef5ff..00000000000
--- a/pkg/campaign/interactive_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"os"
-	"testing"
-)
-
-func TestRunInteractiveCampaignCreation_SkipInAutomation(t *testing.T) {
-	// Set GO_TEST_MODE to simulate test environment
-	os.Setenv("GO_TEST_MODE", "true")
-	defer os.Unsetenv("GO_TEST_MODE")
-
-	tmpDir := t.TempDir()
-
-	err := RunInteractiveCampaignCreation(tmpDir, false, false)
-	if err == nil {
-		t.Error("Expected error when running interactive mode in test environment")
-	}
-
-	expectedMsg := "interactive mode cannot be used in automated tests or CI environments"
-	if !containsString(err.Error(), expectedMsg) {
-		t.Errorf("Expected error containing %q, got: %v", expectedMsg, err)
-	}
-}
-
-func TestFormatCampaignName(t *testing.T) {
-	tests := []struct {
-		input    string
-		expected string
-	}{
-		{"security-q1-2025", "Security Q1 2025"},
-		{"my-test-campaign", "My Test Campaign"},
-		{"single", "Single"},
-		{"", ""},
-		{"already-capitalized", "Already Capitalized"},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.input, func(t *testing.T) {
-			result := formatCampaignName(tt.input)
-			if result != tt.expected {
-				t.Errorf("formatCampaignName(%q) = %q, want %q", tt.input, result, tt.expected)
-			}
-		})
-	}
-}
-
-func containsString(s, substr string) bool {
-	return len(s) > 0 && len(substr) > 0 && (s == substr || (len(s) >= len(substr) && findSubstring(s, substr)))
-}
-
-func findSubstring(s, substr string) bool {
-	for i := 0; i <= len(s)-len(substr); i++ {
-		if s[i:i+len(substr)] == substr {
-			return true
-		}
-	}
-	return false
-}
diff --git a/pkg/campaign/loader.go b/pkg/campaign/loader.go
deleted file mode 100644
index 11851e34be3..00000000000
--- a/pkg/campaign/loader.go
+++ /dev/null
@@ -1,282 +0,0 @@
-package campaign
-
-import (
-	"errors"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/cli/go-gh/v2/pkg/repository"
-	"github.com/githubnext/gh-aw/pkg/logger"
-	"github.com/githubnext/gh-aw/pkg/parser"
-	"github.com/goccy/go-yaml"
-)
-
-var log = logger.New("campaign:loader")
-
-// LoadSpecs scans the repository for campaign spec files and returns
-// a slice of CampaignSpec. Campaign specs are stored as .campaign.md files
-// in .github/workflows/. If the workflows directory does not exist, it
-// returns an empty slice and no error.
-func LoadSpecs(rootDir string) ([]CampaignSpec, error) {
-	log.Printf("Loading campaign specs from rootDir=%s", rootDir)
-
-	workflowsDir := filepath.Join(rootDir, ".github", "workflows")
-	entries, err := os.ReadDir(workflowsDir)
-	if err != nil {
-		if errors.Is(err, os.ErrNotExist) {
-			log.Print("No .github/workflows directory found; returning empty list")
-			return []CampaignSpec{}, nil
-		}
-		return nil, fmt.Errorf("failed to read .github/workflows directory '%s': %w", workflowsDir, err)
-	}
-
-	var specs []CampaignSpec
-
-	for _, entry := range entries {
-		if entry.IsDir() {
-			continue
-		}
-
-		name := entry.Name()
-		if !strings.HasSuffix(name, ".campaign.md") {
-			continue
-		}
-
-		fullPath := filepath.Join(workflowsDir, name)
-		log.Printf("Found campaign spec file: %s", fullPath)
-
-		data, err := os.ReadFile(fullPath)
-		if err != nil {
-			return nil, fmt.Errorf("failed to read campaign spec '%s': %w", fullPath, err)
-		}
-
-		// Use parser package's frontmatter extraction helper
-		result, err := parser.ExtractFrontmatterFromContent(string(data))
-		if err != nil {
-			return nil, fmt.Errorf("failed to parse campaign spec frontmatter '%s': %w", fullPath, err)
-		}
-
-		if len(result.Frontmatter) == 0 {
-			return nil, fmt.Errorf("campaign spec '%s' must start with YAML frontmatter delimited by '---'", filepath.ToSlash(filepath.Join(".github", "workflows", name)))
-		}
-
-		// Marshal frontmatter map to YAML and unmarshal to CampaignSpec
-		frontmatterYAML, err := yaml.Marshal(result.Frontmatter)
-		if err != nil {
-			return nil, fmt.Errorf("failed to marshal frontmatter for '%s': %w", fullPath, err)
-		}
-
-		var spec CampaignSpec
-		if err := yaml.Unmarshal(frontmatterYAML, &spec); err != nil {
-			return nil, fmt.Errorf("failed to parse campaign spec frontmatter '%s': %w", fullPath, err)
-		}
-
-		if strings.TrimSpace(spec.ID) == "" {
-			base := strings.TrimSuffix(name, ".campaign.md")
-			spec.ID = base
-		}
-
-		if strings.TrimSpace(spec.Name) == "" {
-			spec.Name = spec.ID
-		}
-
-		// Default state to "active" if not specified
-		if strings.TrimSpace(spec.State) == "" {
-			spec.State = "active"
-			log.Printf("Defaulted state to 'active' for campaign '%s'", spec.ID)
-		}
-
-		// Default tracker-label based on campaign ID if not specified
-		if strings.TrimSpace(spec.TrackerLabel) == "" {
-			spec.TrackerLabel = fmt.Sprintf("z_campaign_%s", spec.ID)
-			log.Printf("Defaulted tracker-label to '%s' for campaign '%s'", spec.TrackerLabel, spec.ID)
-		}
-
-		// Default memory-paths based on campaign ID if not specified
-		if len(spec.MemoryPaths) == 0 {
-			spec.MemoryPaths = []string{fmt.Sprintf("memory/campaigns/%s/**", spec.ID)}
-			log.Printf("Defaulted memory-paths to '%s' for campaign '%s'", spec.MemoryPaths[0], spec.ID)
-		}
-
-		// Default metrics-glob based on campaign ID if not specified
-		if strings.TrimSpace(spec.MetricsGlob) == "" {
-			spec.MetricsGlob = fmt.Sprintf("memory/campaigns/%s/metrics/*.json", spec.ID)
-			log.Printf("Defaulted metrics-glob to '%s' for campaign '%s'", spec.MetricsGlob, spec.ID)
-		}
-
-		// Default cursor-glob based on campaign ID if not specified
-		if strings.TrimSpace(spec.CursorGlob) == "" {
-			spec.CursorGlob = fmt.Sprintf("memory/campaigns/%s/cursor.json", spec.ID)
-			log.Printf("Defaulted cursor-glob to '%s' for campaign '%s'", spec.CursorGlob, spec.ID)
-		}
-
-		// Default scope to current repository if not specified
-		if len(spec.Scope) == 0 {
-			currentRepo, err := getCurrentRepository()
-			if err != nil {
-				log.Printf("Warning: Could not determine current repository for campaign '%s': %v. Campaign will require explicit scope.", spec.ID, err)
-			} else {
-				spec.Scope = []string{currentRepo}
-				log.Printf("Defaulted scope to current repository for campaign '%s': %s", spec.ID, currentRepo)
-			}
-		}
-
-		spec.ConfigPath = filepath.ToSlash(filepath.Join(".github", "workflows", name))
-		specs = append(specs, spec)
-	}
-
-	log.Printf("Loaded %d campaign specs", len(specs))
-	return specs, nil
-}
-
-// FilterSpecs filters campaigns by a simple substring match on ID or
-// Name (case-insensitive). When pattern is empty, all campaigns are returned.
-func FilterSpecs(specs []CampaignSpec, pattern string) []CampaignSpec {
-	if pattern == "" {
-		return specs
-	}
-
-	var filtered []CampaignSpec
-	lowerPattern := strings.ToLower(pattern)
-
-	for _, spec := range specs {
-		if strings.Contains(strings.ToLower(spec.ID), lowerPattern) || strings.Contains(strings.ToLower(spec.Name), lowerPattern) {
-			filtered = append(filtered, spec)
-		}
-	}
-
-	return filtered
-}
-
-// CreateSpecSkeleton creates a new campaign spec YAML file under
-// .github/workflows/ with a minimal skeleton definition. It returns the
-// relative file path created.
-func CreateSpecSkeleton(rootDir, id string, force bool) (string, error) {
-	id = strings.TrimSpace(id)
-	if id == "" {
-		return "", fmt.Errorf("campaign id is required")
-	}
-
-	// Reuse the same simple rules as ValidateSpec for IDs
-	for _, ch := range id {
-		if (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' {
-			continue
-		}
-		return "", fmt.Errorf("campaign id must use only lowercase letters, digits, and hyphens (%s)", id)
-	}
-
-	workflowsDir := filepath.Join(rootDir, ".github", "workflows")
-	if err := os.MkdirAll(workflowsDir, 0o755); err != nil {
-		return "", fmt.Errorf("failed to create .github/workflows directory: %w", err)
-	}
-
-	fileName := id + ".campaign.md"
-	fullPath := filepath.Join(workflowsDir, fileName)
-	relPath := filepath.ToSlash(filepath.Join(".github", "workflows", fileName))
-
-	if _, err := os.Stat(fullPath); err == nil && !force {
-		return "", fmt.Errorf("campaign spec already exists at %s (use --force to overwrite)", relPath)
-	}
-
-	name := strings.ReplaceAll(id, "-", " ")
-	if name != "" {
-		first := strings.ToUpper(name[:1])
-		if len(name) > 1 {
-			name = first + name[1:]
-		} else {
-			name = first
-		}
-	}
-
-	spec := CampaignSpec{
-		ID:          id,
-		Name:        name,
-		ProjectURL:  "https://github.com/orgs/ORG/projects/1",
-		Version:     "v1",
-		State:       "planned",
-		MemoryPaths: []string{"memory/campaigns/" + id + "/**"},
-		MetricsGlob: "memory/campaigns/" + id + "/metrics/*.json",
-		CursorGlob:  "memory/campaigns/" + id + "/cursor.json",
-		Governance: &CampaignGovernancePolicy{
-			MaxNewItemsPerRun:       25,
-			MaxDiscoveryItemsPerRun: 200,
-			MaxDiscoveryPagesPerRun: 10,
-			OptOutLabels:            []string{"no-campaign", "no-bot"},
-			DoNotDowngradeDoneItems: boolPtr(true),
-			MaxProjectUpdatesPerRun: 10,
-			MaxCommentsPerRun:       10,
-		},
-	}
-
-	data, err := yaml.Marshal(&spec)
-	if err != nil {
-		return "", fmt.Errorf("failed to marshal campaign spec: %w", err)
-	}
-
-	var buf strings.Builder
-	buf.WriteString("---\n")
-	buf.Write(data)
-	buf.WriteString("---\n\n")
-	if name != "" {
-		buf.WriteString("# " + name + "\n\n")
-	} else {
-		buf.WriteString("# " + id + "\n\n")
-	}
-	buf.WriteString("Describe this campaign's goals, guardrails, stakeholders, and playbook.\n\n")
-	buf.WriteString("## Quick Start\n\n")
-	buf.WriteString("By default, this campaign will target the current repository. To target additional repositories:\n\n")
-	buf.WriteString("1. **Add scope** (optional): Specify repositories and/or organizations to target\n")
-	buf.WriteString("2. **Define workflows**: List workflows to execute (e.g., `vulnerability-scanner`)\n")
-	buf.WriteString("3. **Add narrative context**: Define campaign goals, workflows, and timeline in the markdown body\n")
-	buf.WriteString("4. **Set owners**: Specify who is responsible for this campaign\n")
-	buf.WriteString("5. **Compile**: Run `gh aw compile` to generate the orchestrator\n\n")
-	buf.WriteString("## Example Configuration\n\n")
-	buf.WriteString("```yaml\n")
-	buf.WriteString("# Add to the frontmatter above:\n")
-	buf.WriteString("\n")
-	buf.WriteString("# Optional: Target specific repositories and/or organizations (defaults to current repo)\n")
-	buf.WriteString("# scope:\n")
-	buf.WriteString("#   - myorg/backend\n")
-	buf.WriteString("#   - myorg/frontend\n")
-	buf.WriteString("#   - org:myorg\n")
-	buf.WriteString("\n")
-	buf.WriteString("# Campaigns with workflows MUST be scoped via scope\n")
-	buf.WriteString("\n")
-	buf.WriteString("workflows:\n")
-	buf.WriteString("  - vulnerability-scanner\n")
-	buf.WriteString("  - dependency-updater\n")
-	buf.WriteString("owners:\n")
-	buf.WriteString("  - @security-team\n")
-	buf.WriteString("```\n")
-
-	// Use restrictive permissions (0644) for proper git tracking
-	if err := os.WriteFile(fullPath, []byte(buf.String()), 0o644); err != nil {
-		return "", fmt.Errorf("failed to write campaign spec file '%s': %w", relPath, err)
-	}
-
-	return relPath, nil
-}
-
-func boolPtr(v bool) *bool {
-	return &v
-}
-
-// getCurrentRepository gets the current repository from git context
-// This function mirrors the logic from pkg/workflow/repository_features_validation.go
-func getCurrentRepository() (string, error) {
-	// Use native repository.Current() to get the current repository
-	// This works when in a git repository with GitHub remote and respects GH_REPO
-	repo, err := repository.Current()
-	if err != nil {
-		return "", fmt.Errorf("failed to get current repository: %w", err)
-	}
-
-	// Validate that owner and name are not empty
-	if repo.Owner == "" || repo.Name == "" {
-		return "", fmt.Errorf("repository owner or name is empty (owner: %q, name: %q)", repo.Owner, repo.Name)
-	}
-
-	return fmt.Sprintf("%s/%s", repo.Owner, repo.Name), nil
-}
diff --git a/pkg/campaign/loader_test.go b/pkg/campaign/loader_test.go
deleted file mode 100644
index 8974ae673f3..00000000000
--- a/pkg/campaign/loader_test.go
+++ /dev/null
@@ -1,242 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-)
-
-func TestLoadSpecs_EmptyDirectory(t *testing.T) {
-	// Create temporary directory
-	tmpDir := t.TempDir()
-	campaignsDir := filepath.Join(tmpDir, ".github", "workflows")
-	if err := os.MkdirAll(campaignsDir, 0755); err != nil {
-		t.Fatalf("Failed to create .github/workflows directory: %v", err)
-	}
-
-	specs, err := LoadSpecs(tmpDir)
-	if err != nil {
-		t.Fatalf("LoadSpecs failed: %v", err)
-	}
-
-	if len(specs) != 0 {
-		t.Errorf("Expected 0 specs in empty directory, got %d", len(specs))
-	}
-}
-
-func TestLoadSpecs_NonExistentDirectory(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	specs, err := LoadSpecs(tmpDir)
-	if err != nil {
-		t.Fatalf("LoadSpecs should not fail for non-existent .github/workflows directory: %v", err)
-	}
-
-	if len(specs) != 0 {
-		t.Errorf("Expected 0 specs when .github/workflows directory doesn't exist, got %d", len(specs))
-	}
-}
-
-func TestLoadSpecs_InvalidFrontmatter(t *testing.T) {
-	tmpDir := t.TempDir()
-	campaignsDir := filepath.Join(tmpDir, ".github", "workflows")
-	if err := os.MkdirAll(campaignsDir, 0755); err != nil {
-		t.Fatalf("Failed to create .github/workflows directory: %v", err)
-	}
-
-	// Create file with invalid frontmatter
-	invalidFile := filepath.Join(campaignsDir, "invalid.campaign.md")
-	content := `---
-id: test
-name: [invalid yaml here
----
-Test content`
-	if err := os.WriteFile(invalidFile, []byte(content), 0644); err != nil {
-		t.Fatalf("Failed to write test file: %v", err)
-	}
-
-	_, err := LoadSpecs(tmpDir)
-	if err == nil {
-		t.Fatalf("Expected error for invalid frontmatter, got nil")
-	}
-
-	if !strings.Contains(err.Error(), "failed to parse") {
-		t.Errorf("Expected parse error, got: %v", err)
-	}
-}
-
-func TestLoadSpecs_MissingFrontmatter(t *testing.T) {
-	tmpDir := t.TempDir()
-	campaignsDir := filepath.Join(tmpDir, ".github", "workflows")
-	if err := os.MkdirAll(campaignsDir, 0755); err != nil {
-		t.Fatalf("Failed to create .github/workflows directory: %v", err)
-	}
-
-	// Create file without frontmatter
-	noFrontmatterFile := filepath.Join(campaignsDir, "no-frontmatter.campaign.md")
-	content := `# Test Campaign
-
-This file has no frontmatter.`
-	if err := os.WriteFile(noFrontmatterFile, []byte(content), 0644); err != nil {
-		t.Fatalf("Failed to write test file: %v", err)
-	}
-
-	_, err := LoadSpecs(tmpDir)
-	if err == nil {
-		t.Fatalf("Expected error for missing frontmatter, got nil")
-	}
-
-	if !strings.Contains(err.Error(), "must start with YAML frontmatter") {
-		t.Errorf("Expected frontmatter error, got: %v", err)
-	}
-}
-
-func TestLoadSpecs_IDDefaults(t *testing.T) {
-	tmpDir := t.TempDir()
-	campaignsDir := filepath.Join(tmpDir, ".github", "workflows")
-	if err := os.MkdirAll(campaignsDir, 0755); err != nil {
-		t.Fatalf("Failed to create .github/workflows directory: %v", err)
-	}
-
-	// Create file without ID in frontmatter
-	testFile := filepath.Join(campaignsDir, "test-campaign.campaign.md")
-	content := `---
-name: Test Campaign
-version: v1
----
-Test content`
-	if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
-		t.Fatalf("Failed to write test file: %v", err)
-	}
-
-	specs, err := LoadSpecs(tmpDir)
-	if err != nil {
-		t.Fatalf("LoadSpecs failed: %v", err)
-	}
-
-	if len(specs) != 1 {
-		t.Fatalf("Expected 1 spec, got %d", len(specs))
-	}
-
-	if specs[0].ID != "test-campaign" {
-		t.Errorf("Expected ID 'test-campaign' (derived from filename), got '%s'", specs[0].ID)
-	}
-}
-
-func TestLoadSpecs_NameDefaults(t *testing.T) {
-	tmpDir := t.TempDir()
-	campaignsDir := filepath.Join(tmpDir, ".github", "workflows")
-	if err := os.MkdirAll(campaignsDir, 0755); err != nil {
-		t.Fatalf("Failed to create .github/workflows directory: %v", err)
-	}
-
-	// Create file without name in frontmatter
-	testFile := filepath.Join(campaignsDir, "test-id.campaign.md")
-	content := `---
-id: test-id
-version: v1
----
-Test content`
-	if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
-		t.Fatalf("Failed to write test file: %v", err)
-	}
-
-	specs, err := LoadSpecs(tmpDir)
-	if err != nil {
-		t.Fatalf("LoadSpecs failed: %v", err)
-	}
-
-	if len(specs) != 1 {
-		t.Fatalf("Expected 1 spec, got %d", len(specs))
-	}
-
-	if specs[0].Name != "test-id" {
-		t.Errorf("Expected Name 'test-id' (derived from ID), got '%s'", specs[0].Name)
-	}
-}
-
-func TestLoadSpecs_ConfigPath(t *testing.T) {
-	tmpDir := t.TempDir()
-	campaignsDir := filepath.Join(tmpDir, ".github", "workflows")
-	if err := os.MkdirAll(campaignsDir, 0755); err != nil {
-		t.Fatalf("Failed to create .github/workflows directory: %v", err)
-	}
-
-	testFile := filepath.Join(campaignsDir, "test.campaign.md")
-	content := `---
-id: test
-name: Test
----
-Test content`
-	if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
-		t.Fatalf("Failed to write test file: %v", err)
-	}
-
-	specs, err := LoadSpecs(tmpDir)
-	if err != nil {
-		t.Fatalf("LoadSpecs failed: %v", err)
-	}
-
-	if len(specs) != 1 {
-		t.Fatalf("Expected 1 spec, got %d", len(specs))
-	}
-
-	expectedPath := ".github/workflows/test.campaign.md"
-	if specs[0].ConfigPath != expectedPath {
-		t.Errorf("Expected ConfigPath '%s', got '%s'", expectedPath, specs[0].ConfigPath)
-	}
-}
-
-func TestLoadSpecs_MultipleSpecs(t *testing.T) {
-	tmpDir := t.TempDir()
-	campaignsDir := filepath.Join(tmpDir, ".github", "workflows")
-	if err := os.MkdirAll(campaignsDir, 0755); err != nil {
-		t.Fatalf("Failed to create .github/workflows directory: %v", err)
-	}
-
-	// Create multiple campaign files
-	campaigns := []struct {
-		filename string
-		id       string
-	}{
-		{"campaign1.campaign.md", "campaign1"},
-		{"campaign2.campaign.md", "campaign2"},
-		{"campaign3.campaign.md", "campaign3"},
-	}
-
-	for _, c := range campaigns {
-		content := `---
-id: ` + c.id + `
-name: ` + c.id + `
----
-Content`
-		testFile := filepath.Join(campaignsDir, c.filename)
-		if err := os.WriteFile(testFile, []byte(content), 0644); err != nil {
-			t.Fatalf("Failed to write test file %s: %v", c.filename, err)
-		}
-	}
-
-	specs, err := LoadSpecs(tmpDir)
-	if err != nil {
-		t.Fatalf("LoadSpecs failed: %v", err)
-	}
-
-	if len(specs) != 3 {
-		t.Fatalf("Expected 3 specs, got %d", len(specs))
-	}
-
-	// Verify all IDs are present
-	foundIDs := make(map[string]bool)
-	for _, spec := range specs {
-		foundIDs[spec.ID] = true
-	}
-
-	for _, c := range campaigns {
-		if !foundIDs[c.id] {
-			t.Errorf("Expected to find campaign with ID '%s'", c.id)
-		}
-	}
-}
diff --git a/pkg/campaign/metrics_snapshot_test.go b/pkg/campaign/metrics_snapshot_test.go
deleted file mode 100644
index c0ffcbec94d..00000000000
--- a/pkg/campaign/metrics_snapshot_test.go
+++ /dev/null
@@ -1,224 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"encoding/json"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-// TestCampaignMetricsSnapshot_JSONMarshaling verifies that required fields
-// are always present in the JSON output, even when they have zero values.
-// This ensures compatibility with push_repo_memory.cjs validation which
-// requires tasks_total and tasks_completed to be present (not undefined).
-func TestCampaignMetricsSnapshot_JSONMarshaling(t *testing.T) {
-	tests := []struct {
-		name           string
-		snapshot       CampaignMetricsSnapshot
-		expectedFields []string
-		omittedFields  []string
-	}{
-		{
-			name: "all fields populated",
-			snapshot: CampaignMetricsSnapshot{
-				Date:                "2025-01-05",
-				CampaignID:          "test-campaign",
-				TasksTotal:          100,
-				TasksCompleted:      50,
-				TasksInProgress:     30,
-				TasksBlocked:        5,
-				VelocityPerDay:      7.5,
-				EstimatedCompletion: "2025-02-15",
-			},
-			expectedFields: []string{
-				"date", "campaign_id", "tasks_total", "tasks_completed",
-				"tasks_in_progress", "tasks_blocked", "velocity_per_day", "estimated_completion",
-			},
-			omittedFields: []string{},
-		},
-		{
-			name: "zero values for required fields (must be present)",
-			snapshot: CampaignMetricsSnapshot{
-				Date:           "2025-01-05",
-				CampaignID:     "test-campaign",
-				TasksTotal:     0, // Zero but must be present
-				TasksCompleted: 0, // Zero but must be present
-			},
-			expectedFields: []string{
-				"date", "campaign_id", "tasks_total", "tasks_completed",
-			},
-			omittedFields: []string{
-				"tasks_in_progress", "tasks_blocked", "velocity_per_day", "estimated_completion",
-			},
-		},
-		{
-			name: "only required fields",
-			snapshot: CampaignMetricsSnapshot{
-				Date:           "2025-01-05",
-				CampaignID:     "test-campaign",
-				TasksTotal:     10,
-				TasksCompleted: 5,
-			},
-			expectedFields: []string{
-				"date", "campaign_id", "tasks_total", "tasks_completed",
-			},
-			omittedFields: []string{
-				"tasks_in_progress", "tasks_blocked", "velocity_per_day", "estimated_completion",
-			},
-		},
-		{
-			name: "zero velocity should be omitted",
-			snapshot: CampaignMetricsSnapshot{
-				Date:           "2025-01-05",
-				CampaignID:     "test-campaign",
-				TasksTotal:     10,
-				TasksCompleted: 5,
-				VelocityPerDay: 0.0, // Zero optional field should be omitted
-			},
-			expectedFields: []string{
-				"date", "campaign_id", "tasks_total", "tasks_completed",
-			},
-			omittedFields: []string{
-				"tasks_in_progress", "tasks_blocked", "velocity_per_day", "estimated_completion",
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			// Marshal to JSON
-			jsonBytes, err := json.Marshal(tt.snapshot)
-			require.NoError(t, err, "Failed to marshal snapshot")
-
-			// Unmarshal to map to check presence of fields
-			var result map[string]interface{}
-			err = json.Unmarshal(jsonBytes, &result)
-			require.NoError(t, err, "Failed to unmarshal JSON")
-
-			// Verify expected fields are present
-			for _, field := range tt.expectedFields {
-				assert.Contains(t, result, field, "Expected field %q to be present in JSON", field)
-			}
-
-			// Verify omitted fields are not present
-			for _, field := range tt.omittedFields {
-				assert.NotContains(t, result, field, "Expected field %q to be omitted from JSON", field)
-			}
-
-			// Specifically verify required integer fields are present even when zero
-			if tt.snapshot.TasksTotal == 0 {
-				assert.Contains(t, result, "tasks_total", "tasks_total must be present even when zero")
-			}
-			if tt.snapshot.TasksCompleted == 0 {
-				assert.Contains(t, result, "tasks_completed", "tasks_completed must be present even when zero")
-			}
-		})
-	}
-}
-
-// TestCampaignMetricsSnapshot_PushRepoMemoryValidation tests that the JSON
-// output is compatible with push_repo_memory.cjs validation rules.
-func TestCampaignMetricsSnapshot_PushRepoMemoryValidation(t *testing.T) {
-	// This test simulates what push_repo_memory.cjs expects:
-	// - campaign_id: required, non-empty string
-	// - date: required, non-empty string in YYYY-MM-DD format
-	// - tasks_total: required, must be present (not undefined), non-negative integer
-	// - tasks_completed: required, must be present (not undefined), non-negative integer
-
-	snapshot := CampaignMetricsSnapshot{
-		Date:           "2025-01-05",
-		CampaignID:     "file-size-reduction-project71",
-		TasksTotal:     0,
-		TasksCompleted: 0,
-	}
-
-	jsonBytes, err := json.Marshal(snapshot)
-	require.NoError(t, err, "Failed to marshal snapshot")
-
-	// Unmarshal to verify structure
-	var result map[string]interface{}
-	err = json.Unmarshal(jsonBytes, &result)
-	require.NoError(t, err, "Failed to unmarshal JSON")
-
-	// Validate required fields (matching push_repo_memory.cjs validation)
-	assert.Contains(t, result, "campaign_id", "campaign_id must be present")
-	assert.IsType(t, "", result["campaign_id"], "campaign_id must be a string")
-	assert.NotEmpty(t, result["campaign_id"], "campaign_id must not be empty")
-
-	assert.Contains(t, result, "date", "date must be present")
-	assert.IsType(t, "", result["date"], "date must be a string")
-	assert.NotEmpty(t, result["date"], "date must not be empty")
-
-	assert.Contains(t, result, "tasks_total", "tasks_total must be present (not undefined)")
-	assert.IsType(t, float64(0), result["tasks_total"], "tasks_total must be a number")
-	assert.GreaterOrEqual(t, result["tasks_total"].(float64), 0.0, "tasks_total must be non-negative")
-
-	assert.Contains(t, result, "tasks_completed", "tasks_completed must be present (not undefined)")
-	assert.IsType(t, float64(0), result["tasks_completed"], "tasks_completed must be a number")
-	assert.GreaterOrEqual(t, result["tasks_completed"].(float64), 0.0, "tasks_completed must be non-negative")
-}
-
-// TestCampaignMetricsSnapshot_RealWorldScenarios tests common real-world cases
-// that might trigger the push_repo_memory validation error.
-func TestCampaignMetricsSnapshot_RealWorldScenarios(t *testing.T) {
-	tests := []struct {
-		name        string
-		snapshot    CampaignMetricsSnapshot
-		description string
-	}{
-		{
-			name: "new campaign with no tasks yet",
-			snapshot: CampaignMetricsSnapshot{
-				Date:           "2025-01-05",
-				CampaignID:     "file-size-reduction-project71",
-				TasksTotal:     0,
-				TasksCompleted: 0,
-			},
-			description: "First run of orchestrator before any tasks discovered",
-		},
-		{
-			name: "campaign just started",
-			snapshot: CampaignMetricsSnapshot{
-				Date:           "2025-01-05",
-				CampaignID:     "file-size-reduction-project71",
-				TasksTotal:     10,
-				TasksCompleted: 0,
-			},
-			description: "Tasks discovered but none completed yet",
-		},
-		{
-			name: "campaign in progress",
-			snapshot: CampaignMetricsSnapshot{
-				Date:                "2025-01-05",
-				CampaignID:          "file-size-reduction-project71",
-				TasksTotal:          100,
-				TasksCompleted:      25,
-				TasksInProgress:     15,
-				TasksBlocked:        2,
-				VelocityPerDay:      3.5,
-				EstimatedCompletion: "2025-02-15",
-			},
-			description: "Active campaign with full metrics",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			jsonBytes, err := json.Marshal(tt.snapshot)
-			require.NoError(t, err, "Failed to marshal snapshot: %s", tt.description)
-
-			var result map[string]interface{}
-			err = json.Unmarshal(jsonBytes, &result)
-			require.NoError(t, err, "Failed to unmarshal JSON: %s", tt.description)
-
-			// Verify all required fields are present
-			assert.Contains(t, result, "campaign_id")
-			assert.Contains(t, result, "date")
-			assert.Contains(t, result, "tasks_total")
-			assert.Contains(t, result, "tasks_completed")
-		})
-	}
-}
diff --git a/pkg/campaign/orchestrator.go b/pkg/campaign/orchestrator.go
deleted file mode 100644
index cf979e10554..00000000000
--- a/pkg/campaign/orchestrator.go
+++ /dev/null
@@ -1,376 +0,0 @@
-package campaign
-
-import (
-	"fmt"
-	"strings"
-
-	"github.com/githubnext/gh-aw/pkg/logger"
-	"github.com/githubnext/gh-aw/pkg/workflow"
-	"github.com/goccy/go-yaml"
-)
-
-var orchestratorLog = logger.New("campaign:orchestrator")
-
-// convertStringsToAny converts a slice of strings to a slice of any
-func convertStringsToAny(strings []string) []any {
-	result := make([]any, len(strings))
-	for i, s := range strings {
-		result[i] = s
-	}
-	return result
-}
-
-// extractFileGlobPatterns extracts all file glob patterns from memory-paths or
-// metrics-glob configuration. These patterns are used for the file-glob filter in
-// repo-memory configuration to match files that the agent creates.
-//
-// For campaigns that use dated directory patterns (e.g., campaign-id-*/), this
-// function preserves all wildcard patterns from memory-paths to support multiple
-// directory structures (both dated and non-dated).
-//
-// Examples:
-//   - memory-paths: ["memory/campaigns/project64-*/**", "memory/campaigns/project64/**"]
-//     -> ["project64-*/**", "project64/**"]
-//   - memory-paths: ["memory/campaigns/project64-*/**"] -> ["project64-*/**"]
-//   - metrics-glob: "memory/campaigns/project64-*/metrics/*.json" -> ["project64-*/**"]
-//   - no patterns with wildcards -> ["project64/**"] (fallback to ID)
-func extractFileGlobPatterns(spec *CampaignSpec) []string {
-	var patterns []string
-
-	// Extract all patterns from memory-paths
-	for _, memPath := range spec.MemoryPaths {
-		// Remove "memory/campaigns/" prefix if present
-		pattern := strings.TrimPrefix(memPath, "memory/campaigns/")
-		// If pattern has both wildcards and slashes, it's a valid pattern
-		if strings.Contains(pattern, "*") && strings.Contains(pattern, "/") {
-			patterns = append(patterns, pattern)
-			orchestratorLog.Printf("Extracted file-glob pattern from memory-paths: %s", pattern)
-		}
-	}
-
-	// If we found patterns from memory-paths, return them
-	if len(patterns) > 0 {
-		return patterns
-	}
-
-	// Try to extract pattern from metrics-glob as fallback
-	if spec.MetricsGlob != "" {
-		pattern := strings.TrimPrefix(spec.MetricsGlob, "memory/campaigns/")
-		if strings.Contains(pattern, "*") {
-			// Extract the base directory pattern (everything before /metrics/ or first file-specific part)
-			if idx := strings.Index(pattern, "/metrics/"); idx > 0 {
-				basePattern := pattern[:idx] + "/**"
-				orchestratorLog.Printf("Extracted file-glob pattern from metrics-glob: %s", basePattern)
-				return []string{basePattern}
-			}
-		}
-	}
-
-	// Fallback to simple ID-based pattern
-	fallbackPattern := fmt.Sprintf("%s/**", spec.ID)
-	orchestratorLog.Printf("Using fallback file-glob pattern: %s", fallbackPattern)
-	return []string{fallbackPattern}
-}
-
-// BuildOrchestrator constructs a minimal agentic workflow representation for a
-// given CampaignSpec. The resulting WorkflowData is compiled via the standard
-// CompileWorkflowDataWithValidation pipeline, and the orchestratorPath
-// determines the emitted .lock.yml name.
-func BuildOrchestrator(spec *CampaignSpec, campaignFilePath string) (*workflow.WorkflowData, string) {
-	orchestratorLog.Printf("Building orchestrator for campaign: id=%s, file=%s", spec.ID, campaignFilePath)
-
-	// Derive orchestrator markdown path alongside the campaign spec, using a
-	// distinct suffix to avoid colliding with existing workflows. We use
-	// a `.campaign.g.md` suffix to make it clear that the file is generated
-	// from the corresponding `.campaign.md` spec.
-	base := strings.TrimSuffix(campaignFilePath, ".campaign.md")
-	orchestratorPath := base + ".campaign.g.md"
-	orchestratorLog.Printf("Generated orchestrator path: %s", orchestratorPath)
-
-	name := spec.Name
-	if strings.TrimSpace(name) == "" {
-		name = fmt.Sprintf("Campaign: %s", spec.ID)
-	}
-
-	description := spec.Description
-	if strings.TrimSpace(description) == "" {
-		description = fmt.Sprintf("Orchestrator workflow for campaign '%s'", spec.ID)
-	}
-
-	// Default triggers: hourly schedule plus manual workflow_dispatch.
-	onSection := "on:\n  schedule:\n    - cron: \"0 * * * *\"\n  workflow_dispatch:\n"
-
-	// Prevent overlapping runs. This reduces sustained automated traffic on GitHub's
-	// infrastructure by ensuring only one orchestrator run executes at a time per ref.
-	concurrency := fmt.Sprintf("concurrency:\n  group: \"campaign-%s-orchestrator-${{ github.ref }}\"\n  cancel-in-progress: false", spec.ID)
-
-	// Simple markdown body giving the agent context about the campaign.
-	markdownBuilder := &strings.Builder{}
-	markdownBuilder.WriteString("# Campaign Orchestrator\n\n")
-	fmt.Fprintf(markdownBuilder, "This workflow orchestrates the '%s' campaign.\n\n", name)
-
-	// Track whether we have any meaningful campaign details
-	hasDetails := false
-
-	if len(spec.Workflows) > 0 {
-		markdownBuilder.WriteString("- Associated workflows: ")
-		markdownBuilder.WriteString(strings.Join(spec.Workflows, ", "))
-		markdownBuilder.WriteString("\n")
-		hasDetails = true
-	}
-	if len(spec.MemoryPaths) > 0 {
-		markdownBuilder.WriteString("- Memory paths: ")
-		markdownBuilder.WriteString(strings.Join(spec.MemoryPaths, ", "))
-		markdownBuilder.WriteString("\n")
-		hasDetails = true
-	}
-	if spec.MetricsGlob != "" {
-		fmt.Fprintf(markdownBuilder, "- Metrics glob: `%s`\n", spec.MetricsGlob)
-		hasDetails = true
-	}
-	if spec.CursorGlob != "" {
-		fmt.Fprintf(markdownBuilder, "- Cursor glob: `%s`\n", spec.CursorGlob)
-		hasDetails = true
-	}
-	if strings.TrimSpace(spec.ProjectURL) != "" {
-		fmt.Fprintf(markdownBuilder, "- Project URL: %s\n", strings.TrimSpace(spec.ProjectURL))
-		hasDetails = true
-	}
-	if spec.Governance != nil {
-		if spec.Governance.MaxNewItemsPerRun > 0 {
-			fmt.Fprintf(markdownBuilder, "- Governance: max new items per run: %d\n", spec.Governance.MaxNewItemsPerRun)
-			hasDetails = true
-		}
-		if spec.Governance.MaxDiscoveryItemsPerRun > 0 {
-			fmt.Fprintf(markdownBuilder, "- Governance: max discovery items per run: %d\n", spec.Governance.MaxDiscoveryItemsPerRun)
-			hasDetails = true
-		}
-		if spec.Governance.MaxDiscoveryPagesPerRun > 0 {
-			fmt.Fprintf(markdownBuilder, "- Governance: max discovery pages per run: %d\n", spec.Governance.MaxDiscoveryPagesPerRun)
-			hasDetails = true
-		}
-		if len(spec.Governance.OptOutLabels) > 0 {
-			markdownBuilder.WriteString("- Governance: opt-out labels: ")
-			markdownBuilder.WriteString(strings.Join(spec.Governance.OptOutLabels, ", "))
-			markdownBuilder.WriteString("\n")
-			hasDetails = true
-		}
-		if spec.Governance.DoNotDowngradeDoneItems != nil {
-			fmt.Fprintf(markdownBuilder, "- Governance: do not downgrade done items: %t\n", *spec.Governance.DoNotDowngradeDoneItems)
-			hasDetails = true
-		}
-		if spec.Governance.MaxProjectUpdatesPerRun > 0 {
-			fmt.Fprintf(markdownBuilder, "- Governance: max project updates per run: %d\n", spec.Governance.MaxProjectUpdatesPerRun)
-			hasDetails = true
-		}
-		if spec.Governance.MaxCommentsPerRun > 0 {
-			fmt.Fprintf(markdownBuilder, "- Governance: max comments per run: %d\n", spec.Governance.MaxCommentsPerRun)
-			hasDetails = true
-		}
-	}
-
-	// Return nil if the campaign spec has no meaningful details for the prompt
-	if !hasDetails {
-		orchestratorLog.Printf("Campaign '%s' has no meaningful details, skipping orchestrator build", spec.ID)
-		return nil, ""
-	}
-
-	orchestratorLog.Printf("Campaign '%s' orchestrator includes: workflows=%d, memory_paths=%d",
-		spec.ID, len(spec.Workflows), len(spec.MemoryPaths))
-
-	// Render orchestrator instructions using templates
-	// All orchestrators follow the same system-agnostic rules with no conditional logic
-	promptData := CampaignPromptData{
-		CampaignID:   spec.ID,
-		CampaignName: spec.Name,
-		ProjectURL:   strings.TrimSpace(spec.ProjectURL),
-		CursorGlob:   strings.TrimSpace(spec.CursorGlob),
-		MetricsGlob:  strings.TrimSpace(spec.MetricsGlob),
-		Workflows:    spec.Workflows,
-	}
-	if spec.Governance != nil {
-		promptData.MaxDiscoveryItemsPerRun = spec.Governance.MaxDiscoveryItemsPerRun
-		promptData.MaxDiscoveryPagesPerRun = spec.Governance.MaxDiscoveryPagesPerRun
-		promptData.MaxProjectUpdatesPerRun = spec.Governance.MaxProjectUpdatesPerRun
-		promptData.MaxProjectCommentsPerRun = spec.Governance.MaxCommentsPerRun
-	}
-
-	// Add bootstrap configuration if present
-	if spec.Bootstrap != nil {
-		promptData.BootstrapMode = spec.Bootstrap.Mode
-		if spec.Bootstrap.SeederWorker != nil {
-			promptData.SeederWorkerID = spec.Bootstrap.SeederWorker.WorkflowID
-			// Convert payload map to JSON string for template rendering
-			if len(spec.Bootstrap.SeederWorker.Payload) > 0 {
-				payloadBytes, err := yaml.Marshal(spec.Bootstrap.SeederWorker.Payload)
-				if err == nil {
-					promptData.SeederPayload = string(payloadBytes)
-				}
-			}
-			promptData.SeederMaxItems = spec.Bootstrap.SeederWorker.MaxItems
-		}
-		if spec.Bootstrap.ProjectTodos != nil {
-			promptData.StatusField = spec.Bootstrap.ProjectTodos.StatusField
-			if promptData.StatusField == "" {
-				promptData.StatusField = "Status"
-			}
-			promptData.TodoValue = spec.Bootstrap.ProjectTodos.TodoValue
-			if promptData.TodoValue == "" {
-				promptData.TodoValue = "Todo"
-			}
-			promptData.TodoMaxItems = spec.Bootstrap.ProjectTodos.MaxItems
-			promptData.RequireFields = spec.Bootstrap.ProjectTodos.RequireFields
-		}
-	}
-
-	// Add worker metadata if present
-	if len(spec.Workers) > 0 {
-		promptData.WorkerMetadata = spec.Workers
-	}
-
-	// Render bootstrap instructions if bootstrap is configured
-	if spec.Bootstrap != nil && spec.Bootstrap.Mode != "" {
-		bootstrapInstructions := RenderBootstrapInstructions(promptData)
-		if bootstrapInstructions == "" {
-			orchestratorLog.Print("Warning: Failed to render bootstrap instructions, template may be missing")
-		} else {
-			AppendPromptSection(markdownBuilder, "BOOTSTRAP INSTRUCTIONS (PHASE 0)", bootstrapInstructions)
-			orchestratorLog.Printf("Campaign '%s' orchestrator includes bootstrap mode: %s", spec.ID, spec.Bootstrap.Mode)
-		}
-	}
-
-	// All campaigns include workflow execution capabilities
-	// The orchestrator can dispatch workflows and make decisions regardless of initial configuration
-	workflowExecution := RenderWorkflowExecution(promptData)
-	if workflowExecution == "" {
-		orchestratorLog.Print("Warning: Failed to render workflow execution instructions, template may be missing")
-	} else {
-		AppendPromptSection(markdownBuilder, "WORKFLOW EXECUTION (PHASE 0)", workflowExecution)
-		orchestratorLog.Printf("Campaign '%s' orchestrator includes workflow execution", spec.ID)
-	}
-
-	orchestratorInstructions := RenderOrchestratorInstructions(promptData)
-	if orchestratorInstructions == "" {
-		orchestratorLog.Print("Warning: Failed to render orchestrator instructions, template may be missing")
-	} else {
-		AppendPromptSection(markdownBuilder, "ORCHESTRATOR INSTRUCTIONS", orchestratorInstructions)
-	}
-
-	projectInstructions := RenderProjectUpdateInstructions(promptData)
-	if projectInstructions == "" {
-		orchestratorLog.Print("Warning: Failed to render project update instructions, template may be missing")
-	} else {
-		AppendPromptSection(markdownBuilder, "PROJECT UPDATE INSTRUCTIONS (AUTHORITATIVE FOR WRITES)", projectInstructions)
-	}
-
-	closingInstructions := RenderClosingInstructions()
-	if closingInstructions == "" {
-		orchestratorLog.Print("Warning: Failed to render closing instructions, template may be missing")
-	} else {
-		AppendPromptSection(markdownBuilder, "CLOSING INSTRUCTIONS (HIGHEST PRIORITY)", closingInstructions)
-	}
-
-	// Campaign orchestrators can dispatch workflows and perform limited Project operations.
-	// Project writes (update-project, create-project-status-update) are allowed to enable
-	// orchestrators to maintain campaign dashboards and status updates.
-	//
-	// Note: Campaign orchestrators intentionally omit explicit `permissions:` from
-	// the generated markdown; safe-output jobs have their own scoped permissions.
-	safeOutputs := &workflow.SafeOutputsConfig{}
-
-	// Configure dispatch-workflow for worker coordination
-	if len(spec.Workflows) > 0 {
-		dispatchWorkflowConfig := &workflow.DispatchWorkflowConfig{
-			BaseSafeOutputConfig: workflow.BaseSafeOutputConfig{Max: 3},
-			Workflows:            spec.Workflows,
-		}
-		safeOutputs.DispatchWorkflow = dispatchWorkflowConfig
-		orchestratorLog.Printf("Campaign orchestrator '%s' configured with dispatch_workflow for %d workflows", spec.ID, len(spec.Workflows))
-	}
-
-	// Configure update-project for campaign dashboard maintenance
-	maxProjectUpdates := 100 // default - increased from 10 to handle larger discovery sets
-	if spec.Governance != nil && spec.Governance.MaxProjectUpdatesPerRun > 0 {
-		maxProjectUpdates = spec.Governance.MaxProjectUpdatesPerRun
-	}
-	updateProjectConfig := &workflow.UpdateProjectConfig{
-		BaseSafeOutputConfig: workflow.BaseSafeOutputConfig{Max: maxProjectUpdates},
-	}
-	safeOutputs.UpdateProjects = updateProjectConfig
-	orchestratorLog.Printf("Campaign orchestrator '%s' configured with update-project (max: %d)", spec.ID, maxProjectUpdates)
-
-	// Configure create-project-status-update for campaign summaries
-	statusUpdateConfig := &workflow.CreateProjectStatusUpdateConfig{
-		BaseSafeOutputConfig: workflow.BaseSafeOutputConfig{Max: 1},
-	}
-	safeOutputs.CreateProjectStatusUpdates = statusUpdateConfig
-	orchestratorLog.Printf("Campaign orchestrator '%s' configured with create-project-status-update", spec.ID)
-
-	orchestratorLog.Printf("Campaign orchestrator '%s' built successfully with dispatch-workflow, update-project, and create-project-status-update safe outputs", spec.ID)
-
-	// Extract file-glob patterns from memory-paths or metrics-glob to support
-	// multiple directory structures (e.g., both dated "campaign-id-*/**" and non-dated "campaign-id/**")
-	fileGlobPatterns := extractFileGlobPatterns(spec)
-
-	// Determine engine to use (default to claude if not specified)
-	engineID := "claude"
-	if spec.Engine != "" {
-		engineID = spec.Engine
-		orchestratorLog.Printf("Campaign orchestrator '%s' using specified engine: %s", spec.ID, engineID)
-	} else {
-		orchestratorLog.Printf("Campaign orchestrator '%s' using default engine: %s", spec.ID, engineID)
-	}
-
-	// Configure GitHub MCP for discovery with budget enforcement
-	maxDiscoveryItems := 100
-	maxDiscoveryPages := 10
-	if spec.Governance != nil {
-		if spec.Governance.MaxDiscoveryItemsPerRun > 0 {
-			maxDiscoveryItems = spec.Governance.MaxDiscoveryItemsPerRun
-		}
-		if spec.Governance.MaxDiscoveryPagesPerRun > 0 {
-			maxDiscoveryPages = spec.Governance.MaxDiscoveryPagesPerRun
-		}
-	}
-
-	tools := map[string]any{
-		"github": map[string]any{
-			"toolsets": []string{"repos", "issues", "pull_requests"},
-			"mode":     "remote",
-		},
-		"repo-memory": []any{
-			map[string]any{
-				"id":          "campaigns",
-				"branch-name": "memory/campaigns",
-				"file-glob":   convertStringsToAny(fileGlobPatterns),
-				"campaign-id": spec.ID,
-			},
-		},
-		"bash": []any{"*"},
-		"edit": nil,
-	}
-	orchestratorLog.Printf("Campaign orchestrator '%s' configured with GitHub MCP (max items: %d, max pages: %d)",
-		spec.ID, maxDiscoveryItems, maxDiscoveryPages)
-
-	data := &workflow.WorkflowData{
-		Name:            name,
-		Description:     description,
-		MarkdownContent: markdownBuilder.String(),
-		On:              onSection,
-		Concurrency:     concurrency,
-		// Use a standard Ubuntu runner for the main agent job so the
-		// compiled orchestrator always has a valid runs-on value.
-		RunsOn: "runs-on: ubuntu-latest",
-		// Default roles match the workflow compiler's defaults so that
-		// membership checks have a non-empty GH_AW_REQUIRED_ROLES value.
-		Roles: []string{"admin", "maintainer", "write"},
-		// Set the engine configuration from campaign spec
-		EngineConfig: &workflow.EngineConfig{
-			ID: engineID,
-		},
-		Tools:       tools,
-		SafeOutputs: safeOutputs,
-	}
-
-	return data, orchestratorPath
-}
diff --git a/pkg/campaign/orchestrator_test.go b/pkg/campaign/orchestrator_test.go
deleted file mode 100644
index 1ba75cc99c1..00000000000
--- a/pkg/campaign/orchestrator_test.go
+++ /dev/null
@@ -1,452 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"strings"
-	"testing"
-)
-
-func TestBuildOrchestrator_BasicShape(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		spec := &CampaignSpec{
-			ID:          "go-file-size-reduction-project64",
-			Name:        "Campaign: Go File Size Reduction (Project 64)",
-			Description: "Reduce oversized non-test Go files under pkg/ to ≤800 LOC via tracked refactors.",
-			ProjectURL:  "https://github.com/orgs/githubnext/projects/64",
-			Workflows:   []string{"daily-file-diet"},
-			MemoryPaths: []string{"memory/campaigns/go-file-size-reduction-project64/**"},
-			MetricsGlob: "memory/campaigns/go-file-size-reduction-project64/metrics/*.json",
-		}
-
-		mdPath := ".github/workflows/go-file-size-reduction-project64.campaign.md"
-		data, orchestratorPath := BuildOrchestrator(spec, mdPath)
-
-		if orchestratorPath != ".github/workflows/go-file-size-reduction-project64.campaign.g.md" {
-			t.Fatalf("unexpected orchestrator path: got %q", orchestratorPath)
-		}
-
-		if data == nil {
-			t.Fatalf("expected non-nil WorkflowData")
-		}
-
-		if data.Name != spec.Name {
-			t.Fatalf("unexpected workflow name: got %q, want %q", data.Name, spec.Name)
-		}
-
-		if strings.TrimSpace(data.On) == "" || !strings.Contains(data.On, "workflow_dispatch") {
-			t.Fatalf("expected On section with workflow_dispatch trigger, got %q", data.On)
-		}
-
-		if !strings.Contains(data.On, "schedule:") || !strings.Contains(data.On, "0 * * * *") {
-			t.Fatalf("expected On section with hourly schedule cron, got %q", data.On)
-		}
-
-		if strings.TrimSpace(data.Concurrency) == "" || !strings.Contains(data.Concurrency, "concurrency:") {
-			t.Fatalf("expected workflow-level concurrency to be set, got %q", data.Concurrency)
-		}
-		if !strings.Contains(data.Concurrency, "campaign-go-file-size-reduction-project64-orchestrator") {
-			t.Fatalf("expected concurrency group to include campaign id, got %q", data.Concurrency)
-		}
-
-		if !strings.Contains(data.MarkdownContent, "Go File Size Reduction") {
-			t.Fatalf("expected markdown content to mention campaign name, got: %q", data.MarkdownContent)
-		}
-
-		// Campaign orchestrators intentionally omit permissions from the generated markdown.
-		// Job permissions are computed during compilation.
-		if strings.TrimSpace(data.Permissions) != "" {
-			t.Fatalf("expected no permissions in generated orchestrator data, got: %q", data.Permissions)
-		}
-	})
-}
-
-func TestBuildOrchestrator_CompletionInstructions(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		spec := &CampaignSpec{
-			ID:          "test-campaign",
-			Name:        "Test Campaign",
-			Description: "A test campaign",
-			ProjectURL:  "https://github.com/orgs/test/projects/1",
-			Workflows:   []string{"test-workflow"},
-		}
-
-		mdPath := ".github/workflows/test-campaign.campaign.md"
-		data, _ := BuildOrchestrator(spec, mdPath)
-
-		if data == nil {
-			t.Fatalf("expected non-nil WorkflowData")
-		}
-
-		// Governed invariant: completion is reported explicitly in Step 5.
-		expectedPhrases := []string{
-			"### Step 5 — Report",
-			"**Completion:**",
-		}
-		for _, expected := range expectedPhrases {
-			if !strings.Contains(data.MarkdownContent, expected) {
-				t.Errorf("expected markdown to contain %q, got: %q", expected, data.MarkdownContent)
-			}
-		}
-	})
-}
-
-func TestBuildOrchestrator_WorkflowsInDiscovery(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		spec := &CampaignSpec{
-			ID:          "test-campaign",
-			Name:        "Test Campaign",
-			Description: "A test campaign",
-			ProjectURL:  "https://github.com/orgs/test/projects/1",
-			Workflows: []string{
-				"daily-doc-updater",
-				"docs-noob-tester",
-				"daily-multi-device-docs-tester",
-			},
-		}
-
-		mdPath := ".github/workflows/test-campaign.campaign.md"
-		data, _ := BuildOrchestrator(spec, mdPath)
-
-		if data == nil {
-			t.Fatalf("expected non-nil WorkflowData")
-		}
-
-		// Verify each workflow is mentioned in the header list
-		for _, workflow := range spec.Workflows {
-			if !strings.Contains(data.MarkdownContent, workflow) {
-				t.Errorf("expected markdown to mention workflow %q, got: %q", workflow, data.MarkdownContent)
-			}
-		}
-
-		// Verify that discovery is now precomputed (not agent-side)
-		if !strings.Contains(data.MarkdownContent, "Discovery has been precomputed") {
-			t.Errorf("expected markdown to indicate precomputed discovery, got: %q", data.MarkdownContent)
-		}
-		if !strings.Contains(data.MarkdownContent, "./.gh-aw/campaign.discovery.json") {
-			t.Errorf("expected markdown to reference discovery manifest, got: %q", data.MarkdownContent)
-		}
-
-		// Verify that discovered results reference normalized items from manifest
-		if !strings.Contains(data.MarkdownContent, "Parse discovered items from the manifest") {
-			t.Errorf("expected markdown to mention parsing items from manifest, got: %q", data.MarkdownContent)
-		}
-	})
-}
-
-func TestBuildOrchestrator_DispatchOnlyPolicy(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		spec := &CampaignSpec{
-			ID:          "dispatch-only-campaign",
-			Name:        "Dispatch Only Campaign",
-			Description: "Campaign orchestrator with dispatch and project capabilities",
-			ProjectURL:  "https://github.com/orgs/test/projects/1",
-			Workflows:   []string{"worker-a", "worker-b"},
-			MemoryPaths: []string{"memory/campaigns/dispatch-only-campaign/**"},
-		}
-
-		mdPath := ".github/workflows/dispatch-only-campaign.campaign.md"
-		data, _ := BuildOrchestrator(spec, mdPath)
-		if data == nil {
-			t.Fatalf("expected non-nil WorkflowData")
-		}
-
-		if data.SafeOutputs == nil {
-			t.Fatalf("expected SafeOutputs to be set")
-		}
-		if data.SafeOutputs.DispatchWorkflow == nil {
-			t.Fatalf("expected dispatch-workflow safe output to be enabled")
-		}
-		if len(data.SafeOutputs.DispatchWorkflow.Workflows) != 2 {
-			t.Fatalf("expected 2 allowlisted workflows, got %d", len(data.SafeOutputs.DispatchWorkflow.Workflows))
-		}
-
-		// Orchestrators should have update-project and create-project-status-update for dashboard maintenance
-		if data.SafeOutputs.UpdateProjects == nil {
-			t.Fatalf("expected update-project safe output to be enabled")
-		}
-		if data.SafeOutputs.CreateProjectStatusUpdates == nil {
-			t.Fatalf("expected create-project-status-update safe output to be enabled")
-		}
-
-		// Orchestrators should NOT have create-issue or add-comment (workers handle those)
-		if data.SafeOutputs.CreateIssues != nil || data.SafeOutputs.AddComments != nil {
-			t.Fatalf("expected orchestrator to omit create-issue and add-comment safe outputs")
-		}
-
-		// Orchestrators should have GitHub tool access for discovery operations
-		if data.Tools == nil {
-			t.Fatalf("expected Tools to be configured")
-		}
-		if _, ok := data.Tools["github"]; !ok {
-			t.Fatalf("expected orchestrator to have github tools configured")
-		}
-	})
-}
-
-func TestBuildOrchestrator_TrackerIDMonitoring(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		spec := &CampaignSpec{
-			ID:          "test-campaign",
-			Name:        "Test Campaign",
-			Description: "A test campaign",
-			ProjectURL:  "https://github.com/orgs/test/projects/1",
-			Workflows:   []string{"daily-file-diet"},
-		}
-
-		mdPath := ".github/workflows/test-campaign.campaign.md"
-		data, _ := BuildOrchestrator(spec, mdPath)
-
-		if data == nil {
-			t.Fatalf("expected non-nil WorkflowData")
-		}
-
-		// Verify that the orchestrator uses manifest-based discovery (not agent-side search)
-		if !strings.Contains(data.MarkdownContent, "Correlation is explicit (tracker-id AND labels)") {
-			t.Errorf("expected markdown to mention tracker-id and labels correlation rule, got: %q", data.MarkdownContent)
-		}
-		if !strings.Contains(data.MarkdownContent, "Read the precomputed discovery manifest") {
-			t.Errorf("expected markdown to include manifest-based discovery instructions, got: %q", data.MarkdownContent)
-		}
-		if !strings.Contains(data.MarkdownContent, "./.gh-aw/campaign.discovery.json") {
-			t.Errorf("expected markdown to reference discovery manifest file, got: %q", data.MarkdownContent)
-		}
-
-		// Verify that orchestrator does NOT monitor workflow runs by file name
-		if strings.Contains(data.MarkdownContent, "list_workflow_runs") {
-			t.Errorf("expected markdown to NOT use list_workflow_runs for monitoring, but it does: %q", data.MarkdownContent)
-		}
-
-		if strings.Contains(data.MarkdownContent, ".lock.yml") {
-			t.Errorf("expected markdown to NOT reference .lock.yml files for monitoring, but it does: %q", data.MarkdownContent)
-		}
-
-		// Verify it follows system-agnostic rules
-		if !strings.Contains(data.MarkdownContent, "Core Principles") {
-			t.Errorf("expected markdown to contain core principles section, got: %q", data.MarkdownContent)
-		}
-
-		// Verify separation of steps (read / decide / dispatch / report)
-		if !strings.Contains(data.MarkdownContent, "Step 1") || !strings.Contains(data.MarkdownContent, "Read State") {
-			t.Errorf("expected markdown to contain Step 1 Read State, got: %q", data.MarkdownContent)
-		}
-		if !strings.Contains(data.MarkdownContent, "Step 2") || !strings.Contains(data.MarkdownContent, "Make Decisions") {
-			t.Errorf("expected markdown to contain Step 2 Make Decisions, got: %q", data.MarkdownContent)
-		}
-		if !strings.Contains(data.MarkdownContent, "Step 3") || !strings.Contains(data.MarkdownContent, "Dispatch Workers") {
-			t.Errorf("expected markdown to contain Step 3 Dispatch Workers, got: %q", data.MarkdownContent)
-		}
-		if !strings.Contains(data.MarkdownContent, "Step 4") || !strings.Contains(data.MarkdownContent, "Report") {
-			t.Errorf("expected markdown to contain Step 4 Report, got: %q", data.MarkdownContent)
-		}
-	})
-}
-
-func TestBuildOrchestrator_GovernanceDoesNotGrantWriteSafeOutputs(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		spec := &CampaignSpec{
-			ID:         "test-campaign",
-			Name:       "Test Campaign",
-			ProjectURL: "https://github.com/orgs/test/projects/1",
-			Workflows:  []string{"test-workflow"},
-			Governance: &CampaignGovernancePolicy{
-				MaxCommentsPerRun:       3,
-				MaxProjectUpdatesPerRun: 4,
-			},
-		}
-
-		mdPath := ".github/workflows/test-campaign.campaign.md"
-		data, _ := BuildOrchestrator(spec, mdPath)
-		if data == nil {
-			t.Fatalf("expected non-nil WorkflowData")
-		}
-		if data.SafeOutputs == nil || data.SafeOutputs.DispatchWorkflow == nil {
-			t.Fatalf("expected dispatch-workflow safe output to be enabled")
-		}
-		if data.SafeOutputs.DispatchWorkflow.Max != 3 {
-			t.Fatalf("unexpected dispatch-workflow max: got %d, want %d", data.SafeOutputs.DispatchWorkflow.Max, 3)
-		}
-
-		// Governance should control update-project max
-		if data.SafeOutputs.UpdateProjects == nil {
-			t.Fatalf("expected update-project safe output to be enabled")
-		}
-		if data.SafeOutputs.UpdateProjects.Max != 4 {
-			t.Fatalf("unexpected update-project max: got %d, want %d", data.SafeOutputs.UpdateProjects.Max, 4)
-		}
-
-		// create-project-status-update should always be enabled
-		if data.SafeOutputs.CreateProjectStatusUpdates == nil {
-			t.Fatalf("expected create-project-status-update safe output to be enabled")
-		}
-		if data.SafeOutputs.CreateProjectStatusUpdates.Max != 1 {
-			t.Fatalf("unexpected create-project-status-update max: got %d, want %d", data.SafeOutputs.CreateProjectStatusUpdates.Max, 1)
-		}
-
-		// Orchestrators should NOT have create-issue or add-comment (governance MaxCommentsPerRun doesn't grant add-comment)
-		if data.SafeOutputs.CreateIssues != nil || data.SafeOutputs.AddComments != nil {
-			t.Fatalf("expected orchestrator to omit create-issue and add-comment safe outputs regardless of governance")
-		}
-	})
-}
-
-func TestExtractFileGlobPatterns(t *testing.T) {
-	tests := []struct {
-		name           string
-		spec           *CampaignSpec
-		expectedGlobs  []string
-		expectedLogMsg string
-	}{
-		{
-			name: "flexible pattern matching both dated and non-dated",
-			spec: &CampaignSpec{
-				ID:          "go-file-size-reduction-project64",
-				MemoryPaths: []string{"memory/campaigns/go-file-size-reduction-project64*/**"},
-				MetricsGlob: "memory/campaigns/go-file-size-reduction-project64-*/metrics/*.json",
-			},
-			expectedGlobs:  []string{"go-file-size-reduction-project64*/**"},
-			expectedLogMsg: "Extracted file-glob pattern from memory-paths",
-		},
-		{
-			name: "dated pattern in memory-paths",
-			spec: &CampaignSpec{
-				ID:          "go-file-size-reduction-project64",
-				MemoryPaths: []string{"memory/campaigns/go-file-size-reduction-project64-*/**"},
-				MetricsGlob: "memory/campaigns/go-file-size-reduction-project64-*/metrics/*.json",
-			},
-			expectedGlobs:  []string{"go-file-size-reduction-project64-*/**"},
-			expectedLogMsg: "Extracted file-glob pattern from memory-paths",
-		},
-		{
-			name: "multiple patterns in memory-paths",
-			spec: &CampaignSpec{
-				ID: "go-file-size-reduction-project64",
-				MemoryPaths: []string{
-					"memory/campaigns/go-file-size-reduction-project64-*/**",
-					"memory/campaigns/go-file-size-reduction-project64/**",
-				},
-				MetricsGlob: "memory/campaigns/go-file-size-reduction-project64-*/metrics/*.json",
-			},
-			expectedGlobs:  []string{"go-file-size-reduction-project64-*/**", "go-file-size-reduction-project64/**"},
-			expectedLogMsg: "Extracted file-glob pattern from memory-paths",
-		},
-		{
-			name: "dated pattern in metrics-glob only",
-			spec: &CampaignSpec{
-				ID:          "go-file-size-reduction-project64",
-				MetricsGlob: "memory/campaigns/go-file-size-reduction-project64-*/metrics/*.json",
-			},
-			expectedGlobs:  []string{"go-file-size-reduction-project64-*/**"},
-			expectedLogMsg: "Extracted file-glob pattern from metrics-glob",
-		},
-		{
-			name: "simple pattern without wildcards",
-			spec: &CampaignSpec{
-				ID:          "simple-campaign",
-				MemoryPaths: []string{"memory/campaigns/simple-campaign/**"},
-			},
-			expectedGlobs:  []string{"simple-campaign/**"},
-			expectedLogMsg: "Extracted file-glob pattern from memory-paths",
-		},
-		{
-			name: "no memory paths or metrics glob",
-			spec: &CampaignSpec{
-				ID: "minimal-campaign",
-			},
-			expectedGlobs:  []string{"minimal-campaign/**"},
-			expectedLogMsg: "Using fallback file-glob pattern",
-		},
-		{
-			name: "multiple memory paths with wildcard",
-			spec: &CampaignSpec{
-				ID: "multi-path",
-				MemoryPaths: []string{
-					"memory/campaigns/multi-path-staging/**",
-					"memory/campaigns/multi-path-*/data/**",
-				},
-			},
-			expectedGlobs:  []string{"multi-path-staging/**", "multi-path-*/data/**"},
-			expectedLogMsg: "Extracted file-glob pattern from memory-paths",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result := extractFileGlobPatterns(tt.spec)
-			if len(result) != len(tt.expectedGlobs) {
-				t.Errorf("extractFileGlobPatterns(%q) returned %d patterns, want %d", tt.spec.ID, len(result), len(tt.expectedGlobs))
-				return
-			}
-			for i, expected := range tt.expectedGlobs {
-				if result[i] != expected {
-					t.Errorf("extractFileGlobPatterns(%q)[%d] = %q, want %q", tt.spec.ID, i, result[i], expected)
-				}
-			}
-		})
-	}
-}
-
-func TestBuildOrchestrator_FileGlobMatchesMemoryPaths(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		// This test verifies that the file-glob pattern in repo-memory configuration
-		// matches the pattern defined in memory-paths, including wildcards
-		spec := &CampaignSpec{
-			ID:          "go-file-size-reduction-project64",
-			Name:        "Go File Size Reduction Campaign",
-			Description: "Test campaign with dated memory paths",
-			ProjectURL:  "https://github.com/orgs/githubnext/projects/64",
-			Workflows:   []string{"daily-file-diet"},
-			MemoryPaths: []string{"memory/campaigns/go-file-size-reduction-project64-*/**"},
-			MetricsGlob: "memory/campaigns/go-file-size-reduction-project64-*/metrics/*.json",
-		}
-
-		mdPath := ".github/workflows/go-file-size-reduction-project64.campaign.md"
-		data, _ := BuildOrchestrator(spec, mdPath)
-
-		if data == nil {
-			t.Fatalf("expected non-nil WorkflowData")
-		}
-
-		// Extract repo-memory configuration from Tools
-		repoMemoryConfig, ok := data.Tools["repo-memory"]
-		if !ok {
-			t.Fatalf("expected repo-memory to be configured in Tools")
-		}
-
-		repoMemoryArray, ok := repoMemoryConfig.([]any)
-		if !ok || len(repoMemoryArray) == 0 {
-			t.Fatalf("expected repo-memory to be an array with at least one entry")
-		}
-
-		repoMemoryEntry, ok := repoMemoryArray[0].(map[string]any)
-		if !ok {
-			t.Fatalf("expected repo-memory entry to be a map")
-		}
-
-		fileGlob, ok := repoMemoryEntry["file-glob"]
-		if !ok {
-			t.Fatalf("expected file-glob to be present in repo-memory entry")
-		}
-
-		fileGlobArray, ok := fileGlob.([]any)
-		if !ok || len(fileGlobArray) == 0 {
-			t.Fatalf("expected file-glob to be an array with at least one entry")
-		}
-
-		fileGlobPattern, ok := fileGlobArray[0].(string)
-		if !ok {
-			t.Fatalf("expected file-glob pattern to be a string")
-		}
-
-		// Verify that the file-glob pattern includes the wildcard for dated directories
-		expectedPattern := "go-file-size-reduction-project64-*/**"
-		if fileGlobPattern != expectedPattern {
-			t.Errorf("file-glob pattern = %q, want %q", fileGlobPattern, expectedPattern)
-		}
-
-		// Verify that the pattern would match dated directories
-		if !strings.Contains(fileGlobPattern, "*") {
-			t.Errorf("file-glob pattern should include wildcard for dated directories, got %q", fileGlobPattern)
-		}
-	})
-}
diff --git a/pkg/campaign/project.go b/pkg/campaign/project.go
deleted file mode 100644
index f3310c3a479..00000000000
--- a/pkg/campaign/project.go
+++ /dev/null
@@ -1,800 +0,0 @@
-package campaign
-
-import (
-	"bytes"
-	"encoding/json"
-	"fmt"
-	"os"
-	"os/exec"
-	"regexp"
-	"strconv"
-	"strings"
-
-	"github.com/githubnext/gh-aw/pkg/console"
-	"github.com/githubnext/gh-aw/pkg/logger"
-)
-
-var projectLog = logger.New("campaign:project")
-
-// ProjectCreationConfig holds configuration for creating a campaign project
-type ProjectCreationConfig struct {
-	CampaignID   string
-	CampaignName string
-	Owner        string // GitHub org or user
-	LinkRepo     string // Optional: owner/name to link the project to
-	NoLinkRepo   bool   // Disable best-effort repo linking
-	Verbose      bool
-}
-
-// ProjectCreationResult holds the result of project creation
-type ProjectCreationResult struct {
-	ProjectURL    string
-	ProjectNumber int
-}
-
-// CreateCampaignProject creates a GitHub Project with required views and fields for a campaign
-func CreateCampaignProject(config ProjectCreationConfig) (*ProjectCreationResult, error) {
-	projectLog.Printf("Creating campaign project for campaign ID: %s", config.CampaignID)
-
-	// Check if gh CLI is available
-	if !isGHCLIAvailable() {
-		return nil, fmt.Errorf("GitHub CLI (gh) is not available. Install it from https://cli.github.com/")
-	}
-
-	// Create the project
-	projectURL, projectNumber, err := createProject(config)
-	if err != nil {
-		return nil, fmt.Errorf("failed to create project: %w", err)
-	}
-
-	console.LogVerbose(config.Verbose, fmt.Sprintf("Created project: %s", projectURL))
-
-	// Create required fields
-	if err := createProjectFields(config, projectNumber); err != nil {
-		return nil, fmt.Errorf("failed to create project fields: %w", err)
-	}
-
-	console.LogVerbose(config.Verbose, "Created project fields")
-
-	// Create standard views (board/table/roadmap)
-	if err := createProjectViews(config, projectURL); err != nil {
-		return nil, fmt.Errorf("failed to create project views: %w", err)
-	}
-
-	console.LogVerbose(config.Verbose, "Created project views")
-
-	// Best-effort: link the project to the current repository.
-	// This should not block campaign creation if linking fails due to permissions.
-	if err := linkProjectToRepo(config, projectURL); err != nil {
-		fmt.Fprintln(os.Stderr, console.FormatWarningMessage(
-			"Could not link project to repository automatically: "+err.Error(),
-		))
-	}
-
-	// Ensure the Progress Board's typical grouping field (Status) includes a
-	// "Review Required" option, which effectively becomes a new column.
-	if err := ensureStatusOption(config, projectURL, "Review Required"); err != nil {
-		return nil, fmt.Errorf("failed to update project status options: %w", err)
-	}
-
-	result := &ProjectCreationResult{
-		ProjectURL:    projectURL,
-		ProjectNumber: projectNumber,
-	}
-
-	return result, nil
-}
-
-func linkProjectToRepo(config ProjectCreationConfig, projectURL string) error {
-	if config.NoLinkRepo {
-		console.LogVerbose(config.Verbose, "Skipping project-to-repo linking (--no-link-repo)")
-		return nil
-	}
-
-	nameWithOwner := strings.TrimSpace(config.LinkRepo)
-	if nameWithOwner == "" {
-		var err error
-		nameWithOwner, err = getCurrentRepoNameWithOwner()
-		if err != nil {
-			return err
-		}
-	}
-
-	owner, repo, err := parseRepoNameWithOwner(nameWithOwner)
-	if err != nil {
-		return err
-	}
-
-	info, err := parseProjectURL(projectURL)
-	if err != nil {
-		return err
-	}
-
-	// GitHub only allows linking when the project and repository share the same owner.
-	// Avoid calling the mutation to prevent a noisy (and expected) GraphQL validation error.
-	if !strings.EqualFold(info.ownerLogin, owner) {
-		return fmt.Errorf(
-			"project is owned by %q but current repository is %q; GitHub only allows linking projects to repositories owned by the same account/org. Re-run with --owner %s (or --owner @me for personal repos) from the repo you want linked",
-			info.ownerLogin,
-			nameWithOwner,
-			owner,
-		)
-	}
-
-	projectID, err := getProjectID(info)
-	if err != nil {
-		return err
-	}
-
-	repoID, err := getRepositoryID(owner, repo)
-	if err != nil {
-		return err
-	}
-
-	if err := linkProjectV2ToRepository(projectID, repoID); err != nil {
-		return err
-	}
-
-	console.LogVerbose(config.Verbose, fmt.Sprintf("Linked project to repository: %s", nameWithOwner))
-	return nil
-}
-
-func parseRepoNameWithOwner(nameWithOwner string) (string, string, error) {
-	trimmed := strings.TrimSpace(nameWithOwner)
-	owner, repo, ok := strings.Cut(trimmed, "/")
-	owner = strings.TrimSpace(owner)
-	repo = strings.TrimSpace(repo)
-	owner = strings.TrimPrefix(owner, "@")
-	if !ok || owner == "" || repo == "" {
-		return "", "", fmt.Errorf("invalid repository %q; expected format owner/name", nameWithOwner)
-	}
-	return owner, repo, nil
-}
-
-func getCurrentRepoNameWithOwner() (string, error) {
-	cmd := exec.Command("gh", "repo", "view", "--json", "nameWithOwner", "--jq", ".nameWithOwner")
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		return "", fmt.Errorf("gh repo view failed: %w\nOutput: %s", err, string(out))
-	}
-	nameWithOwner := strings.TrimSpace(string(out))
-	if nameWithOwner == "" {
-		return "", fmt.Errorf("failed to determine current repository (empty nameWithOwner)")
-	}
-	return nameWithOwner, nil
-}
-
-func getRepositoryID(owner, name string) (string, error) {
-	query := `query($owner: String!, $name: String!) {
-		repository(owner: $owner, name: $name) { id }
-	}`
-
-	cmd := exec.Command(
-		"gh",
-		"api",
-		"graphql",
-		"-F",
-		"owner="+owner,
-		"-F",
-		"name="+name,
-		"-f",
-		"query="+query,
-	)
-
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		return "", fmt.Errorf("gh api graphql failed: %w\nOutput: %s", err, string(out))
-	}
-
-	var resp struct {
-		Data struct {
-			Repository struct {
-				ID string `json:"id"`
-			} `json:"repository"`
-		} `json:"data"`
-	}
-
-	if err := json.Unmarshal(out, &resp); err != nil {
-		return "", fmt.Errorf("failed to parse GraphQL response: %w\nOutput: %s", err, string(out))
-	}
-
-	if resp.Data.Repository.ID == "" {
-		return "", fmt.Errorf("failed to find repository ID for %s/%s", owner, name)
-	}
-
-	return resp.Data.Repository.ID, nil
-}
-
-func getProjectID(info projectURLInfo) (string, error) {
-	query := ""
-	if info.scope == "orgs" {
-		query = `query($login: String!, $number: Int!) {
-			organization(login: $login) {
-				projectV2(number: $number) { id }
-			}
-		}`
-	} else {
-		query = `query($login: String!, $number: Int!) {
-			user(login: $login) {
-				projectV2(number: $number) { id }
-			}
-		}`
-	}
-
-	cmd := exec.Command(
-		"gh",
-		"api",
-		"graphql",
-		"-F",
-		"login="+info.ownerLogin,
-		"-F",
-		fmt.Sprintf("number=%d", info.projectNumber),
-		"-f",
-		"query="+query,
-	)
-
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		return "", fmt.Errorf("gh api graphql failed: %w\nOutput: %s", err, string(out))
-	}
-
-	if info.scope == "orgs" {
-		var resp struct {
-			Data struct {
-				Organization struct {
-					ProjectV2 struct {
-						ID string `json:"id"`
-					} `json:"projectV2"`
-				} `json:"organization"`
-			} `json:"data"`
-		}
-		if err := json.Unmarshal(out, &resp); err != nil {
-			return "", fmt.Errorf("failed to parse GraphQL response: %w\nOutput: %s", err, string(out))
-		}
-		if resp.Data.Organization.ProjectV2.ID == "" {
-			return "", fmt.Errorf("failed to find project ID in GraphQL response")
-		}
-		return resp.Data.Organization.ProjectV2.ID, nil
-	}
-
-	var resp struct {
-		Data struct {
-			User struct {
-				ProjectV2 struct {
-					ID string `json:"id"`
-				} `json:"projectV2"`
-			} `json:"user"`
-		} `json:"data"`
-	}
-	if err := json.Unmarshal(out, &resp); err != nil {
-		return "", fmt.Errorf("failed to parse GraphQL response: %w\nOutput: %s", err, string(out))
-	}
-	if resp.Data.User.ProjectV2.ID == "" {
-		return "", fmt.Errorf("failed to find project ID in GraphQL response")
-	}
-	return resp.Data.User.ProjectV2.ID, nil
-}
-
-func linkProjectV2ToRepository(projectID, repositoryID string) error {
-	mutation := `mutation($input: LinkProjectV2ToRepositoryInput!) {
-		linkProjectV2ToRepository(input: $input) {
-			clientMutationId
-		}
-	}`
-
-	requestBody := map[string]any{
-		"query": mutation,
-		"variables": map[string]any{
-			"input": map[string]any{
-				"projectId":    projectID,
-				"repositoryId": repositoryID,
-			},
-		},
-	}
-
-	requestJSON, err := json.Marshal(requestBody)
-	if err != nil {
-		return fmt.Errorf("failed to marshal GraphQL request body: %w", err)
-	}
-
-	cmd := exec.Command("gh", "api", "graphql", "--input", "-")
-	cmd.Stdin = bytes.NewReader(requestJSON)
-
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		// If it's already linked, treat it as success.
-		msg := string(out)
-		if strings.Contains(strings.ToLower(msg), "already") && strings.Contains(strings.ToLower(msg), "link") {
-			return nil
-		}
-		return fmt.Errorf("gh api graphql link failed: %w\nOutput: %s", err, msg)
-	}
-
-	return nil
-}
-
-type projectURLInfo struct {
-	scope         string // "users" or "orgs"
-	ownerLogin    string
-	projectNumber int
-}
-
-var projectURLRegexp = regexp.MustCompile(`github\.com\/(users|orgs)\/([^/]+)\/projects\/(\d+)`)
-
-func parseProjectURL(projectURL string) (projectURLInfo, error) {
-	match := projectURLRegexp.FindStringSubmatch(projectURL)
-	if match == nil {
-		return projectURLInfo{}, fmt.Errorf("invalid project URL: %q. Expected format: https://github.com/orgs/myorg/projects/123", projectURL)
-	}
-
-	projectNumber, err := strconv.Atoi(match[3])
-	if err != nil {
-		return projectURLInfo{}, fmt.Errorf("invalid project number in URL %q: %w", projectURL, err)
-	}
-
-	return projectURLInfo{
-		scope:         match[1],
-		ownerLogin:    match[2],
-		projectNumber: projectNumber,
-	}, nil
-}
-
-func createProjectViews(config ProjectCreationConfig, projectURL string) error {
-	projectLog.Printf("Creating standard views for project URL: %s", projectURL)
-
-	info, err := parseProjectURL(projectURL)
-	if err != nil {
-		return err
-	}
-
-	views := []struct {
-		name   string
-		layout string
-	}{
-		{name: "Progress Board", layout: "board"},
-		{name: "Task Tracker", layout: "table"},
-		{name: "Campaign Roadmap", layout: "roadmap"},
-	}
-
-	for _, view := range views {
-		if err := createProjectView(info, view.name, view.layout); err != nil {
-			return fmt.Errorf("failed to create view %q (%s): %w", view.name, view.layout, err)
-		}
-		console.LogVerbose(config.Verbose, fmt.Sprintf("Created view: %s (%s)", view.name, view.layout))
-	}
-
-	return nil
-}
-
-func createProjectView(info projectURLInfo, name, layout string) error {
-	path := ""
-	if info.scope == "orgs" {
-		path = fmt.Sprintf("/orgs/%s/projectsV2/%d/views", info.ownerLogin, info.projectNumber)
-	} else {
-		path = fmt.Sprintf("/users/%s/projectsV2/%d/views", info.ownerLogin, info.projectNumber)
-	}
-
-	cmd := exec.Command(
-		"gh",
-		"api",
-		"--method",
-		"POST",
-		path,
-		"-H",
-		"Accept: application/vnd.github+json",
-		"-H",
-		"X-GitHub-Api-Version: 2022-11-28",
-		"-f",
-		"name="+name,
-		"-f",
-		"layout="+layout,
-	)
-
-	output, err := cmd.CombinedOutput()
-	if err != nil {
-		return fmt.Errorf("gh api failed: %w\nOutput: %s", err, string(output))
-	}
-
-	return nil
-}
-
-type singleSelectOption struct {
-	Name        string `json:"name"`
-	Color       string `json:"color"`
-	Description string `json:"description"`
-}
-
-type statusFieldLookup struct {
-	ProjectID string
-	FieldID   string
-	Options   []singleSelectOption
-}
-
-func ensureStatusOption(config ProjectCreationConfig, projectURL string, optionName string) error {
-	projectLog.Printf("Ensuring Status option %q exists for project URL: %s", optionName, projectURL)
-
-	info, err := parseProjectURL(projectURL)
-	if err != nil {
-		return err
-	}
-
-	lookup, err := getStatusField(config, info)
-	if err != nil {
-		return err
-	}
-
-	updatedOptions, changed := ensureSingleSelectOptionBefore(
-		lookup.Options,
-		singleSelectOption{Name: optionName, Color: "BLUE", Description: "Needs review before moving to Done"},
-		"Done",
-	)
-	if !changed {
-		console.LogVerbose(config.Verbose, fmt.Sprintf("Status option already present and ordered: %s", optionName))
-		return nil
-	}
-
-	if err := updateSingleSelectFieldOptions(lookup.FieldID, updatedOptions); err != nil {
-		return err
-	}
-
-	console.LogVerbose(config.Verbose, fmt.Sprintf("Ensured Status option is ordered before Done: %s", optionName))
-	return nil
-}
-
-func ensureSingleSelectOptionBefore(options []singleSelectOption, desired singleSelectOption, beforeName string) ([]singleSelectOption, bool) {
-	var existing *singleSelectOption
-	without := make([]singleSelectOption, 0, len(options))
-	for _, opt := range options {
-		if opt.Name == desired.Name {
-			if existing == nil {
-				copyOpt := opt
-				existing = &copyOpt
-			}
-			continue
-		}
-		without = append(without, opt)
-	}
-
-	toInsert := desired
-	if existing != nil {
-		toInsert = *existing
-		toInsert.Color = desired.Color
-		if desired.Description != "" {
-			toInsert.Description = desired.Description
-		}
-	}
-
-	insertAt := len(without)
-	for i, opt := range without {
-		if opt.Name == beforeName {
-			insertAt = i
-			break
-		}
-	}
-
-	withInserted := make([]singleSelectOption, 0, len(without)+1)
-	withInserted = append(withInserted, without[:insertAt]...)
-	withInserted = append(withInserted, toInsert)
-	withInserted = append(withInserted, without[insertAt:]...)
-
-	return withInserted, !singleSelectOptionsEqual(options, withInserted)
-}
-
-func singleSelectOptionsEqual(a, b []singleSelectOption) bool {
-	if len(a) != len(b) {
-		return false
-	}
-	for i := range a {
-		if a[i] != b[i] {
-			return false
-		}
-	}
-	return true
-}
-
-func getStatusField(config ProjectCreationConfig, info projectURLInfo) (statusFieldLookup, error) {
-	// Query the project ID and the built-in Status single-select field.
-	// We use the REST URL components (org/user + number) to locate the project.
-	query := ""
-	if info.scope == "orgs" {
-		query = `query($login: String!, $number: Int!) {
-			organization(login: $login) {
-				projectV2(number: $number) {
-					id
-					fields(first: 100) {
-						nodes {
-							... on ProjectV2SingleSelectField {
-								id
-								name
-								options { name color description }
-							}
-						}
-					}
-				}
-			}
-		}`
-	} else {
-		query = `query($login: String!, $number: Int!) {
-			user(login: $login) {
-				projectV2(number: $number) {
-					id
-					fields(first: 100) {
-						nodes {
-							... on ProjectV2SingleSelectField {
-								id
-								name
-								options { name color description }
-							}
-						}
-					}
-				}
-			}
-		}`
-	}
-
-	cmd := exec.Command(
-		"gh",
-		"api",
-		"graphql",
-		"-F",
-		"login="+info.ownerLogin,
-		"-F",
-		fmt.Sprintf("number=%d", info.projectNumber),
-		"-f",
-		"query="+query,
-	)
-
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		return statusFieldLookup{}, fmt.Errorf("gh api graphql failed: %w\nOutput: %s", err, string(out))
-	}
-
-	// Parse response
-	if info.scope == "orgs" {
-		var resp struct {
-			Data struct {
-				Organization struct {
-					ProjectV2 struct {
-						ID     string `json:"id"`
-						Fields struct {
-							Nodes []struct {
-								ID      string               `json:"id"`
-								Name    string               `json:"name"`
-								Options []singleSelectOption `json:"options"`
-							} `json:"nodes"`
-						} `json:"fields"`
-					} `json:"projectV2"`
-				} `json:"organization"`
-			} `json:"data"`
-		}
-
-		if err := json.Unmarshal(out, &resp); err != nil {
-			return statusFieldLookup{}, fmt.Errorf("failed to parse GraphQL response: %w\nOutput: %s", err, string(out))
-		}
-
-		projectID := resp.Data.Organization.ProjectV2.ID
-		if projectID == "" {
-			return statusFieldLookup{}, fmt.Errorf("failed to find project ID in GraphQL response")
-		}
-
-		for _, node := range resp.Data.Organization.ProjectV2.Fields.Nodes {
-			if node.Name == "Status" {
-				return statusFieldLookup{ProjectID: projectID, FieldID: node.ID, Options: node.Options}, nil
-			}
-		}
-	} else {
-		var resp struct {
-			Data struct {
-				User struct {
-					ProjectV2 struct {
-						ID     string `json:"id"`
-						Fields struct {
-							Nodes []struct {
-								ID      string               `json:"id"`
-								Name    string               `json:"name"`
-								Options []singleSelectOption `json:"options"`
-							} `json:"nodes"`
-						} `json:"fields"`
-					} `json:"projectV2"`
-				} `json:"user"`
-			} `json:"data"`
-		}
-
-		if err := json.Unmarshal(out, &resp); err != nil {
-			return statusFieldLookup{}, fmt.Errorf("failed to parse GraphQL response: %w\nOutput: %s", err, string(out))
-		}
-
-		projectID := resp.Data.User.ProjectV2.ID
-		if projectID == "" {
-			return statusFieldLookup{}, fmt.Errorf("failed to find project ID in GraphQL response")
-		}
-
-		for _, node := range resp.Data.User.ProjectV2.Fields.Nodes {
-			if node.Name == "Status" {
-				return statusFieldLookup{ProjectID: projectID, FieldID: node.ID, Options: node.Options}, nil
-			}
-		}
-	}
-
-	return statusFieldLookup{}, fmt.Errorf("failed to locate Status field on project")
-}
-
-func updateSingleSelectFieldOptions(fieldID string, options []singleSelectOption) error {
-	mutation := `mutation($input: UpdateProjectV2FieldInput!) {
-		updateProjectV2Field(input: $input) {
-			projectV2Field {
-				... on ProjectV2SingleSelectField {
-					name
-					options { name }
-				}
-			}
-		}
-	}`
-
-	input := map[string]any{
-		"fieldId":             fieldID,
-		"singleSelectOptions": options,
-	}
-
-	requestBody := map[string]any{
-		"query": mutation,
-		"variables": map[string]any{
-			"input": input,
-		},
-	}
-
-	requestJSON, err := json.Marshal(requestBody)
-	if err != nil {
-		return fmt.Errorf("failed to marshal GraphQL request body: %w", err)
-	}
-
-	cmd := exec.Command("gh", "api", "graphql", "--input", "-")
-	cmd.Stdin = bytes.NewReader(requestJSON)
-
-	out, err := cmd.CombinedOutput()
-	if err != nil {
-		return fmt.Errorf("gh api graphql update failed: %w\nOutput: %s", err, string(out))
-	}
-
-	return nil
-}
-
-// isGHCLIAvailable checks if the gh CLI is installed and available
-func isGHCLIAvailable() bool {
-	cmd := exec.Command("gh", "--version")
-	return cmd.Run() == nil
-}
-
-func normalizeProjectOwner(owner string) string {
-	trimmed := strings.TrimSpace(owner)
-	if strings.EqualFold(trimmed, "@me") {
-		return "@me"
-	}
-	trimmed = strings.TrimPrefix(trimmed, "@")
-	return trimmed
-}
-
-// createProject creates a new GitHub Project and returns its URL and number
-func createProject(config ProjectCreationConfig) (string, int, error) {
-	projectLog.Printf("Creating project with title: %s", config.CampaignName)
-
-	owner := normalizeProjectOwner(config.Owner)
-
-	// Create project using gh CLI
-	cmd := exec.Command("gh", "project", "create",
-		"--owner", owner,
-		"--title", config.CampaignName,
-		"--format", "json")
-
-	output, err := cmd.CombinedOutput()
-	if err != nil {
-		return "", 0, fmt.Errorf("failed to create project: %w\nOutput: %s", err, string(output))
-	}
-
-	// Parse JSON output to get project URL and number
-	var result struct {
-		URL    string `json:"url"`
-		Number int    `json:"number"`
-	}
-
-	if err := json.Unmarshal(output, &result); err != nil {
-		return "", 0, fmt.Errorf("failed to parse project creation output: %w\nOutput: %s", err, string(output))
-	}
-
-	projectLog.Printf("Project created: URL=%s, Number=%d", result.URL, result.Number)
-	return result.URL, result.Number, nil
-}
-
-// createProjectFields creates the required fields for a campaign project
-func createProjectFields(config ProjectCreationConfig, projectNumber int) error {
-	projectLog.Printf("Creating fields for project number: %d", projectNumber)
-
-	// Define required fields
-	// Note: We use "Target Repo" instead of "Repository" because GitHub has a built-in
-	// REPOSITORY field type that conflicts with custom field creation
-	fields := []struct {
-		name     string
-		dataType string
-		options  []string // For SINGLE_SELECT fields
-	}{
-		{"Campaign Id", "TEXT", nil},
-		{"Worker Workflow", "TEXT", nil},
-		{"Target Repo", "TEXT", nil},
-		{"Priority", "SINGLE_SELECT", []string{"High", "Medium", "Low"}},
-		{"Size", "SINGLE_SELECT", []string{"Small", "Medium", "Large"}},
-		{"Start Date", "DATE", nil},
-		{"End Date", "DATE", nil},
-	}
-
-	// Create each field
-	for _, field := range fields {
-		if err := createField(config, projectNumber, field.name, field.dataType, field.options); err != nil {
-			return fmt.Errorf("failed to create field '%s': %w", field.name, err)
-		}
-		console.LogVerbose(config.Verbose, fmt.Sprintf("Created field: %s", field.name))
-	}
-
-	return nil
-}
-
-// createField creates a single field in the project
-func createField(config ProjectCreationConfig, projectNumber int, name, dataType string, options []string) error {
-	projectLog.Printf("Creating field: name=%s, type=%s", name, dataType)
-
-	owner := normalizeProjectOwner(config.Owner)
-
-	args := []string{
-		"project", "field-create", fmt.Sprintf("%d", projectNumber),
-		"--owner", owner,
-		"--name", name,
-		"--data-type", dataType,
-	}
-
-	// Add options for SINGLE_SELECT fields
-	// The gh CLI expects --single-select-options to be passed multiple times,
-	// once for each option, rather than as a comma-separated list
-	if dataType == "SINGLE_SELECT" && len(options) > 0 {
-		for _, option := range options {
-			args = append(args, "--single-select-options", option)
-		}
-	}
-
-	cmd := exec.Command("gh", args...)
-	output, err := cmd.CombinedOutput()
-	if err != nil {
-		return fmt.Errorf("failed to create field: %w\nOutput: %s", err, string(output))
-	}
-
-	return nil
-}
-
-// UpdateSpecWithProjectURL updates a campaign spec file with the project URL
-func UpdateSpecWithProjectURL(specPath, projectURL string) error {
-	projectLog.Printf("Updating spec file %s with project URL: %s", specPath, projectURL)
-
-	// Read the spec file
-	content, err := os.ReadFile(specPath)
-	if err != nil {
-		return fmt.Errorf("failed to read spec file: %w", err)
-	}
-
-	specContent := string(content)
-
-	// Replace the placeholder project URL with the actual one
-	placeholderURL := "https://github.com/orgs/ORG/projects/1"
-	if !strings.Contains(specContent, placeholderURL) {
-		// If placeholder doesn't exist, the spec might have been manually edited
-		projectLog.Print("Placeholder project URL not found, spec may have been edited")
-		return nil
-	}
-
-	updatedContent := strings.Replace(specContent, placeholderURL, projectURL, 1)
-
-	// Write the updated content back
-	if err := os.WriteFile(specPath, []byte(updatedContent), 0o644); err != nil {
-		return fmt.Errorf("failed to write updated spec file: %w", err)
-	}
-
-	projectLog.Print("Successfully updated spec file with project URL")
-	return nil
-}
diff --git a/pkg/campaign/project_owner_normalization_test.go b/pkg/campaign/project_owner_normalization_test.go
deleted file mode 100644
index dae0f2f6869..00000000000
--- a/pkg/campaign/project_owner_normalization_test.go
+++ /dev/null
@@ -1,48 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import "testing"
-
-func TestNormalizeProjectOwner(t *testing.T) {
-	tests := []struct {
-		name string
-		in   string
-		want string
-	}{
-		{
-			name: "plain login unchanged",
-			in:   "mnkiefer",
-			want: "mnkiefer",
-		},
-		{
-			name: "strip leading at",
-			in:   "@mnkiefer",
-			want: "mnkiefer",
-		},
-		{
-			name: "keep special @me",
-			in:   "@me",
-			want: "@me",
-		},
-		{
-			name: "trim whitespace",
-			in:   "  @mnkiefer  ",
-			want: "mnkiefer",
-		},
-		{
-			name: "empty stays empty",
-			in:   "",
-			want: "",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			got := normalizeProjectOwner(tt.in)
-			if got != tt.want {
-				t.Fatalf("normalizeProjectOwner(%q) = %q, want %q", tt.in, got, tt.want)
-			}
-		})
-	}
-}
diff --git a/pkg/campaign/project_repo_parsing_test.go b/pkg/campaign/project_repo_parsing_test.go
deleted file mode 100644
index 7fffbd9225c..00000000000
--- a/pkg/campaign/project_repo_parsing_test.go
+++ /dev/null
@@ -1,72 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import "testing"
-
-func TestParseRepoNameWithOwner(t *testing.T) {
-	tests := []struct {
-		name      string
-		in        string
-		wantOwner string
-		wantRepo  string
-		wantErr   bool
-	}{
-		{
-			name:      "basic",
-			in:        "githubnext/gh-aw",
-			wantOwner: "githubnext",
-			wantRepo:  "gh-aw",
-		},
-		{
-			name:      "trims whitespace",
-			in:        "  githubnext / gh-aw  ",
-			wantOwner: "githubnext",
-			wantRepo:  "gh-aw",
-		},
-		{
-			name:      "strips leading at on owner",
-			in:        "@mnkiefer/gh-aw",
-			wantOwner: "mnkiefer",
-			wantRepo:  "gh-aw",
-		},
-		{
-			name:    "missing slash",
-			in:      "githubnext",
-			wantErr: true,
-		},
-		{
-			name:    "empty owner",
-			in:      "/repo",
-			wantErr: true,
-		},
-		{
-			name:    "empty repo",
-			in:      "owner/",
-			wantErr: true,
-		},
-		{
-			name:    "empty",
-			in:      "",
-			wantErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			owner, repo, err := parseRepoNameWithOwner(tt.in)
-			if tt.wantErr {
-				if err == nil {
-					t.Fatalf("parseRepoNameWithOwner(%q) expected error", tt.in)
-				}
-				return
-			}
-			if err != nil {
-				t.Fatalf("parseRepoNameWithOwner(%q) unexpected error: %v", tt.in, err)
-			}
-			if owner != tt.wantOwner || repo != tt.wantRepo {
-				t.Fatalf("parseRepoNameWithOwner(%q) = (%q, %q), want (%q, %q)", tt.in, owner, repo, tt.wantOwner, tt.wantRepo)
-			}
-		})
-	}
-}
diff --git a/pkg/campaign/project_status_options_test.go b/pkg/campaign/project_status_options_test.go
deleted file mode 100644
index 90870e5bc88..00000000000
--- a/pkg/campaign/project_status_options_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import "testing"
-
-func TestEnsureSingleSelectOptionBefore(t *testing.T) {
-	tests := []struct {
-		name    string
-		options []singleSelectOption
-		want    []singleSelectOption
-		changed bool
-	}{
-		{
-			name: "inserts before Done when missing",
-			options: []singleSelectOption{
-				{Name: "Todo", Color: "GRAY", Description: ""},
-				{Name: "In Progress", Color: "BLUE", Description: ""},
-				{Name: "Done", Color: "GREEN", Description: ""},
-			},
-			want: []singleSelectOption{
-				{Name: "Todo", Color: "GRAY", Description: ""},
-				{Name: "In Progress", Color: "BLUE", Description: ""},
-				{Name: "Review Required", Color: "BLUE", Description: "Needs review before moving to Done"},
-				{Name: "Done", Color: "GREEN", Description: ""},
-			},
-			changed: true,
-		},
-		{
-			name: "moves existing option before Done",
-			options: []singleSelectOption{
-				{Name: "Todo", Color: "GRAY", Description: ""},
-				{Name: "Done", Color: "GREEN", Description: ""},
-				{Name: "Review Required", Color: "PINK", Description: "keep"},
-			},
-			want: []singleSelectOption{
-				{Name: "Todo", Color: "GRAY", Description: ""},
-				{Name: "Review Required", Color: "BLUE", Description: "Needs review before moving to Done"},
-				{Name: "Done", Color: "GREEN", Description: ""},
-			},
-			changed: true,
-		},
-		{
-			name: "no change when already before Done",
-			options: []singleSelectOption{
-				{Name: "Todo", Color: "GRAY", Description: ""},
-				{Name: "Review Required", Color: "BLUE", Description: "Needs review before moving to Done"},
-				{Name: "Done", Color: "GREEN", Description: ""},
-			},
-			want: []singleSelectOption{
-				{Name: "Todo", Color: "GRAY", Description: ""},
-				{Name: "Review Required", Color: "BLUE", Description: "Needs review before moving to Done"},
-				{Name: "Done", Color: "GREEN", Description: ""},
-			},
-			changed: false,
-		},
-		{
-			name: "appends when Done missing",
-			options: []singleSelectOption{
-				{Name: "Todo", Color: "GRAY", Description: ""},
-			},
-			want: []singleSelectOption{
-				{Name: "Todo", Color: "GRAY", Description: ""},
-				{Name: "Review Required", Color: "BLUE", Description: "Needs review before moving to Done"},
-			},
-			changed: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			got, changed := ensureSingleSelectOptionBefore(
-				tt.options,
-				singleSelectOption{Name: "Review Required", Color: "BLUE", Description: "Needs review before moving to Done"},
-				"Done",
-			)
-
-			if changed != tt.changed {
-				t.Fatalf("changed=%v, want %v", changed, tt.changed)
-			}
-			if len(got) != len(tt.want) {
-				t.Fatalf("len(got)=%d, want %d", len(got), len(tt.want))
-			}
-			for i := range got {
-				if got[i] != tt.want[i] {
-					t.Fatalf("got[%d]=%+v, want %+v", i, got[i], tt.want[i])
-				}
-			}
-		})
-	}
-}
diff --git a/pkg/campaign/project_test.go b/pkg/campaign/project_test.go
deleted file mode 100644
index ce2e2634cd6..00000000000
--- a/pkg/campaign/project_test.go
+++ /dev/null
@@ -1,163 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-)
-
-func TestUpdateSpecWithProjectURL(t *testing.T) {
-	// Create a temporary directory
-	tmpDir := t.TempDir()
-
-	// Create a spec file with placeholder URL
-	specContent := `---
-id: test-campaign
-name: Test Campaign
-project-url: https://github.com/orgs/ORG/projects/1
-version: v1
-state: planned
----
-
-# Test Campaign
-
-This is a test campaign.
-`
-
-	specPath := filepath.Join(tmpDir, "test.campaign.md")
-	if err := os.WriteFile(specPath, []byte(specContent), 0o644); err != nil {
-		t.Fatalf("Failed to write test spec file: %v", err)
-	}
-
-	// Update the spec with a real project URL
-	newProjectURL := "https://github.com/orgs/myorg/projects/42"
-	if err := UpdateSpecWithProjectURL(specPath, newProjectURL); err != nil {
-		t.Fatalf("UpdateSpecWithProjectURL failed: %v", err)
-	}
-
-	// Read the updated spec
-	updatedContent, err := os.ReadFile(specPath)
-	if err != nil {
-		t.Fatalf("Failed to read updated spec file: %v", err)
-	}
-
-	updatedStr := string(updatedContent)
-
-	// Verify the URL was updated
-	if !strings.Contains(updatedStr, newProjectURL) {
-		t.Errorf("Expected updated spec to contain '%s', but it doesn't", newProjectURL)
-	}
-
-	// Verify the placeholder URL is gone
-	if strings.Contains(updatedStr, "https://github.com/orgs/ORG/projects/1") {
-		t.Error("Updated spec still contains placeholder URL")
-	}
-}
-
-func TestUpdateSpecWithProjectURL_NoPlaceholder(t *testing.T) {
-	// Create a temporary directory
-	tmpDir := t.TempDir()
-
-	// Create a spec file without placeholder URL
-	specContent := `---
-id: test-campaign
-name: Test Campaign
-project-url: https://github.com/orgs/myorg/projects/99
-version: v1
-state: planned
----
-
-# Test Campaign
-
-This is a test campaign.
-`
-
-	specPath := filepath.Join(tmpDir, "test.campaign.md")
-	if err := os.WriteFile(specPath, []byte(specContent), 0o644); err != nil {
-		t.Fatalf("Failed to write test spec file: %v", err)
-	}
-
-	// Try to update the spec (should succeed but not change anything)
-	newProjectURL := "https://github.com/orgs/myorg/projects/42"
-	if err := UpdateSpecWithProjectURL(specPath, newProjectURL); err != nil {
-		t.Fatalf("UpdateSpecWithProjectURL failed: %v", err)
-	}
-
-	// Read the content
-	updatedContent, err := os.ReadFile(specPath)
-	if err != nil {
-		t.Fatalf("Failed to read updated spec file: %v", err)
-	}
-
-	updatedStr := string(updatedContent)
-
-	// Verify the original URL is still there (not replaced)
-	if !strings.Contains(updatedStr, "https://github.com/orgs/myorg/projects/99") {
-		t.Error("Original project URL was incorrectly modified")
-	}
-
-	// Verify the new URL was not added
-	if strings.Contains(updatedStr, newProjectURL) {
-		t.Error("New project URL was incorrectly added")
-	}
-}
-
-func TestUpdateSpecWithProjectURL_FileNotFound(t *testing.T) {
-	// Try to update a non-existent file
-	err := UpdateSpecWithProjectURL("/nonexistent/path/spec.md", "https://github.com/orgs/myorg/projects/1")
-	if err == nil {
-		t.Error("Expected error for non-existent file, got nil")
-	}
-
-	if !strings.Contains(err.Error(), "failed to read spec file") {
-		t.Errorf("Expected 'failed to read spec file' error, got: %v", err)
-	}
-}
-
-func TestIsGHCLIAvailable(t *testing.T) {
-
-	// This test just verifies the function doesn't panic
-	// The actual result depends on the test environment
-	available := isGHCLIAvailable()
-	t.Logf("GitHub CLI available: %v", available)
-}
-
-func TestCreateProjectFieldsConfiguration(t *testing.T) {
-	// This test verifies that project fields are correctly configured
-	// We can't actually create fields without gh CLI and authentication,
-	// but we can verify the field definitions are correct
-
-	// Expected fields that should be created
-	expectedFields := map[string]struct {
-		dataType   string
-		hasOptions bool
-	}{
-		"Campaign Id":     {"TEXT", false},
-		"Worker Workflow": {"TEXT", false},
-		"Target Repo":     {"TEXT", false},
-		"Priority":        {"SINGLE_SELECT", true},
-		"Size":            {"SINGLE_SELECT", true},
-		"Start Date":      {"DATE", false},
-		"End Date":        {"DATE", false},
-	}
-
-	// Note: This is a design test - we're checking that the field configuration
-	// matches our requirements. The actual field creation is tested via integration tests.
-	for fieldName := range expectedFields {
-		// Just verify the expected field names exist in our test expectations
-		t.Logf("Expected field: %s", fieldName)
-	}
-
-	// Verify "Repository" is NOT in the expected fields (to avoid GitHub conflict)
-	if _, exists := expectedFields["Repository"]; exists {
-		t.Error("Field 'Repository' should not be created (conflicts with GitHub built-in REPOSITORY field)")
-	}
-
-	// Verify "Target Repo" IS in the expected fields
-	if _, exists := expectedFields["Target Repo"]; !exists {
-		t.Error("Field 'Target Repo' should be created (replacement for 'repository')")
-	}
-}
diff --git a/pkg/campaign/project_update_contract_validator.go b/pkg/campaign/project_update_contract_validator.go
deleted file mode 100644
index 5bfc0f57949..00000000000
--- a/pkg/campaign/project_update_contract_validator.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package campaign
-
-import (
-	"embed"
-	"encoding/json"
-	"fmt"
-	"strings"
-	"sync"
-
-	"github.com/santhosh-tekuri/jsonschema/v6"
-)
-
-//go:embed schemas/project_update_schema.json
-var projectUpdateSchemaFS embed.FS
-
-var (
-	compiledProjectUpdateSchemaOnce sync.Once
-	compiledProjectUpdateSchema     *jsonschema.Schema
-	projectUpdateSchemaCompileError error
-)
-
-func getCompiledProjectUpdateSchema() (*jsonschema.Schema, error) {
-	compiledProjectUpdateSchemaOnce.Do(func() {
-		schemaData, err := projectUpdateSchemaFS.ReadFile("schemas/project_update_schema.json")
-		if err != nil {
-			projectUpdateSchemaCompileError = fmt.Errorf("failed to load project update schema: %w", err)
-			return
-		}
-
-		var schemaDoc any
-		if err := json.Unmarshal(schemaData, &schemaDoc); err != nil {
-			projectUpdateSchemaCompileError = fmt.Errorf("failed to parse project update schema: %w", err)
-			return
-		}
-
-		compiler := jsonschema.NewCompiler()
-		schemaURL := "project-update.json"
-		if err := compiler.AddResource(schemaURL, schemaDoc); err != nil {
-			projectUpdateSchemaCompileError = fmt.Errorf("failed to add project update schema resource: %w", err)
-			return
-		}
-
-		schema, err := compiler.Compile(schemaURL)
-		if err != nil {
-			projectUpdateSchemaCompileError = fmt.Errorf("failed to compile project update schema: %w", err)
-			return
-		}
-
-		compiledProjectUpdateSchema = schema
-	})
-
-	return compiledProjectUpdateSchema, projectUpdateSchemaCompileError
-}
-
-// ValidateProjectUpdatePayload enforces the governed update-project contract.
-//
-// This validator is intentionally deterministic:
-// - JSON Schema enforces payload shape.
-// - Semantic checks enforce equality constraints that JSON Schema cannot.
-//
-// payload is expected to be a YAML/JSON-decoded structure (map[string]any, etc.).
-func ValidateProjectUpdatePayload(payload any, expectedProjectURL string, expectedCampaignID string) []string {
-	schema, err := getCompiledProjectUpdateSchema()
-	if err != nil {
-		return []string{err.Error()}
-	}
-
-	normalized := normalizeYAMLValue(payload)
-	if err := schema.Validate(normalized); err != nil {
-		return formatValidationErrors(err)
-	}
-
-	var problems []string
-	root, ok := normalized.(map[string]any)
-	if !ok {
-		return []string{"root: payload must be an object"}
-	}
-
-	if expectedProjectURL != "" {
-		if project, _ := root["project"].(string); project != expectedProjectURL {
-			problems = append(problems, fmt.Sprintf("project: must equal %q", expectedProjectURL))
-		}
-	}
-
-	if expectedCampaignID != "" {
-		if campaignID, _ := root["campaign_id"].(string); campaignID != expectedCampaignID {
-			problems = append(problems, fmt.Sprintf("campaign_id: must equal %q", expectedCampaignID))
-		}
-	}
-
-	fields, _ := root["fields"].(map[string]any)
-	if expectedCampaignID != "" {
-		if backfillCampaignID, ok := fields["campaign_id"].(string); ok && backfillCampaignID != expectedCampaignID {
-			problems = append(problems, fmt.Sprintf("fields.campaign_id: must equal %q", expectedCampaignID))
-		}
-	}
-
-	// Deterministic normalization: for status-only updates, never allow other writes.
-	if len(fields) > 1 {
-		// Schema already enforces the full-backfill set, but we keep a stable message for checklist enforcement.
-		requiredKeys := []string{"campaign_id", "worker_workflow", "target_repo", "priority", "size", "start_date", "end_date"}
-		var missing []string
-		for _, key := range requiredKeys {
-			if _, ok := fields[key]; !ok {
-				missing = append(missing, key)
-			}
-		}
-		if len(missing) > 0 {
-			problems = append(problems, fmt.Sprintf("fields: missing required backfill keys: %s", strings.Join(missing, ", ")))
-		}
-	}
-
-	return problems
-}
-
-// normalizeYAMLValue converts map[any]any (from YAML decoding) into JSON-friendly structures.
-func normalizeYAMLValue(value any) any {
-	switch v := value.(type) {
-	case map[string]any:
-		out := make(map[string]any, len(v))
-		for key, child := range v {
-			out[key] = normalizeYAMLValue(child)
-		}
-		return out
-	case map[any]any:
-		out := make(map[string]any, len(v))
-		for key, child := range v {
-			keyStr, ok := key.(string)
-			if !ok {
-				keyStr = fmt.Sprintf("%v", key)
-			}
-			out[keyStr] = normalizeYAMLValue(child)
-		}
-		return out
-	case []any:
-		out := make([]any, 0, len(v))
-		for _, child := range v {
-			out = append(out, normalizeYAMLValue(child))
-		}
-		return out
-	default:
-		return value
-	}
-}
diff --git a/pkg/campaign/project_views_test.go b/pkg/campaign/project_views_test.go
deleted file mode 100644
index 8b601483a37..00000000000
--- a/pkg/campaign/project_views_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import "testing"
-
-func TestParseProjectURL(t *testing.T) {
-	tests := []struct {
-		name      string
-		input     string
-		wantScope string
-		wantOwner string
-		wantNum   int
-		wantErr   bool
-	}{
-		{
-			name:      "org project",
-			input:     "https://github.com/orgs/githubnext/projects/123",
-			wantScope: "orgs",
-			wantOwner: "githubnext",
-			wantNum:   123,
-		},
-		{
-			name:      "user project",
-			input:     "https://github.com/users/mnkiefer/projects/7",
-			wantScope: "users",
-			wantOwner: "mnkiefer",
-			wantNum:   7,
-		},
-		{
-			name:    "invalid url",
-			input:   "https://github.com/githubnext/projects/123",
-			wantErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			got, err := parseProjectURL(tt.input)
-			if tt.wantErr {
-				if err == nil {
-					t.Fatalf("expected error, got nil")
-				}
-				return
-			}
-			if err != nil {
-				t.Fatalf("parseProjectURL error: %v", err)
-			}
-			if got.scope != tt.wantScope || got.ownerLogin != tt.wantOwner || got.projectNumber != tt.wantNum {
-				t.Fatalf("parseProjectURL(%q) = %+v, want scope=%q owner=%q number=%d", tt.input, got, tt.wantScope, tt.wantOwner, tt.wantNum)
-			}
-		})
-	}
-}
diff --git a/pkg/campaign/prompt_sections.go b/pkg/campaign/prompt_sections.go
deleted file mode 100644
index 118b2ba0832..00000000000
--- a/pkg/campaign/prompt_sections.go
+++ /dev/null
@@ -1,19 +0,0 @@
-package campaign
-
-import (
-	"fmt"
-	"strings"
-)
-
-// AppendPromptSection appends a section with a header and body to the builder.
-// This is used to structure campaign orchestrator prompts with clear section boundaries.
-func AppendPromptSection(b *strings.Builder, title, body string) {
-	body = strings.TrimSpace(body)
-	if body == "" {
-		return
-	}
-
-	// Titles should be single-line to keep markdown structure stable.
-	title = strings.Join(strings.Fields(title), " ")
-	fmt.Fprintf(b, "\n---\n# %s\n---\n%s", title, body)
-}
diff --git a/pkg/campaign/schemas/campaign_spec_schema.json b/pkg/campaign/schemas/campaign_spec_schema.json
deleted file mode 100644
index 45534f0be36..00000000000
--- a/pkg/campaign/schemas/campaign_spec_schema.json
+++ /dev/null
@@ -1,386 +0,0 @@
-{
-  "$schema": "http://json-schema.org/draft-07/schema#",
-  "$id": "https://github.com/githubnext/gh-aw/schemas/campaign_spec_schema.json",
-  "title": "Campaign Spec Schema",
-  "description": "Schema for GitHub Agentic Workflows campaign specifications",
-  "type": "object",
-  "properties": {
-    "id": {
-      "type": "string",
-      "description": "Unique identifier for the campaign (lowercase letters, digits, and hyphens only)",
-      "pattern": "^[a-z0-9-]+$",
-      "minLength": 1
-    },
-    "name": {
-      "type": "string",
-      "description": "Human-readable name for the campaign",
-      "minLength": 1
-    },
-    "description": {
-      "type": "string",
-      "description": "Brief description of the campaign"
-    },
-    "project-url": {
-      "type": "string",
-      "description": "URL of the GitHub Project used as the primary campaign dashboard",
-      "format": "uri",
-      "pattern": "/projects/",
-      "minLength": 1
-    },
-    "version": {
-      "type": "string",
-      "description": "Spec version (e.g., v1)",
-      "pattern": "^v[0-9]+$",
-      "default": "v1"
-    },
-    "workflows": {
-      "type": "array",
-      "description": "List of workflow IDs (basenames without .md) implementing this campaign",
-      "items": {
-        "type": "string",
-        "minLength": 1
-      },
-      "minItems": 1
-    },
-    "scope": {
-      "type": "array",
-      "description": "Optional scope selectors defining which repositories and organizations this campaign is allowed to discover and operate on. When omitted, defaults to the current repository where the campaign is defined.",
-      "items": {
-        "type": "string",
-        "minLength": 1,
-        "anyOf": [
-          {
-            "pattern": "^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$",
-            "description": "Repository selector: owner/repo"
-          },
-          {
-            "pattern": "^org:[a-zA-Z0-9_.-]+$",
-            "description": "Organization selector: org:<name>"
-          },
-          {
-            "pattern": "^[a-zA-Z0-9_.-]+/\\*$",
-            "description": "Organization selector (sugar): <org>/*"
-          }
-        ]
-      },
-      "minItems": 1
-    },
-    "memory-paths": {
-      "type": "array",
-      "description": "Paths where this campaign writes its repo-memory",
-      "items": {
-        "type": "string",
-        "minLength": 1
-      }
-    },
-    "metrics-glob": {
-      "type": "string",
-      "description": "Glob pattern to locate JSON metrics snapshots in memory/campaigns branch"
-    },
-    "cursor-glob": {
-      "type": "string",
-      "description": "Glob pattern to locate a durable cursor/checkpoint file in memory/campaigns branch",
-      "pattern": "^memory/campaigns/",
-      "minLength": 1
-    },
-    "owners": {
-      "type": "array",
-      "description": "Primary human owners for this campaign",
-      "items": {
-        "type": "string",
-        "minLength": 1
-      }
-    },
-    "executive-sponsors": {
-      "type": "array",
-      "description": "Executive stakeholders or sponsors",
-      "items": {
-        "type": "string",
-        "minLength": 1
-      }
-    },
-    "risk-level": {
-      "type": "string",
-      "description": "Risk level (e.g., low, medium, high)",
-      "enum": ["low", "medium", "high"]
-    },
-    "state": {
-      "type": "string",
-      "description": "Lifecycle stage of the campaign",
-      "enum": ["planned", "active", "paused", "completed", "archived"]
-    },
-    "tags": {
-      "type": "array",
-      "description": "Free-form categorization tags",
-      "items": {
-        "type": "string",
-        "minLength": 1
-      }
-    },
-    "allowed-safe-outputs": {
-      "type": "array",
-      "description": "Safe-outputs operations this campaign is expected to use",
-      "items": {
-        "type": "string",
-        "minLength": 1
-      }
-    },
-    "approval-policy": {
-      "type": "object",
-      "description": "Approval expectations for this campaign",
-      "properties": {
-        "required-approvals": {
-          "type": "integer",
-          "description": "Number of required approvals",
-          "minimum": 0
-        },
-        "required-roles": {
-          "type": "array",
-          "description": "Required roles for approval",
-          "items": {
-            "type": "string",
-            "minLength": 1
-          }
-        },
-        "change-control": {
-          "type": "boolean",
-          "description": "Whether change control is required"
-        }
-      },
-      "additionalProperties": false
-    },
-    "launcher": {
-      "type": "object",
-      "description": "Optional launcher layer configuration. When omitted, the launcher is enabled by default.",
-      "properties": {
-        "enabled": {
-          "type": "boolean",
-          "description": "Whether to generate and compile the campaign launcher workflow"
-        }
-      },
-      "additionalProperties": false
-    },
-    "engine": {
-      "type": "string",
-      "description": "AI engine to use for the campaign orchestrator",
-      "enum": ["copilot", "claude", "codex", "custom"]
-    },
-    "bootstrap": {
-      "type": "object",
-      "description": "Initial work item creation strategy when discovery returns zero items",
-      "properties": {
-        "mode": {
-          "type": "string",
-          "description": "Bootstrap strategy: seeder-worker (dispatch scanner), project-todos (read from Project board), manual (skip bootstrap)",
-          "enum": ["seeder-worker", "project-todos", "manual"]
-        },
-        "seeder-worker": {
-          "type": "object",
-          "description": "Configuration for dispatching a seeder/scanner worker. Only used when mode is 'seeder-worker'.",
-          "properties": {
-            "workflow-id": {
-              "type": "string",
-              "description": "Workflow identifier (basename without .md) to dispatch",
-              "minLength": 1
-            },
-            "payload": {
-              "type": "object",
-              "description": "JSON payload to send to the seeder worker"
-            },
-            "max-items": {
-              "type": "integer",
-              "description": "Maximum number of work items the seeder should return (0 = worker default)",
-              "minimum": 0
-            }
-          },
-          "required": ["workflow-id", "payload"],
-          "additionalProperties": false
-        },
-        "project-todos": {
-          "type": "object",
-          "description": "Configuration for reading from Project board's Todo column. Only used when mode is 'project-todos'.",
-          "properties": {
-            "status-field": {
-              "type": "string",
-              "description": "Name of the Project status field to filter on (default: 'Status')",
-              "minLength": 1
-            },
-            "todo-value": {
-              "type": "string",
-              "description": "Status value indicating a work item is ready to start (default: 'Todo')",
-              "minLength": 1
-            },
-            "max-items": {
-              "type": "integer",
-              "description": "Maximum number of Todo items to process per run (0 = governance default)",
-              "minimum": 0
-            },
-            "require-fields": {
-              "type": "array",
-              "description": "Project field names that must be populated for an item to be valid work",
-              "items": {
-                "type": "string",
-                "minLength": 1
-              }
-            }
-          },
-          "additionalProperties": false
-        }
-      },
-      "required": ["mode"],
-      "additionalProperties": false
-    },
-    "workers": {
-      "type": "array",
-      "description": "Worker workflow metadata including capabilities, payload schemas, and output labeling contracts",
-      "items": {
-        "type": "object",
-        "properties": {
-          "id": {
-            "type": "string",
-            "description": "Workflow identifier (basename without .md)",
-            "minLength": 1
-          },
-          "name": {
-            "type": "string",
-            "description": "Human-readable worker name",
-            "minLength": 1
-          },
-          "description": {
-            "type": "string",
-            "description": "Description of what the worker does",
-            "minLength": 1
-          },
-          "capabilities": {
-            "type": "array",
-            "description": "Types of work this worker can perform",
-            "items": {
-              "type": "string",
-              "minLength": 1
-            },
-            "minItems": 1
-          },
-          "payload-schema": {
-            "type": "object",
-            "description": "Expected structure of the JSON payload",
-            "additionalProperties": {
-              "type": "object",
-              "properties": {
-                "type": {
-                  "type": "string",
-                  "description": "Field data type",
-                  "enum": ["string", "number", "boolean", "array", "object"]
-                },
-                "description": {
-                  "type": "string",
-                  "description": "Field description",
-                  "minLength": 1
-                },
-                "required": {
-                  "type": "boolean",
-                  "description": "Whether this field is required"
-                },
-                "example": {
-                  "description": "Sample value for this field"
-                }
-              },
-              "required": ["type", "description"],
-              "additionalProperties": false
-            }
-          },
-          "output-labeling": {
-            "type": "object",
-            "description": "Labeling and metadata contract for worker outputs",
-            "properties": {
-              "labels": {
-                "type": "array",
-                "description": "Labels the worker applies to created items (in addition to the campaign's tracker-label)",
-                "items": {
-                  "type": "string",
-                  "minLength": 1
-                }
-              },
-              "key-in-title": {
-                "type": "boolean",
-                "description": "Whether worker includes deterministic key in title"
-              },
-              "key-format": {
-                "type": "string",
-                "description": "Key format when key-in-title is true",
-                "minLength": 1
-              },
-              "metadata-fields": {
-                "type": "array",
-                "description": "Project fields the worker populates",
-                "items": {
-                  "type": "string",
-                  "minLength": 1
-                }
-              }
-            },
-            "required": ["key-in-title"],
-            "additionalProperties": false
-          },
-          "idempotency-strategy": {
-            "type": "string",
-            "description": "How the worker ensures idempotent execution",
-            "enum": ["branch-based", "pr-title-based", "issue-title-based", "cursor-based"]
-          },
-          "priority": {
-            "type": "integer",
-            "description": "Worker priority for deterministic selection (higher = higher priority)",
-            "minimum": 0
-          }
-        },
-        "required": ["id", "capabilities", "payload-schema", "output-labeling", "idempotency-strategy"],
-        "additionalProperties": false
-      }
-    },
-    "governance": {
-      "type": "object",
-      "description": "Lightweight pacing and opt-out policies for campaign coordinator workflows (launcher/orchestrator)",
-      "properties": {
-        "max-new-items-per-run": {
-          "type": "integer",
-          "description": "Maximum number of new items (issues/PRs) the launcher should add to the Project board per run",
-          "minimum": 0
-        },
-        "max-discovery-items-per-run": {
-          "type": "integer",
-          "description": "Maximum number of candidate issues/PRs to scan during discovery in a single run",
-          "minimum": 0
-        },
-        "max-discovery-pages-per-run": {
-          "type": "integer",
-          "description": "Maximum number of result pages to fetch during discovery in a single run",
-          "minimum": 0
-        },
-        "opt-out-labels": {
-          "type": "array",
-          "description": "Labels that opt an issue/PR out of campaign tracking",
-          "items": {
-            "type": "string",
-            "minLength": 1
-          }
-        },
-        "do-not-downgrade-done-items": {
-          "type": "boolean",
-          "description": "If true, prevent moving Project status backwards (e.g., Done -> In Progress) when issues/PRs are reopened"
-        },
-        "max-project-updates-per-run": {
-          "type": "integer",
-          "description": "Maximum number of update-project safe-output operations per run for generated coordinator workflows",
-          "minimum": 0
-        },
-        "max-comments-per-run": {
-          "type": "integer",
-          "description": "Maximum number of add-comment safe-output operations per run for generated coordinator workflows",
-          "minimum": 0
-        }
-      },
-      "additionalProperties": false
-    }
-  },
-  "required": ["id", "name", "project-url"],
-  "additionalProperties": false
-}
diff --git a/pkg/campaign/schemas/project_update_schema.json b/pkg/campaign/schemas/project_update_schema.json
deleted file mode 100644
index 7f51c670d69..00000000000
--- a/pkg/campaign/schemas/project_update_schema.json
+++ /dev/null
@@ -1,90 +0,0 @@
-{
-  "$schema": "https://json-schema.org/draft/2020-12/schema",
-  "title": "update-project payload",
-  "type": "object",
-  "$comment": "Governed contract for campaign orchestrator writes. See prompts/project_update_instructions.md (law) and prompts/project_update_contract_checklist.md (enforcement). This schema validates payload shape only; it cannot enforce read/write phase separation or equality to a specific campaign ID value.",
-  "additionalProperties": false,
-  "required": ["project", "campaign_id", "content_type", "content_number", "fields"],
-  "properties": {
-    "project": {
-      "type": "string",
-      "minLength": 1
-    },
-    "campaign_id": {
-      "type": "string",
-      "minLength": 1,
-      "maxLength": 200
-    },
-    "content_type": {
-      "type": "string",
-      "enum": ["issue", "pull_request"]
-    },
-    "content_number": {
-      "type": "integer",
-      "minimum": 1
-    },
-    "fields": {
-      "$ref": "#/$defs/fields"
-    }
-  },
-  "$defs": {
-    "isoDate": {
-      "type": "string",
-      "pattern": "^\\d{4}-\\d{2}-\\d{2}$"
-    },
-    "fields": {
-      "$comment": "Either a status-only update, or a full backfill payload. Normal status updates MUST NOT overwrite other fields with defaults.",
-      "oneOf": [
-        {
-          "type": "object",
-          "additionalProperties": false,
-          "properties": {
-            "status": {
-              "$ref": "#/$defs/status"
-            }
-          },
-          "required": ["status"],
-          "maxProperties": 1
-        },
-        {
-          "type": "object",
-          "additionalProperties": false,
-          "properties": {
-            "status": {
-              "$ref": "#/$defs/status"
-            },
-            "campaign_id": {
-              "type": "string",
-              "minLength": 1,
-              "maxLength": 200
-            },
-            "worker_workflow": {
-              "type": "string",
-              "minLength": 1,
-              "maxLength": 200
-            },
-            "target_repo": {
-              "type": "string",
-              "pattern": "^[^/]+/[^/]+$"
-            },
-            "priority": {
-              "type": "string",
-              "enum": ["High", "Medium", "Low"]
-            },
-            "size": {
-              "type": "string",
-              "enum": ["Small", "Medium", "Large"]
-            },
-            "start_date": {
-              "$ref": "#/$defs/isoDate"
-            },
-            "end_date": {
-              "$ref": "#/$defs/isoDate"
-            }
-          },
-          "required": ["status", "campaign_id", "worker_workflow", "target_repo", "priority", "size", "start_date", "end_date"]
-        }
-      ]
-    }
-  }
-}
diff --git a/pkg/campaign/scope.go b/pkg/campaign/scope.go
deleted file mode 100644
index a8d0dd96e21..00000000000
--- a/pkg/campaign/scope.go
+++ /dev/null
@@ -1,100 +0,0 @@
-package campaign
-
-import (
-	"fmt"
-	"regexp"
-	"strings"
-)
-
-type ParsedScope struct {
-	Repos []string
-	Orgs  []string
-}
-
-var (
-	repoSelectorPattern = regexp.MustCompile(`^[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+$`)
-	orgNamePattern      = regexp.MustCompile(`^[a-zA-Z0-9_.-]+$`)
-)
-
-func parseScopeSelectors(selectors []string) (ParsedScope, []string) {
-	var parsed ParsedScope
-	var problems []string
-
-	for _, raw := range selectors {
-		trimmed := strings.TrimSpace(raw)
-		if trimmed == "" {
-			problems = append(problems, "scope must not contain empty entries - remove empty strings from the list")
-			continue
-		}
-
-		// org:<name>
-		if strings.HasPrefix(trimmed, "org:") {
-			org := strings.TrimSpace(strings.TrimPrefix(trimmed, "org:"))
-			if org == "" {
-				problems = append(problems, "scope entry 'org:' must include an organization name - example: 'org:github'")
-				continue
-			}
-			if strings.Contains(org, "/") {
-				problems = append(problems, fmt.Sprintf("scope entry '%s' must be 'org:<name>' (no slashes) - example: 'org:github'", trimmed))
-				continue
-			}
-			if strings.Contains(org, "*") {
-				problems = append(problems, fmt.Sprintf("scope entry '%s' cannot contain wildcards - example: 'org:github'", trimmed))
-				continue
-			}
-			if !orgNamePattern.MatchString(org) {
-				problems = append(problems, fmt.Sprintf("scope entry '%s' has an invalid organization name - example: 'org:github'", trimmed))
-				continue
-			}
-			parsed.Orgs = appendUniqueString(parsed.Orgs, org)
-			continue
-		}
-
-		// Optional sugar: <org>/*
-		if strings.HasSuffix(trimmed, "/*") && strings.Count(trimmed, "/") == 1 && !strings.Contains(trimmed, ":") {
-			org := strings.TrimSuffix(trimmed, "/*")
-			if org == "" {
-				problems = append(problems, fmt.Sprintf("scope entry '%s' must include an organization name - example: 'org:github'", trimmed))
-				continue
-			}
-			if strings.Contains(org, "*") {
-				problems = append(problems, fmt.Sprintf("scope entry '%s' cannot contain wildcards - example: 'org:github'", trimmed))
-				continue
-			}
-			if !orgNamePattern.MatchString(org) {
-				problems = append(problems, fmt.Sprintf("scope entry '%s' has an invalid organization name - example: 'org:github'", trimmed))
-				continue
-			}
-			parsed.Orgs = appendUniqueString(parsed.Orgs, org)
-			continue
-		}
-
-		if strings.Contains(trimmed, "*") {
-			problems = append(problems, fmt.Sprintf("scope entry '%s' cannot contain wildcards - list repositories explicitly or use 'org:<name>' for organization-wide scope", trimmed))
-			continue
-		}
-
-		if strings.Contains(trimmed, ":") {
-			problems = append(problems, fmt.Sprintf("scope entry '%s' is not recognized - valid formats: 'owner/repo' or 'org:<name>'", trimmed))
-			continue
-		}
-
-		if repoSelectorPattern.MatchString(trimmed) {
-			parsed.Repos = appendUniqueString(parsed.Repos, trimmed)
-			continue
-		}
-
-		problems = append(problems, fmt.Sprintf("scope entry '%s' must be 'owner/repo' or 'org:<name>' - example: 'github/docs' or 'org:github'", trimmed))
-	}
-
-	return parsed, problems
-}
-
-func appendUniqueString(values []string, value string) []string {
-	for _, v := range values {
-		if v == value {
-			return values
-		}
-	}
-	return append(values, value)
-}
diff --git a/pkg/campaign/spec.go b/pkg/campaign/spec.go
deleted file mode 100644
index d53481138c3..00000000000
--- a/pkg/campaign/spec.go
+++ /dev/null
@@ -1,333 +0,0 @@
-package campaign
-
-// CampaignSpec defines a first-class campaign configuration loaded from
-// YAML frontmatter in Markdown files.
-//
-// Files are discovered from the local repository under:
-//
-//	.github/workflows/*.campaign.md
-//
-// This provides a thin, declarative layer on top of existing agentic
-// workflows and repo-memory conventions.
-type CampaignSpec struct {
-	ID          string `yaml:"id" json:"id" console:"header:ID"`
-	Name        string `yaml:"name" json:"name" console:"header:Name,maxlen:30"`
-	Description string `yaml:"description,omitempty" json:"description,omitempty" console:"header:Description,omitempty,maxlen:60"`
-
-	// ProjectURL points to the GitHub Project used as the primary campaign
-	// dashboard.
-	ProjectURL string `yaml:"project-url,omitempty" json:"project_url,omitempty" console:"header:Project URL,omitempty,maxlen:40"`
-
-	// Version is an optional spec version string (for example: v1).
-	// When omitted, it defaults to v1 during validation.
-	Version string `yaml:"version,omitempty" json:"version,omitempty" console:"header:Version,omitempty"`
-
-	// Workflows associates this campaign with one or more workflow IDs
-	// (basename of the Markdown file without .md).
-	Workflows []string `yaml:"workflows,omitempty" json:"workflows,omitempty" console:"header:Workflows,omitempty,maxlen:40"`
-
-	// TrackerLabel is an optional label used to discover worker-created issues/PRs
-	// (for example: campaign:security-q1-2025). When set, the discovery precomputation
-	// step will search for items with this label.
-	TrackerLabel string `yaml:"tracker-label,omitempty" json:"tracker_label,omitempty" console:"header:Tracker Label,omitempty,maxlen:40"`
-
-	// Scope defines the explicit set of repositories and organizations that this
-	// campaign is allowed to discover and operate on.
-	//
-	// Supported selectors:
-	//   - "owner/repo" (specific repository)
-	//   - "org:<name>" (all repositories in an organization)
-	//
-	// When omitted, it defaults to the current repository where the campaign is defined.
-	Scope []string `yaml:"scope,omitempty" json:"scope,omitempty" console:"header:Scope,omitempty,maxlen:60"`
-
-	// MemoryPaths documents where this campaign writes its repo-memory
-	// (for example: memory/campaigns/incident-response/**).
-	MemoryPaths []string `yaml:"memory-paths,omitempty" json:"memory_paths,omitempty" console:"header:Memory Paths,omitempty,maxlen:40"`
-
-	// MetricsGlob is an optional glob (relative to the repository root)
-	// used to locate JSON metrics snapshots stored in the
-	// memory/campaigns branch. When set, `gh aw campaign status` will
-	// attempt to read the latest matching metrics file and surface a few
-	// key fields.
-	MetricsGlob string `yaml:"metrics-glob,omitempty" json:"metrics_glob,omitempty" console:"header:Metrics Glob,omitempty,maxlen:30"`
-
-	// CursorGlob is an optional glob (relative to the repository root)
-	// used to locate a durable cursor/checkpoint file stored in the
-	// memory/campaigns branch. When set, generated coordinator workflows
-	// will be instructed to continue incremental discovery from this cursor
-	// and `gh aw campaign status` will surface its freshness.
-	CursorGlob string `yaml:"cursor-glob,omitempty" json:"cursor_glob,omitempty" console:"header:Cursor Glob,omitempty,maxlen:30"`
-
-	// Owners lists the primary human owners for this campaign.
-	Owners []string `yaml:"owners,omitempty" json:"owners,omitempty" console:"header:Owners,omitempty,maxlen:30"`
-
-	// ExecutiveSponsors lists executive stakeholders or sponsors who are
-	// accountable for the outcome of this campaign.
-	ExecutiveSponsors []string `yaml:"executive-sponsors,omitempty" json:"executive_sponsors,omitempty" console:"header:Executive Sponsors,omitempty,maxlen:30"`
-
-	// RiskLevel is an optional free-form field (e.g. low/medium/high).
-	RiskLevel string `yaml:"risk-level,omitempty" json:"risk_level,omitempty" console:"header:Risk Level,omitempty"`
-
-	// State describes the lifecycle stage of the campaign definition.
-	// Valid values are: planned, active, paused, completed, archived.
-	State string `yaml:"state,omitempty" json:"state,omitempty" console:"header:State,omitempty"`
-
-	// Tags provide free-form categorization for reporting (for example:
-	// security, modernization, rollout).
-	Tags []string `yaml:"tags,omitempty" json:"tags,omitempty" console:"header:Tags,omitempty,maxlen:30"`
-
-	// AllowedSafeOutputs documents which safe-outputs operations this
-	// campaign is expected to use (for example: create-issue,
-	// create-pull-request). This is currently informational but can be
-	// enforced by validation in the future.
-	AllowedSafeOutputs []string `yaml:"allowed-safe-outputs,omitempty" json:"allowed_safe_outputs,omitempty" console:"header:Allowed Safe Outputs,omitempty,maxlen:30"`
-
-	// Governance configures lightweight pacing and opt-out policies for campaign
-	// orchestrator workflows. These guardrails are primarily enforced through
-	// generated prompts and safe-output maxima.
-	Governance *CampaignGovernancePolicy `yaml:"governance,omitempty" json:"governance,omitempty"`
-
-	// ApprovalPolicy describes high-level approval expectations for this
-	// campaign (for example: number of approvals and required roles).
-	ApprovalPolicy *CampaignApprovalPolicy `yaml:"approval-policy,omitempty" json:"approval-policy,omitempty"`
-
-	// Engine specifies the AI engine to use for the campaign orchestrator.
-	// Valid values: copilot, claude, codex, custom.
-	// Default: copilot (when not specified).
-	Engine string `yaml:"engine,omitempty" json:"engine,omitempty" console:"header:Engine,omitempty"`
-
-	// Bootstrap defines the initial work item creation strategy when discovery
-	// returns zero items. This provides a way to seed the campaign with initial
-	// work when there are no worker-created items to discover.
-	Bootstrap *CampaignBootstrapConfig `yaml:"bootstrap,omitempty" json:"bootstrap,omitempty"`
-
-	// Workers defines metadata for worker workflows, including their capabilities,
-	// payload schemas, and output labeling contracts. This enables deterministic
-	// worker selection and ensures worker outputs are discoverable.
-	Workers []WorkerMetadata `yaml:"workers,omitempty" json:"workers,omitempty"`
-
-	// ConfigPath is populated at load time with the relative path of
-	// the YAML file on disk, to help users locate definitions.
-	ConfigPath string `yaml:"-" json:"config_path" console:"header:Config Path,maxlen:60"`
-}
-
-// CampaignGovernancePolicy captures lightweight pacing and opt-out policies.
-// This is intentionally scoped to what gh-aw can apply safely and consistently
-// via prompts and safe-output job limits.
-type CampaignGovernancePolicy struct {
-	// MaxNewItemsPerRun caps how many new items (issues/PRs) the launcher should
-	// add to the Project board per run. 0 means "use defaults".
-	MaxNewItemsPerRun int `yaml:"max-new-items-per-run,omitempty" json:"max_new_items_per_run,omitempty"`
-
-	// MaxDiscoveryItemsPerRun caps how many candidate issues/PRs the launcher
-	// and orchestrator may scan during discovery in a single run.
-	// 0 means "use defaults".
-	MaxDiscoveryItemsPerRun int `yaml:"max-discovery-items-per-run,omitempty" json:"max_discovery_items_per_run,omitempty"`
-
-	// MaxDiscoveryPagesPerRun caps how many pages of results the launcher and
-	// orchestrator may fetch in a single run.
-	// 0 means "use defaults".
-	MaxDiscoveryPagesPerRun int `yaml:"max-discovery-pages-per-run,omitempty" json:"max_discovery_pages_per_run,omitempty"`
-
-	// OptOutLabels is a list of labels that opt an issue/PR out of campaign
-	// tracking. Items with any of these labels should be ignored by launcher/
-	// orchestrator.
-	OptOutLabels []string `yaml:"opt-out-labels,omitempty" json:"opt_out_labels,omitempty"`
-
-	// DoNotDowngradeDoneItems prevents moving Project status backwards (e.g.
-	// Done -> In Progress) if the underlying issue/PR is reopened.
-	DoNotDowngradeDoneItems *bool `yaml:"do-not-downgrade-done-items,omitempty" json:"do_not_downgrade_done_items,omitempty"`
-
-	// MaxProjectUpdatesPerRun controls the update-project safe-output maximum
-	// for generated coordinator workflows. 0 means "use defaults".
-	MaxProjectUpdatesPerRun int `yaml:"max-project-updates-per-run,omitempty" json:"max_project_updates_per_run,omitempty"`
-
-	// MaxCommentsPerRun controls the add-comment safe-output maximum for
-	// generated coordinator workflows. 0 means "use defaults".
-	MaxCommentsPerRun int `yaml:"max-comments-per-run,omitempty" json:"max_comments_per_run,omitempty"`
-}
-
-// CampaignApprovalPolicy captures basic approval expectations for a
-// campaign. It is intentionally lightweight and advisory; enforcement
-// is left to workflows and organizational process.
-type CampaignApprovalPolicy struct {
-	RequiredApprovals int      `yaml:"required-approvals,omitempty" json:"required-approvals,omitempty"`
-	RequiredRoles     []string `yaml:"required-roles,omitempty" json:"required-roles,omitempty"`
-	ChangeControl     bool     `yaml:"change-control,omitempty" json:"change-control,omitempty"`
-}
-
-// CampaignRuntimeStatus represents the live status of a campaign, including
-// compiled workflow state and optional metrics/cursor info.
-type CampaignRuntimeStatus struct {
-	ID        string   `json:"id" console:"header:ID"`
-	Name      string   `json:"name" console:"header:Name"`
-	Workflows []string `json:"workflows,omitempty" console:"header:Workflows,omitempty"`
-	Compiled  string   `json:"compiled" console:"header:Compiled"`
-
-	// Optional metrics from repo-memory (when MetricsGlob is set and a
-	// matching JSON snapshot is found on the memory/campaigns branch).
-	MetricsTasksTotal          int     `json:"metrics_tasks_total,omitempty" console:"header:Tasks Total,omitempty"`
-	MetricsTasksCompleted      int     `json:"metrics_tasks_completed,omitempty" console:"header:Tasks Completed,omitempty"`
-	MetricsVelocityPerDay      float64 `json:"metrics_velocity_per_day,omitempty" console:"header:Velocity/Day,omitempty"`
-	MetricsEstimatedCompletion string  `json:"metrics_estimated_completion,omitempty" console:"header:ETA,omitempty"`
-
-	// Optional durable cursor/checkpoint info from repo-memory.
-	CursorPath      string `json:"cursor_path,omitempty" console:"header:Cursor Path,omitempty,maxlen:40"`
-	CursorUpdatedAt string `json:"cursor_updated_at,omitempty" console:"header:Cursor Updated,omitempty,maxlen:30"`
-}
-
-// CampaignMetricsSnapshot describes the JSON structure used by campaign
-// metrics snapshots written into the memory/campaigns branch.
-//
-// This mirrors the example in the campaigns guide:
-//
-//	{
-//	  "date": "2025-01-16",
-//	  "campaign_id": "security-q1-2025",
-//	  "tasks_total": 200,
-//	  "tasks_completed": 15,
-//	  "tasks_in_progress": 30,
-//	  "tasks_blocked": 5,
-//	  "velocity_per_day": 7.5,
-//	  "estimated_completion": "2025-02-12"
-//	}
-type CampaignMetricsSnapshot struct {
-	Date                string  `json:"date"`            // Required: YYYY-MM-DD format
-	CampaignID          string  `json:"campaign_id"`     // Required: campaign identifier
-	TasksTotal          int     `json:"tasks_total"`     // Required: total task count (>= 0)
-	TasksCompleted      int     `json:"tasks_completed"` // Required: completed task count (>= 0)
-	TasksInProgress     int     `json:"tasks_in_progress,omitempty"`
-	TasksBlocked        int     `json:"tasks_blocked,omitempty"`
-	VelocityPerDay      float64 `json:"velocity_per_day,omitempty"`
-	EstimatedCompletion string  `json:"estimated_completion,omitempty"`
-}
-
-// CampaignValidationResult represents the result of validating a campaign spec.
-type CampaignValidationResult struct {
-	ID         string   `json:"id" console:"header:ID"`
-	Name       string   `json:"name" console:"header:Name"`
-	ConfigPath string   `json:"config_path" console:"header:Config Path"`
-	Problems   []string `json:"problems,omitempty" console:"header:Problems,omitempty"`
-}
-
-// CampaignBootstrapConfig defines how to create initial work items when discovery
-// returns zero items. This provides multiple strategies for bootstrapping a campaign.
-type CampaignBootstrapConfig struct {
-	// Mode determines the bootstrap strategy when no work items are discovered.
-	// Valid values:
-	//   - "seeder-worker": Dispatch a seeder/scanner worker workflow to discover work
-	//   - "project-todos": Read items from Project board's "Todo" column
-	//   - "manual": Skip bootstrap, wait for manual work item creation
-	Mode string `yaml:"mode" json:"mode"`
-
-	// SeederWorker specifies which worker workflow to dispatch for initial scanning.
-	// Only used when Mode is "seeder-worker".
-	SeederWorker *SeederWorkerConfig `yaml:"seeder-worker,omitempty" json:"seeder_worker,omitempty"`
-
-	// ProjectTodos specifies configuration for reading from Project board.
-	// Only used when Mode is "project-todos".
-	ProjectTodos *ProjectTodosConfig `yaml:"project-todos,omitempty" json:"project_todos,omitempty"`
-}
-
-// SeederWorkerConfig defines configuration for dispatching a seeder/scanner worker.
-type SeederWorkerConfig struct {
-	// WorkflowID is the workflow identifier (basename without .md) to dispatch.
-	WorkflowID string `yaml:"workflow-id" json:"workflow_id"`
-
-	// Payload is the JSON payload to send to the seeder worker.
-	// This should conform to the worker's payload schema.
-	Payload map[string]any `yaml:"payload" json:"payload"`
-
-	// MaxItems caps the number of work items the seeder should return.
-	// 0 means use the worker's default.
-	MaxItems int `yaml:"max-items,omitempty" json:"max_items,omitempty"`
-}
-
-// ProjectTodosConfig defines configuration for reading from a Project board's Todo column.
-type ProjectTodosConfig struct {
-	// StatusField is the name of the Project status field to filter on.
-	// Defaults to "Status".
-	StatusField string `yaml:"status-field,omitempty" json:"status_field,omitempty"`
-
-	// TodoValue is the status value that indicates a work item is ready to start.
-	// Defaults to "Todo".
-	TodoValue string `yaml:"todo-value,omitempty" json:"todo_value,omitempty"`
-
-	// MaxItems caps the number of Todo items to process per orchestrator run.
-	// 0 means use the governance default.
-	MaxItems int `yaml:"max-items,omitempty" json:"max_items,omitempty"`
-
-	// RequireFields lists Project field names that must be populated for an
-	// item to be considered valid work. Empty items are skipped.
-	RequireFields []string `yaml:"require-fields,omitempty" json:"require_fields,omitempty"`
-}
-
-// WorkerMetadata defines metadata for a worker workflow, including its capabilities,
-// payload schema, and output labeling contract.
-type WorkerMetadata struct {
-	// ID is the workflow identifier (basename without .md).
-	ID string `yaml:"id" json:"id"`
-
-	// Name is a human-readable name for the worker.
-	Name string `yaml:"name,omitempty" json:"name,omitempty"`
-
-	// Description describes what the worker does.
-	Description string `yaml:"description,omitempty" json:"description,omitempty"`
-
-	// Capabilities lists what types of work this worker can perform.
-	// Examples: "scan-security-alerts", "fix-dependencies", "update-docs"
-	Capabilities []string `yaml:"capabilities" json:"capabilities"`
-
-	// PayloadSchema defines the expected structure of the JSON payload.
-	// This is a map of field names to field types/descriptions.
-	PayloadSchema map[string]WorkerPayloadField `yaml:"payload-schema" json:"payload_schema"`
-
-	// OutputLabeling defines the labeling contract for worker outputs.
-	// This guarantees outputs are discoverable and attributable.
-	OutputLabeling WorkerOutputLabeling `yaml:"output-labeling" json:"output_labeling"`
-
-	// IdempotencyStrategy describes how the worker ensures idempotent execution.
-	// Valid values: "branch-based", "pr-title-based", "issue-title-based", "cursor-based"
-	IdempotencyStrategy string `yaml:"idempotency-strategy" json:"idempotency_strategy"`
-
-	// Priority indicates worker priority for deterministic selection when multiple
-	// workers can handle the same work item. Higher numbers = higher priority.
-	Priority int `yaml:"priority,omitempty" json:"priority,omitempty"`
-}
-
-// WorkerPayloadField defines a single field in the worker payload schema.
-type WorkerPayloadField struct {
-	// Type is the field's data type: "string", "number", "boolean", "array", "object"
-	Type string `yaml:"type" json:"type"`
-
-	// Description explains what this field contains and how it's used.
-	Description string `yaml:"description" json:"description"`
-
-	// Required indicates whether this field must be present in the payload.
-	Required bool `yaml:"required,omitempty" json:"required,omitempty"`
-
-	// Example provides a sample value for this field.
-	Example any `yaml:"example,omitempty" json:"example,omitempty"`
-}
-
-// WorkerOutputLabeling defines the labeling and metadata contract for worker outputs.
-type WorkerOutputLabeling struct {
-	// Labels are the labels the worker applies to created items.
-	// The campaign's tracker-label is automatically applied by the worker
-	// in addition to these labels.
-	// Examples: "security", "automated", "dependencies"
-	Labels []string `yaml:"labels,omitempty" json:"labels,omitempty"`
-
-	// KeyInTitle indicates whether the worker includes a deterministic key in
-	// the title of created items (format: "[{key}] {title}").
-	KeyInTitle bool `yaml:"key-in-title" json:"key_in_title"`
-
-	// KeyFormat describes the key format when KeyInTitle is true.
-	// Example: "campaign-{campaign_id}-{repository}-{work_item_id}"
-	KeyFormat string `yaml:"key-format,omitempty" json:"key_format,omitempty"`
-
-	// MetadataFields lists Project fields the worker populates with work metadata.
-	// Examples: "Campaign Id", "Worker Workflow", "Priority"
-	MetadataFields []string `yaml:"metadata-fields,omitempty" json:"metadata_fields,omitempty"`
-}
diff --git a/pkg/campaign/status.go b/pkg/campaign/status.go
deleted file mode 100644
index 528b690d3b6..00000000000
--- a/pkg/campaign/status.go
+++ /dev/null
@@ -1,226 +0,0 @@
-package campaign
-
-import (
-	"bufio"
-	"bytes"
-	"encoding/json"
-	"fmt"
-	"os"
-	"os/exec"
-	"path"
-	"path/filepath"
-	"strings"
-
-	"github.com/githubnext/gh-aw/pkg/logger"
-)
-
-var statusLog = logger.New("campaign:status")
-
-// ComputeCompiledState inspects the compiled state of all
-// workflows referenced by a campaign. It returns:
-//
-//	"Yes"   - all referenced workflows exist and are compiled & up-to-date
-//	"No"    - at least one workflow exists but is missing a lock file or is stale
-//	"Missing workflow" - at least one referenced workflow markdown file does not exist
-//	"N/A"   - campaign does not reference any workflows
-func ComputeCompiledState(spec CampaignSpec, workflowsDir string) string {
-	statusLog.Printf("Computing compiled state for campaign '%s' with %d workflows", spec.ID, len(spec.Workflows))
-
-	if len(spec.Workflows) == 0 {
-		return "N/A"
-	}
-
-	compiledAll := true
-	missingAny := false
-
-	for _, wf := range spec.Workflows {
-		mdPath := filepath.Join(workflowsDir, wf+".md")
-		lockPath := filepath.Join(workflowsDir, wf+".lock.yml")
-
-		mdInfo, err := os.Stat(mdPath)
-		if err != nil {
-			statusLog.Printf("Workflow markdown not found for campaign '%s': %s", spec.ID, mdPath)
-			missingAny = true
-			compiledAll = false
-			continue
-		}
-
-		lockInfo, err := os.Stat(lockPath)
-		if err != nil {
-			statusLog.Printf("Lock file not found for workflow '%s' in campaign '%s': %s", wf, spec.ID, lockPath)
-			compiledAll = false
-			continue
-		}
-
-		if mdInfo.ModTime().After(lockInfo.ModTime()) {
-			statusLog.Printf("Lock file out of date for workflow '%s' in campaign '%s'", wf, spec.ID)
-			compiledAll = false
-		}
-	}
-
-	if missingAny {
-		return "Missing workflow"
-	}
-	if compiledAll {
-		return "Yes"
-	}
-	return "No"
-}
-
-// FetchMetricsFromRepoMemory attempts to load the latest JSON
-// metrics snapshot matching the provided glob from the
-// memory/campaigns branch. It is best-effort: errors are logged and
-// treated as "no metrics" rather than failing the command.
-func FetchMetricsFromRepoMemory(metricsGlob string) (*CampaignMetricsSnapshot, error) {
-	statusLog.Printf("Fetching metrics from repo memory with glob: %s", metricsGlob)
-
-	if strings.TrimSpace(metricsGlob) == "" {
-		return nil, nil
-	}
-
-	// List all files in the memory/campaigns branch
-	cmd := exec.Command("git", "ls-tree", "-r", "--name-only", "memory/campaigns")
-	output, err := cmd.Output()
-	if err != nil {
-		statusLog.Printf("Unable to list repo-memory branch for metrics (memory/campaigns): %v", err)
-		return nil, nil
-	}
-
-	scanner := bufio.NewScanner(bytes.NewReader(output))
-	var matches []string
-	for scanner.Scan() {
-		pathStr := strings.TrimSpace(scanner.Text())
-		if pathStr == "" {
-			continue
-		}
-		matched, err := path.Match(metricsGlob, pathStr)
-		if err != nil {
-			statusLog.Printf("Invalid metrics_glob '%s': %v", metricsGlob, err)
-			return nil, nil
-		}
-		if matched {
-			matches = append(matches, pathStr)
-		}
-	}
-
-	if len(matches) == 0 {
-		return nil, nil
-	}
-
-	// Pick the lexicographically last match as the "latest" snapshot.
-	latest := matches[0]
-	for _, m := range matches[1:] {
-		if m > latest {
-			latest = m
-		}
-	}
-
-	showArg := fmt.Sprintf("memory/campaigns:%s", latest)
-	showCmd := exec.Command("git", "show", showArg)
-	fileData, err := showCmd.Output()
-	if err != nil {
-		statusLog.Printf("Failed to read metrics file '%s' from memory/campaigns: %v", latest, err)
-		return nil, nil
-	}
-
-	var snapshot CampaignMetricsSnapshot
-	if err := json.Unmarshal(fileData, &snapshot); err != nil {
-		statusLog.Printf("Failed to decode metrics JSON from '%s': %v", latest, err)
-		return nil, nil
-	}
-
-	return &snapshot, nil
-}
-
-// FetchCursorFreshnessFromRepoMemory finds the latest cursor/checkpoint file
-// matching cursorGlob in the memory/campaigns branch and returns the matched
-// path along with a best-effort freshness timestamp derived from git history.
-//
-// Errors are treated as "no cursor" rather than failing the command.
-func FetchCursorFreshnessFromRepoMemory(cursorGlob string) (cursorPath string, cursorUpdatedAt string) {
-	statusLog.Printf("Fetching cursor freshness from repo memory with glob: %s", cursorGlob)
-
-	if strings.TrimSpace(cursorGlob) == "" {
-		return "", ""
-	}
-
-	cmd := exec.Command("git", "ls-tree", "-r", "--name-only", "memory/campaigns")
-	output, err := cmd.Output()
-	if err != nil {
-		statusLog.Printf("Unable to list repo-memory branch for cursor (memory/campaigns): %v", err)
-		return "", ""
-	}
-
-	scanner := bufio.NewScanner(bytes.NewReader(output))
-	var matches []string
-	for scanner.Scan() {
-		pathStr := strings.TrimSpace(scanner.Text())
-		if pathStr == "" {
-			continue
-		}
-		matched, err := path.Match(cursorGlob, pathStr)
-		if err != nil {
-			statusLog.Printf("Invalid cursor_glob '%s': %v", cursorGlob, err)
-			return "", ""
-		}
-		if matched {
-			matches = append(matches, pathStr)
-		}
-	}
-
-	if len(matches) == 0 {
-		return "", ""
-	}
-
-	latest := matches[0]
-	for _, m := range matches[1:] {
-		if m > latest {
-			latest = m
-		}
-	}
-
-	// Best-effort: use git log to get the last commit time for this path
-	// on the memory/campaigns branch.
-	logCmd := exec.Command("git", "log", "-1", "--format=%cI", "memory/campaigns", "--", latest)
-	logOut, err := logCmd.Output()
-	if err != nil {
-		statusLog.Printf("Failed to read cursor freshness for '%s' from memory/campaigns: %v", latest, err)
-		return latest, ""
-	}
-
-	return latest, strings.TrimSpace(string(logOut))
-}
-
-// BuildRuntimeStatus builds a CampaignRuntimeStatus for a single campaign spec.
-func BuildRuntimeStatus(spec CampaignSpec, workflowsDir string) CampaignRuntimeStatus {
-	compiled := ComputeCompiledState(spec, workflowsDir)
-
-	cursorPath, cursorUpdatedAt := FetchCursorFreshnessFromRepoMemory(spec.CursorGlob)
-
-	var metricsTasksTotal, metricsTasksCompleted int
-	var metricsVelocity float64
-	var metricsETA string
-	if strings.TrimSpace(spec.MetricsGlob) != "" {
-		if snapshot, err := FetchMetricsFromRepoMemory(spec.MetricsGlob); err != nil {
-			statusLog.Printf("Failed to fetch metrics for campaign '%s': %v", spec.ID, err)
-		} else if snapshot != nil {
-			metricsTasksTotal = snapshot.TasksTotal
-			metricsTasksCompleted = snapshot.TasksCompleted
-			metricsVelocity = snapshot.VelocityPerDay
-			metricsETA = snapshot.EstimatedCompletion
-		}
-	}
-
-	return CampaignRuntimeStatus{
-		ID:                         spec.ID,
-		Name:                       spec.Name,
-		Workflows:                  spec.Workflows,
-		Compiled:                   compiled,
-		MetricsTasksTotal:          metricsTasksTotal,
-		MetricsTasksCompleted:      metricsTasksCompleted,
-		MetricsVelocityPerDay:      metricsVelocity,
-		MetricsEstimatedCompletion: metricsETA,
-		CursorPath:                 cursorPath,
-		CursorUpdatedAt:            cursorUpdatedAt,
-	}
-}
diff --git a/pkg/campaign/template.go b/pkg/campaign/template.go
deleted file mode 100644
index 1f21b585ea9..00000000000
--- a/pkg/campaign/template.go
+++ /dev/null
@@ -1,200 +0,0 @@
-package campaign
-
-import (
-	"bytes"
-	"fmt"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"strings"
-	"text/template"
-
-	"github.com/githubnext/gh-aw/pkg/logger"
-)
-
-var templateLog = logger.New("campaign:template")
-
-// findGitRoot finds the root directory of the git repository
-func findGitRoot() (string, error) {
-	cmd := exec.Command("git", "rev-parse", "--show-toplevel")
-	output, err := cmd.Output()
-	if err != nil {
-		return "", fmt.Errorf("not in a git repository or git command failed: %w", err)
-	}
-	return strings.TrimSpace(string(output)), nil
-}
-
-// loadTemplate loads a template file from .github/aw/ directory
-func loadTemplate(filename string) (string, error) {
-	gitRoot, err := findGitRoot()
-	if err != nil {
-		return "", fmt.Errorf("failed to find git root: %w", err)
-	}
-
-	templatePath := filepath.Join(gitRoot, ".github", "aw", filename)
-	templateLog.Printf("Loading template from: %s", templatePath)
-
-	content, err := os.ReadFile(templatePath)
-	if err != nil {
-		return "", fmt.Errorf("failed to read template file %s: %w", filename, err)
-	}
-
-	return string(content), nil
-}
-
-// CampaignPromptData holds data for rendering campaign orchestrator prompts.
-type CampaignPromptData struct {
-	// CampaignID is the unique identifier for this campaign.
-	CampaignID string
-
-	// CampaignName is the human-readable name of this campaign.
-	CampaignName string
-
-	// ProjectURL is the GitHub Project URL
-	ProjectURL string
-
-	// CursorGlob is a glob for locating the durable cursor/checkpoint file in repo-memory.
-	CursorGlob string
-
-	// MetricsGlob is a glob for locating the metrics snapshot directory in repo-memory.
-	MetricsGlob string
-
-	// MaxDiscoveryItemsPerRun caps how many candidate items may be scanned during discovery.
-	MaxDiscoveryItemsPerRun int
-
-	// MaxDiscoveryPagesPerRun caps how many pages may be fetched during discovery.
-	MaxDiscoveryPagesPerRun int
-
-	// MaxProjectUpdatesPerRun caps how many project update writes may happen per run.
-	MaxProjectUpdatesPerRun int
-
-	// MaxProjectCommentsPerRun caps how many comments may be written per run.
-	MaxProjectCommentsPerRun int
-
-	// Workflows is the list of worker workflow IDs associated with this campaign.
-	Workflows []string
-
-	// Bootstrap configuration
-	BootstrapMode  string
-	SeederWorkerID string
-	SeederPayload  string
-	SeederMaxItems int
-	StatusField    string
-	TodoValue      string
-	TodoMaxItems   int
-	RequireFields  []string
-	WorkerMetadata []WorkerMetadata
-}
-
-// renderTemplate renders a template string with the given data.
-func renderTemplate(tmplStr string, data CampaignPromptData) (string, error) {
-	// Create custom template functions for Handlebars-style conditionals
-	funcMap := template.FuncMap{
-		"if": func(condition bool) bool {
-			return condition
-		},
-		"add1": func(i int) int {
-			return i + 1
-		},
-	}
-
-	// Parse template with custom delimiters to match Handlebars style
-	tmpl, err := template.New("prompt").
-		Delims("{{", "}}").
-		Funcs(funcMap).
-		Parse(tmplStr)
-	if err != nil {
-		templateLog.Printf("Failed to parse template: %v", err)
-		return "", err
-	}
-
-	var buf bytes.Buffer
-	if err := tmpl.Execute(&buf, data); err != nil {
-		templateLog.Printf("Failed to execute template: %v", err)
-		return "", err
-	}
-
-	return buf.String(), nil
-}
-
-// RenderWorkflowExecution renders the workflow execution instructions with the given data.
-func RenderWorkflowExecution(data CampaignPromptData) string {
-	tmplStr, err := loadTemplate("execute-agentic-campaign-workflow.md")
-	if err != nil {
-		templateLog.Printf("Failed to load workflow execution template: %v", err)
-		return ""
-	}
-
-	result, err := renderTemplate(tmplStr, data)
-	if err != nil {
-		templateLog.Printf("Failed to render workflow execution instructions: %v", err)
-		return ""
-	}
-	return strings.TrimSpace(result)
-}
-
-// RenderOrchestratorInstructions renders the orchestrator instructions with the given data.
-func RenderOrchestratorInstructions(data CampaignPromptData) string {
-	tmplStr, err := loadTemplate("orchestrate-agentic-campaign.md")
-	if err != nil {
-		templateLog.Printf("Failed to load orchestrator instructions template: %v", err)
-		// Fallback to a simple version if template loading fails
-		return "Each time this orchestrator runs, generate a concise status report for this campaign."
-	}
-
-	result, err := renderTemplate(tmplStr, data)
-	if err != nil {
-		templateLog.Printf("Failed to render orchestrator instructions: %v", err)
-		// Fallback to a simple version if template rendering fails
-		return "Each time this orchestrator runs, generate a concise status report for this campaign."
-	}
-	return strings.TrimSpace(result)
-}
-
-// RenderProjectUpdateInstructions renders the project update instructions with the given data
-func RenderProjectUpdateInstructions(data CampaignPromptData) string {
-	tmplStr, err := loadTemplate("update-agentic-campaign-project.md")
-	if err != nil {
-		templateLog.Printf("Failed to load project update instructions template: %v", err)
-		return ""
-	}
-
-	result, err := renderTemplate(tmplStr, data)
-	if err != nil {
-		templateLog.Printf("Failed to render project update instructions: %v", err)
-		return ""
-	}
-	return strings.TrimSpace(result)
-}
-
-// RenderClosingInstructions renders the closing instructions
-func RenderClosingInstructions() string {
-	tmplStr, err := loadTemplate("close-agentic-campaign.md")
-	if err != nil {
-		templateLog.Printf("Failed to load closing instructions template: %v", err)
-		return "Use these details to coordinate workers and track progress."
-	}
-
-	result, err := renderTemplate(tmplStr, CampaignPromptData{})
-	if err != nil {
-		templateLog.Printf("Failed to render closing instructions: %v", err)
-		return "Use these details to coordinate workers and track progress."
-	}
-	return strings.TrimSpace(result)
-}
-
-// RenderBootstrapInstructions renders the bootstrap instructions with the given data.
-func RenderBootstrapInstructions(data CampaignPromptData) string {
-	tmplStr, err := loadTemplate("bootstrap-agentic-campaign.md")
-	if err != nil {
-		templateLog.Printf("Failed to load bootstrap instructions template: %v", err)
-		return ""
-	}
-
-	result, err := renderTemplate(tmplStr, data)
-	if err != nil {
-		templateLog.Printf("Failed to render bootstrap instructions: %v", err)
-		return ""
-	}
-	return strings.TrimSpace(result)
-}
diff --git a/pkg/campaign/template_test.go b/pkg/campaign/template_test.go
deleted file mode 100644
index 9ece54978da..00000000000
--- a/pkg/campaign/template_test.go
+++ /dev/null
@@ -1,174 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"strings"
-	"testing"
-)
-
-func TestRenderOrchestratorInstructions(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		tests := []struct {
-			name          string
-			data          CampaignPromptData
-			shouldContain []string
-		}{
-			{
-				name: "system-agnostic rules",
-				data: CampaignPromptData{},
-				shouldContain: []string{
-					"Orchestrator Instructions",
-					"Traffic and Rate Limits (Required)",
-					"Prefer incremental discovery",
-					"strict pagination budgets",
-					"durable cursor/checkpoint",
-					"On throttling",
-					"Workers are immutable and campaign-agnostic",
-					"Step 1",
-					"Read State",
-					"Step 2",
-					"Make Decisions",
-					"Step 3",
-					"Dispatch Workers",
-					"Step 4",
-					"Report",
-				},
-			},
-			{
-				name: "explicit state management",
-				data: CampaignPromptData{},
-				shouldContain: []string{
-					"Parse discovered items from the manifest",
-					"Discovery cursor is maintained automatically",
-					"Determine desired `status`",
-				},
-			},
-			{
-				name: "separation of concerns",
-				data: CampaignPromptData{},
-				shouldContain: []string{
-					"Reads and writes are separate steps",
-					"never interleave",
-				},
-			},
-			{
-				name: "date field calculation in Step 2",
-				data: CampaignPromptData{},
-				shouldContain: []string{
-					"Calculate required date fields",
-					"start_date",
-					"end_date",
-					"format `created_at` as `YYYY-MM-DD`",
-					"format `closed_at`/`merged_at` as `YYYY-MM-DD`",
-					"today's date",
-				},
-			},
-		}
-
-		for _, tt := range tests {
-			t.Run(tt.name, func(t *testing.T) {
-				result := RenderOrchestratorInstructions(tt.data)
-
-				for _, expected := range tt.shouldContain {
-					if !strings.Contains(result, expected) {
-						t.Errorf("Expected result to contain %q, but it didn't. Result: %s", expected, result)
-					}
-				}
-			})
-		}
-	})
-}
-
-func TestRenderProjectUpdateInstructions(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		tests := []struct {
-			name          string
-			data          CampaignPromptData
-			shouldContain []string
-			shouldBeEmpty bool
-		}{
-			{
-				name: "with project URL",
-				data: CampaignPromptData{
-					ProjectURL: "https://github.com/orgs/test/projects/1",
-				},
-				shouldContain: []string{
-					"Project Update Instructions (Authoritative Write Contract)",
-					"update-project",
-					"https://github.com/orgs/test/projects/1",
-					"Hard Requirements",
-					"Required Project Fields",
-					"Read-Write Separation",
-					"Adding an Issue or PR",
-					"Updating an Existing Item",
-					"Idempotency Rules",
-				},
-				shouldBeEmpty: false,
-			},
-			{
-				name: "with project URL and campaign ID",
-				data: CampaignPromptData{
-					ProjectURL: "https://github.com/orgs/test/projects/1",
-					CampaignID: "my-campaign",
-				},
-				shouldContain: []string{
-					"Project Update Instructions (Authoritative Write Contract)",
-					"update-project",
-					"https://github.com/orgs/test/projects/1",
-					"campaign_id",
-					"campaign_id:",
-					"my-campaign",
-				},
-				shouldBeEmpty: false,
-			},
-			{
-				name: "without project URL",
-				data: CampaignPromptData{
-					ProjectURL: "",
-				},
-				shouldContain: []string{},
-				shouldBeEmpty: true,
-			},
-		}
-
-		for _, tt := range tests {
-			t.Run(tt.name, func(t *testing.T) {
-				result := RenderProjectUpdateInstructions(tt.data)
-
-				if tt.shouldBeEmpty && result != "" {
-					t.Errorf("Expected empty result, but got: %s", result)
-				}
-
-				for _, expected := range tt.shouldContain {
-					if !strings.Contains(result, expected) {
-						t.Errorf("Expected result to contain %q, but it didn't. Result: %s", expected, result)
-					}
-				}
-			})
-		}
-	})
-}
-
-func TestRenderClosingInstructions(t *testing.T) {
-	withTempGitRepoWithInstalledCampaignPrompts(t, func(_ string) {
-		result := RenderClosingInstructions()
-
-		expectedPhrases := []string{
-			"Closing Instructions (Highest Priority)",
-			"Execute all four steps in strict order",
-			"Read State (no writes)",
-			"Make Decisions (no writes)",
-			"Apply Updates (writes)",
-			"Report",
-			"Workers are immutable and campaign-agnostic",
-			"GitHub Project board is the single source of truth",
-		}
-
-		for _, expected := range expectedPhrases {
-			if !strings.Contains(result, expected) {
-				t.Errorf("Expected result to contain %q, but it didn't. Result: %s", expected, result)
-			}
-		}
-	})
-}
diff --git a/pkg/campaign/test_helpers_test.go b/pkg/campaign/test_helpers_test.go
deleted file mode 100644
index a8b0b800cf2..00000000000
--- a/pkg/campaign/test_helpers_test.go
+++ /dev/null
@@ -1,64 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"fmt"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"testing"
-)
-
-func withTempGitRepoWithInstalledCampaignPrompts(t *testing.T, run func(repoRoot string)) {
-	t.Helper()
-
-	originalDir, err := os.Getwd()
-	if err != nil {
-		t.Fatalf("failed to get current directory: %v", err)
-	}
-
-	repoRoot := t.TempDir()
-
-	if err := os.MkdirAll(filepath.Join(repoRoot, ".github", "aw"), 0o755); err != nil {
-		t.Fatalf("failed to create .github/aw directory: %v", err)
-	}
-
-	srcTemplatesDir := filepath.Clean(filepath.Join(originalDir, "..", "cli", "templates"))
-	installed := map[string]string{
-		"generate-agentic-campaign.md":         "generate-agentic-campaign.md",
-		"orchestrate-agentic-campaign.md":      "orchestrate-agentic-campaign.md",
-		"execute-agentic-campaign-workflow.md": "execute-agentic-campaign-workflow.md",
-		"update-agentic-campaign-project.md":   "update-agentic-campaign-project.md",
-		"close-agentic-campaign.md":            "close-agentic-campaign.md",
-	}
-
-	for srcName, dstName := range installed {
-		srcPath := filepath.Join(srcTemplatesDir, srcName)
-		dstPath := filepath.Join(repoRoot, ".github", "aw", dstName)
-		content, err := os.ReadFile(srcPath)
-		if err != nil {
-			t.Fatalf("failed to read template %s: %v", srcPath, err)
-		}
-		if err := os.WriteFile(dstPath, content, 0o644); err != nil {
-			t.Fatalf("failed to write installed prompt %s: %v", dstPath, err)
-		}
-	}
-
-	cmd := exec.Command("git", "init")
-	cmd.Dir = repoRoot
-	if out, err := cmd.CombinedOutput(); err != nil {
-		t.Fatalf("failed to init git repo: %v (output: %s)", err, string(out))
-	}
-
-	if err := os.Chdir(repoRoot); err != nil {
-		t.Fatalf("failed to chdir to temp repo: %v", err)
-	}
-	t.Cleanup(func() {
-		if err := os.Chdir(originalDir); err != nil {
-			panic(fmt.Sprintf("failed to restore working dir: %v", err))
-		}
-	})
-
-	run(repoRoot)
-}
diff --git a/pkg/campaign/validation.go b/pkg/campaign/validation.go
deleted file mode 100644
index 146031a75c2..00000000000
--- a/pkg/campaign/validation.go
+++ /dev/null
@@ -1,416 +0,0 @@
-package campaign
-
-import (
-	"embed"
-	"encoding/json"
-	"fmt"
-	"net/url"
-	"os"
-	"path/filepath"
-	"strings"
-	"sync"
-
-	"github.com/githubnext/gh-aw/pkg/logger"
-	"github.com/githubnext/gh-aw/pkg/parser"
-	"github.com/goccy/go-yaml"
-	"github.com/santhosh-tekuri/jsonschema/v6"
-)
-
-var validationLog = logger.New("campaign:validation")
-
-//go:embed schemas/campaign_spec_schema.json
-var campaignSpecSchemaFS embed.FS
-
-// Cached compiled schema to avoid recompiling on every validation
-var (
-	compiledSchemaOnce sync.Once
-	compiledSchema     *jsonschema.Schema
-	schemaCompileError error
-)
-
-// ValidateSpec performs lightweight semantic validation of a
-// single CampaignSpec and returns a slice of human-readable problems.
-//
-// It uses JSON schema validation first, then adds additional semantic checks.
-func ValidateSpec(spec *CampaignSpec) []string {
-	validationLog.Printf("Validating campaign spec: id=%s", spec.ID)
-	var problems []string
-
-	// First, validate against JSON schema
-	schemaProblems := ValidateSpecWithSchema(spec)
-	problems = append(problems, schemaProblems...)
-	if len(schemaProblems) > 0 {
-		validationLog.Printf("Schema validation found %d problems for campaign '%s'", len(schemaProblems), spec.ID)
-	}
-
-	// Additional semantic validation beyond schema
-	trimmedID := strings.TrimSpace(spec.ID)
-	if trimmedID == "" {
-		problems = append(problems, "id is required and must be non-empty - example: 'security-q1-2025'")
-	} else {
-		// Enforce a simple, URL-safe pattern for IDs
-		for _, ch := range trimmedID {
-			if (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || ch == '-' {
-				continue
-			}
-			problems = append(problems, fmt.Sprintf("id must use only lowercase letters, digits, and hyphens - got '%s', try '%s'", trimmedID, suggestValidID(trimmedID)))
-			break
-		}
-	}
-
-	if strings.TrimSpace(spec.Name) == "" {
-		problems = append(problems, "name should be provided (falls back to id, but explicit names are recommended) - example: 'Security Q1 2025'")
-	}
-
-	if len(spec.Workflows) == 0 {
-		problems = append(problems, "workflows should list at least one workflow implementing this campaign - example: ['vulnerability-scanner', 'dependency-updater']")
-	}
-
-	// Validate tracker-label format if provided
-	if strings.TrimSpace(spec.TrackerLabel) != "" {
-		trimmedLabel := strings.TrimSpace(spec.TrackerLabel)
-		// Tracker labels should follow the pattern "z_campaign_<campaign-id>"
-		expectedLabel := fmt.Sprintf("z_campaign_%s", strings.ToLower(strings.ReplaceAll(spec.ID, "_", "-")))
-		if !strings.HasPrefix(trimmedLabel, "z_campaign_") {
-			problems = append(problems, fmt.Sprintf("tracker-label should start with 'z_campaign_' prefix - got '%s', recommended: '%s'", trimmedLabel, expectedLabel))
-		}
-		// Check for invalid characters in labels (GitHub label restrictions)
-		if strings.Contains(trimmedLabel, " ") {
-			problems = append(problems, fmt.Sprintf("tracker-label cannot contain spaces - got '%s', try replacing spaces with hyphens", trimmedLabel))
-		}
-	}
-
-	_, scopeProblems := parseScopeSelectors(spec.Scope)
-	problems = append(problems, scopeProblems...)
-
-	// Note: scope validation removed - loader defaults to current repository when omitted
-	// See pkg/campaign/loader.go lines 115-124
-
-	if strings.TrimSpace(spec.ProjectURL) == "" {
-		problems = append(problems, "project-url is required (GitHub Project URL used as the campaign dashboard) - example: 'https://github.com/orgs/myorg/projects/1'")
-	} else {
-		parsed, err := url.Parse(strings.TrimSpace(spec.ProjectURL))
-		if err != nil || parsed.Scheme == "" || parsed.Host == "" {
-			problems = append(problems, "project-url must be a valid absolute URL - example: 'https://github.com/orgs/myorg/projects/1' or 'https://github.com/users/username/projects/1'")
-		} else if !strings.Contains(parsed.Path, "/projects/") {
-			problems = append(problems, "project-url must point to a GitHub Project (URL path should include '/projects/') - you may have provided a repository URL instead")
-		}
-	}
-
-	// Normalize and validate version/state when present.
-	if strings.TrimSpace(spec.Version) == "" {
-		// Default version for v1 specs when omitted.
-		spec.Version = "v1"
-	}
-
-	if spec.State != "" {
-		switch spec.State {
-		case "planned", "active", "paused", "completed", "archived":
-			// valid
-		default:
-			problems = append(problems, "state must be one of: planned, active, paused, completed, archived")
-		}
-	}
-
-	if spec.Governance != nil {
-		if spec.Governance.MaxNewItemsPerRun < 0 {
-			problems = append(problems, "governance.max-new-items-per-run must be >= 0")
-		}
-		if spec.Governance.MaxDiscoveryItemsPerRun < 0 {
-			problems = append(problems, "governance.max-discovery-items-per-run must be >= 0")
-		}
-		if spec.Governance.MaxDiscoveryPagesPerRun < 0 {
-			problems = append(problems, "governance.max-discovery-pages-per-run must be >= 0")
-		}
-		if spec.Governance.MaxProjectUpdatesPerRun < 0 {
-			problems = append(problems, "governance.max-project-updates-per-run must be >= 0")
-		}
-		if spec.Governance.MaxCommentsPerRun < 0 {
-			problems = append(problems, "governance.max-comments-per-run must be >= 0")
-		}
-	}
-
-	if len(problems) == 0 {
-		validationLog.Printf("Campaign spec '%s' validation passed with no problems", spec.ID)
-	} else {
-		validationLog.Printf("Campaign spec '%s' validation completed with %d problems", spec.ID, len(problems))
-	}
-
-	return problems
-}
-
-// suggestValidID converts an invalid campaign ID into a valid one by:
-// - Converting to lowercase
-// - Replacing invalid characters with hyphens
-// - Collapsing multiple hyphens
-// - Trimming leading/trailing hyphens
-func suggestValidID(id string) string {
-	// Convert to lowercase
-	result := strings.ToLower(id)
-
-	// Replace invalid characters with hyphens
-	var builder strings.Builder
-	for _, ch := range result {
-		if (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') {
-			builder.WriteRune(ch)
-		} else {
-			builder.WriteRune('-')
-		}
-	}
-	result = builder.String()
-
-	// Collapse multiple hyphens into single hyphen
-	for strings.Contains(result, "--") {
-		result = strings.ReplaceAll(result, "--", "-")
-	}
-
-	// Trim leading/trailing hyphens
-	result = strings.Trim(result, "-")
-
-	return result
-}
-
-// getCompiledSchema returns the compiled campaign spec schema, compiling it once and caching
-func getCompiledSchema() (*jsonschema.Schema, error) {
-	compiledSchemaOnce.Do(func() {
-		// Read embedded schema
-		schemaData, err := campaignSpecSchemaFS.ReadFile("schemas/campaign_spec_schema.json")
-		if err != nil {
-			schemaCompileError = fmt.Errorf("failed to load campaign spec schema: %w", err)
-			return
-		}
-
-		// Parse schema as JSON
-		var schemaDoc any
-		if err := json.Unmarshal(schemaData, &schemaDoc); err != nil {
-			schemaCompileError = fmt.Errorf("failed to parse campaign spec schema: %w", err)
-			return
-		}
-
-		// Create compiler and add schema resource
-		compiler := jsonschema.NewCompiler()
-		schemaURL := "campaign-spec.json"
-		if err := compiler.AddResource(schemaURL, schemaDoc); err != nil {
-			schemaCompileError = fmt.Errorf("failed to add schema resource: %w", err)
-			return
-		}
-
-		// Compile schema once
-		schema, err := compiler.Compile(schemaURL)
-		if err != nil {
-			schemaCompileError = fmt.Errorf("failed to compile schema: %w", err)
-			return
-		}
-
-		compiledSchema = schema
-	})
-
-	return compiledSchema, schemaCompileError
-}
-
-// ValidateSpecWithSchema validates a CampaignSpec against the JSON schema.
-// Returns a list of validation error messages, or an empty list if valid.
-func ValidateSpecWithSchema(spec *CampaignSpec) []string {
-	// Get the cached compiled schema
-	schema, err := getCompiledSchema()
-	if err != nil {
-		return []string{err.Error()}
-	}
-
-	// Convert spec to JSON for validation, excluding runtime fields.
-	// Create a copy without ConfigPath (which is set at runtime, not in YAML).
-	//
-	// JSON property names intentionally mirror the kebab-case YAML keys so the
-	// JSON Schema can validate both YAML and JSON representations consistently.
-	type CampaignGovernancePolicyForValidation struct {
-		MaxNewItemsPerRun       int      `json:"max-new-items-per-run,omitempty"`
-		MaxDiscoveryItemsPerRun int      `json:"max-discovery-items-per-run,omitempty"`
-		MaxDiscoveryPagesPerRun int      `json:"max-discovery-pages-per-run,omitempty"`
-		OptOutLabels            []string `json:"opt-out-labels,omitempty"`
-		DoNotDowngradeDoneItems *bool    `json:"do-not-downgrade-done-items,omitempty"`
-		MaxProjectUpdatesPerRun int      `json:"max-project-updates-per-run,omitempty"`
-		MaxCommentsPerRun       int      `json:"max-comments-per-run,omitempty"`
-	}
-
-	type CampaignSpecForValidation struct {
-		ID                 string                                 `json:"id"`
-		Name               string                                 `json:"name"`
-		Description        string                                 `json:"description,omitempty"`
-		ProjectURL         string                                 `json:"project-url,omitempty"`
-		Version            string                                 `json:"version,omitempty"`
-		Workflows          []string                               `json:"workflows,omitempty"`
-		Scope              []string                               `json:"scope,omitempty"`
-		MemoryPaths        []string                               `json:"memory-paths,omitempty"`
-		MetricsGlob        string                                 `json:"metrics-glob,omitempty"`
-		CursorGlob         string                                 `json:"cursor-glob,omitempty"`
-		Owners             []string                               `json:"owners,omitempty"`
-		ExecutiveSponsors  []string                               `json:"executive-sponsors,omitempty"`
-		RiskLevel          string                                 `json:"risk-level,omitempty"`
-		State              string                                 `json:"state,omitempty"`
-		Tags               []string                               `json:"tags,omitempty"`
-		AllowedSafeOutputs []string                               `json:"allowed-safe-outputs,omitempty"`
-		Governance         *CampaignGovernancePolicyForValidation `json:"governance,omitempty"`
-		ApprovalPolicy     *CampaignApprovalPolicy                `json:"approval-policy,omitempty"`
-	}
-
-	validationSpec := CampaignSpecForValidation{
-		ID:                 spec.ID,
-		Name:               spec.Name,
-		Description:        spec.Description,
-		ProjectURL:         spec.ProjectURL,
-		Version:            spec.Version,
-		Workflows:          spec.Workflows,
-		Scope:              spec.Scope,
-		MemoryPaths:        spec.MemoryPaths,
-		MetricsGlob:        spec.MetricsGlob,
-		CursorGlob:         spec.CursorGlob,
-		Owners:             spec.Owners,
-		ExecutiveSponsors:  spec.ExecutiveSponsors,
-		RiskLevel:          spec.RiskLevel,
-		State:              spec.State,
-		Tags:               spec.Tags,
-		AllowedSafeOutputs: spec.AllowedSafeOutputs,
-		Governance: func() *CampaignGovernancePolicyForValidation {
-			if spec.Governance == nil {
-				return nil
-			}
-			return &CampaignGovernancePolicyForValidation{
-				MaxNewItemsPerRun:       spec.Governance.MaxNewItemsPerRun,
-				MaxDiscoveryItemsPerRun: spec.Governance.MaxDiscoveryItemsPerRun,
-				MaxDiscoveryPagesPerRun: spec.Governance.MaxDiscoveryPagesPerRun,
-				OptOutLabels:            spec.Governance.OptOutLabels,
-				DoNotDowngradeDoneItems: spec.Governance.DoNotDowngradeDoneItems,
-				MaxProjectUpdatesPerRun: spec.Governance.MaxProjectUpdatesPerRun,
-				MaxCommentsPerRun:       spec.Governance.MaxCommentsPerRun,
-			}
-		}(),
-		ApprovalPolicy: spec.ApprovalPolicy,
-	}
-
-	// Marshal spec to JSON then unmarshal to any for validation
-	// This is necessary because the jsonschema library validates against the JSON representation
-	specJSON, err := json.Marshal(validationSpec)
-	if err != nil {
-		return []string{fmt.Sprintf("failed to marshal spec to JSON: %v", err)}
-	}
-
-	var specData any
-	if err := json.Unmarshal(specJSON, &specData); err != nil {
-		return []string{fmt.Sprintf("failed to unmarshal spec data: %v", err)}
-	}
-
-	// Validate the spec against the schema
-	if err := schema.Validate(specData); err != nil {
-		return formatValidationErrors(err)
-	}
-
-	return nil
-}
-
-// formatValidationErrors converts jsonschema validation errors to a list of human-readable messages
-func formatValidationErrors(err error) []string {
-	var problems []string
-
-	ve, ok := err.(*jsonschema.ValidationError)
-	if !ok {
-		// Not a validation error, return as-is
-		return []string{err.Error()}
-	}
-
-	// Process the main error and all causes
-	var collectErrors func(*jsonschema.ValidationError)
-	collectErrors = func(e *jsonschema.ValidationError) {
-		// Skip collecting if there are causes - we'll collect those instead
-		// to avoid duplicate/redundant messages
-		if len(e.Causes) > 0 {
-			for _, cause := range e.Causes {
-				collectErrors(cause)
-			}
-			return
-		}
-
-		// Format the error message with field path
-		field := "root"
-		if len(e.InstanceLocation) > 0 {
-			field = strings.Join(e.InstanceLocation, ".")
-		}
-
-		// Use the error's Error() method to get the message
-		msg := e.Error()
-
-		problems = append(problems, fmt.Sprintf("%s: %s", field, msg))
-	}
-
-	collectErrors(ve)
-	return problems
-}
-
-// ValidateSpecFromFile validates a campaign spec file by loading and validating it.
-// This is useful for validation commands that operate on files directly.
-func ValidateSpecFromFile(filePath string) (*CampaignSpec, []string, error) {
-	validationLog.Printf("Validating campaign spec from file: %s", filePath)
-
-	// Read the campaign spec file content first, then extract frontmatter
-	content, err := os.ReadFile(filePath)
-	if err != nil {
-		validationLog.Printf("Failed to read campaign spec file: %s", err)
-		return nil, nil, fmt.Errorf("failed to read campaign spec file: %w", err)
-	}
-
-	data, err := parser.ExtractFrontmatterFromContent(string(content))
-	if err != nil {
-		return nil, nil, fmt.Errorf("failed to parse frontmatter: %w", err)
-	}
-
-	if len(data.Frontmatter) == 0 {
-		return nil, nil, fmt.Errorf("no frontmatter found in campaign spec file")
-	}
-
-	// Convert frontmatter map into YAML, then unmarshal into CampaignSpec using
-	// YAML tags so kebab-case keys (e.g. project-url) map correctly.
-	yamlBytes, err := yaml.Marshal(data.Frontmatter)
-	if err != nil {
-		return nil, nil, fmt.Errorf("failed to marshal frontmatter to YAML: %w", err)
-	}
-
-	var spec CampaignSpec
-	if err := yaml.Unmarshal(yamlBytes, &spec); err != nil {
-		return nil, nil, fmt.Errorf("failed to unmarshal spec from YAML: %w", err)
-	}
-
-	problems := ValidateSpec(&spec)
-	return &spec, problems, nil
-}
-
-// ValidateWorkflowsExist checks that all workflows referenced in a campaign spec
-// actually exist in the .github/workflows directory.
-// Returns a list of problems for workflows that don't exist.
-func ValidateWorkflowsExist(spec *CampaignSpec, workflowsDir string) []string {
-	validationLog.Printf("Validating workflow existence for campaign '%s': checking %d workflows in %s",
-		spec.ID, len(spec.Workflows), workflowsDir)
-	var problems []string
-
-	for _, workflowID := range spec.Workflows {
-		// Check for both .md and .lock.yml versions
-		mdPath := filepath.Join(workflowsDir, workflowID+".md")
-		lockPath := filepath.Join(workflowsDir, workflowID+".lock.yml")
-
-		mdExists := false
-		lockExists := false
-
-		if _, err := os.Stat(mdPath); err == nil {
-			mdExists = true
-		}
-		if _, err := os.Stat(lockPath); err == nil {
-			lockExists = true
-		}
-
-		if !mdExists && !lockExists {
-			problems = append(problems, fmt.Sprintf("workflow '%s' not found", workflowID))
-		} else if !mdExists {
-			problems = append(problems, fmt.Sprintf("workflow '%s' missing source .md file", workflowID))
-		}
-	}
-
-	return problems
-}
diff --git a/pkg/campaign/validation_test.go b/pkg/campaign/validation_test.go
deleted file mode 100644
index 04021fd79f2..00000000000
--- a/pkg/campaign/validation_test.go
+++ /dev/null
@@ -1,566 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"strings"
-	"testing"
-)
-
-func TestValidateSpec_ValidSpec(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo1"},
-		Version:    "v1",
-		State:      "active",
-		Workflows:  []string{"workflow1", "workflow2"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) != 0 {
-		t.Errorf("Expected no validation problems, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_MissingID(t *testing.T) {
-	spec := &CampaignSpec{
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo1"},
-		Workflows:  []string{"workflow1"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for missing ID")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "id is required") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected ID validation problem, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_InvalidIDCharacters(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "Test_Campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo1"},
-		Workflows:  []string{"workflow1"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for invalid ID characters")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "lowercase letters, digits, and hyphens") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected ID character validation problem, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_MissingName(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo1"},
-		Workflows:  []string{"workflow1"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for missing name")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "name should be provided") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected name validation problem, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_MissingWorkflows(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo1"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for missing workflows")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "workflows should list at least one workflow") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected workflows validation problem, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_InvalidState(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo1"},
-		Workflows:  []string{"workflow1"},
-		State:      "invalid-state",
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for invalid state")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "state must be one of") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected state validation problem, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_ValidStates(t *testing.T) {
-	validStates := []string{"planned", "active", "paused", "completed", "archived"}
-
-	for _, state := range validStates {
-		spec := &CampaignSpec{
-			ID:         "test-campaign",
-			Name:       "Test Campaign",
-			ProjectURL: "https://github.com/orgs/org/projects/1",
-			Scope:      []string{"org/repo1"},
-			Workflows:  []string{"workflow1"},
-			State:      state,
-		}
-
-		problems := ValidateSpec(spec)
-		if len(problems) != 0 {
-			t.Errorf("Expected no validation problems for state '%s', got: %v", state, problems)
-		}
-	}
-}
-
-func TestValidateSpec_VersionDefault(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo1"},
-		Workflows:  []string{"workflow1"},
-	}
-
-	_ = ValidateSpec(spec)
-
-	if spec.Version != "v1" {
-		t.Errorf("Expected version to default to 'v1', got '%s'", spec.Version)
-	}
-}
-
-func TestValidateSpec_RiskLevel(t *testing.T) {
-	validRiskLevels := []string{"low", "medium", "high"}
-
-	for _, riskLevel := range validRiskLevels {
-		spec := &CampaignSpec{
-			ID:         "test-campaign",
-			Name:       "Test Campaign",
-			ProjectURL: "https://github.com/orgs/org/projects/1",
-			Scope:      []string{"org/repo1"},
-			Workflows:  []string{"workflow1"},
-			RiskLevel:  riskLevel,
-		}
-
-		problems := ValidateSpec(spec)
-		// Risk level validation is currently not enforced beyond schema
-		// This test ensures the field is accepted
-		if len(problems) != 0 {
-			t.Errorf("Expected no validation problems for risk level '%s', got: %v", riskLevel, problems)
-		}
-	}
-}
-
-func TestValidateSpec_WithApprovalPolicy(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo1"},
-		Workflows:  []string{"workflow1"},
-		ApprovalPolicy: &CampaignApprovalPolicy{
-			RequiredApprovals: 2,
-			RequiredRoles:     []string{"admin", "security"},
-			ChangeControl:     true,
-		},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) != 0 {
-		t.Errorf("Expected no validation problems with approval policy, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_CompleteSpec(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:                 "complete-campaign",
-		Name:               "Complete Campaign",
-		Description:        "A complete campaign spec for testing",
-		ProjectURL:         "https://github.com/orgs/org/projects/1",
-		Scope:              []string{"org/repo1"},
-		Version:            "v1",
-		Workflows:          []string{"workflow1", "workflow2"},
-		MemoryPaths:        []string{"memory/campaigns/complete/**"},
-		MetricsGlob:        "memory/campaigns/complete-*.json",
-		Owners:             []string{"owner1", "owner2"},
-		ExecutiveSponsors:  []string{"sponsor1"},
-		RiskLevel:          "medium",
-		State:              "active",
-		Tags:               []string{"security", "compliance"},
-		AllowedSafeOutputs: []string{"create-issue", "create-pull-request"},
-		ApprovalPolicy: &CampaignApprovalPolicy{
-			RequiredApprovals: 3,
-			RequiredRoles:     []string{"admin", "security", "compliance"},
-			ChangeControl:     true,
-		},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) != 0 {
-		t.Errorf("Expected no validation problems for complete spec, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_MissingScopeIsValid(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		// No workflows - scope is not required
-	}
-
-	problems := ValidateSpec(spec)
-	// Should have one problem: missing workflows
-	if len(problems) != 1 {
-		t.Errorf("Expected 1 validation problem (workflows), got: %v", problems)
-	}
-}
-
-func TestValidateSpec_InvalidScopeRepoFormat(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Workflows:  []string{"workflow1"},
-		Scope:      []string{"invalid-repo-format", "org/repo1"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for invalid repo format")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "must be 'owner/repo'") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected repo format validation problem, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_EmptyScopeIsValid(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{}, // Empty list, no workflows
-	}
-
-	problems := ValidateSpec(spec)
-	// Should have one problem: missing workflows
-	if len(problems) != 1 {
-		t.Errorf("Expected 1 validation problem (workflows), got: %v", problems)
-	}
-}
-
-func TestValidateSpec_ValidScopeWithOrgs(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Workflows:  []string{"workflow1"},
-		Scope:      []string{"org/repo1", "org:github", "org:microsoft"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) != 0 {
-		t.Errorf("Expected no validation problems with valid scope orgs, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_InvalidScopeOrgFormat(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Workflows:  []string{"workflow1"},
-		Scope:      []string{"org/repo1", "org:github/repo"}, // Invalid - contains slash
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for invalid org format")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "must be 'org:<name>'") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected org format validation problem, got: %v", problems)
-	}
-}
-
-func TestSuggestValidID(t *testing.T) {
-	tests := []struct {
-		name     string
-		input    string
-		expected string
-	}{
-		{
-			name:     "uppercase to lowercase",
-			input:    "Security-Q1-2025",
-			expected: "security-q1-2025",
-		},
-		{
-			name:     "underscores to hyphens",
-			input:    "test_campaign_id",
-			expected: "test-campaign-id",
-		},
-		{
-			name:     "spaces to hyphens",
-			input:    "my campaign id",
-			expected: "my-campaign-id",
-		},
-		{
-			name:     "mixed invalid characters",
-			input:    "Test Campaign! @2025",
-			expected: "test-campaign-2025",
-		},
-		{
-			name:     "multiple hyphens collapsed",
-			input:    "test--campaign---id",
-			expected: "test-campaign-id",
-		},
-		{
-			name:     "leading and trailing hyphens removed",
-			input:    "-test-campaign-",
-			expected: "test-campaign",
-		},
-		{
-			name:     "already valid id",
-			input:    "valid-campaign-123",
-			expected: "valid-campaign-123",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result := suggestValidID(tt.input)
-			if result != tt.expected {
-				t.Errorf("suggestValidID(%q) = %q, want %q", tt.input, result, tt.expected)
-			}
-		})
-	}
-}
-
-func TestValidateSpec_InvalidIDWithSuggestion(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "Test Campaign 2025",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo1"},
-		Workflows:  []string{"workflow1"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for invalid ID")
-	}
-
-	// Check that the error message includes a suggestion
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "test-campaign-2025") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected ID validation problem with suggestion, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_ScopeRepoWildcard(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org/repo*"}, // Invalid - wildcards not allowed in repo selectors
-		Workflows:  []string{"workflow1"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for wildcard in scope")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "cannot contain wildcards") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected wildcard validation problem, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_ScopeOrgWildcard(t *testing.T) {
-	spec := &CampaignSpec{
-		ID:         "test-campaign",
-		Name:       "Test Campaign",
-		ProjectURL: "https://github.com/orgs/org/projects/1",
-		Scope:      []string{"org:github*"}, // Invalid - wildcards not allowed in org selectors
-		Workflows:  []string{"workflow1"},
-	}
-
-	problems := ValidateSpec(spec)
-	if len(problems) == 0 {
-		t.Fatal("Expected validation problems for wildcard in scope")
-	}
-
-	found := false
-	for _, p := range problems {
-		if strings.Contains(p, "cannot contain wildcards") {
-			found = true
-			break
-		}
-	}
-	if !found {
-		t.Errorf("Expected wildcard validation problem, got: %v", problems)
-	}
-}
-
-func TestValidateSpec_TrackerLabelFormat(t *testing.T) {
-	tests := []struct {
-		name          string
-		campaignID    string
-		trackerLabel  string
-		expectError   bool
-		errorContains string
-	}{
-		{
-			name:         "valid tracker label with z_campaign_ prefix",
-			campaignID:   "security-alert-burndown",
-			trackerLabel: "z_campaign_security-alert-burndown",
-			expectError:  false,
-		},
-		{
-			name:          "invalid tracker label with old campaign: prefix",
-			campaignID:    "security-alert-burndown",
-			trackerLabel:  "campaign:security-alert-burndown",
-			expectError:   true,
-			errorContains: "tracker-label should start with 'z_campaign_' prefix",
-		},
-		{
-			name:          "invalid tracker label with wrong format",
-			campaignID:    "test-campaign",
-			trackerLabel:  "wrong-label-format",
-			expectError:   true,
-			errorContains: "tracker-label should start with 'z_campaign_' prefix",
-		},
-		{
-			name:         "empty tracker label is valid (optional field)",
-			campaignID:   "test-campaign",
-			trackerLabel: "",
-			expectError:  false,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			spec := &CampaignSpec{
-				ID:           tt.campaignID,
-				Name:         "Test Campaign",
-				ProjectURL:   "https://github.com/orgs/org/projects/1",
-				Scope:        []string{"org/repo1"},
-				Workflows:    []string{"workflow1"},
-				TrackerLabel: tt.trackerLabel,
-			}
-
-			problems := ValidateSpec(spec)
-
-			if tt.expectError {
-				if len(problems) == 0 {
-					t.Errorf("Expected validation error but got none")
-					return
-				}
-				found := false
-				for _, p := range problems {
-					if strings.Contains(p, tt.errorContains) {
-						found = true
-						break
-					}
-				}
-				if !found {
-					t.Errorf("Expected error containing %q, got: %v", tt.errorContains, problems)
-				}
-			} else {
-				// Filter out problems that are not related to tracker-label
-				for _, p := range problems {
-					if strings.Contains(p, "tracker-label") {
-						t.Errorf("Unexpected tracker-label validation error: %s", p)
-					}
-				}
-			}
-		})
-	}
-}
diff --git a/pkg/campaign/workflow_discovery.go b/pkg/campaign/workflow_discovery.go
deleted file mode 100644
index 06bc319c506..00000000000
--- a/pkg/campaign/workflow_discovery.go
+++ /dev/null
@@ -1,170 +0,0 @@
-package campaign
-
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-
-	"github.com/githubnext/gh-aw/pkg/logger"
-	"github.com/githubnext/gh-aw/pkg/parser"
-)
-
-var workflowDiscoveryLog = logger.New("campaign:workflow_discovery")
-
-// WorkflowMatch represents a discovered workflow that matches campaign goals
-type WorkflowMatch struct {
-	ID          string   // Workflow ID (basename without .md)
-	FilePath    string   // Relative path to workflow file
-	Name        string   // Workflow name from frontmatter
-	Description string   // Workflow description
-	Keywords    []string // Matching keywords
-	Score       int      // Match score (higher is better)
-}
-
-// CampaignGoalKeywords maps campaign types to relevant keywords
-var CampaignGoalKeywords = map[string][]string{
-	"security": {
-		"security", "vulnerability", "vulnerabilities", "scan", "scanning",
-		"cve", "audit", "compliance", "threat", "detection",
-	},
-	"dependencies": {
-		"dependency", "dependencies", "upgrade", "update", "npm", "pip",
-		"package", "packages", "version", "outdated",
-	},
-	"documentation": {
-		"doc", "docs", "documentation", "guide", "guides", "readme",
-		"wiki", "reference", "tutorial",
-	},
-	"quality": {
-		"quality", "test", "testing", "lint", "linting", "coverage",
-		"code-quality", "static-analysis", "sonar",
-	},
-	"cicd": {
-		"ci", "cd", "build", "deploy", "deployment", "release",
-		"pipeline", "automation", "continuous",
-	},
-}
-
-// DiscoverWorkflows scans the repository for existing workflows that match campaign goals
-func DiscoverWorkflows(rootDir string, campaignGoals []string) ([]WorkflowMatch, error) {
-	workflowDiscoveryLog.Printf("Discovering workflows for goals: %v", campaignGoals)
-
-	workflowsDir := filepath.Join(rootDir, ".github", "workflows")
-	if _, err := os.Stat(workflowsDir); os.IsNotExist(err) {
-		workflowDiscoveryLog.Print("Workflows directory does not exist")
-		return []WorkflowMatch{}, nil
-	}
-
-	// Scan for .md files (agentic workflows)
-	entries, err := os.ReadDir(workflowsDir)
-	if err != nil {
-		return nil, fmt.Errorf("failed to read workflows directory: %w", err)
-	}
-
-	var matches []WorkflowMatch
-	for _, entry := range entries {
-		if entry.IsDir() || !strings.HasSuffix(entry.Name(), ".md") {
-			continue
-		}
-
-		// Skip campaign files and generated files
-		if strings.HasSuffix(entry.Name(), ".campaign.md") || strings.HasSuffix(entry.Name(), ".g.md") {
-			continue
-		}
-
-		filePath := filepath.Join(workflowsDir, entry.Name())
-		match, err := matchWorkflow(filePath, campaignGoals)
-		if err != nil {
-			workflowDiscoveryLog.Printf("Failed to match workflow %s: %v", entry.Name(), err)
-			continue
-		}
-
-		if match != nil {
-			matches = append(matches, *match)
-			workflowDiscoveryLog.Printf("Found matching workflow: %s (score: %d)", match.ID, match.Score)
-		}
-	}
-
-	// Sort by score (highest first)
-	sortWorkflowMatches(matches)
-
-	workflowDiscoveryLog.Printf("Discovered %d matching workflows", len(matches))
-	return matches, nil
-}
-
-// matchWorkflow checks if a workflow matches the campaign goals
-func matchWorkflow(filePath string, campaignGoals []string) (*WorkflowMatch, error) {
-	// Read workflow file
-	content, err := os.ReadFile(filePath)
-	if err != nil {
-		return nil, fmt.Errorf("failed to read workflow file: %w", err)
-	}
-
-	// Extract frontmatter
-	result, err := parser.ExtractFrontmatterFromContent(string(content))
-	if err != nil {
-		return nil, fmt.Errorf("failed to extract frontmatter: %w", err)
-	}
-
-	// Get workflow name and description
-	name := getStringField(result.Frontmatter, "name")
-	description := getStringField(result.Frontmatter, "description")
-
-	// Build searchable text (lowercase)
-	searchText := strings.ToLower(name + " " + description)
-
-	// Calculate match score
-	score := 0
-	matchedKeywords := []string{}
-
-	for _, goal := range campaignGoals {
-		keywords := CampaignGoalKeywords[strings.ToLower(goal)]
-		for _, keyword := range keywords {
-			if strings.Contains(searchText, keyword) {
-				score += 10
-				matchedKeywords = append(matchedKeywords, keyword)
-			}
-		}
-	}
-
-	// No match if score is 0
-	if score == 0 {
-		return nil, nil
-	}
-
-	// Extract workflow ID from filename
-	filename := filepath.Base(filePath)
-	workflowID := strings.TrimSuffix(filename, ".md")
-
-	return &WorkflowMatch{
-		ID:          workflowID,
-		FilePath:    filePath,
-		Name:        name,
-		Description: description,
-		Keywords:    matchedKeywords,
-		Score:       score,
-	}, nil
-}
-
-// sortWorkflowMatches sorts workflow matches by score (descending)
-func sortWorkflowMatches(matches []WorkflowMatch) {
-	// Simple bubble sort (good enough for small lists)
-	for i := 0; i < len(matches); i++ {
-		for j := i + 1; j < len(matches); j++ {
-			if matches[j].Score > matches[i].Score {
-				matches[i], matches[j] = matches[j], matches[i]
-			}
-		}
-	}
-}
-
-// getStringField safely extracts a string field from frontmatter
-func getStringField(frontmatter map[string]any, field string) string {
-	if val, ok := frontmatter[field]; ok {
-		if str, ok := val.(string); ok {
-			return str
-		}
-	}
-	return ""
-}
diff --git a/pkg/campaign/workflow_discovery_test.go b/pkg/campaign/workflow_discovery_test.go
deleted file mode 100644
index 0d36c221cf7..00000000000
--- a/pkg/campaign/workflow_discovery_test.go
+++ /dev/null
@@ -1,199 +0,0 @@
-//go:build !integration
-
-package campaign
-
-import (
-	"os"
-	"path/filepath"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
-)
-
-func TestDiscoverWorkflows(t *testing.T) {
-	tests := []struct {
-		name          string
-		workflows     map[string]string // filename -> content
-		goals         []string
-		expectedCount int
-		expectedIDs   []string
-	}{
-		{
-			name: "discover security workflow",
-			workflows: map[string]string{
-				"security-scanner.md": `---
-name: Security Scanner
-description: Scan for vulnerabilities
----
-# Security Scanner
-Scan repositories for security vulnerabilities`,
-			},
-			goals:         []string{"security"},
-			expectedCount: 1,
-			expectedIDs:   []string{"security-scanner"},
-		},
-		{
-			name: "discover multiple matching workflows",
-			workflows: map[string]string{
-				"dependency-updater.md": `---
-name: Dependency Updater
-description: Update npm packages
----
-# Dependency Updater`,
-				"package-scanner.md": `---
-name: Package Scanner
-description: Scan for outdated dependencies
----
-# Package Scanner`,
-			},
-			goals:         []string{"dependencies"},
-			expectedCount: 2,
-			expectedIDs:   []string{"dependency-updater", "package-scanner"},
-		},
-		{
-			name: "skip campaign files",
-			workflows: map[string]string{
-				"my-campaign.campaign.md": `---
-name: My Campaign
----
-# Campaign`,
-				"security-scanner.md": `---
-name: Security Scanner
-description: Scan for vulnerabilities
----
-# Scanner`,
-			},
-			goals:         []string{"security"},
-			expectedCount: 1,
-			expectedIDs:   []string{"security-scanner"},
-		},
-		{
-			name: "no matching workflows",
-			workflows: map[string]string{
-				"random-workflow.md": `---
-name: Random Workflow
-description: Does something random
----
-# Random`,
-			},
-			goals:         []string{"security"},
-			expectedCount: 0,
-			expectedIDs:   []string{},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			// Create temporary directory
-			tmpDir := t.TempDir()
-			workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
-			require.NoError(t, os.MkdirAll(workflowsDir, 0755))
-
-			// Create workflow files
-			for filename, content := range tt.workflows {
-				filePath := filepath.Join(workflowsDir, filename)
-				require.NoError(t, os.WriteFile(filePath, []byte(content), 0644))
-			}
-
-			// Discover workflows
-			matches, err := DiscoverWorkflows(tmpDir, tt.goals)
-			require.NoError(t, err)
-
-			// Verify results
-			assert.Len(t, matches, tt.expectedCount, "Expected %d matches, got %d", tt.expectedCount, len(matches))
-
-			// Verify IDs
-			actualIDs := make([]string, len(matches))
-			for i, match := range matches {
-				actualIDs[i] = match.ID
-			}
-
-			if tt.expectedCount > 0 {
-				for _, expectedID := range tt.expectedIDs {
-					assert.Contains(t, actualIDs, expectedID, "Expected workflow ID %s not found", expectedID)
-				}
-			}
-		})
-	}
-}
-
-func TestMatchWorkflow(t *testing.T) {
-	tests := []struct {
-		name            string
-		content         string
-		goals           []string
-		expectedMatch   bool
-		expectedScore   int
-		minKeywordCount int
-	}{
-		{
-			name: "security workflow matches security goal",
-			content: `---
-name: Security Scanner
-description: Scan for security vulnerabilities
----
-# Security Scanner`,
-			goals:           []string{"security"},
-			expectedMatch:   true,
-			expectedScore:   20, // "security" and "vulnerabilities"
-			minKeywordCount: 2,
-		},
-		{
-			name: "dependency workflow matches dependency goal",
-			content: `---
-name: Dependency Updater
-description: Update npm dependencies and packages
----
-# Updater`,
-			goals:           []string{"dependencies"},
-			expectedMatch:   true,
-			expectedScore:   20,
-			minKeywordCount: 2,
-		},
-		{
-			name: "no match for unrelated workflow",
-			content: `---
-name: Random Workflow
-description: Does something random
----
-# Random`,
-			goals:         []string{"security"},
-			expectedMatch: false,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			// Create temporary file
-			tmpFile := filepath.Join(t.TempDir(), "test.md")
-			require.NoError(t, os.WriteFile(tmpFile, []byte(tt.content), 0644))
-
-			// Match workflow
-			match, err := matchWorkflow(tmpFile, tt.goals)
-			require.NoError(t, err)
-
-			if tt.expectedMatch {
-				require.NotNil(t, match, "Expected a match but got nil")
-				assert.GreaterOrEqual(t, match.Score, tt.expectedScore, "Expected score >= %d, got %d", tt.expectedScore, match.Score)
-				assert.GreaterOrEqual(t, len(match.Keywords), tt.minKeywordCount, "Expected at least %d keywords", tt.minKeywordCount)
-			} else {
-				assert.Nil(t, match, "Expected no match but got one")
-			}
-		})
-	}
-}
-
-func TestSortWorkflowMatches(t *testing.T) {
-	matches := []WorkflowMatch{
-		{ID: "low", Score: 10},
-		{ID: "high", Score: 50},
-		{ID: "medium", Score: 30},
-	}
-
-	sortWorkflowMatches(matches)
-
-	assert.Equal(t, "high", matches[0].ID)
-	assert.Equal(t, "medium", matches[1].ID)
-	assert.Equal(t, "low", matches[2].ID)
-}
diff --git a/pkg/cli/commands.go b/pkg/cli/commands.go
index fc4c2ce07f6..ed766b846a1 100644
--- a/pkg/cli/commands.go
+++ b/pkg/cli/commands.go
@@ -49,21 +49,6 @@ var debugWorkflowPromptTemplate string
 //go:embed templates/upgrade-agentic-workflows.md
 var upgradeAgenticWorkflowsPromptTemplate string
 
-//go:embed templates/generate-agentic-campaign.md
-var campaignGeneratorInstructionsTemplate string
-
-//go:embed templates/orchestrate-agentic-campaign.md
-var campaignOrchestratorInstructionsTemplate string
-
-//go:embed templates/update-agentic-campaign-project.md
-var campaignProjectUpdateInstructionsTemplate string
-
-//go:embed templates/execute-agentic-campaign-workflow.md
-var campaignWorkflowExecutionTemplate string
-
-//go:embed templates/close-agentic-campaign.md
-var campaignClosingInstructionsTemplate string
-
 // SetVersionInfo sets the version information for the CLI and workflow package
 func SetVersionInfo(v string) {
 	version = v
diff --git a/pkg/cli/compile_campaign.go b/pkg/cli/compile_campaign.go
deleted file mode 100644
index 3c8ba255b95..00000000000
--- a/pkg/cli/compile_campaign.go
+++ /dev/null
@@ -1,167 +0,0 @@
-// This file provides campaign workflow compilation and validation.
-//
-// This file handles validation of campaign spec files and their referenced workflows.
-// Campaign workflows are special workflows that orchestrate multiple sub-workflows
-// across a set of target repositories.
-//
-// # Organization Rationale
-//
-// The validateCampaigns function is co-located with campaign compilation logic because:
-//   - It's domain-specific to campaign workflows
-//   - It's only called during compile operations
-//   - It's tightly coupled to the campaign compilation process
-//   - The file is small (98 lines) and focused
-//
-// This follows the principle that domain-specific validation belongs in domain files.
-// See skills/developer/SKILL.md for validation architecture patterns.
-//
-// # Validation Functions
-//
-// Campaign Validation:
-//   - validateCampaigns() - Validates campaign specs and referenced workflows
-//
-// This validation ensures that:
-//   - Campaign spec files are syntactically valid
-//   - Referenced workflow files exist in the workflows directory
-//   - Campaign configuration is complete and correct
-
-package cli
-
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-
-	"github.com/githubnext/gh-aw/pkg/campaign"
-	"github.com/githubnext/gh-aw/pkg/console"
-	"github.com/githubnext/gh-aw/pkg/logger"
-)
-
-var compileCampaignLog = logger.New("cli:compile_campaign")
-
-// validateCampaigns validates campaign spec files and their referenced workflows.
-// If campaignFiles is provided (non-nil), only those specific campaign files are validated.
-// If campaignFiles is nil, all campaign specs are validated.
-// Returns an error if any campaign specs are invalid or reference missing workflows.
-func validateCampaigns(workflowDir string, verbose bool, campaignFiles []string) error {
-	compileCampaignLog.Printf("Validating campaigns with workflow directory: %s", workflowDir)
-
-	// Get absolute path to workflows directory
-	absWorkflowDir := workflowDir
-	if !filepath.IsAbs(absWorkflowDir) {
-		gitRoot, err := findGitRoot()
-		if err != nil {
-			compileCampaignLog.Print("Not in a git repository, using current directory")
-			// If not in a git repo, use current directory
-			cwd, cwdErr := os.Getwd()
-			if cwdErr != nil {
-				return nil // Silently skip if we can't determine the directory
-			}
-			absWorkflowDir = filepath.Join(cwd, workflowDir)
-		} else {
-			absWorkflowDir = filepath.Join(gitRoot, workflowDir)
-		}
-	}
-	compileCampaignLog.Printf("Using absolute workflow directory: %s", absWorkflowDir)
-
-	// Load campaign specs
-	gitRoot, err := findGitRoot()
-	if err != nil {
-		compileCampaignLog.Print("Cannot validate campaigns: not in a git repository")
-		// Not in a git repo, can't validate campaigns
-		return nil
-	}
-
-	specs, err := campaign.LoadSpecs(gitRoot)
-	if err != nil {
-		compileCampaignLog.Printf("Failed to load campaign specs: %v", err)
-		return fmt.Errorf("failed to load campaign specs: %w", err)
-	}
-
-	if len(specs) == 0 {
-		compileCampaignLog.Print("No campaign specs found to validate")
-		// No campaign specs to validate
-		return nil
-	}
-
-	// Filter specs if specific campaign files were provided
-	var specsToValidate []campaign.CampaignSpec
-	if len(campaignFiles) > 0 {
-		compileCampaignLog.Printf("Filtering to validate only %d specific campaign file(s)", len(campaignFiles))
-		// Create a map of absolute paths for quick lookup
-		campaignFileMap := make(map[string]bool)
-		for _, cf := range campaignFiles {
-			absPath, err := filepath.Abs(cf)
-			if err == nil {
-				campaignFileMap[absPath] = true
-			}
-		}
-
-		for _, spec := range specs {
-			// Get absolute path of the spec's config file
-			specPath := spec.ConfigPath
-			if !filepath.IsAbs(specPath) {
-				specPath = filepath.Join(gitRoot, specPath)
-			}
-			absSpecPath, err := filepath.Abs(specPath)
-			if err == nil && campaignFileMap[absSpecPath] {
-				specsToValidate = append(specsToValidate, spec)
-			}
-		}
-		compileCampaignLog.Printf("Filtered to %d campaign spec(s) for validation", len(specsToValidate))
-	} else {
-		// Validate all specs
-		specsToValidate = specs
-		compileCampaignLog.Printf("Loaded %d campaign specs for validation", len(specs))
-	}
-
-	if len(specsToValidate) == 0 {
-		compileCampaignLog.Print("No matching campaign specs found to validate")
-		return nil
-	}
-
-	if verbose {
-		fmt.Fprintln(os.Stderr, console.FormatInfoMessage(fmt.Sprintf("Validating %d campaign spec(s)...", len(specsToValidate))))
-	}
-
-	var allProblems []string
-	hasErrors := false
-
-	for _, spec := range specsToValidate {
-		// Validate the spec itself
-		problems := campaign.ValidateSpec(&spec)
-
-		// Validate that referenced workflows exist in the same directory as the campaign spec
-		// Use the directory of the campaign spec file, not a global workflow directory
-		campaignDir := filepath.Dir(spec.ConfigPath)
-		if !filepath.IsAbs(campaignDir) {
-			campaignDir = filepath.Join(gitRoot, campaignDir)
-		}
-		workflowProblems := campaign.ValidateWorkflowsExist(&spec, campaignDir)
-		problems = append(problems, workflowProblems...)
-
-		if len(problems) > 0 {
-			hasErrors = true
-			// Extract just the filename from the config path for more concise messages
-			configFile := filepath.Base(spec.ConfigPath)
-			for _, problem := range problems {
-				msg := fmt.Sprintf("%s: %s", configFile, problem)
-				allProblems = append(allProblems, msg)
-				// Always display problems, not just in verbose mode
-				fmt.Fprintln(os.Stderr, console.FormatErrorMessage(msg))
-			}
-		}
-	}
-
-	if hasErrors {
-		compileCampaignLog.Printf("Campaign validation completed with %d problems", len(allProblems))
-		return fmt.Errorf("found %d problem(s) in campaign specs", len(allProblems))
-	}
-
-	compileCampaignLog.Printf("All %d campaign specs validated successfully", len(specsToValidate))
-	if verbose {
-		fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("All %d campaign spec(s) validated successfully", len(specsToValidate))))
-	}
-
-	return nil
-}
diff --git a/pkg/cli/compile_campaign_orchestrator_test.go b/pkg/cli/compile_campaign_orchestrator_test.go
deleted file mode 100644
index 61a298b526d..00000000000
--- a/pkg/cli/compile_campaign_orchestrator_test.go
+++ /dev/null
@@ -1,268 +0,0 @@
-//go:build !integration
-
-package cli
-
-import (
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-
-	"github.com/githubnext/gh-aw/pkg/campaign"
-	"github.com/githubnext/gh-aw/pkg/workflow"
-)
-
-func TestGenerateAndCompileCampaignOrchestrator(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	campaignSpecPath := filepath.Join(tmpDir, "test-campaign.campaign.md")
-
-	spec := &campaign.CampaignSpec{
-		ID:          "test-campaign",
-		Name:        "Test Campaign",
-		Description: "A test campaign",
-		Workflows:   []string{"example-workflow"},
-		MemoryPaths: []string{"memory/campaigns/test-campaign/**"},
-	}
-
-	// Compiler with auto-detected version and action mode
-	compiler := workflow.NewCompiler(
-		workflow.WithSkipValidation(true),
-		workflow.WithNoEmit(false),
-		workflow.WithStrictMode(false),
-	)
-
-	orchestratorPath, err := generateAndCompileCampaignOrchestrator(GenerateCampaignOrchestratorOptions{
-		Compiler:             compiler,
-		Spec:                 spec,
-		CampaignSpecPath:     campaignSpecPath,
-		Verbose:              false,
-		NoEmit:               false,
-		RunZizmorPerFile:     false,
-		RunPoutinePerFile:    false,
-		RunActionlintPerFile: false,
-		Strict:               false,
-		ValidateActionSHAs:   false,
-	})
-	if err != nil {
-		t.Fatalf("generateAndCompileCampaignOrchestrator() error: %v", err)
-	}
-
-	expectedPath := strings.TrimSuffix(campaignSpecPath, ".campaign.md") + ".campaign.g.md"
-	if orchestratorPath != expectedPath {
-		t.Fatalf("unexpected orchestrator path: got %q, want %q", orchestratorPath, expectedPath)
-	}
-
-	if _, statErr := os.Stat(orchestratorPath); statErr != nil {
-		t.Fatalf("expected orchestrator markdown to exist, stat error: %v", statErr)
-	}
-
-	// For campaign orchestrators (*.campaign.g.md), the lock file should be *.campaign.g.lock.yml
-	lockPath := strings.TrimSuffix(orchestratorPath, ".md") + ".lock.yml"
-	if _, statErr := os.Stat(lockPath); statErr != nil {
-		t.Fatalf("expected orchestrator lock file to exist at %s, stat error: %v", lockPath, statErr)
-	}
-
-	// Verify that the generated orchestrator has the required permissions
-	lockContent, readErr := os.ReadFile(lockPath)
-	if readErr != nil {
-		t.Fatalf("failed to read lock file: %v", readErr)
-	}
-	lockStr := string(lockContent)
-
-	if !strings.Contains(lockStr, "engine_id: \"claude\"") {
-		t.Errorf("expected lock file to use claude engine, got: %s", lockPath)
-	}
-
-	requiredPermissions := []string{
-		"contents: read",
-		"issues: read",
-	}
-
-	for _, perm := range requiredPermissions {
-		if !strings.Contains(lockStr, perm) {
-			t.Errorf("expected lock file to contain permission %q", perm)
-		}
-	}
-
-	// Note: Issue/project write operations are handled via safe-outputs which mint
-	// app tokens with appropriate permissions, not direct workflow permissions.
-
-	// Read the generated markdown file to verify the Source comment contains a relative path
-	mdContent, readErr := os.ReadFile(orchestratorPath)
-	if readErr != nil {
-		t.Fatalf("failed to read generated markdown file: %v", readErr)
-	}
-	mdStr := string(mdContent)
-
-	if !strings.Contains(mdStr, "engine: claude") {
-		t.Errorf("expected generated markdown to set engine: claude")
-	}
-
-	// Verify dispatch-workflow safe output is rendered (used for orchestration)
-	if !strings.Contains(mdStr, "dispatch-workflow:") {
-		t.Errorf("expected generated markdown to include dispatch-workflow safe output")
-	}
-	if !strings.Contains(mdStr, "example-workflow") {
-		t.Errorf("expected generated markdown to include allowlisted workflow 'example-workflow'")
-	}
-
-	// Verify that the Source comment exists and contains a relative path (not absolute)
-	if !strings.Contains(mdStr, "<!-- Source:") {
-		t.Errorf("expected generated markdown to contain Source comment")
-	}
-
-	// Extract the source path from the comment
-	if strings.Contains(mdStr, "<!-- Source:") {
-		// Find the source path in the comment
-		startIdx := strings.Index(mdStr, "<!-- Source:")
-		endIdx := strings.Index(mdStr[startIdx:], "-->")
-		if endIdx > 0 {
-			sourceComment := mdStr[startIdx : startIdx+endIdx]
-			// Verify it's not an absolute path (no leading / or drive letter)
-			if strings.Contains(sourceComment, "<!-- Source: /") ||
-				(len(sourceComment) > 15 && sourceComment[13] == ':' && sourceComment[14] == '\\') {
-				t.Errorf("Source comment contains absolute path: %q", sourceComment)
-			}
-			// Verify it contains the campaign filename
-			if !strings.Contains(sourceComment, "test-campaign.campaign.md") {
-				t.Errorf("Source comment doesn't contain expected filename: %q", sourceComment)
-			}
-		}
-	}
-}
-
-// TestCampaignSourceCommentStability verifies that the source path in the generated
-// campaign orchestrator is stable regardless of the current working directory
-func TestCampaignSourceCommentStability(t *testing.T) {
-	// Create a temporary git repository structure with .github directory
-	tmpDir := t.TempDir()
-	githubDir := filepath.Join(tmpDir, ".github", "workflows")
-	if err := os.MkdirAll(githubDir, 0755); err != nil {
-		t.Fatalf("failed to create .github/workflows directory: %v", err)
-	}
-
-	// Create a campaign spec in the .github/workflows directory
-	campaignSpecPath := filepath.Join(githubDir, "test-campaign.campaign.md")
-
-	spec := &campaign.CampaignSpec{
-		ID:          "test-campaign",
-		Name:        "Test Campaign",
-		Description: "A test campaign for path stability",
-		Workflows:   []string{"example-workflow"},
-		MemoryPaths: []string{"memory/campaigns/test-campaign/**"},
-	}
-
-	compiler := workflow.NewCompiler(
-		workflow.WithSkipValidation(true),
-		workflow.WithNoEmit(false),
-		workflow.WithStrictMode(false),
-	)
-
-	// Save original working directory
-	originalWd, err := os.Getwd()
-	if err != nil {
-		t.Fatalf("failed to get current working directory: %v", err)
-	}
-	defer func() {
-		// Restore original working directory
-		if err := os.Chdir(originalWd); err != nil {
-			t.Logf("warning: failed to restore working directory: %v", err)
-		}
-	}()
-
-	// Test 1: Generate from the tmp directory root
-	if err := os.Chdir(tmpDir); err != nil {
-		t.Fatalf("failed to change to tmp directory: %v", err)
-	}
-
-	orchestratorPath1, err := generateAndCompileCampaignOrchestrator(GenerateCampaignOrchestratorOptions{
-		Compiler:             compiler,
-		Spec:                 spec,
-		CampaignSpecPath:     campaignSpecPath,
-		Verbose:              false,
-		NoEmit:               false,
-		RunZizmorPerFile:     false,
-		RunPoutinePerFile:    false,
-		RunActionlintPerFile: false,
-		Strict:               false,
-		ValidateActionSHAs:   false,
-	})
-	if err != nil {
-		t.Fatalf("first generation error: %v", err)
-	}
-
-	mdContent1, err := os.ReadFile(orchestratorPath1)
-	if err != nil {
-		t.Fatalf("failed to read first generated file: %v", err)
-	}
-	sourcePath1 := extractSourcePath(t, string(mdContent1))
-
-	// Test 2: Generate from a subdirectory
-	pkgDir := filepath.Join(tmpDir, "pkg", "cli")
-	if err := os.MkdirAll(pkgDir, 0755); err != nil {
-		t.Fatalf("failed to create pkg/cli directory: %v", err)
-	}
-	if err := os.Chdir(pkgDir); err != nil {
-		t.Fatalf("failed to change to pkg/cli directory: %v", err)
-	}
-
-	// Remove the previously generated file to regenerate it
-	if err := os.Remove(orchestratorPath1); err != nil {
-		t.Fatalf("failed to remove first generated file: %v", err)
-	}
-
-	orchestratorPath2, err := generateAndCompileCampaignOrchestrator(GenerateCampaignOrchestratorOptions{
-		Compiler:             compiler,
-		Spec:                 spec,
-		CampaignSpecPath:     campaignSpecPath,
-		Verbose:              false,
-		NoEmit:               false,
-		RunZizmorPerFile:     false,
-		RunPoutinePerFile:    false,
-		RunActionlintPerFile: false,
-		Strict:               false,
-		ValidateActionSHAs:   false,
-	})
-	if err != nil {
-		t.Fatalf("second generation error: %v", err)
-	}
-
-	mdContent2, err := os.ReadFile(orchestratorPath2)
-	if err != nil {
-		t.Fatalf("failed to read second generated file: %v", err)
-	}
-	sourcePath2 := extractSourcePath(t, string(mdContent2))
-
-	// Verify both paths are identical and stable
-	if sourcePath1 != sourcePath2 {
-		t.Errorf("source paths differ based on working directory:\n  from root: %q\n  from subdir: %q", sourcePath1, sourcePath2)
-	}
-
-	// Verify the path is normalized to .github/workflows/...
-	expectedPath := ".github/workflows/test-campaign.campaign.md"
-	if sourcePath1 != expectedPath {
-		t.Errorf("expected stable path %q, got %q", expectedPath, sourcePath1)
-	}
-}
-
-// extractSourcePath extracts the source path from a markdown file's source comment
-func extractSourcePath(t *testing.T, content string) string {
-	t.Helper()
-
-	startMarker := "<!-- Source: "
-	endMarker := " -->"
-
-	startIdx := strings.Index(content, startMarker)
-	if startIdx == -1 {
-		t.Fatalf("source comment not found in content")
-	}
-
-	startIdx += len(startMarker)
-	endIdx := strings.Index(content[startIdx:], endMarker)
-	if endIdx == -1 {
-		t.Fatalf("source comment end marker not found")
-	}
-
-	return strings.TrimSpace(content[startIdx : startIdx+endIdx])
-}
diff --git a/pkg/cli/compile_campaign_validation_test.go b/pkg/cli/compile_campaign_validation_test.go
deleted file mode 100644
index 742d109d822..00000000000
--- a/pkg/cli/compile_campaign_validation_test.go
+++ /dev/null
@@ -1,90 +0,0 @@
-//go:build !integration
-
-package cli
-
-import (
-	"os"
-	"path/filepath"
-	"testing"
-
-	"github.com/githubnext/gh-aw/pkg/campaign"
-)
-
-// TestValidateCampaignsUsesCorrectDirectory tests that validateCampaigns uses
-// the campaign spec file's directory to validate referenced workflows, not
-// a global workflow directory parameter.
-func TestValidateCampaignsUsesCorrectDirectory(t *testing.T) {
-	tmpDir := t.TempDir()
-
-	// Create a campaign spec file
-	workflowsDir := filepath.Join(tmpDir, ".github", "workflows")
-	if err := os.MkdirAll(workflowsDir, 0755); err != nil {
-		t.Fatal(err)
-	}
-
-	// Create referenced workflow files
-	workflowFile := filepath.Join(workflowsDir, "example-workflow.md")
-	workflowContent := `---
-engine: copilot
----
-
-# Test workflow
-Test workflow content
-`
-	if err := os.WriteFile(workflowFile, []byte(workflowContent), 0644); err != nil {
-		t.Fatal(err)
-	}
-
-	// Create campaign spec file
-	campaignFile := filepath.Join(workflowsDir, "test-campaign.campaign.md")
-	campaignContent := `---
-id: test-campaign
-name: Test Campaign
-description: A test campaign
-version: v1
-project-url: https://github.com/orgs/test/projects/1
-workflows:
-  - example-workflow
-state: active
----
-
-# Campaign Test
-Test campaign
-`
-	if err := os.WriteFile(campaignFile, []byte(campaignContent), 0644); err != nil {
-		t.Fatal(err)
-	}
-
-	// Change to tmpDir so it becomes the git root for the test
-	origDir, err := os.Getwd()
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.Chdir(origDir)
-
-	if err := os.Chdir(tmpDir); err != nil {
-		t.Fatal(err)
-	}
-
-	// Initialize a git repo
-	if err := os.WriteFile(".git", []byte("fake git"), 0644); err != nil {
-		t.Fatal(err)
-	}
-
-	// Load the campaign spec
-	specs, err := campaign.LoadSpecs(tmpDir)
-	if err != nil {
-		t.Fatalf("LoadSpecs failed: %v", err)
-	}
-
-	if len(specs) == 0 {
-		t.Fatal("Expected to load 1 campaign spec, got 0")
-	}
-
-	// Validate campaigns with an incorrect workflow directory
-	// This should still work because validateCampaigns should use each spec's directory
-	err = validateCampaigns(".github/workflows", false, []string{campaignFile})
-	if err != nil {
-		t.Fatalf("validateCampaigns failed: %v", err)
-	}
-}
diff --git a/pkg/cli/compile_helpers.go b/pkg/cli/compile_helpers.go
index e9fe1065ac8..00ff0244ec9 100644
--- a/pkg/cli/compile_helpers.go
+++ b/pkg/cli/compile_helpers.go
@@ -48,6 +48,36 @@ import (
 
 var compileHelpersLog = logger.New("cli:compile_helpers")
 
+// getRepositoryRelativePath converts an absolute file path to a repository-relative path
+// This ensures stable workflow identifiers regardless of where the repository is cloned
+func getRepositoryRelativePath(absPath string) (string, error) {
+	// Get the repository root for the specific file
+	repoRoot, err := findGitRootForPath(absPath)
+	if err != nil {
+		// If we can't get the repo root, just use the basename as fallback
+		compileHelpersLog.Printf("Warning: could not get repository root for %s: %v, using basename", absPath, err)
+		return filepath.Base(absPath), nil
+	}
+
+	// Convert both paths to absolute to ensure they can be compared
+	absPath, err = filepath.Abs(absPath)
+	if err != nil {
+		return "", fmt.Errorf("failed to get absolute path: %w", err)
+	}
+
+	// Get the relative path from repo root
+	relPath, err := filepath.Rel(repoRoot, absPath)
+	if err != nil {
+		return "", fmt.Errorf("failed to get relative path: %w", err)
+	}
+
+	// Normalize path separators to forward slashes for consistency across platforms
+	// This ensures the same hash value on Windows, Linux, and macOS
+	relPath = filepath.ToSlash(relPath)
+
+	return relPath, nil
+}
+
 // compileSingleFile compiles a single markdown workflow file and updates compilation statistics
 // If checkExists is true, the function will check if the file exists before compiling
 // Returns true if compilation was attempted (file exists or checkExists is false), false otherwise
diff --git a/pkg/cli/compile_orchestration.go b/pkg/cli/compile_orchestration.go
index 55a08c80091..3866db2b998 100644
--- a/pkg/cli/compile_orchestration.go
+++ b/pkg/cli/compile_orchestration.go
@@ -26,7 +26,6 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
-	"strings"
 
 	"github.com/githubnext/gh-aw/pkg/stringutil"
 
@@ -59,7 +58,6 @@ func compileSpecificFiles(
 	var errorMessages []string
 	var lockFilesForActionlint []string
 	var lockFilesForZizmor []string
-	var campaignFiles []string
 
 	// Compile each specified file
 	for _, markdownFile := range config.MarkdownFiles {
@@ -97,31 +95,6 @@ func compileSpecificFiles(
 		// Update result with resolved file name
 		result.Workflow = filepath.Base(resolvedFile)
 
-		// Handle campaign spec files separately
-		if strings.HasSuffix(resolvedFile, ".campaign.md") {
-			campaignFiles = append(campaignFiles, resolvedFile)
-			campaignResult, success := processCampaignSpec(ProcessCampaignSpecOptions{
-				Compiler:     compiler,
-				ResolvedFile: resolvedFile,
-				Verbose:      config.Verbose,
-				JSONOutput:   config.JSONOutput,
-				NoEmit:       config.NoEmit,
-				Zizmor:       false,
-				Poutine:      false,
-				Actionlint:   false,
-				Strict:       config.Strict,
-				Validate:     shouldValidate,
-			})
-			if !success {
-				errorCount++
-				stats.Errors++
-				trackWorkflowFailure(stats, resolvedFile, len(campaignResult.Errors))
-				errorMessages = append(errorMessages, campaignResult.Errors[0].Message)
-			}
-			*validationResults = append(*validationResults, campaignResult)
-			continue
-		}
-
 		// Compile regular workflow file (disable per-file security tools)
 		fileResult := compileWorkflowFile(
 			compiler, resolvedFile, config.Verbose, config.JSONOutput,
@@ -190,7 +163,7 @@ func compileSpecificFiles(
 	displayScheduleWarnings(compiler, config.JSONOutput)
 
 	// Post-processing
-	if err := runPostProcessing(compiler, workflowDataList, config, compiledCount, campaignFiles); err != nil {
+	if err := runPostProcessing(compiler, workflowDataList, config, compiledCount); err != nil {
 		return workflowDataList, err
 	}
 
@@ -277,29 +250,6 @@ func compileAllFilesInDirectory(
 	for _, file := range mdFiles {
 		stats.Total++
 
-		// Handle campaign spec files
-		if strings.HasSuffix(file, ".campaign.md") {
-			campaignResult, success := processCampaignSpec(ProcessCampaignSpecOptions{
-				Compiler:     compiler,
-				ResolvedFile: file,
-				Verbose:      config.Verbose,
-				JSONOutput:   config.JSONOutput,
-				NoEmit:       config.NoEmit,
-				Zizmor:       false,
-				Poutine:      false,
-				Actionlint:   false,
-				Strict:       config.Strict,
-				Validate:     shouldValidate,
-			})
-			if !success {
-				errorCount++
-				stats.Errors++
-				trackWorkflowFailure(stats, file, len(campaignResult.Errors))
-			}
-			*validationResults = append(*validationResults, campaignResult)
-			continue
-		}
-
 		// Compile regular workflow file (disable per-file security tools)
 		fileResult := compileWorkflowFile(
 			compiler, file, config.Verbose, config.JSONOutput,
@@ -447,7 +397,6 @@ func runPostProcessing(
 	workflowDataList []*workflow.WorkflowData,
 	config CompileConfig,
 	successCount int,
-	campaignFiles []string,
 ) error {
 	// Get action cache
 	actionCache := compiler.GetSharedActionCache()
@@ -475,17 +424,6 @@ func runPostProcessing(
 	// to check for expires fields, so we skip it when compiling specific files to avoid
 	// unnecessary parsing and warnings from unrelated workflows
 
-	// Validate campaigns only if we're compiling campaign files
-	// When compiling specific non-campaign workflows, skip campaign validation
-	// When compiling specific campaign files, validate only those campaign files
-	if len(campaignFiles) > 0 {
-		if err := validateCampaignsWrapper(config.WorkflowDir, config.Verbose, config.Strict, campaignFiles); err != nil {
-			if config.Strict {
-				return err
-			}
-		}
-	}
-
 	// Save action cache (errors are logged but non-fatal)
 	_ = saveActionCache(actionCache, config.Verbose)
 
@@ -528,13 +466,6 @@ func runPostProcessingForDirectory(
 		}
 	}
 
-	// Validate campaigns
-	if err := validateCampaignsWrapper(config.WorkflowDir, config.Verbose, config.Strict, nil); err != nil {
-		if config.Strict {
-			return err
-		}
-	}
-
 	// Save action cache (errors are logged but non-fatal)
 	_ = saveActionCache(actionCache, config.Verbose)
 
diff --git a/pkg/cli/compile_orchestrator.go b/pkg/cli/compile_orchestrator.go
index 522426ea658..97ac2a41020 100644
--- a/pkg/cli/compile_orchestrator.go
+++ b/pkg/cli/compile_orchestrator.go
@@ -5,231 +5,14 @@ import (
 	"fmt"
 	"os"
 	"path/filepath"
-	"strings"
 
-	"github.com/githubnext/gh-aw/pkg/campaign"
 	"github.com/githubnext/gh-aw/pkg/console"
 	"github.com/githubnext/gh-aw/pkg/logger"
 	"github.com/githubnext/gh-aw/pkg/workflow"
-	"github.com/goccy/go-yaml"
 )
 
 var compileOrchestratorLog = logger.New("cli:compile_orchestrator")
 
-// getRepositoryRelativePath converts an absolute file path to a repository-relative path
-// This ensures stable workflow identifiers regardless of where the repository is cloned
-func getRepositoryRelativePath(absPath string) (string, error) {
-	// Get the repository root for the specific file
-	repoRoot, err := findGitRootForPath(absPath)
-	if err != nil {
-		// If we can't get the repo root, just use the basename as fallback
-		compileOrchestratorLog.Printf("Warning: could not get repository root for %s: %v, using basename", absPath, err)
-		return filepath.Base(absPath), nil
-	}
-
-	// Convert both paths to absolute to ensure they can be compared
-	absPath, err = filepath.Abs(absPath)
-	if err != nil {
-		return "", fmt.Errorf("failed to get absolute path: %w", err)
-	}
-
-	// Get the relative path from repo root
-	relPath, err := filepath.Rel(repoRoot, absPath)
-	if err != nil {
-		return "", fmt.Errorf("failed to get relative path: %w", err)
-	}
-
-	// Normalize path separators to forward slashes for consistency across platforms
-	// This ensures the same hash value on Windows, Linux, and macOS
-	relPath = filepath.ToSlash(relPath)
-
-	return relPath, nil
-}
-
-func renderGeneratedCampaignOrchestratorMarkdown(data *workflow.WorkflowData, sourceCampaignPath string) string {
-	// Produce a conventional gh-aw workflow markdown file so users can review
-	// the generated orchestrator and recompile it like any other workflow.
-	//
-	// NOTE: The generated .campaign.g.md file is a debug artifact that is NOT
-	// committed to git (it's in .gitignore). Users can review it locally to
-	// understand the generated workflow structure. Only the source .campaign.md
-	// and the compiled .campaign.lock.yml files are committed.
-	b := &strings.Builder{}
-	b.WriteString("---\n")
-	if strings.TrimSpace(data.Name) != "" {
-		fmt.Fprintf(b, "name: %q\n", data.Name)
-	}
-	if strings.TrimSpace(data.Description) != "" {
-		fmt.Fprintf(b, "description: %q\n", data.Description)
-	}
-	if strings.TrimSpace(data.On) != "" {
-		b.WriteString(strings.TrimSuffix(data.On, "\n"))
-		b.WriteString("\n")
-	}
-	if strings.TrimSpace(data.Concurrency) != "" {
-		b.WriteString(strings.TrimSuffix(data.Concurrency, "\n"))
-		b.WriteString("\n")
-	}
-
-	// Make the orchestrator runnable by default.
-	// Use engine from EngineConfig if available, otherwise default to claude.
-	engineID := "claude"
-	if data.EngineConfig != nil && data.EngineConfig.ID != "" {
-		engineID = data.EngineConfig.ID
-	}
-	fmt.Fprintf(b, "engine: %s\n", engineID)
-
-	// Render safe-outputs if configured by the campaign orchestrator generator.
-	// Campaign orchestrators support dispatch-workflow, update-project, and create-project-status-update.
-	if data.SafeOutputs != nil {
-		// NOTE: We must emit the public frontmatter keys (e.g. "add-comment") rather
-		// than the internal struct YAML tags (e.g. "add-comments").
-		outputs := map[string]any{}
-		if data.SafeOutputs.DispatchWorkflow != nil {
-			dispatchWorkflowConfig := map[string]any{
-				"max": data.SafeOutputs.DispatchWorkflow.Max,
-			}
-			if len(data.SafeOutputs.DispatchWorkflow.Workflows) > 0 {
-				dispatchWorkflowConfig["workflows"] = data.SafeOutputs.DispatchWorkflow.Workflows
-			}
-			outputs["dispatch-workflow"] = dispatchWorkflowConfig
-		}
-		if data.SafeOutputs.UpdateProjects != nil {
-			updateProjectConfig := map[string]any{
-				"max": data.SafeOutputs.UpdateProjects.Max,
-			}
-			if data.SafeOutputs.UpdateProjects.GitHubToken != "" {
-				updateProjectConfig["github-token"] = data.SafeOutputs.UpdateProjects.GitHubToken
-			}
-			outputs["update-project"] = updateProjectConfig
-		}
-		if data.SafeOutputs.CreateProjectStatusUpdates != nil {
-			createStatusUpdateConfig := map[string]any{
-				"max": data.SafeOutputs.CreateProjectStatusUpdates.Max,
-			}
-			if data.SafeOutputs.CreateProjectStatusUpdates.GitHubToken != "" {
-				createStatusUpdateConfig["github-token"] = data.SafeOutputs.CreateProjectStatusUpdates.GitHubToken
-			}
-			outputs["create-project-status-update"] = createStatusUpdateConfig
-		}
-		if len(outputs) > 0 {
-			payload := map[string]any{"safe-outputs": outputs}
-			if out, err := yaml.Marshal(payload); err == nil {
-				b.WriteString(string(out))
-			} else {
-				compileOrchestratorLog.Printf("Failed to render safe-outputs for generated campaign orchestrator: %v", err)
-			}
-		}
-	}
-
-	// Intentionally omit permissions from generated campaign orchestrator frontmatter.
-	// Workflow/job permissions are handled during compilation.
-	if strings.TrimSpace(data.RunsOn) != "" {
-		b.WriteString(strings.TrimSuffix(data.RunsOn, "\n"))
-		b.WriteString("\n")
-	}
-	if len(data.Roles) > 0 {
-		b.WriteString("roles:\n")
-		for _, role := range data.Roles {
-			if strings.TrimSpace(role) == "" {
-				continue
-			}
-			fmt.Fprintf(b, "  - %q\n", role)
-		}
-	}
-	// Render tools configuration if present
-	if len(data.Tools) > 0 {
-		payload := map[string]any{"tools": data.Tools}
-		if out, err := yaml.Marshal(payload); err == nil {
-			b.WriteString(string(out))
-		} else {
-			compileOrchestratorLog.Printf("Failed to render tools for generated campaign orchestrator: %v", err)
-		}
-	}
-	// Render custom steps if present (e.g., discovery precomputation)
-	if strings.TrimSpace(data.CustomSteps) != "" {
-		// CustomSteps is already YAML-formatted, just write it as is
-		b.WriteString("steps:\n")
-		b.WriteString(data.CustomSteps)
-	}
-	b.WriteString("---\n\n")
-	// Include version for released builds only (not "dev", "dirty", or "test")
-	version := workflow.GetVersion()
-	if workflow.IsReleasedVersion(version) {
-		fmt.Fprintf(b, "<!-- This file was automatically generated by gh-aw (%s). DO NOT EDIT. -->\n", version)
-	} else {
-		b.WriteString("<!-- This file was automatically generated by gh-aw. DO NOT EDIT. -->\n")
-	}
-	if strings.TrimSpace(sourceCampaignPath) != "" {
-		// Normalize path to be relative to git root (where .github folder exists)
-		// This ensures stable paths regardless of current working directory
-		relativePath := ToGitRootRelativePath(sourceCampaignPath)
-		fmt.Fprintf(b, "<!-- Source: %s -->\n", relativePath)
-	}
-	b.WriteString("\n")
-	b.WriteString(strings.TrimSpace(data.MarkdownContent))
-	b.WriteString("\n")
-	return b.String()
-}
-
-// GenerateCampaignOrchestratorOptions holds the options for generateAndCompileCampaignOrchestrator
-type GenerateCampaignOrchestratorOptions struct {
-	Compiler             *workflow.Compiler
-	Spec                 *campaign.CampaignSpec
-	CampaignSpecPath     string
-	Verbose              bool
-	NoEmit               bool
-	RunZizmorPerFile     bool
-	RunPoutinePerFile    bool
-	RunActionlintPerFile bool
-	Strict               bool
-	ValidateActionSHAs   bool
-}
-
-func generateAndCompileCampaignOrchestrator(opts GenerateCampaignOrchestratorOptions) (string, error) {
-	data, orchestratorPath := campaign.BuildOrchestrator(opts.Spec, opts.CampaignSpecPath)
-	if data == nil || orchestratorPath == "" {
-		return "", nil
-	}
-
-	// Ensure we pick a real engine in the YAML compiler path.
-	// Campaign orchestrators should default to claude (matching the orchestrator generator).
-	if strings.TrimSpace(data.AI) == "" {
-		engineID := "claude"
-		if data.EngineConfig != nil && strings.TrimSpace(data.EngineConfig.ID) != "" {
-			engineID = strings.TrimSpace(data.EngineConfig.ID)
-		}
-		data.AI = engineID
-	}
-
-	if !opts.NoEmit {
-		content := renderGeneratedCampaignOrchestratorMarkdown(data, opts.CampaignSpecPath)
-		// Write with restrictive permissions (0600) to follow security best practices
-		if err := os.WriteFile(orchestratorPath, []byte(content), 0600); err != nil {
-			return "", fmt.Errorf("failed to write generated campaign orchestrator %s: %w", orchestratorPath, err)
-		}
-		if opts.Verbose {
-			fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Generated campaign orchestrator %s", filepath.Base(orchestratorPath))))
-		}
-	}
-
-	// Prefer compiling from the generated markdown so defaults and validation behavior
-	// match normal workflows (including computed permissions).
-	if !opts.NoEmit {
-		if err := CompileWorkflowWithValidation(opts.Compiler, orchestratorPath, opts.Verbose, opts.RunZizmorPerFile, opts.RunPoutinePerFile, opts.RunActionlintPerFile, opts.Strict, opts.ValidateActionSHAs); err != nil {
-			return orchestratorPath, err
-		}
-		return orchestratorPath, nil
-	}
-
-	// No-emit mode: compile from the in-memory WorkflowData.
-	if err := CompileWorkflowDataWithValidation(opts.Compiler, data, orchestratorPath, opts.Verbose, opts.RunZizmorPerFile, opts.RunPoutinePerFile, opts.RunActionlintPerFile, opts.Strict, opts.ValidateActionSHAs); err != nil {
-		return orchestratorPath, err
-	}
-
-	return orchestratorPath, nil
-}
-
 // CompileWorkflows compiles workflows based on the provided configuration
 func CompileWorkflows(ctx context.Context, config CompileConfig) ([]*workflow.WorkflowData, error) {
 	compileOrchestratorLog.Printf("Starting workflow compilation: files=%d, validate=%v, watch=%v, noEmit=%v",
diff --git a/pkg/cli/compile_post_processing.go b/pkg/cli/compile_post_processing.go
index dd6f53df5c5..ff13dfdc7fb 100644
--- a/pkg/cli/compile_post_processing.go
+++ b/pkg/cli/compile_post_processing.go
@@ -1,7 +1,7 @@
 // This file provides post-processing operations for workflow compilation.
 //
 // This file contains functions that perform post-compilation operations such as
-// generating Dependabot manifests, maintenance workflows, and validating campaigns.
+// generating Dependabot manifests and maintenance workflows.
 //
 // # Organization Rationale
 //
@@ -17,9 +17,6 @@
 //   - generateDependabotManifestsWrapper() - Generate Dependabot manifests
 //   - generateMaintenanceWorkflowWrapper() - Generate maintenance workflow
 //
-// Validation:
-//   - validateCampaignsWrapper() - Validate campaign specs
-//
 // Statistics:
 //   - collectWorkflowStatisticsWrapper() - Collect workflow statistics
 //
@@ -83,21 +80,6 @@ func generateMaintenanceWorkflowWrapper(
 	return nil
 }
 
-// validateCampaignsWrapper validates campaign specs if they exist
-func validateCampaignsWrapper(workflowDir string, verbose bool, strict bool, campaignFiles []string) error {
-	compilePostProcessingLog.Print("Validating campaign specs")
-
-	if err := validateCampaigns(workflowDir, verbose, campaignFiles); err != nil {
-		if strict {
-			return fmt.Errorf("campaign validation failed: %w", err)
-		}
-		// Non-strict mode: just report as warning
-		fmt.Fprintln(os.Stderr, console.FormatWarningMessage(fmt.Sprintf("Campaign validation: %v", err)))
-	}
-
-	return nil
-}
-
 // collectWorkflowStatisticsWrapper collects and returns workflow statistics
 func collectWorkflowStatisticsWrapper(markdownFiles []string) []*WorkflowStats {
 	compilePostProcessingLog.Printf("Collecting workflow statistics for %d files", len(markdownFiles))
diff --git a/pkg/cli/compile_workflow_processor.go b/pkg/cli/compile_workflow_processor.go
index db4465b425f..8c07cfdc84e 100644
--- a/pkg/cli/compile_workflow_processor.go
+++ b/pkg/cli/compile_workflow_processor.go
@@ -1,13 +1,12 @@
 // This file provides workflow file processing functions for compilation.
 //
-// This file contains functions that process individual workflow files and
-// campaign specs, handling both regular workflows and campaign orchestrators.
+// This file contains functions that process individual workflow files.
 //
 // # Organization Rationale
 //
 // These workflow processing functions are grouped here because they:
 //   - Handle per-file processing logic
-//   - Process both regular workflows and campaign specs
+//   - Process workflow files with compilation and validation
 //   - Have a clear domain focus (workflow file processing)
 //   - Keep the main orchestrator focused on batch operations
 //
@@ -15,7 +14,6 @@
 //
 // Workflow Processing:
 //   - processWorkflowFile() - Process a single workflow markdown file
-//   - processCampaignSpec() - Process a campaign spec file
 //   - collectLockFilesForLinting() - Collect lock files for batch linting
 //
 // These functions abstract per-file processing, allowing the main compile
@@ -28,7 +26,6 @@ import (
 	"os"
 	"path/filepath"
 
-	"github.com/githubnext/gh-aw/pkg/campaign"
 	"github.com/githubnext/gh-aw/pkg/console"
 	"github.com/githubnext/gh-aw/pkg/logger"
 	"github.com/githubnext/gh-aw/pkg/stringutil"
@@ -129,21 +126,6 @@ func compileWorkflowFile(
 	}
 	result.workflowData = workflowData
 
-	// Inject campaign orchestrator features if project field has campaign configuration
-	// This transforms the workflow into a campaign orchestrator in-place
-	if err := campaign.InjectOrchestratorFeatures(workflowData); err != nil {
-		errMsg := fmt.Sprintf("failed to inject campaign orchestrator features: %v", err)
-		if !jsonOutput {
-			fmt.Fprintln(os.Stderr, console.FormatErrorMessage(errMsg))
-		}
-		result.validationResult.Valid = false
-		result.validationResult.Errors = append(result.validationResult.Errors, CompileValidationError{
-			Type:    "campaign_injection_error",
-			Message: err.Error(),
-		})
-		return result
-	}
-
 	compileWorkflowProcessorLog.Printf("Starting compilation of %s", resolvedFile)
 
 	// Compile the workflow
@@ -165,93 +147,3 @@ func compileWorkflowFile(
 	compileWorkflowProcessorLog.Printf("Successfully processed workflow file: %s", resolvedFile)
 	return result
 }
-
-// ProcessCampaignSpecOptions holds the options for processCampaignSpec
-type ProcessCampaignSpecOptions struct {
-	Compiler     *workflow.Compiler
-	ResolvedFile string
-	Verbose      bool
-	JSONOutput   bool
-	NoEmit       bool
-	Zizmor       bool
-	Poutine      bool
-	Actionlint   bool
-	Strict       bool
-	Validate     bool
-}
-
-// processCampaignSpec processes a campaign spec file
-// Returns the validation result and success status
-func processCampaignSpec(opts ProcessCampaignSpecOptions) (ValidationResult, bool) {
-	compileWorkflowProcessorLog.Printf("Processing campaign spec file: %s", opts.ResolvedFile)
-
-	result := ValidationResult{
-		Workflow: filepath.Base(opts.ResolvedFile),
-		Valid:    true,
-		Errors:   []CompileValidationError{},
-		Warnings: []CompileValidationError{},
-	}
-
-	// Validate the campaign spec file and referenced workflows
-	spec, problems, vErr := campaign.ValidateSpecFromFile(opts.ResolvedFile)
-	if vErr != nil {
-		errMsg := fmt.Sprintf("failed to validate campaign spec %s: %v", opts.ResolvedFile, vErr)
-		if !opts.JSONOutput {
-			fmt.Fprintln(os.Stderr, console.FormatErrorMessage(errMsg))
-		}
-		result.Valid = false
-		result.Errors = append(result.Errors, CompileValidationError{
-			Type:    "campaign_validation_error",
-			Message: vErr.Error(),
-		})
-		return result, false
-	}
-
-	// Also ensure that workflows referenced by the campaign spec exist
-	workflowsDir := filepath.Dir(opts.ResolvedFile)
-	workflowProblems := campaign.ValidateWorkflowsExist(spec, workflowsDir)
-	problems = append(problems, workflowProblems...)
-
-	if len(problems) > 0 {
-		for _, p := range problems {
-			if !opts.JSONOutput {
-				fmt.Fprintln(os.Stderr, console.FormatErrorMessage(p))
-			}
-			result.Valid = false
-			result.Errors = append(result.Errors, CompileValidationError{
-				Type:    "campaign_validation_error",
-				Message: p,
-			})
-		}
-		return result, false
-	}
-
-	if opts.Verbose && !opts.JSONOutput {
-		fmt.Fprintln(os.Stderr, console.FormatSuccessMessage(fmt.Sprintf("Validated campaign spec %s", filepath.Base(opts.ResolvedFile))))
-	}
-
-	// Generate and compile the campaign orchestrator
-	if _, genErr := generateAndCompileCampaignOrchestrator(GenerateCampaignOrchestratorOptions{
-		Compiler:             opts.Compiler,
-		Spec:                 spec,
-		CampaignSpecPath:     opts.ResolvedFile,
-		Verbose:              opts.Verbose && !opts.JSONOutput,
-		NoEmit:               opts.NoEmit,
-		RunZizmorPerFile:     opts.Zizmor && !opts.NoEmit,
-		RunPoutinePerFile:    opts.Poutine && !opts.NoEmit,
-		RunActionlintPerFile: opts.Actionlint && !opts.NoEmit,
-		Strict:               opts.Strict,
-		ValidateActionSHAs:   opts.Validate && !opts.NoEmit,
-	}); genErr != nil {
-		errMsg := fmt.Sprintf("failed to compile campaign orchestrator for %s: %v", filepath.Base(opts.ResolvedFile), genErr)
-		if !opts.JSONOutput {
-			fmt.Fprintln(os.Stderr, console.FormatErrorMessage(errMsg))
-		}
-		result.Valid = false
-		result.Errors = append(result.Errors, CompileValidationError{Type: "campaign_orchestrator_error", Message: errMsg})
-		return result, false
-	}
-
-	compileWorkflowProcessorLog.Printf("Successfully processed campaign spec: %s", opts.ResolvedFile)
-	return result, true
-}
diff --git a/pkg/cli/copilot-agents.go b/pkg/cli/copilot-agents.go
index bd639f0a103..c8d4bf4e10b 100644
--- a/pkg/cli/copilot-agents.go
+++ b/pkg/cli/copilot-agents.go
@@ -212,66 +212,6 @@ func ensureUpgradeAgenticWorkflowsPrompt(verbose bool, skipInstructions bool) er
 	)
 }
 
-// ensureCampaignOrchestratorInstructions ensures that .github/aw/orchestrate-agentic-campaign.md exists
-func ensureCampaignOrchestratorInstructions(verbose bool, skipInstructions bool) error {
-	return ensureFileMatchesTemplate(
-		filepath.Join(".github", "aw"),
-		"orchestrate-agentic-campaign.md",
-		campaignOrchestratorInstructionsTemplate,
-		"campaign orchestrator instructions",
-		verbose,
-		skipInstructions,
-	)
-}
-
-// ensureCampaignProjectUpdateInstructions ensures that .github/aw/update-agentic-campaign-project.md exists
-func ensureCampaignProjectUpdateInstructions(verbose bool, skipInstructions bool) error {
-	return ensureFileMatchesTemplate(
-		filepath.Join(".github", "aw"),
-		"update-agentic-campaign-project.md",
-		campaignProjectUpdateInstructionsTemplate,
-		"campaign project update instructions",
-		verbose,
-		skipInstructions,
-	)
-}
-
-// ensureCampaignWorkflowExecution ensures that .github/aw/execute-agentic-campaign-workflow.md exists
-func ensureCampaignWorkflowExecution(verbose bool, skipInstructions bool) error {
-	return ensureFileMatchesTemplate(
-		filepath.Join(".github", "aw"),
-		"execute-agentic-campaign-workflow.md",
-		campaignWorkflowExecutionTemplate,
-		"campaign workflow execution",
-		verbose,
-		skipInstructions,
-	)
-}
-
-// ensureCampaignClosingInstructions ensures that .github/aw/close-agentic-campaign.md exists
-func ensureCampaignClosingInstructions(verbose bool, skipInstructions bool) error {
-	return ensureFileMatchesTemplate(
-		filepath.Join(".github", "aw"),
-		"close-agentic-campaign.md",
-		campaignClosingInstructionsTemplate,
-		"campaign closing instructions",
-		verbose,
-		skipInstructions,
-	)
-}
-
-// ensureCampaignGeneratorInstructions ensures that .github/aw/generate-agentic-campaign.md exists
-func ensureCampaignGeneratorInstructions(verbose bool, skipInstructions bool) error {
-	return ensureFileMatchesTemplate(
-		filepath.Join(".github", "aw"),
-		"generate-agentic-campaign.md",
-		campaignGeneratorInstructionsTemplate,
-		"campaign generator instructions",
-		verbose,
-		skipInstructions,
-	)
-}
-
 // deleteSetupAgenticWorkflowsAgent deletes the setup-agentic-workflows.agent.md file if it exists
 func deleteSetupAgenticWorkflowsAgent(verbose bool) error {
 	gitRoot, err := findGitRoot()
diff --git a/pkg/cli/init.go b/pkg/cli/init.go
index 31d0ff00167..46e2d0a2381 100644
--- a/pkg/cli/init.go
+++ b/pkg/cli/init.go
@@ -564,32 +564,6 @@ func InitRepository(verbose bool, mcp bool, campaign bool, tokens bool, engine s
 		fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Created upgrade workflows prompt"))
 	}
 
-	// Write campaign dispatcher agent if requested
-	if campaign {
-		// Write campaign instruction files
-		initLog.Print("Writing campaign instruction files")
-		campaignEnsureFuncs := []struct {
-			fn   func(bool, bool) error
-			name string
-		}{
-			{ensureCampaignGeneratorInstructions, "campaign generator instructions"},
-			{ensureCampaignOrchestratorInstructions, "campaign orchestrator instructions"},
-			{ensureCampaignProjectUpdateInstructions, "campaign project update instructions"},
-			{ensureCampaignWorkflowExecution, "campaign workflow execution"},
-			{ensureCampaignClosingInstructions, "campaign closing instructions"},
-		}
-
-		for _, item := range campaignEnsureFuncs {
-			if err := item.fn(verbose, false); err != nil {
-				initLog.Printf("Failed to write %s: %v", item.name, err)
-				return fmt.Errorf("failed to write %s: %w", item.name, err)
-			}
-		}
-		if verbose {
-			fmt.Fprintln(os.Stderr, console.FormatSuccessMessage("Created campaign instruction files"))
-		}
-	}
-
 	// Configure MCP if requested
 	if mcp {
 		initLog.Print("Configuring GitHub Copilot Agent MCP integration")
diff --git a/pkg/workflow/compiler.go b/pkg/workflow/compiler.go
index 0fbc9ad6c97..5a216bf635f 100644
--- a/pkg/workflow/compiler.go
+++ b/pkg/workflow/compiler.go
@@ -213,15 +213,6 @@ func (c *Compiler) CompileWorkflowData(workflowData *WorkflowData, markdownPath
 		c.IncrementWarningCount()
 	}
 
-	// Emit experimental warning for campaigns feature
-	// Campaign workflows (.campaign.md) are compiled by the campaign system in pkg/campaign/
-	// This warning is part of the general workflow compilation pipeline and simply
-	// detects campaign files to inform users about the experimental status.
-	if strings.HasSuffix(markdownPath, ".campaign.md") {
-		fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Using experimental feature: campaigns - This is a preview feature for multi-workflow orchestration. The campaign spec format, CLI commands, and repo-memory conventions may change in future releases. Workflows may break or require migration when the feature stabilizes."))
-		c.IncrementWarningCount()
-	}
-
 	// Validate workflow_run triggers have branch restrictions
 	log.Printf("Validating workflow_run triggers for branch restrictions")
 	if err := c.validateWorkflowRunBranches(workflowData, markdownPath); err != nil {
diff --git a/pkg/workflow/tools.go b/pkg/workflow/tools.go
index fbe23f91f0a..7c7ac662eb2 100644
--- a/pkg/workflow/tools.go
+++ b/pkg/workflow/tools.go
@@ -169,66 +169,18 @@ func (c *Compiler) applyDefaults(data *WorkflowData, markdownPath string) error
 		// When no permissions are specified, set default to contents: read.
 		// This provides minimal access needed for most workflows while following
 		// the principle of least privilege.
-		//
-		// CAMPAIGN-SPECIFIC HANDLING:
-		// Campaign orchestrator workflows (.campaign.g.md files) are auto-generated
-		// by the BuildOrchestrator function in pkg/campaign/orchestrator.go.
-		// These generated workflows intentionally omit explicit permissions in their
-		// frontmatter, so we compute minimal read permissions here at compile time.
-		//
-		// This is part of the campaign orchestrator generation pattern where:
-		// 1. Campaign specs (.campaign.md) define high-level campaign configuration
-		// 2. BuildOrchestrator generates orchestrator workflows (.campaign.g.md)
-		// 3. The compiler applies default permissions to the generated workflow
-		//
-		// This separation allows campaign configuration to remain declarative while
-		// ensuring generated orchestrators have appropriate permissions.
 		// ============================================================================
-		if strings.HasSuffix(markdownPath, ".campaign.g.md") {
-			perms := NewPermissions()
-			// Campaign orchestrators always need to read repository contents and tracker issues.
-			perms.Set(PermissionContents, PermissionRead)
-			perms.Set(PermissionIssues, PermissionRead)
-
-			// If GitHub MCP toolsets are configured (including defaults), add any additional
-			// required permissions to avoid validation warnings.
-			var githubTool any
-			if data.Tools != nil {
-				githubTool = data.Tools["github"]
+		perms := NewPermissionsContentsRead()
+		yaml := perms.RenderToYAML()
+		// RenderToYAML uses job-friendly indentation (6 spaces). WorkflowData.Permissions
+		// is stored in workflow-level indentation (2 spaces) and later re-indented for jobs.
+		lines := strings.Split(yaml, "\n")
+		for i := 1; i < len(lines); i++ {
+			if strings.HasPrefix(lines[i], "      ") {
+				lines[i] = "  " + lines[i][6:]
 			}
-			if githubTool != nil {
-				toolsetsStr := getGitHubToolsets(githubTool)
-				readOnly := getGitHubReadOnly(githubTool)
-				required := collectRequiredPermissions(ParseGitHubToolsets(toolsetsStr), readOnly)
-				for scope, level := range required {
-					perms.Set(scope, level)
-				}
-			}
-
-			yaml := perms.RenderToYAML()
-			// RenderToYAML uses job-friendly indentation (6 spaces). WorkflowData.Permissions
-			// is stored in workflow-level indentation (2 spaces) and later re-indented for jobs.
-			lines := strings.Split(yaml, "\n")
-			for i := 1; i < len(lines); i++ {
-				if strings.HasPrefix(lines[i], "      ") {
-					lines[i] = "  " + lines[i][6:]
-				}
-			}
-			data.Permissions = strings.Join(lines, "\n")
-		} else {
-			// For non-campaign workflows, set default to contents: read
-			perms := NewPermissionsContentsRead()
-			yaml := perms.RenderToYAML()
-			// RenderToYAML uses job-friendly indentation (6 spaces). WorkflowData.Permissions
-			// is stored in workflow-level indentation (2 spaces) and later re-indented for jobs.
-			lines := strings.Split(yaml, "\n")
-			for i := 1; i < len(lines); i++ {
-				if strings.HasPrefix(lines[i], "      ") {
-					lines[i] = "  " + lines[i][6:]
-				}
-			}
-			data.Permissions = strings.Join(lines, "\n")
 		}
+		data.Permissions = strings.Join(lines, "\n")
 	}
 	return nil
 }

From be669b8df645d93f10f6a4d3c69021bcf8ffc67c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 29 Jan 2026 23:33:09 +0000
Subject: [PATCH 4/7] Fix test files to remove campaign references

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .../security-alert-burndown.lock.yml          | 720 ------------------
 cmd/gh-aw/argument_syntax_test.go             |  58 --
 cmd/gh-aw/capitalization_test.go              |   2 -
 go.mod                                        |   2 +-
 4 files changed, 1 insertion(+), 781 deletions(-)

diff --git a/.github/workflows/security-alert-burndown.lock.yml b/.github/workflows/security-alert-burndown.lock.yml
index 36a706788b5..b3c8afad78d 100644
--- a/.github/workflows/security-alert-burndown.lock.yml
+++ b/.github/workflows/security-alert-burndown.lock.yml
@@ -966,726 +966,6 @@ jobs:
             - PRs: open only
           - Limit updates to 100 items per run to respect rate limits (prioritize highest severity/most recent first)
           
-          
-          
-          ---
-          # WORKFLOW EXECUTION (PHASE 0)
-          ---
-          # Workflow Execution
-          
-          This campaign references the following campaign workers. These workers follow the first-class worker pattern: they are dispatch-only workflows with standardized input contracts.
-          
-          **IMPORTANT: Workers are orchestrated, not autonomous. They accept `campaign_id` and `payload` inputs via workflow_dispatch.**
-          
-          ---
-          
-          ## Campaign Workers
-          
-          
-          
-          **Worker Pattern**: All workers MUST:
-          - Use `workflow_dispatch` as the ONLY trigger (no schedule/push/pull_request)
-          - Accept `campaign_id` (string) and `payload` (string; JSON) inputs
-          - Implement idempotency via deterministic work item keys
-          - Label all created items with `z_campaign_security-alert-burndown`
-          
-          ---
-          
-          ## Workflow Creation Guardrails
-          
-          ### Before Creating Any Worker Workflow, Ask:
-          
-          1. **Does this workflow already exist?** - Check `.github/workflows/` thoroughly
-          2. **Can an existing workflow be adapted?** - Even if not perfect, existing is safer
-          3. **Is the requirement clear?** - Can you articulate exactly what it should do?
-          4. **Is it testable?** - Can you verify it works with test inputs?
-          5. **Is it reusable?** - Could other campaigns benefit from this worker?
-          
-          ### Only Create New Workers When:
-          
-          ✅ **All these conditions are met:**
-          - No existing workflow does the required task
-          - The campaign objective explicitly requires this capability
-          - You have a clear, specific design for the worker
-          - The worker has a focused, single-purpose scope
-          - You can test it independently before campaign use
-          
-          ❌ **Never create workers when:**
-          - You're unsure about requirements
-          - An existing workflow "mostly" works
-          - The worker would be complex or multi-purpose
-          - You haven't verified it doesn't already exist
-          - You can't clearly explain what it does in one sentence
-          
-          ---
-          
-          ## Worker Creation Template
-          
-          If you must create a new worker (only after checking ALL guardrails above), use this template:
-          
-          **Create the workflow file at `.github/workflows/<workflow-id>.md`:**
-          
-          ```yaml
-          ---
-          name: <Worker Name>
-          description: <One sentence describing what it does>
-          
-          on:
-            workflow_dispatch:
-              inputs:
-                campaign_id:
-                  description: 'Campaign identifier'
-                  required: true
-                  type: string
-                payload:
-                  description: 'JSON payload with work item details'
-                  required: true
-                  type: string
-          
-          tracker-id: <workflow-id>
-          
-          tools:
-            github:
-              toolsets: [default]
-            # Add minimal additional tools as needed
-          
-          safe-outputs:
-            create-pull-request:
-              max: 1  # Start conservative
-            add-comment:
-              max: 2
-          ---
-          
-          # <Worker Name>
-          
-          You are a campaign worker that processes work items.
-          
-          ## Input Contract
-          
-          Parse inputs:
-          ```javascript
-          const campaignId = context.payload.inputs.campaign_id;
-          const payload = JSON.parse(context.payload.inputs.payload);
-          ```
-          
-          Expected payload structure:
-          ```json
-          {
-            "repository": "owner/repo",
-            "work_item_id": "unique-id",
-            "target_ref": "main",
-            // Additional context...
-          }
-          ```
-          
-          ## Idempotency Requirements
-          
-          1. **Generate deterministic key**:
-             ```
-             const workKey = `campaign-${campaignId}-${payload.repository}-${payload.work_item_id}`;
-             ```
-          
-          2. **Check for existing work**:
-             - Search for PRs/issues with `workKey` in title
-            - Filter by label: `z_campaign_${campaignId}`
-             - If found: Skip or update
-             - If not: Create new
-          
-          3. **Label all created items**:
-            - Apply `z_campaign_${campaignId}` label
-             - This enables discovery by orchestrator
-          
-          ## Task
-          
-          <Specific task description>
-          
-          ## Output
-          
-          Report:
-          - Link to created/updated PR or issue
-          - Whether work was skipped (exists) or completed
-          - Any errors or blockers
-          ```
-          
-          **After creating:**
-          - Compile: `gh aw compile <workflow-id>.md`
-          - **CRITICAL: Test with sample inputs** (see testing requirements below)
-          
-          ---
-          
-          ## Worker Testing (MANDATORY)
-          
-          **Why test?** - Untested workers may fail during campaign execution. Test with sample inputs first to catch issues early.
-          
-          **Testing steps:**
-          
-          1. **Prepare test payload**:
-             ```json
-             {
-               "repository": "test-org/test-repo",
-               "work_item_id": "test-1",
-               "target_ref": "main"
-             }
-             ```
-          
-          2. **Trigger test run**:
-             ```bash
-             gh workflow run <workflow-id>.yml \
-               -f campaign_id=security-alert-burndown \
-               -f payload='{"repository":"test-org/test-repo","work_item_id":"test-1"}'
-             ```
-             
-             Or via GitHub MCP:
-             ```javascript
-             mcp__github__run_workflow(
-               workflow_id: "<workflow-id>", 
-               ref: "main",
-               inputs: {
-                 campaign_id: "security-alert-burndown",
-                 payload: JSON.stringify({repository: "test-org/test-repo", work_item_id: "test-1"})
-               }
-             )
-             ```
-          
-          3. **Wait for completion**: Poll until status is "completed"
-          
-          4. **Verify success**:
-             - Check that workflow succeeded
-             - Verify idempotency: Run again with same inputs, should skip/update
-             - Review created items have correct labels
-             - Confirm deterministic keys are used
-          
-          5. **Test failure actions**:
-             - DO NOT use the worker if testing fails
-             - Analyze failure logs
-             - Make corrections
-             - Recompile and retest
-             - If unfixable after 2 attempts, report in status and skip
-          
-          **Note**: Workflows that accept `workflow_dispatch` inputs can receive parameters from the orchestrator. This enables the orchestrator to provide context, priorities, or targets based on its decisions. See [DispatchOps documentation](https://githubnext.github.io/gh-aw/guides/dispatchops/#with-input-parameters) for input parameter examples.
-          
-          ---
-          
-          ## Orchestration Guidelines
-          
-          **Execution pattern:**
-          - Workers are **orchestrated, not autonomous**
-          - Orchestrator discovers work items via discovery manifest
-          - Orchestrator decides which workers to run and with what inputs
-          - Workers receive `campaign_id` and `payload` via workflow_dispatch
-          - Sequential vs parallel execution is orchestrator's decision
-          
-          **Worker dispatch:**
-          - Parse discovery manifest (`./.gh-aw/campaign.discovery.json`)
-          - For each work item needing processing:
-            1. Determine appropriate worker for this item type
-            2. Construct payload with work item details
-            3. Dispatch worker via workflow_dispatch with campaign_id and payload
-            4. Track dispatch status
-          
-          **Input construction:**
-          ```javascript
-          // Example: Dispatching security-fix worker
-          const workItem = discoveryManifest.items[0];
-          const payload = {
-            repository: workItem.repo,
-            work_item_id: `alert-${workItem.number}`,
-            target_ref: "main",
-            alert_type: "sql-injection",
-            file_path: "src/db.go",
-            line_number: 42
-          };
-          
-          await github.actions.createWorkflowDispatch({
-            owner: context.repo.owner,
-            repo: context.repo.repo,
-            workflow_id: "security-fix-worker.yml",
-            ref: "main",
-            inputs: {
-              campaign_id: "security-alert-burndown",
-              payload: JSON.stringify(payload)
-            }
-          });
-          ```
-          
-          **Idempotency by design:**
-          - Workers implement their own idempotency checks
-          - Orchestrator doesn't need to track what's been processed
-          - Can safely re-dispatch work items across runs
-          - Workers will skip or update existing items
-          
-          **Failure handling:**
-          - If a worker dispatch fails, note it but continue
-          - Worker failures don't block entire campaign
-          - Report all failures in status update with context
-          - Humans can intervene if needed
-          
-          ---
-          
-          ## After Worker Orchestration
-          
-          Once workers have been dispatched (or new workers created and tested), proceed with normal orchestrator steps:
-          
-          1. **Discovery** - Read state from discovery manifest and project board
-          2. **Planning** - Determine what needs updating on project board
-          3. **Project Updates** - Write state changes to project board  
-          4. **Status Reporting** - Report progress, worker dispatches, failures, next steps
-          
-          ---
-          
-          ## Key Differences from Fusion Approach
-          
-          **Old fusion approach (REMOVED)**:
-          - Workers had mixed triggers (schedule + workflow_dispatch)
-          - Fusion dynamically added workflow_dispatch to existing workflows
-          - Workers stored in campaign-specific folders
-          - Ambiguous ownership and trigger precedence
-          
-          **New first-class worker approach**:
-          - Workers are dispatch-only (on: workflow_dispatch)
-          - Standardized input contract (campaign_id, payload)
-          - Explicit idempotency via deterministic keys
-          - Clear ownership: workers are orchestrated, not autonomous
-          - Workers stored with regular workflows (not campaign-specific folders)
-          - Orchestration policy kept explicit in orchestrator
-          
-          This eliminates duplicate execution problems and makes orchestration concerns explicit.
-          ---
-          # ORCHESTRATOR INSTRUCTIONS
-          ---
-          # Orchestrator Instructions
-          
-          This orchestrator coordinates a single campaign by discovering worker outputs and making deterministic decisions.
-          
-          **Scope:** orchestration + project sync + reporting (discovery, planning, pacing, writing, reporting).
-          **Actuation model:** **hybrid** — the orchestrator may update campaign state directly (Projects and status updates) and may also dispatch allowlisted worker workflows.
-          **Write authority:** the orchestrator may write GitHub state when explicitly allowlisted via safe outputs; delegate repo/code changes (e.g., PRs) to workers unless this campaign explicitly defines otherwise.
-          
-          ---
-          
-          ## Traffic and Rate Limits (Required)
-          
-          - Minimize API calls; avoid full rescans when possible.
-          - Prefer incremental discovery with deterministic ordering (e.g., by `updatedAt`, tie-break by ID).
-          - Enforce strict pagination budgets; if a query requires many pages, stop early and continue next run.
-          - Use a durable cursor/checkpoint so the next run continues without rescanning.
-          - On throttling (HTTP 429 / rate-limit 403), do not retry aggressively; back off and end the run after reporting what remains.
-          
-          
-          **Cursor file (repo-memory)**: `memory/campaigns/security-alert-burndown/cursor.json`
-          PROMPT_EOF
-          cat << 'PROMPT_EOF' >> "$GH_AW_PROMPT"
-          **File system path**: `/tmp/gh-aw/repo-memory/campaigns/security-alert-burndown/cursor.json`
-          - If it exists: read first and continue from its boundary.
-          - If it does not exist: create it by end of run.
-          - Always write the updated cursor back to the same path.
-          
-          
-          
-          **Metrics snapshots (repo-memory)**: `memory/campaigns/security-alert-burndown/metrics/*.json`
-          **File system path**: `/tmp/gh-aw/repo-memory/campaigns/security-alert-burndown/metrics/*.json`
-          - Persist one append-only JSON metrics snapshot per run (new file per run; do not rewrite history).
-          - Use UTC date (`YYYY-MM-DD`) in the filename (example: `metrics/2025-12-22.json`).
-          
-          
-          
-          
-          
-          ---
-          
-          ## Core Principles
-          
-          1. Workers are immutable and campaign-agnostic
-          2. The GitHub Project board is the authoritative campaign state
-          3. Correlation is explicit (tracker-id AND labels)
-          4. Reads and writes are separate steps (never interleave)
-          5. Idempotent operation is mandatory (safe to re-run)
-          6. Orchestrator writes must be deterministic and minimal
-          
-          ---
-          
-          ## Execution Steps (Required Order)
-          
-          ### Step 1 — Read State (Discovery) [NO WRITES]
-          
-          **IMPORTANT**: Discovery has been precomputed. Read the discovery manifest instead of performing GitHub-wide searches.
-          
-          1) Read the precomputed discovery manifest: `./.gh-aw/campaign.discovery.json`
-          
-          2) Parse discovered items from the manifest:
-             - Each item has: url, content_type (issue/pull_request/discussion), number, repo, created_at, updated_at, state
-             - Closed items have: closed_at (for issues) or merged_at (for PRs)
-             - Items are pre-sorted by updated_at for deterministic processing
-          
-          3) Check the manifest summary for work counts.
-          
-          4) Discovery cursor is maintained automatically in repo-memory; do not modify it manually.
-          
-          ### Step 2 — Make Decisions (Planning) [NO WRITES]
-          
-          5) Determine desired `status` strictly from explicit GitHub state:
-          - Open → `Todo` (or `In Progress` only if explicitly indicated elsewhere)
-          - Closed (issue/discussion) → `Done`
-          - Merged (PR) → `Done`
-          
-          6) Calculate required date fields (for workers that sync Projects):
-          - `start_date`: format `created_at` as `YYYY-MM-DD`
-          - `end_date`:
-            - if closed/merged → format `closed_at`/`merged_at` as `YYYY-MM-DD`
-            - if open → **today's date** formatted `YYYY-MM-DD`
-          
-          7) Reads and writes are separate steps (never interleave).
-          
-          ### Step 3 — Apply Updates (Execution) [WRITES]
-          
-          8) Apply required GitHub state updates in a single write phase.
-          
-          Allowed writes (when allowlisted via safe outputs):
-          - Update the campaign Project board (add/update items and fields)
-          - Post status updates (e.g., update an issue or add a comment)
-          - Create Copilot agent sessions for repo-side work (use when you need code changes)
-          
-          Constraints:
-          - Use only allowlisted safe outputs.
-          - Keep within configured max counts and API budgets.
-          - Do not interleave reads and writes.
-          
-          ### Step 4 — Dispatch Workers (Optional) [DISPATCH]
-          
-          9) For repo-side actions (e.g., code changes), dispatch allowlisted worker workflows using `dispatch-workflow`.
-          
-          Constraints:
-          - Only dispatch allowlisted workflows.
-          - Keep within the dispatch-workflow max for this run.
-          
-          ### Step 5 — Report
-          
-          10) Summarize what you updated and/or dispatched, what remains, and what should run next.
-          
-              **Discovered:** 25 items (15 issues, 10 PRs)
-              **Processed:** 10 items added to project, 5 updated
-              **Completion:** 60% (30/50 total tasks)
-          
-              ## Most Important Findings
-          
-              1. **Critical accessibility gaps identified**: 3 high-severity accessibility issues discovered in mobile navigation, requiring immediate attention
-              2. **Documentation coverage acceleration**: Achieved 5% improvement in one week (best velocity so far)
-              3. **Worker efficiency improving**: daily-doc-updater now processing 40% more items per run
-          
-              ## What Was Learned
-          
-              - Multi-device testing reveals issues that desktop-only testing misses - should be prioritized
-              - Documentation updates tied to code changes have higher accuracy and completeness
-              - Users report fewer issues when examples include error handling patterns
-          
-              ## Campaign Progress
-          
-              **Documentation Coverage** (Primary Metric):
-              - Baseline: 85% → Current: 88% → Target: 95%
-              - Direction: ↑ Increasing (+3% this week, +1% velocity/week)
-              - Status: ON TRACK - At current velocity, will reach 95% in 7 weeks
-          
-              **Accessibility Score** (Supporting Metric):
-              - Baseline: 90% → Current: 91% → Target: 98%
-              - Direction: ↑ Increasing (+1% this month)
-              - Status: AT RISK - Slower progress than expected, may need dedicated focus
-          
-              **User-Reported Issues** (Supporting Metric):
-              - Baseline: 15/month → Current: 12/month → Target: 5/month
-              - Direction: ↓ Decreasing (-3 this month, -20% velocity)
-              - Status: ON TRACK - Trending toward target
-          
-              ## Next Steps
-          
-              1. Address 3 critical accessibility issues identified this run (high priority)
-              2. Continue processing remaining 15 discovered items
-              3. Focus on accessibility improvements to accelerate supporting KPI
-              4. Maintain current documentation coverage velocity
-          ```
-          
-          12) Report:
-          - counts discovered (by type)
-          - counts processed this run (by action: add/status_update/backfill/noop/failed)
-          - counts deferred due to budgets
-          - failures (with reasons)
-          - completion state (work items only)
-          - cursor advanced / remaining backlog estimate
-          
-          ---
-          
-          ## Authority
-          
-          If any instruction in this file conflicts with **Project Update Instructions**, the Project Update Instructions win for all project writes.
-          ---
-          # PROJECT UPDATE INSTRUCTIONS (AUTHORITATIVE FOR WRITES)
-          ---
-          # Project Update Instructions (Authoritative Write Contract)
-          
-          ## Project Board Integration
-          
-          This file defines the ONLY allowed rules for writing to the GitHub Project board.
-          If any other instructions conflict with this file, THIS FILE TAKES PRECEDENCE for all project writes.
-          
-          ---
-          
-          ## 0) Hard Requirements (Do Not Deviate)
-          
-          - Any workflow performing project writes (orchestrators or workers) MUST use only the `update-project` safe-output.
-          - All writes MUST target exactly:
-            - **Project URL**: `https://github.com/orgs/githubnext/projects/144`
-          - Every item MUST include:
-            - `campaign_id: "security-alert-burndown"`
-          
-          ## Campaign ID
-          
-          All campaign tracking MUST key off `campaign_id: "security-alert-burndown"`.
-          
-          ---
-          
-          ## 1) Required Project Fields (Must Already Exist)
-          
-          | Field | Type | Allowed / Notes |
-          |---|---|---|
-          | `status` | single-select | `Todo` / `In Progress` / `Review required` / `Blocked` / `Done` |
-          | `campaign_id` | text | Must equal `security-alert-burndown` |
-          | `worker_workflow` | text | workflow ID or `"unknown"` |
-          | `target_repo` | text | `owner/repo` |
-          | `priority` | single-select | `High` / `Medium` / `Low` |
-          | `size` | single-select | `Small` / `Medium` / `Large` |
-          | `start_date` | date | `YYYY-MM-DD` |
-          | `end_date` | date | `YYYY-MM-DD` |
-          
-          Field names are case-sensitive.
-          
-          ---
-          
-          ## 2) Content Identification (Mandatory)
-          
-          Use **content number** (integer), never the URL as an identifier.
-          
-          - Issue URL: `.../issues/123` → `content_type: "issue"`, `content_number: 123`
-          - PR URL: `.../pull/456` → `content_type: "pull_request"`, `content_number: 456`
-          
-          ---
-          
-          ## 3) Deterministic Field Rules (No Inference)
-          
-          These rules apply to any time you write fields:
-          
-          - `campaign_id`: always `security-alert-burndown`
-          - `worker_workflow`: workflow ID if known, else `"unknown"`
-          - `target_repo`: extract `owner/repo` from the issue/PR URL
-          - `priority`: default `Medium` unless explicitly known
-          - `size`: default `Medium` unless explicitly known
-          - `start_date`: issue/PR `created_at` formatted `YYYY-MM-DD`
-          - `end_date`:
-            - if closed/merged → `closed_at` / `merged_at` formatted `YYYY-MM-DD`
-            - if open → **today’s date** formatted `YYYY-MM-DD` (**required for roadmap view; do not leave blank**)
-          
-          For open items, `end_date` is a UI-required placeholder and does NOT represent actual completion.
-          
-          ---
-          
-          ## 4) Read-Write Separation (Prevents Read/Write Mixing)
-          
-          1. **READ STEP (no writes)** — validate existence and gather metadata
-          2. **WRITE STEP (writes only)** — execute `update-project`
-          
-          Never interleave reads and writes.
-          
-          ---
-          
-          ## 5) Adding an Issue or PR (First Write)
-          
-          ### Adding New Issues
-          
-          When first adding an item to the project, you MUST write ALL required fields.
-          
-          ```yaml
-          update-project:
-            project: "https://github.com/orgs/githubnext/projects/144"
-            campaign_id: "security-alert-burndown"
-            content_type: "issue"              # or "pull_request"
-            content_number: 123
-            fields:
-              status: "Todo"                   # "Done" if already closed/merged
-              campaign_id: "security-alert-burndown"
-              worker_workflow: "unknown"
-              target_repo: "owner/repo"
-              priority: "Medium"
-              size: "Medium"
-              start_date: "2025-12-15"
-              end_date: "2026-01-03"
-          ```
-          
-          ---
-          
-          ## 6) Updating an Existing Item (Minimal Writes)
-          
-          ### Updating Existing Items
-          
-          Preferred behavior is minimal, idempotent writes:
-          
-          - If item exists and `status` is unchanged → **No-op**
-          - If item exists and `status` differs → **Update `status` only**
-          - If any required field is missing/empty/invalid → **One-time full backfill** (repair only)
-          
-          ### Status-only Update (Default)
-          
-          ```yaml
-          update-project:
-            project: "https://github.com/orgs/githubnext/projects/144"
-            campaign_id: "security-alert-burndown"
-            content_type: "issue"              # or "pull_request"
-            content_number: 123
-            fields:
-              status: "Done"
-          ```
-          
-          ### Full Backfill (Repair Only)
-          
-          ```yaml
-          update-project:
-            project: "https://github.com/orgs/githubnext/projects/144"
-            campaign_id: "security-alert-burndown"
-            content_type: "issue"              # or "pull_request"
-            content_number: 123
-            fields:
-              status: "Done"
-              campaign_id: "security-alert-burndown"
-              worker_workflow: "WORKFLOW_ID"
-              target_repo: "owner/repo"
-              priority: "Medium"
-              size: "Medium"
-              start_date: "2025-12-15"
-              end_date: "2026-01-02"
-          ```
-          
-          ---
-          
-          ## 7) Idempotency Rules
-          
-          - Matching status already set → **No-op**
-          - Different status → **Status-only update**
-          - Invalid/deleted/inaccessible URL → **Record failure and continue**
-          
-          ## Write Operation Rules
-          
-          All writes MUST conform to this file and use `update-project` only.
-          
-          ---
-          
-          ## 8) Logging + Failure Handling (Mandatory)
-          
-          For every attempted item, record:
-          
-          - `content_type`, `content_number`, `target_repo`
-          - action taken: `noop | add | status_update | backfill | failed`
-          - error details if failed
-          
-          Failures must not stop processing remaining items.
-          
-          ---
-          
-          ## 9) Worker Workflow Policy
-          
-          - Workers are campaign-agnostic.
-          - Orchestrator populates `worker_workflow`.
-          - If `worker_workflow` cannot be determined, it MUST remain `"unknown"` unless explicitly reclassified by the orchestrator.
-          
-          ---
-          
-          ## 10) Parent / Sub-Issue Rules (Campaign Hierarchy)
-          
-          - Each project board MUST have exactly **one Epic issue** representing the campaign.
-          - The Epic issue MUST:
-            - Be added to the project board
-            - Use the same `campaign_id`
-            - Use `worker_workflow: "unknown"`
-          
-          - All campaign work issues (non-epic) MUST be created as **sub-issues of the Epic**.
-          - Issues MUST NOT be re-parented based on worker assignment.
-          
-          - Pull requests cannot be sub-issues:
-            - PRs MUST reference their related issue via standard GitHub linking (e.g. “Closes #123”).
-          
-          - Worker grouping MUST be done via the `worker_workflow` project field, not via parent issues.
-          
-          - The Epic issue is narrative only.
-          - The project board is the sole authoritative source of campaign state.
-          
-          ---
-          
-          ## Appendix — Machine Check Checklist (Optional)
-          
-          This checklist is designed to validate outputs before executing project writes.
-          
-          ### A) Output Structure Checks
-          
-          - [ ] All writes use `update-project:` blocks (no other write mechanism).
-          - [ ] Each `update-project` block includes:
-            - [ ] `project: "https://github.com/orgs/githubnext/projects/144"`
-            - [ ] `campaign_id: "security-alert-burndown"` (top-level)
-            - [ ] `content_type` ∈ {`issue`, `pull_request`}
-            - [ ] `content_number` is an integer
-            - [ ] `fields` object is present
-          
-          ### B) Field Validity Checks
-          
-          - [ ] `fields.status` ∈ {`Todo`, `In Progress`, `Review required`, `Blocked`, `Done`}
-          - [ ] `fields.campaign_id` is present on first-add/backfill and equals `security-alert-burndown`
-          - [ ] `fields.worker_workflow` is present on first-add/backfill and is either a known workflow ID or `"unknown"`
-          - [ ] `fields.target_repo` matches `owner/repo`
-          - [ ] `fields.priority` ∈ {`High`, `Medium`, `Low`}
-          - [ ] `fields.size` ∈ {`Small`, `Medium`, `Large`}
-          - [ ] `fields.start_date` matches `YYYY-MM-DD`
-          - [ ] `fields.end_date` matches `YYYY-MM-DD`
-          
-          ### C) Update Semantics Checks
-          
-          - [ ] For existing items, payload is **status-only** unless explicitly doing a backfill repair.
-          - [ ] Backfill is used only when required fields are missing/empty/invalid.
-          - [ ] No payload overwrites `priority`/`size`/`worker_workflow` with defaults during a normal status update.
-          
-          ### D) Read-Write Separation Checks
-          
-          - [ ] All reads occur before any writes (no read/write interleaving).
-          - [ ] Writes are batched separately from discovery.
-          
-          ### E) Epic/Hierarchy Checks (Policy-Level)
-          
-          - [ ] Exactly one Epic exists for the campaign board.
-          - [ ] Epic is on the board and uses `worker_workflow: "unknown"`.
-          - [ ] All campaign work issues are sub-issues of the Epic (if supported by environment/tooling).
-          - [ ] PRs are linked to issues via GitHub linking (e.g. “Closes #123”).
-          
-          ### F) Failure Handling Checks
-          
-          - [ ] Invalid/deleted/inaccessible items are logged as failures and processing continues.
-          - [ ] Idempotency is delegated to the `update-project` tool; no pre-filtering by board presence.
-          ---
-          # CLOSING INSTRUCTIONS (HIGHEST PRIORITY)
-          ---
-          # Closing Instructions (Highest Priority)
-          
-          Execute all four steps in strict order:
-          
-          1. Read State (no writes)
-          2. Make Decisions (no writes)
-          3. Apply Updates (writes)
-          4. Report
-          
-          The following rules are mandatory and override inferred behavior:
-          
-          - The GitHub Project board is the single source of truth.
-          - All project writes MUST comply with the Project Update Instructions.
-          - State reads and state writes MUST NOT be interleaved.
-          - Do NOT infer missing data or invent values.
-          - Do NOT reorganize hierarchy.
-          - Do NOT overwrite fields except as explicitly allowed.
-          - Workers are immutable and campaign-agnostic.
-          
-          If any instruction conflicts, the Project Update Instructions take precedence for all writes.
           PROMPT_EOF
       - name: Substitute placeholders
         uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
diff --git a/cmd/gh-aw/argument_syntax_test.go b/cmd/gh-aw/argument_syntax_test.go
index d6fb147e043..1c6f28fe825 100644
--- a/cmd/gh-aw/argument_syntax_test.go
+++ b/cmd/gh-aw/argument_syntax_test.go
@@ -7,7 +7,6 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/githubnext/gh-aw/pkg/campaign"
 	"github.com/githubnext/gh-aw/pkg/cli"
 	"github.com/spf13/cobra"
 	"github.com/stretchr/testify/assert"
@@ -45,13 +44,6 @@ func TestArgumentSyntaxConsistency(t *testing.T) {
 			argsValidator:  "MinimumNArgs(1)",
 			shouldValidate: func(cmd *cobra.Command) error { return cmd.Args(cmd, []string{"test"}) },
 		},
-		{
-			name:           "campaign new requires campaign-id",
-			command:        findSubcommand(campaign.NewCommand(), "new"),
-			expectedUse:    "new <campaign-id>",
-			argsValidator:  "MaximumNArgs(1) with custom validation",
-			shouldValidate: nil, // Skip validation as it has custom error handling
-		},
 
 		// Commands with optional arguments (using square brackets [])
 		{
@@ -124,13 +116,6 @@ func TestArgumentSyntaxConsistency(t *testing.T) {
 			argsValidator:  "no validator (all optional)",
 			shouldValidate: func(cmd *cobra.Command) error { return nil },
 		},
-		{
-			name:           "campaign command has optional filter",
-			command:        campaign.NewCommand(),
-			expectedUse:    "campaign [filter]",
-			argsValidator:  "MaximumNArgs(1)",
-			shouldValidate: func(cmd *cobra.Command) error { return cmd.Args(cmd, []string{}) },
-		},
 	}
 
 	for _, tt := range tests {
@@ -249,47 +234,6 @@ func TestPRSubcommandArgumentSyntax(t *testing.T) {
 	}
 }
 
-// TestCampaignSubcommandArgumentSyntax verifies campaign subcommands have consistent syntax
-func TestCampaignSubcommandArgumentSyntax(t *testing.T) {
-	campaignCmd := campaign.NewCommand()
-
-	tests := []struct {
-		name        string
-		subcommand  string
-		expectedUse string
-	}{
-		{
-			name:        "campaign status has optional filter",
-			subcommand:  "status",
-			expectedUse: "status [filter]",
-		},
-		{
-			name:        "campaign new requires campaign-id",
-			subcommand:  "new",
-			expectedUse: "new <campaign-id>",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			// Find the subcommand - setup step
-			foundCmd := findSubcommand(campaignCmd, tt.subcommand)
-			require.NotNil(t, foundCmd,
-				"Test requires campaign subcommand %q to exist in command list",
-				tt.subcommand)
-
-			use := foundCmd.Use
-			assert.Equal(t, tt.expectedUse, use,
-				"Campaign subcommand %q should have expected Use syntax", tt.subcommand)
-
-			// Validate the Use pattern format
-			assert.True(t, isValidUseSyntax(use),
-				"Campaign subcommand %q Use=%q should follow valid syntax pattern",
-				tt.subcommand, use)
-		})
-	}
-}
-
 // findSubcommand finds a subcommand by name in a command
 func findSubcommand(cmd *cobra.Command, name string) *cobra.Command {
 	for _, subcmd := range cmd.Commands() {
@@ -370,7 +314,6 @@ func TestArgumentNamingConventions(t *testing.T) {
 		cli.NewStatusCommand(),
 		cli.NewMCPCommand(),
 		cli.NewPRCommand(),
-		campaign.NewCommand(),
 	}
 
 	// Also collect subcommands
@@ -384,7 +327,6 @@ func TestArgumentNamingConventions(t *testing.T) {
 		"pattern":       "Filter/search commands should use 'pattern' or 'filter'",
 		"run-id":        "Audit command should use 'run-id' for clarity",
 		"workflow-spec": "Trial command should use 'workflow-spec' to indicate special format",
-		"campaign-id":   "Campaign new should use 'campaign-id' for clarity",
 		"pr-url":        "PR transfer should use 'pr-url' for clarity",
 		"server":        "MCP commands should use 'server' for MCP server names",
 	}
diff --git a/cmd/gh-aw/capitalization_test.go b/cmd/gh-aw/capitalization_test.go
index bcc1fffd7ea..14061ad430a 100644
--- a/cmd/gh-aw/capitalization_test.go
+++ b/cmd/gh-aw/capitalization_test.go
@@ -6,7 +6,6 @@ import (
 	"strings"
 	"testing"
 
-	"github.com/githubnext/gh-aw/pkg/campaign"
 	"github.com/githubnext/gh-aw/pkg/cli"
 	"github.com/spf13/cobra"
 )
@@ -78,7 +77,6 @@ func TestTechnicalTermsCapitalization(t *testing.T) {
 	commandsToCheck := []*cobra.Command{
 		compileCmd,
 		newCmd,
-		campaign.NewCommand(),
 	}
 
 	// Check all commands and their subcommands
diff --git a/go.mod b/go.mod
index e73d99feac9..8189ed8d50a 100644
--- a/go.mod
+++ b/go.mod
@@ -21,7 +21,6 @@ require (
 	github.com/sourcegraph/conc v0.3.0
 	github.com/spf13/cobra v1.10.2
 	github.com/stretchr/testify v1.11.1
-	go.yaml.in/yaml/v3 v3.0.4
 	golang.org/x/crypto v0.47.0
 	golang.org/x/mod v0.32.0
 	golang.org/x/term v0.39.0
@@ -265,6 +264,7 @@ require (
 	go.uber.org/automaxprocs v1.6.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.27.0 // indirect
+	go.yaml.in/yaml/v3 v3.0.4 // indirect
 	go.yaml.in/yaml/v4 v4.0.0-rc.3 // indirect
 	golang.org/x/exp/typeparams v0.0.0-20251023183803-a4bb9ffd2546 // indirect
 	golang.org/x/net v0.48.0 // indirect

From 8d6747d8f8f15879bb46ba663cb931738884f114 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 00:06:44 +0000
Subject: [PATCH 5/7] Remove special case handling for .campaign.md and
 .campaign.g.md files

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 pkg/cli/git.go                          | 10 ---------
 pkg/cli/gitattributes_test.go           |  5 -----
 pkg/cli/init.go                         |  5 -----
 pkg/cli/run_workflow_validation.go      |  8 -------
 pkg/cli/run_workflow_validation_test.go | 10 ---------
 pkg/stringutil/identifiers.go           | 22 +++---------------
 pkg/stringutil/identifiers_test.go      | 30 -------------------------
 7 files changed, 3 insertions(+), 87 deletions(-)

diff --git a/pkg/cli/git.go b/pkg/cli/git.go
index ae2375f2894..54586dbfd30 100644
--- a/pkg/cli/git.go
+++ b/pkg/cli/git.go
@@ -203,16 +203,6 @@ func ensureGitAttributes() error {
 		}
 	}
 
-	// Remove old campaign.g.md entries if they exist (they're now in .gitignore)
-	for i := len(lines) - 1; i >= 0; i-- {
-		trimmedLine := strings.TrimSpace(lines[i])
-		if strings.HasPrefix(trimmedLine, ".github/workflows/*.campaign.g.md") {
-			gitLog.Print("Removing obsolete .campaign.g.md .gitattributes entry")
-			lines = append(lines[:i], lines[i+1:]...)
-			modified = true
-		}
-	}
-
 	if !modified {
 		gitLog.Print(".gitattributes already contains required entries")
 		return nil
diff --git a/pkg/cli/gitattributes_test.go b/pkg/cli/gitattributes_test.go
index fd70814529c..7c764dee429 100644
--- a/pkg/cli/gitattributes_test.go
+++ b/pkg/cli/gitattributes_test.go
@@ -68,11 +68,6 @@ func TestEnsureGitAttributes(t *testing.T) {
 			existingContent: "*.md linguist-documentation=true\n.github/workflows/*.lock.yml linguist-generated=true\n*.txt text=auto\n",
 			expectedContent: "*.md linguist-documentation=true\n.github/workflows/*.lock.yml linguist-generated=true merge=ours\n*.txt text=auto",
 		},
-		{
-			name:            "removes obsolete campaign.g.md entry",
-			existingContent: ".github/workflows/*.lock.yml linguist-generated=true merge=ours\n.github/workflows/*.campaign.g.md linguist-generated=true merge=ours\n",
-			expectedContent: ".github/workflows/*.lock.yml linguist-generated=true merge=ours",
-		},
 	}
 
 	for _, tt := range tests {
diff --git a/pkg/cli/init.go b/pkg/cli/init.go
index 46e2d0a2381..eb459c077ed 100644
--- a/pkg/cli/init.go
+++ b/pkg/cli/init.go
@@ -806,11 +806,6 @@ func ensureMaintenanceWorkflow(verbose bool) error {
 	// Parse all workflows to collect WorkflowData
 	var workflowDataList []*workflow.WorkflowData
 	for _, file := range files {
-		// Skip campaign specs and generated files
-		if strings.HasSuffix(file, ".campaign.md") || strings.HasSuffix(file, ".campaign.g.md") {
-			continue
-		}
-
 		initLog.Printf("Parsing workflow: %s", file)
 		workflowData, err := compiler.ParseWorkflowFile(file)
 		if err != nil {
diff --git a/pkg/cli/run_workflow_validation.go b/pkg/cli/run_workflow_validation.go
index 00169f31e8f..7a523cc17ab 100644
--- a/pkg/cli/run_workflow_validation.go
+++ b/pkg/cli/run_workflow_validation.go
@@ -22,14 +22,6 @@ var validationLog = logger.New("cli:run_workflow_validation")
 // getLockFilePath converts a markdown workflow path to its compiled lock file path
 // Example: "/path/to/workflow.md" -> "/path/to/workflow.lock.yml"
 func getLockFilePath(markdownPath string) string {
-	// Handle campaign orchestrator files
-	if strings.HasSuffix(markdownPath, ".campaign.g.md") {
-		return strings.TrimSuffix(markdownPath, ".campaign.g.md") + ".campaign.lock.yml"
-	}
-	// Handle regular campaign files
-	if strings.HasSuffix(markdownPath, ".campaign.md") {
-		return strings.TrimSuffix(markdownPath, ".campaign.md") + ".campaign.lock.yml"
-	}
 	// Handle regular workflow files
 	return strings.TrimSuffix(markdownPath, ".md") + ".lock.yml"
 }
diff --git a/pkg/cli/run_workflow_validation_test.go b/pkg/cli/run_workflow_validation_test.go
index 1231f47e1a2..8a34825fbd4 100644
--- a/pkg/cli/run_workflow_validation_test.go
+++ b/pkg/cli/run_workflow_validation_test.go
@@ -23,16 +23,6 @@ func TestGetLockFilePath(t *testing.T) {
 			markdownPath: "/path/to/workflow.md",
 			expected:     "/path/to/workflow.lock.yml",
 		},
-		{
-			name:         "campaign workflow",
-			markdownPath: "/path/to/campaign.campaign.md",
-			expected:     "/path/to/campaign.campaign.lock.yml",
-		},
-		{
-			name:         "campaign orchestrator",
-			markdownPath: "/path/to/campaign.campaign.g.md",
-			expected:     "/path/to/campaign.campaign.lock.yml",
-		},
 		{
 			name:         "workflow in nested directory",
 			markdownPath: "/path/to/workflows/nested/workflow.md",
diff --git a/pkg/stringutil/identifiers.go b/pkg/stringutil/identifiers.go
index 306f015b48b..92099832125 100644
--- a/pkg/stringutil/identifiers.go
+++ b/pkg/stringutil/identifiers.go
@@ -103,43 +103,27 @@ func LockFileToMarkdown(lockPath string) string {
 }
 
 // IsAgenticWorkflow returns true if the file path is an agentic workflow file.
-// Agentic workflows end with .md but exclude campaign spec files (.campaign.md)
-// and campaign orchestrator files (.campaign.g.md).
+// Agentic workflows end with .md.
 //
 // Examples:
 //
 //	IsAgenticWorkflow("test.md")                                // returns true
 //	IsAgenticWorkflow("weekly-research.md")                     // returns true
 //	IsAgenticWorkflow(".github/workflows/workflow.md")          // returns true
-//	IsAgenticWorkflow("test.campaign.md")                       // returns false (campaign spec)
-//	IsAgenticWorkflow("test.campaign.g.md")                     // returns false (campaign orchestrator)
 //	IsAgenticWorkflow("test.lock.yml")                          // returns false
 func IsAgenticWorkflow(path string) bool {
 	// Must end with .md
-	if !strings.HasSuffix(path, ".md") {
-		return false
-	}
-	// Exclude campaign spec files (.campaign.md)
-	if strings.HasSuffix(path, ".campaign.md") {
-		return false
-	}
-	// Exclude campaign orchestrator files (.campaign.g.md)
-	if strings.HasSuffix(path, ".campaign.g.md") {
-		return false
-	}
-	return true
+	return strings.HasSuffix(path, ".md")
 }
 
 // IsLockFile returns true if the file path is a compiled lock file.
-// Lock files end with .lock.yml and can be compiled from agentic workflows or campaign orchestrators.
+// Lock files end with .lock.yml and are compiled from agentic workflows.
 //
 // Examples:
 //
 //	IsLockFile("test.lock.yml")                                 // returns true
-//	IsLockFile("test.campaign.lock.yml")                        // returns true
 //	IsLockFile(".github/workflows/workflow.lock.yml")           // returns true
 //	IsLockFile("test.md")                                       // returns false
-//	IsLockFile("test.campaign.md")                              // returns false
 func IsLockFile(path string) bool {
 	return strings.HasSuffix(path, ".lock.yml")
 }
diff --git a/pkg/stringutil/identifiers_test.go b/pkg/stringutil/identifiers_test.go
index d8ab5ce7c1c..279e1046d68 100644
--- a/pkg/stringutil/identifiers_test.go
+++ b/pkg/stringutil/identifiers_test.go
@@ -305,26 +305,11 @@ func TestIsAgenticWorkflow(t *testing.T) {
 			path:     "my.workflow.test.md",
 			expected: true,
 		},
-		{
-			name:     "campaign spec",
-			path:     "test.campaign.md",
-			expected: false,
-		},
-		{
-			name:     "campaign orchestrator",
-			path:     "test.campaign.g.md",
-			expected: false,
-		},
 		{
 			name:     "lock file",
 			path:     "test.lock.yml",
 			expected: false,
 		},
-		{
-			name:     "campaign lock file",
-			path:     "test.campaign.lock.yml",
-			expected: false,
-		},
 		{
 			name:     "no extension",
 			path:     "test",
@@ -358,26 +343,11 @@ func TestIsLockFile(t *testing.T) {
 			path:     ".github/workflows/test.lock.yml",
 			expected: true,
 		},
-		{
-			name:     "campaign lock file",
-			path:     "test.campaign.lock.yml",
-			expected: true,
-		},
 		{
 			name:     "workflow file",
 			path:     "test.md",
 			expected: false,
 		},
-		{
-			name:     "campaign spec",
-			path:     "test.campaign.md",
-			expected: false,
-		},
-		{
-			name:     "campaign orchestrator",
-			path:     "test.campaign.g.md",
-			expected: false,
-		},
 		{
 			name:     "yaml file",
 			path:     "test.yml",

From caf8f65fafb1faeeae0db4ff33d96277097837c6 Mon Sep 17 00:00:00 2001
From: Copilot <198982749+Copilot@users.noreply.github.com>
Date: Thu, 29 Jan 2026 16:12:43 -0800
Subject: [PATCH 6/7] Remove obsolete campaign command test case (#12646)

* Initial plan

* Remove campaign command test case from integration tests

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Co-authored-by: Peli de Halleux <pelikhan@users.noreply.github.com>
---
 cmd/gh-aw/command_groups_test.go | 1 -
 1 file changed, 1 deletion(-)

diff --git a/cmd/gh-aw/command_groups_test.go b/cmd/gh-aw/command_groups_test.go
index 0dc09a8cf49..5d840fb7f33 100644
--- a/cmd/gh-aw/command_groups_test.go
+++ b/cmd/gh-aw/command_groups_test.go
@@ -39,7 +39,6 @@ func TestCommandGroupAssignments(t *testing.T) {
 		// Analysis Commands
 		{name: "logs command in analysis group", commandName: "logs", expectedGroup: "analysis", shouldHaveGroup: true},
 		{name: "audit command in analysis group", commandName: "audit", expectedGroup: "analysis", shouldHaveGroup: true},
-		{name: "campaign command in analysis group", commandName: "campaign", expectedGroup: "analysis", shouldHaveGroup: true},
 
 		// Utilities
 		{name: "mcp-server command in utilities group", commandName: "mcp-server", expectedGroup: "utilities", shouldHaveGroup: true},

From adf209748c92198081b47e90c9c2e9f0a82cba3e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 30 Jan 2026 00:24:01 +0000
Subject: [PATCH 7/7] Remove campaign template copy commands from Makefile

Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
 .github/aw/bootstrap-agentic-campaign.md      | 154 ----------
 .github/aw/close-agentic-campaign.md          |  20 --
 .../aw/execute-agentic-campaign-workflow.md   | 284 ------------------
 .github/aw/generate-agentic-campaign.md       | 210 -------------
 .github/aw/orchestrate-agentic-campaign.md    | 165 ----------
 .github/aw/update-agentic-campaign-project.md | 247 ---------------
 Makefile                                      |   7 -
 pkg/cli/templates/close-agentic-campaign.md   |  20 --
 .../execute-agentic-campaign-workflow.md      | 284 ------------------
 .../templates/generate-agentic-campaign.md    | 210 -------------
 .../templates/orchestrate-agentic-campaign.md | 165 ----------
 .../update-agentic-campaign-project.md        | 247 ---------------
 12 files changed, 2013 deletions(-)
 delete mode 100644 .github/aw/bootstrap-agentic-campaign.md
 delete mode 100644 .github/aw/close-agentic-campaign.md
 delete mode 100644 .github/aw/execute-agentic-campaign-workflow.md
 delete mode 100644 .github/aw/generate-agentic-campaign.md
 delete mode 100644 .github/aw/orchestrate-agentic-campaign.md
 delete mode 100644 .github/aw/update-agentic-campaign-project.md
 delete mode 100644 pkg/cli/templates/close-agentic-campaign.md
 delete mode 100644 pkg/cli/templates/execute-agentic-campaign-workflow.md
 delete mode 100644 pkg/cli/templates/generate-agentic-campaign.md
 delete mode 100644 pkg/cli/templates/orchestrate-agentic-campaign.md
 delete mode 100644 pkg/cli/templates/update-agentic-campaign-project.md

diff --git a/.github/aw/bootstrap-agentic-campaign.md b/.github/aw/bootstrap-agentic-campaign.md
deleted file mode 100644
index 4ebec8d8e19..00000000000
--- a/.github/aw/bootstrap-agentic-campaign.md
+++ /dev/null
@@ -1,154 +0,0 @@
-# Bootstrap Instructions (Phase 0)
-
-This phase runs when discovery returns zero work items, providing initial work for the campaign to begin.
-
-{{ if .BootstrapMode }}
-## Bootstrap Strategy: {{ .BootstrapMode }}
-
-{{ if eq .BootstrapMode "seeder-worker" }}
-### Seeder Worker Dispatch
-
-When no work items are discovered, dispatch a seeder/scanner worker to discover initial work:
-
-**Worker ID**: `{{ .SeederWorkerID }}`
-
-**Payload**:
-```json
-{{ .SeederPayload }}
-```
-
-{{ if gt .SeederMaxItems 0 }}
-**Max Items**: {{ .SeederMaxItems }} (limit how many items the seeder returns)
-{{ end }}
-
-**Implementation Steps**:
-
-1. Check if discovery returned zero items (no worker outputs found)
-2. If zero items:
-   - Use the `dispatch_workflow` safe output to trigger the seeder worker
-   - Pass the campaign_id and configured payload
-   - Wait for the seeder worker to complete and create initial work items
-3. On next orchestrator run, the discovery step will find the seeder's outputs
-
-**Seeder Worker Contract**:
-- MUST accept `campaign_id` and `payload` inputs (standard worker contract)
-- MUST create discoverable outputs (issues, PRs, or discussions)
-- MUST apply the tracker label: `z_campaign_{{ .CampaignID }}`
-- SHOULD limit output count to configured max-items if provided
-- SHOULD use deterministic work item keys for idempotency
-
-{{ else if eq .BootstrapMode "project-todos" }}
-### Project Board Todo Items
-
-When no work items are discovered, read from the Project board's "{{ .TodoValue }}" column:
-
-{{ if .StatusField }}
-**Status Field**: `{{ .StatusField }}`
-{{ else }}
-**Status Field**: `Status` (default)
-{{ end }}
-
-{{ if .TodoValue }}
-**Todo Value**: `{{ .TodoValue }}`
-{{ else }}
-**Todo Value**: `Todo` (default)
-{{ end }}
-
-{{ if gt .TodoMaxItems 0 }}
-**Max Items**: {{ .TodoMaxItems }} (limit how many Todo items to process)
-{{ end }}
-
-{{ if .RequireFields }}
-**Required Fields**: {{ range $index, $field := .RequireFields }}{{ if $index }}, {{ end }}`{{ $field }}`{{ end }}
-  - Skip Todo items where any of these fields are empty
-{{ end }}
-
-**Implementation Steps**:
-
-1. Check if discovery returned zero items (no worker outputs found)
-2. If zero items:
-   - Query the Project board at `{{ .ProjectURL }}`
-   - Filter items where Status = "{{ .TodoValue }}"
-   {{ if .RequireFields }}- Skip items missing required fields{{ end }}
-   {{ if gt .TodoMaxItems 0 }}- Limit to {{ .TodoMaxItems }} items{{ end }}
-   - Select workers based on item metadata (see Worker Selection below)
-   - Dispatch appropriate worker workflows for each Todo item
-3. Update Project status to "In Progress" for dispatched items
-4. On next orchestrator run, the discovery step will find the worker outputs
-
-**Project Item to Payload Mapping**:
-- Read Project field values from the Todo item
-- Map to worker payload schema based on worker metadata
-- Include campaign_id in every worker dispatch
-- Use the item's URL or number as the work_item_id
-
-{{ else if eq .BootstrapMode "manual" }}
-### Manual Bootstrap
-
-No automatic bootstrap configured. Wait for manual work item creation:
-
-- Work items should be created manually (issues, PRs, or discussions)
-- All items MUST have the tracker label: `z_campaign_{{ .CampaignID }}`
-- Items MUST follow the worker output labeling contract
-- Once items exist, the orchestrator will discover them normally
-
-{{ end }}
-{{ end }}
-
----
-
-## Worker Selection
-
-{{ if .WorkerMetadata }}
-When dispatching workers during bootstrap, use deterministic selection:
-
-{{ range $index, $worker := .WorkerMetadata }}
-### Worker {{ add1 $index }}: {{ .ID }}
-
-**Capabilities**: {{ range $capIndex, $cap := .Capabilities }}{{ if $capIndex }}, {{ end }}`{{ $cap }}`{{ end }}
-
-**Payload Schema**:
-{{ range $fieldName, $fieldDef := .PayloadSchema }}- `{{ $fieldName }}` ({{ .Type }}{{ if .Required }}, required{{ end }}): {{ .Description }}
-{{ end }}
-
-**Output Labeling**:
-{{ if .OutputLabeling.Labels }}- Labels: {{ range $labelIndex, $label := .OutputLabeling.Labels }}{{ if $labelIndex }}, {{ end }}`{{ $label }}`{{ end }}
-{{ end }}- Key in Title: {{ .OutputLabeling.KeyInTitle }}
-{{ if .OutputLabeling.KeyFormat }}- Key Format: `{{ .OutputLabeling.KeyFormat }}`
-{{ end }}
-- Campaign tracker label applied automatically: `z_campaign_{{ $.CampaignID }}`
-
-**Idempotency Strategy**: {{ .IdempotencyStrategy }}
-
-{{ if .Priority }}**Priority**: {{ .Priority }} (higher = preferred when multiple workers match)
-{{ end }}
-
-{{ end }}
-
-**Selection Algorithm**:
-
-1. For each work item, check which workers can handle it:
-   - Match work item type/metadata to worker capabilities
-   - Check if worker's payload schema requirements can be satisfied
-2. If multiple workers match:
-   - Select the worker with highest priority
-   - If priorities are equal, select first alphabetically by ID
-3. Build payload from work item metadata according to worker's payload schema
-4. Dispatch worker with campaign_id and constructed payload
-
-{{ else }}
-**Note**: No worker metadata configured. Use workflow IDs from campaign spec for dispatch.
-{{ end }}
-
----
-
-## Bootstrap Success Criteria
-
-After bootstrap completes:
-
-1. ✅ At least one worker workflow dispatched (or manual items created)
-2. ✅ All dispatched items will have proper tracker labels
-3. ✅ Next orchestrator run will discover >= 1 work item
-4. ✅ Campaign transitions from bootstrap phase to normal operation
-
-**Idempotency**: Bootstrap only runs when discovery = 0. Once work items exist, normal orchestration takes over.
diff --git a/.github/aw/close-agentic-campaign.md b/.github/aw/close-agentic-campaign.md
deleted file mode 100644
index f08b2179660..00000000000
--- a/.github/aw/close-agentic-campaign.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Closing Instructions (Highest Priority)
-
-Execute all four steps in strict order:
-
-1. Read State (no writes)
-2. Make Decisions (no writes)
-3. Apply Updates (writes)
-4. Report
-
-The following rules are mandatory and override inferred behavior:
-
-- The GitHub Project board is the single source of truth.
-- All project writes MUST comply with the Project Update Instructions.
-- State reads and state writes MUST NOT be interleaved.
-- Do NOT infer missing data or invent values.
-- Do NOT reorganize hierarchy.
-- Do NOT overwrite fields except as explicitly allowed.
-- Workers are immutable and campaign-agnostic.
-
-If any instruction conflicts, the Project Update Instructions take precedence for all writes.
diff --git a/.github/aw/execute-agentic-campaign-workflow.md b/.github/aw/execute-agentic-campaign-workflow.md
deleted file mode 100644
index 921a6120647..00000000000
--- a/.github/aw/execute-agentic-campaign-workflow.md
+++ /dev/null
@@ -1,284 +0,0 @@
-# Workflow Execution
-
-This campaign references the following campaign workers. These workers follow the first-class worker pattern: they are dispatch-only workflows with standardized input contracts.
-
-**IMPORTANT: Workers are orchestrated, not autonomous. They accept `campaign_id` and `payload` inputs via workflow_dispatch.**
-
----
-
-## Campaign Workers
-
-{{ if .Workflows }}
-The following campaign workers are referenced by this campaign:
-{{ range $idx, $workflow := .Workflows }}
-{{ add1 $idx }}. `{{ $workflow }}`
-{{ end }}
-{{ end }}
-
-**Worker Pattern**: All workers MUST:
-- Use `workflow_dispatch` as the ONLY trigger (no schedule/push/pull_request)
-- Accept `campaign_id` (string) and `payload` (string; JSON) inputs
-- Implement idempotency via deterministic work item keys
-- Label all created items with `z_campaign_{{ .CampaignID }}`
-
----
-
-## Workflow Creation Guardrails
-
-### Before Creating Any Worker Workflow, Ask:
-
-1. **Does this workflow already exist?** - Check `.github/workflows/` thoroughly
-2. **Can an existing workflow be adapted?** - Even if not perfect, existing is safer
-3. **Is the requirement clear?** - Can you articulate exactly what it should do?
-4. **Is it testable?** - Can you verify it works with test inputs?
-5. **Is it reusable?** - Could other campaigns benefit from this worker?
-
-### Only Create New Workers When:
-
-✅ **All these conditions are met:**
-- No existing workflow does the required task
-- The campaign objective explicitly requires this capability
-- You have a clear, specific design for the worker
-- The worker has a focused, single-purpose scope
-- You can test it independently before campaign use
-
-❌ **Never create workers when:**
-- You're unsure about requirements
-- An existing workflow "mostly" works
-- The worker would be complex or multi-purpose
-- You haven't verified it doesn't already exist
-- You can't clearly explain what it does in one sentence
-
----
-
-## Worker Creation Template
-
-If you must create a new worker (only after checking ALL guardrails above), use this template:
-
-**Create the workflow file at `.github/workflows/<workflow-id>.md`:**
-
-```yaml
----
-name: <Worker Name>
-description: <One sentence describing what it does>
-
-on:
-  workflow_dispatch:
-    inputs:
-      campaign_id:
-        description: 'Campaign identifier'
-        required: true
-        type: string
-      payload:
-        description: 'JSON payload with work item details'
-        required: true
-        type: string
-
-tracker-id: <workflow-id>
-
-tools:
-  github:
-    toolsets: [default]
-  # Add minimal additional tools as needed
-
-safe-outputs:
-  create-pull-request:
-    max: 1  # Start conservative
-  add-comment:
-    max: 2
----
-
-# <Worker Name>
-
-You are a campaign worker that processes work items.
-
-## Input Contract
-
-Parse inputs:
-```javascript
-const campaignId = context.payload.inputs.campaign_id;
-const payload = JSON.parse(context.payload.inputs.payload);
-```
-
-Expected payload structure:
-```json
-{
-  "repository": "owner/repo",
-  "work_item_id": "unique-id",
-  "target_ref": "main",
-  // Additional context...
-}
-```
-
-## Idempotency Requirements
-
-1. **Generate deterministic key**:
-   ```
-   const workKey = `campaign-${campaignId}-${payload.repository}-${payload.work_item_id}`;
-   ```
-
-2. **Check for existing work**:
-   - Search for PRs/issues with `workKey` in title
-  - Filter by label: `z_campaign_${campaignId}`
-   - If found: Skip or update
-   - If not: Create new
-
-3. **Label all created items**:
-  - Apply `z_campaign_${campaignId}` label
-   - This enables discovery by orchestrator
-
-## Task
-
-<Specific task description>
-
-## Output
-
-Report:
-- Link to created/updated PR or issue
-- Whether work was skipped (exists) or completed
-- Any errors or blockers
-```
-
-**After creating:**
-- Compile: `gh aw compile <workflow-id>.md`
-- **CRITICAL: Test with sample inputs** (see testing requirements below)
-
----
-
-## Worker Testing (MANDATORY)
-
-**Why test?** - Untested workers may fail during campaign execution. Test with sample inputs first to catch issues early.
-
-**Testing steps:**
-
-1. **Prepare test payload**:
-   ```json
-   {
-     "repository": "test-org/test-repo",
-     "work_item_id": "test-1",
-     "target_ref": "main"
-   }
-   ```
-
-2. **Trigger test run**:
-   ```bash
-   gh workflow run <workflow-id>.yml \
-     -f campaign_id={{ .CampaignID }} \
-     -f payload='{"repository":"test-org/test-repo","work_item_id":"test-1"}'
-   ```
-   
-   Or via GitHub MCP:
-   ```javascript
-   mcp__github__run_workflow(
-     workflow_id: "<workflow-id>", 
-     ref: "main",
-     inputs: {
-       campaign_id: "{{ .CampaignID }}",
-       payload: JSON.stringify({repository: "test-org/test-repo", work_item_id: "test-1"})
-     }
-   )
-   ```
-
-3. **Wait for completion**: Poll until status is "completed"
-
-4. **Verify success**:
-   - Check that workflow succeeded
-   - Verify idempotency: Run again with same inputs, should skip/update
-   - Review created items have correct labels
-   - Confirm deterministic keys are used
-
-5. **Test failure actions**:
-   - DO NOT use the worker if testing fails
-   - Analyze failure logs
-   - Make corrections
-   - Recompile and retest
-   - If unfixable after 2 attempts, report in status and skip
-
-**Note**: Workflows that accept `workflow_dispatch` inputs can receive parameters from the orchestrator. This enables the orchestrator to provide context, priorities, or targets based on its decisions. See [DispatchOps documentation](https://githubnext.github.io/gh-aw/guides/dispatchops/#with-input-parameters) for input parameter examples.
-
----
-
-## Orchestration Guidelines
-
-**Execution pattern:**
-- Workers are **orchestrated, not autonomous**
-- Orchestrator discovers work items via discovery manifest
-- Orchestrator decides which workers to run and with what inputs
-- Workers receive `campaign_id` and `payload` via workflow_dispatch
-- Sequential vs parallel execution is orchestrator's decision
-
-**Worker dispatch:**
-- Parse discovery manifest (`./.gh-aw/campaign.discovery.json`)
-- For each work item needing processing:
-  1. Determine appropriate worker for this item type
-  2. Construct payload with work item details
-  3. Dispatch worker via workflow_dispatch with campaign_id and payload
-  4. Track dispatch status
-
-**Input construction:**
-```javascript
-// Example: Dispatching security-fix worker
-const workItem = discoveryManifest.items[0];
-const payload = {
-  repository: workItem.repo,
-  work_item_id: `alert-${workItem.number}`,
-  target_ref: "main",
-  alert_type: "sql-injection",
-  file_path: "src/db.go",
-  line_number: 42
-};
-
-await github.actions.createWorkflowDispatch({
-  owner: context.repo.owner,
-  repo: context.repo.repo,
-  workflow_id: "security-fix-worker.yml",
-  ref: "main",
-  inputs: {
-    campaign_id: "{{ .CampaignID }}",
-    payload: JSON.stringify(payload)
-  }
-});
-```
-
-**Idempotency by design:**
-- Workers implement their own idempotency checks
-- Orchestrator doesn't need to track what's been processed
-- Can safely re-dispatch work items across runs
-- Workers will skip or update existing items
-
-**Failure handling:**
-- If a worker dispatch fails, note it but continue
-- Worker failures don't block entire campaign
-- Report all failures in status update with context
-- Humans can intervene if needed
-
----
-
-## After Worker Orchestration
-
-Once workers have been dispatched (or new workers created and tested), proceed with normal orchestrator steps:
-
-1. **Discovery** - Read state from discovery manifest and project board
-2. **Planning** - Determine what needs updating on project board
-3. **Project Updates** - Write state changes to project board  
-4. **Status Reporting** - Report progress, worker dispatches, failures, next steps
-
----
-
-## Key Differences from Fusion Approach
-
-**Old fusion approach (REMOVED)**:
-- Workers had mixed triggers (schedule + workflow_dispatch)
-- Fusion dynamically added workflow_dispatch to existing workflows
-- Workers stored in campaign-specific folders
-- Ambiguous ownership and trigger precedence
-
-**New first-class worker approach**:
-- Workers are dispatch-only (on: workflow_dispatch)
-- Standardized input contract (campaign_id, payload)
-- Explicit idempotency via deterministic keys
-- Clear ownership: workers are orchestrated, not autonomous
-- Workers stored with regular workflows (not campaign-specific folders)
-- Orchestration policy kept explicit in orchestrator
-
-This eliminates duplicate execution problems and makes orchestration concerns explicit.
diff --git a/.github/aw/generate-agentic-campaign.md b/.github/aw/generate-agentic-campaign.md
deleted file mode 100644
index 56b7cd9e2e8..00000000000
--- a/.github/aw/generate-agentic-campaign.md
+++ /dev/null
@@ -1,210 +0,0 @@
-# Campaign Generator
-
-You are a campaign workflow coordinator for GitHub Agentic Workflows. You create campaigns, set up project boards, and assign compilation to the Copilot Coding Agent.
-
-**Issue Context:** Read the campaign requirements from the issue that triggered this workflow (via the `create-agentic-campaign` label).
-
-## Using Safe Output Tools
-
-When creating or modifying GitHub resources, **use MCP tool calls directly** (not markdown or JSON):
-
-- `create_project` - Create project board
-- `update_project` - Create/update project fields, views, and items
-- `update_issue` - Update issue details
-- `create_agent_session` - Create a Copilot coding agent session (preferred handoff)
-- `assign_to_agent` - Assign to agent (optional; use for existing issues/PRs)
-
-## Workflow
-
-**Your Responsibilities:**
-
-1. Create GitHub Project
-2. Create views: Roadmap (roadmap), Task Tracker (table), Progress Board (board)
-3. Create required campaign project fields (see “Project Fields (Required)”) using `update_project` with `operation: "create_fields"`
-4. Parse campaign requirements from the triggering issue (available via GitHub event context)
-5. Discover workflows: scan `.github/workflows/*.md` and check [agentics collection](https://github.com/githubnext/agentics)
-6. Generate `.campaign.md` spec in `.github/workflows/`
-7. Update the triggering issue with a human-readable status + Copilot Coding Agent instructions
-8. Create a Copilot coding agent session (preferred) or assign to agent (fallback)
-
-**Agent Responsibilities:** Compile with `gh aw compile`, commit files, create PR
-
-## Campaign Spec Format
-
-```yaml
----
-id: <kebab-case-id>
-name: <Campaign Name>
-description: <One sentence>
-project-url: <GitHub Project URL>
-workflows: [<workflow-1>, <workflow-2>]
-scope: [owner/repo1, owner/repo2, org:org-name]  # Optional: defaults to current repository
-owners: [@<username>]
-risk-level: <low|medium|high>
-state: planned
-allowed-safe-outputs: [create-issue, add-comment]
----
-
-# <Campaign Name>
-
-<Purpose and goals>
-
-## Workflows
-
-### <workflow-1>
-<What this workflow does>
-
-## Timeline
-- **Start**: <Date or TBD>
-- **Target**: <Date or Ongoing>
-```
-
-## Key Guidelines
-
-## Project Fields (Required)
-
-Campaign orchestrators and project-updaters assume these fields exist. Create them up-front with `update_project` using `operation: "create_fields"` and `field_definitions` so single-select options are created correctly (GitHub does not support adding options later).
-
-Required fields:
-
-- `status` (single-select): `Todo`, `In Progress`, `Review required`, `Blocked`, `Done`
-- `campaign_id` (text)
-- `worker_workflow` (text)
-- `target_repo` (text, `owner/repo`)
-- `priority` (single-select): `High`, `Medium`, `Low`
-- `size` (single-select): `Small`, `Medium`, `Large`
-- `start_date` (date, `YYYY-MM-DD`)
-- `end_date` (date, `YYYY-MM-DD`)
-
-Create them before adding any items to the project.
-
-## Copilot Coding Agent Handoff (Required)
-
-Before creating an agent session, update the triggering issue (via `update_issue`) to include a clear, human-friendly status update.
-
-The issue update MUST be easy to follow for someone unfamiliar with campaigns. Include:
-
-- What you did (Project created, fields/views created, spec generated)
-- What you are about to do next (handoff to agent)
-- What the human should do next (review PR, merge, run orchestrator)
-- Links to documentation
-
-Use `update_issue` with `operation: "append"` so you **do not overwrite** the original issue text.
-
-Docs to link:
-- Getting started: https://githubnext.github.io/gh-aw/guides/campaigns/getting-started/
-- Flow & lifecycle: https://githubnext.github.io/gh-aw/guides/campaigns/flow/
-- Campaign specs: https://githubnext.github.io/gh-aw/guides/campaigns/scratchpad/
-
-### Required structure for the issue update
-
-Add a section like this (fill in real values):
-
-```markdown
-## Campaign setup status
-
-**Status:** Ready for PR review
-
-### What just happened
-- Created Project: <project-url>
-- Created standard fields + views (Roadmap, Task Tracker, Progress Board)
-- Generated campaign spec: `.github/workflows/<campaign-id>.campaign.md`
-- Selected workflows: `<workflow-1>`, `<workflow-2>`
-
-### What happens next
-1. Copilot Coding Agent will open a pull request with the generated files (via agent session).
-2. You review the PR and merge it.
-3. After merge, run the orchestrator workflow from the Actions tab.
-
-### Copilot Coding Agent handoff
-- **Campaign ID:** `<campaign-id>`
-- **Project URL:** <project-url>
-- **Workflows:** `<workflow-1>`, `<workflow-2>`
-- **Agent session:** <session-url (if created)>
-
-Run:
-```bash
-gh aw compile
-```
-
-Commit + include in the PR:
-- `.github/workflows/<campaign-id>.campaign.md`
-- `.github/workflows/<campaign-id>.campaign.g.md`
-- `.github/workflows/<campaign-id>.campaign.lock.yml`
-
-Acceptance checklist:
-- `gh aw compile` succeeds
-- Orchestrator lock file updated
-- PR opened and linked back to this issue
-
-Docs:
-- https://githubnext.github.io/gh-aw/guides/campaigns/getting-started/
-- https://githubnext.github.io/gh-aw/guides/campaigns/flow/
-```
-
-### Minimum handoff requirements
-
-In addition to the structure above, include these exact items:
-
-- The generated `campaign-id` and `project-url`
-- The list of selected workflow IDs
-- Exact commands for the agent to run (at minimum): `gh aw compile`
-- What files must be committed (the new `.github/workflows/<campaign-id>.campaign.md`, generated `.campaign.g.md`, and compiled `.campaign.lock.yml`)
-- A short acceptance checklist (e.g., “`gh aw compile` succeeds; lock file updated; PR opened”)
-
-**Campaign ID:** Convert names to kebab-case (e.g., "Security Q1 2025" → "security-q1-2025"). Check for conflicts in `.github/workflows/`.
-
-**Allowed Repos/Orgs (Required):**
-
-- `scope`: **Optional** - Scope selectors for repos and orgs this campaign can discover and operate on (defaults to current repo)
-- Defines campaign scope as a reviewable contract for security and governance
-
-**Workflow Discovery:**
-
-- Scan existing: `.github/workflows/*.md` (agentic), `*.yml` (regular)
-- Match by keywords: security, dependency, documentation, quality, CI/CD
-- Select 2-4 workflows (prioritize existing, identify AI enhancement candidates)
-
-**Safe Outputs (Least Privilege):**
-
-- For this campaign generator workflow, use `update-issue` for status updates (this workflow does not enable `add-comment`).
-- Project-based: `create-project`, `update-project`, `update-issue`, `create-agent-session` (preferred)
-
-**Operation Order for Project Setup:**
-
-1. `create-project` (creates project + views)
-2. `update-project` (adds items/fields)
-3. `update-issue` (updates metadata, optional)
-4. `create-agent-session` (preferred) or `assign-to-agent` (fallback)
-
-**Example Safe Outputs Configuration for Project-Based Campaigns:**
-
-```yaml
-safe-outputs:
-  create-project:
-    max: 1
-    github-token: "<GH_AW_PROJECT_GITHUB_TOKEN>"  # Provide via workflow secret/env; avoid secrets expressions in runtime-import files
-    target-owner: "${{ github.repository_owner }}"
-    views:  # Views are created automatically when project is created
-      - name: "Campaign Roadmap"
-        layout: "roadmap"
-        filter: "is:issue is:pr"
-      - name: "Task Tracker"
-        layout: "table"
-        filter: "is:issue is:pr"
-      - name: "Progress Board"
-        layout: "board"
-        filter: "is:issue is:pr"
-  update-project:
-    max: 10
-    github-token: "<GH_AW_PROJECT_GITHUB_TOKEN>"  # Provide via workflow secret/env; avoid secrets expressions in runtime-import files
-  update-issue:
-  create-agent-session:
-    base: "${{ github.ref_name }}"  # Prefer main/default branch when appropriate
-```
-
-**Risk Levels:**
-
-- High: Sensitive/multi-repo/breaking → 2 approvals + sponsor
-- Medium: Cross-repo/automated → 1 approval
-- Low: Read-only/single repo → No approval
diff --git a/.github/aw/orchestrate-agentic-campaign.md b/.github/aw/orchestrate-agentic-campaign.md
deleted file mode 100644
index 9f758036159..00000000000
--- a/.github/aw/orchestrate-agentic-campaign.md
+++ /dev/null
@@ -1,165 +0,0 @@
-# Orchestrator Instructions
-
-This orchestrator coordinates a single campaign by discovering worker outputs and making deterministic decisions.
-
-**Scope:** orchestration + project sync + reporting (discovery, planning, pacing, writing, reporting).
-**Actuation model:** **hybrid** — the orchestrator may update campaign state directly (Projects and status updates) and may also dispatch allowlisted worker workflows.
-**Write authority:** the orchestrator may write GitHub state when explicitly allowlisted via safe outputs; delegate repo/code changes (e.g., PRs) to workers unless this campaign explicitly defines otherwise.
-
----
-
-## Traffic and Rate Limits (Required)
-
-- Minimize API calls; avoid full rescans when possible.
-- Prefer incremental discovery with deterministic ordering (e.g., by `updatedAt`, tie-break by ID).
-- Enforce strict pagination budgets; if a query requires many pages, stop early and continue next run.
-- Use a durable cursor/checkpoint so the next run continues without rescanning.
-- On throttling (HTTP 429 / rate-limit 403), do not retry aggressively; back off and end the run after reporting what remains.
-
-{{ if .CursorGlob }}
-**Cursor file (repo-memory)**: `{{ .CursorGlob }}`
-**File system path**: `/tmp/gh-aw/repo-memory/campaigns/{{.CampaignID}}/cursor.json`
-- If it exists: read first and continue from its boundary.
-- If it does not exist: create it by end of run.
-- Always write the updated cursor back to the same path.
-{{ end }}
-
-{{ if .MetricsGlob }}
-**Metrics snapshots (repo-memory)**: `{{ .MetricsGlob }}`
-**File system path**: `/tmp/gh-aw/repo-memory/campaigns/{{.CampaignID}}/metrics/*.json`
-- Persist one append-only JSON metrics snapshot per run (new file per run; do not rewrite history).
-- Use UTC date (`YYYY-MM-DD`) in the filename (example: `metrics/2025-12-22.json`).
-{{ end }}
-
-{{ if gt .MaxDiscoveryItemsPerRun 0 }}
-**Read budget**: max discovery items per run: {{ .MaxDiscoveryItemsPerRun }}
-{{ end }}
-{{ if gt .MaxDiscoveryPagesPerRun 0 }}
-**Read budget**: max discovery pages per run: {{ .MaxDiscoveryPagesPerRun }}
-{{ end }}
-
----
-
-## Core Principles
-
-1. Workers are immutable and campaign-agnostic
-2. The GitHub Project board is the authoritative campaign state
-3. Correlation is explicit (tracker-id AND labels)
-4. Reads and writes are separate steps (never interleave)
-5. Idempotent operation is mandatory (safe to re-run)
-6. Orchestrator writes must be deterministic and minimal
-
----
-
-## Execution Steps (Required Order)
-
-### Step 1 — Read State (Discovery) [NO WRITES]
-
-**IMPORTANT**: Discovery has been precomputed. Read the discovery manifest instead of performing GitHub-wide searches.
-
-1) Read the precomputed discovery manifest: `./.gh-aw/campaign.discovery.json`
-
-2) Parse discovered items from the manifest:
-   - Each item has: url, content_type (issue/pull_request/discussion), number, repo, created_at, updated_at, state
-   - Closed items have: closed_at (for issues) or merged_at (for PRs)
-   - Items are pre-sorted by updated_at for deterministic processing
-
-3) Check the manifest summary for work counts.
-
-4) Discovery cursor is maintained automatically in repo-memory; do not modify it manually.
-
-### Step 2 — Make Decisions (Planning) [NO WRITES]
-
-5) Determine desired `status` strictly from explicit GitHub state:
-- Open → `Todo` (or `In Progress` only if explicitly indicated elsewhere)
-- Closed (issue/discussion) → `Done`
-- Merged (PR) → `Done`
-
-6) Calculate required date fields (for workers that sync Projects):
-- `start_date`: format `created_at` as `YYYY-MM-DD`
-- `end_date`:
-  - if closed/merged → format `closed_at`/`merged_at` as `YYYY-MM-DD`
-  - if open → **today's date** formatted `YYYY-MM-DD`
-
-7) Reads and writes are separate steps (never interleave).
-
-### Step 3 — Apply Updates (Execution) [WRITES]
-
-8) Apply required GitHub state updates in a single write phase.
-
-Allowed writes (when allowlisted via safe outputs):
-- Update the campaign Project board (add/update items and fields)
-- Post status updates (e.g., update an issue or add a comment)
-- Create Copilot agent sessions for repo-side work (use when you need code changes)
-
-Constraints:
-- Use only allowlisted safe outputs.
-- Keep within configured max counts and API budgets.
-- Do not interleave reads and writes.
-
-### Step 4 — Dispatch Workers (Optional) [DISPATCH]
-
-9) For repo-side actions (e.g., code changes), dispatch allowlisted worker workflows using `dispatch-workflow`.
-
-Constraints:
-- Only dispatch allowlisted workflows.
-- Keep within the dispatch-workflow max for this run.
-
-### Step 5 — Report
-
-10) Summarize what you updated and/or dispatched, what remains, and what should run next.
-
-    **Discovered:** 25 items (15 issues, 10 PRs)
-    **Processed:** 10 items added to project, 5 updated
-    **Completion:** 60% (30/50 total tasks)
-
-    ## Most Important Findings
-
-    1. **Critical accessibility gaps identified**: 3 high-severity accessibility issues discovered in mobile navigation, requiring immediate attention
-    2. **Documentation coverage acceleration**: Achieved 5% improvement in one week (best velocity so far)
-    3. **Worker efficiency improving**: daily-doc-updater now processing 40% more items per run
-
-    ## What Was Learned
-
-    - Multi-device testing reveals issues that desktop-only testing misses - should be prioritized
-    - Documentation updates tied to code changes have higher accuracy and completeness
-    - Users report fewer issues when examples include error handling patterns
-
-    ## Campaign Progress
-
-    **Documentation Coverage** (Primary Metric):
-    - Baseline: 85% → Current: 88% → Target: 95%
-    - Direction: ↑ Increasing (+3% this week, +1% velocity/week)
-    - Status: ON TRACK - At current velocity, will reach 95% in 7 weeks
-
-    **Accessibility Score** (Supporting Metric):
-    - Baseline: 90% → Current: 91% → Target: 98%
-    - Direction: ↑ Increasing (+1% this month)
-    - Status: AT RISK - Slower progress than expected, may need dedicated focus
-
-    **User-Reported Issues** (Supporting Metric):
-    - Baseline: 15/month → Current: 12/month → Target: 5/month
-    - Direction: ↓ Decreasing (-3 this month, -20% velocity)
-    - Status: ON TRACK - Trending toward target
-
-    ## Next Steps
-
-    1. Address 3 critical accessibility issues identified this run (high priority)
-    2. Continue processing remaining 15 discovered items
-    3. Focus on accessibility improvements to accelerate supporting KPI
-    4. Maintain current documentation coverage velocity
-```
-
-12) Report:
-- counts discovered (by type)
-- counts processed this run (by action: add/status_update/backfill/noop/failed)
-- counts deferred due to budgets
-- failures (with reasons)
-- completion state (work items only)
-- cursor advanced / remaining backlog estimate
-
----
-
-## Authority
-
-If any instruction in this file conflicts with **Project Update Instructions**, the Project Update Instructions win for all project writes.
diff --git a/.github/aw/update-agentic-campaign-project.md b/.github/aw/update-agentic-campaign-project.md
deleted file mode 100644
index 0b35d0272a6..00000000000
--- a/.github/aw/update-agentic-campaign-project.md
+++ /dev/null
@@ -1,247 +0,0 @@
-{{if .ProjectURL}}
-# Project Update Instructions (Authoritative Write Contract)
-
-## Project Board Integration
-
-This file defines the ONLY allowed rules for writing to the GitHub Project board.
-If any other instructions conflict with this file, THIS FILE TAKES PRECEDENCE for all project writes.
-
----
-
-## 0) Hard Requirements (Do Not Deviate)
-
-- Any workflow performing project writes (orchestrators or workers) MUST use only the `update-project` safe-output.
-- All writes MUST target exactly:
-  - **Project URL**: `{{.ProjectURL}}`
-- Every item MUST include:
-  - `campaign_id: "{{.CampaignID}}"`
-
-## Campaign ID
-
-All campaign tracking MUST key off `campaign_id: "{{.CampaignID}}"`.
-
----
-
-## 1) Required Project Fields (Must Already Exist)
-
-| Field | Type | Allowed / Notes |
-|---|---|---|
-| `status` | single-select | `Todo` / `In Progress` / `Review required` / `Blocked` / `Done` |
-| `campaign_id` | text | Must equal `{{.CampaignID}}` |
-| `worker_workflow` | text | workflow ID or `"unknown"` |
-| `target_repo` | text | `owner/repo` |
-| `priority` | single-select | `High` / `Medium` / `Low` |
-| `size` | single-select | `Small` / `Medium` / `Large` |
-| `start_date` | date | `YYYY-MM-DD` |
-| `end_date` | date | `YYYY-MM-DD` |
-
-Field names are case-sensitive.
-
----
-
-## 2) Content Identification (Mandatory)
-
-Use **content number** (integer), never the URL as an identifier.
-
-- Issue URL: `.../issues/123` → `content_type: "issue"`, `content_number: 123`
-- PR URL: `.../pull/456` → `content_type: "pull_request"`, `content_number: 456`
-
----
-
-## 3) Deterministic Field Rules (No Inference)
-
-These rules apply to any time you write fields:
-
-- `campaign_id`: always `{{.CampaignID}}`
-- `worker_workflow`: workflow ID if known, else `"unknown"`
-- `target_repo`: extract `owner/repo` from the issue/PR URL
-- `priority`: default `Medium` unless explicitly known
-- `size`: default `Medium` unless explicitly known
-- `start_date`: issue/PR `created_at` formatted `YYYY-MM-DD`
-- `end_date`:
-  - if closed/merged → `closed_at` / `merged_at` formatted `YYYY-MM-DD`
-  - if open → **today’s date** formatted `YYYY-MM-DD` (**required for roadmap view; do not leave blank**)
-
-For open items, `end_date` is a UI-required placeholder and does NOT represent actual completion.
-
----
-
-## 4) Read-Write Separation (Prevents Read/Write Mixing)
-
-1. **READ STEP (no writes)** — validate existence and gather metadata
-2. **WRITE STEP (writes only)** — execute `update-project`
-
-Never interleave reads and writes.
-
----
-
-## 5) Adding an Issue or PR (First Write)
-
-### Adding New Issues
-
-When first adding an item to the project, you MUST write ALL required fields.
-
-```yaml
-update-project:
-  project: "{{.ProjectURL}}"
-  campaign_id: "{{.CampaignID}}"
-  content_type: "issue"              # or "pull_request"
-  content_number: 123
-  fields:
-    status: "Todo"                   # "Done" if already closed/merged
-    campaign_id: "{{.CampaignID}}"
-    worker_workflow: "unknown"
-    target_repo: "owner/repo"
-    priority: "Medium"
-    size: "Medium"
-    start_date: "2025-12-15"
-    end_date: "2026-01-03"
-```
-
----
-
-## 6) Updating an Existing Item (Minimal Writes)
-
-### Updating Existing Items
-
-Preferred behavior is minimal, idempotent writes:
-
-- If item exists and `status` is unchanged → **No-op**
-- If item exists and `status` differs → **Update `status` only**
-- If any required field is missing/empty/invalid → **One-time full backfill** (repair only)
-
-### Status-only Update (Default)
-
-```yaml
-update-project:
-  project: "{{.ProjectURL}}"
-  campaign_id: "{{.CampaignID}}"
-  content_type: "issue"              # or "pull_request"
-  content_number: 123
-  fields:
-    status: "Done"
-```
-
-### Full Backfill (Repair Only)
-
-```yaml
-update-project:
-  project: "{{.ProjectURL}}"
-  campaign_id: "{{.CampaignID}}"
-  content_type: "issue"              # or "pull_request"
-  content_number: 123
-  fields:
-    status: "Done"
-    campaign_id: "{{.CampaignID}}"
-    worker_workflow: "WORKFLOW_ID"
-    target_repo: "owner/repo"
-    priority: "Medium"
-    size: "Medium"
-    start_date: "2025-12-15"
-    end_date: "2026-01-02"
-```
-
----
-
-## 7) Idempotency Rules
-
-- Matching status already set → **No-op**
-- Different status → **Status-only update**
-- Invalid/deleted/inaccessible URL → **Record failure and continue**
-
-## Write Operation Rules
-
-All writes MUST conform to this file and use `update-project` only.
-
----
-
-## 8) Logging + Failure Handling (Mandatory)
-
-For every attempted item, record:
-
-- `content_type`, `content_number`, `target_repo`
-- action taken: `noop | add | status_update | backfill | failed`
-- error details if failed
-
-Failures must not stop processing remaining items.
-
----
-
-## 9) Worker Workflow Policy
-
-- Workers are campaign-agnostic.
-- Orchestrator populates `worker_workflow`.
-- If `worker_workflow` cannot be determined, it MUST remain `"unknown"` unless explicitly reclassified by the orchestrator.
-
----
-
-## 10) Parent / Sub-Issue Rules (Campaign Hierarchy)
-
-- Each project board MUST have exactly **one Epic issue** representing the campaign.
-- The Epic issue MUST:
-  - Be added to the project board
-  - Use the same `campaign_id`
-  - Use `worker_workflow: "unknown"`
-
-- All campaign work issues (non-epic) MUST be created as **sub-issues of the Epic**.
-- Issues MUST NOT be re-parented based on worker assignment.
-
-- Pull requests cannot be sub-issues:
-  - PRs MUST reference their related issue via standard GitHub linking (e.g. “Closes #123”).
-
-- Worker grouping MUST be done via the `worker_workflow` project field, not via parent issues.
-
-- The Epic issue is narrative only.
-- The project board is the sole authoritative source of campaign state.
-
----
-
-## Appendix — Machine Check Checklist (Optional)
-
-This checklist is designed to validate outputs before executing project writes.
-
-### A) Output Structure Checks
-
-- [ ] All writes use `update-project:` blocks (no other write mechanism).
-- [ ] Each `update-project` block includes:
-  - [ ] `project: "{{.ProjectURL}}"`
-  - [ ] `campaign_id: "{{.CampaignID}}"` (top-level)
-  - [ ] `content_type` ∈ {`issue`, `pull_request`}
-  - [ ] `content_number` is an integer
-  - [ ] `fields` object is present
-
-### B) Field Validity Checks
-
-- [ ] `fields.status` ∈ {`Todo`, `In Progress`, `Review required`, `Blocked`, `Done`}
-- [ ] `fields.campaign_id` is present on first-add/backfill and equals `{{.CampaignID}}`
-- [ ] `fields.worker_workflow` is present on first-add/backfill and is either a known workflow ID or `"unknown"`
-- [ ] `fields.target_repo` matches `owner/repo`
-- [ ] `fields.priority` ∈ {`High`, `Medium`, `Low`}
-- [ ] `fields.size` ∈ {`Small`, `Medium`, `Large`}
-- [ ] `fields.start_date` matches `YYYY-MM-DD`
-- [ ] `fields.end_date` matches `YYYY-MM-DD`
-
-### C) Update Semantics Checks
-
-- [ ] For existing items, payload is **status-only** unless explicitly doing a backfill repair.
-- [ ] Backfill is used only when required fields are missing/empty/invalid.
-- [ ] No payload overwrites `priority`/`size`/`worker_workflow` with defaults during a normal status update.
-
-### D) Read-Write Separation Checks
-
-- [ ] All reads occur before any writes (no read/write interleaving).
-- [ ] Writes are batched separately from discovery.
-
-### E) Epic/Hierarchy Checks (Policy-Level)
-
-- [ ] Exactly one Epic exists for the campaign board.
-- [ ] Epic is on the board and uses `worker_workflow: "unknown"`.
-- [ ] All campaign work issues are sub-issues of the Epic (if supported by environment/tooling).
-- [ ] PRs are linked to issues via GitHub linking (e.g. “Closes #123”).
-
-### F) Failure Handling Checks
-
-- [ ] Invalid/deleted/inaccessible items are logged as failures and processing continues.
-- [ ] Idempotency is delegated to the `update-project` tool; no pre-filtering by board presence.
-
-{{end}}
diff --git a/Makefile b/Makefile
index 6908df7e951..2765bf10b9a 100644
--- a/Makefile
+++ b/Makefile
@@ -594,13 +594,6 @@ sync-templates:
 
 	# Agent templates
 	@cp .github/agents/agentic-workflows.agent.md pkg/cli/templates/
-	
-	# Campaign management templates (lifecycle order)
-	@cp .github/aw/generate-agentic-campaign.md pkg/cli/templates/
-	@cp .github/aw/orchestrate-agentic-campaign.md pkg/cli/templates/
-	@cp .github/aw/execute-agentic-campaign-workflow.md pkg/cli/templates/
-	@cp .github/aw/update-agentic-campaign-project.md pkg/cli/templates/
-	@cp .github/aw/close-agentic-campaign.md pkg/cli/templates/
 
 	@echo "✓ Templates synced successfully"
 
diff --git a/pkg/cli/templates/close-agentic-campaign.md b/pkg/cli/templates/close-agentic-campaign.md
deleted file mode 100644
index f08b2179660..00000000000
--- a/pkg/cli/templates/close-agentic-campaign.md
+++ /dev/null
@@ -1,20 +0,0 @@
-# Closing Instructions (Highest Priority)
-
-Execute all four steps in strict order:
-
-1. Read State (no writes)
-2. Make Decisions (no writes)
-3. Apply Updates (writes)
-4. Report
-
-The following rules are mandatory and override inferred behavior:
-
-- The GitHub Project board is the single source of truth.
-- All project writes MUST comply with the Project Update Instructions.
-- State reads and state writes MUST NOT be interleaved.
-- Do NOT infer missing data or invent values.
-- Do NOT reorganize hierarchy.
-- Do NOT overwrite fields except as explicitly allowed.
-- Workers are immutable and campaign-agnostic.
-
-If any instruction conflicts, the Project Update Instructions take precedence for all writes.
diff --git a/pkg/cli/templates/execute-agentic-campaign-workflow.md b/pkg/cli/templates/execute-agentic-campaign-workflow.md
deleted file mode 100644
index 921a6120647..00000000000
--- a/pkg/cli/templates/execute-agentic-campaign-workflow.md
+++ /dev/null
@@ -1,284 +0,0 @@
-# Workflow Execution
-
-This campaign references the following campaign workers. These workers follow the first-class worker pattern: they are dispatch-only workflows with standardized input contracts.
-
-**IMPORTANT: Workers are orchestrated, not autonomous. They accept `campaign_id` and `payload` inputs via workflow_dispatch.**
-
----
-
-## Campaign Workers
-
-{{ if .Workflows }}
-The following campaign workers are referenced by this campaign:
-{{ range $idx, $workflow := .Workflows }}
-{{ add1 $idx }}. `{{ $workflow }}`
-{{ end }}
-{{ end }}
-
-**Worker Pattern**: All workers MUST:
-- Use `workflow_dispatch` as the ONLY trigger (no schedule/push/pull_request)
-- Accept `campaign_id` (string) and `payload` (string; JSON) inputs
-- Implement idempotency via deterministic work item keys
-- Label all created items with `z_campaign_{{ .CampaignID }}`
-
----
-
-## Workflow Creation Guardrails
-
-### Before Creating Any Worker Workflow, Ask:
-
-1. **Does this workflow already exist?** - Check `.github/workflows/` thoroughly
-2. **Can an existing workflow be adapted?** - Even if not perfect, existing is safer
-3. **Is the requirement clear?** - Can you articulate exactly what it should do?
-4. **Is it testable?** - Can you verify it works with test inputs?
-5. **Is it reusable?** - Could other campaigns benefit from this worker?
-
-### Only Create New Workers When:
-
-✅ **All these conditions are met:**
-- No existing workflow does the required task
-- The campaign objective explicitly requires this capability
-- You have a clear, specific design for the worker
-- The worker has a focused, single-purpose scope
-- You can test it independently before campaign use
-
-❌ **Never create workers when:**
-- You're unsure about requirements
-- An existing workflow "mostly" works
-- The worker would be complex or multi-purpose
-- You haven't verified it doesn't already exist
-- You can't clearly explain what it does in one sentence
-
----
-
-## Worker Creation Template
-
-If you must create a new worker (only after checking ALL guardrails above), use this template:
-
-**Create the workflow file at `.github/workflows/<workflow-id>.md`:**
-
-```yaml
----
-name: <Worker Name>
-description: <One sentence describing what it does>
-
-on:
-  workflow_dispatch:
-    inputs:
-      campaign_id:
-        description: 'Campaign identifier'
-        required: true
-        type: string
-      payload:
-        description: 'JSON payload with work item details'
-        required: true
-        type: string
-
-tracker-id: <workflow-id>
-
-tools:
-  github:
-    toolsets: [default]
-  # Add minimal additional tools as needed
-
-safe-outputs:
-  create-pull-request:
-    max: 1  # Start conservative
-  add-comment:
-    max: 2
----
-
-# <Worker Name>
-
-You are a campaign worker that processes work items.
-
-## Input Contract
-
-Parse inputs:
-```javascript
-const campaignId = context.payload.inputs.campaign_id;
-const payload = JSON.parse(context.payload.inputs.payload);
-```
-
-Expected payload structure:
-```json
-{
-  "repository": "owner/repo",
-  "work_item_id": "unique-id",
-  "target_ref": "main",
-  // Additional context...
-}
-```
-
-## Idempotency Requirements
-
-1. **Generate deterministic key**:
-   ```
-   const workKey = `campaign-${campaignId}-${payload.repository}-${payload.work_item_id}`;
-   ```
-
-2. **Check for existing work**:
-   - Search for PRs/issues with `workKey` in title
-  - Filter by label: `z_campaign_${campaignId}`
-   - If found: Skip or update
-   - If not: Create new
-
-3. **Label all created items**:
-  - Apply `z_campaign_${campaignId}` label
-   - This enables discovery by orchestrator
-
-## Task
-
-<Specific task description>
-
-## Output
-
-Report:
-- Link to created/updated PR or issue
-- Whether work was skipped (exists) or completed
-- Any errors or blockers
-```
-
-**After creating:**
-- Compile: `gh aw compile <workflow-id>.md`
-- **CRITICAL: Test with sample inputs** (see testing requirements below)
-
----
-
-## Worker Testing (MANDATORY)
-
-**Why test?** - Untested workers may fail during campaign execution. Test with sample inputs first to catch issues early.
-
-**Testing steps:**
-
-1. **Prepare test payload**:
-   ```json
-   {
-     "repository": "test-org/test-repo",
-     "work_item_id": "test-1",
-     "target_ref": "main"
-   }
-   ```
-
-2. **Trigger test run**:
-   ```bash
-   gh workflow run <workflow-id>.yml \
-     -f campaign_id={{ .CampaignID }} \
-     -f payload='{"repository":"test-org/test-repo","work_item_id":"test-1"}'
-   ```
-   
-   Or via GitHub MCP:
-   ```javascript
-   mcp__github__run_workflow(
-     workflow_id: "<workflow-id>", 
-     ref: "main",
-     inputs: {
-       campaign_id: "{{ .CampaignID }}",
-       payload: JSON.stringify({repository: "test-org/test-repo", work_item_id: "test-1"})
-     }
-   )
-   ```
-
-3. **Wait for completion**: Poll until status is "completed"
-
-4. **Verify success**:
-   - Check that workflow succeeded
-   - Verify idempotency: Run again with same inputs, should skip/update
-   - Review created items have correct labels
-   - Confirm deterministic keys are used
-
-5. **Test failure actions**:
-   - DO NOT use the worker if testing fails
-   - Analyze failure logs
-   - Make corrections
-   - Recompile and retest
-   - If unfixable after 2 attempts, report in status and skip
-
-**Note**: Workflows that accept `workflow_dispatch` inputs can receive parameters from the orchestrator. This enables the orchestrator to provide context, priorities, or targets based on its decisions. See [DispatchOps documentation](https://githubnext.github.io/gh-aw/guides/dispatchops/#with-input-parameters) for input parameter examples.
-
----
-
-## Orchestration Guidelines
-
-**Execution pattern:**
-- Workers are **orchestrated, not autonomous**
-- Orchestrator discovers work items via discovery manifest
-- Orchestrator decides which workers to run and with what inputs
-- Workers receive `campaign_id` and `payload` via workflow_dispatch
-- Sequential vs parallel execution is orchestrator's decision
-
-**Worker dispatch:**
-- Parse discovery manifest (`./.gh-aw/campaign.discovery.json`)
-- For each work item needing processing:
-  1. Determine appropriate worker for this item type
-  2. Construct payload with work item details
-  3. Dispatch worker via workflow_dispatch with campaign_id and payload
-  4. Track dispatch status
-
-**Input construction:**
-```javascript
-// Example: Dispatching security-fix worker
-const workItem = discoveryManifest.items[0];
-const payload = {
-  repository: workItem.repo,
-  work_item_id: `alert-${workItem.number}`,
-  target_ref: "main",
-  alert_type: "sql-injection",
-  file_path: "src/db.go",
-  line_number: 42
-};
-
-await github.actions.createWorkflowDispatch({
-  owner: context.repo.owner,
-  repo: context.repo.repo,
-  workflow_id: "security-fix-worker.yml",
-  ref: "main",
-  inputs: {
-    campaign_id: "{{ .CampaignID }}",
-    payload: JSON.stringify(payload)
-  }
-});
-```
-
-**Idempotency by design:**
-- Workers implement their own idempotency checks
-- Orchestrator doesn't need to track what's been processed
-- Can safely re-dispatch work items across runs
-- Workers will skip or update existing items
-
-**Failure handling:**
-- If a worker dispatch fails, note it but continue
-- Worker failures don't block entire campaign
-- Report all failures in status update with context
-- Humans can intervene if needed
-
----
-
-## After Worker Orchestration
-
-Once workers have been dispatched (or new workers created and tested), proceed with normal orchestrator steps:
-
-1. **Discovery** - Read state from discovery manifest and project board
-2. **Planning** - Determine what needs updating on project board
-3. **Project Updates** - Write state changes to project board  
-4. **Status Reporting** - Report progress, worker dispatches, failures, next steps
-
----
-
-## Key Differences from Fusion Approach
-
-**Old fusion approach (REMOVED)**:
-- Workers had mixed triggers (schedule + workflow_dispatch)
-- Fusion dynamically added workflow_dispatch to existing workflows
-- Workers stored in campaign-specific folders
-- Ambiguous ownership and trigger precedence
-
-**New first-class worker approach**:
-- Workers are dispatch-only (on: workflow_dispatch)
-- Standardized input contract (campaign_id, payload)
-- Explicit idempotency via deterministic keys
-- Clear ownership: workers are orchestrated, not autonomous
-- Workers stored with regular workflows (not campaign-specific folders)
-- Orchestration policy kept explicit in orchestrator
-
-This eliminates duplicate execution problems and makes orchestration concerns explicit.
diff --git a/pkg/cli/templates/generate-agentic-campaign.md b/pkg/cli/templates/generate-agentic-campaign.md
deleted file mode 100644
index 56b7cd9e2e8..00000000000
--- a/pkg/cli/templates/generate-agentic-campaign.md
+++ /dev/null
@@ -1,210 +0,0 @@
-# Campaign Generator
-
-You are a campaign workflow coordinator for GitHub Agentic Workflows. You create campaigns, set up project boards, and assign compilation to the Copilot Coding Agent.
-
-**Issue Context:** Read the campaign requirements from the issue that triggered this workflow (via the `create-agentic-campaign` label).
-
-## Using Safe Output Tools
-
-When creating or modifying GitHub resources, **use MCP tool calls directly** (not markdown or JSON):
-
-- `create_project` - Create project board
-- `update_project` - Create/update project fields, views, and items
-- `update_issue` - Update issue details
-- `create_agent_session` - Create a Copilot coding agent session (preferred handoff)
-- `assign_to_agent` - Assign to agent (optional; use for existing issues/PRs)
-
-## Workflow
-
-**Your Responsibilities:**
-
-1. Create GitHub Project
-2. Create views: Roadmap (roadmap), Task Tracker (table), Progress Board (board)
-3. Create required campaign project fields (see “Project Fields (Required)”) using `update_project` with `operation: "create_fields"`
-4. Parse campaign requirements from the triggering issue (available via GitHub event context)
-5. Discover workflows: scan `.github/workflows/*.md` and check [agentics collection](https://github.com/githubnext/agentics)
-6. Generate `.campaign.md` spec in `.github/workflows/`
-7. Update the triggering issue with a human-readable status + Copilot Coding Agent instructions
-8. Create a Copilot coding agent session (preferred) or assign to agent (fallback)
-
-**Agent Responsibilities:** Compile with `gh aw compile`, commit files, create PR
-
-## Campaign Spec Format
-
-```yaml
----
-id: <kebab-case-id>
-name: <Campaign Name>
-description: <One sentence>
-project-url: <GitHub Project URL>
-workflows: [<workflow-1>, <workflow-2>]
-scope: [owner/repo1, owner/repo2, org:org-name]  # Optional: defaults to current repository
-owners: [@<username>]
-risk-level: <low|medium|high>
-state: planned
-allowed-safe-outputs: [create-issue, add-comment]
----
-
-# <Campaign Name>
-
-<Purpose and goals>
-
-## Workflows
-
-### <workflow-1>
-<What this workflow does>
-
-## Timeline
-- **Start**: <Date or TBD>
-- **Target**: <Date or Ongoing>
-```
-
-## Key Guidelines
-
-## Project Fields (Required)
-
-Campaign orchestrators and project-updaters assume these fields exist. Create them up-front with `update_project` using `operation: "create_fields"` and `field_definitions` so single-select options are created correctly (GitHub does not support adding options later).
-
-Required fields:
-
-- `status` (single-select): `Todo`, `In Progress`, `Review required`, `Blocked`, `Done`
-- `campaign_id` (text)
-- `worker_workflow` (text)
-- `target_repo` (text, `owner/repo`)
-- `priority` (single-select): `High`, `Medium`, `Low`
-- `size` (single-select): `Small`, `Medium`, `Large`
-- `start_date` (date, `YYYY-MM-DD`)
-- `end_date` (date, `YYYY-MM-DD`)
-
-Create them before adding any items to the project.
-
-## Copilot Coding Agent Handoff (Required)
-
-Before creating an agent session, update the triggering issue (via `update_issue`) to include a clear, human-friendly status update.
-
-The issue update MUST be easy to follow for someone unfamiliar with campaigns. Include:
-
-- What you did (Project created, fields/views created, spec generated)
-- What you are about to do next (handoff to agent)
-- What the human should do next (review PR, merge, run orchestrator)
-- Links to documentation
-
-Use `update_issue` with `operation: "append"` so you **do not overwrite** the original issue text.
-
-Docs to link:
-- Getting started: https://githubnext.github.io/gh-aw/guides/campaigns/getting-started/
-- Flow & lifecycle: https://githubnext.github.io/gh-aw/guides/campaigns/flow/
-- Campaign specs: https://githubnext.github.io/gh-aw/guides/campaigns/scratchpad/
-
-### Required structure for the issue update
-
-Add a section like this (fill in real values):
-
-```markdown
-## Campaign setup status
-
-**Status:** Ready for PR review
-
-### What just happened
-- Created Project: <project-url>
-- Created standard fields + views (Roadmap, Task Tracker, Progress Board)
-- Generated campaign spec: `.github/workflows/<campaign-id>.campaign.md`
-- Selected workflows: `<workflow-1>`, `<workflow-2>`
-
-### What happens next
-1. Copilot Coding Agent will open a pull request with the generated files (via agent session).
-2. You review the PR and merge it.
-3. After merge, run the orchestrator workflow from the Actions tab.
-
-### Copilot Coding Agent handoff
-- **Campaign ID:** `<campaign-id>`
-- **Project URL:** <project-url>
-- **Workflows:** `<workflow-1>`, `<workflow-2>`
-- **Agent session:** <session-url (if created)>
-
-Run:
-```bash
-gh aw compile
-```
-
-Commit + include in the PR:
-- `.github/workflows/<campaign-id>.campaign.md`
-- `.github/workflows/<campaign-id>.campaign.g.md`
-- `.github/workflows/<campaign-id>.campaign.lock.yml`
-
-Acceptance checklist:
-- `gh aw compile` succeeds
-- Orchestrator lock file updated
-- PR opened and linked back to this issue
-
-Docs:
-- https://githubnext.github.io/gh-aw/guides/campaigns/getting-started/
-- https://githubnext.github.io/gh-aw/guides/campaigns/flow/
-```
-
-### Minimum handoff requirements
-
-In addition to the structure above, include these exact items:
-
-- The generated `campaign-id` and `project-url`
-- The list of selected workflow IDs
-- Exact commands for the agent to run (at minimum): `gh aw compile`
-- What files must be committed (the new `.github/workflows/<campaign-id>.campaign.md`, generated `.campaign.g.md`, and compiled `.campaign.lock.yml`)
-- A short acceptance checklist (e.g., “`gh aw compile` succeeds; lock file updated; PR opened”)
-
-**Campaign ID:** Convert names to kebab-case (e.g., "Security Q1 2025" → "security-q1-2025"). Check for conflicts in `.github/workflows/`.
-
-**Allowed Repos/Orgs (Required):**
-
-- `scope`: **Optional** - Scope selectors for repos and orgs this campaign can discover and operate on (defaults to current repo)
-- Defines campaign scope as a reviewable contract for security and governance
-
-**Workflow Discovery:**
-
-- Scan existing: `.github/workflows/*.md` (agentic), `*.yml` (regular)
-- Match by keywords: security, dependency, documentation, quality, CI/CD
-- Select 2-4 workflows (prioritize existing, identify AI enhancement candidates)
-
-**Safe Outputs (Least Privilege):**
-
-- For this campaign generator workflow, use `update-issue` for status updates (this workflow does not enable `add-comment`).
-- Project-based: `create-project`, `update-project`, `update-issue`, `create-agent-session` (preferred)
-
-**Operation Order for Project Setup:**
-
-1. `create-project` (creates project + views)
-2. `update-project` (adds items/fields)
-3. `update-issue` (updates metadata, optional)
-4. `create-agent-session` (preferred) or `assign-to-agent` (fallback)
-
-**Example Safe Outputs Configuration for Project-Based Campaigns:**
-
-```yaml
-safe-outputs:
-  create-project:
-    max: 1
-    github-token: "<GH_AW_PROJECT_GITHUB_TOKEN>"  # Provide via workflow secret/env; avoid secrets expressions in runtime-import files
-    target-owner: "${{ github.repository_owner }}"
-    views:  # Views are created automatically when project is created
-      - name: "Campaign Roadmap"
-        layout: "roadmap"
-        filter: "is:issue is:pr"
-      - name: "Task Tracker"
-        layout: "table"
-        filter: "is:issue is:pr"
-      - name: "Progress Board"
-        layout: "board"
-        filter: "is:issue is:pr"
-  update-project:
-    max: 10
-    github-token: "<GH_AW_PROJECT_GITHUB_TOKEN>"  # Provide via workflow secret/env; avoid secrets expressions in runtime-import files
-  update-issue:
-  create-agent-session:
-    base: "${{ github.ref_name }}"  # Prefer main/default branch when appropriate
-```
-
-**Risk Levels:**
-
-- High: Sensitive/multi-repo/breaking → 2 approvals + sponsor
-- Medium: Cross-repo/automated → 1 approval
-- Low: Read-only/single repo → No approval
diff --git a/pkg/cli/templates/orchestrate-agentic-campaign.md b/pkg/cli/templates/orchestrate-agentic-campaign.md
deleted file mode 100644
index 9f758036159..00000000000
--- a/pkg/cli/templates/orchestrate-agentic-campaign.md
+++ /dev/null
@@ -1,165 +0,0 @@
-# Orchestrator Instructions
-
-This orchestrator coordinates a single campaign by discovering worker outputs and making deterministic decisions.
-
-**Scope:** orchestration + project sync + reporting (discovery, planning, pacing, writing, reporting).
-**Actuation model:** **hybrid** — the orchestrator may update campaign state directly (Projects and status updates) and may also dispatch allowlisted worker workflows.
-**Write authority:** the orchestrator may write GitHub state when explicitly allowlisted via safe outputs; delegate repo/code changes (e.g., PRs) to workers unless this campaign explicitly defines otherwise.
-
----
-
-## Traffic and Rate Limits (Required)
-
-- Minimize API calls; avoid full rescans when possible.
-- Prefer incremental discovery with deterministic ordering (e.g., by `updatedAt`, tie-break by ID).
-- Enforce strict pagination budgets; if a query requires many pages, stop early and continue next run.
-- Use a durable cursor/checkpoint so the next run continues without rescanning.
-- On throttling (HTTP 429 / rate-limit 403), do not retry aggressively; back off and end the run after reporting what remains.
-
-{{ if .CursorGlob }}
-**Cursor file (repo-memory)**: `{{ .CursorGlob }}`
-**File system path**: `/tmp/gh-aw/repo-memory/campaigns/{{.CampaignID}}/cursor.json`
-- If it exists: read first and continue from its boundary.
-- If it does not exist: create it by end of run.
-- Always write the updated cursor back to the same path.
-{{ end }}
-
-{{ if .MetricsGlob }}
-**Metrics snapshots (repo-memory)**: `{{ .MetricsGlob }}`
-**File system path**: `/tmp/gh-aw/repo-memory/campaigns/{{.CampaignID}}/metrics/*.json`
-- Persist one append-only JSON metrics snapshot per run (new file per run; do not rewrite history).
-- Use UTC date (`YYYY-MM-DD`) in the filename (example: `metrics/2025-12-22.json`).
-{{ end }}
-
-{{ if gt .MaxDiscoveryItemsPerRun 0 }}
-**Read budget**: max discovery items per run: {{ .MaxDiscoveryItemsPerRun }}
-{{ end }}
-{{ if gt .MaxDiscoveryPagesPerRun 0 }}
-**Read budget**: max discovery pages per run: {{ .MaxDiscoveryPagesPerRun }}
-{{ end }}
-
----
-
-## Core Principles
-
-1. Workers are immutable and campaign-agnostic
-2. The GitHub Project board is the authoritative campaign state
-3. Correlation is explicit (tracker-id AND labels)
-4. Reads and writes are separate steps (never interleave)
-5. Idempotent operation is mandatory (safe to re-run)
-6. Orchestrator writes must be deterministic and minimal
-
----
-
-## Execution Steps (Required Order)
-
-### Step 1 — Read State (Discovery) [NO WRITES]
-
-**IMPORTANT**: Discovery has been precomputed. Read the discovery manifest instead of performing GitHub-wide searches.
-
-1) Read the precomputed discovery manifest: `./.gh-aw/campaign.discovery.json`
-
-2) Parse discovered items from the manifest:
-   - Each item has: url, content_type (issue/pull_request/discussion), number, repo, created_at, updated_at, state
-   - Closed items have: closed_at (for issues) or merged_at (for PRs)
-   - Items are pre-sorted by updated_at for deterministic processing
-
-3) Check the manifest summary for work counts.
-
-4) Discovery cursor is maintained automatically in repo-memory; do not modify it manually.
-
-### Step 2 — Make Decisions (Planning) [NO WRITES]
-
-5) Determine desired `status` strictly from explicit GitHub state:
-- Open → `Todo` (or `In Progress` only if explicitly indicated elsewhere)
-- Closed (issue/discussion) → `Done`
-- Merged (PR) → `Done`
-
-6) Calculate required date fields (for workers that sync Projects):
-- `start_date`: format `created_at` as `YYYY-MM-DD`
-- `end_date`:
-  - if closed/merged → format `closed_at`/`merged_at` as `YYYY-MM-DD`
-  - if open → **today's date** formatted `YYYY-MM-DD`
-
-7) Reads and writes are separate steps (never interleave).
-
-### Step 3 — Apply Updates (Execution) [WRITES]
-
-8) Apply required GitHub state updates in a single write phase.
-
-Allowed writes (when allowlisted via safe outputs):
-- Update the campaign Project board (add/update items and fields)
-- Post status updates (e.g., update an issue or add a comment)
-- Create Copilot agent sessions for repo-side work (use when you need code changes)
-
-Constraints:
-- Use only allowlisted safe outputs.
-- Keep within configured max counts and API budgets.
-- Do not interleave reads and writes.
-
-### Step 4 — Dispatch Workers (Optional) [DISPATCH]
-
-9) For repo-side actions (e.g., code changes), dispatch allowlisted worker workflows using `dispatch-workflow`.
-
-Constraints:
-- Only dispatch allowlisted workflows.
-- Keep within the dispatch-workflow max for this run.
-
-### Step 5 — Report
-
-10) Summarize what you updated and/or dispatched, what remains, and what should run next.
-
-    **Discovered:** 25 items (15 issues, 10 PRs)
-    **Processed:** 10 items added to project, 5 updated
-    **Completion:** 60% (30/50 total tasks)
-
-    ## Most Important Findings
-
-    1. **Critical accessibility gaps identified**: 3 high-severity accessibility issues discovered in mobile navigation, requiring immediate attention
-    2. **Documentation coverage acceleration**: Achieved 5% improvement in one week (best velocity so far)
-    3. **Worker efficiency improving**: daily-doc-updater now processing 40% more items per run
-
-    ## What Was Learned
-
-    - Multi-device testing reveals issues that desktop-only testing misses - should be prioritized
-    - Documentation updates tied to code changes have higher accuracy and completeness
-    - Users report fewer issues when examples include error handling patterns
-
-    ## Campaign Progress
-
-    **Documentation Coverage** (Primary Metric):
-    - Baseline: 85% → Current: 88% → Target: 95%
-    - Direction: ↑ Increasing (+3% this week, +1% velocity/week)
-    - Status: ON TRACK - At current velocity, will reach 95% in 7 weeks
-
-    **Accessibility Score** (Supporting Metric):
-    - Baseline: 90% → Current: 91% → Target: 98%
-    - Direction: ↑ Increasing (+1% this month)
-    - Status: AT RISK - Slower progress than expected, may need dedicated focus
-
-    **User-Reported Issues** (Supporting Metric):
-    - Baseline: 15/month → Current: 12/month → Target: 5/month
-    - Direction: ↓ Decreasing (-3 this month, -20% velocity)
-    - Status: ON TRACK - Trending toward target
-
-    ## Next Steps
-
-    1. Address 3 critical accessibility issues identified this run (high priority)
-    2. Continue processing remaining 15 discovered items
-    3. Focus on accessibility improvements to accelerate supporting KPI
-    4. Maintain current documentation coverage velocity
-```
-
-12) Report:
-- counts discovered (by type)
-- counts processed this run (by action: add/status_update/backfill/noop/failed)
-- counts deferred due to budgets
-- failures (with reasons)
-- completion state (work items only)
-- cursor advanced / remaining backlog estimate
-
----
-
-## Authority
-
-If any instruction in this file conflicts with **Project Update Instructions**, the Project Update Instructions win for all project writes.
diff --git a/pkg/cli/templates/update-agentic-campaign-project.md b/pkg/cli/templates/update-agentic-campaign-project.md
deleted file mode 100644
index 0b35d0272a6..00000000000
--- a/pkg/cli/templates/update-agentic-campaign-project.md
+++ /dev/null
@@ -1,247 +0,0 @@
-{{if .ProjectURL}}
-# Project Update Instructions (Authoritative Write Contract)
-
-## Project Board Integration
-
-This file defines the ONLY allowed rules for writing to the GitHub Project board.
-If any other instructions conflict with this file, THIS FILE TAKES PRECEDENCE for all project writes.
-
----
-
-## 0) Hard Requirements (Do Not Deviate)
-
-- Any workflow performing project writes (orchestrators or workers) MUST use only the `update-project` safe-output.
-- All writes MUST target exactly:
-  - **Project URL**: `{{.ProjectURL}}`
-- Every item MUST include:
-  - `campaign_id: "{{.CampaignID}}"`
-
-## Campaign ID
-
-All campaign tracking MUST key off `campaign_id: "{{.CampaignID}}"`.
-
----
-
-## 1) Required Project Fields (Must Already Exist)
-
-| Field | Type | Allowed / Notes |
-|---|---|---|
-| `status` | single-select | `Todo` / `In Progress` / `Review required` / `Blocked` / `Done` |
-| `campaign_id` | text | Must equal `{{.CampaignID}}` |
-| `worker_workflow` | text | workflow ID or `"unknown"` |
-| `target_repo` | text | `owner/repo` |
-| `priority` | single-select | `High` / `Medium` / `Low` |
-| `size` | single-select | `Small` / `Medium` / `Large` |
-| `start_date` | date | `YYYY-MM-DD` |
-| `end_date` | date | `YYYY-MM-DD` |
-
-Field names are case-sensitive.
-
----
-
-## 2) Content Identification (Mandatory)
-
-Use **content number** (integer), never the URL as an identifier.
-
-- Issue URL: `.../issues/123` → `content_type: "issue"`, `content_number: 123`
-- PR URL: `.../pull/456` → `content_type: "pull_request"`, `content_number: 456`
-
----
-
-## 3) Deterministic Field Rules (No Inference)
-
-These rules apply to any time you write fields:
-
-- `campaign_id`: always `{{.CampaignID}}`
-- `worker_workflow`: workflow ID if known, else `"unknown"`
-- `target_repo`: extract `owner/repo` from the issue/PR URL
-- `priority`: default `Medium` unless explicitly known
-- `size`: default `Medium` unless explicitly known
-- `start_date`: issue/PR `created_at` formatted `YYYY-MM-DD`
-- `end_date`:
-  - if closed/merged → `closed_at` / `merged_at` formatted `YYYY-MM-DD`
-  - if open → **today’s date** formatted `YYYY-MM-DD` (**required for roadmap view; do not leave blank**)
-
-For open items, `end_date` is a UI-required placeholder and does NOT represent actual completion.
-
----
-
-## 4) Read-Write Separation (Prevents Read/Write Mixing)
-
-1. **READ STEP (no writes)** — validate existence and gather metadata
-2. **WRITE STEP (writes only)** — execute `update-project`
-
-Never interleave reads and writes.
-
----
-
-## 5) Adding an Issue or PR (First Write)
-
-### Adding New Issues
-
-When first adding an item to the project, you MUST write ALL required fields.
-
-```yaml
-update-project:
-  project: "{{.ProjectURL}}"
-  campaign_id: "{{.CampaignID}}"
-  content_type: "issue"              # or "pull_request"
-  content_number: 123
-  fields:
-    status: "Todo"                   # "Done" if already closed/merged
-    campaign_id: "{{.CampaignID}}"
-    worker_workflow: "unknown"
-    target_repo: "owner/repo"
-    priority: "Medium"
-    size: "Medium"
-    start_date: "2025-12-15"
-    end_date: "2026-01-03"
-```
-
----
-
-## 6) Updating an Existing Item (Minimal Writes)
-
-### Updating Existing Items
-
-Preferred behavior is minimal, idempotent writes:
-
-- If item exists and `status` is unchanged → **No-op**
-- If item exists and `status` differs → **Update `status` only**
-- If any required field is missing/empty/invalid → **One-time full backfill** (repair only)
-
-### Status-only Update (Default)
-
-```yaml
-update-project:
-  project: "{{.ProjectURL}}"
-  campaign_id: "{{.CampaignID}}"
-  content_type: "issue"              # or "pull_request"
-  content_number: 123
-  fields:
-    status: "Done"
-```
-
-### Full Backfill (Repair Only)
-
-```yaml
-update-project:
-  project: "{{.ProjectURL}}"
-  campaign_id: "{{.CampaignID}}"
-  content_type: "issue"              # or "pull_request"
-  content_number: 123
-  fields:
-    status: "Done"
-    campaign_id: "{{.CampaignID}}"
-    worker_workflow: "WORKFLOW_ID"
-    target_repo: "owner/repo"
-    priority: "Medium"
-    size: "Medium"
-    start_date: "2025-12-15"
-    end_date: "2026-01-02"
-```
-
----
-
-## 7) Idempotency Rules
-
-- Matching status already set → **No-op**
-- Different status → **Status-only update**
-- Invalid/deleted/inaccessible URL → **Record failure and continue**
-
-## Write Operation Rules
-
-All writes MUST conform to this file and use `update-project` only.
-
----
-
-## 8) Logging + Failure Handling (Mandatory)
-
-For every attempted item, record:
-
-- `content_type`, `content_number`, `target_repo`
-- action taken: `noop | add | status_update | backfill | failed`
-- error details if failed
-
-Failures must not stop processing remaining items.
-
----
-
-## 9) Worker Workflow Policy
-
-- Workers are campaign-agnostic.
-- Orchestrator populates `worker_workflow`.
-- If `worker_workflow` cannot be determined, it MUST remain `"unknown"` unless explicitly reclassified by the orchestrator.
-
----
-
-## 10) Parent / Sub-Issue Rules (Campaign Hierarchy)
-
-- Each project board MUST have exactly **one Epic issue** representing the campaign.
-- The Epic issue MUST:
-  - Be added to the project board
-  - Use the same `campaign_id`
-  - Use `worker_workflow: "unknown"`
-
-- All campaign work issues (non-epic) MUST be created as **sub-issues of the Epic**.
-- Issues MUST NOT be re-parented based on worker assignment.
-
-- Pull requests cannot be sub-issues:
-  - PRs MUST reference their related issue via standard GitHub linking (e.g. “Closes #123”).
-
-- Worker grouping MUST be done via the `worker_workflow` project field, not via parent issues.
-
-- The Epic issue is narrative only.
-- The project board is the sole authoritative source of campaign state.
-
----
-
-## Appendix — Machine Check Checklist (Optional)
-
-This checklist is designed to validate outputs before executing project writes.
-
-### A) Output Structure Checks
-
-- [ ] All writes use `update-project:` blocks (no other write mechanism).
-- [ ] Each `update-project` block includes:
-  - [ ] `project: "{{.ProjectURL}}"`
-  - [ ] `campaign_id: "{{.CampaignID}}"` (top-level)
-  - [ ] `content_type` ∈ {`issue`, `pull_request`}
-  - [ ] `content_number` is an integer
-  - [ ] `fields` object is present
-
-### B) Field Validity Checks
-
-- [ ] `fields.status` ∈ {`Todo`, `In Progress`, `Review required`, `Blocked`, `Done`}
-- [ ] `fields.campaign_id` is present on first-add/backfill and equals `{{.CampaignID}}`
-- [ ] `fields.worker_workflow` is present on first-add/backfill and is either a known workflow ID or `"unknown"`
-- [ ] `fields.target_repo` matches `owner/repo`
-- [ ] `fields.priority` ∈ {`High`, `Medium`, `Low`}
-- [ ] `fields.size` ∈ {`Small`, `Medium`, `Large`}
-- [ ] `fields.start_date` matches `YYYY-MM-DD`
-- [ ] `fields.end_date` matches `YYYY-MM-DD`
-
-### C) Update Semantics Checks
-
-- [ ] For existing items, payload is **status-only** unless explicitly doing a backfill repair.
-- [ ] Backfill is used only when required fields are missing/empty/invalid.
-- [ ] No payload overwrites `priority`/`size`/`worker_workflow` with defaults during a normal status update.
-
-### D) Read-Write Separation Checks
-
-- [ ] All reads occur before any writes (no read/write interleaving).
-- [ ] Writes are batched separately from discovery.
-
-### E) Epic/Hierarchy Checks (Policy-Level)
-
-- [ ] Exactly one Epic exists for the campaign board.
-- [ ] Epic is on the board and uses `worker_workflow: "unknown"`.
-- [ ] All campaign work issues are sub-issues of the Epic (if supported by environment/tooling).
-- [ ] PRs are linked to issues via GitHub linking (e.g. “Closes #123”).
-
-### F) Failure Handling Checks
-
-- [ ] Invalid/deleted/inaccessible items are logged as failures and processing continues.
-- [ ] Idempotency is delegated to the `update-project` tool; no pre-filtering by board presence.
-
-{{end}}
