Skip to content

ci(merge-queue): name a no-runner red as infra:no-runner, with a once-per-head re-queue verdict - #21946

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21933-mq-no-runner-requeue
Oct 6, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-21933-mq-no-runner-requeue

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

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 fix(plugin-auth)!: implicit account linking requires the standard local-ownership condition; unlink is honoured #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

@github-actions github-actions Bot added ci/cd documentation Improvements or additions to documentation labels Oct 6, 2026
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 6, 2026
@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 6, 2026 03:39
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 6, 2026 03:39
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 6, 2026
Merged via the queue into main with commit f57627b Oct 6, 2026
36 of 37 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-21933-mq-no-runner-requeue branch October 6, 2026 04:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci/cd documentation Improvements or additions to documentation size/xl skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants