Migrate Copilot PAT rotation to shared pat_pool import - #743
Merged
Conversation
Adopt the shared workflow import pattern from dotnet/runtime PR #127946, replacing the per-workflow select-copilot-pat action + custom job (shipped in #736) with a reusable shared/pat_pool.md import. What changed: - Add .github/workflows/shared/pat_pool.md: an import that defines a `pat_pool` job (inline bash, no separate action) exposing a `pat_number` output, plus an import-schema mapping COPILOT_PAT_0..7 to this repo's pool secrets (COPILOT_GITHUB_TOKEN, COPILOT_GITHUB_TOKEN_2..8). - Add .github/workflows/shared/pat_pool.README.md documenting the pattern. - Convert all 8 agentic workflows to `imports: - shared/pat_pool.md` + `engine.env` `case(needs.pat_pool.outputs.pat_number ...)`. - Delete the now-unused .github/actions/select-copilot-pat action. - Add .github/workflows/validate-pat-pool.yml: a daily standalone workflow that validates each pool PAT with a Copilot CLI request and summarizes pool health. Wiring note (adaptation from runtime): consuming workflows declare `on.needs: [pat_pool]` instead of runtime's `needs: [pre_activation]` + `on.permissions: {}`. This wires pat_pool ahead of the pre_activation and activation jobs so the selected PAT is validated by the activation job and used by the agent, and it works for `roles: all` workflows (issue-triage), which do not produce a pre_activation job for the runtime workaround to attach to. Compiled with gh-aw v0.77.5. Verified end-to-end with a temporary test-pat-rotation workflow (since removed): a run selected token #2 of the 3-token pool and the agent job observed pat_number='2', confirming the rotated PAT reaches the agent.
JanKrivanek
force-pushed
the
dev/jankrivanek/pat-pool-import
branch
from
June 9, 2026 19:24
286d5f5 to
3dfa153
Compare
JanKrivanek
marked this pull request as ready for review
June 10, 2026 08:19
JanKrivanek
enabled auto-merge (squash)
June 10, 2026 08:19
ViktorHofer
approved these changes
Jun 10, 2026
Contributor
There was a problem hiding this comment.
Pull request overview
This PR migrates Copilot PAT rotation across this repo’s agentic workflows from a per-workflow local action/job to a shared gh aw workflow import (shared/pat_pool.md), aligning with the shared pattern used across dotnet org repos. It also adds a scheduled workflow to proactively validate the health of the PAT pool.
Changes:
- Added a shared workflow import (
.github/workflows/shared/pat_pool.md) plus documentation (pat_pool.README.md) to select a random non-empty PAT slot and exposepat_number. - Updated agentic workflows to import
pat_pooland useneeds.pat_pool.outputs.pat_numberinengine.envto rotateCOPILOT_GITHUB_TOKEN. - Removed the local
.github/actions/select-copilot-pataction and added a new dailyvalidate-pat-pool.ymlworkflow to validate each PAT in the pool.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/validate-pat-pool.yml | New daily/manual workflow to validate each PAT in the pool via a Copilot CLI probe and produce a summary. |
| .github/workflows/shared/pat_pool.md | New shared import defining the pat_pool job that selects a PAT slot and outputs pat_number. |
| .github/workflows/shared/pat_pool.README.md | New documentation for onboarding, usage, and security/design notes for the shared PAT pool import. |
| .github/workflows/pr-malicious-scan.agent.md | Switched from local select_copilot_pat job to importing shared/pat_pool.md + on.needs: [pat_pool]. |
| .github/workflows/pr-malicious-scan.agent.lock.yml | Recompiled lock workflow reflecting the new pat_pool job dependency and token mapping. |
| .github/workflows/markdown-linter.md | Switched to imports: shared/pat_pool.md + on.needs: [pat_pool] and updated engine.env mapping. |
| .github/workflows/markdown-linter.lock.yml | Recompiled lock workflow reflecting pat_pool dependency and updated secret mapping. |
| .github/workflows/issue-triage.md | Switched to importing shared/pat_pool.md and updated engine.env mapping to use needs.pat_pool. |
| .github/workflows/issue-triage.lock.yml | Recompiled lock workflow reflecting pat_pool dependency and updated secret mapping. |
| .github/workflows/issue-investigate.md | Switched to importing shared/pat_pool.md and updated engine.env mapping to use needs.pat_pool. |
| .github/workflows/issue-investigate.lock.yml | Recompiled lock workflow reflecting pat_pool dependency and updated secret mapping. |
| .github/workflows/devops-health-investigate.md | Updated to use imported pat_pool and updated engine.env mapping; added import alongside existing devops imports. |
| .github/workflows/devops-health-investigate.lock.yml | Recompiled lock workflow reflecting pat_pool dependency and updated secret mapping. |
| .github/workflows/devops-health-groom.md | Updated to use imported pat_pool and updated engine.env mapping; added import alongside existing devops imports. |
| .github/workflows/devops-health-groom.lock.yml | Recompiled lock workflow reflecting pat_pool dependency and updated secret mapping. |
| .github/workflows/devops-health-check.md | Updated to use imported pat_pool and updated engine.env mapping; added import alongside existing devops imports. |
| .github/workflows/devops-health-check.lock.yml | Recompiled lock workflow reflecting pat_pool dependency and updated secret mapping. |
| .github/workflows/close-stale-prs.agent.md | Switched to importing shared/pat_pool.md + on.needs: [pat_pool] and updated engine.env mapping. |
| .github/workflows/close-stale-prs.agent.lock.yml | Recompiled lock workflow reflecting pat_pool dependency and updated secret mapping. |
| .github/actions/select-copilot-pat/README.md | Removed (rotation logic now lives in the shared import). |
| .github/actions/select-copilot-pat/action.yml | Removed (rotation logic now lives in the shared import). |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 21/21 changed files
- Comments generated: 4
|
|
||
| ## Workflow Output Attribution | ||
|
|
||
| Team members' PATs are _only_ used for the Copilot requests from within the agentic portion of the workflow. All outputs from the workflow use the `github-actions[bot]` account token. Issues, PRs, comments, and all other content generated by the workflow will be attributed to `github-actions[bot]`--not the team member's account or token. |
| # Unspecified inputs default to the repository's COPILOT_GITHUB_TOKEN[_#] secrets | ||
| ``` | ||
|
|
||
| The secrets passed via `with:` must match the secrets referenced in the consuming workflow's `case` expression that overrides `COPILOT_GITHUB_TOKEN`--both sides need to agree on which secret backs each `COPILOT_PAT_#` slot. Update the `case` expression accordingly: |
| 1. **Secrets adhere to existing trust boundaries.** The pool of PAT secrets is | ||
| provided to a dedicated step within the `pat_pool` job. That job runs | ||
| before the activation gate and contains only the trusted token-selection | ||
| step--no untrusted context or input is within scope. The |
Comment on lines
+24
to
+27
| - name: Setup gh-aw scripts | ||
| uses: github/gh-aw-actions/setup@v0.77.5 | ||
| with: | ||
| destination: ${{ runner.temp }}/gh-aw/actions |
JanKrivanek
added a commit
to dotnet/msbuild
that referenced
this pull request
Jun 10, 2026
Add a Copilot PAT pool so the agentic workflows select a random PAT from a numbered pool of secrets instead of all sharing a single token, avoiding rate-limiting. Uses the shared-import pattern from dotnet/runtime#127946 (also adopted in dotnet/skills#743). - Add .github/workflows/shared/pat_pool.md: a gh aw import that defines a pat_pool job (inline bash, no extra action) exposing a pat_number output, with an import-schema mapping COPILOT_PAT_0..7 to the repo's pool secrets (COPILOT_GITHUB_TOKEN, COPILOT_GITHUB_TOKEN_2..8). - Add .github/workflows/shared/pat_pool.README.md documenting the pattern. - Apply the rotation to all 5 agentic workflows (close-stale-prs, flaky-test-detector, flaky-test-fixer, review, review-on-open): each declares on.needs:[pat_pool], imports shared/pat_pool.md, and overrides engine.env.COPILOT_GITHUB_TOKEN with a case(needs.pat_pool.outputs.pat_number ...) expression. on.needs wires pat_pool ahead of the activation gate so the selected PAT is validated by the activation job and used by the agent (and it works with the roles:-gated review workflows). - Add .github/workflows/validate-pat-pool.yml: a daily standalone workflow that validates each pool PAT via a Copilot CLI probe and summarizes pool health. Compiled with gh-aw v0.77.5. The mechanism was validated with a temporary test workflow (removed): a run selected a pool token and the agent job observed the non-empty pat_number, confirming the rotated PAT reaches the agent.
JanKrivanek
added a commit
to dotnet/msbuild
that referenced
this pull request
Jun 10, 2026
## Copilot PAT rotation for agentic workflows Introduces a Copilot **PAT pool** so the agentic workflows select a random PAT from a numbered pool of secrets instead of all sharing a single token, avoiding rate-limiting. Uses the shared-import pattern from [dotnet/runtime#127946](dotnet/runtime#127946) (also adopted in [dotnet/skills#743](dotnet/skills#743)). ### Changes - **`shared/pat_pool.md`** — a `gh aw` import defining a `pat_pool` job (inline bash, no extra action) that exposes a `pat_number` output, with an `import-schema` mapping `COPILOT_PAT_0..7` to this repo's pool secrets (`COPILOT_GITHUB_TOKEN`, `COPILOT_GITHUB_TOKEN_2..8`). - **`shared/pat_pool.README.md`** — documents the pattern. - **All 5 agentic workflows** (`close-stale-prs`, `flaky-test-detector`, `flaky-test-fixer`, `review`, `review-on-open`) now declare `on.needs: [pat_pool]`, import `shared/pat_pool.md`, and override `engine.env.COPILOT_GITHUB_TOKEN` with a `case(needs.pat_pool.outputs.pat_number ...)` expression. `on.needs` wires `pat_pool` ahead of the activation gate so the selected PAT is validated by the activation job and used by the agent (and it works with the `roles:`-gated review workflows). - **`validate-pat-pool.yml`** — a daily standalone workflow that validates each pool PAT via a Copilot CLI probe and summarizes pool health. ### Notes - Defines 8 pool slots but only selects from **non-empty** secrets, so it works today with just `COPILOT_GITHUB_TOKEN` configured and is forward-compatible as more `COPILOT_GITHUB_TOKEN_#` secrets are added. - Compiled with gh-aw **v0.77.5**. Action `uses:` lines keep the repo's existing pins (`actions/checkout` v6.0.3, `gh-aw-actions/setup` v0.78.3) so the lock diffs show only the PAT-rotation wiring. ### Validation ✅ Validated on this branch with a temporary `test-pat-rotation` workflow (since removed). A pull_request-triggered run: - `pat_pool` logged `Pool size: 1 · Selected PAT number 0 (index: 0)`, - the **agent** job logged `Agent job sees pat_number = '0'` — i.e. the rotated number reached the agent (the verify step `exit 1`s if it is empty). > [!NOTE] > Draft for review. The rotation is validated end-to-end; the temporary test workflow has been removed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Follow-up to #736 (now merged). Migrates the Copilot PAT rotation from the per-workflow
select-copilot-pataction + custom job to the shared workflow import pattern adopted by dotnet/runtime#127946, so the dotnet org repos share one consistent mechanism.Changes
.github/workflows/shared/pat_pool.md— agh awimport that defines apat_pooljob (inline bash, no separate action) exposing apat_numberoutput, with animport-schemamappingCOPILOT_PAT_0..7to this repo's pool secrets (COPILOT_GITHUB_TOKEN,COPILOT_GITHUB_TOKEN_2..8)..github/workflows/shared/pat_pool.README.md— documents the pattern.imports: - shared/pat_pool.md+engine.envcase(needs.pat_pool.outputs.pat_number ...)..github/actions/select-copilot-pat— the bash now lives inline in the import..github/workflows/validate-pat-pool.yml— a daily standalone workflow that validates each pool PAT with a Copilot CLI request and summarizes pool health (mirrors the runtime PR).Wiring note (adaptation from runtime)
Consuming workflows declare
on.needs: [pat_pool]instead of runtime'sneeds: [pre_activation]+on.permissions: {}workaround. Reasons:on.needswirespat_poolahead of thepre_activationandactivationjobs, so the selected PAT is validated by the activation job's secret check and used by the agent. (Relying only on theengine.envreference makes the compiler attachpat_poolafter activation, so activation would validate the default token instead of the selected one.)roles: allcompatibility:issue-triageusesroles: all, which produces nopre_activationjob — so runtime'son.permissions: {}workaround has nothing to attach to and fails to compile.on.needs: [pat_pool]generates the dependency correctly.The
pat_pooljob declares noneeds:of its own, soon.needscan pull it in front ofpre_activationwithout a dependency cycle. Seeshared/pat_pool.README.md→ "Design Note" for details.Validation ✅
Verified end-to-end on this branch with a temporary
test-pat-rotationworkflow (now removed). A pull_request-triggered run:pat_poolloggedPool size: 3 · Selected PAT number 2 (index: 2)— i.e. it picked a non-default pool token (COPILOT_GITHUB_TOKEN_3).Agent job sees pat_number = '2'— the rotated number reached the agent (the verify stepexit 1s if it is empty).Security review (compilation manifest)
COPILOT_GITHUB_TOKEN,COPILOT_GITHUB_TOKEN_2..8). Thepat_pooljob reads them only to pick a non-empty number (never the secret value); the secret materializes later via theengine.envcase()mapping. Fork PRs receive no secrets.validate-pat-pool.ymlreads each PAT only to make aSay OKCopilot probe and never writes it anywhere.select-copilot-patlocal action is removed; its logic is now inline bash inpat_pool.md.validate-pat-pool.ymlusesgithub/gh-aw-actions/setup@v0.77.5(the same first-party setup action the compiled workflows pin).Note
Draft for review. The rotation is validated; the temporary test workflow has been removed. Mark ready when you're happy with it.