Skip to content

CI: skip queue suites already passed on the exact same merge (#1130) - #1176

Merged
rmcdaniel merged 2 commits into
mainfrom
ci/1130-merge-queue-latency
Nov 12, 2026
Merged

rmcdaniel merged 2 commits into
mainfrom
ci/1130-merge-queue-latency

Conversation

@rmcdaniel

@rmcdaniel rmcdaniel commented Nov 12, 2026 •

Copy link
Copy Markdown
Member

Refs #1130. Cuts merge-queue time for the entries whose exact merge was already tested in the PR run, and keeps every check that run passed.

Before (measured)

Change

A group skips a suite only if a pull request run already passed the same merge: same first parent, same second parent (the PR head), same tree.

  • PR run: after the suite passes, the run uploads a marker artifact tested-merge-<workflow>-<base>-<tree> (kept 7 days). In PHPUnit Feature the upload is in qualification; in fixtures it is in result. The upload has continue-on-error, so a failed upload just makes the queue run the suite.

  • Merge group (scripts/ci/tested-merge.mjs check): skips only if all of these hold:

    • the checkout is this group's head commit;
    • a marker exists for this group's first parent and tree;
    • the run that uploaded it is a completed/success pull_request run of the same workflow file, in this repository, from a head branch in this repository (no forks), with head_sha equal to the group's second parent.

    Anything else runs the suite: a group stacked on a queued predecessor (its first parent was never tested), a fork, a failure, a missing token, or an API error.

  • What is skipped:

    • PHPUnit Feature: only the feature job. PHPUnit feature suite shows as skipped, which GitHub counts as passing for a required check. qualification accepts that only when event=merge_group and tested=true.
    • Shared Server fixtures: changes outputs run=false, so every fixture job is skipped. aggregate.mjs already requires all of them to be skipped and now gives this as the reason.
  • Never skipped: Central action policy preflight (reads central .github main live), Repository contracts (OpenAPI evolution, corpus policy, Composer audit, trust tests), Public Boundary, the Sonar queue analysis, and every suite on push, PR and dispatch.

Expected after

  • Entries whose tree matches the PR run (about 30–40% recently): ~18–27 min → ~4–5 min. That is Repository contracts (~2 min, now the longest job), then Sonar (~1 min), then the merge.
  • Mean enqueue → merged: about 25 → 19 min on the same sample. The median stays near 24 min, because most entries are still stacked or were tested on an older main. Cutting those further means shortening the fixture critical path (the 16–19 min matrix). That is out of scope here.
  • The push to main after a skipped group already runs only the cache-refresh jobs (queueQualified), so nothing changes there.

Checks run locally

  • node --test on TestedMergeTest (new, 33 cases), ServerParityAggregate, MergeGroupTriggers, MergeQueueSonar, ServerParityParts: 160/160 pass. All 10 .mjs suites that read workflows: 233/233 pass. Against the old workflows, the new tests fail (5 failures), so they detect a reverted workflow.
  • A mutation that drops TESTED_MERGE = true from the qualification gate fails the gate test.
  • Central action policy (qualification_policy.py validate --target server, .github main): pass. Only actions already pinned in the repo are used.
  • check-docker-workflow-isolation.py: pass (calls go through scripts/ci/tested-merge.sh, the same wrapper pattern as changes.sh). The docker isolation, Composer, pip, SBOM and cargo trust unittests: 174 OK.
  • actionlint 1.7.7: clean.
  • PhpunitFeatureWorkflowContractTest.php is updated for the new needs and gate. PHP wasn't available locally, so CI runs it.

For Security review

This is a CI trust change. Please check:

  1. The marker is trusted only as far as the tree. The marker name comes from the PR run's own workflow code, which is part of the tree being merged. A PR that changes a workflow to fake a marker would also be merging that workflow, so this adds no capability a reviewed PR doesn't already have. Fork PR runs are never trusted (head_repository.id must equal repository.id).
  2. actions: read is added to one new job in PHPUnit Feature (tested-merge). The fixtures changes job already had it. The token goes only to the merge-group check step, never to PR runs.
  3. A skipped job as the required check. PHPUnit feature suite shows as skipped on a tested group. The alternative is gating each of its ~20 steps, which is more invasive.
  4. Matching on parents and tree, not tree alone. Workflows read merge_group.base_sha and pull_request.base.sha (corpus/OpenAPI base). Matching on the first parent keeps those identical.
  5. The skip covers neither the action policy (which can change on central main) nor Repository contracts. Both still run on every group.

Ruleset (not changed here)

max_entries_to_build: 5, max_entries_to_merge: 5 and ALLGREEN grouping are already live (2026-11-11). I am proposing no other ruleset change in this PR. I'll put a separate check_response_timeout_minutes proposal on #1130.

🤖 Generated with Claude Code

A merge-queue group built straight onto the base its pull request run was
tested on merges the same two parents into the same tree. When that pull
request run passed, PHPUnit Feature now skips only its feature suite and
Shared Server fixtures skips its fixture jobs. Repository contracts, the
central action policy, Public Boundary and the Sonar queue analysis still
run on every group.

A passing same-repository pull request run records a marker artifact named
for the workflow, the merge's first parent and its tree. The group checks
that a marker exists for its own first parent and tree, and that the run
that uploaded it is a completed, successful pull_request run of the same
workflow file, from this repository (no forks), on exactly the group's
second parent. Groups stacked on a queued predecessor, forks, failures and
any API doubt run every suite.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Addresses SonarCloud S4036 (PATH lookup) and S6535 on #1176.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@rmcdaniel

Copy link
Copy Markdown
Member Author

Security: PASS (head f8279101, 63/63).

Integrity of the skip decision (scripts/ci/tested-merge.mjs):

  • A group skips only when HEAD is the merge-group head and a two-parent merge, and a non-expired marker exists named tested-merge-<key>-<first parent>-<tree>.
  • The run that uploaded that marker must also check out as event=pull_request, status=completed, conclusion=success, the same workflow path, head_sha equal to the group's second parent, and repository.id == head_repository.id == this repository (so no forks).
  • Every API error, missing token or malformed value resolves to tested=false, so the suite runs.
  • Git runs as /usr/bin/git through execFileSync, with no shell.

Forgery analysis: the marker name is chosen by the pull request run's own workflow code. Faking one means changing phpunit-feature.yml or server-parity.yml inside the PR's tree. But that same tree is what the merge group runs, and the central action policy still runs on every group and refuses unreviewed workflow bytes, so a forged marker gains nothing a workflow edit couldn't already do. Another PR can't lend its marker: head_sha must equal this group's second parent.

Who may skip:

  • Feature source qualification accepts a skipped feature suite only when event=merge_group, tested=true and FEATURE_RESULT=skipped. Pull request runs still require success.
  • A pull request run records a marker only after the qualification passed. The server-parity result job records only after aggregate passed, and the aggregate's new reason branch fires only when tested=true.
  • Central policy, contracts, Public Boundary and Sonar always run.

Token: actions: read sits on the tested-merge job alone, and the token reaches only the merge-group check step. The record step and the marker upload use no token.

Two notes, neither blocking:

  1. The skip applies only when the queue uses the merge method, because the group commit must have two parents. Squash or rebase groups always run everything, which is safe.
  2. Markers are kept for 7 days, and expired === false is checked. A PR that waits longer than that simply runs again.

@rmcdaniel
rmcdaniel added this pull request to the merge queue Nov 12, 2026
Merged via the queue into main with commit 00ee298 Nov 12, 2026
64 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant