Summary
Nine hosted CI workflows feed script/ci-changes-detector the same stale base SHA that caused #4756. The fix for that issue is scoped to .github/workflows/ci-required.yml only, so the same misrouting is still latent everywhere else.
Mechanism
For pull_request events actions/checkout resolves refs/pull/<n>/merge, so HEAD is the PR head merged into the current base tip.
github.event.pull_request.base.sha is a different commit: it only refreshes when the PR is opened or synchronized. On a branch that has not been pushed for a while, it lags HEAD's own base parent. Diffing from it folds every base-branch commit in between into the changed-file set, so the detector routes suites for changes the PR never made.
This is not a shallow-clone or fetch-depth problem. The merge base is resolved correctly; the input is wrong. It reproduces on a full, unshallow clone.
Evidence from #4756 (PR #4739, a one-file docs-only change):
Affected workflows
Each of these invokes script/ci-changes-detector "$BASE_REF" where BASE_REF falls back to github.event.pull_request.base.sha:
.github/workflows/lint-js-and-ruby.yml:89
.github/workflows/package-js-tests.yml:90
.github/workflows/gem-tests.yml:101
.github/workflows/integration-tests.yml:96
.github/workflows/precompile-check.yml:81
.github/workflows/examples.yml:97
.github/workflows/playwright.yml:87
.github/workflows/pro-integration-tests.yml:94
.github/workflows/pro-test-package-and-gem.yml:92
Secondary, lower severity (same stale base, but they gate on a changed-file diff rather than suite routing):
.github/workflows/check-docs-sidebar.yml:39
.github/workflows/check-llms-full.yml:62
.github/workflows/actionlint.yml:32
.github/workflows/bundle-size.yml is already correct and is the precedent to copy.
Severity
Lower than #4756. These workflows only run once hosted CI is enabled, and a stale base widens the diff, so the failure mode is over-selecting suites (wasted hosted runner minutes on a stale branch) rather than skipping a suite that was needed. There is no known case of it hiding a required check.
Remedy
Take the base from the merge commit's first parent, which is the exact commit the merge was computed against, and which is always present locally because it is a parent of HEAD.
.github/workflows/bundle-size.yml:82-99 already does this and documents the same reasoning. .github/workflows/ci-required.yml adopts it in the fix for #4756.
Guard it on HEAD having exactly two parents. A single-parent HEAD on a pull_request event is reachable (GitHub produces no merge ref for a PR that conflicts with its base), and there HEAD^1 is the PR's own previous commit, not the base. Taking it would be silently wrong.
Because the shared shape is repeated across nine files, consider extracting the resolution into a small composite action or a helper in script/lib/ rather than pasting it nine times.
Notes
- No changelog entry: CI plumbing is not user-visible per the
CHANGELOG.md seam.
- Semantic
.github/workflows/** changes require a linked Follow-up: Exercise GitHub Actions changes from PR #NNNN issue before merge (see .github/read-me.md).
Summary
Nine hosted CI workflows feed
script/ci-changes-detectorthe same stale base SHA that caused #4756. The fix for that issue is scoped to.github/workflows/ci-required.ymlonly, so the same misrouting is still latent everywhere else.Mechanism
For
pull_requesteventsactions/checkoutresolvesrefs/pull/<n>/merge, soHEADis the PR head merged into the current base tip.github.event.pull_request.base.shais a different commit: it only refreshes when the PR is opened or synchronized. On a branch that has not been pushed for a while, it lagsHEAD's own base parent. Diffing from it folds every base-branch commit in between into the changed-file set, so the detector routes suites for changes the PR never made.This is not a shallow-clone or
fetch-depthproblem. The merge base is resolved correctly; the input is wrong. It reproduces on a full, unshallow clone.Evidence from #4756 (PR #4739, a one-file docs-only change):
refs/remotes/pull/4739/merge=ed86829364509ac3e4e3782e52f44300d11e0f17, whose parents are613c6c2a(base side) andbf4711c1(PR head side). The workflow passedbase.sha=18bdc4f8, dated2026-07-18T08:06:09Z, about 15 hours behind. Result:run_generators=trueon a docs-only PR.613c6c2a, which is exactlyHEAD^1of the failing run's merge commit. Result: documentation-only.Affected workflows
Each of these invokes
script/ci-changes-detector "$BASE_REF"whereBASE_REFfalls back togithub.event.pull_request.base.sha:.github/workflows/lint-js-and-ruby.yml:89.github/workflows/package-js-tests.yml:90.github/workflows/gem-tests.yml:101.github/workflows/integration-tests.yml:96.github/workflows/precompile-check.yml:81.github/workflows/examples.yml:97.github/workflows/playwright.yml:87.github/workflows/pro-integration-tests.yml:94.github/workflows/pro-test-package-and-gem.yml:92Secondary, lower severity (same stale base, but they gate on a changed-file diff rather than suite routing):
.github/workflows/check-docs-sidebar.yml:39.github/workflows/check-llms-full.yml:62.github/workflows/actionlint.yml:32.github/workflows/bundle-size.ymlis already correct and is the precedent to copy.Severity
Lower than #4756. These workflows only run once hosted CI is enabled, and a stale base widens the diff, so the failure mode is over-selecting suites (wasted hosted runner minutes on a stale branch) rather than skipping a suite that was needed. There is no known case of it hiding a required check.
Remedy
Take the base from the merge commit's first parent, which is the exact commit the merge was computed against, and which is always present locally because it is a parent of
HEAD..github/workflows/bundle-size.yml:82-99already does this and documents the same reasoning..github/workflows/ci-required.ymladopts it in the fix for #4756.Guard it on
HEADhaving exactly two parents. A single-parentHEADon apull_requestevent is reachable (GitHub produces no merge ref for a PR that conflicts with its base), and thereHEAD^1is the PR's own previous commit, not the base. Taking it would be silently wrong.Because the shared shape is repeated across nine files, consider extracting the resolution into a small composite action or a helper in
script/lib/rather than pasting it nine times.Notes
CHANGELOG.mdseam..github/workflows/**changes require a linkedFollow-up: Exercise GitHub Actions changes from PR #NNNNissue before merge (see.github/read-me.md).