Sibling of #12823. That issue covers PRs left with zero checks because GITHUB_TOKEN bot pushes do not trigger workflows. This is a second, distinct route to the same dangerous end state: a PR that reads as green but is structurally unmergeable.
Observed 2026-07-28 on PRs #12895 and #12896
While the singleton self-hosted runner was offline:
runner: Little-Slave status=offline busy=false labels=self-hosted,Linux,X64
run: status=pending conclusion=null jobs=[] <- zero jobs ever created
Both PRs reported 19 success / 0 failures across every check that ran, while being blocked the whole time: the required context Unit & Integration Tests had produced no check-run at all, so commits/{sha}/status sat at pending.
Three properties made this hard to diagnose, and each is independently worth guarding against:
- Absence is not failure. Any tooling that summarises success/failure counts reports these PRs as clean. The only way to see the block is to diff the reported contexts against
branches/<base>/protection.required_status_checks.contexts.
- The approval sweep does not catch it. The standard remedy searches for
conclusion == "action_required". These runs are status=pending, conclusion=null, and POST /actions/runs/{id}/approve returns 403 not waiting for approval.
- Runs created during the outage never self-schedule. After the runner came back, both runs went to
completed/cancelled with jobs: 0. gh run rerun cannot help a run that never created jobs — it has nothing to re-run.
frontend-test.yml is correctly written for the ordinary case: it deliberately carries no pull_request.paths filter and ships a unit-tests-skip shim (same name:, ubuntu-latest) precisely so the required context always reports. That design is defeated when the runner hosting the real job is offline, because the run never reaches the point of choosing a branch.
Scope
- Give the required-context shim a path that does not depend on the self-hosted runner, so the context reports even when the runner pool is empty.
- Or: surface the condition — a check that compares reported contexts against the required list and fails loudly with "required context X never reported", instead of leaving the PR silently at
pending.
Done when
- A runner outage produces a visible, named failure on affected PRs rather than an indefinite
pending with no explanation.
- The diagnosis does not require manually diffing check-runs against branch protection.
Refs #12823
Sibling of #12823. That issue covers PRs left with zero checks because
GITHUB_TOKENbot pushes do not trigger workflows. This is a second, distinct route to the same dangerous end state: a PR that reads as green but is structurally unmergeable.Observed 2026-07-28 on PRs #12895 and #12896
While the singleton self-hosted runner was offline:
Both PRs reported 19 success / 0 failures across every check that ran, while being blocked the whole time: the required context
Unit & Integration Testshad produced no check-run at all, socommits/{sha}/statussat atpending.Three properties made this hard to diagnose, and each is independently worth guarding against:
branches/<base>/protection.required_status_checks.contexts.conclusion == "action_required". These runs arestatus=pending, conclusion=null, andPOST /actions/runs/{id}/approvereturns403 not waiting for approval.completed/cancelledwithjobs: 0.gh run reruncannot help a run that never created jobs — it has nothing to re-run.frontend-test.ymlis correctly written for the ordinary case: it deliberately carries nopull_request.pathsfilter and ships aunit-tests-skipshim (samename:,ubuntu-latest) precisely so the required context always reports. That design is defeated when the runner hosting the real job is offline, because the run never reaches the point of choosing a branch.Scope
pending.Done when
pendingwith no explanation.Refs #12823