ci(runner pools): one pool per Blacksmith Linux size, so a stalled size moves by declaration
Leo-ask: ask-973a915bea
Leo, 2026-10-28: "Where the f***ing are the generalized solution to all these problems that these incessant pins?"
475 runs-on lines on feat-cmux-next end in a literal single-size Blacksmith label (265 at 4 vCPU, 61 at macOS 26, 57 at 8 vCPU, 42 at 2 vCPU, 29 at 32 vCPU, 9 at 16 vCPU and others). A re-run cannot move a literal label, so a stalled size strands every job that names it. On 2026-10-28 that happened to blacksmith-4vcpu-ubuntu-2404 in nightly.yml.
This PR is step 1. It declares one pool per Blacksmith Linux size, plus macos_latest, in .github/runners.json pools:
| Pool | Members |
|---|---|
linux_2 | 2 vCPU, then 4 vCPU |
linux_8 | 8 vCPU, then 16 vCPU |
linux_16 | 16 vCPU, then 32 vCPU |
linux_32 | 32 vCPU, then 16 vCPU |
macos_latest | blacksmith-6vcpu-macos-latest |
runner_pools.py is unchanged. On its next pass it writes each new key into RUNNER_POOLS, and it moves a key off a member the probe or the janitor marks dead. The follow-up PRs on feat-cmux-next replace each literal last resort with fromJSON(vars.RUNNER_POOLS).<size pool>. A fork pull request keeps its literal fork branch, because a fork's run gets no repository variables. Those PRs land only after this one has merged and the variable carries the keys.
Changed:
.github/runners.jsonadds the five pools.docs/ci-runners.mdupdates the pool table and fixes thelinuxrow, which now lists 4 vCPU first.tests/test_ci_runner_pools.pychecks that each Linux size has its own pool with a member to fail over to.
Tests: tests/test_ci_runner_pools.py (42 OK), test_ci_runner_set, test_ci_queue_janitor, test_ci_simple_pool_picker and test_ci_starved_labels all pass.
Performance
| metric | before | after | delta | harness |
|---|---|---|---|---|
| Blacksmith minutes per day | unchanged | unchanged | 0 | declaration only; each pool's first member is the size jobs already name, so no job moves until a size stalls |
Agent session: agent session
š¤ Generated with Claude Code
![View with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v3-3x.png)
![Autofix with [code]smith](https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark-3x.png)
Need help on this PR? Tag @codesmith-bot with what you need.
Summary by cubic
Declares one pool per Blacksmith Linux size plus macos_latest in .github/runners.json, so a stalled size moves its jobs by declaration instead of stranding them on a literal label.
Hundreds of runs-on lines end in a literal single-size Blacksmith label; a re-run cannot move a literal label, so when a size stops starting jobs every job that names it is stuck. This adds linux_2, linux_4, linux_8, linux_16, linux_32, and macos_latest, each with a first (preferred) member and a fail-over member. macos_latest fails over to the macOS 26 image. linux_4 is ephemeral-only: it serves secret-bearing and write-token jobs that must not ride the shared linux pool. runner_pools.py is unchanged ā it writes the new keys into RUNNER_POOLS on its next pass, and follow-up PRs replace literal labels with fromJSON(vars.RUNNER_POOLS) lookups.
- Adds a test asserting each Linux size has its own pool with a fail-over member, and adjusts the dead-mark fixture so every pool holds a live member when
linux_4shares the 4 vCPU first slot. - The
docs/ci-runners.mdpool table update moves to the same follow-up change that starts reading the pools.
Summary by CodeRabbit
- New Features
- CI jobs can use Linux runner pools sized from 2 to 32 vCPUs, with alternate runner options available for each size.
- Added a macOS latest runner option with a macOS 26 fallback.
- Documentation
- Updated CI runner guidance to describe size-matched Linux pools and the available macOS options.