QA Use Cases — CI / ARC runners
Tool page: tools/github-actions-runners.md.
Post-migration state (2026-10-29, lab-monorepo#42 phase 3):
Actions Runner Controller scale sets on pro — an org-level
set named arc-runner-set (max 1) and one named mini-k8s
(max 2). As of 2026-11-01: lab-monorepo#102 closed; all ARC runners are on pro. The mini stack was removed, including its actions-runner-system namespace. Runner labels are unchanged. Two facts define the contract:
- Scale-set runners carry only the set name as a label —
no
self-hostedlabel — so lab workflows target a single label:runs-on: mini-k8s. The set kept themini-k8sname on pro deliberately; a rename topro-k8sis a tracked follow-up (source: phase-3 record, PR #65 — the label blocker the review caught). - GitHub-hosted runners “cannot reach the tailnet, the directory, or either cluster — for lab work they are not a fallback, they are a category error” (source: github-actions-runners.md).
Admin stories
CIR-A1 — Scale sets registered and listening [config/API]
- Persona: Platform admin.
- Steps: query the GitHub org’s Actions runner scale sets; read the ARC controller/listener pods on pro.
- Expected: both sets registered to the
rhelsorg; listeners healthy; the mini stack is removed and itsactions-runner-systemnamespace is absent (source: lab-monorepo#102, 2026-11-01). - Assert: GitHub API: scale-set list contains
{arc-runner-set, mini-k8s}; pro: listener podsRunning; mini:actions-runner-systemnamespaceNotFound; no summerwind CRDs, ClusterRoles, bindings or webhooks remain.
CIR-A2 — The label contract holds across workflows [CLI]
- Persona: Platform admin. Goal: no workflow can silently target a runner that does not exist.
- Steps: grep every
runs-onin the lab’s repos (local-k8s-gitops,lab-agentops,lab-docs-source,lab-monorepoworkflows). - Expected: every lab-touching job uses
runs-on: mini-k8s(single label) or thearc-runner-setname; the old two-label form[self-hosted, mini-k8s]appears nowhere — it can never match a scale-set runner. This defect bit for real: the docs publish workflow queued ~15 minutes on the old form (fixed in lab-docs-source PR #17) (source: phase-3 record; docs publish record 2026-10-29). - Assert: grep for
self-hosted, mini-k8sandself-hosted,mini-k8sacross workflow files →0matches; everyruns-onvalue ∈ the manifest set.
CIR-A3 — Auth is the GitHub App, escrowed in Vault [config/API]
- Persona: Platform admin.
- Steps: confirm the ARC GitHub App credential exists in Vault (metadata only) and the runner auth path references it; confirm the retired classic-PAT path is gone or explicitly in its recorded interim state.
- Expected: App credentials at the platform ARC path in Vault (they exist — verified during the Service Directory build); the burned classic PAT’s revocation is tracked on lab-monorepo#46, whose end state is App-only auth (source: service-directory.md; lab record — #46 reframe).
- Assert: Vault metadata read on the ARC App path →
200with the three recorded fields present (names only); the interim state, while it lasts, diffs loudly in the run report rather than passing silently.
CIR-A4 — Capacity ceilings are the recorded ones [config/API]
- Persona: Platform admin.
- Steps: read both scale sets’ max-runners from the GitOps values on pro.
- Expected:
arc-runner-setmax 1,mini-k8smax 2 — the sizing that fit pro’s request-floored node (source: phase-3 record). - Assert: rendered scale-set specs:
maxRunners == 1and== 2respectively; live GitHub-side counts agree.
User stories
CIR-U1 — Smoke workflow lands on a pro runner and goes green [config/API]
- Persona: A developer dispatching
arc-smoke-test. - Steps: dispatch the workflow; watch the run.
- Expected: the job is picked up by a pro scale-set runner (runner name carries the set prefix) and completes green. Proven at delivery: smoke green ×4 on pro runners (source: phase-3 proof record).
- Assert: run conclusion ==
success; the job’s runner name matches^(arc-runner-set|mini-k8s)-; runner host is pro’s cluster (pod namespacearc-runners).
CIR-U2 — A real pipeline runs end to end [config/API]
- Persona: A developer pushing to a pipeline repo.
- Steps: dispatch the service-health signed pipeline.
- Expected: build (buildah) → Harbor push → cosign sign → Trivy gate → digest artifact, all green on a pro runner — twice consecutively was the phase-3 proof bar (source: phase-3 record — runs green ×2).
- Assert: two consecutive runs: conclusion ==
successand the digest artifact exists on both.
CIR-U3 — A wrong label queues visibly, never silently (negative) [config/API]
- Persona: A developer who typos a label.
- Steps: dispatch a probe workflow with a nonexistent
runs-onlabel; observe for the queue window; cancel it. - Expected: the job stays Queued with no runner assigned — visible in the Actions UI/API the whole time, never dispatched to a wrong runner and never reported as lost. The book’s diagnosis order is the story’s other half: “a job stuck at Queued is almost always: the cap slot is busy, the labels drifted, or the controller is down — in that order” (source: github-actions-runners.md Part B).
- Assert: after the window: run status ==
queued,runner_nameempty; after cancel: status ==cancelled; CIR-A1 still green (the probe did not disturb the sets).
Gaps
- Retirement verified (2026-11-01): all consumer runs after mini removal used pro runners; no workflow labels changed (lab-monorepo#102).
- TBD: no documented queue-wait SLO (how long Queued is normal at cap); CIR-U3 asserts visibility, not a duration.
- Manual-only: none.