From a9de0a0874793bb20c76e8c8f29c3df26496b22d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 16:18:59 +0000
Subject: [PATCH 1/8] Initial plan
From 844878bd2230f2a914f1d7feccd1d320d895e154 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 16:26:26 +0000
Subject: [PATCH 2/8] Plan: fix checkout manifest template injection handling
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.github/workflows/ab-testing-advisor.lock.yml | 3 ++-
.github/workflows/ace-editor.lock.yml | 3 ++-
.github/workflows/agent-performance-analyzer.lock.yml | 3 ++-
.github/workflows/agent-persona-explorer.lock.yml | 3 ++-
.github/workflows/agentic-token-audit.lock.yml | 3 ++-
.github/workflows/agentic-token-optimizer.lock.yml | 3 ++-
.github/workflows/agentic-token-trend-audit.lock.yml | 3 ++-
.github/workflows/architecture-guardian.lock.yml | 3 ++-
.github/workflows/aw-failure-investigator.lock.yml | 3 ++-
.github/workflows/bot-detection.lock.yml | 3 ++-
.github/workflows/breaking-change-checker.lock.yml | 3 ++-
.github/workflows/ci-doctor.lock.yml | 3 ++-
.github/workflows/cli-consistency-checker.lock.yml | 3 ++-
.github/workflows/cli-version-checker.lock.yml | 3 ++-
.github/workflows/codex-github-remote-mcp-test.lock.yml | 3 ++-
.github/workflows/contribution-check.lock.yml | 3 ++-
.github/workflows/copilot-opt.lock.yml | 3 ++-
.github/workflows/daily-agentrx-trace-optimizer.lock.yml | 3 ++-
.github/workflows/daily-ambient-context-optimizer.lock.yml | 3 ++-
.github/workflows/daily-architecture-diagram.lock.yml | 3 ++-
.github/workflows/daily-aw-cross-repo-compile-check.lock.yml | 3 ++-
.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml | 3 ++-
.github/workflows/daily-byok-ollama-test.lock.yml | 3 ++-
.github/workflows/daily-cache-strategy-analyzer.lock.yml | 3 ++-
.github/workflows/daily-cli-performance.lock.yml | 3 ++-
.github/workflows/daily-cli-tools-tester.lock.yml | 3 ++-
.github/workflows/daily-community-attribution.lock.yml | 3 ++-
.github/workflows/daily-credit-limit-test.lock.yml | 3 ++-
.github/workflows/daily-doc-healer.lock.yml | 3 ++-
.github/workflows/daily-file-diet.lock.yml | 3 ++-
.github/workflows/daily-formal-spec-verifier.lock.yml | 3 ++-
.github/workflows/daily-function-namer.lock.yml | 3 ++-
.../daily-grafana-otel-instrumentation-advisor.lock.yml | 3 ++-
.github/workflows/daily-max-ai-credits-test.lock.yml | 3 ++-
.github/workflows/daily-mcp-concurrency-analysis.lock.yml | 3 ++-
.github/workflows/daily-model-inventory.lock.yml | 3 ++-
.github/workflows/daily-multi-device-docs-tester.lock.yml | 3 ++-
.github/workflows/daily-otel-instrumentation-advisor.lock.yml | 3 ++-
.github/workflows/daily-reliability-review.lock.yml | 3 ++-
.github/workflows/daily-safe-output-optimizer.lock.yml | 3 ++-
.github/workflows/daily-safe-outputs-conformance.lock.yml | 3 ++-
.github/workflows/daily-safeoutputs-git-simulator.lock.yml | 3 ++-
.github/workflows/daily-security-red-team.lock.yml | 3 ++-
.github/workflows/daily-skill-optimizer.lock.yml | 3 ++-
.github/workflows/daily-spdd-spec-planner.lock.yml | 3 ++-
.github/workflows/daily-syntax-error-quality.lock.yml | 3 ++-
.github/workflows/daily-team-status.lock.yml | 3 ++-
.github/workflows/daily-testify-uber-super-expert.lock.yml | 3 ++-
.github/workflows/daily-token-consumption-report.lock.yml | 3 ++-
.../daily-windows-terminal-integration-builder.lock.yml | 3 ++-
.github/workflows/deep-report.lock.yml | 3 ++-
.github/workflows/delight.lock.yml | 3 ++-
.github/workflows/dependabot-burner.lock.yml | 3 ++-
.github/workflows/dependabot-go-checker.lock.yml | 3 ++-
.github/workflows/deployment-incident-monitor.lock.yml | 3 ++-
.github/workflows/designer-drift-audit.lock.yml | 3 ++-
.github/workflows/dev.lock.yml | 3 ++-
.github/workflows/discussion-task-miner.lock.yml | 3 ++-
.github/workflows/duplicate-code-detector.lock.yml | 3 ++-
.github/workflows/example-permissions-warning.lock.yml | 3 ++-
.github/workflows/firewall.lock.yml | 3 ++-
.github/workflows/go-fan.lock.yml | 3 ++-
.github/workflows/go-pattern-detector.lock.yml | 3 ++-
.github/workflows/gpclean.lock.yml | 3 ++-
.github/workflows/hippo-embed.lock.yml | 3 ++-
.github/workflows/issue-arborist.lock.yml | 3 ++-
.github/workflows/lint-monster.lock.yml | 3 ++-
.github/workflows/metrics-collector.lock.yml | 3 ++-
.github/workflows/objective-impact-report.lock.yml | 3 ++-
.github/workflows/otlp-data-quality-validator.lock.yml | 3 ++-
.github/workflows/outcome-collector.lock.yml | 3 ++-
.github/workflows/plan.lock.yml | 3 ++-
.github/workflows/poem-bot.lock.yml | 3 ++-
.github/workflows/pr-triage-agent.lock.yml | 3 ++-
.github/workflows/refactoring-cadence.lock.yml | 3 ++-
.github/workflows/ruflo-backed-task.lock.yml | 3 ++-
.github/workflows/security-compliance.lock.yml | 3 ++-
.github/workflows/semantic-function-refactor.lock.yml | 3 ++-
.github/workflows/sergo.lock.yml | 3 ++-
.github/workflows/smoke-antigravity.lock.yml | 3 ++-
.github/workflows/smoke-ci.lock.yml | 3 ++-
.github/workflows/smoke-claude.lock.yml | 3 ++-
.github/workflows/smoke-codex.lock.yml | 3 ++-
.github/workflows/smoke-copilot-aoai-apikey.lock.yml | 3 ++-
.github/workflows/smoke-copilot-arm.lock.yml | 3 ++-
.github/workflows/smoke-copilot-sdk.lock.yml | 3 ++-
.github/workflows/smoke-copilot.lock.yml | 3 ++-
.github/workflows/smoke-create-cross-repo-pr.lock.yml | 3 ++-
.github/workflows/smoke-crush.lock.yml | 3 ++-
.github/workflows/smoke-gemini.lock.yml | 3 ++-
.github/workflows/smoke-opencode.lock.yml | 3 ++-
.github/workflows/smoke-otel-backends.lock.yml | 3 ++-
.github/workflows/smoke-pi.lock.yml | 3 ++-
.github/workflows/smoke-project.lock.yml | 3 ++-
.github/workflows/smoke-temporary-id.lock.yml | 3 ++-
.github/workflows/smoke-update-cross-repo-pr.lock.yml | 3 ++-
.github/workflows/smoke-workflow-call-with-inputs.lock.yml | 3 ++-
.github/workflows/spec-librarian.lock.yml | 3 ++-
.github/workflows/stale-repo-identifier.lock.yml | 3 ++-
.github/workflows/static-analysis-report.lock.yml | 3 ++-
.github/workflows/step-name-alignment.lock.yml | 3 ++-
.github/workflows/super-linter.lock.yml | 3 ++-
.github/workflows/test-workflow.lock.yml | 3 ++-
.github/workflows/video-analyzer.lock.yml | 3 ++-
.github/workflows/workflow-health-manager.lock.yml | 3 ++-
.github/workflows/workflow-normalizer.lock.yml | 3 ++-
.github/workflows/workflow-skill-extractor.lock.yml | 3 ++-
107 files changed, 214 insertions(+), 107 deletions(-)
diff --git a/.github/workflows/ab-testing-advisor.lock.yml b/.github/workflows/ab-testing-advisor.lock.yml
index ced4e0eb656..a15eb0f0570 100644
--- a/.github/workflows/ab-testing-advisor.lock.yml
+++ b/.github/workflows/ab-testing-advisor.lock.yml
@@ -552,7 +552,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/ace-editor.lock.yml b/.github/workflows/ace-editor.lock.yml
index 9be6736e082..e69d6e4ae5d 100644
--- a/.github/workflows/ace-editor.lock.yml
+++ b/.github/workflows/ace-editor.lock.yml
@@ -550,7 +550,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/agent-performance-analyzer.lock.yml b/.github/workflows/agent-performance-analyzer.lock.yml
index fdefe1c691f..921540ca4f5 100644
--- a/.github/workflows/agent-performance-analyzer.lock.yml
+++ b/.github/workflows/agent-performance-analyzer.lock.yml
@@ -706,7 +706,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/agent-persona-explorer.lock.yml b/.github/workflows/agent-persona-explorer.lock.yml
index 7a9b7259aae..c6d05fb6172 100644
--- a/.github/workflows/agent-persona-explorer.lock.yml
+++ b/.github/workflows/agent-persona-explorer.lock.yml
@@ -654,7 +654,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/agentic-token-audit.lock.yml b/.github/workflows/agentic-token-audit.lock.yml
index 9df3850acf2..7be5747052b 100644
--- a/.github/workflows/agentic-token-audit.lock.yml
+++ b/.github/workflows/agentic-token-audit.lock.yml
@@ -626,7 +626,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/agentic-token-optimizer.lock.yml b/.github/workflows/agentic-token-optimizer.lock.yml
index fae3d7e51e5..6d61db0c2a5 100644
--- a/.github/workflows/agentic-token-optimizer.lock.yml
+++ b/.github/workflows/agentic-token-optimizer.lock.yml
@@ -549,7 +549,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/agentic-token-trend-audit.lock.yml b/.github/workflows/agentic-token-trend-audit.lock.yml
index 0ed34cf78e9..8ce30b44f78 100644
--- a/.github/workflows/agentic-token-trend-audit.lock.yml
+++ b/.github/workflows/agentic-token-trend-audit.lock.yml
@@ -600,7 +600,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/architecture-guardian.lock.yml b/.github/workflows/architecture-guardian.lock.yml
index fca70b28a0f..1dabcb92e2b 100644
--- a/.github/workflows/architecture-guardian.lock.yml
+++ b/.github/workflows/architecture-guardian.lock.yml
@@ -544,7 +544,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/aw-failure-investigator.lock.yml b/.github/workflows/aw-failure-investigator.lock.yml
index 0472ba7c421..913c54dd38f 100644
--- a/.github/workflows/aw-failure-investigator.lock.yml
+++ b/.github/workflows/aw-failure-investigator.lock.yml
@@ -650,7 +650,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/bot-detection.lock.yml b/.github/workflows/bot-detection.lock.yml
index 3a14518c158..0895d352196 100644
--- a/.github/workflows/bot-detection.lock.yml
+++ b/.github/workflows/bot-detection.lock.yml
@@ -544,7 +544,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/breaking-change-checker.lock.yml b/.github/workflows/breaking-change-checker.lock.yml
index 023d21a426d..16ac432babd 100644
--- a/.github/workflows/breaking-change-checker.lock.yml
+++ b/.github/workflows/breaking-change-checker.lock.yml
@@ -534,7 +534,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/ci-doctor.lock.yml b/.github/workflows/ci-doctor.lock.yml
index a8b774300c6..2befb341cda 100644
--- a/.github/workflows/ci-doctor.lock.yml
+++ b/.github/workflows/ci-doctor.lock.yml
@@ -680,7 +680,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/cli-consistency-checker.lock.yml b/.github/workflows/cli-consistency-checker.lock.yml
index 983ae5f52fd..b7fe6122c2c 100644
--- a/.github/workflows/cli-consistency-checker.lock.yml
+++ b/.github/workflows/cli-consistency-checker.lock.yml
@@ -525,7 +525,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/cli-version-checker.lock.yml b/.github/workflows/cli-version-checker.lock.yml
index c4379b58ec4..9be8eccdba3 100644
--- a/.github/workflows/cli-version-checker.lock.yml
+++ b/.github/workflows/cli-version-checker.lock.yml
@@ -552,7 +552,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/codex-github-remote-mcp-test.lock.yml b/.github/workflows/codex-github-remote-mcp-test.lock.yml
index 5c0ddebe845..62579a36e00 100644
--- a/.github/workflows/codex-github-remote-mcp-test.lock.yml
+++ b/.github/workflows/codex-github-remote-mcp-test.lock.yml
@@ -519,7 +519,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/contribution-check.lock.yml b/.github/workflows/contribution-check.lock.yml
index 91a011ece04..451e2994419 100644
--- a/.github/workflows/contribution-check.lock.yml
+++ b/.github/workflows/contribution-check.lock.yml
@@ -654,7 +654,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/copilot-opt.lock.yml b/.github/workflows/copilot-opt.lock.yml
index 10a173af164..89777007b59 100644
--- a/.github/workflows/copilot-opt.lock.yml
+++ b/.github/workflows/copilot-opt.lock.yml
@@ -576,7 +576,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-agentrx-trace-optimizer.lock.yml b/.github/workflows/daily-agentrx-trace-optimizer.lock.yml
index 7d7683ac742..2f476255e74 100644
--- a/.github/workflows/daily-agentrx-trace-optimizer.lock.yml
+++ b/.github/workflows/daily-agentrx-trace-optimizer.lock.yml
@@ -660,7 +660,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-ambient-context-optimizer.lock.yml b/.github/workflows/daily-ambient-context-optimizer.lock.yml
index bf9cb233f74..00168572be7 100644
--- a/.github/workflows/daily-ambient-context-optimizer.lock.yml
+++ b/.github/workflows/daily-ambient-context-optimizer.lock.yml
@@ -598,7 +598,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-architecture-diagram.lock.yml b/.github/workflows/daily-architecture-diagram.lock.yml
index 61ee41615b4..0103efca5b6 100644
--- a/.github/workflows/daily-architecture-diagram.lock.yml
+++ b/.github/workflows/daily-architecture-diagram.lock.yml
@@ -619,7 +619,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-aw-cross-repo-compile-check.lock.yml b/.github/workflows/daily-aw-cross-repo-compile-check.lock.yml
index d52c91dde36..b1f7ee8f042 100644
--- a/.github/workflows/daily-aw-cross-repo-compile-check.lock.yml
+++ b/.github/workflows/daily-aw-cross-repo-compile-check.lock.yml
@@ -550,7 +550,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml b/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
index fc92cdcf02e..1af8b586686 100644
--- a/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
+++ b/.github/workflows/daily-awf-spec-compiler-surfacing.lock.yml
@@ -547,7 +547,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-byok-ollama-test.lock.yml b/.github/workflows/daily-byok-ollama-test.lock.yml
index c506bf7cbec..b345e5708e8 100644
--- a/.github/workflows/daily-byok-ollama-test.lock.yml
+++ b/.github/workflows/daily-byok-ollama-test.lock.yml
@@ -521,7 +521,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-cache-strategy-analyzer.lock.yml b/.github/workflows/daily-cache-strategy-analyzer.lock.yml
index b6518f3194b..81167c982e2 100644
--- a/.github/workflows/daily-cache-strategy-analyzer.lock.yml
+++ b/.github/workflows/daily-cache-strategy-analyzer.lock.yml
@@ -696,7 +696,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-cli-performance.lock.yml b/.github/workflows/daily-cli-performance.lock.yml
index 404d2d0e5dc..cb3efe85e49 100644
--- a/.github/workflows/daily-cli-performance.lock.yml
+++ b/.github/workflows/daily-cli-performance.lock.yml
@@ -638,7 +638,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-cli-tools-tester.lock.yml b/.github/workflows/daily-cli-tools-tester.lock.yml
index 73d89ee01ac..2a92f0fc31c 100644
--- a/.github/workflows/daily-cli-tools-tester.lock.yml
+++ b/.github/workflows/daily-cli-tools-tester.lock.yml
@@ -622,7 +622,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-community-attribution.lock.yml b/.github/workflows/daily-community-attribution.lock.yml
index b386c6b2e7a..1d88ca92b3e 100644
--- a/.github/workflows/daily-community-attribution.lock.yml
+++ b/.github/workflows/daily-community-attribution.lock.yml
@@ -613,7 +613,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-credit-limit-test.lock.yml b/.github/workflows/daily-credit-limit-test.lock.yml
index 44d9bbf557e..d3f2eaf8f71 100644
--- a/.github/workflows/daily-credit-limit-test.lock.yml
+++ b/.github/workflows/daily-credit-limit-test.lock.yml
@@ -500,7 +500,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-doc-healer.lock.yml b/.github/workflows/daily-doc-healer.lock.yml
index d912bb5e785..7c454d34f79 100644
--- a/.github/workflows/daily-doc-healer.lock.yml
+++ b/.github/workflows/daily-doc-healer.lock.yml
@@ -625,7 +625,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-file-diet.lock.yml b/.github/workflows/daily-file-diet.lock.yml
index beb5cbfd292..d26a9880195 100644
--- a/.github/workflows/daily-file-diet.lock.yml
+++ b/.github/workflows/daily-file-diet.lock.yml
@@ -569,7 +569,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-formal-spec-verifier.lock.yml b/.github/workflows/daily-formal-spec-verifier.lock.yml
index a8965cc87a8..b39cf2cd39d 100644
--- a/.github/workflows/daily-formal-spec-verifier.lock.yml
+++ b/.github/workflows/daily-formal-spec-verifier.lock.yml
@@ -580,7 +580,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-function-namer.lock.yml b/.github/workflows/daily-function-namer.lock.yml
index 9e1880b7344..c1742237a07 100644
--- a/.github/workflows/daily-function-namer.lock.yml
+++ b/.github/workflows/daily-function-namer.lock.yml
@@ -654,7 +654,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-grafana-otel-instrumentation-advisor.lock.yml b/.github/workflows/daily-grafana-otel-instrumentation-advisor.lock.yml
index ced006d2eff..0542b66cf95 100644
--- a/.github/workflows/daily-grafana-otel-instrumentation-advisor.lock.yml
+++ b/.github/workflows/daily-grafana-otel-instrumentation-advisor.lock.yml
@@ -566,7 +566,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-max-ai-credits-test.lock.yml b/.github/workflows/daily-max-ai-credits-test.lock.yml
index 3d1a8ef12ad..dec63cb7cc6 100644
--- a/.github/workflows/daily-max-ai-credits-test.lock.yml
+++ b/.github/workflows/daily-max-ai-credits-test.lock.yml
@@ -475,7 +475,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
index e04f70d56c2..2430d509a22 100644
--- a/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
+++ b/.github/workflows/daily-mcp-concurrency-analysis.lock.yml
@@ -627,7 +627,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-model-inventory.lock.yml b/.github/workflows/daily-model-inventory.lock.yml
index fd0162c2f6e..bea4d7f1065 100644
--- a/.github/workflows/daily-model-inventory.lock.yml
+++ b/.github/workflows/daily-model-inventory.lock.yml
@@ -565,7 +565,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-multi-device-docs-tester.lock.yml b/.github/workflows/daily-multi-device-docs-tester.lock.yml
index 5a650edbace..5799cdd08c4 100644
--- a/.github/workflows/daily-multi-device-docs-tester.lock.yml
+++ b/.github/workflows/daily-multi-device-docs-tester.lock.yml
@@ -598,7 +598,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-otel-instrumentation-advisor.lock.yml b/.github/workflows/daily-otel-instrumentation-advisor.lock.yml
index b6c70883f80..667b6291091 100644
--- a/.github/workflows/daily-otel-instrumentation-advisor.lock.yml
+++ b/.github/workflows/daily-otel-instrumentation-advisor.lock.yml
@@ -577,7 +577,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-reliability-review.lock.yml b/.github/workflows/daily-reliability-review.lock.yml
index 1f90f626237..111ce28bafc 100644
--- a/.github/workflows/daily-reliability-review.lock.yml
+++ b/.github/workflows/daily-reliability-review.lock.yml
@@ -536,7 +536,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-safe-output-optimizer.lock.yml b/.github/workflows/daily-safe-output-optimizer.lock.yml
index f2babf1caa7..21bed458a66 100644
--- a/.github/workflows/daily-safe-output-optimizer.lock.yml
+++ b/.github/workflows/daily-safe-output-optimizer.lock.yml
@@ -706,7 +706,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-safe-outputs-conformance.lock.yml b/.github/workflows/daily-safe-outputs-conformance.lock.yml
index fdf6d901af2..ad8f85442b3 100644
--- a/.github/workflows/daily-safe-outputs-conformance.lock.yml
+++ b/.github/workflows/daily-safe-outputs-conformance.lock.yml
@@ -555,7 +555,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-safeoutputs-git-simulator.lock.yml b/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
index 5e68f1fbd71..c08d3242353 100644
--- a/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
+++ b/.github/workflows/daily-safeoutputs-git-simulator.lock.yml
@@ -554,7 +554,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-security-red-team.lock.yml b/.github/workflows/daily-security-red-team.lock.yml
index c670f5d9ba4..d4fc9b56ba1 100644
--- a/.github/workflows/daily-security-red-team.lock.yml
+++ b/.github/workflows/daily-security-red-team.lock.yml
@@ -627,7 +627,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-skill-optimizer.lock.yml b/.github/workflows/daily-skill-optimizer.lock.yml
index ca0f5837378..096921944d6 100644
--- a/.github/workflows/daily-skill-optimizer.lock.yml
+++ b/.github/workflows/daily-skill-optimizer.lock.yml
@@ -534,7 +534,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-spdd-spec-planner.lock.yml b/.github/workflows/daily-spdd-spec-planner.lock.yml
index 4d56d19315d..06b051cd31b 100644
--- a/.github/workflows/daily-spdd-spec-planner.lock.yml
+++ b/.github/workflows/daily-spdd-spec-planner.lock.yml
@@ -555,7 +555,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-syntax-error-quality.lock.yml b/.github/workflows/daily-syntax-error-quality.lock.yml
index a306b1096e5..2636ba4fea3 100644
--- a/.github/workflows/daily-syntax-error-quality.lock.yml
+++ b/.github/workflows/daily-syntax-error-quality.lock.yml
@@ -536,7 +536,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-team-status.lock.yml b/.github/workflows/daily-team-status.lock.yml
index 9aba0af1671..824e2be7351 100644
--- a/.github/workflows/daily-team-status.lock.yml
+++ b/.github/workflows/daily-team-status.lock.yml
@@ -573,7 +573,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-testify-uber-super-expert.lock.yml b/.github/workflows/daily-testify-uber-super-expert.lock.yml
index 8d190bbdbf3..a892ae1745c 100644
--- a/.github/workflows/daily-testify-uber-super-expert.lock.yml
+++ b/.github/workflows/daily-testify-uber-super-expert.lock.yml
@@ -595,7 +595,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-token-consumption-report.lock.yml b/.github/workflows/daily-token-consumption-report.lock.yml
index bb88c1268f3..53c4599961c 100644
--- a/.github/workflows/daily-token-consumption-report.lock.yml
+++ b/.github/workflows/daily-token-consumption-report.lock.yml
@@ -565,7 +565,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/daily-windows-terminal-integration-builder.lock.yml b/.github/workflows/daily-windows-terminal-integration-builder.lock.yml
index a9011c682ea..76d51864d1f 100644
--- a/.github/workflows/daily-windows-terminal-integration-builder.lock.yml
+++ b/.github/workflows/daily-windows-terminal-integration-builder.lock.yml
@@ -503,7 +503,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/deep-report.lock.yml b/.github/workflows/deep-report.lock.yml
index c30487e9adc..56c07e58309 100644
--- a/.github/workflows/deep-report.lock.yml
+++ b/.github/workflows/deep-report.lock.yml
@@ -929,7 +929,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/delight.lock.yml b/.github/workflows/delight.lock.yml
index 3e9038385da..20f2830b5ef 100644
--- a/.github/workflows/delight.lock.yml
+++ b/.github/workflows/delight.lock.yml
@@ -583,7 +583,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/dependabot-burner.lock.yml b/.github/workflows/dependabot-burner.lock.yml
index 34b6ada5d18..2ec2a049511 100644
--- a/.github/workflows/dependabot-burner.lock.yml
+++ b/.github/workflows/dependabot-burner.lock.yml
@@ -524,7 +524,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/dependabot-go-checker.lock.yml b/.github/workflows/dependabot-go-checker.lock.yml
index 6fe110fd2b1..b7cbef83cc5 100644
--- a/.github/workflows/dependabot-go-checker.lock.yml
+++ b/.github/workflows/dependabot-go-checker.lock.yml
@@ -586,7 +586,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/deployment-incident-monitor.lock.yml b/.github/workflows/deployment-incident-monitor.lock.yml
index 4cc19e7cf2b..a5810aa3f01 100644
--- a/.github/workflows/deployment-incident-monitor.lock.yml
+++ b/.github/workflows/deployment-incident-monitor.lock.yml
@@ -539,7 +539,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/designer-drift-audit.lock.yml b/.github/workflows/designer-drift-audit.lock.yml
index 8d0a8bd5293..aba81bb0a65 100644
--- a/.github/workflows/designer-drift-audit.lock.yml
+++ b/.github/workflows/designer-drift-audit.lock.yml
@@ -508,7 +508,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/dev.lock.yml b/.github/workflows/dev.lock.yml
index 9df675cdb04..8f2b75495ee 100644
--- a/.github/workflows/dev.lock.yml
+++ b/.github/workflows/dev.lock.yml
@@ -579,7 +579,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/discussion-task-miner.lock.yml b/.github/workflows/discussion-task-miner.lock.yml
index 15c716c4ad2..e1566484c3d 100644
--- a/.github/workflows/discussion-task-miner.lock.yml
+++ b/.github/workflows/discussion-task-miner.lock.yml
@@ -572,7 +572,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/duplicate-code-detector.lock.yml b/.github/workflows/duplicate-code-detector.lock.yml
index 9f474ab4fe7..d2f1b3be606 100644
--- a/.github/workflows/duplicate-code-detector.lock.yml
+++ b/.github/workflows/duplicate-code-detector.lock.yml
@@ -568,7 +568,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/example-permissions-warning.lock.yml b/.github/workflows/example-permissions-warning.lock.yml
index e99bab226e9..f15283e3eda 100644
--- a/.github/workflows/example-permissions-warning.lock.yml
+++ b/.github/workflows/example-permissions-warning.lock.yml
@@ -518,7 +518,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/firewall.lock.yml b/.github/workflows/firewall.lock.yml
index 55e2dca57fb..47ede9d47b3 100644
--- a/.github/workflows/firewall.lock.yml
+++ b/.github/workflows/firewall.lock.yml
@@ -522,7 +522,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/go-fan.lock.yml b/.github/workflows/go-fan.lock.yml
index f88f092c9a4..75579e62d6e 100644
--- a/.github/workflows/go-fan.lock.yml
+++ b/.github/workflows/go-fan.lock.yml
@@ -591,7 +591,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/go-pattern-detector.lock.yml b/.github/workflows/go-pattern-detector.lock.yml
index 3158400f806..1dbd5d6b9ac 100644
--- a/.github/workflows/go-pattern-detector.lock.yml
+++ b/.github/workflows/go-pattern-detector.lock.yml
@@ -536,7 +536,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/gpclean.lock.yml b/.github/workflows/gpclean.lock.yml
index 0460f28b093..29585a40477 100644
--- a/.github/workflows/gpclean.lock.yml
+++ b/.github/workflows/gpclean.lock.yml
@@ -595,7 +595,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/hippo-embed.lock.yml b/.github/workflows/hippo-embed.lock.yml
index ffc99988d7e..c21403a2e33 100644
--- a/.github/workflows/hippo-embed.lock.yml
+++ b/.github/workflows/hippo-embed.lock.yml
@@ -556,7 +556,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/issue-arborist.lock.yml b/.github/workflows/issue-arborist.lock.yml
index 4691e2b5344..5eebe80567f 100644
--- a/.github/workflows/issue-arborist.lock.yml
+++ b/.github/workflows/issue-arborist.lock.yml
@@ -640,7 +640,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/lint-monster.lock.yml b/.github/workflows/lint-monster.lock.yml
index d7b5a05f82e..cd976082f74 100644
--- a/.github/workflows/lint-monster.lock.yml
+++ b/.github/workflows/lint-monster.lock.yml
@@ -602,7 +602,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/metrics-collector.lock.yml b/.github/workflows/metrics-collector.lock.yml
index 1097fca59ec..f47c73941f0 100644
--- a/.github/workflows/metrics-collector.lock.yml
+++ b/.github/workflows/metrics-collector.lock.yml
@@ -623,7 +623,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/objective-impact-report.lock.yml b/.github/workflows/objective-impact-report.lock.yml
index 255085af3d0..acea8084414 100644
--- a/.github/workflows/objective-impact-report.lock.yml
+++ b/.github/workflows/objective-impact-report.lock.yml
@@ -473,7 +473,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/otlp-data-quality-validator.lock.yml b/.github/workflows/otlp-data-quality-validator.lock.yml
index f7b2f40669c..71fb20801a4 100644
--- a/.github/workflows/otlp-data-quality-validator.lock.yml
+++ b/.github/workflows/otlp-data-quality-validator.lock.yml
@@ -527,7 +527,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/outcome-collector.lock.yml b/.github/workflows/outcome-collector.lock.yml
index 6a712489970..b9b16dd7d57 100644
--- a/.github/workflows/outcome-collector.lock.yml
+++ b/.github/workflows/outcome-collector.lock.yml
@@ -563,7 +563,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/plan.lock.yml b/.github/workflows/plan.lock.yml
index 2ad05a65f7f..1e20be88814 100644
--- a/.github/workflows/plan.lock.yml
+++ b/.github/workflows/plan.lock.yml
@@ -615,7 +615,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/poem-bot.lock.yml b/.github/workflows/poem-bot.lock.yml
index 06be335139a..e8e2af62ae7 100644
--- a/.github/workflows/poem-bot.lock.yml
+++ b/.github/workflows/poem-bot.lock.yml
@@ -737,7 +737,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/pr-triage-agent.lock.yml b/.github/workflows/pr-triage-agent.lock.yml
index 1f324806781..8223646bcc0 100644
--- a/.github/workflows/pr-triage-agent.lock.yml
+++ b/.github/workflows/pr-triage-agent.lock.yml
@@ -603,7 +603,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/refactoring-cadence.lock.yml b/.github/workflows/refactoring-cadence.lock.yml
index d0d88a19f9c..f8224ffdddc 100644
--- a/.github/workflows/refactoring-cadence.lock.yml
+++ b/.github/workflows/refactoring-cadence.lock.yml
@@ -555,7 +555,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/ruflo-backed-task.lock.yml b/.github/workflows/ruflo-backed-task.lock.yml
index 3b034e3b64d..9b421386e12 100644
--- a/.github/workflows/ruflo-backed-task.lock.yml
+++ b/.github/workflows/ruflo-backed-task.lock.yml
@@ -601,7 +601,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/security-compliance.lock.yml b/.github/workflows/security-compliance.lock.yml
index acac419be95..2cd442355fe 100644
--- a/.github/workflows/security-compliance.lock.yml
+++ b/.github/workflows/security-compliance.lock.yml
@@ -565,7 +565,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/semantic-function-refactor.lock.yml b/.github/workflows/semantic-function-refactor.lock.yml
index 54e23db60c8..309007970d1 100644
--- a/.github/workflows/semantic-function-refactor.lock.yml
+++ b/.github/workflows/semantic-function-refactor.lock.yml
@@ -580,7 +580,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/sergo.lock.yml b/.github/workflows/sergo.lock.yml
index 66eeec4202b..25cefea58be 100644
--- a/.github/workflows/sergo.lock.yml
+++ b/.github/workflows/sergo.lock.yml
@@ -616,7 +616,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-antigravity.lock.yml b/.github/workflows/smoke-antigravity.lock.yml
index 104582374c1..e2863a273f7 100644
--- a/.github/workflows/smoke-antigravity.lock.yml
+++ b/.github/workflows/smoke-antigravity.lock.yml
@@ -693,7 +693,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-ci.lock.yml b/.github/workflows/smoke-ci.lock.yml
index 8d9f519742d..d40d96e4461 100644
--- a/.github/workflows/smoke-ci.lock.yml
+++ b/.github/workflows/smoke-ci.lock.yml
@@ -675,7 +675,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-claude.lock.yml b/.github/workflows/smoke-claude.lock.yml
index 3a330c0ddae..3a941a39b56 100644
--- a/.github/workflows/smoke-claude.lock.yml
+++ b/.github/workflows/smoke-claude.lock.yml
@@ -1032,7 +1032,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-codex.lock.yml b/.github/workflows/smoke-codex.lock.yml
index 4094e1fab04..a6f1683b7f5 100644
--- a/.github/workflows/smoke-codex.lock.yml
+++ b/.github/workflows/smoke-codex.lock.yml
@@ -798,7 +798,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml
index cae82b7e3ed..044a30ae255 100644
--- a/.github/workflows/smoke-copilot-aoai-apikey.lock.yml
+++ b/.github/workflows/smoke-copilot-aoai-apikey.lock.yml
@@ -947,7 +947,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-copilot-arm.lock.yml b/.github/workflows/smoke-copilot-arm.lock.yml
index f277e07c518..1766a09127a 100644
--- a/.github/workflows/smoke-copilot-arm.lock.yml
+++ b/.github/workflows/smoke-copilot-arm.lock.yml
@@ -856,7 +856,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-copilot-sdk.lock.yml b/.github/workflows/smoke-copilot-sdk.lock.yml
index 82d1fe3003a..8aa6504476b 100644
--- a/.github/workflows/smoke-copilot-sdk.lock.yml
+++ b/.github/workflows/smoke-copilot-sdk.lock.yml
@@ -577,7 +577,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-copilot.lock.yml b/.github/workflows/smoke-copilot.lock.yml
index 62233845038..c13d3072483 100644
--- a/.github/workflows/smoke-copilot.lock.yml
+++ b/.github/workflows/smoke-copilot.lock.yml
@@ -952,7 +952,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-create-cross-repo-pr.lock.yml b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
index 75690ab0e68..7f2b2e5838b 100644
--- a/.github/workflows/smoke-create-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
@@ -658,7 +658,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-crush.lock.yml b/.github/workflows/smoke-crush.lock.yml
index adecc2bc389..5870c96dfaa 100644
--- a/.github/workflows/smoke-crush.lock.yml
+++ b/.github/workflows/smoke-crush.lock.yml
@@ -632,7 +632,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-gemini.lock.yml b/.github/workflows/smoke-gemini.lock.yml
index a58d59b1a65..6d07d40c329 100644
--- a/.github/workflows/smoke-gemini.lock.yml
+++ b/.github/workflows/smoke-gemini.lock.yml
@@ -697,7 +697,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-opencode.lock.yml b/.github/workflows/smoke-opencode.lock.yml
index a0d4eecbea5..810b3cc23bb 100644
--- a/.github/workflows/smoke-opencode.lock.yml
+++ b/.github/workflows/smoke-opencode.lock.yml
@@ -636,7 +636,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-otel-backends.lock.yml b/.github/workflows/smoke-otel-backends.lock.yml
index d6334aa5a55..4d30358f917 100644
--- a/.github/workflows/smoke-otel-backends.lock.yml
+++ b/.github/workflows/smoke-otel-backends.lock.yml
@@ -623,7 +623,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-pi.lock.yml b/.github/workflows/smoke-pi.lock.yml
index cb5501705c8..1ca5e0daba0 100644
--- a/.github/workflows/smoke-pi.lock.yml
+++ b/.github/workflows/smoke-pi.lock.yml
@@ -664,7 +664,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-project.lock.yml b/.github/workflows/smoke-project.lock.yml
index 0b6c5ae34a2..37da00ab3d2 100644
--- a/.github/workflows/smoke-project.lock.yml
+++ b/.github/workflows/smoke-project.lock.yml
@@ -677,7 +677,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-temporary-id.lock.yml b/.github/workflows/smoke-temporary-id.lock.yml
index 72c7394e79f..e90cf37dd61 100644
--- a/.github/workflows/smoke-temporary-id.lock.yml
+++ b/.github/workflows/smoke-temporary-id.lock.yml
@@ -646,7 +646,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-update-cross-repo-pr.lock.yml b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
index 77b35c7f4a9..8d6a5ca9818 100644
--- a/.github/workflows/smoke-update-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
@@ -691,7 +691,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/smoke-workflow-call-with-inputs.lock.yml b/.github/workflows/smoke-workflow-call-with-inputs.lock.yml
index 35b588107d0..bcc34ef9977 100644
--- a/.github/workflows/smoke-workflow-call-with-inputs.lock.yml
+++ b/.github/workflows/smoke-workflow-call-with-inputs.lock.yml
@@ -605,7 +605,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/spec-librarian.lock.yml b/.github/workflows/spec-librarian.lock.yml
index 0f62d095e6d..8448adde481 100644
--- a/.github/workflows/spec-librarian.lock.yml
+++ b/.github/workflows/spec-librarian.lock.yml
@@ -569,7 +569,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/stale-repo-identifier.lock.yml b/.github/workflows/stale-repo-identifier.lock.yml
index 40ec37d9c65..d21133464aa 100644
--- a/.github/workflows/stale-repo-identifier.lock.yml
+++ b/.github/workflows/stale-repo-identifier.lock.yml
@@ -704,7 +704,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/static-analysis-report.lock.yml b/.github/workflows/static-analysis-report.lock.yml
index b0f0507fec9..68dd51096f2 100644
--- a/.github/workflows/static-analysis-report.lock.yml
+++ b/.github/workflows/static-analysis-report.lock.yml
@@ -666,7 +666,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/step-name-alignment.lock.yml b/.github/workflows/step-name-alignment.lock.yml
index 9c70f988a1c..4c72f51fb3a 100644
--- a/.github/workflows/step-name-alignment.lock.yml
+++ b/.github/workflows/step-name-alignment.lock.yml
@@ -550,7 +550,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/super-linter.lock.yml b/.github/workflows/super-linter.lock.yml
index 32fa0a13b9b..423610b2eeb 100644
--- a/.github/workflows/super-linter.lock.yml
+++ b/.github/workflows/super-linter.lock.yml
@@ -566,7 +566,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/test-workflow.lock.yml b/.github/workflows/test-workflow.lock.yml
index 67a90973cc5..d5e441b4117 100644
--- a/.github/workflows/test-workflow.lock.yml
+++ b/.github/workflows/test-workflow.lock.yml
@@ -519,7 +519,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/video-analyzer.lock.yml b/.github/workflows/video-analyzer.lock.yml
index 1d31be06fde..b253aa9f57b 100644
--- a/.github/workflows/video-analyzer.lock.yml
+++ b/.github/workflows/video-analyzer.lock.yml
@@ -537,7 +537,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/workflow-health-manager.lock.yml b/.github/workflows/workflow-health-manager.lock.yml
index 29e8331f81e..af19cdd776d 100644
--- a/.github/workflows/workflow-health-manager.lock.yml
+++ b/.github/workflows/workflow-health-manager.lock.yml
@@ -583,7 +583,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/workflow-normalizer.lock.yml b/.github/workflows/workflow-normalizer.lock.yml
index d4aa87885e9..9d765d77996 100644
--- a/.github/workflows/workflow-normalizer.lock.yml
+++ b/.github/workflows/workflow-normalizer.lock.yml
@@ -592,7 +592,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
diff --git a/.github/workflows/workflow-skill-extractor.lock.yml b/.github/workflows/workflow-skill-extractor.lock.yml
index f41d94c9d79..10957a917d6 100644
--- a/.github/workflows/workflow-skill-extractor.lock.yml
+++ b/.github/workflows/workflow-skill-extractor.lock.yml
@@ -559,7 +559,8 @@ jobs:
"required": true,
"type": "string",
"sanitize": true,
- "maxLength": 65000
+ "maxLength": 65000,
+ "minLength": 20
},
"fields": {
"type": "array"
From a5e69ca64c8fa09c8651de9d7016f5f5aeabd1b5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 16:40:13 +0000
Subject: [PATCH 3/8] Fix checkout manifest step template-injection regression
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
actions/setup/js/build_checkout_manifest.cjs | 126 +++++++++++++++++
.../setup/js/build_checkout_manifest.test.cjs | 129 ++++++++++++++++++
pkg/workflow/checkout_manager_test.go | 60 ++++----
.../checkout_manifest_compile_test.go | 52 +++++++
pkg/workflow/checkout_step_generator.go | 55 +++-----
pkg/workflow/compiler_yaml_main_job.go | 2 +-
6 files changed, 365 insertions(+), 59 deletions(-)
create mode 100644 actions/setup/js/build_checkout_manifest.cjs
create mode 100644 actions/setup/js/build_checkout_manifest.test.cjs
create mode 100644 pkg/workflow/checkout_manifest_compile_test.go
diff --git a/actions/setup/js/build_checkout_manifest.cjs b/actions/setup/js/build_checkout_manifest.cjs
new file mode 100644
index 00000000000..b824ae14bbe
--- /dev/null
+++ b/actions/setup/js/build_checkout_manifest.cjs
@@ -0,0 +1,126 @@
+// @ts-check
+///
+
+const fs = require("fs");
+const path = require("path");
+const { execFileSync } = require("child_process");
+
+const { getErrorMessage } = require("./error_helpers.cjs");
+
+function parseManifestEntries(entriesJSON = process.env.GH_AW_CHECKOUT_MANIFEST_ENTRIES || "[]") {
+ const parsed = JSON.parse(entriesJSON);
+ if (!Array.isArray(parsed)) {
+ throw new Error("GH_AW_CHECKOUT_MANIFEST_ENTRIES must be a JSON array");
+ }
+ return parsed;
+}
+
+function readManifestEntriesFromEnv() {
+ const count = Number.parseInt(process.env.GH_AW_CHECKOUT_MANIFEST_COUNT || "0", 10);
+ if (!Number.isFinite(count) || count < 0) {
+ throw new Error("GH_AW_CHECKOUT_MANIFEST_COUNT must be a non-negative integer");
+ }
+
+ const entries = [];
+ for (let i = 0; i < count; i += 1) {
+ entries.push({
+ repository: process.env[`GH_AW_CHECKOUT_REPO_${i}`] || "",
+ path: process.env[`GH_AW_CHECKOUT_PATH_${i}`] || "",
+ });
+ }
+ return entries;
+}
+
+function resolveDefaultBranch(repository, checkoutPath, options = {}) {
+ const workspace = options.workspace || process.env.GITHUB_WORKSPACE || "";
+ const runGit = options.runGit || ((args, execOptions = {}) => execFileSync("git", args, { encoding: "utf8", ...execOptions }));
+ const runGH = options.runGH || ((args, execOptions = {}) => execFileSync("gh", args, { encoding: "utf8", ...execOptions }));
+ let defaultBranch = "";
+
+ const repoPath = checkoutPath ? path.join(workspace, checkoutPath) : workspace;
+ if (repoPath && fs.existsSync(path.join(repoPath, ".git"))) {
+ try {
+ const output = runGit(["-C", repoPath, "symbolic-ref", "--short", "refs/remotes/origin/HEAD"], {
+ stdio: ["ignore", "pipe", "pipe"],
+ });
+ defaultBranch = output.trim().replace(/^origin\//, "");
+ } catch (error) {
+ if (typeof core !== "undefined") {
+ core.debug(`build_checkout_manifest: git default branch lookup failed for ${repository}: ${getErrorMessage(error)}`);
+ }
+ }
+ }
+
+ if (defaultBranch === "") {
+ try {
+ defaultBranch = runGH(["api", `repos/${repository}`, "--jq", ".default_branch"], {
+ stdio: ["ignore", "pipe", "pipe"],
+ }).trim();
+ } catch (error) {
+ if (typeof core !== "undefined") {
+ core.debug(`build_checkout_manifest: gh api default branch lookup failed for ${repository}: ${getErrorMessage(error)}`);
+ }
+ }
+ }
+
+ return defaultBranch;
+}
+
+function buildCheckoutManifest(entries, options = {}) {
+ const runnerTemp = options.runnerTemp || process.env.RUNNER_TEMP;
+ if (!runnerTemp) {
+ throw new Error("RUNNER_TEMP is required to build checkout manifest");
+ }
+
+ const runGit = options.runGit;
+ const runGH = options.runGH;
+
+ const manifestDir = path.join(runnerTemp, "gh-aw");
+ fs.mkdirSync(manifestDir, { recursive: true });
+ const manifestPath = path.join(manifestDir, "checkout-manifest.json");
+ const manifest = {};
+
+ for (const entry of entries) {
+ if (!entry || typeof entry !== "object") continue;
+ const repository = String(entry.repository || "").trim();
+ if (repository === "") continue;
+ const checkoutPath = String(entry.path || "");
+ const defaultBranch = resolveDefaultBranch(repository, checkoutPath, {
+ workspace: options.workspace,
+ runGit,
+ runGH,
+ });
+ manifest[repository.toLowerCase()] = {
+ repository,
+ path: checkoutPath,
+ default_branch: defaultBranch,
+ };
+ if (typeof core !== "undefined") {
+ core.info(`checkout-manifest: ${repository} -> path=${checkoutPath} default_branch=${defaultBranch || ""}`);
+ }
+ }
+
+ fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf8");
+ if (typeof core !== "undefined") {
+ core.info(`checkout-manifest written to ${manifestPath}`);
+ }
+ return { manifestPath, manifest };
+}
+
+async function main(options = {}) {
+ let entries;
+ if (typeof options.entriesJSON === "string" && options.entriesJSON.trim() !== "") {
+ entries = parseManifestEntries(options.entriesJSON);
+ } else {
+ entries = readManifestEntriesFromEnv();
+ }
+ return buildCheckoutManifest(entries, options);
+}
+
+module.exports = {
+ buildCheckoutManifest,
+ main,
+ parseManifestEntries,
+ readManifestEntriesFromEnv,
+ resolveDefaultBranch,
+};
diff --git a/actions/setup/js/build_checkout_manifest.test.cjs b/actions/setup/js/build_checkout_manifest.test.cjs
new file mode 100644
index 00000000000..9115ff2d9be
--- /dev/null
+++ b/actions/setup/js/build_checkout_manifest.test.cjs
@@ -0,0 +1,129 @@
+import { afterEach, describe, expect, it } from "vitest";
+import fs from "fs";
+import os from "os";
+import path from "path";
+import { spawnSync } from "child_process";
+
+import { buildCheckoutManifest, readManifestEntriesFromEnv, resolveDefaultBranch } from "./build_checkout_manifest.cjs";
+
+function execGit(args, options = {}) {
+ const result = spawnSync("git", args, { encoding: "utf8", ...options });
+ if (result.error) throw result.error;
+ if (result.status !== 0) {
+ throw new Error(`git ${args.join(" ")} failed:\nstdout: ${result.stdout}\nstderr: ${result.stderr}`);
+ }
+ return result.stdout;
+}
+
+function createTempDir(prefix) {
+ return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
+}
+
+function removeDir(dir) {
+ if (dir && fs.existsSync(dir)) {
+ fs.rmSync(dir, { recursive: true, force: true });
+ }
+}
+
+function setEnv(key, value) {
+ if (value === undefined) {
+ delete process.env[key];
+ } else {
+ process.env[key] = value;
+ }
+}
+
+describe("build_checkout_manifest.cjs", () => {
+ const originalEnv = { ...process.env };
+ const tempDirs = [];
+
+ afterEach(() => {
+ for (const key of Object.keys(process.env)) {
+ if (!(key in originalEnv)) {
+ delete process.env[key];
+ }
+ }
+ Object.assign(process.env, originalEnv);
+ while (tempDirs.length > 0) {
+ removeDir(tempDirs.pop());
+ }
+ });
+
+ it("reads entries from environment variables", () => {
+ setEnv("GH_AW_CHECKOUT_MANIFEST_COUNT", "2");
+ setEnv("GH_AW_CHECKOUT_REPO_0", "owner/a");
+ setEnv("GH_AW_CHECKOUT_PATH_0", "./a");
+ setEnv("GH_AW_CHECKOUT_REPO_1", "owner/b");
+ setEnv("GH_AW_CHECKOUT_PATH_1", "");
+
+ expect(readManifestEntriesFromEnv()).toEqual([
+ { repository: "owner/a", path: "./a" },
+ { repository: "owner/b", path: "" },
+ ]);
+ });
+
+ it("resolves default branch from local git checkout before gh fallback", () => {
+ const workspace = createTempDir("checkout-manifest-workspace-");
+ tempDirs.push(workspace);
+ const checkoutPath = "target";
+ const repoDir = path.join(workspace, checkoutPath);
+ fs.mkdirSync(repoDir, { recursive: true });
+
+ execGit(["init", "-q"], { cwd: repoDir });
+ execGit(["symbolic-ref", "refs/remotes/origin/HEAD", "refs/remotes/origin/main"], { cwd: repoDir });
+
+ const ghCalls = [];
+ const defaultBranch = resolveDefaultBranch("owner/repo", checkoutPath, {
+ workspace,
+ runGH: args => {
+ ghCalls.push(args);
+ return "should-not-be-used\n";
+ },
+ });
+
+ expect(defaultBranch).toBe("main");
+ expect(ghCalls).toHaveLength(0);
+ });
+
+ it("falls back to gh api when local git default branch is unavailable", () => {
+ const workspace = createTempDir("checkout-manifest-workspace-");
+ tempDirs.push(workspace);
+
+ const defaultBranch = resolveDefaultBranch("owner/repo", "missing", {
+ workspace,
+ runGH: () => "trunk\n",
+ });
+
+ expect(defaultBranch).toBe("trunk");
+ });
+
+ it("writes manifest with lowercase keys", () => {
+ const workspace = createTempDir("checkout-manifest-workspace-");
+ const runnerTemp = createTempDir("checkout-manifest-runner-temp-");
+ tempDirs.push(workspace, runnerTemp);
+
+ const { manifestPath, manifest } = buildCheckoutManifest(
+ [
+ { repository: "Owner/Repo", path: "./repo" },
+ { repository: "", path: "./skip" },
+ ],
+ {
+ workspace,
+ runnerTemp,
+ runGH: () => "main\n",
+ }
+ );
+
+ expect(manifestPath).toBe(path.join(runnerTemp, "gh-aw", "checkout-manifest.json"));
+ expect(manifest).toEqual({
+ "owner/repo": {
+ repository: "Owner/Repo",
+ path: "./repo",
+ default_branch: "main",
+ },
+ });
+
+ const fileContents = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
+ expect(fileContents).toEqual(manifest);
+ });
+});
diff --git a/pkg/workflow/checkout_manager_test.go b/pkg/workflow/checkout_manager_test.go
index 43a5838b362..7275bfd8043 100644
--- a/pkg/workflow/checkout_manager_test.go
+++ b/pkg/workflow/checkout_manager_test.go
@@ -1376,59 +1376,62 @@ func TestWikiCheckout(t *testing.T) {
// The manifest step is consumed by the safe-outputs MCP server to resolve a
// per-repo default branch without making any network calls at request time.
func TestGenerateCheckoutManifestStep(t *testing.T) {
+ getActionPin := func(action string) string {
+ return action + "@pin"
+ }
+
t.Run("no configs emits nothing", func(t *testing.T) {
cm := NewCheckoutManager(nil)
- assert.Empty(t, cm.GenerateCheckoutManifestStep(), "empty manager should not emit a manifest step")
+ assert.Empty(t, cm.GenerateCheckoutManifestStep(getActionPin), "empty manager should not emit a manifest step")
})
t.Run("default-only checkout emits nothing", func(t *testing.T) {
cm := NewCheckoutManager([]*CheckoutConfig{
{Path: "."},
})
- assert.Empty(t, cm.GenerateCheckoutManifestStep(), "manifest is for cross-repo entries only; default checkout should not produce one")
+ assert.Empty(t, cm.GenerateCheckoutManifestStep(getActionPin), "manifest is for cross-repo entries only; default checkout should not produce one")
})
t.Run("path-only additional checkout (no repository) emits nothing", func(t *testing.T) {
cm := NewCheckoutManager([]*CheckoutConfig{
{Path: "./workspace"},
})
- assert.Empty(t, cm.GenerateCheckoutManifestStep(), "additional checkout without repository should not be in manifest")
+ assert.Empty(t, cm.GenerateCheckoutManifestStep(getActionPin), "additional checkout without repository should not be in manifest")
})
t.Run("wiki cross-repo checkout is excluded", func(t *testing.T) {
cm := NewCheckoutManager([]*CheckoutConfig{
{Repository: "owner/docs", Path: "./wiki", Wiki: true},
})
- assert.Empty(t, cm.GenerateCheckoutManifestStep(), "wiki checkouts must be excluded from manifest")
+ assert.Empty(t, cm.GenerateCheckoutManifestStep(getActionPin), "wiki checkouts must be excluded from manifest")
})
t.Run("cross-repo additional checkout emits entry", func(t *testing.T) {
cm := NewCheckoutManager([]*CheckoutConfig{
{Repository: "owner/other", Path: "./other"},
})
- steps := cm.GenerateCheckoutManifestStep()
+ steps := cm.GenerateCheckoutManifestStep(getActionPin)
require.Len(t, steps, 1, "should emit one manifest step")
out := steps[0]
assert.Contains(t, out, "name: Build checkout manifest for safe-outputs handlers")
- assert.Contains(t, out, "${RUNNER_TEMP}/gh-aw/checkout-manifest.json")
+ assert.Contains(t, out, "uses: actions/github-script@pin")
assert.Contains(t, out, "GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}")
- assert.Contains(t, out, "resolve_default_branch", "should define the resolver helper")
- assert.Contains(t, out, "git -C \"${GITHUB_WORKSPACE}/${path}\" symbolic-ref --short refs/remotes/origin/HEAD", "should attempt local git resolution first")
- assert.Contains(t, out, "gh api \"repos/${repo}\" --jq '.default_branch'", "should fall back to gh api")
- assert.Contains(t, out, "repo='owner/other'", "repo must be shell-single-quoted")
- assert.Contains(t, out, "path='./other'", "path must be shell-single-quoted")
- assert.Contains(t, out, "ascii_downcase", "manifest key must be lowercased via jq")
+ assert.Contains(t, out, `GH_AW_CHECKOUT_MANIFEST_COUNT: "1"`)
+ assert.Contains(t, out, `GH_AW_CHECKOUT_REPO_0: "owner/other"`)
+ assert.Contains(t, out, `GH_AW_CHECKOUT_PATH_0: "./other"`)
+ assert.Contains(t, out, "build_checkout_manifest.cjs")
+ assert.NotContains(t, out, "run: |", "manifest step should use github-script instead of shell run block")
})
t.Run("cross-repo root checkout (empty path) is included", func(t *testing.T) {
cm := NewCheckoutManager([]*CheckoutConfig{
{Repository: "owner/other"},
})
- steps := cm.GenerateCheckoutManifestStep()
+ steps := cm.GenerateCheckoutManifestStep(getActionPin)
require.Len(t, steps, 1, "cross-repo root checkout must be in manifest")
out := steps[0]
- assert.Contains(t, out, "repo='owner/other'")
- assert.Contains(t, out, "path=''", "empty path should still be emitted (manifest consumer expects the key)")
+ assert.Contains(t, out, `GH_AW_CHECKOUT_REPO_0: "owner/other"`)
+ assert.Contains(t, out, `GH_AW_CHECKOUT_PATH_0: ""`, "empty path should still be emitted (manifest consumer expects the key)")
})
t.Run("multiple cross-repo entries each get a jq update", func(t *testing.T) {
@@ -1438,24 +1441,31 @@ func TestGenerateCheckoutManifestStep(t *testing.T) {
{Path: "./local-only"}, // no repo → skipped
{Repository: "owner/c", Path: "./c"}, // included
})
- steps := cm.GenerateCheckoutManifestStep()
+ steps := cm.GenerateCheckoutManifestStep(getActionPin)
require.Len(t, steps, 1)
out := steps[0]
- assert.Equal(t, 3, strings.Count(out, "resolve_default_branch \"$repo\" \"$path\""), "should invoke resolver once per cross-repo entry")
- assert.Contains(t, out, "repo='owner/a'")
- assert.Contains(t, out, "repo='owner/b'")
- assert.Contains(t, out, "repo='owner/c'")
+ assert.Contains(t, out, `GH_AW_CHECKOUT_MANIFEST_COUNT: "3"`)
+ assert.Contains(t, out, `GH_AW_CHECKOUT_REPO_0: "owner/a"`)
+ assert.Contains(t, out, `GH_AW_CHECKOUT_REPO_1: "owner/b"`)
+ assert.Contains(t, out, `GH_AW_CHECKOUT_REPO_2: "owner/c"`)
assert.NotContains(t, out, "./local-only", "path-only entries must not be in the manifest step")
})
- t.Run("repository names containing single quotes are escaped", func(t *testing.T) {
- // Repository names cannot actually contain single quotes per GitHub rules,
- // but the shellSingleQuote helper must still defend against it.
+ t.Run("repository names containing single quotes are yaml-escaped", func(t *testing.T) {
cm := NewCheckoutManager([]*CheckoutConfig{
{Repository: "weird'owner/repo", Path: "./x"},
})
- steps := cm.GenerateCheckoutManifestStep()
+ steps := cm.GenerateCheckoutManifestStep(getActionPin)
+ require.Len(t, steps, 1)
+ assert.Contains(t, steps[0], `GH_AW_CHECKOUT_REPO_0: "weird'owner/repo"`)
+ })
+
+ t.Run("dynamic repository expressions are emitted as raw env expressions", func(t *testing.T) {
+ cm := NewCheckoutManager([]*CheckoutConfig{
+ {Repository: "${{ github.event.inputs.trigger_ref }}", Path: "./target"},
+ })
+ steps := cm.GenerateCheckoutManifestStep(getActionPin)
require.Len(t, steps, 1)
- assert.Contains(t, steps[0], `repo='weird'\''owner/repo'`, "single quote must be escaped with '\\''")
+ assert.Contains(t, steps[0], "GH_AW_CHECKOUT_REPO_0: ${{ github.event.inputs.trigger_ref }}")
})
}
diff --git a/pkg/workflow/checkout_manifest_compile_test.go b/pkg/workflow/checkout_manifest_compile_test.go
new file mode 100644
index 00000000000..ed92d453a16
--- /dev/null
+++ b/pkg/workflow/checkout_manifest_compile_test.go
@@ -0,0 +1,52 @@
+//go:build !integration
+
+package workflow
+
+import (
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/github/gh-aw/pkg/stringutil"
+ "github.com/stretchr/testify/require"
+)
+
+func TestCompileDynamicCheckoutRepositoryManifestStepUsesGitHubScript(t *testing.T) {
+ tmpDir := t.TempDir()
+ workflowPath := filepath.Join(tmpDir, "repro.md")
+ content := `---
+on:
+ workflow_dispatch:
+ inputs:
+ trigger_ref:
+ type: string
+ required: true
+engine: copilot
+timeout-minutes: 5
+checkout:
+ - repository: ${{ github.event.inputs.trigger_ref }}
+ current: true
+safe-outputs:
+ noop:
+ report-as-issue: false
+---
+# Repro
+Do nothing.
+`
+ require.NoError(t, os.WriteFile(workflowPath, []byte(content), 0o644))
+
+ compiler := NewCompiler()
+ require.NoError(t, compiler.CompileWorkflow(workflowPath))
+
+ lockPath := stringutil.MarkdownToLockFile(workflowPath)
+ lockBytes, err := os.ReadFile(lockPath)
+ require.NoError(t, err)
+ lock := string(lockBytes)
+
+ require.Contains(t, lock, "name: Build checkout manifest for safe-outputs handlers")
+ require.Contains(t, lock, "uses: actions/github-script")
+ require.Contains(t, lock, "build_checkout_manifest.cjs")
+ require.Contains(t, lock, "GH_AW_CHECKOUT_REPO_0: ${{ github.event.inputs.trigger_ref }}")
+ require.NotContains(t, lock, "repo='${{ github.event.inputs.trigger_ref }}'")
+ require.NotContains(t, lock, "Build checkout manifest for safe-outputs handlers\n run: |", "manifest step must not use run block")
+}
diff --git a/pkg/workflow/checkout_step_generator.go b/pkg/workflow/checkout_step_generator.go
index 45020086ac8..f9d0f5f9cf0 100644
--- a/pkg/workflow/checkout_step_generator.go
+++ b/pkg/workflow/checkout_step_generator.go
@@ -2,6 +2,7 @@ package workflow
import (
"fmt"
+ "strconv"
"strings"
)
@@ -79,7 +80,7 @@ func (cm *CheckoutManager) GenerateAdditionalCheckoutSteps(getActionPin func(str
// 2. `gh api repos// --jq .default_branch` as a credentialed fallback
//
// Returns an empty slice when there are no non-default cross-repo checkouts to record.
-func (cm *CheckoutManager) GenerateCheckoutManifestStep() []string {
+func (cm *CheckoutManager) GenerateCheckoutManifestStep(getActionPin func(string) string) []string {
type manifestEntry struct {
repository string
path string
@@ -102,43 +103,31 @@ func (cm *CheckoutManager) GenerateCheckoutManifestStep() []string {
var sb strings.Builder
sb.WriteString(" - name: Build checkout manifest for safe-outputs handlers\n")
+ fmt.Fprintf(&sb, " uses: %s\n", getActionPin("actions/github-script"))
sb.WriteString(" env:\n")
sb.WriteString(" GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}\n")
- sb.WriteString(" run: |\n")
- sb.WriteString(" set -euo pipefail\n")
- sb.WriteString(" mkdir -p \"${RUNNER_TEMP}/gh-aw\"\n")
- sb.WriteString(" manifest=\"${RUNNER_TEMP}/gh-aw/checkout-manifest.json\"\n")
- sb.WriteString(" printf '{}' > \"$manifest\"\n")
- sb.WriteString(" resolve_default_branch() {\n")
- sb.WriteString(" local repo=\"$1\" path=\"$2\" db=\"\"\n")
- sb.WriteString(" if [ -d \"${GITHUB_WORKSPACE}/${path}/.git\" ]; then\n")
- sb.WriteString(" db=$(git -C \"${GITHUB_WORKSPACE}/${path}\" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||' || true)\n")
- sb.WriteString(" fi\n")
- sb.WriteString(" if [ -z \"$db\" ]; then\n")
- sb.WriteString(" db=$(gh api \"repos/${repo}\" --jq '.default_branch' 2>/dev/null || true)\n")
- sb.WriteString(" fi\n")
- sb.WriteString(" printf '%s' \"$db\"\n")
- sb.WriteString(" }\n")
- for _, e := range entries {
- // Both repo and path are static at compile time. Use shell-quoted literals.
- fmt.Fprintf(&sb, " repo=%s\n", shellSingleQuote(e.repository))
- fmt.Fprintf(&sb, " path=%s\n", shellSingleQuote(e.path))
- sb.WriteString(" db=$(resolve_default_branch \"$repo\" \"$path\")\n")
- sb.WriteString(" tmp=$(mktemp)\n")
- sb.WriteString(" jq --arg repo \"$repo\" --arg path \"$path\" --arg db \"$db\" \\\n")
- sb.WriteString(" '.[($repo | ascii_downcase)] = {repository: $repo, path: $path, default_branch: $db}' \\\n")
- sb.WriteString(" \"$manifest\" > \"$tmp\" && mv \"$tmp\" \"$manifest\"\n")
- sb.WriteString(" echo \"checkout-manifest: ${repo} -> path=${path} default_branch=${db:-}\"\n")
- }
- sb.WriteString(" cat \"$manifest\"\n")
+ writeYAMLEnv(&sb, " ", "GH_AW_CHECKOUT_MANIFEST_COUNT", strconv.Itoa(len(entries)))
+ for i, e := range entries {
+ repoKey := fmt.Sprintf("GH_AW_CHECKOUT_REPO_%d", i)
+ pathKey := fmt.Sprintf("GH_AW_CHECKOUT_PATH_%d", i)
+ if strings.Contains(e.repository, "${{") {
+ fmt.Fprintf(&sb, " %s: %s\n", repoKey, githubExpressionWhitespaceReplacer.Replace(e.repository))
+ } else {
+ writeYAMLEnv(&sb, " ", repoKey, e.repository)
+ }
+ if strings.Contains(e.path, "${{") {
+ fmt.Fprintf(&sb, " %s: %s\n", pathKey, githubExpressionWhitespaceReplacer.Replace(e.path))
+ } else {
+ writeYAMLEnv(&sb, " ", pathKey, e.path)
+ }
+ }
+ sb.WriteString(" with:\n")
+ sb.WriteString(" script: |\n")
+ sb.WriteString(" const { main } = require('${{ runner.temp }}/gh-aw/actions/build_checkout_manifest.cjs');\n")
+ sb.WriteString(" await main();\n")
return []string{sb.String()}
}
-// shellSingleQuote returns s wrapped in single quotes, escaping any embedded single quotes.
-func shellSingleQuote(s string) string {
- return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'"
-}
-
// GenerateGitHubFolderCheckoutStep generates YAML step lines for a sparse checkout of
// the .github and .agents folders. This is used in the activation job to access workflow
// configuration and runtime imports.
diff --git a/pkg/workflow/compiler_yaml_main_job.go b/pkg/workflow/compiler_yaml_main_job.go
index f0fbb29f3b4..7043017e68e 100644
--- a/pkg/workflow/compiler_yaml_main_job.go
+++ b/pkg/workflow/compiler_yaml_main_job.go
@@ -143,7 +143,7 @@ func (c *Compiler) generateInitialAndCheckoutSteps(yaml *strings.Builder, data *
// Emit a manifest step that records the path and resolved default branch for each
// non-default cross-repo checkout. The safe-outputs MCP server reads this file to
// resolve base branches without making any credentialed network calls.
- for _, line := range checkoutMgr.GenerateCheckoutManifestStep() {
+ for _, line := range checkoutMgr.GenerateCheckoutManifestStep(c.getActionPin) {
yaml.WriteString(line)
}
From 45bcda4e8dfaa237e49e3f4d1394f05636dcb134 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 17:13:06 +0000
Subject: [PATCH 4/8] chore: plan follow-up for review feedback
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.../smoke-create-cross-repo-pr.lock.yml | 32 +++++--------------
.../smoke-update-cross-repo-pr.lock.yml | 32 +++++--------------
2 files changed, 16 insertions(+), 48 deletions(-)
diff --git a/.github/workflows/smoke-create-cross-repo-pr.lock.yml b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
index 7f2b2e5838b..7d6f7720071 100644
--- a/.github/workflows/smoke-create-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
@@ -508,32 +508,16 @@ jobs:
repository: github/gh-aw-side-repo
token: ${{ secrets.GH_AW_SIDE_REPO_PAT }}
- name: Build checkout manifest for safe-outputs handlers
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- run: |
- set -euo pipefail
- mkdir -p "${RUNNER_TEMP}/gh-aw"
- manifest="${RUNNER_TEMP}/gh-aw/checkout-manifest.json"
- printf '{}' > "$manifest"
- resolve_default_branch() {
- local repo="$1" path="$2" db=""
- if [ -d "${GITHUB_WORKSPACE}/${path}/.git" ]; then
- db=$(git -C "${GITHUB_WORKSPACE}/${path}" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||' || true)
- fi
- if [ -z "$db" ]; then
- db=$(gh api "repos/${repo}" --jq '.default_branch' 2>/dev/null || true)
- fi
- printf '%s' "$db"
- }
- repo='github/gh-aw-side-repo'
- path=''
- db=$(resolve_default_branch "$repo" "$path")
- tmp=$(mktemp)
- jq --arg repo "$repo" --arg path "$path" --arg db "$db" \
- '.[($repo | ascii_downcase)] = {repository: $repo, path: $path, default_branch: $db}' \
- "$manifest" > "$tmp" && mv "$tmp" "$manifest"
- echo "checkout-manifest: ${repo} -> path=${path} default_branch=${db:-}"
- cat "$manifest"
+ GH_AW_CHECKOUT_MANIFEST_COUNT: "1"
+ GH_AW_CHECKOUT_REPO_0: "github/gh-aw-side-repo"
+ GH_AW_CHECKOUT_PATH_0: ""
+ with:
+ script: |
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/build_checkout_manifest.cjs');
+ await main();
- name: Create gh-aw temp directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
- name: Configure gh CLI for GitHub Enterprise
diff --git a/.github/workflows/smoke-update-cross-repo-pr.lock.yml b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
index 8d6a5ca9818..c1790922973 100644
--- a/.github/workflows/smoke-update-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
@@ -527,32 +527,16 @@ jobs:
header=$(printf "x-access-token:%s" "${GH_AW_FETCH_TOKEN}" | base64 -w 0)
git -c "http.extraheader=Authorization: Basic ${header}" fetch origin '+refs/heads/main:refs/remotes/origin/main' '+refs/pull/*/head:refs/remotes/origin/pull/*/head'
- name: Build checkout manifest for safe-outputs handlers
+ uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
- run: |
- set -euo pipefail
- mkdir -p "${RUNNER_TEMP}/gh-aw"
- manifest="${RUNNER_TEMP}/gh-aw/checkout-manifest.json"
- printf '{}' > "$manifest"
- resolve_default_branch() {
- local repo="$1" path="$2" db=""
- if [ -d "${GITHUB_WORKSPACE}/${path}/.git" ]; then
- db=$(git -C "${GITHUB_WORKSPACE}/${path}" symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|^origin/||' || true)
- fi
- if [ -z "$db" ]; then
- db=$(gh api "repos/${repo}" --jq '.default_branch' 2>/dev/null || true)
- fi
- printf '%s' "$db"
- }
- repo='github/gh-aw-side-repo'
- path=''
- db=$(resolve_default_branch "$repo" "$path")
- tmp=$(mktemp)
- jq --arg repo "$repo" --arg path "$path" --arg db "$db" \
- '.[($repo | ascii_downcase)] = {repository: $repo, path: $path, default_branch: $db}' \
- "$manifest" > "$tmp" && mv "$tmp" "$manifest"
- echo "checkout-manifest: ${repo} -> path=${path} default_branch=${db:-}"
- cat "$manifest"
+ GH_AW_CHECKOUT_MANIFEST_COUNT: "1"
+ GH_AW_CHECKOUT_REPO_0: "github/gh-aw-side-repo"
+ GH_AW_CHECKOUT_PATH_0: ""
+ with:
+ script: |
+ const { main } = require('${{ runner.temp }}/gh-aw/actions/build_checkout_manifest.cjs');
+ await main();
- name: Create gh-aw temp directory
run: bash "${RUNNER_TEMP}/gh-aw/actions/create_gh_aw_tmp_dir.sh"
- name: Configure gh CLI for GitHub Enterprise
From 71f094a0f5fc07e90957c652e5437d601efb4c90 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 17:20:29 +0000
Subject: [PATCH 5/8] Add checkout manifest logging and shim core usage
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
actions/setup/js/build_checkout_manifest.cjs | 31 +++++++++++---------
1 file changed, 17 insertions(+), 14 deletions(-)
diff --git a/actions/setup/js/build_checkout_manifest.cjs b/actions/setup/js/build_checkout_manifest.cjs
index b824ae14bbe..8b4c826761f 100644
--- a/actions/setup/js/build_checkout_manifest.cjs
+++ b/actions/setup/js/build_checkout_manifest.cjs
@@ -1,6 +1,8 @@
// @ts-check
///
+require("./shim.cjs");
+
const fs = require("fs");
const path = require("path");
const { execFileSync } = require("child_process");
@@ -44,10 +46,9 @@ function resolveDefaultBranch(repository, checkoutPath, options = {}) {
stdio: ["ignore", "pipe", "pipe"],
});
defaultBranch = output.trim().replace(/^origin\//, "");
+ core.debug(`build_checkout_manifest: git resolved default branch for ${repository}: ${defaultBranch}`);
} catch (error) {
- if (typeof core !== "undefined") {
- core.debug(`build_checkout_manifest: git default branch lookup failed for ${repository}: ${getErrorMessage(error)}`);
- }
+ core.debug(`build_checkout_manifest: git default branch lookup failed for ${repository}: ${getErrorMessage(error)}`);
}
}
@@ -56,10 +57,9 @@ function resolveDefaultBranch(repository, checkoutPath, options = {}) {
defaultBranch = runGH(["api", `repos/${repository}`, "--jq", ".default_branch"], {
stdio: ["ignore", "pipe", "pipe"],
}).trim();
+ core.debug(`build_checkout_manifest: gh api resolved default branch for ${repository}: ${defaultBranch}`);
} catch (error) {
- if (typeof core !== "undefined") {
- core.debug(`build_checkout_manifest: gh api default branch lookup failed for ${repository}: ${getErrorMessage(error)}`);
- }
+ core.debug(`build_checkout_manifest: gh api default branch lookup failed for ${repository}: ${getErrorMessage(error)}`);
}
}
@@ -79,11 +79,18 @@ function buildCheckoutManifest(entries, options = {}) {
fs.mkdirSync(manifestDir, { recursive: true });
const manifestPath = path.join(manifestDir, "checkout-manifest.json");
const manifest = {};
+ core.info(`checkout-manifest: building manifest for ${entries.length} checkout entries`);
for (const entry of entries) {
- if (!entry || typeof entry !== "object") continue;
+ if (!entry || typeof entry !== "object") {
+ core.debug("checkout-manifest: skipping non-object entry");
+ continue;
+ }
const repository = String(entry.repository || "").trim();
- if (repository === "") continue;
+ if (repository === "") {
+ core.debug("checkout-manifest: skipping entry with empty repository");
+ continue;
+ }
const checkoutPath = String(entry.path || "");
const defaultBranch = resolveDefaultBranch(repository, checkoutPath, {
workspace: options.workspace,
@@ -95,15 +102,11 @@ function buildCheckoutManifest(entries, options = {}) {
path: checkoutPath,
default_branch: defaultBranch,
};
- if (typeof core !== "undefined") {
- core.info(`checkout-manifest: ${repository} -> path=${checkoutPath} default_branch=${defaultBranch || ""}`);
- }
+ core.info(`checkout-manifest: ${repository} -> path=${checkoutPath} default_branch=${defaultBranch || ""}`);
}
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf8");
- if (typeof core !== "undefined") {
- core.info(`checkout-manifest written to ${manifestPath}`);
- }
+ core.info(`checkout-manifest written to ${manifestPath}`);
return { manifestPath, manifest };
}
From 627d66bc2a50e10edc429986ece5449bdc050fea Mon Sep 17 00:00:00 2001
From: Don Syme
Date: Tue, 9 Jun 2026 18:27:59 +0100
Subject: [PATCH 6/8] Add checkout-credential-review skill and wire from
AGENTS.md
---
.../checkout-credential-review/SKILL.md | 36 +++++++++++++++++++
AGENTS.md | 1 +
2 files changed, 37 insertions(+)
create mode 100644 .github/skills/checkout-credential-review/SKILL.md
diff --git a/.github/skills/checkout-credential-review/SKILL.md b/.github/skills/checkout-credential-review/SKILL.md
new file mode 100644
index 00000000000..30e559f74e4
--- /dev/null
+++ b/.github/skills/checkout-credential-review/SKILL.md
@@ -0,0 +1,36 @@
+---
+name: checkout-credential-review
+description: Review code that performs git or gh operations against repository checkouts in gh-aw, checking that the right credentials are available at the right time and that sparseness, shallowness and credential-free factors are properly considered.
+---
+
+# Checkout Credential Review
+
+Use this skill when reviewing or writing code in `pkg/workflow/`, `actions/setup/js/`, or compiled `.lock.yml` workflows that runs `git`, `gh`, or any other remote-touching operation against a repository checkout.
+
+## Background
+
+Each entry in a workflow's `checkout:` block may declare its own credentials (`github-token:`, `github-app:`), and the compiler wires those into the corresponding `actions/checkout` step ([pkg/workflow/checkout_step_generator.go](pkg/workflow/checkout_step_generator.go)). Generated checkouts always set `persist-credentials: false`, so the on-disk repo retains **no** credentials after the step finishes — only `actions/checkout`'s own internal token is used during the clone, and it is scrubbed in its post-step.
+
+A separate step that wants to authenticate later must either (a) re-inject a token at command level (e.g. `git -c http.extraheader=...`) or (b) be passed the per-checkout token via env. The compiler does *not* automatically thread per-checkout `github-token`s into downstream steps.
+
+Two important contexts deliberately run with **no git credentials**:
+
+- The **safe-outputs MCP server** and its handlers (`generate_git_bundle.cjs`, `generate_git_patch.cjs`, `create_pull_request.cjs`). Errors in these paths explicitly say "the safe-outputs MCP server has no credentials for private repositories" — fetch/push will fail for private repos.
+- The **agent runtime** after `actions/checkout`. The agent prompt in [actions/setup/md/safe_outputs_push_to_pr_branch.md](actions/setup/md/safe_outputs_push_to_pr_branch.md) explicitly tells the model not to attempt `git fetch`, `git pull`, `git push`, or any other authenticated git operation, and to report unavailable branches rather than try to fetch them.
+
+## Review checklist
+
+When you see a new `git`, `gh`, `execFileSync('git'…)`, or compiled `run:` block:
+
+1. **Does it touch a remote?** Local-only commands (`symbolic-ref`, `rev-parse`, `log`, `show`, `merge-base`, `diff`, `status`) need no credentials. Anything in `fetch | pull | push | clone | ls-remote | remote (set-url|add|update)` does, plus on-demand blob fetches in partial clones.
+2. **Which checkout is it operating on?** If it's a cross-repo entry from `checkout:`, the relevant credential is *that entry's* `github-token`, not the workflow's default `GITHUB_TOKEN`. Confirm the per-entry token is actually threaded into the step's env (or refuse to do remote operations and degrade gracefully).
+3. **Which job/context emits it?** Agent job and safe-outputs MCP server both run without git credentials by design. Any remote git operation there must be wrapped in `try/catch`, fail soft, and surface a clear "no credentials" error rather than a raw git stderr.
+4. **Sparse / shallow / monorepo concerns.** Avoid emitting steps that deepen (`git fetch --unshallow`, `--deepen=N`) or widen (`git fetch origin '+refs/heads/*'`) a sparse or shallow checkout of a large monorepo — these need credentials *and* can pull hundreds of MB. Prefer expanding `fetch:` / `fetch-depth:` / `sparse-checkout:` at compile time so it happens during `actions/checkout` with its internal token, never later.
+5. **`gh` is REST, not git.** `gh api …` uses whatever `GH_TOKEN` is in the step's env — it does **not** automatically inherit per-checkout PATs. For cross-org private repos, either thread the right token in or accept the call will 404 and handle it.
+
+## Related
+
+- [docs/src/content/docs/reference/checkout.md](docs/src/content/docs/reference/checkout.md) — "Git Credentials After Checkout"
+- [docs/sparseness.md](docs/sparseness.md) — sparse/blobless credential lifecycle
+- [pkg/workflow/checkout_step_generator.go](pkg/workflow/checkout_step_generator.go) — token wiring per checkout
+- [actions/setup/md/safe_outputs_push_to_pr_branch.md](actions/setup/md/safe_outputs_push_to_pr_branch.md) — agent-facing guidance
diff --git a/AGENTS.md b/AGENTS.md
index 195392da6f8..a6439ed93e3 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -50,6 +50,7 @@ Use skills only when the task requires specialized guidance. Do not pre-load eve
- GitHub MCP usage patterns → `.github/skills/github-mcp-server/SKILL.md`
- Query helpers for issues/PRs/workflows/discussions/labels → matching `.github/skills/github-*-query/SKILL.md`
- Doc-writing conventions → `.github/skills/documentation/SKILL.md`
+- Reviewing or writing `git`/`gh`/remote operations against checkouts (per-checkout credentials, sparse/shallow monorepos, safe-outputs MCP runs without credentials) → `.github/skills/checkout-credential-review/SKILL.md`
## Why this file is intentionally short
From 5a208c91ed5226ee3e673fd050752fa2a9c1944f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 17:44:43 +0000
Subject: [PATCH 7/8] Use per-checkout tokens for checkout-manifest gh fallback
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
actions/setup/js/build_checkout_manifest.cjs | 11 +++-
.../setup/js/build_checkout_manifest.test.cjs | 13 ++++-
pkg/workflow/checkout_manager_test.go | 20 ++++++-
pkg/workflow/checkout_step_generator.go | 56 +++++++++++--------
4 files changed, 71 insertions(+), 29 deletions(-)
diff --git a/actions/setup/js/build_checkout_manifest.cjs b/actions/setup/js/build_checkout_manifest.cjs
index 8b4c826761f..c267d07aa4f 100644
--- a/actions/setup/js/build_checkout_manifest.cjs
+++ b/actions/setup/js/build_checkout_manifest.cjs
@@ -28,6 +28,7 @@ function readManifestEntriesFromEnv() {
entries.push({
repository: process.env[`GH_AW_CHECKOUT_REPO_${i}`] || "",
path: process.env[`GH_AW_CHECKOUT_PATH_${i}`] || "",
+ token: process.env[`GH_AW_CHECKOUT_TOKEN_${i}`] || "",
});
}
return entries;
@@ -54,9 +55,14 @@ function resolveDefaultBranch(repository, checkoutPath, options = {}) {
if (defaultBranch === "") {
try {
- defaultBranch = runGH(["api", `repos/${repository}`, "--jq", ".default_branch"], {
+ const checkoutToken = String(options.checkoutToken || "");
+ const ghExecOptions = {
stdio: ["ignore", "pipe", "pipe"],
- }).trim();
+ };
+ if (checkoutToken !== "") {
+ ghExecOptions.env = { ...process.env, GH_TOKEN: checkoutToken };
+ }
+ defaultBranch = runGH(["api", `repos/${repository}`, "--jq", ".default_branch"], ghExecOptions).trim();
core.debug(`build_checkout_manifest: gh api resolved default branch for ${repository}: ${defaultBranch}`);
} catch (error) {
core.debug(`build_checkout_manifest: gh api default branch lookup failed for ${repository}: ${getErrorMessage(error)}`);
@@ -96,6 +102,7 @@ function buildCheckoutManifest(entries, options = {}) {
workspace: options.workspace,
runGit,
runGH,
+ checkoutToken: String(entry.token || ""),
});
manifest[repository.toLowerCase()] = {
repository,
diff --git a/actions/setup/js/build_checkout_manifest.test.cjs b/actions/setup/js/build_checkout_manifest.test.cjs
index 9115ff2d9be..1da95cb1635 100644
--- a/actions/setup/js/build_checkout_manifest.test.cjs
+++ b/actions/setup/js/build_checkout_manifest.test.cjs
@@ -53,12 +53,13 @@ describe("build_checkout_manifest.cjs", () => {
setEnv("GH_AW_CHECKOUT_MANIFEST_COUNT", "2");
setEnv("GH_AW_CHECKOUT_REPO_0", "owner/a");
setEnv("GH_AW_CHECKOUT_PATH_0", "./a");
+ setEnv("GH_AW_CHECKOUT_TOKEN_0", "${{ secrets.REPO_A_TOKEN }}");
setEnv("GH_AW_CHECKOUT_REPO_1", "owner/b");
setEnv("GH_AW_CHECKOUT_PATH_1", "");
expect(readManifestEntriesFromEnv()).toEqual([
- { repository: "owner/a", path: "./a" },
- { repository: "owner/b", path: "" },
+ { repository: "owner/a", path: "./a", token: "${{ secrets.REPO_A_TOKEN }}" },
+ { repository: "owner/b", path: "", token: "" },
]);
});
@@ -88,13 +89,19 @@ describe("build_checkout_manifest.cjs", () => {
it("falls back to gh api when local git default branch is unavailable", () => {
const workspace = createTempDir("checkout-manifest-workspace-");
tempDirs.push(workspace);
+ let ghOptions = null;
const defaultBranch = resolveDefaultBranch("owner/repo", "missing", {
workspace,
- runGH: () => "trunk\n",
+ checkoutToken: "${{ secrets.CROSS_REPO_PAT }}",
+ runGH: (_args, options) => {
+ ghOptions = options;
+ return "trunk\n";
+ },
});
expect(defaultBranch).toBe("trunk");
+ expect(ghOptions?.env?.GH_TOKEN).toBe("${{ secrets.CROSS_REPO_PAT }}");
});
it("writes manifest with lowercase keys", () => {
diff --git a/pkg/workflow/checkout_manager_test.go b/pkg/workflow/checkout_manager_test.go
index 7275bfd8043..9ab8c22d4fe 100644
--- a/pkg/workflow/checkout_manager_test.go
+++ b/pkg/workflow/checkout_manager_test.go
@@ -1408,7 +1408,7 @@ func TestGenerateCheckoutManifestStep(t *testing.T) {
t.Run("cross-repo additional checkout emits entry", func(t *testing.T) {
cm := NewCheckoutManager([]*CheckoutConfig{
- {Repository: "owner/other", Path: "./other"},
+ {Repository: "owner/other", Path: "./other", GitHubToken: "${{ secrets.CROSS_REPO_PAT }}"},
})
steps := cm.GenerateCheckoutManifestStep(getActionPin)
require.Len(t, steps, 1, "should emit one manifest step")
@@ -1419,6 +1419,7 @@ func TestGenerateCheckoutManifestStep(t *testing.T) {
assert.Contains(t, out, `GH_AW_CHECKOUT_MANIFEST_COUNT: "1"`)
assert.Contains(t, out, `GH_AW_CHECKOUT_REPO_0: "owner/other"`)
assert.Contains(t, out, `GH_AW_CHECKOUT_PATH_0: "./other"`)
+ assert.Contains(t, out, "GH_AW_CHECKOUT_TOKEN_0: ${{ secrets.CROSS_REPO_PAT }}")
assert.Contains(t, out, "build_checkout_manifest.cjs")
assert.NotContains(t, out, "run: |", "manifest step should use github-script instead of shell run block")
})
@@ -1468,4 +1469,21 @@ func TestGenerateCheckoutManifestStep(t *testing.T) {
require.Len(t, steps, 1)
assert.Contains(t, steps[0], "GH_AW_CHECKOUT_REPO_0: ${{ github.event.inputs.trigger_ref }}")
})
+
+ t.Run("github-app token expression preserves checkout index in manifest env", func(t *testing.T) {
+ cm := NewCheckoutManager([]*CheckoutConfig{
+ {Path: "./local"},
+ {
+ Repository: "owner/private",
+ Path: "./private",
+ GitHubApp: &GitHubAppConfig{
+ AppID: "${{ vars.APP_ID }}",
+ PrivateKey: "${{ secrets.APP_PRIVATE_KEY }}",
+ },
+ },
+ })
+ steps := cm.GenerateCheckoutManifestStep(getActionPin)
+ require.Len(t, steps, 1)
+ assert.Contains(t, steps[0], "GH_AW_CHECKOUT_TOKEN_0: ${{ steps.checkout-app-token-1.outputs.token }}")
+ })
}
diff --git a/pkg/workflow/checkout_step_generator.go b/pkg/workflow/checkout_step_generator.go
index f9d0f5f9cf0..66ecf56a5df 100644
--- a/pkg/workflow/checkout_step_generator.go
+++ b/pkg/workflow/checkout_step_generator.go
@@ -84,16 +84,21 @@ func (cm *CheckoutManager) GenerateCheckoutManifestStep(getActionPin func(string
type manifestEntry struct {
repository string
path string
+ token string
}
var entries []manifestEntry
- for _, entry := range cm.ordered {
+ for i, entry := range cm.ordered {
if entry.key.wiki {
continue
}
if entry.key.repository == "" {
continue
}
- entries = append(entries, manifestEntry{repository: entry.key.repository, path: entry.key.path})
+ entries = append(entries, manifestEntry{
+ repository: entry.key.repository,
+ path: entry.key.path,
+ token: resolveCheckoutTokenExpression(entry, i, false),
+ })
}
if len(entries) == 0 {
return nil
@@ -120,6 +125,14 @@ func (cm *CheckoutManager) GenerateCheckoutManifestStep(getActionPin func(string
} else {
writeYAMLEnv(&sb, " ", pathKey, e.path)
}
+ if e.token != "" {
+ tokenKey := fmt.Sprintf("GH_AW_CHECKOUT_TOKEN_%d", i)
+ if strings.Contains(e.token, "${{") {
+ fmt.Fprintf(&sb, " %s: %s\n", tokenKey, githubExpressionWhitespaceReplacer.Replace(e.token))
+ } else {
+ writeYAMLEnv(&sb, " ", tokenKey, e.token)
+ }
+ }
}
sb.WriteString(" with:\n")
sb.WriteString(" script: |\n")
@@ -328,15 +341,7 @@ func generateCheckoutStepLines(entry *resolvedCheckout, index int, getActionPin
fmt.Fprintf(&sb, " path: %s\n", entry.key.path)
}
// Determine effective token: github-app-minted token takes precedence
- effectiveToken := entry.token
- if entry.githubApp != nil {
- // The token is minted in the agent job itself (same-job step reference).
- //nolint:gosec // G101: False positive - this is a GitHub Actions expression template placeholder, not a hardcoded credential
- effectiveToken = fmt.Sprintf("${{ steps.checkout-app-token-%d.outputs.token }}", index)
- if entry.githubApp.shouldIgnoreMissingKey() {
- effectiveToken = combineTokenExpressions(effectiveToken, getEffectiveGitHubToken(entry.token))
- }
- }
+ effectiveToken := resolveCheckoutTokenExpression(entry, index, false)
if effectiveToken != "" {
fmt.Fprintf(&sb, " token: %s\n", effectiveToken)
}
@@ -458,18 +463,7 @@ func generateFetchStepLines(entry *resolvedCheckout, index int) string {
}
// Determine authentication token
- token := entry.token
- if entry.githubApp != nil {
- // The token is minted in the agent job itself (same-job step reference).
- //nolint:gosec // G101: False positive - this is a GitHub Actions expression template placeholder, not a hardcoded credential
- token = fmt.Sprintf("${{ steps.checkout-app-token-%d.outputs.token }}", index)
- if entry.githubApp.shouldIgnoreMissingKey() {
- token = combineTokenExpressions(token, getEffectiveGitHubToken(entry.token))
- }
- }
- if token == "" {
- token = getEffectiveGitHubToken("")
- }
+ token := resolveCheckoutTokenExpression(entry, index, true)
// Build refspecs
refspecs := make([]string, 0, len(entry.fetchRefs))
@@ -507,3 +501,19 @@ func generateFetchStepLines(entry *resolvedCheckout, index int) string {
gitPrefix, depthFlag, strings.Join(refspecs, " "))
return sb.String()
}
+
+func resolveCheckoutTokenExpression(entry *resolvedCheckout, index int, defaultWhenEmpty bool) string {
+ token := entry.token
+ if entry.githubApp != nil {
+ // The token is minted in the agent job itself (same-job step reference).
+ //nolint:gosec // G101: False positive - this is a GitHub Actions expression template placeholder, not a hardcoded credential
+ token = fmt.Sprintf("${{ steps.checkout-app-token-%d.outputs.token }}", index)
+ if entry.githubApp.shouldIgnoreMissingKey() {
+ token = combineTokenExpressions(token, getEffectiveGitHubToken(entry.token))
+ }
+ }
+ if token == "" && defaultWhenEmpty {
+ token = getEffectiveGitHubToken("")
+ }
+ return token
+}
From ba29dbf9fc043ba93759c2c41fa188c344a2bab9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 9 Jun 2026 17:58:44 +0000
Subject: [PATCH 8/8] Use per-checkout tokens in checkout manifest fallback and
recompile
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
---
.../smoke-create-cross-repo-pr.lock.yml | 1 +
.../smoke-update-cross-repo-pr.lock.yml | 1 +
actions/setup/js/build_checkout_manifest.cjs | 15 +++++++---
pkg/workflow/checkout_step_generator.go | 28 +++++++++----------
4 files changed, 27 insertions(+), 18 deletions(-)
diff --git a/.github/workflows/smoke-create-cross-repo-pr.lock.yml b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
index 7d6f7720071..33690526074 100644
--- a/.github/workflows/smoke-create-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-create-cross-repo-pr.lock.yml
@@ -514,6 +514,7 @@ jobs:
GH_AW_CHECKOUT_MANIFEST_COUNT: "1"
GH_AW_CHECKOUT_REPO_0: "github/gh-aw-side-repo"
GH_AW_CHECKOUT_PATH_0: ""
+ GH_AW_CHECKOUT_TOKEN_0: ${{ secrets.GH_AW_SIDE_REPO_PAT }}
with:
script: |
const { main } = require('${{ runner.temp }}/gh-aw/actions/build_checkout_manifest.cjs');
diff --git a/.github/workflows/smoke-update-cross-repo-pr.lock.yml b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
index c1790922973..0ccc6399f1e 100644
--- a/.github/workflows/smoke-update-cross-repo-pr.lock.yml
+++ b/.github/workflows/smoke-update-cross-repo-pr.lock.yml
@@ -533,6 +533,7 @@ jobs:
GH_AW_CHECKOUT_MANIFEST_COUNT: "1"
GH_AW_CHECKOUT_REPO_0: "github/gh-aw-side-repo"
GH_AW_CHECKOUT_PATH_0: ""
+ GH_AW_CHECKOUT_TOKEN_0: ${{ secrets.GH_AW_SIDE_REPO_PAT }}
with:
script: |
const { main } = require('${{ runner.temp }}/gh-aw/actions/build_checkout_manifest.cjs');
diff --git a/actions/setup/js/build_checkout_manifest.cjs b/actions/setup/js/build_checkout_manifest.cjs
index c267d07aa4f..0e77a571407 100644
--- a/actions/setup/js/build_checkout_manifest.cjs
+++ b/actions/setup/js/build_checkout_manifest.cjs
@@ -37,7 +37,14 @@ function readManifestEntriesFromEnv() {
function resolveDefaultBranch(repository, checkoutPath, options = {}) {
const workspace = options.workspace || process.env.GITHUB_WORKSPACE || "";
const runGit = options.runGit || ((args, execOptions = {}) => execFileSync("git", args, { encoding: "utf8", ...execOptions }));
- const runGH = options.runGH || ((args, execOptions = {}) => execFileSync("gh", args, { encoding: "utf8", ...execOptions }));
+ const runGH =
+ options.runGH ||
+ ((args, execOptions = {}) =>
+ execFileSync("gh", args, {
+ encoding: "utf8",
+ env: { ...process.env, ...(execOptions.env || {}) },
+ ...execOptions,
+ }));
let defaultBranch = "";
const repoPath = checkoutPath ? path.join(workspace, checkoutPath) : workspace;
@@ -55,12 +62,12 @@ function resolveDefaultBranch(repository, checkoutPath, options = {}) {
if (defaultBranch === "") {
try {
- const checkoutToken = String(options.checkoutToken || "");
+ const checkoutToken = options.checkoutToken || "";
const ghExecOptions = {
stdio: ["ignore", "pipe", "pipe"],
};
if (checkoutToken !== "") {
- ghExecOptions.env = { ...process.env, GH_TOKEN: checkoutToken };
+ ghExecOptions.env = { GH_TOKEN: checkoutToken };
}
defaultBranch = runGH(["api", `repos/${repository}`, "--jq", ".default_branch"], ghExecOptions).trim();
core.debug(`build_checkout_manifest: gh api resolved default branch for ${repository}: ${defaultBranch}`);
@@ -102,7 +109,7 @@ function buildCheckoutManifest(entries, options = {}) {
workspace: options.workspace,
runGit,
runGH,
- checkoutToken: String(entry.token || ""),
+ checkoutToken: entry.token || "",
});
manifest[repository.toLowerCase()] = {
repository,
diff --git a/pkg/workflow/checkout_step_generator.go b/pkg/workflow/checkout_step_generator.go
index 66ecf56a5df..11ef5c81d47 100644
--- a/pkg/workflow/checkout_step_generator.go
+++ b/pkg/workflow/checkout_step_generator.go
@@ -31,11 +31,11 @@ func wikiRepository(repository string) string {
func (cm *CheckoutManager) GenerateCheckoutAppTokenSteps(c *Compiler, permissions *Permissions) []string {
checkoutManagerLog.Printf("Building app token minting steps for %d checkout entries", len(cm.ordered))
var steps []string
- for i, entry := range cm.ordered {
+ for checkoutIndex, entry := range cm.ordered {
if entry.githubApp == nil {
continue
}
- checkoutManagerLog.Printf("Generating app token minting step for checkout index=%d repo=%q", i, entry.key.repository)
+ checkoutManagerLog.Printf("Generating app token minting step for checkout index=%d repo=%q", checkoutIndex, entry.key.repository)
// Pass empty fallback so the app token defaults to github.event.repository.name.
// Checkout-specific cross-repo scoping is handled via the explicit repository field.
steps = append(steps, c.buildGitHubAppTokenMintStepWithMeta(
@@ -43,8 +43,8 @@ func (cm *CheckoutManager) GenerateCheckoutAppTokenSteps(c *Compiler, permission
permissions,
"",
entry.key.repository,
- fmt.Sprintf("Generate GitHub App token for checkout (%d)", i),
- fmt.Sprintf("checkout-app-token-%d", i),
+ fmt.Sprintf("Generate GitHub App token for checkout (%d)", checkoutIndex),
+ fmt.Sprintf("checkout-app-token-%d", checkoutIndex),
)...)
}
return steps
@@ -56,12 +56,12 @@ func (cm *CheckoutManager) GenerateCheckoutAppTokenSteps(c *Compiler, permission
func (cm *CheckoutManager) GenerateAdditionalCheckoutSteps(getActionPin func(string) string) []string {
checkoutManagerLog.Printf("Generating additional checkout steps from %d configured entries", len(cm.ordered))
var lines []string
- for i, entry := range cm.ordered {
+ for checkoutIndex, entry := range cm.ordered {
// Skip the default checkout (handled separately)
if entry.key.path == "" && entry.key.repository == "" {
continue
}
- lines = append(lines, generateCheckoutStepLines(entry, i, getActionPin)...)
+ lines = append(lines, generateCheckoutStepLines(entry, checkoutIndex, getActionPin)...)
}
checkoutManagerLog.Printf("Generated %d additional checkout step(s)", len(lines))
return lines
@@ -87,7 +87,7 @@ func (cm *CheckoutManager) GenerateCheckoutManifestStep(getActionPin func(string
token string
}
var entries []manifestEntry
- for i, entry := range cm.ordered {
+ for checkoutIndex, entry := range cm.ordered {
if entry.key.wiki {
continue
}
@@ -97,7 +97,7 @@ func (cm *CheckoutManager) GenerateCheckoutManifestStep(getActionPin func(string
entries = append(entries, manifestEntry{
repository: entry.key.repository,
path: entry.key.path,
- token: resolveCheckoutTokenExpression(entry, i, false),
+ token: resolveCheckoutTokenExpression(entry, checkoutIndex, false),
})
}
if len(entries) == 0 {
@@ -112,9 +112,9 @@ func (cm *CheckoutManager) GenerateCheckoutManifestStep(getActionPin func(string
sb.WriteString(" env:\n")
sb.WriteString(" GH_TOKEN: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}\n")
writeYAMLEnv(&sb, " ", "GH_AW_CHECKOUT_MANIFEST_COUNT", strconv.Itoa(len(entries)))
- for i, e := range entries {
- repoKey := fmt.Sprintf("GH_AW_CHECKOUT_REPO_%d", i)
- pathKey := fmt.Sprintf("GH_AW_CHECKOUT_PATH_%d", i)
+ for manifestIndex, e := range entries {
+ repoKey := fmt.Sprintf("GH_AW_CHECKOUT_REPO_%d", manifestIndex)
+ pathKey := fmt.Sprintf("GH_AW_CHECKOUT_PATH_%d", manifestIndex)
if strings.Contains(e.repository, "${{") {
fmt.Fprintf(&sb, " %s: %s\n", repoKey, githubExpressionWhitespaceReplacer.Replace(e.repository))
} else {
@@ -126,7 +126,7 @@ func (cm *CheckoutManager) GenerateCheckoutManifestStep(getActionPin func(string
writeYAMLEnv(&sb, " ", pathKey, e.path)
}
if e.token != "" {
- tokenKey := fmt.Sprintf("GH_AW_CHECKOUT_TOKEN_%d", i)
+ tokenKey := fmt.Sprintf("GH_AW_CHECKOUT_TOKEN_%d", manifestIndex)
if strings.Contains(e.token, "${{") {
fmt.Fprintf(&sb, " %s: %s\n", tokenKey, githubExpressionWhitespaceReplacer.Replace(e.token))
} else {
@@ -502,12 +502,12 @@ func generateFetchStepLines(entry *resolvedCheckout, index int) string {
return sb.String()
}
-func resolveCheckoutTokenExpression(entry *resolvedCheckout, index int, defaultWhenEmpty bool) string {
+func resolveCheckoutTokenExpression(entry *resolvedCheckout, checkoutIndex int, defaultWhenEmpty bool) string {
token := entry.token
if entry.githubApp != nil {
// The token is minted in the agent job itself (same-job step reference).
//nolint:gosec // G101: False positive - this is a GitHub Actions expression template placeholder, not a hardcoded credential
- token = fmt.Sprintf("${{ steps.checkout-app-token-%d.outputs.token }}", index)
+ token = fmt.Sprintf("${{ steps.checkout-app-token-%d.outputs.token }}", checkoutIndex)
if entry.githubApp.shouldIgnoreMissingKey() {
token = combineTokenExpressions(token, getEffectiveGitHubToken(entry.token))
}