From 90f82783725b06f259e531b99bbb2f2008d8b8ca Mon Sep 17 00:00:00 2001 From: PureWeen <223556219+Copilot@users.noreply.github.com> Date: Tue, 6 Oct 2026 10:32:05 -0500 Subject: [PATCH 1/3] CI-fix dispatcher: Handle deadline exhaustion during startup Separate the validated caller budget from effective remaining time and report expired preparation deadlines before discovery or authentication. Sparse-check out only the trusted standalone dispatcher script without changing the absolute deadline or hard job timeout. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1e0cc90a-a521-4055-9e7a-9565cdaf5504 --- .github/docs/trigger-azdo-pipeline-setup.md | 13 ++ .../Queue-CiFixAzdoValidation.Tests.ps1 | 119 +++++++++++++++++- .github/scripts/Queue-CiFixAzdoValidation.ps1 | 19 ++- .github/workflows/ci-fix-azdo-validation.yml | 3 + 4 files changed, 151 insertions(+), 3 deletions(-) diff --git a/.github/docs/trigger-azdo-pipeline-setup.md b/.github/docs/trigger-azdo-pipeline-setup.md index 823b30723cec..80ebef6fcab3 100644 --- a/.github/docs/trigger-azdo-pipeline-setup.md +++ b/.github/docs/trigger-azdo-pipeline-setup.md @@ -206,6 +206,19 @@ Automated CI-fix PR validation is implemented separately by revalidation, dedupe reads, queue POSTs, retries, and reconciliation. The ten-minute job timeout therefore preserves a real three-minute reserve for summaries and an explicit failed outcome instead of a hard cancellation; +- uses exact-file, non-cone sparse checkout of the standalone dispatcher script + at the trusted `github.sha`, keeping the pinned checkout action, shallow fetch, + and disabled credential persistence. This avoids materializing unrelated + repository files; it does not guarantee checkout finishes within the deadline + or establish production checkout performance; +- keeps the validated caller budget separate from the effective remaining + budget. If preparation consumes the deadline, startup fails explicitly with + `[dispatcher-budget-exhausted]`, records the preparation/deadline stage in the + job summary, and states that no queue POST was attempted before any PR + discovery, authentication, HTTP request, or retry sleep. An expired deadline + is a failure even when no eligible work exists; it is never restarted after + checkout. Checkout or startup exceeding the hard job timeout can still cancel + the job before the script can report this diagnostic; - caps every HTTP timeout and retry sleep to the remaining shared budget. If it expires, no new requests start, completed results remain visible, all unprocessed PR/pipeline work is marked failed, and an ambiguous one-time POST diff --git a/.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 b/.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 index 937b6c749b29..bcba83f4296f 100644 --- a/.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 +++ b/.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 @@ -206,6 +206,8 @@ Describe 'trusted workflow configuration' { $workflow | Should -Match 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7\.0\.1' $workflow | Should -Match 'ref: \$\{\{ github\.sha \}\}' $workflow | Should -Not -Match 'github\.event\.pull_request\.base\.sha' + $workflow | Should -Match 'fetch-depth: 1' + $workflow | Should -Match '(?m)^\s+sparse-checkout: \|\r?\n\s+/\.github/scripts/Queue-CiFixAzdoValidation\.ps1\r?\n\s+sparse-checkout-cone-mode: false$' $workflow | Should -Match 'persist-credentials: false' $workflow | Should -Match 'id-token: write' $workflow | Should -Match 'timeout-minutes: 10' @@ -880,7 +882,7 @@ Describe 'Invoke-AzdoPipelineQueue retry safety' { $script:TransientHttpStatusCodes = @(408, 429, 500, 502, 503, 504) $script:MaxHttpAttempts = 4 $script:RetryBaseDelaySeconds = 2 - $script:DispatcherBudgetSeconds = 480 + $script:EffectiveDispatcherBudgetSeconds = 480 $script:DispatcherStopwatch = [System.Diagnostics.Stopwatch]::StartNew() $script:DispatcherBudgetPrefix = '[dispatcher-budget-exhausted]' foreach ($definition in $FunctionDefinitions) { @@ -1517,6 +1519,121 @@ Describe 'event payload validation' { } } +Describe 'entrypoint dispatcher deadline' { + BeforeEach { + $script:oldStepSummary = $env:GITHUB_STEP_SUMMARY + $summaryPath = Join-Path $TestDrive "deadline-summary-$([Guid]::NewGuid().ToString('N')).md" + $env:GITHUB_STEP_SUMMARY = $summaryPath + $eventPath = Join-Path $TestDrive 'deadline-event.json' + $fixturePath = Join-Path $TestDrive 'deadline-fixture.json' + New-TestEvent | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $eventPath + New-TestPullRequestFixture -PullRequests @((New-TestPullRequest)) | + ConvertTo-Json -Depth 20 | + Set-Content -LiteralPath $fixturePath + + Mock Invoke-RestMethod { throw 'HTTP, OIDC, and Azure requests must not run.' } + Mock Invoke-WebRequest { throw 'HTTP requests must not run.' } + Mock Start-Sleep { throw 'Sleep must not run.' } + } + + AfterEach { + $env:GITHUB_STEP_SUMMARY = $script:oldStepSummary + } + + It 'fails explicitly before external work when preparation exhausted the deadline ()' -ForEach @( + @{ Mode = 'live discovery'; UseFixture = $false; EmptyFixture = $false } + @{ Mode = 'eligible offline fixture'; UseFixture = $true; EmptyFixture = $false } + @{ Mode = 'no eligible offline work'; UseFixture = $true; EmptyFixture = $true } + ) { + if ($EmptyFixture) { + [pscustomobject]@{ pullRequests = @() } | + ConvertTo-Json -Depth 10 | + Set-Content -LiteralPath $fixturePath + } + $parameters = @{ + EventPath = $eventPath + Repository = 'dotnet/maui' + EventName = 'pull_request_target' + DispatcherBudgetSeconds = 420 + DispatcherDeadlineUnixSeconds = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds() - 60 + } + if ($UseFixture) { + $parameters.PullRequestsFixturePath = $fixturePath + $parameters.DryRun = $true + } + + { & $scriptPath @parameters } | + Should -Throw '*dispatcher-budget-exhausted*preparation*No queue POST was attempted.*' + + $summary = Get-Content -Raw -LiteralPath $summaryPath + $summary | Should -Match '\[dispatcher-budget-exhausted\]' + $summary | Should -Match 'Stage: preparation / dispatcher deadline' + $summary | Should -Match 'Result: failed' + $summary | Should -Match 'before PR discovery or authentication' + $summary | Should -Match 'No queue POST was attempted\.' + Should -Invoke Invoke-RestMethod -Times 0 -Exactly + Should -Invoke Invoke-WebRequest -Times 0 -Exactly + Should -Invoke Start-Sleep -Times 0 -Exactly + } + + It 'returns a nonzero process exit with the preparation diagnostic for an expired deadline' { + $output = & pwsh -NoLogo -NoProfile -File $scriptPath ` + -EventPath $eventPath ` + -Repository dotnet/maui ` + -EventName pull_request_target ` + -PullRequestsFixturePath $fixturePath ` + -DispatcherBudgetSeconds 420 ` + -DispatcherDeadlineUnixSeconds ([DateTimeOffset]::UtcNow.ToUnixTimeSeconds() - 60) ` + -DryRun 2>&1 + + $LASTEXITCODE | Should -Not -Be 0 + $diagnostic = $output -join [Environment]::NewLine + $diagnostic | Should -Match '\[dispatcher-budget-exhausted\]' + $diagnostic | Should -Match 'preparation' + $diagnostic | Should -Not -Match 'variable cannot be validated' + (Get-Content -Raw -LiteralPath $summaryPath) | + Should -Match 'No queue POST was attempted\.' + } + + It 'retains caller validation that rejects a zero budget before writing a startup summary' { + { + & $scriptPath ` + -EventPath $eventPath ` + -Repository dotnet/maui ` + -EventName pull_request_target ` + -DispatcherBudgetSeconds 0 ` + -DispatcherDeadlineUnixSeconds ([DateTimeOffset]::UtcNow.ToUnixTimeSeconds() - 60) + } | Should -Throw "*'DispatcherBudgetSeconds'*" + + Test-Path -LiteralPath $summaryPath | Should -BeFalse + Should -Invoke Invoke-RestMethod -Times 0 -Exactly + Should -Invoke Invoke-WebRequest -Times 0 -Exactly + Should -Invoke Start-Sleep -Times 0 -Exactly + } + + It 'reaches verified offline payload generation with a valid future deadline' { + $output = & $scriptPath ` + -EventPath $eventPath ` + -Repository dotnet/maui ` + -EventName pull_request_target ` + -PullRequestsFixturePath $fixturePath ` + -DispatcherBudgetSeconds 420 ` + -DispatcherDeadlineUnixSeconds ([DateTimeOffset]::UtcNow.ToUnixTimeSeconds() + 120) ` + -DryRun + + $results = @($output -join [Environment]::NewLine | ConvertFrom-Json -Depth 20) + $results.Count | Should -Be 3 + @($results.Outcome | Select-Object -Unique) | Should -Be @('dry-run') + @($results.DefinitionId | Sort-Object) | Should -Be @(302, 313, 314) + @($results.Request.sourceVersion | Select-Object -Unique) | + Should -Be @('2222222222222222222222222222222222222222') + (Get-Content -Raw -LiteralPath $summaryPath) | Should -Match 'dotnet/maui#123' + Should -Invoke Invoke-RestMethod -Times 0 -Exactly + Should -Invoke Invoke-WebRequest -Times 0 -Exactly + Should -Invoke Start-Sleep -Times 0 -Exactly + } +} + Describe 'entrypoint verification failure routing' { BeforeEach { $script:oldStepSummary = $env:GITHUB_STEP_SUMMARY diff --git a/.github/scripts/Queue-CiFixAzdoValidation.ps1 b/.github/scripts/Queue-CiFixAzdoValidation.ps1 index c279109c44fb..e87041816ddf 100644 --- a/.github/scripts/Queue-CiFixAzdoValidation.ps1 +++ b/.github/scripts/Queue-CiFixAzdoValidation.ps1 @@ -25,7 +25,7 @@ $secondsUntilDeadline = if ($DispatcherDeadlineUnixSeconds -gt 0) { else { $DispatcherBudgetSeconds } -$script:DispatcherBudgetSeconds = [Math]::Max( +$script:EffectiveDispatcherBudgetSeconds = [Math]::Max( 0, [Math]::Min($DispatcherBudgetSeconds, $secondsUntilDeadline)) $script:DispatcherStopwatch = [System.Diagnostics.Stopwatch]::StartNew() @@ -36,7 +36,7 @@ function Get-DispatcherElapsedSeconds { } function Get-DispatcherRemainingSeconds { - return [Math]::Max(0, $script:DispatcherBudgetSeconds - (Get-DispatcherElapsedSeconds)) + return [Math]::Max(0, $script:EffectiveDispatcherBudgetSeconds - (Get-DispatcherElapsedSeconds)) } function Test-IsDispatcherBudgetException { @@ -1001,6 +1001,21 @@ function Invoke-CiFixQueueWork { return $workResults.ToArray() } +if ([Math]::Floor((Get-DispatcherRemainingSeconds)) -lt 1) { + $preparationFailure = "$($script:DispatcherBudgetPrefix) Dispatcher deadline exhausted during preparation (trusted checkout / PowerShell startup), before PR discovery or authentication. No queue POST was attempted." + if (-not [string]::IsNullOrWhiteSpace($env:GITHUB_STEP_SUMMARY)) { + $lines = @( + '## Automated CI-fix Azure DevOps validation', + '', + '- Stage: preparation / dispatcher deadline', + '- Result: failed', + "- Details: $preparationFailure" + ) + Add-Content -LiteralPath $env:GITHUB_STEP_SUMMARY -Value ($lines -join [Environment]::NewLine) + } + throw $preparationFailure +} + if ([string]::IsNullOrWhiteSpace($EventPath) -or -not (Test-Path -LiteralPath $EventPath -PathType Leaf)) { throw 'GITHUB_EVENT_PATH must identify a supported GitHub event payload file.' } diff --git a/.github/workflows/ci-fix-azdo-validation.yml b/.github/workflows/ci-fix-azdo-validation.yml index 5ed011fdd0b8..ee490666a86e 100644 --- a/.github/workflows/ci-fix-azdo-validation.yml +++ b/.github/workflows/ci-fix-azdo-validation.yml @@ -43,6 +43,9 @@ jobs: ref: ${{ github.sha }} fetch-depth: 1 persist-credentials: false + sparse-checkout: | + /.github/scripts/Queue-CiFixAzdoValidation.ps1 + sparse-checkout-cone-mode: false - name: Queue Azure DevOps PR validation shell: pwsh From f144017565c2a89c2e80ff063f320a647838c325 Mon Sep 17 00:00:00 2001 From: PureWeen <223556219+Copilot@users.noreply.github.com> Date: Tue, 6 Oct 2026 13:04:52 -0500 Subject: [PATCH 2/3] CI-fix producers: Correct PowerShell helper availability guidance Remove the prose contradiction that excludes an already allowed pwsh command required for deterministic safe-output registration and transport checks. Preserve tool and policy configuration, cover both producer prompts, and regenerate body hashes using gh-aw v0.86.2. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1e0cc90a-a521-4055-9e7a-9565cdaf5504 --- ...ister-CiFixSafeOutputExpectation.Tests.ps1 | 28 +++++++++++++++++++ .../workflows/ci-status-fix-net11.lock.yml | 2 +- .github/workflows/ci-status-fix-net11.md | 9 ++++-- .github/workflows/ci-status-fix.lock.yml | 2 +- .github/workflows/ci-status-fix.md | 9 ++++-- 5 files changed, 44 insertions(+), 6 deletions(-) diff --git a/.github/scripts/Register-CiFixSafeOutputExpectation.Tests.ps1 b/.github/scripts/Register-CiFixSafeOutputExpectation.Tests.ps1 index 5c1586a877e1..a084c29727c9 100644 --- a/.github/scripts/Register-CiFixSafeOutputExpectation.Tests.ps1 +++ b/.github/scripts/Register-CiFixSafeOutputExpectation.Tests.ps1 @@ -1,6 +1,34 @@ #!/usr/bin/env pwsh #Requires -Modules Pester +Describe 'CI-fixer safe-output helper availability' { + It 'keeps environment guidance consistent with the existing helper allowlist' -ForEach @( + @{ Workflow = 'ci-status-fix.md' } + @{ Workflow = 'ci-status-fix-net11.md' } + ) { + $workflowPath = Join-Path (Split-Path $PSScriptRoot) "workflows/$Workflow" + $source = Get-Content -Raw -LiteralPath $workflowPath + $bashAllowlist = [regex]::Match($source, '(?m)^ bash: \[(?.*)\]\r?$') + $environment = [regex]::Match( + $source, + '(?ms)^## Environment constraints\r?\n(?.*?)(?=^## )') + + $bashAllowlist.Success | Should -BeTrue + $bashAllowlist.Groups['commands'].Value | Should -Match '"pwsh"' + $bashAllowlist.Groups['commands'].Value | Should -Not -Match '"(?:gh|python)"' + $environment.Success | Should -BeTrue + $guidance = $environment.Groups['guidance'].Value + $guidance | Should -Not -Match 'no\s+`pwsh`' + $guidance | Should -Match '`pwsh` is available' + $guidance | Should -Match 'Register-CiFixSafeOutputExpectation\.ps1' + $guidance | Should -Match 'Test-CiFixTransport\.ps1' + $guidance | Should -Match 'Hard Rule 11' + $guidance | Should -Match 'Step 5\.6' + $guidance | Should -Match 'no `gh`, no `python`' + $guidance | Should -Match 'Use `curl` \+ `jq` for all API calls' + } +} + Describe 'Register-CiFixSafeOutputExpectation' { BeforeEach { $script:outputDirectory = Join-Path $TestDrive "expectations-$([Guid]::NewGuid().ToString('N'))" diff --git a/.github/workflows/ci-status-fix-net11.lock.yml b/.github/workflows/ci-status-fix-net11.lock.yml index de7366bf296f..f40280e84ca5 100644 --- a/.github/workflows/ci-status-fix-net11.lock.yml +++ b/.github/workflows/ci-status-fix-net11.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"d683d78d7591e4c6ffd537540bf44fb64e9fba8ce2161051d4c5d4c87069ffaf","body_hash":"403c30218073a3acd2ea3bc73e7608ec7801b89ab53c93d6a4ce279e0901db2f","compiler_version":"v0.86.2","strict":true,"agent_id":"copilot","agent_model":"gpt-6.1-sol","engine_versions":{"copilot":"1.0.79"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"d683d78d7591e4c6ffd537540bf44fb64e9fba8ce2161051d4c5d4c87069ffaf","body_hash":"95dc5f5d929a9bae2e8a5dd77f919d305e0332be2051af91a4aa9d778ff0f44a","compiler_version":"v0.86.2","strict":true,"agent_id":"copilot","agent_model":"gpt-6.1-sol","engine_versions":{"copilot":"1.0.79"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"6aab9e5b5c91c615506061f09bedd81a23babe3c","version":"v0.86.2"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.44","digest":"sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.44@sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44","digest":"sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44@sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.44","digest":"sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.44@sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.9","digest":"sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.9@sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196","pinned_image":"ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196"},{"image":"ghcr.io/github/github-mcp-server:v1.9.0","digest":"sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e","pinned_image":"ghcr.io/github/github-mcp-server:v1.9.0@sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e"}]} # This file was automatically generated by gh-aw (v0.86.2). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/ci-status-fix-net11.md b/.github/workflows/ci-status-fix-net11.md index 87384dce0c43..070a69f76cfb 100644 --- a/.github/workflows/ci-status-fix-net11.md +++ b/.github/workflows/ci-status-fix-net11.md @@ -2205,8 +2205,13 @@ These look like permission errors but are physical: - OData `$top` must be encoded as `%24top` in URLs. - Each bash call runs in a fresh subshell. Persist state to `/tmp/gh-aw/agent/`. -- Bash allowlist per frontmatter `tools.bash`: no `gh`, no `pwsh`, no - `python`. Use `curl` + `jq` for all API calls. +- Bash allowlist per frontmatter `tools.bash`: no `gh`, no `python`. + Use `curl` + `jq` for all API calls. +- `pwsh` is available for deterministic safe-output expectation registration + (`.github/scripts/Register-CiFixSafeOutputExpectation.ps1`) and transport + validation/registration (`.github/scripts/Test-CiFixTransport.ps1`). Run these + helpers as required by Hard Rule 11 and Step 5.6; never bypass their fail-closed + registration or transport checks. ## Output discipline diff --git a/.github/workflows/ci-status-fix.lock.yml b/.github/workflows/ci-status-fix.lock.yml index 9f3f2e891444..d8286e6226c3 100644 --- a/.github/workflows/ci-status-fix.lock.yml +++ b/.github/workflows/ci-status-fix.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"0a2fab5e8553740e2264d6db88e423885eb49c8aeff820d870d6b1c44b3bf1ba","body_hash":"f02e70a8b091b54d4f7fc31fc3cce44993c9eec8444b727d2857c76d19e29739","compiler_version":"v0.86.2","strict":true,"agent_id":"copilot","agent_model":"gpt-6.1-sol","engine_versions":{"copilot":"1.0.79"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"0a2fab5e8553740e2264d6db88e423885eb49c8aeff820d870d6b1c44b3bf1ba","body_hash":"ba9a142d27c0d34a4625ce6c45e0186fee85a6c98defd81a0934f74acf6eb126","compiler_version":"v0.86.2","strict":true,"agent_id":"copilot","agent_model":"gpt-6.1-sol","engine_versions":{"copilot":"1.0.79"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_PAT_0","COPILOT_PAT_1","COPILOT_PAT_2","COPILOT_PAT_3","COPILOT_PAT_4","COPILOT_PAT_5","COPILOT_PAT_6","COPILOT_PAT_7","COPILOT_PAT_8","COPILOT_PAT_9","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"3d3c42e5aac5ba805825da76410c181273ba90b1","version":"v7.0.1"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"820762786026740c76f36085b0efc47a31fe5020","version":"v7.0.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"6aab9e5b5c91c615506061f09bedd81a23babe3c","version":"v0.86.2"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.44","digest":"sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.44@sha256:0d727725c737b58c7bdf51f640cffb928385ec46517e0917c7f1a02f1bada8b4"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44","digest":"sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.44@sha256:b50fbadba138f6e9aba94aca09711335c489bb3b15861220cb66f6092e042dc7"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.44","digest":"sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.44@sha256:83e48bbe12c634be8c228a576832fe45f66c529ac3659db92bddbcf2eeb6d627"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.4.9","digest":"sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.4.9@sha256:e5a1569aeaf41820fa7bdee3e94468cae448133cdbf00119ad24f5b74db1ab9f"},{"image":"ghcr.io/github/gh-aw-node","digest":"sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196","pinned_image":"ghcr.io/github/gh-aw-node@sha256:0d9f1fb5fd6610c0ac1f5194a38e45a8a1e81f8a390d5142d8e4e6f26a4b3196"},{"image":"ghcr.io/github/github-mcp-server:v1.9.0","digest":"sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e","pinned_image":"ghcr.io/github/github-mcp-server:v1.9.0@sha256:881b53d6f75f69bdbc1b5b10fc2f1361717c19054143b3a8529fb5c32061a50e"}]} # This file was automatically generated by gh-aw (v0.86.2). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/ci-status-fix.md b/.github/workflows/ci-status-fix.md index f0a6cd4db73f..61f8a0c943b2 100644 --- a/.github/workflows/ci-status-fix.md +++ b/.github/workflows/ci-status-fix.md @@ -2185,8 +2185,13 @@ These look like permission errors but are physical: - OData `$top` must be encoded as `%24top` in URLs. - Each bash call runs in a fresh subshell. Persist state to `/tmp/gh-aw/agent/`. -- Bash allowlist per frontmatter `tools.bash`: no `gh`, no `pwsh`, no - `python`. Use `curl` + `jq` for all API calls. +- Bash allowlist per frontmatter `tools.bash`: no `gh`, no `python`. + Use `curl` + `jq` for all API calls. +- `pwsh` is available for deterministic safe-output expectation registration + (`.github/scripts/Register-CiFixSafeOutputExpectation.ps1`) and transport + validation/registration (`.github/scripts/Test-CiFixTransport.ps1`). Run these + helpers as required by Hard Rule 11 and Step 5.6; never bypass their fail-closed + registration or transport checks. ## Output discipline From ddc5d50a606fa3349510412e1dec4007fbd71a0c Mon Sep 17 00:00:00 2001 From: PureWeen <223556219+Copilot@users.noreply.github.com> Date: Tue, 6 Oct 2026 15:35:45 -0500 Subject: [PATCH 3/3] CI-fix dispatcher tests: Support CRLF workflow checkout Accept the optional carriage return before the final multiline anchor and exercise the trusted sparse-checkout assertion with both LF and CRLF input. Reproduces and addresses top-level Copilot review 5432776264 without changing runtime behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 1e0cc90a-a521-4055-9e7a-9565cdaf5504 --- .github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 b/.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 index bcba83f4296f..4c4f527f481e 100644 --- a/.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 +++ b/.github/scripts/Queue-CiFixAzdoValidation.Tests.ps1 @@ -200,14 +200,18 @@ Describe 'Test-CiFixPrFingerprint' { } Describe 'trusted workflow configuration' { - It 'pins checkout to the reviewed v7.0.1 commit in the id-token job' { + It 'pins checkout to the reviewed v7.0.1 commit in the id-token job with line endings' -ForEach @( + @{ LineEnding = 'LF'; NewLine = "`n" } + @{ LineEnding = 'CRLF'; NewLine = "`r`n" } + ) { $workflow = Get-Content -Raw -LiteralPath (Join-Path $PSScriptRoot '../workflows/ci-fix-azdo-validation.yml') + $workflow = $workflow -replace '\r?\n', $NewLine $workflow | Should -Match 'actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7\.0\.1' $workflow | Should -Match 'ref: \$\{\{ github\.sha \}\}' $workflow | Should -Not -Match 'github\.event\.pull_request\.base\.sha' $workflow | Should -Match 'fetch-depth: 1' - $workflow | Should -Match '(?m)^\s+sparse-checkout: \|\r?\n\s+/\.github/scripts/Queue-CiFixAzdoValidation\.ps1\r?\n\s+sparse-checkout-cone-mode: false$' + $workflow | Should -Match '(?m)^\s+sparse-checkout: \|\r?\n\s+/\.github/scripts/Queue-CiFixAzdoValidation\.ps1\r?\n\s+sparse-checkout-cone-mode: false\r?$' $workflow | Should -Match 'persist-credentials: false' $workflow | Should -Match 'id-token: write' $workflow | Should -Match 'timeout-minutes: 10'