Nine behaviours of GitHub Actions
that are not documented.
Each was verified by experiment or observed in production, and each changes what a green required check proves. GitHub publishes no executable specification of these; this is one.
Re-verified every time the machine-readable version at arcifact.io/semantics is requested. Nothing here names a repository whose owner did not publish it themselves.
Does a skipped job satisfy a required status check?
A required status check whose job is SKIPPED is treated as satisfied. The pull request can merge without the job having run.
How this was established. observed in production.
· vercel/next.js#97558
· vercel/next.js#97551
· vercel/next.js#97548
What GitHub documents. branch protection documentation does not state what a skipped required check does
Why it matters. 53% of jobs in a 265-repository corpus carry an `if:`, so this is the modal way a required check passes without doing its work
Does continue-on-error make a failing job report success to needs?
A job marked `continue-on-error: true` that FAILS reports `success` in the `needs` context of its dependents, while the jobs API reports its conclusion as `failure`. Two of the platform's own surfaces disagree, and a gate reads the one that says success.
How this was established. settled by experiment.
· arcifact-dev/coe-semantics run 32402586923: RESULT flaky = success, RESULT honest = failure, identical failing steps, one marking apart
What GitHub documents. documented for STEPS. Not documented for jobs, and the surface disagreement is not documented at all
Why it matters. 176 real instances across a corpus could not settle it, because they were reading the API field rather than the needs field. The experiment has a named control, which is what makes it attributable
Do pull_request jobs run in a GitHub merge queue?
A merge queue delivers the `merge_group` event, not `pull_request`. A job conditioned on the pull request event silently does not run in the queue, while its required check still reports green there.
How this was established. observed in production.
· 7 incidental instances across 6 repositories; 4 further gates name merge_group deliberately
What GitHub documents. documented, but the consequence for required checks is not
Why it matters. the queue is the last gate before main, and this is where a check is most load-bearing
Does a run step use bash -e by default?
On Linux runners, `run:` executes as `bash -e`. A bare `false` in body position fails the step; the same word in a condition, a command list, or after `!` does not.
How this was established. settled by experiment.
· executed across all four exception contexts: else-body exit 1, condition 0, list 0, negated 0
What GitHub documents. the default shell is documented; the errexit consequence is not
Why it matters. a recogniser was removed and restored over this single word, and five counterexamples in a shipped pack turned out to be false
Why does a required check name a job that does not exist?
A matrix job publishes check runs named `Job (param, param)`. A required context can therefore name something no job in the workflow is literally called.
How this was established. observed in production.
· trpc/trpc: 29 of 35 required contexts are matrix legs
· urfave/cli: job names are themselves expressions
What GitHub documents. matrix naming is documented; the join to required contexts is not
Why it matters. this single join produced a published claim that 58% of required contexts were federated hearsay. The true figure is 13%
What does if: always() actually do to a gate?
`always()` is a constant, not a free variable. A gate guarded by it runs in every admissible world.
How this was established. derived.
· evaluated over the expression grammar
What GitHub documents. documented
Why it matters. treating it as a variable made a satisfiability check unsound in both directions
Can a cancelled workflow run publish a green check?
A run cancelled by a concurrency group can publish a required check as SUCCESS, when the aggregate uses `always()` and accepts a cancelled dependency result.
How this was established. observed in production (observes production).
· grafana/grafana runs 32363249879, 32362898101, 10 of 10 cancelled runs examined
What GitHub documents. not documented
Why it matters. cancellation is routine under cancel-in-progress, so this is a high-frequency path to a green that proves nothing
Can a later check run of the same name clear a failing one?
A later check run with the SAME NAME on the same head sha supersedes an earlier one for required status checks. A failing required check can be turned green by posting a second run of that name, and the pull request becomes mergeable.
How this was established. settled by experiment (observes production).
· arcifact-dev/gate-demo PR#3, sha d7f61a2d17a140be4d1f250bfa68195cdbab1530: supersession-test posted failure 19:37:44 then success 19:38:08; mergeable_state moved blocked -> unstable, mergeable true, and GitHub's own filter=latest returns the success
What GitHub documents. not documented
Why it matters. it means any workflow path that can produce a green run of a required check name can clear an earlier red one. A gate that exits 0 on some event is therefore not merely uninformative, it is an eraser.
Why is a required check never reported?
A ruleset may require a context that no workflow in the repository publishes. GitHub renders the literal string Expected — Waiting for status to be reported indefinitely and does not say why. several of these are documented for steps and not for jobs, which is a documentation gap rather than a defect. One is a genuine divergence between two of the platform's own surfaces. The corpus reports what the runners do. assertions with no executable check are listed. A conformance corpus that hides which rows are unverified is a claim wearing a table.
How this was established. observed in production (observes production).
· 26 of 67 open pull requests older than two hours were missing a required context; 14 were fork approvals
What GitHub documents. not documented
Why it matters. this is the highest-traffic confusion in the constituency and nothing in the platform diagnoses it
Every one of these was found by being wrong.
None of these was researched in the abstract. Each was established because an analyser said something confident and a repository proved it wrong, and the rule that came out of it was written down with the evidence rather than the intuition.
The machine-readable register is at arcifact.io/semantics and re-runs its assertions when you request it. The product that uses them is Arcifact Gate: it reads a repository's declared workflow and reports what a green required check actually establishes.