pytorch/pytorch-runners-routing
> How PyTorch CI jobs in .github/workflows/ resolve to a specific runner, AND which vs current OSDC ARC-on-EKS) and the dot-vs-dash prefix split (linux./lf./c. = old EC2; mt-/lf-/c-mt- = OSDC ARC). Covers the runner determinator (fleet prefixes mt- default / lf- / c-mt- plus the separate amd-do-), the test-infra #5132 experiment config (lf, amd-do), the now-unconditional EC2->ARC label translation via .github/arc.yaml and map_ec2_to_arc.py (the meta_only_runners H100/B200 override and the onnx exclusion), build-vs-test runner decoupling, and the _runner-determinator.yml / _linux-build.yml / _linux-test.yml flow. Old-infra ownership spans test-infra/terraform-aws-github-runner, test-infra scale-config.yml / lf-scale-config.yml, and pytorch-gha-infra/runners; OSDC ownership lives in the osdc repo (clusters.yaml + modules/arc-runners*). Applies to ~/meta/pytorch. LOAD THIS SKILL for ANY question about PyTorch CI runners, runner labels, test matrices, runs-on values, runner prefixes (mt-, lf-, c-mt-, amd-do-, or legacy linux./lf./c.), WHICH infra a label runs on ("does lf.linux.2xlarge run on ARC or EC2?", "mt- vs lf- vs lf.?", "old ALI vs OSDC runners", "where is this runner defined?"), experiments (lf / amd-do), the determinator, ARC fleet routing, H100/B200/ A100 placement, or how a job gets assigned to a runner pool.
npx skills add https://github.com/pytorch/ci-infra --skill pytorch-runners-routing
Everything about how .github/workflows/*.yml jobs in pytorch/pytorch land on a
specific runner — label definition, which infrastructure serves it, fleet-prefix
resolution, the experiment system, and the EC2-to-ARC translation pipeline. Read this
BEFORE touching any runs-on: value, any test-matrix: entry, any runner_prefix:
interpolation, any check_experiments: / opt_out_experiments: input, arc.yaml, or
runner_determinator.py / map_ec2_to_arc.py.
For the legacy EC2 world in depth (the Lambda autoscaler, scale-config, dot prefixes,
who owns what), see references/old-ali-infra.md.
Two completely different infrastructures serve PyTorch CI. Which one a job lands on is
encoded in its runner label's prefix.
workflow_job webhook drives ascale-up Lambda that launches ONE ephemeral EC2 instance per job; the runner
self-registers with an exact-match label set from scale-config.yml, runs the job,
then is torn down. Owned by test-infra/terraform-aws-github-runner (the module),
test-infra:.github/scale-config.yml + lf-scale-config.yml (labels), and
pytorch-gha-infra/runners (Meta deploy). Being decommissioned; still serves
passthrough/identity labels. Depth in references/old-ali-infra.md.
runner as a POD on a pre-existing EKS nodepool — no per-job VM boot. Owned by the
osdc repo: clusters.yaml sets each cluster's runner_name_prefix, runner defs live
in modules/arc-runners*/defs/. Essentially every routed job lands here now.
The prefix tells you the infra. Dot/bare prefixes = old ALI EC2; dash prefixes =
OSDC ARC. The dot->dash shift *is* the ALI->OSDC migration (completed in pytorch/pytorch
PR #189219, which also removed the arc experiment and the old use-arc output).
| Prefix in runs-on | Style | Infrastructure | Operator / funding | Defined / managed in |
|---|---|---|---|---|
| mt- | dash | OSDC — ARC on EKS | Meta | osdc clusters.yaml meta-prod-aws-uw1/ue1/ue2; defs modules/arc-runners*/defs/. Determinator default (META_LABEL_PREFIX); also the error fallback. |
| c-mt- | dash | OSDC — ARC on EKS | Meta (staging/canary) | osdc meta-staging-aws-*; META_CANARY_LABEL_PREFIX, emitted only on the pytorch/pytorch-canary repo. |
| lf- | dash | OSDC — ARC on EKS | Linux Foundation | osdc clusters.yaml lf-prod-aws-ue1/ue2; LF_LABEL_PREFIX, emitted when the lf experiment is on. Same github.com/pytorch org — distinguished only by the scale-set-name prefix. |
| amd-do- | dash | Partner ROCm (AMD dedicated) | AMD | separate amd-do-label-type output; identity-mapped in arc.yaml. |
| linux. and other bare | bare | Old ALI — EC2 | Meta (gh-ci, AWS_PROFILE=fbossci) | test-infra:.github/scale-config.yml, deployed by pytorch-gha-infra. Legacy — determinator no longer emits it. map_ec2_to_arc.py translates ordinary bare linux.* -> mt-… (OSDC ARC); only arc.yaml identity/passthrough entries stay bare and land on literal old-infra/partner runners. |
| lf. | dot | Old ALI — EC2 | Linux Foundation (parallel deploy) | test-infra:.github/lf-scale-config.yml. Dead in the determinator. |
| c. / lf.c. | dot | Old ALI — EC2 canary | Meta / LF | test-infra generated canary-scale-config.yml / lf-canary-scale-config.yml (prefix-substituted from scale-config.yml by validate_scale_config.py --generate). |
| identity passthrough (linux.rocm.gpu.*, linux.idc.xpu, linux.client.xpu, linux.google.tpuv7x.1, linux.dgx.b200.8) | bare | Literal self-hosted (old-infra / partner HW) | arc.yaml identity entries — no prefix; land on a runner registering that exact string. |
For the Linux build/test path, the runs-on: value is assembled at runtime from:
linux.c7i.2xlarge, linux.aws.h100)— what a human writes in the test-matrix.
label-type output: mt- (default, MetaOSDC), lf- (LF OSDC, when the lf experiment is on), or c-mt- (canary repo).
amd-do- is exposed *separately* via amd-do-label-type and wired per-job.
arc.yaml translation via map_ec2_to_arc.py — runs unconditionally on everytest-matrix, rewriting each EC2 label to its ARC equivalent (or leaving identity-
passthrough labels bare), and forcing mt- for meta_only_runners. There is no
use-arc toggle anymore; ARC is the destination for everything the determinator routes.
Examples: linux.c7i.2xlarge -> mt-l-x86iavx512-8-64 (default) or
lf-l-x86iavx512-8-64 (lf on). linux.aws.h100 -> mt-l-x86iamx-22-225-h100
regardless of prefix (H100/B200 pinned to Meta via meta_only_runners, see
Meta-Only Override for H100/B200).
linux.dgx.b200.8 stays linux.dgx.b200.8 (identity passthrough — old runner until
OSDC has capacity).
| File | Purpose |
|------|---------|
| .github/workflows/_runner-determinator.yml | Reusable workflow. Caller passes check_experiments / opt_out_experiments. Emits label-type (the fleet prefix), amd-do-label-type, runner-config, runner-type, runner-label, ci-docker-hash. |
| .github/scripts/runner_determinator.py | The script. Fetches the rollout config from pytorch/test-infra#5132 (first comment), evaluates per-user opt-in/out + per-workflow allowlist + rollout %, and emits the fleet prefix. |
| .github/scripts/test_runner_determinator.py | Tests for the determinator. Run on PR changes to the script. |
| .github/arc.yaml | EC2 -> ARC label mapping (runner_mapping) AND meta_only_runners override list for H100/B200. |
| .github/scripts/map_ec2_to_arc.py + test_map_ec2_to_arc.py | Script that rewrites a test-matrix's runner: field from EC2 labels to ARC labels using arc.yaml. Invoked unconditionally from _linux-build.yml. |
| File | Role |
|------|------|
| .github/workflows/_linux-build.yml | Build job. runs-on: ${{ inputs.runner_prefix }}${{ startsWith(inputs.runner, 'l-') && inputs.runner || contains(inputs.runner, 'arm64') && 'l-arm64g4-16-62' || 'l-x86iavx512-8-64' }} — an inline ternary: the build lands on a fixed small ARC CPU (x86 AVX-512 8/64, or arm64 g4 16/62), unless inputs.runner is already an l--prefixed ARC label (then it passes through). Then it runs map_ec2_to_arc.py --prefix "${RUNNER_PREFIX}" over the test-matrix (via uv run so pyyaml is present even in build images that lack it) and re-emits it as the build job's test-matrix output. That translated matrix is what the test job consumes. |
| .github/workflows/_linux-test.yml | Test job. A single job — runs-on: ${{ matrix.runner }} from the already-translated test-matrix, always in a container with options: "--gpus all", always assuming ARC IAM role arn:aws:iam::308535385114:role/arc. There is no longer a test vs test-osdc branch and no use-arc gate; the ARC path is unconditional. (setup-linux is called with a hardcoded use-arc: true input — that is an action input meaning "we are on ARC", unrelated to the deleted determinator output.) |
Sibling workflows exist for other platforms (_mac-build.yml, _mac-test.yml,
_win-build.yml, _win-test.yml, _rocm-test.yml, _xpu-test.yml, _vllm-build.yml,
_vllm-benchmark.yml, _linux-test-stable-fa3.yml). Not all participate in the
determinator/ARC flow — Linux build + test are the primary path.
Every workflow that wants experiment-driven runner placement has a get-label-type
job at the top calling _runner-determinator.yml, then passes its outputs into the
build/test jobs:
jobs:
get-label-type:
name: get-label-type
uses: pytorch/pytorch/.github/workflows/_runner-determinator.yml@main
with:
triggering_actor: ${{ github.triggering_actor }}
issue_owner: ${{ github.event.pull_request.user.login || github.event.issue.user.login }}
curr_branch: ${{ github.head_ref || github.ref_name }}
curr_ref_type: ${{ github.ref_type }}
check_experiments: lf # non-default experiments to consider for THIS workflow (optional)
# opt_out_experiments: lf # experiments to explicitly skip for THIS workflow (optional)
some-build:
uses: ./.github/workflows/_linux-build.yml
needs: get-label-type
with:
runner_prefix: "${{ needs.get-label-type.outputs.label-type }}" # fleet prefix
ci-docker-hash: ${{ needs.get-label-type.outputs.ci-docker-hash }} # image tag suffix
runner: linux.c7i.2xlarge # base build label
test-matrix: |
{ include: [
{ config: "default", shard: 1, num_shards: 1, runner: "linux.aws.h100" },
]}
The default fleet prefix is mt- (Meta OSDC) — the determinator never emits an empty
prefix. The build translates the test-matrix on every run and re-emits it; the test job
consumes needs.<build>.outputs.test-matrix, so matrix.runner is already the
translated ARC label. There is no use-arc: input to forward.
The rollout config lives in the first comment of GitHub issue
pytorch/test-infra#5132. The script fetches it at runtime via the GitHub API.
The issue body has two ----separated sections:
@username,experiment[:percent],-experiment_to_opt_out.The issue number is overridable via the issue_number input on
_runner-determinator.yml (default "5132").
The live experiments are lf (switches the fleet to Linux Foundation OSDC) and
amd-do (routes ROCm mi350 tests to AMD's dedicated runners, exposed via the
separate amd-do-label-type output). Any other experiment name is still parsed, but if
it is neither lf nor amd-do the determinator logs "enabled but no longer affects the
runner label prefix; ignoring" and it changes nothing — there is no arc experiment
anymore.
experiments:
lf:
rollout_perc: 25
all_branches: false
default: true
Field-name caveat: the working field is rollout_perc (the Experiment NamedTuple
field). The docstring example inside runner_determinator.py writes rollout_percent —
a typo. parse_settings_from_text logs any unknown key as an "Unexpected setting" and
never applies it, so a config copied from that docstring gets rollout_perc = 0 (no
percentage rollout).
Per-experiment fields (Experiment NamedTuple in runner_determinator.py):
rollout_perc: float — % of workflows that get this experiment when no user opted in.all_branches: bool — if False, exception branches (main, nightly, release,landchecks) skip the experiment.
default: bool — if False, the experiment only runs when the caller passes it incheck_experiments.
workflows: str — comma-separated allowlist of github.workflow names. "ALL" orempty = every workflow. "-Name" prefix excludes that workflow even when "ALL" is
present. Exclusions win over inclusions.
After the --- separator: each line @user,experiment1,experiment2:N,-experiment3:
experiment:N -> per-user N% rollout (0-100).-experiment -> explicit opt-out.#@user,... -> user opts out of ALL experiments.opted-in requestors wins, so a conservative author % is respected).
| Input | Effect |
|-------|--------|
| check_experiments: lf | Only consider these experiments (overrides default: true for non-listed ones — they will NOT run unless listed). |
| check_experiments unset | Use each experiment's default: flag from the config (so lf is eligible by default). |
| opt_out_experiments: lf | Explicitly skip the named experiment regardless of default: / check_experiments. Higher priority than check_experiments. |
Apply the no-runner-experiments label on a PR -> the determinator adds lf to the
opt-outs, so the run stays on the default Meta fleet (mt-) rather than LF. See
OPT_OUT_LABEL in runner_determinator.py.
| Condition | label-type output | Fleet / infra | Example test label (linux.c7i.2xlarge) |
|---|---|---|---|
| Default (no lf) | mt- (META_LABEL_PREFIX) | Meta OSDC ARC | mt-l-x86iavx512-8-64 |
| lf experiment on | lf- (LF_LABEL_PREFIX) | LF OSDC ARC | lf-l-x86iavx512-8-64 |
| Canary repo (pytorch/pytorch-canary), no lf | c-mt- (META_CANARY_LABEL_PREFIX) | Meta OSDC ARC (staging) | c-mt-l-x86iavx512-8-64 |
| Determinator error / timeout | mt- (fallback) | Meta OSDC ARC | — |
| PR has no-runner-experiments | mt- (opts out of lf) | Meta OSDC ARC | — |
Notes:
"" (bare/EC2) or lf. (dotted). Those areold-ALI prefixes — see references/old-ali-infra.md.
lf takes precedence over canary: on the canary repo with lf on, you get lf-,not c-mt-.
amd-do is exposed via its own amd-do-label-type output ("amd-do-" when enabled,else ""), NOT folded into label-type. Consumers wire it per-job.
The translation happens in the build job, unconditionally, so the test job receives
ARC fleet labels instead of EC2 labels:
_linux-build.yml runs map_ec2_to_arc.py --prefix "${RUNNER_PREFIX}" "${FILTERED_TEST_MATRIX}".runner_mapping and meta_only_runners from .github/arc.yaml.include[].runner: strips the prefix, looks the bare label up inrunner_mapping, then re-prepends the prefix (with the passthrough and meta-only
rules below).
config is in excluded_configs (currently {"onnx"} — seeCommon Failure Modes).
test-matrix output.needs.<build>.outputs.test-matrix, so matrix.runner is thealready-translated ARC label.
arc.yaml — The Mapping Tablerunner_mapping:
linux.c7i.2xlarge: l-x86iavx512-8-64
linux.12xlarge.memory: l-x86iavx512-48-384
linux.aws.h100: l-x86iamx-22-225-h100
linux.aws.h100.4: l-x86iamx-88-900-h100-4
linux.aws.h100.8: l-bx86iamx-176-1800-h100-8
linux.dgx.b200: l-x86iamx-22-225-b200
# ... CPU, A100, H100, B200, A10G, T4, L4, ARM64
linux.dgx.b200.8: linux.dgx.b200.8 # passthrough — identity mapping
linux.rocm.gpu.2: linux.rocm.gpu.2 # passthrough
linux.idc.xpu: linux.idc.xpu # passthrough
Passthrough rule (identity mapping in the table): mapped == clean means the runner
is not OSDC-managed (ROCm, XPU, TPU, and linux.dgx.b200.8 until OSDC has 8-GPU B200
capacity). The script keeps the original label WITHOUT prefixing — so it lands on the
existing self-hosted / old-infra runner.
ARC label naming convention is documented as a comment at the top of arc.yaml:
{os}-[b]{arch}{vendor}{features}-{vcpu}-{memory}[-{gpu_type}[-{gpu_count}]]
Examples: l-x86iavx512-8-64 (Linux, x86, Intel AVX-512, 8 vCPU, 64 GiB),
l-bx86iamx-176-1800-h100-8 (Linux, bare-metal, x86, Intel AMX, 176 vCPU, 1800 GiB,
8x H100).
H100 and B200 hardware exists only on the Meta OSDC fleet — LF and AWS EC2 don't carry
those machines. Routing an H100/B200 job to lf-l-...-h100 would queue forever.
The fix: the meta_only_runners list in .github/arc.yaml:
meta_only_runners:
- linux.aws.h100
- linux.aws.h100.4
- linux.aws.h100.8
- linux.dgx.b200
In map_ec2_to_arc.py, the per-entry loop has an early branch for any label in this
set: entry["runner"] = "mt-" + mapped (forces Meta OSDC, overriding whatever
--prefix was passed). This decouples the H100/B200 test runner from the build runner's
fleet — the build CPU is free to land wherever the experiment routes it, but the GPU
test job is always pinned to Meta OSDC.
Important: linux.dgx.b200.8 (8-GPU B200, used by b200-distributed.yml and
b200-symm-mem.yml) is an identity passthrough in runner_mapping and is NOT in
meta_only_runners — OSDC has 8-GPU B200 runners but not enough capacity yet, so tests
stay on the existing linux.dgx.b200.8 runner while the OSDC build runs. When OSDC gains
capacity, replace the identity mapping with a real ARC label AND add the EC2 label to
meta_only_runners.
A single _runner-determinator.yml call typically feeds the build job's runner_prefix
(and its ci-docker-hash); the test runners come from the translated test-matrix. There
are four patterns in the wild:
runner_prefix: "${{ ...label-type }}". Build CPU andthe (translatable) test runners travel together through the experiment.
mt- (runner_prefix: "mt-") — pins that build job to Meta OSDC,bypassing the determinator's fleet choice. Widely used for B200 builds, all ROCm
(rocm-*, periodic-rocm-*), XPU (xpu.yml), and several inductor perf builds.
opt_out_experiments: lf) — removes lf fromconsideration for that workflow. Used across the inductor family
(inductor.yml, inductor-unittest.yml, inductor-periodic.yml,
inductor-nightly.yml, inductor-perf-test-*), dynamo-unittest.yml, etc.
meta_only_runners — targets a specific runner *label* (H100/B200) rather than awhole workflow. The cleanest tool when "the build can land anywhere but this specific
test runner must always be Meta OSDC".
Workflows touching high-end NVIDIA GPUs (H100, B200, A100). check_experiments /
opt_out_experiments are re-derived from live source. (none) = the get-label-type
job passes no experiment inputs, so defaults apply (lf eligible unless opted out).
| File | GPU test runner(s) | check_experiments | opt_out_experiments | Notes |
|------|--------------------|---------------------|-----------------------|-------|
| test-h100.yml | linux.aws.h100 | lf | — | H100 forced mt- via meta_only_runners |
| h100-cutlass-backend.yml | linux.aws.h100 | lf | — | |
| h100-distributed.yml | linux.aws.h100.8 | lf | — | |
| h100-symm-mem.yml | linux.aws.h100.4 | lf | — | |
| test-b200.yml | linux.dgx.b200 | lf | — | B200 forced mt- via meta_only_runners |
| b200-distributed.yml | linux.dgx.b200.8 | (none) | — | build pinned runner_prefix: "mt-"; get-label-type used only for ci-docker-hash; b200.8 test = identity passthrough -> old bare runner |
| b200-symm-mem.yml | linux.dgx.b200.8 | (none) | — | same as b200-distributed.yml |
| inductor-perf-test-nightly-h100.yml | linux.aws.h100 | (none) | lf | |
| inductor-perf-test-b200.yml | linux.dgx.b200 | (none) | lf | build pinned runner_prefix: "mt-" |
| inductor-pallas.yml | linux.aws.h100 | (none) | — | |
| inductor-periodic.yml | linux.aws.h100 + many g5/a100 | amd-do | lf | |
| attention_op_microbenchmark.yml | linux.aws.a100, linux.aws.h100, linux.dgx.b200 | lf | — | B200 build path pins runner_prefix: "mt-" |
| operator_microbenchmark.yml | linux.aws.h100, linux.aws.a100, linux.dgx.b200 | lf | — | B200 build path pins runner_prefix: "mt-" |
| operator_microbenchmark_compare.yml | conditional H100/A100/B200 via ${{ inputs.gpu }} | (none) | — | B200 path pins runner_prefix: "mt-" |
| vllm-benchmark.yml | external matrix gen | NO get-label-type job | — | does not participate in the determinator |
l-x86i* / l-arm64g* ARC families)linux.c7i.2xlarge, linux.4xlarge, linux.12xlarge, linux.12xlarge.memory,
linux.r7i.4xlarge, linux.24xlarge.memory, linux.24xl.spr-metal, *.amx, *.avx2,
*.amd. The build job itself always lands on a fixed small ARC CPU (see
_linux-build.yml); these labels matter for the *test* matrix.
| Label | GPU |
|-------|-----|
| linux.g4dn.4xlarge.nvidia.gpu, linux.g4dn.12xlarge.nvidia.gpu, linux.g4dn.metal.nvidia.gpu | T4 |
| linux.g5.4xlarge.nvidia.gpu, linux.g5.12xlarge.nvidia.gpu, linux.g5.48xlarge.nvidia.gpu | A10G |
| linux.g6.4xlarge.experimental.nvidia.gpu, linux.g6.12xlarge.nvidia.gpu | L4 |
| linux.aws.a100 | A100 (p4de) |
| linux.aws.h100, linux.aws.h100.4, linux.aws.h100.8 | H100 (p5) — Meta-only |
| linux.dgx.b200 | B200 (p6) — Meta-only |
| linux.dgx.b200.8 | B200 8-GPU — identity passthrough (old runner) |
linux.arm64.2xlarge, linux.arm64.m7g.4xlarge, linux.arm64.m8g.4xlarge,
linux.arm64.r7g.12xlarge.memory, linux.arm64.m7g.metal, linux.arm64.m8g.metal-24xl.
linux.rocm.gpu.2, linux.rocm.gpu.mi210.1/.2, linux.rocm.gpu.gfx942.1/.4,
linux.rocm.gpu.gfx950.1/.2, linux.rocm.gpu.gfx1100, linux.idc.xpu,
linux.client.xpu, linux.google.tpuv7x.1. The amd-do experiment routes mi350 tests
through the amd-do--prefixed identity entries (amd-do-linux.rocm.gpu.gfx950.1/.2).
_runner-determinator.yml Outputs (Full Reference)| Output | What it is | Typical consumer |
|--------|-----------|-----------------|
| label-type | The fleet prefix ("mt-", "c-mt-", "lf-") | runner_prefix: input on _linux-build.yml |
| amd-do-label-type | "amd-do-" if the amd-do experiment is enabled, else "" | Per-job AMD pinning |
| ci-docker-hash | git rev-parse HEAD:.ci/docker (the tree hash, from PR head) | ci-docker-hash: input on _linux-build.yml for the image tag |
| runner-config | Normalized runner config (m7g / m8g) | ARM64 metal runner selection |
| runner-type | Runner suffix (metal / metal-24xl) | ARM64 metal runner selection |
| runner-label | Fully qualified ARM label linux.arm64.<config>.<type> | Direct runs-on: on some ARM workflows |
There is no use-arc output (removed with the arc experiment in PR #189219).
Add opt_out_experiments: lf to the _runner-determinator.yml caller. The prefix will
never be lf- — it stays mt-. Examples: the whole inductor family
(inductor.yml, inductor-unittest.yml, inductor-periodic.yml, inductor-nightly.yml,
inductor-perf-test-*), dynamo-unittest.yml.
Add the EC2 label to meta_only_runners in .github/arc.yaml. No workflow changes
needed. Already done for linux.aws.h100, linux.aws.h100.4, linux.aws.h100.8,
linux.dgx.b200.
Hardcode runner_prefix: "mt-" on the build job. Skips the fleet experiment for that
job. Widely used for B200 builds, all ROCm, XPU, and some inductor perf builds.
Trace through:
get-label-type job and the build/test jobs.check_experiments / opt_out_experiments.mt- unless lf is enabled (lf-) or it's the canaryrepo (c-mt-).
map_ec2_to_arc.py (it runs on EVERY matrix): EC2 label -> ARC label with theprefix; identity-passthrough labels stay bare (old infra); meta_only_runners force
mt-.
references/old-ali-infra.md).Add the EC2 -> ARC mapping to runner_mapping in .github/arc.yaml. If it's a
passthrough (not OSDC-managed), add an identity mapping (linux.foo: linux.foo).
Otherwise map_ec2_to_arc.py errors with error: no ARC runner found for '<label>' —
the lookup is strict, there is no implicit passthrough. If it's a new H100/B200 variant,
also add the EC2 label to meta_only_runners.
Define it in the test-infra #5132 issue body. No code change to runner_determinator.py
is needed UNLESS the experiment must change the label prefix — the script only special-
cases lf (sets the fleet) and amd-do (exposed via amd-do-label-type). Any other
experiment is parsed but "no longer affects the runner label prefix" and does nothing to
routing. Workflows opt in via check_experiments: <name>.
| Symptom | Likely cause |
|---------|-------------|
| Job stuck queued on lf-l-...-h100 or lf-l-...-b200 | H100/B200 not in meta_only_runners. Add it. |
| map_ec2_to_arc.py error error: no ARC runner found for '<label>' | Missing entry in runner_mapping. Add a mapping (or an identity passthrough if not OSDC-managed). |
| GPU test lands on a bare/old label instead of mt-l-... | The bare label is an identity passthrough in runner_mapping (e.g. linux.dgx.b200.8) — intentional until OSDC capacity lands. To move it to OSDC, replace the identity mapping and add a meta_only_runners entry. |
| Job lands on the default Meta fleet despite expecting lf- | User opted out via #@user in #5132, PR has no-runner-experiments (opts out of lf), lf is below rollout %, or the workflow sets opt_out_experiments: lf. Set the user to 100% opt-in to debug. |
| New runner added to a test-matrix breaks the build's map step | Missing from arc.yaml. The per-entry lookup is strict — no implicit passthrough for unknown labels. |
| Onnx tests silently dropped on the ARC path | Intentional. map_ec2_to_arc.py excludes config: onnx because onnxruntime's hardware_concurrency() sees all host CPUs on ARC k8s instead of the container cpuset. See the comment in the script. |
| Determinator script fails or times out | The wrapper falls back to Meta runners (mt-) + no experiments. Look in the get-label-type step logs for the actual error. |
| check_experiments typo / unknown experiment name | Silently ignored. Check spelling against the keys in the #5132 settings YAML (lf, amd-do). |
| A stale determinator checked out from a PR emits lf. | _runner-determinator.yml checks the script out from the merge commit (base), not PR head, precisely so a stale prefix like lf. can't reach main's build job (which has no EC2 fallback). See the checkout comment citing #189113/#189171. |
{os}-[b]{arch}{vendor}{features}-{vcpu}-{memory}[-{gpu_type}[-{gpu_count}]] scheme in
arc.yaml. The convention is reviewed across CI infra.
meta_only_runners without first confirming it has arunner_mapping entry. The script will error.
l-x86iamx-22-225-h100) in a workflow'stest-matrix runner: field. Use the EC2 label and let map_ec2_to_arc.py translate
it — the script only recognizes EC2/bare labels as input.
lf- / mt- prefix directly to runner_mapping entry values. Theprefix is added separately by the script.
"") or dotted (lf.) prefix — main'sbuild/test path has no EC2 fallback, so an old-style prefix breaks routing (see
.ci/docker/ to change runner behavior — that's a Docker image rebuildtrigger, unrelated to runner routing.
pytorch/test-infra#5132 — first comment is the live rollout config. Edit there toroll experiments, not in code.
references/old-ali-infra.md — the legacy Lambda-autoscaled EC2 world: scale-config,dot prefixes, ownership, and how to recognize an old-ALI job today.
arc experiment and the use-arc output; completed thedot->dash (ALI->OSDC) migration. Read when confused why old docs mention use-arc.
docs/runner_naming_convention.md (in ~/meta/ci-infra/osdc/) — source of truth forthe ARC label naming scheme. The osdc-runners-nodepools skill covers the runner
definition side (what runners exist, what hardware they map to).
~/meta/ci-infra/osdc/modules/arc-runners*/defs/*.yaml — the actual ARC runner scaleset definitions. Each runner.name there is what appears as the mapping *target* in
arc.yaml; runnerScaleSetName = {prefix}{runner.name} is the literal runs-on.
# See the current test-infra #5132 config
gh issue view 5132 --repo pytorch/test-infra --json comments --jq '.comments[0].body'
# Run the determinator locally (read-only, hits the GitHub API)
cd ~/meta/pytorch
python .github/scripts/runner_determinator.py --help
# Run the ARC mapping for a test-matrix (matches the _linux-build.yml uv invocation)
cd ~/meta/pytorch/.github/scripts
uv run --no-project --with pyyaml==6.0.2 \
python map_ec2_to_arc.py --prefix "lf-" '{ include: [ { config: "x", runner: "linux.aws.h100" } ] }'
# Expected: runner becomes "mt-l-x86iamx-22-225-h100" (meta_only override forces mt-)
# Run the test suites
python .github/scripts/test_runner_determinator.py
python .github/scripts/test_map_ec2_to_arc.py
Take pytorch/pytorch-runners-routing from the repository into ~/.claude/skills for personal
use, or into .claude/skills inside a project.
The agent identifies a skill by the name field in its header. Two skills with the
same name cannot sit side by side — one of them will be ignored.