perf(ci): pin the light validation jobs to GitHub-hosted runners - #2237
perf(ci): pin the light validation jobs to GitHub-hosted runners#2237willgriffin wants to merge 1 commit into
Conversation
`affected-scope`, `lint`, and `test-packages-result` gain nothing from the metal fleet. None runs a Turbo task or `setup-environment`, so none can restore from the internal cache or the hosted shim, and the fleet's memory is irrelevant to all three. On the fleet they could instead wait out a netboot of up to 780s, or queue behind the heavy shards on an 8-slot pool. Hosted minutes are free and unmetered on this public repository, so pinning them to a plain `ubuntu-latest` costs nothing and returns three slots per validation pass to the work that actually needs them. Measured hosted on the full rehearsal (run 30969467329): 6s for the scope filter and 18s for lint. The aggregator does no checkout at all and is structurally identical to the already-hosted `required-ci`. The pinned jobs take a plain label rather than the CI_HOSTED_FALLBACK_ENABLED selector: a lever that can only ever resolve to ubuntu-latest reads as a choice when there is none, and these jobs need no fleet-outage escape hatch because they never depend on the fleet. The lever stays intact on every job that remains on metal, and its shared explanation moves to `build` — the first job in the file that still carries it. Timeouts come down with the jobs, per the existing rule that hosted ceilings may track observed runtime because hosted runners never enter the self-hosted queue: 10 minutes for the two that check out, 5 for the aggregator, matching `required-ci`. This also resolves the aggregation asymmetry CI.md recorded, where `test-packages-result` sat on metal while `required-ci` — the same shape of job — was hosted. Backing a required status argues for starting promptly, not for queueing. Deliberately not moved: test-core x3, test-packages x3, Coverage Gate, Build, and Typecheck, whose hosted duration under merge_group full mode has never been measured and must not move on assumption; and postgres-tests.yml, whose services container needs the dind lane. Refs #2236 Refs happyvertical/iac#1349
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
Pull request overview
This PR implements phase 0 of runner pinning for SMRT’s CI by moving “light” test-suite.yml jobs that do not benefit from the self-hosted fleet (no Turbo tasks, no setup-environment, no cache usage) onto GitHub-hosted runners, freeing self-hosted capacity for heavier shards while keeping the emergency hosted-fallback lever for the remaining fleet-bound jobs.
Changes:
- Pin
affected-scope,lint, andtest-packages-resulttoruns-on: ubuntu-latestand reduce their timeouts to hosted-appropriate ceilings. - Relocate the shared
CI_HOSTED_FALLBACK_ENABLEDexplanation block to the first job that still uses the lever (build) and explicitly document which jobs are intentionally outside it. - Update
.github/CI.mdto reflect the new steady-state runner split, timeout rationale, and phase-0 measurement table for post-merge tracking.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
.github/workflows/test-suite.yml |
Pins three lightweight jobs to hosted runners, adjusts timeouts, and moves the fallback-lever documentation to build. |
.github/CI.md |
Updates CI architecture docs to reflect the runner split, timeout guidance, and adds a Phase 0 measurement section. |
This PR's own validation measured the problem it fixesBecause
Both were created at This is a single observation, not a distribution, and it landed on a busy
|
|
Superseded by #2242, which carries this branch's commits cherry-picked verbatim (same authors, same messages) alongside the other two CI-lane fixes. Rationale: these three PRs each needed a full validation pass on the very lane they were unclogging, and #2217/#2237 both edit The merged result is the union of both workflow changes, verified job by job — the three light jobs are hosted per #2236, every heavy job keeps the The branch is preserved. Reopen if #2242 needs to be unwound. |
Phase 0 of happyvertical/iac#1349 for this repository: reserve the metal fleet
for the jobs that actually benefit from it.
What moves
Three
test-suite.ymljobs get a plainruns-on: ubuntu-latest:affected-scope(Detect Affected Validation Scope)dorny/paths-filter; no Turbo task, no installlint(Lint)npx; deliberately never runs the workspace installtest-packages-result(Test Packages)needs.*.resultand exitsrequired-ciis the same shape, 3 s)Measurements are from the full hosted rehearsal
(run 30969467329),
cold and cacheless — that run was on
pull_request_target, where the Turboshim is refused, so nothing restored.
None of the three invokes
setup-environment, so none can restore from theinternal Turbo cache or the hosted shim, and the fleet's memory is irrelevant
to all of them. On the fleet they could instead wait out a netboot of up to
780 s or queue behind the heavy shards on an 8-slot pool. smrt is public, so
hosted minutes are free and unmetered: the move costs nothing and returns
three slots per validation pass.
Mechanics
ever resolve to
ubuntu-latestreads as a choice when there is none, andthese jobs need no fleet-outage escape hatch because they never depend on the
fleet.
CI_HOSTED_FALLBACK_ENABLEDlever stays intact on all ten jobs thatremain on metal. Its shared explanation moved from
affected-scopetobuild, the first job in the file that still carries it.(
Required CI) and the two lifecycle rulesets are unaffected..github/actionlint.yamlneeds no change — actionlint does not validatelabels inside expressions.
ceilings may track observed runtime because hosted runners never enter the
self-hosted queue: 10 min for the two that check out, 5 for the aggregator
(matching
required-ci).This also resolves an asymmetry CI.md recorded as unfinished business:
test-packages-resultsat on metal whilerequired-ci— structurally thesame job — was hosted. Backing a required status argues for starting promptly,
not for queueing.
ONNX: a non-issue for this set
setup-environmentinstalls ONNX system deps viasudo apt-getwhenCI_ONNX_DEPS_READYis unset (the hosted case), costing 1-2 min per job. Thatis a real lever, but none of these three jobs invokes that composite action
at all, so there is no
install-deps: 'false'to apply here. Recorded so thenext hosted move of a job that does use it starts from the right place.
Deliberately not moved
test-core×3,test-packages×3, Coverage Gate, Build, Typecheck — thewarm internal Turbo cache and the larger machines matter there, and their
hosted duration under
merge_groupfullmode has never been measured.The epic tracks measuring that separately; they must not move on assumption.
postgres-tests.ymlstays onarc-happyvertical— itsservices:containerneeds the dind lane.
publish-dry-run.yml'spublish-dry-run-summary(8 s hosted) is alegitimate candidate, deferred: perf(ci): stop double-running coverage and packaging gates, drop video/voice isolation #2217 rewrites the
if:immediately aboveits
runs-on, and after that PR the job runs only in the merge group. Notedon perf(ci): pin the light validation jobs to GitHub-hosted runners #2236 to revisit rather than race perf(ci): stop double-running coverage and packaging gates, drop video/voice isolation #2217 for the same lines. This PR
touches no file perf(ci): stop double-running coverage and packaging gates, drop video/voice isolation #2217 touches at overlapping hunks.
When this first executes
on-pull-request.ymlusespull_request_target, so this PR's own checks runagainst main's YAML — with these jobs still on metal. The pinned selection
first executes in the merge queue. That is expected, not a gap; it is also why
a mistake here would eject from the queue rather than redden the PR.
Validation
actionlint .github/workflows/test-suite.yml— cleanyamllint .github/workflows/test-suite.yml— cleannode scripts/publish-workflow-policy.test.mjs— 1 pass, 0 failpnpm smrt dev:knowledge-check—✓ SMRT knowledge is fresh, 0 errors, 0warnings
ubuntu-latest, 10 leverexpressions remaining, job names diffed identical against
origin/maincodex reviewcould not run — the account is out of creditsuntil Aug 7. Covered by own review, the GitHub auto-reviewers, and CI.
Follow-up
.github/CI.mdcarries an empty before/after table under Phase 0 hostedpinning, to be filled after merge with the moved jobs' durations and — the
result that actually matters — whether the heavy shards' queue wait improved
from the freed slots. If it did not, the freed slots were not the binding
constraint, and the capacity oracle in phases 1-2 of the epic is the next
lever rather than a wider pinning.
Closes #2236
Refs happyvertical/iac#1349
{"schema":"hv-agent-run:v1","runtime":"claude","session":"524cd2c5-11c6-450e-b137-756d97181723","issue":"2236","policy_revision":"1.0.0","validation":["actionlint clean on .github/workflows/test-suite.yml","yamllint clean on .github/workflows/test-suite.yml","node scripts/publish-workflow-policy.test.mjs: 1 pass 0 fail","pnpm smrt dev:knowledge-check: SMRT knowledge is fresh, 0 errors 0 warnings","runner split verified: 3 plain ubuntu-latest, 10 CI_HOSTED_FALLBACK_ENABLED lever expressions remaining","job names diffed byte-identical against origin/main, so the required context Required CI is unaffected","repo confirmed public, so the free-hosted-minutes premise holds","hosted durations sourced from measured run 30969467329, not estimated","codex review unavailable (account out of credits until Aug 7); covered by own review, GitHub auto-reviewers, and CI"]}