Conversation
MEASURED on the last merge to main (aa48860, 76 jobs): 630 job-minutes, 59 minutes wall-clock. On the two PRs open while writing this, 78 checks were queued at once and the `thread` sanitizer sat QUEUED for over two hours with no runner assigned — while the two self-hosted sanitizer jobs beside it in the same workflow were picked up in three minutes (pulseengine-ci-01-6 and -10, running concurrently). So the gate is not slow. It is waiting behind GitHub's hosted pool for work our own machine is idle enough to take. The pool is real: runner instances -5, -6, -7, -9, -10, -11 and -12 have all taken jobs recently. This moves the CHEAP REQUIRED GATES, which is where the leverage is. Of the 22 contexts main requires, 19 do not need a container and could move; the nine here need nothing but `actions/checkout` plus cargo or python, both already proven on this runner by fuzz-smoke and sanitizers: kill-criteria data-overlap, fixture-freshness, object-freshness, graph-env, required-contexts, wcet-evt, wcet-sidecar, proof-completeness, renode-targets, retry-loops, zephyr-fork-pin (11 jobs) rivet-v-closure guard, v-closure (2 jobs) gust-targets gust-targets (1 job) Together they are ~9 job-minutes. That is the point: they are trivial work that has been sitting in a queue behind 34 Zephyr matrix jobs, so a merge waits hours on gates that run in seconds. NOT MOVED, and each for a stated reason rather than by omission: * `mpu-enforcement` needs `sudo apt-get install qemu-system-arm`, and the self-hosted containers have no passwordless sudo. Same reason the `thread` sanitizer is deliberately hosted (TSan's vm.mmap_rnd_bits sysctl). It stays on ubuntu-24.04 until qemu-system-arm is in the runner image. * `Rust Coverage` and the cross-arch seam gate likewise need apt (curl, binutils-arm-none-eabi). * Everything with a `container:` — 17 jobs including ALL of Zephyr and LLVM-LTO — is blocked on the podman work on pulseengine-ci-01 (graphroot volume + subuid/newuidmap). That is where 67% of the compute is: Zephyr C Coverage alone is 39 minutes and the 34 qemu_cortex_m3 jobs are 360 job-minutes between them. Moving those is what would actually drain the queue; moving these is what stops merges waiting on it meanwhile. Every moved job gains `timeout-minutes: 30` where it had none, so if the self-hosted pool is ever down these fail loudly instead of hanging a required context forever — the failure mode this change introduces, made visible rather than left implicit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011QG86sovTbfnPNY9SfhSmo
|
[fathom (gale) — it works, with an A/B inside a single run, and one honest wrinkle]
The last row is the control, and it is in the same run as the others: the one job I deliberately did not move — because it needs The gates take twelve seconds. That is the whole finding. Work measured in seconds was waiting behind a queue measured in hours. The wrinkle, because it changes the next askOur own pool is not instant either: ~11 minutes of queue before pickup. Only four instances took work in this run — So this PR converts hours of hosted queue into minutes of our own queue, which is the win, but the residual 11 minutes is pool width and not something the repo can fix. Two levers, both on the host:
I would rather state the 11 minutes than quietly present the 12-second run time as the result. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
`zephyr-fork-pin` failed on the self-hosted runner with exit 127,
`gh: command not found` — the rust-cpu image has no GitHub CLI. Ordinary,
loud, fixed by moving it back to a hosted runner until gale#419 puts `gh`
in the image.
`required-contexts` is the one worth reading twice. It also shells out to
`gh api`, and it **passed** on the self-hosted runner — because it
DEGRADES rather than fails when branch protection cannot be read:
if gh api ".../branches/main/protection" > /tmp/protection.json; then
readable=true # checks the committed list against what main REQUIRES
else
readable=false # checks only the committed list against the workflows
fi
With no `gh` binary the else branch runs, the list-vs-protection drift
check never happens, and a REQUIRED context reports success having done
half of what it claims. Green, for a reason that does not hold — in a job
whose entire purpose is "every required context can actually report".
The degradation itself is right, and deliberate: the comment above it says
the run should say so "instead of passing as though it had checked", and it
does print the reason. What it does not do is distinguish A TOKEN THAT MAY
NOT READ from AN IMAGE THAT HAS NO BINARY. The first is an expected
permission boundary worth degrading for; the second is a broken
environment, and degrading for it silently narrows a gate.
So both are handled:
* the job moves back to ubuntu-24.04, so the FULL check runs today, and
* a `command -v gh` guard now fails loudly before the branch, so the
missing-binary case can never again take the permission path — on any
runner, including a hosted one that changes under us.
That guard is the part that outlives gale#419. When `gh` lands in the
image both jobs move back, and if it ever disappears again the gate says so
instead of quietly checking less.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011QG86sovTbfnPNY9SfhSmo
The gate is not slow. It is queued behind GitHub's hosted pool for work our own machine is idle enough to take.
Measured, not assumed
aa48860): 76 jobs, 630 job-minutes, 59 min wall-clock.threadsanitizer sat QUEUED for over two hours with no runner assigned — while the two self-hosted sanitizer jobs in the same workflow were picked up in three minutes (pulseengine-ci-01-6and-10, running concurrently).-5, -6, -7, -9, -10, -11, -12have all taken jobs recently. Today 1 of 58 job definitions targets it.What moves here, and why these
The cheap required gates — that is where the leverage is. Of the 22 contexts
mainrequires, 19 need no container and could move; these nine need nothing butactions/checkoutplus cargo or python, both already proven on this runner byfuzz-smokeandsanitizers:kill-criteriarivet-v-closuregust-targetsTogether they are ~9 job-minutes. That is the point: trivial work sitting in a queue behind 34 Zephyr matrix jobs, so a merge waits hours on gates that run in seconds.
Not moved — each for a stated reason, not by omission
mpu-enforcementneedssudo apt-get install qemu-system-arm, and the self-hosted containers have no passwordless sudo. Same class as the deliberately-hostedthreadsanitizer (TSan'svm.mmap_rnd_bitssysctl). Stays onubuntu-24.04untilqemu-system-armis in the runner image.Rust Coverageand the cross-arch seam gate likewise need apt (curl,binutils-arm-none-eabi).container:— 17 jobs including all of Zephyr and LLVM-LTO — is blocked on the podman work onpulseengine-ci-01(graphroot volume + subuid/newuidmap). That is where 67% of the compute is:Zephyr C Coveragealone is 39 minutes, and the 34qemu_cortex_m3jobs are 360 job-minutes between them. Moving those is what would drain the queue; moving these is what stops merges waiting on it meanwhile.The failure mode this introduces, made visible
Every moved job gains
timeout-minutes: 30where it had none. If the self-hosted pool is ever down, these fail loudly instead of hanging a required context forever.runs-onhas no fallback, so that is the tradeoff being taken deliberately rather than discovered later.🤖 Generated with Claude Code
https://claude.ai/code/session_011QG86sovTbfnPNY9SfhSmo