Skip to content

CI: required status check embeds the Julia version, so a pin bump will deadlock every PR #38

Closed

Description

@hyperpolymath

Problem

The Optimus-Branch ruleset (created 2026-09-21, id 23793298) requires the status check context:

Julia 1.12.5 / ubuntu-24.04

That context string is generated from the job's matrix values, so the Julia version is part of the required check's name. The moment the pin moves — 1.12.5 → anything else — the workflow starts reporting a check called Julia 1.13.0 / ubuntu-24.04, and the context named in the ruleset is never reported at all.

A required check that never reports is not a failure; it is silence, and silence never satisfies a required status check. Every PR then sits on "Expected — Waiting for status to be reported" indefinitely, clearable only by an admin bypass.

This is latent, not live: the pin currently matches. It will fire on the next Julia bump — most likely via Dependabot or a routine toolchain update, i.e. exactly when nobody is looking at the ruleset.

The same reasoning applies to ubuntu-24.04, which is also pinned deliberately (the R apt pin targets it).

Acceptance criteria

  • The test job's rendered check name is version-stable — e.g. Julia tests — with the Julia and runner versions moved into the step body or kept in the matrix but excluded from the job name:.
  • The Optimus-Branch ruleset's required_status_checks entry is updated to the new context in the same change, so the two never disagree.
  • A Julia pin bump is demonstrated (locally or on a scratch branch) to still report the required context.
  • config/ci/ pin-coupling tests (test_install_pins.jl) still pass — the version must remain asserted somewhere, just not in the check's name.

Notes

Non-obvious constraint for whoever picks this up: do not add a path filter to the workflow carrying a required context. A paths:-filtered workflow reports nothing on a PR that misses its paths, which deadlocks the gate in the same way. This is why contracts (ui.yml, filtered to ui/**) was deliberately excluded from the ruleset.

🤖 Generated with Claude Code

https://claude.ai/code/session_01X3hgXxWm6umMgZkjYyHnnm

Activity

  1. hyperpolymath commented on Sep 21, 2026

    @hyperpolymath
    OwnerAuthor

    Correction: the first attempt (#39) did not fix this, and made it live on main

    #39 renamed the job to Julia tests and I reported the required check as version-stable. That was wrong, and the reason is worth recording here because it is the whole substance of this issue.

    Measured on #39's own head 776e340d, after it had landed:

    Julia tests (1.12.5, ubuntu-24.04)    <- what GitHub actually posted
    Julia tests                           <- what the ruleset requires
    

    GitHub appends the matrix combination to a job's posted check name whenever the name: does not itself reference the matrix. The direction is counter-intuitive: the old name, Julia ${{ matrix.julia-version }} / ${{ matrix.os }}, was suppressing the suffix; replacing it with a static string switched the suffix on. A 1×1 matrix is still a matrix. So a fixed name made the check name less stable, not more — both pins stayed embedded, the required context Julia tests was never reported at all, and main went into precisely the deadlock described above. #39 merged only because an admin bypass was available.

    The control was in the same API response the whole time: Repo hygiene (licence · format · lint · commit), a job with no matrix, posts its name: verbatim.

    Why the guard I added in #39 did not catch it. It asserted !occursin("${{", name) over the YAML name: field. The consumer — the ruleset — matches the rendered check-run name. Different artefact, so it went green on a broken fix. The "bump demonstration" had the same defect: it parsed name: across three hypothetical pins and called the output "the check name". Neither could fail. ci.yml triggers only on push: branches: [main] and pull_request: branches: [main], so a branch push renders no check at all — the name is observable only on a PR, and that constraint is exactly what tempts you into asserting it "by construction".

    The real fix — #40

    The matrix selected exactly one combination and bought nothing, so it is removed rather than worked around. runs-on: ubuntu-24.04 and setup-julia version: "1.12.5" are literals, each keeping the reasoning that sat beside its matrix entry.

    Evidence, read off #40's head 7adaeb74 via commits/<sha>/check-runs — the consumer's artefact, not the YAML:

    Julia tests                                       <- exact, no suffix
    Repo hygiene (licence · format · lint · commit)
    

    The guard now asserts both necessary conditions over every job in the file: the name interpolates nothing and the job has no strategy.matrix. Against the previous commit it fails on test alone (5 pass / 1 fail), so it discriminates rather than blanket-failing. If a matrix is ever genuinely wanted, the required context must first move to a matrix-free aggregator job — which would then need if: ${{ always() }} and an explicit needs.test.result == 'success', since a skipped job passes a required check.

    # acceptance criterion status
    1 version-stable job name ✅ posted name is exactly Julia tests
    2 ruleset context updated in the same change ✅ Julia tests, unchanged and now satisfiable
    3 bump demonstrated ✅ a bump moves only a literal; with no matrix there is nothing for GitHub to append
    4 test_install_pins.jl passing ✅ 102 / 102

    🤖 Generated with Claude Code

    https://claude.ai/code/session_01X3hgXxWm6umMgZkjYyHnnm

  2. added 2 commits that reference this issue on Oct 14, 2026
    d0b31f4
    5b45d09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions