Skip to content

CI: move every job to the org self-hosted fleet — scry was the last holdout - #205

Draft
avrabe wants to merge 1 commit into
mainfrom
migrate-ci-self-hosted
Draft

CI: move every job to the org self-hosted fleet — scry was the last holdout#205
avrabe wants to merge 1 commit into
mainfrom
migrate-ci-self-hosted

Conversation

@avrabe

@avrabe avrabe commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

On 2026-09-02 a PR sat 85 minutes with 0 of 13 jobs started, surviving a full cancel + re-run, while 5 runners in the pulseengine fleet sat idle. This is not contention:

scry 19 of 19 jobs on GitHub-hosted ubuntu-latest
rivet / witness / meld heavy jobs already on [self-hosted, linux, x64, light|rust-cpu]
org fleet 12 runners, all online, 7 busy / 5 idle

The org migrated to a Hetzner self-hosted fleet and scry never followed, so it is the one repo fully exposed when hosted capacity doesn't materialise. rivet even ships a job named "Traceability (hosted fallback)" — that is what engineering around this failure mode looks like.

Mapping (mirrors rivet's ci.yml)

light — no heavy Rust compile: Format · WIT round-trip · AADL (spar) · claim-check · Required-checks · Rivet validate / delta / traceability · Pages

rust-cpu — builds or proves: Clippy · Test · Bazel build · cargo-deny · MC/DC · Rocq · Verus · Publish · Release · MC/DC evidence

Required contexts are unaffected

Job names are unchanged, and runs-on does not affect a check's context name. Verified rather than assumed — check-required-checks.py --against-file: 12 contexts, 13 jobs, PASS.

Corrected a comment this change falsified

rocq-proofs.yml said:

"Stays on ubuntu-latest: requires Nix + Bazel for the hermetic Rocq 9.0 toolchain."

The Nix and Bazel requirement is satisfied by the install steps in that job, not by the runner image — so it was never a reason to stay hosted. Leaving it would have been the same drift as the "carry no verifies link by construction" line repaired in #198.

The risk, recorded rather than discovered

bazel-build and test install Nix via cachix/install-nix-action, because Bazel resolves @rocq_toolchains through rules_nixpkgs and fails with "nix-build not found in PATH" without it. That install has to work on self-hosted too.

ci.yml's header now says so, so a failure on a Nix step points at the runner class rather than sending someone into the build. Rocq and Verus are continue-on-error, making them the cheapest early signal.

This PR is its own test: if the jobs start, the diagnosis was right.

Refs: FEAT-093

🤖 Generated with Claude Code

https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc

…oldout

On 2026-09-02 a PR sat 85 minutes with 0 of 13 jobs started, surviving a
full cancel + re-run, while 5 runners in the pulseengine fleet sat IDLE.
The cause is not contention:

  scry            19 of 19 jobs on GitHub-hosted `ubuntu-latest`
  rivet/witness/meld  heavy jobs already on
                  [self-hosted, linux, x64, light|rust-cpu]
  org fleet       12 runners, ALL online, 7 busy / 5 idle

The org migrated to a Hetzner self-hosted fleet and scry never followed,
so it is the one repo fully exposed when hosted capacity does not
materialise. rivet even ships a job literally named "Traceability (hosted
fallback)", which is what engineering around this failure mode looks
like.

Mapping mirrors rivet's: `light` for checks that do no heavy Rust compile
(Format, WIT round-trip, AADL, claim-check, required-checks, rivet
validate/delta/traceability, Pages), `rust-cpu` for anything that builds
or proves (Clippy, Test, Bazel, cargo-deny, MC/DC, Rocq, Verus, publish,
release, MC/DC evidence).

