Repository navigation
Commit f57627b
ci(merge-queue): name a no-runner red as infra:no-runner, with a once-per-head re-queue verdict (#21946)
Part of #21933 — this PR delivers the classification, the comment, and
the once-per-head re-queue verdict. It does not deliver the automatic
re-queue act: the workflow holds no credential that can enqueue a pull
request (see "Blocker" below). The card should stay open for that half.
## What changed
`.github/workflows/merge-queue-triage.yml` gains limb ③:
1. **Classify, from the job record only.** A job is `infra:no-runner`
when it ended `cancelled` with an empty `runner_name` and no steps. A
cancelled job the record does not decide (it did get a runner) is
checked for the platform's "The job was not acquired by Runner"
check-run annotation. No log text is read. The **build** is
`infra:no-runner` only when that explains every red. Every other red job
must be an aggregate gate whose failed steps are all `Verify … results`,
and whose own shard family (`NAME (k/n)`) has a no-runner member.
Anything else makes the build `failure`, read as before: a real failing
step, a cancelled job that had a runner, an aggregate from a different
family, or an annotation that could not be read.
2. **Re-queue budget: one per PR head.** On an `infra:no-runner` build,
the script reads the PR's current head through `pulls.get`. The comment
carries a durable per-head marker: a hidden HTML comment named
`merge-queue-infra:no-runner` with the PR number, head sha and run id.
The next `infra:no-runner` red on the same head finds that marker. Its
verdict is then "hand to a person, do not re-queue", and it names the
earlier queue build. A `failure` build never gets a verdict and never
writes the marker. If the head or the PR's comments cannot be read, the
verdict is `unknown`, never `once`.
3. **The comment names the class:** `分类:infra:no-runner` or
`分类:failure`. For `infra:no-runner` it lists the no-runner jobs, the
aggregates they explain, and the verdict. For a mixed build it still
lists the no-runner jobs, and it names the red those jobs cannot
explain.
⛔ The attestation rule is unchanged: a shard that never ran still does
not count as passing (#6082). The comment says so too. Nothing here
changes whether a build is green. The idempotency read of the PR's
comments now paginates, because the per-head markers live in those
comments.
Permission change, declared: the job gains **`checks: read`** (read
only) for the annotation leg. No other grant changes. The harness now
pins that the job holds no `contents:` grant.
## Blocker — the re-queue act
The workflow's verdict cannot be carried out by the workflow.
- **No enqueue credential.** The only enqueue path measured in this repo
is `enablePullRequestAutoMerge` through the fleet App token.
`fleet-write.yml` documents that this needs `contents: write`, which is
"ONE consumer". This workflow's `GITHUB_TOKEN` holds `actions: read`,
`pull-requests: write`, `issues: write` and now `checks: read`.
- **The token's own grant would not be enough either.** Granting
`contents: write` to `GITHUB_TOKEN` and calling `enqueuePullRequest` /
`enablePullRequestAutoMerge` would widen this job's permissions. Even
then, the merge group would be created by `GITHUB_TOKEN`, and GitHub
does not start workflow runs for events that `GITHUB_TOKEN` causes. It
is not established that CI would ever build such a merge group, and this
PR does not measure it.
So the comment says the workflow will not re-queue, and asks a person to
re-queue once. The harness makes an enqueue call an unmodelled API that
fails the battery (mutation M28). Wiring the act needs a decision:
either a second consumer of the fleet App token with `contents: write`,
or a measured `GITHUB_TOKEN` path.
## Done-when, clause by clause
- **A synthetic no-runner cancellation is classified and re-queued
once.**
- Classified: proven. N1 replays the real job records of queue build
37374282440. N2 replays queue build 37371558473, with eight no-runner
jobs and two aggregates. N7 uses the real annotation of check run
111979038621.
- Re-queued once: only the verdict is proven. N1 grants one re-queue,
and the N3 pair (run 2 reads run 1's own comment) sends a second red on
the same head to a person. N4 proves the budget is per head. The act is
blocked, as described above.
- **A real shard failure is not re-queued.** N5 (real failure), N6
(no-runner beside a real failure) and N9 (an aggregate from another
family) all classify as `failure`, with no verdict and no marker.
- **The triage comment names the class.** N1 through N10 all assert the
class line.
- **Test / dry-run fixture for both cases.**
- Ten scenarios cover limb ③. The new fixtures in
`scripts/fixtures/merge-queue-triage/` are trimmed real records, with
provenance in the README: `run-37374282440.jobs.json`,
`run-37371558473.jobs.json` and
`check-run-111979038621.annotations.json`.
- Nine new self-test mutations (M20 to M28) each turn the battery red at
the scenario they name, with a named control scenario that stays green.
## Validation (at 499d0ae)
- `node scripts/check-merge-queue-triage-outcome.mjs` → `OK (133
assertions over 34 scenarios …)`. The base had 90 assertions over 24
scenarios.
- `node scripts/check-merge-queue-triage-outcome.mjs --self-test` → `181
assertions, 32 mutations of the shipped script each driven to red`.
- `node scripts/pm/dispatch-gates.mjs --commands` derived 51 commands.
50 ran with exit 0, including `pnpm check:workflow-status-functions`,
`check:nul-bytes`, `check:required-contexts` and
`check:shard-attestation`. Reconciliation `--ran`: 51 accounted, 0
UNRUN.
- NOT MEASURED: `pnpm check:pm-dispatch-gates`. Its `--self-test` half
alone exceeded the 10-minute foreground cap (exit 124). Its log showed
1785 pass marks and 1 fail mark. The fail mark is on
`packages/qa/dogfood/test/per-file-cwd.setup.ts`, a path this diff does
not touch. Declared to CI.
- Narrowed lint: `eslint --no-inline-config --format json
scripts/check-merge-queue-triage-outcome.mjs` → 1 file, 0 errors, 0
warnings. The repo config enables no type-aware linting (no
`parserOptions.project`), so this diff cannot move the verdict on any
untouched file. The workflow YAML is not an eslint target.
## Acceptance notes
- The triage workflow listens to `workflows: [CI]` only. Run 37371558492
is the **Governed Surface Guard** workflow, and its no-runner red is
never triaged. The same holds for the sibling guard runs that ejected
#21872 that afternoon. Out of this card's file surface; noted here only.
- The budget keys on the PR head read at triage time. A push that lands
between the ejection and the triage run would be charged for the earlier
head's red. That is the conservative direction: the next red reaches a
person sooner.
- A run whose conclusion is `cancelled` (not `failure`) still gets no
comment. That is the existing eviction rule, unchanged.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01VDtqoecgES7ScQYGbFVDRv)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent d16b9fb commit f57627b
6 files changed
Lines changed: 3560 additions & 7 deletions
File tree
- .github/workflows
- scripts
- fixtures/merge-queue-triage
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
104 | 143 | | |
105 | 144 | | |
106 | 145 | | |
| |||
110 | 149 | | |
111 | 150 | | |
112 | 151 | | |
113 | | - | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
114 | 155 | | |
115 | 156 | | |
116 | 157 | | |
| |||
156 | 197 | | |
157 | 198 | | |
158 | 199 | | |
| 200 | + | |
159 | 201 | | |
160 | 202 | | |
161 | 203 | | |
| |||
244 | 286 | | |
245 | 287 | | |
246 | 288 | | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
247 | 293 | | |
| 294 | + | |
248 | 295 | | |
249 | | - | |
| 296 | + | |
250 | 297 | | |
251 | 298 | | |
252 | | - | |
| 299 | + | |
253 | 300 | | |
254 | 301 | | |
255 | 302 | | |
| |||
269 | 316 | | |
270 | 317 | | |
271 | 318 | | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
272 | 406 | | |
273 | 407 | | |
274 | 408 | | |
| |||
834 | 968 | | |
835 | 969 | | |
836 | 970 | | |
| 971 | + | |
| 972 | + | |
| 973 | + | |
| 974 | + | |
| 975 | + | |
| 976 | + | |
| 977 | + | |
| 978 | + | |
| 979 | + | |
| 980 | + | |
| 981 | + | |
| 982 | + | |
| 983 | + | |
| 984 | + | |
| 985 | + | |
| 986 | + | |
| 987 | + | |
| 988 | + | |
| 989 | + | |
| 990 | + | |
| 991 | + | |
| 992 | + | |
| 993 | + | |
| 994 | + | |
| 995 | + | |
| 996 | + | |
| 997 | + | |
| 998 | + | |
| 999 | + | |
| 1000 | + | |
| 1001 | + | |
| 1002 | + | |
| 1003 | + | |
| 1004 | + | |
| 1005 | + | |
| 1006 | + | |
| 1007 | + | |
| 1008 | + | |
| 1009 | + | |
| 1010 | + | |
| 1011 | + | |
| 1012 | + | |
| 1013 | + | |
| 1014 | + | |
| 1015 | + | |
| 1016 | + | |
| 1017 | + | |
| 1018 | + | |
837 | 1019 | | |
838 | 1020 | | |
839 | 1021 | | |
| |||
843 | 1025 | | |
844 | 1026 | | |
845 | 1027 | | |
| 1028 | + | |
| 1029 | + | |
846 | 1030 | | |
847 | 1031 | | |
848 | 1032 | | |
| |||
871 | 1055 | | |
872 | 1056 | | |
873 | 1057 | | |
| 1058 | + | |
874 | 1059 | | |
875 | 1060 | | |
876 | 1061 | | |
| |||
0 commit comments