infra-standards/skills/self-hosted-runners/SKILL.md
Use when editing GitHub Actions workflow files (.github/workflows/*.yml) in JacobPEvans repos. Documents when to target self-hosted RunsOn runners vs GitHub-hosted runners, the v3 label catalog used across the org, the required github.run_id segment, and the GitHub App allowlist prereq.
npx skillsauth add jacobpevans/claude-code-plugins self-hosted-runnersInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
JacobPEvans repos use self-hosted RunsOn runners deployed by
terraform-runs-on for
Linux GitHub Actions jobs. The control plane has a fixed monthly cost
whether or not jobs run (App Runner + CloudWatch — see
terraform-runs-on/README.md
for the current estimate). Workflows that stay on ubuntu-latest waste
GitHub Actions minutes that don't need to be spent. Migrate any Linux job
in the org that isn't covered by the GitHub-hosted rows in the decision
table below.
| Workload | Decision |
| --- | --- |
| Linux job (lint, validate, build, test) | RunsOn — almost always |
| Job that needs nix flake check --all-systems | RunsOn with more RAM (see catalog) |
| Job that runs on macos-latest | GitHub-hosted — RunsOn EC2 Mac has a 24-hour minimum allocation, costs more than macos-latest for short jobs |
| Job that runs on windows-latest | RunsOn supports Windows; treat case-by-case |
| Job generated by gh-aw compile (*.lock.yml) | GitHub-hosted — lock file is regenerated; runner label must flow through the .md companion (gh-aw doesn't expose this yet) |
| Job with disabled schedule: (manual dispatch only, rarely runs) | GitHub-hosted — migration saves nothing |
| Job in a repo that hasn't been added to the RunsOn GitHub App allowlist | GitHub-hosted — install the app first |
Use the single-string format. The leading runs-on=${{ github.run_id }}
segment is required so the RunsOn control plane can correlate the
GitHub Actions workflow_job webhook back to the originating run.
Omitting it makes the job hang in queued forever.
| Workload | Label string |
| --- | --- |
| Standard step (lint, validate, small build) | runs-on=${{ github.run_id }}/runner=2cpu-linux-x64 |
| Nix flake check (Linux only) | runs-on=${{ github.run_id }}/cpu=4/ram=16/family=m7+c7/extras=s3-cache |
| Build with large dependency cache | runs-on=${{ github.run_id }}/cpu=4/ram=16/volume=80gb:gp3:500mbs:4000iops/extras=s3-cache |
| Heavy CPU (terraform plan over many modules) | runs-on=${{ github.run_id }}/cpu=8/ram=32/family=c7a |
| GPU (Hugging Face, MLX cross-eval) | runs-on=${{ github.run_id }}/family=g4dn.xlarge/image=ubuntu22-gpu-x64 |
jobs:
validate:
runs-on: "runs-on=${{ github.run_id }}/runner=2cpu-linux-x64"
steps:
- uses: actions/checkout@v6
- run: ...
For reusable workflows in JacobPEvans/.github, callers pass the label
through the runner_label input (default ubuntu-latest):
jobs:
markdown-lint:
uses: JacobPEvans/.github/.github/workflows/_markdown-lint.yml@main
with:
runner_label: "runs-on=${{ github.run_id }}/runner=2cpu-linux-x64"
Each reusable workflow's runs-on: line then expands the input:
jobs:
lint:
runs-on: ${{ inputs.runner_label }}
This keeps ubuntu-latest as the safe default for consumers that haven't
opted in.
terraform-runs-on/main).Merge Gate). Watch one run end-to-end
before migrating the rest.In the GitHub Actions UI, expand the Set up runner group on any step.
A RunsOn run prints:
RUNS_ON_VERSION: v3.x.x
RUNS_ON_INSTANCE_ID: i-...
RUNS_ON_INSTANCE_TYPE: m8i.large
RUNS_ON_INSTANCE_LIFECYCLE: spot
If those variables are missing despite the runs-on=... label, the job
didn't land on RunsOn. GitHub does not silently fall back to github-hosted
when a custom label is unmatched — the job sits in queued state waiting
for a runner that never picks it up. Most common causes: the repo isn't in
the RunsOn GitHub App allowlist, the ${{ github.run_id }} segment is
missing from the label so the control plane can't correlate the
workflow_job webhook, or AWS spot capacity for the requested family is
briefly exhausted (RunsOn v3's spot circuit breaker handles this but a
queue stall can still happen during the fallback). The _ci-gate.yml
watchdog in JacobPEvans/.github cancels any job stuck in queued after
queue_timeout_minutes so the merge gate isn't blocked indefinitely.
Every RunsOn-launched EC2 instance is tagged with runs-on=.... AWS Cost
Explorer can be filtered by that tag group to attribute spend per repo,
workflow, and job. No per-workflow setup is needed — the tag is applied
by the RunsOn control plane.
ci-cd-policy rule (auto-loaded org rule) — billing/runner policytesting
Check PR merge readiness, sync local repo, cleanup stale worktrees; optional cross-repo sweep and stale-branch prune modes
tools
Local rebase-merge workflow for pull requests with signed commits
tools
Canonical reference for all gh CLI command shapes used by skills in this plugin. Defines the placeholder convention, allowed --json fields, GraphQL fallback rules, -f/-F/--raw-field flag semantics, the PR-readiness gate, code-scanning alert query, review-thread fetch/count/resolve mutations, and heredoc bodies. Prevents Unknown JSON field errors and divergent query shapes.
tools
Analyze current Claude Code session token usage via Splunk. Shows per-model, per-tool, and subagent token breakdown with cache efficiency metrics.