diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml index 6b60bde99a..a07eff355a 100644 --- a/.github/workflows/agent-performance-analyzer.lock.yml +++ b/.github/workflows/agent-performance-analyzer.lock.yml @@ -1277,7 +1277,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml index 8b5e9aa558..8e2393c7d0 100644 --- a/.github/workflows/agent-persona-explorer.lock.yml +++ b/.github/workflows/agent-persona-explorer.lock.yml @@ -1214,7 +1214,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/audit-workflows.lock.yml b/.github/workflows/audit-workflows.lock.yml index 7b07e1ea80..8e13876688 100644 --- a/.github/workflows/audit-workflows.lock.yml +++ b/.github/workflows/audit-workflows.lock.yml @@ -1297,7 +1297,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1430,7 +1430,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/ci-coach.lock.yml b/.github/workflows/ci-coach.lock.yml index 34b57a60b5..77e1c37856 100644 --- a/.github/workflows/ci-coach.lock.yml +++ b/.github/workflows/ci-coach.lock.yml @@ -1239,7 +1239,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml index 97c9b48aea..fb0d2539f2 100644 --- a/.github/workflows/ci-doctor.lock.yml +++ b/.github/workflows/ci-doctor.lock.yml @@ -1386,7 +1386,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/claude-code-user-docs-review.lock.yml b/.github/workflows/claude-code-user-docs-review.lock.yml index 2e2b5520d4..a519cf484b 100644 --- a/.github/workflows/claude-code-user-docs-review.lock.yml +++ b/.github/workflows/claude-code-user-docs-review.lock.yml @@ -1175,7 +1175,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml index 181dafb461..7d204c2c77 100644 --- a/.github/workflows/cli-version-checker.lock.yml +++ b/.github/workflows/cli-version-checker.lock.yml @@ -1201,7 +1201,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/cloclo.lock.yml b/.github/workflows/cloclo.lock.yml index 24e7a25645..669616be85 100644 --- a/.github/workflows/cloclo.lock.yml +++ b/.github/workflows/cloclo.lock.yml @@ -1598,7 +1598,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/code-scanning-fixer.lock.yml b/.github/workflows/code-scanning-fixer.lock.yml index 06c466a035..d779ffbc2d 100644 --- a/.github/workflows/code-scanning-fixer.lock.yml +++ b/.github/workflows/code-scanning-fixer.lock.yml @@ -1192,7 +1192,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1352,7 +1352,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/copilot-agent-analysis.lock.yml b/.github/workflows/copilot-agent-analysis.lock.yml index 9a0a4e5e0f..9098bec8ae 100644 --- a/.github/workflows/copilot-agent-analysis.lock.yml +++ b/.github/workflows/copilot-agent-analysis.lock.yml @@ -1169,7 +1169,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1301,7 +1301,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/copilot-cli-deep-research.lock.yml b/.github/workflows/copilot-cli-deep-research.lock.yml index 797bd9bb89..dc6e3c6228 100644 --- a/.github/workflows/copilot-cli-deep-research.lock.yml +++ b/.github/workflows/copilot-cli-deep-research.lock.yml @@ -1087,7 +1087,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/.github/workflows/copilot-pr-merged-report.lock.yml b/.github/workflows/copilot-pr-merged-report.lock.yml index 2bd196cd9f..1a5594e120 100644 --- a/.github/workflows/copilot-pr-merged-report.lock.yml +++ b/.github/workflows/copilot-pr-merged-report.lock.yml @@ -1260,7 +1260,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/copilot-pr-nlp-analysis.lock.yml b/.github/workflows/copilot-pr-nlp-analysis.lock.yml index f677e8e16e..a1746ecbfe 100644 --- a/.github/workflows/copilot-pr-nlp-analysis.lock.yml +++ b/.github/workflows/copilot-pr-nlp-analysis.lock.yml @@ -1183,7 +1183,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1315,7 +1315,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/copilot-pr-prompt-analysis.lock.yml b/.github/workflows/copilot-pr-prompt-analysis.lock.yml index b02f6ce795..e494ad837b 100644 --- a/.github/workflows/copilot-pr-prompt-analysis.lock.yml +++ b/.github/workflows/copilot-pr-prompt-analysis.lock.yml @@ -1107,7 +1107,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1239,7 +1239,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/copilot-session-insights.lock.yml b/.github/workflows/copilot-session-insights.lock.yml index 0f6f613945..6d743bf800 100644 --- a/.github/workflows/copilot-session-insights.lock.yml +++ b/.github/workflows/copilot-session-insights.lock.yml @@ -1248,7 +1248,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1380,7 +1380,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml index 6b04296258..6daa16e748 100644 --- a/.github/workflows/daily-cli-performance.lock.yml +++ b/.github/workflows/daily-cli-performance.lock.yml @@ -1277,7 +1277,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/.github/workflows/daily-code-metrics.lock.yml b/.github/workflows/daily-code-metrics.lock.yml index b3d631d23e..5c50f08258 100644 --- a/.github/workflows/daily-code-metrics.lock.yml +++ b/.github/workflows/daily-code-metrics.lock.yml @@ -1226,7 +1226,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1359,7 +1359,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-compiler-quality.lock.yml b/.github/workflows/daily-compiler-quality.lock.yml index 3ad3b061f0..6fa0282a16 100644 --- a/.github/workflows/daily-compiler-quality.lock.yml +++ b/.github/workflows/daily-compiler-quality.lock.yml @@ -1149,7 +1149,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-copilot-token-report.lock.yml b/.github/workflows/daily-copilot-token-report.lock.yml index 3fdf273929..66538ddc91 100644 --- a/.github/workflows/daily-copilot-token-report.lock.yml +++ b/.github/workflows/daily-copilot-token-report.lock.yml @@ -1194,7 +1194,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1327,7 +1327,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-doc-updater.lock.yml b/.github/workflows/daily-doc-updater.lock.yml index 8d8f9dd494..c23b256268 100644 --- a/.github/workflows/daily-doc-updater.lock.yml +++ b/.github/workflows/daily-doc-updater.lock.yml @@ -1264,7 +1264,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-firewall-report.lock.yml b/.github/workflows/daily-firewall-report.lock.yml index bea723f005..e6b9cbe762 100644 --- a/.github/workflows/daily-firewall-report.lock.yml +++ b/.github/workflows/daily-firewall-report.lock.yml @@ -1256,7 +1256,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-issues-report.lock.yml b/.github/workflows/daily-issues-report.lock.yml index 7a7674e74b..10b8054634 100644 --- a/.github/workflows/daily-issues-report.lock.yml +++ b/.github/workflows/daily-issues-report.lock.yml @@ -1305,7 +1305,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml index 5e3b4fc521..2c81fe1f70 100644 --- a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml +++ b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml @@ -1213,7 +1213,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-news.lock.yml b/.github/workflows/daily-news.lock.yml index 52c1067270..b9d64a43a3 100644 --- a/.github/workflows/daily-news.lock.yml +++ b/.github/workflows/daily-news.lock.yml @@ -1256,7 +1256,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1389,7 +1389,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-performance-summary.lock.yml b/.github/workflows/daily-performance-summary.lock.yml index 9e81507527..362aebb661 100644 --- a/.github/workflows/daily-performance-summary.lock.yml +++ b/.github/workflows/daily-performance-summary.lock.yml @@ -1737,7 +1737,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-rendering-scripts-verifier.lock.yml b/.github/workflows/daily-rendering-scripts-verifier.lock.yml index dc3afa5a57..76d7dcc74b 100644 --- a/.github/workflows/daily-rendering-scripts-verifier.lock.yml +++ b/.github/workflows/daily-rendering-scripts-verifier.lock.yml @@ -1394,7 +1394,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-repo-chronicle.lock.yml b/.github/workflows/daily-repo-chronicle.lock.yml index eed552b7b3..b3ecda1238 100644 --- a/.github/workflows/daily-repo-chronicle.lock.yml +++ b/.github/workflows/daily-repo-chronicle.lock.yml @@ -1191,7 +1191,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml index beff45446f..b855c39708 100644 --- a/.github/workflows/daily-safe-output-optimizer.lock.yml +++ b/.github/workflows/daily-safe-output-optimizer.lock.yml @@ -1334,7 +1334,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml index b0adb64bbc..c1b821d0ae 100644 --- a/.github/workflows/daily-testify-uber-super-expert.lock.yml +++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml @@ -1170,7 +1170,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml index 9415fcea20..db878619b1 100644 --- a/.github/workflows/deep-report.lock.yml +++ b/.github/workflows/deep-report.lock.yml @@ -1295,7 +1295,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1428,7 +1428,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml index 23ef1a3d1e..dd1dfd4d2c 100644 --- a/.github/workflows/delight.lock.yml +++ b/.github/workflows/delight.lock.yml @@ -1172,7 +1172,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/.github/workflows/developer-docs-consolidator.lock.yml b/.github/workflows/developer-docs-consolidator.lock.yml index 3a279438c5..c05a72a729 100644 --- a/.github/workflows/developer-docs-consolidator.lock.yml +++ b/.github/workflows/developer-docs-consolidator.lock.yml @@ -1344,7 +1344,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/discussion-task-miner.lock.yml b/.github/workflows/discussion-task-miner.lock.yml index f1f5947d83..7e9d933149 100644 --- a/.github/workflows/discussion-task-miner.lock.yml +++ b/.github/workflows/discussion-task-miner.lock.yml @@ -1153,7 +1153,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/.github/workflows/firewall-escape.lock.yml b/.github/workflows/firewall-escape.lock.yml index 3ff52bd8ff..08661a2945 100644 --- a/.github/workflows/firewall-escape.lock.yml +++ b/.github/workflows/firewall-escape.lock.yml @@ -1166,7 +1166,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: @@ -1298,7 +1298,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/github-mcp-structural-analysis.lock.yml b/.github/workflows/github-mcp-structural-analysis.lock.yml index 50fc308b9c..528b4908b1 100644 --- a/.github/workflows/github-mcp-structural-analysis.lock.yml +++ b/.github/workflows/github-mcp-structural-analysis.lock.yml @@ -1248,7 +1248,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/github-mcp-tools-report.lock.yml b/.github/workflows/github-mcp-tools-report.lock.yml index f3e680bea0..37f8a6f520 100644 --- a/.github/workflows/github-mcp-tools-report.lock.yml +++ b/.github/workflows/github-mcp-tools-report.lock.yml @@ -1301,7 +1301,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/glossary-maintainer.lock.yml b/.github/workflows/glossary-maintainer.lock.yml index b94ef238a3..a4c7e3d031 100644 --- a/.github/workflows/glossary-maintainer.lock.yml +++ b/.github/workflows/glossary-maintainer.lock.yml @@ -1232,7 +1232,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml index 6457728b45..42c310895b 100644 --- a/.github/workflows/go-fan.lock.yml +++ b/.github/workflows/go-fan.lock.yml @@ -1219,7 +1219,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/go-logger.lock.yml b/.github/workflows/go-logger.lock.yml index 6f369d4cde..b2a2308cf0 100644 --- a/.github/workflows/go-logger.lock.yml +++ b/.github/workflows/go-logger.lock.yml @@ -1428,7 +1428,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/gpclean.lock.yml b/.github/workflows/gpclean.lock.yml index 7e343def11..8b417fb168 100644 --- a/.github/workflows/gpclean.lock.yml +++ b/.github/workflows/gpclean.lock.yml @@ -1128,7 +1128,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/grumpy-reviewer.lock.yml b/.github/workflows/grumpy-reviewer.lock.yml index c96fd80842..4bac6306e9 100644 --- a/.github/workflows/grumpy-reviewer.lock.yml +++ b/.github/workflows/grumpy-reviewer.lock.yml @@ -1268,7 +1268,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/instructions-janitor.lock.yml b/.github/workflows/instructions-janitor.lock.yml index 913eed7b95..eae51da56a 100644 --- a/.github/workflows/instructions-janitor.lock.yml +++ b/.github/workflows/instructions-janitor.lock.yml @@ -1256,7 +1256,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/jsweep.lock.yml b/.github/workflows/jsweep.lock.yml index 0f118dbf7d..eb30bb1276 100644 --- a/.github/workflows/jsweep.lock.yml +++ b/.github/workflows/jsweep.lock.yml @@ -1196,7 +1196,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/lockfile-stats.lock.yml b/.github/workflows/lockfile-stats.lock.yml index cd94860773..08c8a8e44a 100644 --- a/.github/workflows/lockfile-stats.lock.yml +++ b/.github/workflows/lockfile-stats.lock.yml @@ -1174,7 +1174,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/mcp-inspector.lock.yml b/.github/workflows/mcp-inspector.lock.yml index eee7dc265e..aaac07f878 100644 --- a/.github/workflows/mcp-inspector.lock.yml +++ b/.github/workflows/mcp-inspector.lock.yml @@ -1774,7 +1774,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/metrics-collector.lock.yml b/.github/workflows/metrics-collector.lock.yml index 5d4c40df6b..83cfb633f4 100644 --- a/.github/workflows/metrics-collector.lock.yml +++ b/.github/workflows/metrics-collector.lock.yml @@ -653,7 +653,7 @@ jobs: push_repo_memory: needs: agent if: always() - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/.github/workflows/org-health-report.lock.yml b/.github/workflows/org-health-report.lock.yml index 43d59d4d8a..130af10d46 100644 --- a/.github/workflows/org-health-report.lock.yml +++ b/.github/workflows/org-health-report.lock.yml @@ -1189,7 +1189,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/pdf-summary.lock.yml b/.github/workflows/pdf-summary.lock.yml index d38bc6e6aa..975d5d2321 100644 --- a/.github/workflows/pdf-summary.lock.yml +++ b/.github/workflows/pdf-summary.lock.yml @@ -1287,7 +1287,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml index 68544e9609..76c9b24c77 100644 --- a/.github/workflows/poem-bot.lock.yml +++ b/.github/workflows/poem-bot.lock.yml @@ -1928,7 +1928,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/portfolio-analyst.lock.yml b/.github/workflows/portfolio-analyst.lock.yml index 00575ebc0c..28d84e305d 100644 --- a/.github/workflows/portfolio-analyst.lock.yml +++ b/.github/workflows/portfolio-analyst.lock.yml @@ -1267,7 +1267,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/pr-nitpick-reviewer.lock.yml b/.github/workflows/pr-nitpick-reviewer.lock.yml index 3cb0218469..85178b12f3 100644 --- a/.github/workflows/pr-nitpick-reviewer.lock.yml +++ b/.github/workflows/pr-nitpick-reviewer.lock.yml @@ -1365,7 +1365,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml index a1d281e9db..0bd72881a0 100644 --- a/.github/workflows/pr-triage-agent.lock.yml +++ b/.github/workflows/pr-triage-agent.lock.yml @@ -1155,7 +1155,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/.github/workflows/prompt-clustering-analysis.lock.yml b/.github/workflows/prompt-clustering-analysis.lock.yml index 2c48f7411f..26048ccda6 100644 --- a/.github/workflows/prompt-clustering-analysis.lock.yml +++ b/.github/workflows/prompt-clustering-analysis.lock.yml @@ -1305,7 +1305,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/python-data-charts.lock.yml b/.github/workflows/python-data-charts.lock.yml index d7c80468a1..a334dc9e09 100644 --- a/.github/workflows/python-data-charts.lock.yml +++ b/.github/workflows/python-data-charts.lock.yml @@ -1252,7 +1252,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/q.lock.yml b/.github/workflows/q.lock.yml index 20900388ae..e05b8eb9ed 100644 --- a/.github/workflows/q.lock.yml +++ b/.github/workflows/q.lock.yml @@ -1448,7 +1448,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/repo-audit-analyzer.lock.yml b/.github/workflows/repo-audit-analyzer.lock.yml index bd293dd7e8..9cc436affc 100644 --- a/.github/workflows/repo-audit-analyzer.lock.yml +++ b/.github/workflows/repo-audit-analyzer.lock.yml @@ -1129,7 +1129,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/repository-quality-improver.lock.yml b/.github/workflows/repository-quality-improver.lock.yml index 941cd6cf91..afdc673650 100644 --- a/.github/workflows/repository-quality-improver.lock.yml +++ b/.github/workflows/repository-quality-improver.lock.yml @@ -1131,7 +1131,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/safe-output-health.lock.yml b/.github/workflows/safe-output-health.lock.yml index 4a9a4b2828..744478fe50 100644 --- a/.github/workflows/safe-output-health.lock.yml +++ b/.github/workflows/safe-output-health.lock.yml @@ -1267,7 +1267,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/schema-consistency-checker.lock.yml b/.github/workflows/schema-consistency-checker.lock.yml index 008758bc27..d224c857af 100644 --- a/.github/workflows/schema-consistency-checker.lock.yml +++ b/.github/workflows/schema-consistency-checker.lock.yml @@ -1175,7 +1175,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/scout.lock.yml b/.github/workflows/scout.lock.yml index cbccebf720..a83e323b03 100644 --- a/.github/workflows/scout.lock.yml +++ b/.github/workflows/scout.lock.yml @@ -1391,7 +1391,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml index ab14c1a9a7..00fe5944b4 100644 --- a/.github/workflows/security-compliance.lock.yml +++ b/.github/workflows/security-compliance.lock.yml @@ -1101,7 +1101,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/.github/workflows/security-review.lock.yml b/.github/workflows/security-review.lock.yml index fd5358a5b0..8ab025bc91 100644 --- a/.github/workflows/security-review.lock.yml +++ b/.github/workflows/security-review.lock.yml @@ -1339,7 +1339,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml index 825e29f1df..20bcc7ee7e 100644 --- a/.github/workflows/sergo.lock.yml +++ b/.github/workflows/sergo.lock.yml @@ -1218,7 +1218,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/slide-deck-maintainer.lock.yml b/.github/workflows/slide-deck-maintainer.lock.yml index e98e5d77fb..2c31850b04 100644 --- a/.github/workflows/slide-deck-maintainer.lock.yml +++ b/.github/workflows/slide-deck-maintainer.lock.yml @@ -1299,7 +1299,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml index 9c9be178be..57e3ae3fbb 100644 --- a/.github/workflows/smoke-claude.lock.yml +++ b/.github/workflows/smoke-claude.lock.yml @@ -2810,7 +2810,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml index 0e5f7f0dda..47fc015fac 100644 --- a/.github/workflows/smoke-codex.lock.yml +++ b/.github/workflows/smoke-codex.lock.yml @@ -1682,7 +1682,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml index 5b0f068311..4e73002108 100644 --- a/.github/workflows/smoke-copilot-arm.lock.yml +++ b/.github/workflows/smoke-copilot-arm.lock.yml @@ -54,7 +54,7 @@ jobs: if: > (needs.pre_activation.outputs.activated == 'true') && (((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.id == github.repository_id)) && ((github.event_name != 'pull_request') || ((github.event.action != 'labeled') || (github.event.label.name == 'water')))) - runs-on: ubuntu-slim + runs-on: ubuntu-24.04-arm permissions: contents: read discussions: write @@ -1855,7 +1855,7 @@ jobs: - send_slack_message - update_cache_memory if: (always()) && (needs.agent.result != 'skipped') - runs-on: ubuntu-slim + runs-on: ubuntu-24.04-arm permissions: actions: write contents: read @@ -2084,7 +2084,7 @@ jobs: if: > ((github.event_name != 'pull_request') || (github.event.pull_request.head.repo.id == github.repository_id)) && ((github.event_name != 'pull_request') || ((github.event.action != 'labeled') || (github.event.label.name == 'water'))) - runs-on: ubuntu-slim + runs-on: ubuntu-24.04-arm permissions: contents: read discussions: write @@ -2135,7 +2135,7 @@ jobs: - agent - detection if: ((!cancelled()) && (needs.agent.result != 'skipped')) && (needs.detection.outputs.success == 'true') - runs-on: ubuntu-slim + runs-on: ubuntu-24.04-arm permissions: actions: write contents: read @@ -2239,7 +2239,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-24.04-arm permissions: contents: read env: diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml index 2765c48e22..24725b6427 100644 --- a/.github/workflows/smoke-copilot.lock.yml +++ b/.github/workflows/smoke-copilot.lock.yml @@ -2241,7 +2241,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml index fbc22a4cfe..2a72c59d7d 100644 --- a/.github/workflows/smoke-gemini.lock.yml +++ b/.github/workflows/smoke-gemini.lock.yml @@ -1423,7 +1423,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml index 5d4c4393b8..061a2d05ef 100644 --- a/.github/workflows/stale-repo-identifier.lock.yml +++ b/.github/workflows/stale-repo-identifier.lock.yml @@ -1256,7 +1256,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml index da12f59fe2..55c6e42250 100644 --- a/.github/workflows/static-analysis-report.lock.yml +++ b/.github/workflows/static-analysis-report.lock.yml @@ -1249,7 +1249,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/step-name-alignment.lock.yml b/.github/workflows/step-name-alignment.lock.yml index 26ae6d43ad..9f3839d6ee 100644 --- a/.github/workflows/step-name-alignment.lock.yml +++ b/.github/workflows/step-name-alignment.lock.yml @@ -1200,7 +1200,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml index 48e19a468a..b41a4e41da 100644 --- a/.github/workflows/super-linter.lock.yml +++ b/.github/workflows/super-linter.lock.yml @@ -1189,7 +1189,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/technical-doc-writer.lock.yml b/.github/workflows/technical-doc-writer.lock.yml index 686a2133d0..cdf4a14fce 100644 --- a/.github/workflows/technical-doc-writer.lock.yml +++ b/.github/workflows/technical-doc-writer.lock.yml @@ -1302,7 +1302,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/test-create-pr-error-handling.lock.yml b/.github/workflows/test-create-pr-error-handling.lock.yml index f410f9c07c..a3a162efd9 100644 --- a/.github/workflows/test-create-pr-error-handling.lock.yml +++ b/.github/workflows/test-create-pr-error-handling.lock.yml @@ -1230,7 +1230,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/unbloat-docs.lock.yml b/.github/workflows/unbloat-docs.lock.yml index ad1f76e605..21fe1b4d64 100644 --- a/.github/workflows/unbloat-docs.lock.yml +++ b/.github/workflows/unbloat-docs.lock.yml @@ -1515,7 +1515,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/weekly-issue-summary.lock.yml b/.github/workflows/weekly-issue-summary.lock.yml index 1646222d20..89de7c6236 100644 --- a/.github/workflows/weekly-issue-summary.lock.yml +++ b/.github/workflows/weekly-issue-summary.lock.yml @@ -1165,7 +1165,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: read env: diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml index a5740ac7e7..0e92931144 100644 --- a/.github/workflows/workflow-health-manager.lock.yml +++ b/.github/workflows/workflow-health-manager.lock.yml @@ -1273,7 +1273,7 @@ jobs: - agent - detection if: always() && needs.detection.outputs.success == 'true' - runs-on: ubuntu-latest + runs-on: ubuntu-slim permissions: contents: write outputs: diff --git a/docs/src/content/docs/guides/self-hosted-runners.md b/docs/src/content/docs/guides/self-hosted-runners.md new file mode 100644 index 0000000000..c52567ef8e --- /dev/null +++ b/docs/src/content/docs/guides/self-hosted-runners.md @@ -0,0 +1,130 @@ +--- +title: Self-Hosted Runners Guide +description: Learn how to route all generated workflow jobs to self-hosted runners to avoid GitHub-hosted minutes consumption. +sidebar: + order: 460 +--- + +GitHub Agentic Workflows generates several jobs in addition to the main AI agent job (activation, pre-activation, safe-outputs, detection, cache-memory, repo-memory). By default these support jobs run on GitHub-hosted runners (`ubuntu-slim` or `ubuntu-latest`). This guide explains how to run all generated jobs on self-hosted runners with a single frontmatter entry. + +## Quick Start + +Set `runs-on` in the workflow frontmatter to route **every generated job** to your self-hosted runners: + +```aw wrap +--- +on: + issues: + types: [opened] +runs-on: self-hosted +--- + +# My Workflow + +Analyze the issue and respond. +``` + +The `runs-on` value is inherited by all generated jobs: + +| Job | Runner | +|-----|--------| +| `pre_activation` | `self-hosted` | +| `activation` | `self-hosted` | +| `agent` | `self-hosted` | +| `safe_outputs` | `self-hosted` | +| `detection` *(if threat detection enabled)* | `self-hosted` | +| `update_cache_memory` *(if cache-memory enabled)* | `self-hosted` | +| `push_repo_memory` *(if repo-memory enabled)* | `self-hosted` | +| `unlock` *(if locking enabled)* | `self-hosted` | + +## Multi-Label Runners + +Use an array of labels to target runners that match all labels: + +```aw wrap +--- +on: + issues: + types: [opened] +runs-on: + - self-hosted + - linux + - x64 +--- + +# My Workflow + +Analyze the issue and respond. +``` + +## Separate Runners for Agent and Support Jobs + +For cost optimization, you can run the compute-intensive agent job on powerful self-hosted hardware while keeping lightweight support jobs on GitHub-hosted runners: + +```aw wrap +--- +on: + issues: + types: [opened] +runs-on: [self-hosted, heavy] # Agent job uses powerful self-hosted runner +safe-outputs: + runs-on: ubuntu-slim # Support jobs use lightweight GitHub-hosted runner + create-issue: + title-prefix: "[ai] " +--- + +# My Workflow + +Analyze the issue and respond. +``` + +### Precedence Order + +1. **`safe-outputs.runs-on`** – explicit override for all support jobs +2. **`runs-on`** (top-level) – inherited by all jobs when `safe-outputs.runs-on` is not set +3. **`ubuntu-slim`** – built-in default for support jobs when neither is set + +## Runner Group Configuration + +Use an object to target a specific runner group: + +```aw wrap +--- +on: + issues: + types: [opened] +runs-on: + group: my-runner-group + labels: + - ubuntu-latest +--- + +# My Workflow + +Analyze the issue and respond. +``` + +## Requirements + +Self-hosted runners used by GitHub Agentic Workflows must meet these requirements: + +- **Linux only** – macOS runners are not supported because the Agent Workflow Firewall requires Docker containers, which macOS runners do not support. See [FAQ](/gh-aw/reference/faq/#why-are-macos-runners-not-supported). +- **Docker** – the runner must have Docker installed and the user running the workflow must be able to run Docker commands without `sudo`. +- **GitHub Actions runner software** – the standard Actions runner software must be installed and registered. + +## FAQ + +### Why don't macOS runners work? + +macOS GitHub-hosted runners do not support Docker container jobs, which are required for the Agent Workflow Firewall sandbox. Use a Linux self-hosted runner instead. + +### Can I set different runners for specific jobs? + +Not for individual generated jobs, but you can split agent vs. support jobs using `runs-on` and `safe-outputs.runs-on` as shown in the [Separate Runners](#separate-runners-for-agent-and-support-jobs) section above. + +Custom jobs defined in `safe-outputs.jobs` can each specify their own `runs-on`. + +### What happens if I don't set runs-on? + +- The agent job defaults to `ubuntu-latest`. +- Support jobs default to `ubuntu-slim` (a lightweight 1-vCPU GitHub-hosted runner). diff --git a/docs/src/content/docs/reference/frontmatter.md b/docs/src/content/docs/reference/frontmatter.md index 6760d1e982..7f01712612 100644 --- a/docs/src/content/docs/reference/frontmatter.md +++ b/docs/src/content/docs/reference/frontmatter.md @@ -465,10 +465,22 @@ Standard GitHub Actions properties: ```yaml wrap run-name: "Custom workflow run name" # Defaults to workflow name -runs-on: ubuntu-latest # Defaults to ubuntu-latest (main job only) +runs-on: ubuntu-latest # Applies to ALL jobs (agent + all support jobs) timeout-minutes: 30 # Defaults to 20 minutes ``` +When `runs-on` is set, it applies to **every generated job** – the agent job and all support jobs +(activation, pre-activation, safe-outputs, detection, cache-memory, repo-memory). +This is the single entry point for routing all jobs to self-hosted runners. + +To use different runners for the agent job and support jobs, combine `runs-on` with `safe-outputs.runs-on`: + +```yaml wrap +runs-on: [self-hosted, heavy] # Agent job uses powerful self-hosted runner +safe-outputs: + runs-on: ubuntu-slim # Support jobs use the lightweight hosted runner +``` + **Supported runners for `runs-on:`** | Runner | Status | @@ -476,6 +488,7 @@ timeout-minutes: 30 # Defaults to 20 minutes | `ubuntu-latest` | ✅ Default. Recommended for most workflows. | | `ubuntu-24.04` / `ubuntu-22.04` | ✅ Supported. | | `ubuntu-24.04-arm` | ✅ Supported. Linux ARM64 runner. | +| `self-hosted` / `[self-hosted, linux]` | ✅ Supported. Applies to all jobs. See [self-hosted runners guide](/gh-aw/guides/self-hosted-runners/). | | `macos-*` | ❌ Not supported. Docker is unavailable on macOS runners (no nested virtualization). See [FAQ](/gh-aw/reference/faq/). | | `windows-*` | ❌ Not supported. AWF requires Linux. | diff --git a/docs/src/content/docs/reference/safe-outputs.md b/docs/src/content/docs/reference/safe-outputs.md index 89d9527347..8055245494 100644 --- a/docs/src/content/docs/reference/safe-outputs.md +++ b/docs/src/content/docs/reference/safe-outputs.md @@ -1289,7 +1289,14 @@ safe-outputs: ### Custom Runner Image -Specify custom runner for safe output jobs (default: `ubuntu-slim`): `runs-on: ubuntu-22.04` +Specify a custom runner for all support jobs (default: inherits from top-level `runs-on`, otherwise `ubuntu-slim`): + +```yaml +safe-outputs: + runs-on: ubuntu-22.04 +``` + +This overrides the top-level `runs-on` for support jobs only. To route **all** jobs (agent + support) to self-hosted runners, set `runs-on` at the top level instead. See the [Self-Hosted Runners Guide](/gh-aw/guides/self-hosted-runners/) for details. ### Custom Messages (`messages:`) diff --git a/pkg/parser/schemas/main_workflow_schema.json b/pkg/parser/schemas/main_workflow_schema.json index d46a72dbb7..29ede975e4 100644 --- a/pkg/parser/schemas/main_workflow_schema.json +++ b/pkg/parser/schemas/main_workflow_schema.json @@ -1840,7 +1840,7 @@ } }, "runs-on": { - "description": "Runner type for workflow execution (GitHub Actions standard field). Supports multiple forms: simple string for single runner label (e.g., 'ubuntu-latest'), array for runner selection with fallbacks, or object for GitHub-hosted runner groups with specific labels. For agentic workflows, runner selection matters when AI workloads require specific compute resources or when using self-hosted runners with specialized capabilities. Typically configured at the job level instead. See https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job", + "description": "Runner type for all workflow jobs. When set, this value is used for the agent job AND all support jobs (activation, pre-activation, safe-outputs, detection, cache-memory, repo-memory). This is the single entry point for routing every generated job to self-hosted runners. Use safe-outputs.runs-on to override support jobs independently. Supports multiple forms: simple string (e.g., 'self-hosted'), array for runner selection with fallbacks, or object for GitHub-hosted runner groups. See https://docs.github.com/en/actions/using-jobs/choosing-the-runner-for-a-job", "oneOf": [ { "type": "string", @@ -6922,7 +6922,7 @@ }, "runs-on": { "type": "string", - "description": "Runner specification for all safe-outputs jobs (activation, create-issue, add-comment, etc.). Single runner label (e.g., 'ubuntu-slim', 'ubuntu-latest', 'windows-latest', 'self-hosted'). Defaults to 'ubuntu-slim'. See https://github.blog/changelog/2025-10-28-1-vcpu-linux-runner-now-available-in-github-actions-in-public-preview/" + "description": "Runner override for support jobs (activation, pre-activation, safe-outputs, detection, cache-memory, repo-memory). Takes precedence over the top-level runs-on for these jobs. Single runner label (e.g., 'ubuntu-slim', 'ubuntu-latest', 'windows-latest', 'self-hosted'). When not set, inherits from the top-level runs-on; when top-level is also not set, defaults to 'ubuntu-slim'. See https://github.blog/changelog/2025-10-28-1-vcpu-linux-runner-now-available-in-github-actions-in-public-preview/" } }, "additionalProperties": false diff --git a/pkg/workflow/cache.go b/pkg/workflow/cache.go index 6d5b9c13df..879c512f80 100644 --- a/pkg/workflow/cache.go +++ b/pkg/workflow/cache.go @@ -839,7 +839,7 @@ func (c *Compiler) buildUpdateCacheMemoryJob(data *WorkflowData, threatDetection job := &Job{ Name: "update_cache_memory", DisplayName: "", // No display name - job ID is sufficient - RunsOn: "runs-on: ubuntu-latest", + RunsOn: c.formatSafeOutputsRunsOn(data), If: jobCondition, Permissions: permissions, Needs: []string{"agent", "detection"}, diff --git a/pkg/workflow/compiler_activation_jobs.go b/pkg/workflow/compiler_activation_jobs.go index e2b3f7502a..64e8c5dbf3 100644 --- a/pkg/workflow/compiler_activation_jobs.go +++ b/pkg/workflow/compiler_activation_jobs.go @@ -348,7 +348,7 @@ func (c *Compiler) buildPreActivationJob(data *WorkflowData, needsPermissionChec job := &Job{ Name: string(constants.PreActivationJobName), If: jobIfCondition, - RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), + RunsOn: c.formatSafeOutputsRunsOn(data), Permissions: permissions, Steps: steps, Outputs: outputs, @@ -728,7 +728,7 @@ func (c *Compiler) buildActivationJob(data *WorkflowData, preActivationJobCreate Name: string(constants.ActivationJobName), If: activationCondition, HasWorkflowRunSafetyChecks: workflowRunRepoSafety != "", // Mark job as having workflow_run safety checks - RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), + RunsOn: c.formatSafeOutputsRunsOn(data), Permissions: permissions, Environment: environment, Steps: steps, diff --git a/pkg/workflow/compiler_orchestrator_workflow.go b/pkg/workflow/compiler_orchestrator_workflow.go index aa1f1720d0..c2e7476b6d 100644 --- a/pkg/workflow/compiler_orchestrator_workflow.go +++ b/pkg/workflow/compiler_orchestrator_workflow.go @@ -209,6 +209,7 @@ func (c *Compiler) extractYAMLSections(frontmatter map[string]any, workflowData workflowData.TimeoutMinutes = c.extractTopLevelYAMLSection(frontmatter, "timeout-minutes") workflowData.RunsOn = c.extractTopLevelYAMLSection(frontmatter, "runs-on") + workflowData.RunsOnExplicit = workflowData.RunsOn != "" workflowData.Environment = c.extractTopLevelYAMLSection(frontmatter, "environment") workflowData.Container = c.extractTopLevelYAMLSection(frontmatter, "container") workflowData.Cache = c.extractTopLevelYAMLSection(frontmatter, "cache") diff --git a/pkg/workflow/compiler_safe_outputs_job.go b/pkg/workflow/compiler_safe_outputs_job.go index 6d35201fc5..b8f1168f1b 100644 --- a/pkg/workflow/compiler_safe_outputs_job.go +++ b/pkg/workflow/compiler_safe_outputs_job.go @@ -333,7 +333,7 @@ func (c *Compiler) buildConsolidatedSafeOutputsJob(data *WorkflowData, mainJobNa job := &Job{ Name: "safe_outputs", If: jobCondition.Render(), - RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), + RunsOn: c.formatSafeOutputsRunsOn(data), Permissions: permissions.RenderToYAML(), TimeoutMinutes: 15, // Slightly longer timeout for consolidated job with multiple steps Env: jobEnv, diff --git a/pkg/workflow/compiler_types.go b/pkg/workflow/compiler_types.go index cdd43bf97e..3fdd789dea 100644 --- a/pkg/workflow/compiler_types.go +++ b/pkg/workflow/compiler_types.go @@ -412,6 +412,7 @@ type WorkflowData struct { CustomSteps string PostSteps string // steps to run after AI execution RunsOn string + RunsOnExplicit bool // true when runs-on was explicitly set in frontmatter (not just defaulted) Environment string // environment setting for the main job Container string // container setting for the main job Services string // services setting for the main job diff --git a/pkg/workflow/notify_comment.go b/pkg/workflow/notify_comment.go index 9d5dda4326..15d3214f65 100644 --- a/pkg/workflow/notify_comment.go +++ b/pkg/workflow/notify_comment.go @@ -385,7 +385,7 @@ func (c *Compiler) buildConclusionJob(data *WorkflowData, mainJobName string, sa job := &Job{ Name: "conclusion", If: condition.Render(), - RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), + RunsOn: c.formatSafeOutputsRunsOn(data), Permissions: permissions.RenderToYAML(), Steps: steps, Needs: needs, diff --git a/pkg/workflow/repo_memory.go b/pkg/workflow/repo_memory.go index ee0d84d536..9b962371d9 100644 --- a/pkg/workflow/repo_memory.go +++ b/pkg/workflow/repo_memory.go @@ -730,7 +730,7 @@ func (c *Compiler) buildPushRepoMemoryJob(data *WorkflowData, threatDetectionEna job := &Job{ Name: "push_repo_memory", DisplayName: "", // No display name - job ID is sufficient - RunsOn: "runs-on: ubuntu-latest", + RunsOn: c.formatSafeOutputsRunsOn(data), If: jobCondition, Permissions: "permissions:\n contents: write", Needs: []string{"agent"}, // Detection dependency added by caller if needed diff --git a/pkg/workflow/safe_jobs.go b/pkg/workflow/safe_jobs.go index efcd5bc16c..e1e24e9d1d 100644 --- a/pkg/workflow/safe_jobs.go +++ b/pkg/workflow/safe_jobs.go @@ -199,7 +199,7 @@ func (c *Compiler) buildSafeJobs(data *WorkflowData, threatDetectionEnabled bool } } } else { - job.RunsOn = "runs-on: ubuntu-latest" // Default + job.RunsOn = c.formatSafeOutputsRunsOn(data) // Default inherits from safe-outputs.runs-on or top-level runs-on } // Set if condition - combine safe output type check with user-provided condition diff --git a/pkg/workflow/safe_outputs_config_helpers.go b/pkg/workflow/safe_outputs_config_helpers.go index 47305d412c..9a6a1f04ad 100644 --- a/pkg/workflow/safe_outputs_config_helpers.go +++ b/pkg/workflow/safe_outputs_config_helpers.go @@ -112,13 +112,30 @@ func getEnabledSafeOutputToolNamesReflection(safeOutputs *SafeOutputsConfig) []s return tools } -// formatSafeOutputsRunsOn formats the runs-on value from SafeOutputsConfig for job output -func (c *Compiler) formatSafeOutputsRunsOn(safeOutputs *SafeOutputsConfig) string { - if safeOutputs == nil || safeOutputs.RunsOn == "" { - return "runs-on: " + constants.DefaultActivationJobRunnerImage - } - - return "runs-on: " + safeOutputs.RunsOn +// formatSafeOutputsRunsOn formats the runs-on value for support jobs (activation, +// pre-activation, safe-outputs, detection, cache-memory, repo-memory, etc.). +// +// Resolution order: +// 1. safe-outputs.runs-on (explicit per-workflow override for support jobs) +// 2. top-level runs-on (inherited when safe-outputs.runs-on is not set) +// 3. DefaultActivationJobRunnerImage ("ubuntu-slim") as the final default +// +// This allows a single top-level runs-on entry to configure all jobs at once, +// while still providing a fine-grained override via safe-outputs.runs-on when +// the agent job and support jobs need different runners. +func (c *Compiler) formatSafeOutputsRunsOn(data *WorkflowData) string { + // 1. Explicit safe-outputs.runs-on takes priority + if data.SafeOutputs != nil && data.SafeOutputs.RunsOn != "" { + return "runs-on: " + data.SafeOutputs.RunsOn + } + + // 2. Inherit from top-level runs-on when explicitly set by the user + if data.RunsOnExplicit && data.RunsOn != "" { + return c.indentYAMLLines(data.RunsOn, " ") + } + + // 3. Fall back to the lightweight default runner for support jobs + return "runs-on: " + constants.DefaultActivationJobRunnerImage } // builtinSafeOutputFields contains the struct field names for the built-in safe output types diff --git a/pkg/workflow/safe_outputs_jobs.go b/pkg/workflow/safe_outputs_jobs.go index 91fe7a2ba1..5a4c4ee534 100644 --- a/pkg/workflow/safe_outputs_jobs.go +++ b/pkg/workflow/safe_outputs_jobs.go @@ -133,7 +133,7 @@ func (c *Compiler) buildSafeOutputJob(data *WorkflowData, config SafeOutputJobCo job := &Job{ Name: config.JobName, If: jobCondition.Render(), - RunsOn: c.formatSafeOutputsRunsOn(data.SafeOutputs), + RunsOn: c.formatSafeOutputsRunsOn(data), Permissions: config.Permissions.RenderToYAML(), TimeoutMinutes: 10, // 10-minute timeout as required for all safe output jobs Steps: steps, diff --git a/pkg/workflow/safe_outputs_runs_on_test.go b/pkg/workflow/safe_outputs_runs_on_test.go index d9bcdee427..6b93fcb4d9 100644 --- a/pkg/workflow/safe_outputs_runs_on_test.go +++ b/pkg/workflow/safe_outputs_runs_on_test.go @@ -153,31 +153,115 @@ This is a test workflow.` } } +// TestTopLevelRunsOnInheritedByAllJobs verifies that setting a top-level runs-on +// causes all support jobs (activation, pre_activation, safe_outputs, detection, +// cache-memory, repo-memory) to use the same runner as the agent job. +func TestTopLevelRunsOnInheritedByAllJobs(t *testing.T) { + frontmatter := `--- +on: push +runs-on: self-hosted +safe-outputs: + create-issue: + title-prefix: "[ai] " +--- + +# Test Workflow + +This is a test workflow.` + + tmpDir := testutil.TempDir(t, "workflow-top-runs-on-test") + testFile := filepath.Join(tmpDir, "test.md") + if err := os.WriteFile(testFile, []byte(frontmatter), 0644); err != nil { + t.Fatal(err) + } + + compiler := NewCompiler() + if err := compiler.CompileWorkflow(testFile); err != nil { + t.Fatalf("Failed to compile workflow: %v", err) + } + + lockFile := filepath.Join(tmpDir, "test.lock.yml") + yamlContent, err := os.ReadFile(lockFile) + if err != nil { + t.Fatalf("Failed to read lock file: %v", err) + } + + yamlStr := string(yamlContent) + expectedRunsOn := "runs-on: self-hosted" + defaultRunsOn := "runs-on: " + constants.DefaultActivationJobRunnerImage + + // All jobs (agent + all support) should use self-hosted + if strings.Contains(yamlStr, defaultRunsOn) { + t.Errorf("Expected no jobs to use default %q when top-level runs-on is set to self-hosted.\nYAML:\n%s", defaultRunsOn, yamlStr) + } + + // At minimum, verify activation and safe_outputs jobs use self-hosted + for _, jobName := range []string{"pre_activation:", "activation:", "safe_outputs:"} { + jobPattern := "\n " + jobName + jobStart := strings.Index(yamlStr, jobPattern) + if jobStart == -1 { + continue // job may not be present (optional) + } + end := min(jobStart+600, len(yamlStr)) + jobSection := yamlStr[jobStart:end] + if !strings.Contains(jobSection, expectedRunsOn) { + t.Errorf("Job %q does not use expected %q when top-level runs-on is self-hosted.\nJob section:\n%s", jobName, expectedRunsOn, jobSection) + } + } +} + func TestFormatSafeOutputsRunsOnEdgeCases(t *testing.T) { compiler := NewCompiler() tests := []struct { name string - safeOutputs *SafeOutputsConfig + data *WorkflowData expectedRunsOn string }{ { name: "nil safe outputs config", - safeOutputs: nil, + data: &WorkflowData{SafeOutputs: nil}, expectedRunsOn: "runs-on: " + constants.DefaultActivationJobRunnerImage, }, { - name: "safe outputs config with nil runs-on", - safeOutputs: &SafeOutputsConfig{ - RunsOn: "", + name: "safe outputs config with empty runs-on", + data: &WorkflowData{ + SafeOutputs: &SafeOutputsConfig{RunsOn: ""}, }, expectedRunsOn: "runs-on: " + constants.DefaultActivationJobRunnerImage, }, + { + name: "inherits from top-level runs-on when safe-outputs.runs-on is unset", + data: &WorkflowData{ + RunsOn: "runs-on: self-hosted", + RunsOnExplicit: true, + SafeOutputs: nil, + }, + expectedRunsOn: "runs-on: self-hosted", + }, + { + name: "does not inherit top-level runs-on when it is just the default", + data: &WorkflowData{ + RunsOn: "runs-on: ubuntu-latest", + RunsOnExplicit: false, // not explicitly set by user + SafeOutputs: nil, + }, + expectedRunsOn: "runs-on: " + constants.DefaultActivationJobRunnerImage, + }, + { + name: "safe-outputs.runs-on overrides top-level runs-on", + data: &WorkflowData{ + RunsOn: "runs-on: ubuntu-latest", + RunsOnExplicit: true, + SafeOutputs: &SafeOutputsConfig{RunsOn: "ubuntu-slim"}, + }, + expectedRunsOn: "runs-on: ubuntu-slim", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - runsOn := compiler.formatSafeOutputsRunsOn(tt.safeOutputs) + runsOn := compiler.formatSafeOutputsRunsOn(tt.data) if runsOn != tt.expectedRunsOn { t.Errorf("Expected runs-on to be %q, got %q", tt.expectedRunsOn, runsOn) } diff --git a/pkg/workflow/safe_outputs_test.go b/pkg/workflow/safe_outputs_test.go index d51eb4b0c0..5c92aa5888 100644 --- a/pkg/workflow/safe_outputs_test.go +++ b/pkg/workflow/safe_outputs_test.go @@ -705,39 +705,44 @@ func TestFormatSafeOutputsRunsOn(t *testing.T) { tests := []struct { name string - safeOutputs *SafeOutputsConfig + data *WorkflowData expectedRunsOn string }{ { name: "nil safe outputs returns default", - safeOutputs: nil, + data: &WorkflowData{SafeOutputs: nil}, expectedRunsOn: "runs-on: " + constants.DefaultActivationJobRunnerImage, }, { name: "empty runs-on returns default", - safeOutputs: &SafeOutputsConfig{RunsOn: ""}, + data: &WorkflowData{SafeOutputs: &SafeOutputsConfig{RunsOn: ""}}, expectedRunsOn: "runs-on: " + constants.DefaultActivationJobRunnerImage, }, { - name: "custom runs-on", - safeOutputs: &SafeOutputsConfig{RunsOn: "ubuntu-latest"}, + name: "custom safe-outputs.runs-on", + data: &WorkflowData{SafeOutputs: &SafeOutputsConfig{RunsOn: "ubuntu-latest"}}, expectedRunsOn: "runs-on: ubuntu-latest", }, { - name: "self-hosted runs-on", - safeOutputs: &SafeOutputsConfig{RunsOn: "self-hosted"}, + name: "self-hosted safe-outputs.runs-on", + data: &WorkflowData{SafeOutputs: &SafeOutputsConfig{RunsOn: "self-hosted"}}, expectedRunsOn: "runs-on: self-hosted", }, { - name: "windows-latest runs-on", - safeOutputs: &SafeOutputsConfig{RunsOn: "windows-latest"}, + name: "windows-latest safe-outputs.runs-on", + data: &WorkflowData{SafeOutputs: &SafeOutputsConfig{RunsOn: "windows-latest"}}, expectedRunsOn: "runs-on: windows-latest", }, + { + name: "inherits top-level runs-on when safe-outputs.runs-on unset", + data: &WorkflowData{RunsOn: "runs-on: self-hosted", RunsOnExplicit: true, SafeOutputs: nil}, + expectedRunsOn: "runs-on: self-hosted", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - result := compiler.formatSafeOutputsRunsOn(tt.safeOutputs) + result := compiler.formatSafeOutputsRunsOn(tt.data) if result != tt.expectedRunsOn { t.Errorf("formatSafeOutputsRunsOn() = %q, want %q", result, tt.expectedRunsOn) }