@@ -305,37 +305,37 @@ jobs:
305305 # automatically — no `pull_request` round-trip needed for non-fork
306306 # non-release PRs.
307307 required-check :
308- # The name is conditional on purpose. On an in-repo feature PR the
309- # `pull_request` run skips this job (the push run owns the status), and
310- # a skipped job still posts a check run under its name. GitHub treats a
311- # SKIPPED required check as satisfied — so if the skipped job were named
312- # `✅ Required PR Check`, it would green-light the ruleset (and enable
313- # auto-merge) while the push run's real mirror hadn't been created yet
314- # (it only appears once `ci` finishes), letting a PR merge mid-test or
315- # even override a red result. An expression name keeps the skipped job
316- # off the required name: GitHub does not evaluate the name of a skipped
317- # job, so it shows up as the raw expression text (still not the
318- # required name), while every path that runs evaluates to
319- # `✅ Required PR Check`. The condition is written out anyway so the
320- # name stays correct if GitHub ever starts evaluating it, and must stay
321- # identical to the `if:` below.
322- name : ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == true || github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'hotfixes') && '✅ Required PR Check' || '⏭️ Required PR Check (reported by the push run)' }}
323- needs : ci
324- # Mirror the `ci` job's gating exactly. The four cases that run:
308+ # Which name this job reports under is the whole point of it.
309+ #
310+ # The ruleset gates merges on `✅ Required PR Check`, so a check with that
311+ # name must only ever exist on a head SHA after the full test matrix for
312+ # that SHA has finished, mirroring its result. `needs: ci` guarantees the
313+ # timing: the job is only created once every Build & Test leg and the
314+ # coverage job are done.
315+ #
316+ # On an in-repo feature PR the `pull_request` run does not own the status
317+ # (the push run on the head branch does), so it must not post
318+ # `✅ Required PR Check` at all. Two traps rule out the obvious shapes:
319+ # - A job SKIPPED by `if:` still posts a check run under its name, and
320+ # GitHub treats a skipped required check as satisfied. Under the real
321+ # name that let PRs merge mid-test (CLDMV/slothlet#553).
322+ # - GitHub does not evaluate the `name:` of a skipped job, so a
323+ # conditional name on a skippable job shows up as the raw expression
324+ # text (#350).
325+ # So the job never skips: it runs on every path, the name expression is
326+ # always evaluated, and the in-repo PR path lands on a readable,
327+ # non-required name and passes as a no-op. The condition below is
328+ # repeated in the step's OWNS_STATUS and must stay identical. The paths
329+ # that own the status:
325330 # 1. push events (job needs CI run)
326331 # 2. fork PRs (push doesn't cover forks)
327332 # 3. release PRs from `next` → master/main (push covers SHA but commit-gate skips chore-bump)
328333 # 4. release PRs from `hotfixes` → master/main (same reason)
329- # In-repo feature PRs targeting `next` / `hotfixes` skip on
330- # pull_request — the push run on the head branch reports the status
331- # on the SHA. See the `name:` above for why the skipped job is renamed.
332- if : |
333- always() && (
334- github.event_name != 'pull_request' ||
335- github.event.pull_request.head.repo.fork == true ||
336- github.event.pull_request.head.ref == 'next' ||
337- github.event.pull_request.head.ref == 'hotfixes'
338- )
334+ name : ${{ (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == true || github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'hotfixes') && '✅ Required PR Check' || '⏭️ Required PR Check (reported by the push run)' }}
335+ needs : ci
336+ # always(): run even when `ci` is skipped (the in-repo PR path) or failed
337+ # (so the mirror can report red).
338+ if : always()
339339 # Match the reusable's runner routing (workflow-ci.yml): private CLDMV
340340 # repos run on self-hosted cldmv-runners (GitHub-hosted Actions budget is
341341 # private-metered and exhausted), public repos use free GitHub-hosted, and
@@ -349,10 +349,19 @@ jobs:
349349 steps :
350350 - name : Mirror reusable result
351351 env :
352+ OWNS_STATUS : ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == true || github.event.pull_request.head.ref == 'next' || github.event.pull_request.head.ref == 'hotfixes' }}
352353 IS_MASTER_SYNC : ${{ needs.ci.outputs.is_master_sync }}
353354 DOCS_ONLY : ${{ needs.ci.outputs.docs_only }}
354355 CI_RESULT : ${{ needs.ci.result }}
355356 run : |
357+ # In-repo feature PR: the push run on the head branch reports
358+ # `✅ Required PR Check`. This job runs under the
359+ # `⏭️ Required PR Check (reported by the push run)` name and
360+ # must not gate anything.
361+ if [ "$OWNS_STATUS" != "true" ]; then
362+ echo "In-repo PR event — the push run reports ✅ Required PR Check for this SHA."
363+ exit 0
364+ fi
356365 echo "ci.result=$CI_RESULT docs_only=$DOCS_ONLY is_master_sync=$IS_MASTER_SYNC"
357366 # next/hotfixes was force-synced to master — head SHA matches the
358367 # default branch, nothing new to test, green-light without running CI.
0 commit comments