feat(labelfilter): synthesize the pool's platform label so platform-keyed tasks scale the pool (RIG-1471) - #5
Open
rigel-mintaka wants to merge 1 commit into
Open
Conversation
…eyed tasks scale the pool (RIG-1471) `NewPoolFilter` models which pending tasks the elastic pool could run. It synthesized `repo="*"` and `org-id="*"` but never `platform` — a label every real agent self-reports at registration (`cmd/agent/core/agent.go`) and the model has no agent to ask. Once the runner-label taxonomy keys pool selectors on `platform` (e.g. `linux/arm64`), a pending task carrying `platform` is unsatisfiable in the model, so `calcAgents` skips it when counting eligible pending work. With `MIN_AGENTS=0` a cold pool would then **never** scale up for those tasks: they strand forever, with no error. Adds `config.PoolPlatform`, sourced from `WOODPECKER_POOL_PLATFORM` (`--pool-platform`), and synthesizes `platform=<value>` **before** `maps.Copy(labels, extra)` so an explicit `ExtraAgentLabels` entry still wins. `org-id` stays last and non-overridable. An empty value synthesizes no `platform` key at all, so an unconfigured deployment keeps today's behaviour instead of asserting a platform it cannot know. Co-authored-by: Matt Wilkinson <matt@rigel.build>
rigel-mintaka
force-pushed
the
runners/rig-1471-pool-platform-filter
branch
from
September 8, 2026 03:19
4ca110d to
4657931
Compare
rigel-mintaka
marked this pull request as ready for review
September 8, 2026 03:21
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.
Makes the autoscaler's modeled pool filter synthesize the Woodpecker
platformlabel, and makes the agent filter read each agent's self-reported one. First implementation slice of the runner label taxonomy (orion #2292,docs/designs/platform/ci-runners/runner-label-taxonomy.md, task T5). Must merge and publish an image before the orion cutover PR, which pins the digest and emits the new env var.Why
NewPoolFiltermodels what a future pool agent could run, andcalcAgentsuses that to count eligible pending work. It synthesizedrepo="*"andorg-id="*"but neverplatform— a label every real agent self-reports at registration.Once the taxonomy keys pool selectors on
platform, a pending task carrying it is unsatisfiable in the model, socalcAgentsskips it. WithMIN_AGENTS=0a cold pool then never scales up for that work: the tasks sit unscheduled forever, with no error anywhere.What changed
NewPoolFiltertakes the pool's platform and synthesizes it before the custom labels, so an explicitExtraAgentLabelsentry still overrides it.org-id="*"stays applied last and non-overridable. An empty value synthesizes no key at all, preserving today's behaviour exactly for an unconfigured deployment.PoolPlatform, CLI flagpool-platform, env varWOODPECKER_POOL_PLATFORM. These names are a cross-repo contract — orion's T4 emits exactly this env var.AgentFilternow models the agent's own reported platform (review finding, see below).WarnwhenPoolPlatformis unset, because the failure mode it guards is silent.Review round
The review agent returned request-changes with 0 high, 3 medium. All are addressed.
The one that mattered.
AgentFiltermodels a static agent fromCustomLabelsonly, but a real agent'splatformis not a custom label — it is a first-class API field,Agent.Platform(woodpecker-go/types.go:280), which this code ignored. Before this PR the gap was unreachable, because platform-keyed tasks were rejected by the pool filter and never got as far as the netting step. This PR makes them reachable, which activates it.Verified against the live fleet rather than argued: every one of the 20 connected agents reports a truthful
platform, and not one carriesplatformincustom_labels. So the netting step would have stopped crediting an idle static's free slots for exactly the work it is best suited to run.The cost is real and now pinned by a test: an idle arm64 builder with two free slots, two pending arm64 image builds, and the pool boots 2 Spot agents beside the idle machine. Correct answer is 0.
Test_calcAgents/an_idle_static_nets_out_platform-keyed_work_via_its_self-reported_platformasserts that, and reverting theAgentFilterfix reddens it withexpected: 0, actual: 2.Unlike the pool's value this one needs no configuration — the agent already told the server, so it is truthful by construction rather than operator-asserted.
Also fixed: two new
misspellviolations (US locale is pinned in.golangci.yaml), and an over-long table-case name whose rationale moved into a comment.Deliberately not fixed:
mndfires on thelen(extra)+3capacity hints.mainalready trips it at both those exact sites with+2, so this renumbers a pre-existing violation rather than adding a class of one. Fixing it here would be scope creep.Verification
go build ./...clean.go test ./... -count=1→ 0 failures.golangci-lint run→misspellcount 0; the remaining findings are all pre-existing (mnd,unparam, plus generated-mockgci/gofmt/staticcheck).Red-green by mutation, run independently rather than taken from the subagent's report — each mutation applied, observed, and reverted byte-identically:
TestNewPoolFilterPlatform/matching_platform_request_matches_when_PoolPlatform_is_setandTest_calcAgents/platform-keyed_pending_⇒_scales_when_PoolPlatform_matches.AgentFiltersynthesis reds both the newTestAgentFiltersubtest and the netting test above.The reviewer additionally mutated the label precedence, the empty-value guard, a
platform="*"-when-empty variant, and the config plumbing; all four reddened their intended tests.CI is red here for an unrelated pre-existing reason
Every pipeline in this fork —
pull_requestandpush— errors at config-lint before producing any workflow:It comes from
.woodpecker/publish-sealed.yaml, which this PR does not touch, and it hitsmainthe same way. The consequence worth flagging:lintandtestnever run, so this repo currently has no CI gate, which is how the misspell violations reached review in the first place. Filed as RIG-3521 with the server-config fix; not fixed here because it is server config, not repo config.The image publish is unaffected —
publish-image.ymlis a GitHub Actions workflow on a tag push or manual dispatch, and it last succeeded onmain. So the cutover's image dependency is intact. Gate evidence above is local, and stands in for the absent CI.Spec-impact: none — implements the frozen T5 contract in orion #2292 without changing it.
Ledger-impact: none — no design record on a ledgered surface is touched by this fork PR.