Discovered during the Mininglamp-OSS workflow optimization sprint (stage 5 ruleset scan).
Problem
.github/workflows/ci.yml build job currently has:
- Matrix:
node-version: [18, 20] → emits check names build (18) / build (20)
- Job-level guard:
if: needs.changes.outputs.code == true
The main branch protection ruleset (id 16278127) requires build (18) and build (20) as status checks.
Latent deadlock
GitHub behaviour: a matrix parent job that is skipped via job-level if: does not create any matrix children check-runs (unlike a plain job skip, which leaves a conclusion=skipped record).
If a future PR has changes.outputs.code == false, the required checks build (18) and build (20) will be permanently missing, and the PR will be unmergeable under the current ruleset.
Why it has not exploded yet
dorny/paths-filter negation patterns (code: [!**/*.md, !docs/**]) classify .github/workflows/*.yml as "not docs" → code=true even on workflow-only PRs. So merged workflow-only PRs (e.g. #74 history-check) have been triggering the matrix and passing. The moment we tighten the filter (e.g. add !.github/workflows/** to exclude pure workflow PRs from build), the deadlock fires.
This is the same root cause as octo-cli#46 (already fixed in v4 by retaining matrix + step-level skip guard). octo-admin happens to not have triggered it yet.
Reference
Proposed fix (stable wrapper)
Keep the matrix, drop the job-level if:, add a wrapper job whose name matches the ruleset:
build:
needs: [changes]
strategy:
matrix:
node-version: [18, 20]
steps:
- name: Skip notice
if: needs.changes.outputs.code != true
run: echo "docs-only PR; no work needed"
- uses: actions/checkout@...
if: needs.changes.outputs.code == true
# ...rest of build steps each guarded with the same if:
build-required:
# Stable wrapper — always runs, always reports a result, named exactly as ruleset expects
needs: [build]
if: always()
runs-on: ubuntu-latest
steps:
- run: |
if [ "${{ needs.build.result }}" = "failure" ] || [ "${{ needs.build.result }}" = "cancelled" ]; then
exit 1
fi
Then update ruleset 16278127: replace build (18) + build (20) required checks with a single build-required.
Benefit: future Node version additions do not require ruleset changes.
Scope
- Does not affect any currently-merged PR (all 13 stage-3 merges verified clean in the scan report).
- Does not block stage 4. Can be picked up at stage 5.
Owner
To be assigned by @cpipi_bot.
Discovered during the Mininglamp-OSS workflow optimization sprint (stage 5 ruleset scan).
Problem
.github/workflows/ci.ymlbuildjob currently has:node-version: [18, 20]→ emits check namesbuild (18)/build (20)if: needs.changes.outputs.code == trueThe
main branch protectionruleset (id16278127) requiresbuild (18)andbuild (20)as status checks.Latent deadlock
GitHub behaviour: a matrix parent job that is skipped via job-level
if:does not create any matrix children check-runs (unlike a plain job skip, which leaves aconclusion=skippedrecord).If a future PR has
changes.outputs.code == false, the required checksbuild (18)andbuild (20)will be permanently missing, and the PR will be unmergeable under the current ruleset.Why it has not exploded yet
dorny/paths-filternegation patterns (code: [!**/*.md, !docs/**]) classify.github/workflows/*.ymlas "not docs" →code=trueeven on workflow-only PRs. So merged workflow-only PRs (e.g. #74 history-check) have been triggering the matrix and passing. The moment we tighten the filter (e.g. add!.github/workflows/**to exclude pure workflow PRs from build), the deadlock fires.This is the same root cause as octo-cli#46 (already fixed in v4 by retaining matrix + step-level skip guard). octo-admin happens to not have triggered it yet.
Reference
190879f)if:skip 陷阱"Proposed fix (stable wrapper)
Keep the matrix, drop the job-level
if:, add a wrapper job whose name matches the ruleset:Then update ruleset
16278127: replacebuild (18)+build (20)required checks with a singlebuild-required.Benefit: future Node version additions do not require ruleset changes.
Scope
Owner
To be assigned by @cpipi_bot.