diff --git a/.github/skills/pr-attention-queue/SKILL.md b/.github/skills/pr-attention-queue/SKILL.md index 920c7757a7f7..c624f129c54d 100644 --- a/.github/skills/pr-attention-queue/SKILL.md +++ b/.github/skills/pr-attention-queue/SKILL.md @@ -122,6 +122,8 @@ A recorded response does not mean the discussion is resolved. `no-response` is o bounded evidence is complete and there were zero top-level human responses. If the evidence is incomplete, truncated, or requires human interpretation because of unresolved inline discussion, the result remains `unknown` rather than `no-response`. +Do not count automation comments, including `dotnet-policy-service`, as human responses; complete +bot-only discussion evidence can establish `no-response`. A non-author human coordination command is recorded response evidence because it demonstrates engagement, but it does not prove resolution and does not create a discussion-verification signal by itself. Coordination classification applies only to top-level discussion comments, never submitted @@ -275,7 +277,10 @@ Do not promote a PR from `NeedsRescue`, `WaitingOnAuthor`, `WaitingOnCI`, or `De Classification precedence is evidence-driven: - An exact `* NO MERGE *` label requires maintainer triage even when CI is also pending. -- `pending-ci-rerun` routes to `WaitingOnCI`. +- Treat `pending-ci-rerun` as informational for review routing: it is an inactivity marker requiring + CI revalidation before merge, not evidence that CI is running. Preserve `ci-rerun-pending` on every + labeled classification, including drafts and excluded bot-authored PRs. Hold an otherwise + merge-ready PR in `WaitingOnCI` until CI is rerun and the label is removed. - An approved PR whose merge state is `BEHIND` routes to author/maintainer branch-update work rather than CI. - A current non-author `COMMENTED` review routes to `WaitingOnAuthor` unless the author responded or diff --git a/.github/skills/pr-attention-queue/presets.json b/.github/skills/pr-attention-queue/presets.json index 39cb45cedb49..5098e461a0ad 100644 --- a/.github/skills/pr-attention-queue/presets.json +++ b/.github/skills/pr-attention-queue/presets.json @@ -30,6 +30,7 @@ "dependabot", "dotnet-maestro", "dotnet-maestro-bot", + "dotnet-policy-service", "github-actions", "copilot-pull-request-reviewer", "azure-pipelines" diff --git a/.github/skills/pr-attention-queue/scripts/PRAttentionQueue.psm1 b/.github/skills/pr-attention-queue/scripts/PRAttentionQueue.psm1 index 262374d16fba..a936a34ef799 100644 --- a/.github/skills/pr-attention-queue/scripts/PRAttentionQueue.psm1 +++ b/.github/skills/pr-attention-queue/scripts/PRAttentionQueue.psm1 @@ -2433,12 +2433,6 @@ function Get-Classification { $reasonCodes.Add("merge-conflict") $blockers.Add("The pull request conflicts with its base branch.") } - elseif ($ciRerunPending) { - $bucket = "WaitingOnCI" - $nextActor = "CI/automation" - $reasonCodes.Add("ci-rerun-pending") - $blockers.Add("The pull request is explicitly waiting for CI to be rerun.") - } elseif ($reviewDecision -eq "CHANGES_REQUESTED" -or ($ownershipReview -and $ownershipReview.State -eq "CHANGES_REQUESTED")) { if ($authorRespondedAfterReview) { @@ -2585,6 +2579,17 @@ function Get-Classification { $waitingSince = $ownershipReview.SubmittedAt } + if ($ciRerunPending) { + if (-not $reasonCodes.Contains("ci-rerun-pending")) { + $reasonCodes.Add("ci-rerun-pending") + } + if ($bucket -eq "ReadyToMerge") { + $bucket = "WaitingOnCI" + $nextActor = "CI/automation" + $blockers.Add("CI must be rerun before merge.") + } + } + if ($bucket -eq "ReviewNow" -and $checkState -eq "Pending") { $reasonCodes.Add("ci-pending") } @@ -2684,7 +2689,7 @@ function Get-DisplayMetadata { } "ci-rerun-pending" = [pscustomobject]@{ label = "CI rerun pending" - description = "The repository explicitly marks the pull request as waiting for a CI rerun." + description = "An inactivity marker requires CI revalidation before merge; it does not mean CI is running." } "community-contribution" = [pscustomobject]@{ label = "Community contribution" @@ -2926,11 +2931,13 @@ function Get-ResponseEvidence { $commentEvidenceTruncated -or -not $complete -or $signals.Count -gt 0 + $onlyAutomationComments = $commentTotalCount -eq $comments.Count -and + @($comments | Where-Object { $_.Actor -ne "automation" }).Count -eq 0 $status = if ($recordedResponse) { "recorded-response" } - elseif ($complete -and $commentTotalCount -eq 0 -and -not $hasIncompleteOrAmbiguousEvidence) { + elseif ($complete -and $onlyAutomationComments -and -not $hasIncompleteOrAmbiguousEvidence) { "no-response" } else { diff --git a/.github/skills/pr-attention-queue/tests/Test-MergeStateRefresh.ps1 b/.github/skills/pr-attention-queue/tests/Test-MergeStateRefresh.ps1 index 3f4dba666d5c..67a9a35bda61 100644 --- a/.github/skills/pr-attention-queue/tests/Test-MergeStateRefresh.ps1 +++ b/.github/skills/pr-attention-queue/tests/Test-MergeStateRefresh.ps1 @@ -15,6 +15,7 @@ $fixturePath = Join-Path $PSScriptRoot "fixtures\merge-state-refresh.json" $results = [Collections.Generic.List[object]]::new() $cases = @( @{ name = "clear"; kind = "regression"; eligibility = "eligible"; retries = 1; bucket = "ReadyToMerge"; reason = "approved"; actor = "merger" }, + @{ name = "pending-ci-rerun"; kind = "regression"; retries = 1; bucket = "WaitingOnCI"; reason = "ci-rerun-pending"; actor = "CI/automation" }, @{ name = "unresolved-thread"; kind = "regression"; eligibility = "verification-needed"; retries = 1; bucket = "ReadyToMerge"; reason = "approved"; actor = "merger" }, @{ name = "unknown-state"; kind = "control"; retries = 1; reason = "merge-state-not-clean" }, @{ name = "missing-state"; kind = "control"; retries = 1; reason = "merge-state-not-clean" }, @@ -42,6 +43,7 @@ if ($Case) { foreach ($scenario in $cases) { $fixture = Get-Content -Raw -LiteralPath $fixturePath | ConvertFrom-Json -Depth 30 switch ($scenario.name) { + "pending-ci-rerun" { $fixture.labels += [pscustomobject]@{ name = "pending-ci-rerun" } } "unresolved-thread" { $fixture.threads = @(@{ isResolved = $false; isOutdated = $false }) } "unknown-state" { $fixture.retryResponse.mergeStateStatus = "UNKNOWN" } "missing-state" { $fixture.retryResponse.PSObject.Properties.Remove("mergeStateStatus") } @@ -99,6 +101,12 @@ foreach ($scenario in $cases) { throw "Incomplete or blocked merge facts must not grant merge clearance." } + if ($scenario.name -eq "pending-ci-rerun" -and + ($item.mergeStateStatus -ne "CLEAN" -or $item.blockers -notcontains "CI must be rerun before merge." -or + $item.reasonCodes -notcontains "approved")) { + throw "Refreshed CLEAN state must preserve approval and the CI rerun merge gate." + } + $expectedBucket = $scenario.bucket ?? "WaitingOnCI" $expectedActor = $scenario.actor ?? "CI/automation" if ($item.bucket -ne $expectedBucket -or $item.nextActor -ne $expectedActor -or diff --git a/.github/skills/pr-attention-queue/tests/Test-PRAttentionQueue.ps1 b/.github/skills/pr-attention-queue/tests/Test-PRAttentionQueue.ps1 index ca8491722a18..772287cc31a9 100644 --- a/.github/skills/pr-attention-queue/tests/Test-PRAttentionQueue.ps1 +++ b/.github/skills/pr-attention-queue/tests/Test-PRAttentionQueue.ps1 @@ -21,6 +21,8 @@ $fixturePath = Join-Path $PSScriptRoot "fixtures/pull-requests.json" $correctnessFixturePath = Join-Path $PSScriptRoot "fixtures/correctness-pull-requests.json" $discussionFixturePath = Join-Path $PSScriptRoot "fixtures/discussion-pull-requests.json" $inboxFixturePath = Join-Path $PSScriptRoot "fixtures/inbox-pull-requests.json" +$rerunFixturePath = Join-Path $PSScriptRoot "fixtures/rerun-label-pull-requests.json" +$policyBotFixturePath = Join-Path $PSScriptRoot "fixtures/policy-bot-pull-requests.json" $snapshot = [datetime]"2026-09-03T18:00:00Z" Import-Module -Scope Local -Force $modulePath @@ -126,6 +128,12 @@ Assert-True (($correctnessResult.items | Where-Object number -eq 101).bucket -eq Assert-True (($correctnessResult.items | Where-Object number -eq 101).reasonCodes -contains "blocked-label") "The no-merge result must identify the blocking label." Assert-True (($correctnessResult.items | Where-Object number -eq 102).bucket -eq "WaitingOnCI") "A pending CI rerun must not be ready to merge." Assert-True (($correctnessResult.items | Where-Object number -eq 102).reasonCodes -contains "ci-rerun-pending") "The pending rerun must have a stable reason." +Assert-True (($correctnessResult.items | Where-Object number -eq 102).blockers -contains "CI must be rerun before merge.") "A merge-ready PR with a rerun label must explain the merge gate." +Assert-True (($correctnessResult.items | Where-Object number -eq 102).reasonCodes -contains "approved") "A rerun merge gate must preserve the otherwise-ready approval reason." +Assert-True (($correctnessResult.items | Where-Object number -eq 102).nextActor -eq "CI/automation") "A rerun merge gate must assign CI/automation." +Assert-True (($correctnessResult.items | Where-Object number -eq 102).mergeEligibility -eq "not-candidate") "A rerun merge gate must prevent merge candidacy." +Assert-True (-not ($correctnessResult.items | Where-Object number -eq 102).shownInMergeVerification) "A rerun merge gate must not enter merge verification." +Assert-True (($correctnessResult.items | Where-Object number -eq 101).reasonCodes -contains "ci-rerun-pending") "A no-merge label must preserve the informational rerun reason." Assert-True (($correctnessResult.items | Where-Object number -eq 103).bucket -eq "WaitingOnCI") "A non-clean merge state must not be ready to merge." Assert-True (($correctnessResult.items | Where-Object number -eq 103).reasonCodes -contains "merge-state-not-clean") "The non-clean merge state must have a stable reason." Assert-True (($correctnessResult.items | Where-Object number -eq 104).bucket -eq "ReadyToMerge") "An approved clean pull request should be ready to merge." @@ -136,6 +144,56 @@ Assert-True (($correctnessResult.items | Where-Object number -eq 106).bucket -eq Assert-True (($correctnessResult.items | Where-Object number -eq 107).humanReviewCount -eq 0) "Author-authored reviews must not count as human reviewer activity." Assert-True (($correctnessResult.items | Where-Object number -eq 107).bucket -eq "NeedsRescue") "A stale request must remain rescue work when the only review is author-authored." +$rerunJson = & $scriptPath ` + -InputPath $rerunFixturePath ` + -Now $snapshot ` + -OutputFormat Json +$rerunResult = $rerunJson | ConvertFrom-Json -Depth 100 +$rerunCases = @( + [pscustomobject]@{ Number = 301; Bucket = "ReviewNow"; Reason = "review-requested"; Actor = "human reviewer" }, + [pscustomobject]@{ Number = 302; Bucket = "ReviewNow"; Reason = "needs-first-review"; Actor = "human reviewer" }, + [pscustomobject]@{ Number = 303; Bucket = "WaitingOnAuthor"; Reason = "changes-requested"; Actor = "author" }, + [pscustomobject]@{ Number = 304; Bucket = "WaitingOnCI"; Reason = "ci-failed"; Actor = "author/CI investigation" }, + [pscustomobject]@{ Number = 305; Bucket = "Draft"; Reason = "draft"; Actor = "author" }, + [pscustomobject]@{ Number = 306; Bucket = "WaitingOnAuthor"; Reason = "merge-conflict"; Actor = "author" }, + [pscustomobject]@{ Number = 307; Bucket = "WaitingOnAuthor"; Reason = "branch-update-required"; Actor = "author/maintainer" }, + [pscustomobject]@{ Number = 308; Bucket = "NeedsRescue"; Reason = "never-reviewed"; Actor = "maintainer/triager" }, + [pscustomobject]@{ Number = 309; Bucket = "Excluded"; Reason = "bot-authored"; Actor = "none" }, + [pscustomobject]@{ Number = 310; Bucket = "DesignDecision"; Reason = "design-gate"; Actor = "API/design owner" } +) +foreach ($case in $rerunCases) { + $item = $rerunResult.items | Where-Object number -eq $case.Number + Assert-True ($item.bucket -eq $case.Bucket) "Rerun label PR $($case.Number) must retain bucket '$($case.Bucket)'." + Assert-True ($item.nextActor -eq $case.Actor) "Rerun label PR $($case.Number) must retain next actor '$($case.Actor)'." + Assert-True ($item.reasonCodes -contains $case.Reason) "Rerun label PR $($case.Number) must retain normal reason '$($case.Reason)'." + Assert-True (@($item.reasonCodes | Where-Object { $_ -eq "ci-rerun-pending" }).Count -eq 1) "Rerun label PR $($case.Number) must have exactly one informational rerun reason." + Assert-True (-not ($item.blockers -contains "CI must be rerun before merge.")) "Rerun label PR $($case.Number) must not acquire a merge-ready rerun blocker." + Assert-True ($item.mergeEligibility -eq "not-candidate") "Rerun label PR $($case.Number) must remain outside merge candidacy." +} +Assert-True (($rerunResult.items | Where-Object number -eq 304).blockers -contains "The failure is not classified as unrelated or flaky.") "A failed-CI rerun PR must retain the normal CI failure blocker." +Assert-True (($rerunResult.items | Where-Object number -eq 302).reasonCodes -contains "ci-pending") "A first-review rerun PR must retain the informational pending-check reason." + +$policyBotJson = & $scriptPath ` + -InputPath $policyBotFixturePath ` + -Now $snapshot ` + -OutputFormat Json +$policyBotResult = $policyBotJson | ConvertFrom-Json -Depth 100 +$policyBotItem = $policyBotResult.items | Where-Object number -eq 401 +Assert-True ($policyBotItem.discussionAssessment.state -eq "clear") "A policy-bot-only discussion must be clear." +Assert-True (-not ($policyBotItem.discussionAssessment.signals -contains "non-author-discussion-requires-verification")) "A policy-bot-only discussion must not require non-author verification." +Assert-True ($policyBotItem.discussionAssessment.comments[0].actor -eq "automation") "The policy bot's bare login must be attributed to automation." +Assert-True ($policyBotItem.shownInDigest) "A policy-bot-only discussion must remain in the review digest." +$policyBotCommunity = $policyBotResult.inbox.community.inventory | Where-Object number -eq 401 +Assert-True ($policyBotCommunity.responseEvidence.complete) "A policy-bot-only inbox fixture must have complete response evidence." +Assert-True ($policyBotCommunity.responseEvidence.status -eq "no-response") "A policy-bot-only community contribution must have no-response evidence." +Assert-True (-not $policyBotCommunity.responseEvidence.recordedNonAuthorHumanResponse) "A policy bot must not count as a recorded human response." +$policyMentionItem = $policyBotResult.items | Where-Object number -eq 402 +Assert-True ($policyMentionItem.discussionAssessment.comments[0].actor -eq "author") "An author mentioning the policy bot must remain an author comment." +Assert-True ($policyMentionItem.discussionAssessment.comments[0].author -eq "policy-mention-author") "A policy-bot mention must not replace the comment author's login." +Assert-True ($policyMentionItem.discussionAssessment.comments[0].excerpt -eq "@dotnet-policy-service agree") "The author policy-bot acknowledgement must be assessed unchanged." +Assert-True (-not ($policyBotResult.inbox.community.inventory | Where-Object number -eq 402).responseEvidence.recordedNonAuthorHumanResponse) "An author policy-bot acknowledgement must not count as a non-author human response." +Assert-True (($policyBotResult.inbox.community.inventory | Where-Object number -eq 403).responseEvidence.status -eq "unknown") "Truncated policy-bot-only evidence must remain unknown." + $rankingJson = & $scriptPath ` -InputPath $correctnessFixturePath ` -Now $snapshot ` diff --git a/.github/skills/pr-attention-queue/tests/fixtures/policy-bot-pull-requests.json b/.github/skills/pr-attention-queue/tests/fixtures/policy-bot-pull-requests.json new file mode 100644 index 000000000000..1571b1715337 --- /dev/null +++ b/.github/skills/pr-attention-queue/tests/fixtures/policy-bot-pull-requests.json @@ -0,0 +1,113 @@ +[ + { + "number": 401, + "title": "Community contribution with only policy automation", + "url": "https://github.com/dotnet/aspnetcore/pull/401", + "author": { "login": "policy-bot-only-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "community-contribution" }], + "createdAt": "2026-08-25T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "4014014014014014014014014014014014014011", + "headRefName": "policy-bot-only", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [], + "reviewRequests": [], + "comments": [{ + "author": { "login": "dotnet-policy-service" }, + "createdAt": "2026-09-01T18:00:00Z", + "bodyText": "Please rerun validation before merging." + }], + "discussionComments": [{ + "author": { "login": "dotnet-policy-service" }, + "authorAssociation": "NONE", + "createdAt": "2026-09-01T18:00:00Z", + "bodyText": "Please rerun validation before merging." + }], + "discussionCommentsComplete": true, + "discussionCommentTotalCount": 1, + "discussionThreads": [], + "discussionThreadsComplete": true, + "discussionThreadTotalCount": 0, + "files": [{ "path": "src/Components/Components/src/PolicyBotOnly.cs" }], + "changedFiles": 1 + }, + { + "number": 402, + "title": "Author acknowledgement mentioning policy automation", + "url": "https://github.com/dotnet/aspnetcore/pull/402", + "author": { "login": "policy-mention-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "community-contribution" }], + "createdAt": "2026-08-25T18:00:00Z", + "updatedAt": "2026-09-02T18:00:00Z", + "headRefOid": "4024024024024024024024024024024024024022", + "headRefName": "policy-mention", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [], + "reviewRequests": [], + "comments": [{ + "author": { "login": "policy-mention-author", "is_bot": false }, + "createdAt": "2026-09-02T18:00:00Z", + "bodyText": "@dotnet-policy-service agree" + }], + "discussionComments": [{ + "author": { "login": "policy-mention-author" }, + "authorAssociation": "CONTRIBUTOR", + "createdAt": "2026-09-02T18:00:00Z", + "bodyText": "@dotnet-policy-service agree" + }], + "discussionCommentsComplete": true, + "discussionCommentTotalCount": 1, + "discussionThreads": [], + "discussionThreadsComplete": true, + "discussionThreadTotalCount": 0, + "files": [{ "path": "src/Components/Components/src/PolicyMention.cs" }], + "changedFiles": 1 + }, + { + "number": 403, + "title": "Truncated community discussion showing policy automation", + "url": "https://github.com/dotnet/aspnetcore/pull/403", + "author": { "login": "truncated-policy-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "community-contribution" }], + "createdAt": "2026-08-25T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "4034034034034034034034034034034034034033", + "headRefName": "truncated-policy-bot", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [], + "reviewRequests": [], + "comments": [{ + "author": { "login": "dotnet-policy-service" }, + "createdAt": "2026-09-01T18:00:00Z", + "bodyText": "Please rerun validation before merging." + }], + "discussionComments": [{ + "author": { "login": "dotnet-policy-service" }, + "authorAssociation": "NONE", + "createdAt": "2026-09-01T18:00:00Z", + "bodyText": "Please rerun validation before merging." + }], + "discussionCommentsComplete": false, + "discussionCommentTotalCount": 51, + "discussionThreads": [], + "discussionThreadsComplete": true, + "discussionThreadTotalCount": 0, + "files": [{ "path": "src/Components/Components/src/TruncatedPolicyBot.cs" }], + "changedFiles": 1 + } +] diff --git a/.github/skills/pr-attention-queue/tests/fixtures/rerun-label-pull-requests.json b/.github/skills/pr-attention-queue/tests/fixtures/rerun-label-pull-requests.json new file mode 100644 index 000000000000..9715e78d7356 --- /dev/null +++ b/.github/skills/pr-attention-queue/tests/fixtures/rerun-label-pull-requests.json @@ -0,0 +1,232 @@ +[ + { + "number": 301, + "title": "Review request with inactivity marker", + "url": "https://github.com/dotnet/aspnetcore/pull/301", + "author": { "login": "requested-rerun-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }], + "createdAt": "2026-08-01T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "3013013013013013013013013013013013013011", + "headRefName": "requested-rerun", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [], + "reviewRequests": [{ "login": "reviewer", "requestedAt": "2026-09-01T18:00:00Z" }], + "comments": [], + "files": [{ "path": "src/Components/Components/src/RequestedRerun.cs" }], + "changedFiles": 1 + }, + { + "number": 302, + "title": "First review with inactivity marker and pending check", + "url": "https://github.com/dotnet/aspnetcore/pull/302", + "author": { "login": "first-review-rerun-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }], + "createdAt": "2026-08-25T18:00:00Z", + "updatedAt": "2026-08-25T18:00:00Z", + "headRefOid": "3023023023023023023023023023023023023022", + "headRefName": "first-review-rerun", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "IN_PROGRESS", "conclusion": null }], + "latestReviews": [], + "reviewRequests": [], + "comments": [], + "files": [{ "path": "src/Components/Components/src/FirstReviewRerun.cs" }], + "changedFiles": 1 + }, + { + "number": 303, + "title": "Unanswered change request with inactivity marker", + "url": "https://github.com/dotnet/aspnetcore/pull/303", + "author": { "login": "changes-rerun-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }], + "createdAt": "2026-08-01T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "3033033033033033033033033033033033033033", + "headRefName": "changes-rerun", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "CHANGES_REQUESTED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [{ + "author": { "login": "reviewer", "is_bot": false }, + "state": "CHANGES_REQUESTED", + "submittedAt": "2026-09-01T18:00:00Z", + "commit": { "oid": "3033033033033033033033033033033033033033" } + }], + "reviewRequests": [], + "comments": [], + "files": [{ "path": "src/Components/Components/src/ChangesRerun.cs" }], + "changedFiles": 1 + }, + { + "number": 304, + "title": "Failed CI with inactivity marker", + "url": "https://github.com/dotnet/aspnetcore/pull/304", + "author": { "login": "failed-rerun-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }], + "createdAt": "2026-08-01T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "3043043043043043043043043043043043043044", + "headRefName": "failed-rerun", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "FAILURE" }], + "latestReviews": [], + "reviewRequests": [], + "comments": [], + "files": [{ "path": "src/Components/Components/src/FailedRerun.cs" }], + "changedFiles": 1 + }, + { + "number": 305, + "title": "Draft with inactivity marker", + "url": "https://github.com/dotnet/aspnetcore/pull/305", + "author": { "login": "draft-rerun-author", "is_bot": false }, + "isDraft": true, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }], + "createdAt": "2026-08-01T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "3053053053053053053053053053053053053055", + "headRefName": "draft-rerun", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [], + "reviewRequests": [], + "comments": [], + "files": [{ "path": "src/Components/Components/src/DraftRerun.cs" }], + "changedFiles": 1 + }, + { + "number": 306, + "title": "Merge conflict with inactivity marker", + "url": "https://github.com/dotnet/aspnetcore/pull/306", + "author": { "login": "conflict-rerun-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }], + "createdAt": "2026-08-01T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "3063063063063063063063063063063063063066", + "headRefName": "conflict-rerun", + "baseRefName": "main", + "mergeable": "CONFLICTING", + "mergeStateStatus": "DIRTY", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [], + "reviewRequests": [], + "comments": [], + "files": [{ "path": "src/Components/Components/src/ConflictRerun.cs" }], + "changedFiles": 1 + }, + { + "number": 307, + "title": "Approved behind branch with inactivity marker", + "url": "https://github.com/dotnet/aspnetcore/pull/307", + "author": { "login": "behind-rerun-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }], + "createdAt": "2026-08-01T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "3073073073073073073073073073073073073077", + "headRefName": "behind-rerun", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "BEHIND", + "reviewDecision": "APPROVED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [{ + "author": { "login": "reviewer", "is_bot": false }, + "state": "APPROVED", + "submittedAt": "2026-09-01T18:00:00Z", + "commit": { "oid": "3073073073073073073073073073073073073077" } + }], + "reviewRequests": [], + "comments": [], + "files": [{ "path": "src/Components/Components/src/BehindRerun.cs" }], + "changedFiles": 1 + }, + { + "number": 308, + "title": "Stale unreviewed PR with inactivity marker", + "url": "https://github.com/dotnet/aspnetcore/pull/308", + "author": { "login": "stale-rerun-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }], + "createdAt": "2026-08-01T18:00:00Z", + "updatedAt": "2026-08-01T18:00:00Z", + "headRefOid": "3083083083083083083083083083083083083088", + "headRefName": "stale-rerun", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [], + "reviewRequests": [], + "comments": [], + "files": [{ "path": "src/Components/Components/src/StaleRerun.cs" }], + "changedFiles": 1 + }, + { + "number": 309, + "title": "Excluded bot PR with inactivity marker", + "url": "https://github.com/dotnet/aspnetcore/pull/309", + "author": { "login": "dependabot", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }], + "createdAt": "2026-08-01T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "3093093093093093093093093093093093093099", + "headRefName": "bot-rerun", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [], + "reviewRequests": [], + "comments": [], + "files": [{ "path": "src/Components/Components/src/BotRerun.cs" }], + "changedFiles": 1 + }, + { + "number": 310, + "title": "Design decision with inactivity marker", + "url": "https://github.com/dotnet/aspnetcore/pull/310", + "author": { "login": "design-rerun-author", "is_bot": false }, + "isDraft": false, + "labels": [{ "name": "area-blazor" }, { "name": "pending-ci-rerun" }, { "name": "needs-design" }], + "createdAt": "2026-08-01T18:00:00Z", + "updatedAt": "2026-09-01T18:00:00Z", + "headRefOid": "3103103103103103103103103103103103103101", + "headRefName": "design-rerun", + "baseRefName": "main", + "mergeable": "MERGEABLE", + "mergeStateStatus": "CLEAN", + "reviewDecision": "REVIEW_REQUIRED", + "statusCheckRollup": [{ "__typename": "CheckRun", "status": "COMPLETED", "conclusion": "SUCCESS" }], + "latestReviews": [], + "reviewRequests": [], + "comments": [], + "files": [{ "path": "src/Components/Components/src/DesignRerun.cs" }], + "changedFiles": 1 + } +]