fix(ci): run the liveness probe on self-hosted so it survives hosted starvation (REQ-343) - #929
Merged
Merged
Conversation
…starvation (REQ-343) Maintainer decision. The probe ran on ubuntu-latest, so it could not report during GitHub-hosted starvation — one of the five modes it exists to detect. Observed live on 2026-09-09T22:30Z: the probe itself sat queued ten minutes while twelve self-hosted runners were idle (online=12 busy=0). A probe cannot report a stall of the runner class it runs on, so the choice is which blindness to accept, and the two modes are not equally silent. A self-hosted outage is LOUD — #855 moved CI Gate and all eleven of its needs to self-hosted, so an outage blocks every merge and surfaces within one pull request. Hosted starvation is SILENT — it blocks only advisory jobs and the release workflows, which are ubuntu-latest only, so it is invisible until a release is attempted and then blocks it. An alarm should watch the mode that does not announce itself. That reasoning is written into the workflow beside the `runs-on`, because the placement is counterintuitive and the next reader should not have to reconstruct it. A `needs`-based fallback cannot express "run on the other class if this one is starved": a starved job stays QUEUED rather than failing, so a dependent job waits on it forever instead of falling through. That is why this is a move rather than a pair. A RISK THAT WOULD HAVE MADE THIS WORSE THAN THE BUG. The probe is built entirely from `gh` calls, and `gh` ships preinstalled on GitHub-hosted images but not on this fleet — a survey of every workflow found no self-hosted job in the repository using it before now, so its presence could not be assumed. Moving without checking would have traded blind-during-hosted-starvation for blind-always, and an alarm that is silently dead is worse than one that is merely wrong: nothing distinguishes it from an alarm with nothing to report. Two steps handle it, mirroring the zola guard already in ci.yml — install `gh` into RUNNER_TEMP when absent, needing no sudo and persisting nothing on the host, then ASSERT it is on PATH and fail loudly naming the cause. Both directions negative-controlled locally: the assertion exits 0 with `gh` present and exits 1 emitting the error under PATH=/usr/bin:/bin. `jq`, which diagnose.sh depends on, is already proven available on self-hosted light — the YAML Lint job runs diagnose_test.sh there today. Unverified until it runs: whether `gh` is already present on the Hetzner runners, and so whether the install path is exercised at all. The guard covers both cases, so the first scheduled run answers it. Confirmed with fmt 0, clippy 1.97.0 --all-targets -D warnings 0, cargo test --workspace 0 (2368 passed), cargo test -p rivet-cli --test cli_commands 0 (213 passed), rivet validate 0, rivet docs check 0, yamllint 0, diagnose_test.sh 0, and `bash -n` over every embedded run block. Fixes: REQ-343
📐 Rivet artifact delta
Graphgraph LR
REQ_343["REQ-343"]:::modified
classDef added fill:#d4edda,stroke:#28a745,color:#155724
classDef removed fill:#f8d7da,stroke:#dc3545,color:#721c24
classDef modified fill:#fff3cd,stroke:#ffc107,color:#856404
classDef overflow fill:#e2e3e5,stroke:#6c757d,color:#495057,stroke-dasharray: 3 3
Modified
Posted by |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Maintainer decision: self-hosted.
The probe ran on
ubuntu-latest, so it could not report during GitHub-hosted starvation — one of the five modes it exists to detect. Observed live 2026-09-09T22:30Z: the probe itself sat queued ten minutes while twelve self-hosted runners were idle (online=12 busy=0).Why the counterintuitive placement is right
A probe cannot report a stall of the class it runs on, so the choice is which blindness to accept — and the modes aren't equally silent:
CI Gate+ all 11 needs on self-hosted, so it blocks every merge; found within one PRubuntu-latest-only release workflows; invisible until a release is attemptedAn alarm should watch the mode that doesn't announce itself. That rationale is written into the workflow beside the
runs-on.A
needs-based fallback can't express "run on the other class if this one is starved" — a starved job staysqueuedrather than failing, so a dependent waits forever instead of falling through. Hence a move, not a pair.A risk that would have made this worse than the bug
The probe is built entirely from
ghcalls.ghis preinstalled on GitHub-hosted images but not on this fleet — surveying every workflow found no self-hosted job in the repo usingghbefore now.Moving without checking would have traded blind during hosted starvation for blind always. A silently dead alarm is worse than a wrong one: nothing distinguishes it from an alarm with nothing to report.
Two steps handle it, mirroring the zola guard already in
ci.yml— install intoRUNNER_TEMPif absent (no sudo, nothing persisted), then assert and fail loudly. Negative-controlled both ways:jqwas the opposite case — provably available, sinceyaml-lintrunsdiagnose_test.shon self-hostedlighttoday.Unverified until it runs
Whether
ghis already present on the Hetzner runners, and so whether the install path is exercised at all. The guard covers both cases, so the first scheduled run answers it — and a failed install fails visibly rather than reporting nothing.cargo fmt --all --check/clippy -D warnings(1.97.0)cargo test --workspacecargo test -p rivet-cli --test cli_commandsrivet validate/docs check/yamllint/diagnose_test.shbash -nover every embeddedrunblockFixes: REQ-343