JOB NAMES ARE UNCHANGED, so all 12 required contexts (scry#130) keep
reporting — `runs-on` does not affect a check's context name. Verified
with check-required-checks.py: 12 contexts, 13 jobs, PASS.

CORRECTED A COMMENT THIS CHANGE FALSIFIED. rocq-proofs.yml said "Stays on
ubuntu-latest: requires Nix + Bazel for the hermetic Rocq 9.0 toolchain".
The Nix and Bazel requirement is satisfied by the install STEPS in that
job, not by the runner image, so it was never a reason to stay hosted —
and leaving the comment would have been the same drift as the "carry no
verifies link by construction" line repaired in #198.

THE RISK IS RECORDED RATHER THAN DISCOVERED. `bazel-build` and `test`
install Nix via cachix/install-nix-action, because Bazel resolves
@rocq_toolchains through rules_nixpkgs and fails with "nix-build not
found in PATH" without it. That install has to work on self-hosted too.
ci.yml's header now says so, so a failure on a Nix step points at the
runner class rather than sending someone into the build. Rocq and Verus
are `continue-on-error`, making them the cheapest early signal.

Refs: FEAT-093

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KkNzkNYzPh7366DkNijeNc
@avrabe
avrabe marked this pull request as draft September 3, 2026 05:58
@avrabe

avrabe commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Parked as draft — the diagnosis holds, the execution does not, and the urgency evaporated.

What this PR got right

The exposure is real and worth fixing eventually: scry is the only pulseengine repo still on GitHub-hosted ubuntu-latest (19 of 19 jobs), while rivet/witness/meld run their heavy jobs on the org fleet. And the retarget demonstrably works for the runner-availability problem — on ubuntu-latest 0 jobs started in 85 minutes; on the fleet, 5 started within ~85 seconds.

What it got wrong

5 jobs fail, and 4 of them fail on the same step: Install Nix.

job failing step
Test Install Nix
Bazel build (//:scry) Install Nix
Rocq Formal Proofs Install Nix
Verus Formal Proofs Install Nix
cargo-deny cargo deny check (separate cause)

That is exactly the risk this PR's own ci.yml header predicted, which is the one satisfying part — the failure was diagnosed rather than discovered. cachix/install-nix-action does not work on these runners as configured, and no sibling repo installs Nix on the fleet, so there is no in-house pattern to copy. Passing jobs (Clippy, claim-check, AADL) are all local work on a cached toolchain.

scry needs Nix because Bazel resolves @rocq_toolchains through rules_nixpkgs and fails with nix-build not found in PATH without it. Test and Bazel build are required contexts, so this cannot merge in its current state.

And the urgency is gone

The stall that motivated this was transient — long, but transient. #204's re-run completed green with all 12 jobs after roughly nine hours, and rivet/meld have since completed successfully too. So this is no longer blocking the v3.3.0 release, which proceeds on the existing configuration.

What would make it landable

One of:

  1. A Nix installer that works on self-hosted — e.g. DeterminateSystems/nix-installer-action, or detecting a pre-installed Nix and skipping the step.
  2. Provision Nix on the rust-cpu runners and drop the install step entirely — probably the cleanest, and an org-fleet decision rather than a scry one.
  3. Hybrid: fleet for everything that does not need Nix, ubuntu-latest for the four that do. This narrows the exposure without eliminating it, and leaves the two required jobs on the class that stalled.

I have not picked one because (2) is an infrastructure call and (1) needs a runner I can test against. Left as a draft with the evidence rather than closed, since the underlying exposure has not gone away — it just stopped being today's problem.

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown

📐 rivet artifact delta

PR: #205 Base SHA: bc9523ce

Validation

head — `rivet validate` result
  SR-11 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-12 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-13 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-2 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-3 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-4 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-5 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-6 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-7 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-8 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-9 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SYS-1 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-2 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-3 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-4 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-5 (system-req, status: accepted) — missing: sys-integration-verification
  → run `rivet validate --explain SR-1` to see which link type and source types satisfy a gap

Result: PASS (190 warnings)
Schemas: common@0.3.0 (embedded), dev@0.3.0 (embedded), research@0.1.0 (embedded), research-ext@0.1.0 (on-disk), dev-ext@0.1.0 (on-disk), safety-case@0.1.0 (embedded), aspice@0.2.0 (embedded)
base — `rivet validate` result (for comparison)
  SR-11 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-12 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-13 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-2 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-3 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-4 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-5 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-6 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-7 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-8 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SR-9 (sw-req, status: accepted) — missing: sw-integration-verification, unit-verification
  SYS-1 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-2 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-3 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-4 (system-req, status: accepted) — missing: sys-integration-verification
  SYS-5 (system-req, status: accepted) — missing: sys-integration-verification
  → run `rivet validate --explain SR-1` to see which link type and source types satisfy a gap

Result: PASS (190 warnings)
Schemas: common@0.3.0 (embedded), dev@0.3.0 (embedded), research@0.1.0 (embedded), research-ext@0.1.0 (on-disk), dev-ext@0.1.0 (on-disk), safety-case@0.1.0 (embedded), aspice@0.2.0 (embedded)

Artifact stats

base head
Total artifacts 268 268
full stats — head
Artifact summary:
  academic-reference               24
  competitive-analysis             11
  design-decision                  22
  feature                          98
  market-finding                    7
  requirement                      21
  safety-context                    3
  safety-goal                       5
  safety-justification              4
  safety-solution                   6
  safety-strategy                   1
  stakeholder-req                   3
  sw-req                           13
  sw-verification                  13
  sys-verification                  5
  system-req                        5
  technology-evaluation            12
  verification                     15
  TOTAL                           268

Orphan artifacts (no links): 12
  CA-001
  CA-002
  CA-003
  CA-004
  CA-005
  CA-006
  CA-007
  CA-008
  CA-009
  CA-010
  CA-011
  FEAT-078

Diagnostics: 0 error(s), 190 warning(s), 14 info(s)

Diff (base → head)


0 added, 0 removed, 0 modified, 268 unchanged

AADL model — head

spar/scry.aadl: OK

Posted by the rivet-delta workflow. Informational only — does not gate the PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant