[Test] Add a ci-test-audit skill cataloging CI and test audit patterns (#40257)

This commit is contained in:
Liangsheng Yin
2026-09-18 16:35:49 -07:00
committed by GitHub
parent 21e6c98ccb
commit b876213548
2 changed files with 301 additions and 0 deletions
+40
View File
@@ -0,0 +1,40 @@
---
name: ci-test-audit
description: Audit the existing test tree and CI configuration for improvements, using a catalog of patterns previously applied in this repo. Use when asked to audit tests or CI, shrink CI time or cost, find redundant or misplaced tests, clean up a test group, or review whether a CI change follows established practice.
---
# CI / Test Audit
Audits what is already in `test/`, `.github/` and `scripts/ci/`. For writing a new test see
[write-sglang-test](../write-sglang-test/SKILL.md); for how the pipeline dispatches and
gates work see [ci-workflow-guide](../ci-workflow-guide/SKILL.md).
## How to use it
Read [action-items.md](action-items.md) first. It is a catalog of patterns that have
been applied to this repo, each with a way to spot it and example PRs. The catalog is the substance of this skill; everything below is only scaffolding.
The user says what to audit -- a test group, a stage, a workflow file, or a complaint
like "this suite is slow" -- and how thoroughly. Read that scope and decide which
patterns apply.
An audit is only useful if it is specific. "This suite could be trimmed" is not a
finding; "`TestFooLargePage` is a strict subset of `TestFooRetractLargePage`, drop it"
is. Every finding needs the file, the pattern it matches, and what to do.
## Reporting
Group findings by confidence in the claim, not by pattern id. Lead with the ones where
the evidence is in the file you just read; keep the speculative ones separate and say
what you would need to check. Skip anything you are not reasonably sure about -- a long
list of maybes costs more to triage than it saves.
Propose, do not apply. Deleting a test, moving a registration to another stage, and
changing a threshold are all decisions for the user. Land them as separate changes,
since a trim and a threshold change fail for different reasons.
## Keeping the catalog current
Example PRs age. When a pattern shows up in newer work, or a new pattern recurs, update
`action-items.md`: the pattern text should stay general, the examples are
replaceable evidence.
@@ -0,0 +1,261 @@
# CI / Test Action Items
Patterns that have been applied to this repo's tests and CI. Each entry is a general
rule plus a way to spot it in this repo; the PR numbers are evidence, not definitions,
and are expected to be replaced as newer examples appear.
Sections follow the order of questions an audit asks about a test: should it exist, is
it in the right place, does it test the right thing, is it stable. The remaining
sections are about the pipeline around the tests.
---
## A. Should this test exist in this form?
**A1. Every case answers "what future diff would turn this red?"; if the only answer is editing the test, delete it.**
The admission criteria in `.claude/rules/unit-test-admission.md` apply to existing cases as
much as new ones. Spot: a test for a code path or kernel that no longer exists;
assertions that only check a mock was called; a stress loop that cannot reproduce the
failure it claims to guard; a case whose every assertion is also made by another case in
the group. Not this pattern: the rule's own carve-outs -- an external-source literal, or
a completeness / negative-branch contract -- are bookkeeping, even when the code is a
one-liner; and a path that was renamed is not a path that no longer exists, so check
before declaring a test orphaned. Examples: #34464, #34667, #39013, #38881, #38259.
**A2. A test earns its cost by what it protects; a guard on a standalone, rarely touched, rarely used surface can be deleted outright.**
Tests exist to stop other people's changes from breaking code. That value scales with
how often the code changes, how many users hit it, and how entangled it is with other
paths. Spot: the module under test has had no non-test commits in months; it has few
importers; the feature sits behind a flag almost nobody enables or serves one model few
people run. The heavier the test (server launch, multi-GPU), the higher this bar.
Not this pattern: low churn alone. A stable, load-bearing path (cache eviction, PD
transfer) is where a regression hurts most; all three signals -- little churn, few
users, little entanglement -- must hold before deleting on this ground.
Examples: none applied on this ground yet; #37990 (cases for models nobody runs) and
#38011 (low-signal model tests demoted from the PR gate) are partial.
**A3. A variant whose configuration is a strict subset of another variant's is redundant.**
Spot: several classes in one file whose launch configs differ by a single flag or env
var. Holds when three things are true: the configs differ by one dimension; that
dimension is additive (it adds load or a knob position rather than gating a path or skipping
assertions); and the heavier variant's assertions cover the lighter one's. Watch for
variants that override a test method, and for looser expectations in the heavier variant
(a longer timeout or relaxed bound), which make the lighter one still worth keeping.
Not this pattern (and this one has the highest false-positive rate of the catalog):
sharing a helper or fixture, which does not make one variant a subset of another; a
variant whose flipped flag is itself the feature under test (e.g. an overlap-loading
toggle), which fails the additive condition; variants that drive different integration
layers of the same model (engine input format vs. server endpoint), so neither's
assertions cover the other's; or a lighter class that is skip-decorated and launches
nothing, so it costs nothing to keep and its skip is an A6 question instead.
Examples: #38093, #39544, #33745, #33586, #34070, #33763, #34464, #33752, #39013, #34882.
**A4. Classes that can run against one server configuration share the launch.**
Spot: several classes in a group each launching a server in `setUpClass` with the same
or request-equivalent arguments. Launch cost usually dominates the cases themselves, so
this saves more than the case count suggests. Not this pattern: a flag that changes
server state (attention backend, page size, speculative decoding on or off) needs its
own launch; and sharing couples the classes, so one crash takes the others down with it.
Examples: #33641, #33756, #33944, #36736, #38014, #37252.
**A5. An end-to-end matrix whose only varying dimension is one layer belongs in a layer-level unit test.**
Spot: a full server launch matrix that exists to select a kernel or backend. Not this
pattern: the one end-to-end smoke per backend that stays -- the unit test covers the
numerics, but backend selection can still change integration (graph capture, memory pool
layout). Examples: #33596, #33611.
**A6. Skipped, disabled, or unreachable registrations are dead coverage; each carries a reason and an expiry, or is deleted.**
Spot: `disabled=` registrations or `skipTest` calls with no reference to what would
un-skip them; skips older than the issue they were waiting on; registrations naming a
retired `runner_config`; registered files whose TestCase classes never execute. Not
this pattern: a hardware-conditional skip (`skipUnless` on compute capability), which is
a gate; or a `disabled=` whose linked issue is still open, which is doing its job.
Examples: #32324, #39368, #38585, #34377, #33772, #34779, #33654, #34070.
---
## B. Is it in the right place, at the right cost?
**B1. A test runs on the stage and runner its resources actually require.**
Spot: CPU-only tests registered to a GPU `runner_config`; multi-GPU registrations for a
test that launches one server. Not this pattern: a test that does not use a GPU but
imports a CUDA-only module or `sgl_kernel` at import time; check before moving it.
Examples: #34074, #33654, #33605, #34913, #35220.
**B2. Trigger frequency matches signal per unit of cost: per-commit for fast, high-signal checks; nightly for model-level accuracy and perf; weekly or a shadow cadence for slow-moving coverage.**
Spot: a multi-GPU accuracy suite registered per-commit whose failures have never been
PR-specific; a nightly job that has been green for weeks, occupies several GPUs per run,
and whose coverage a weekly run would hold just as well; a retired platform still
running daily. Demoting is not
deleting -- the nightly and weekly grids exist to hold this coverage at the right cost.
Examples: #38011, #37990, #33809, #36814, #37532, #34204, #37586, #31749.
**B3. Declared cost matches measured cost; shard counts and timeouts derive from it.**
Spot: an `est_time` off from the measured run by an order of magnitude (the partitioner
packs shards with it when live stats are missing); a shard count or job timeout that has
not moved while the suite's contents have. Not this pattern: small drift on a file that
already has live stats, which override `est_time`. Examples: #35407, #36242,
#38238, #32408, #37435, #37452, #39194, #37532.
---
## C. Does it test the right thing?
**C1. Fixtures go through the real production path; the oracle is written independently.**
The logic that builds a fixture should stay inside the tested surface, and the reference
value must not come from the implementation under test, or a shared bug makes both sides
agree. Spot: weights copied into parameters by hand instead of through the weight
loader; a config assembled without the override entry points; literals that duplicate
what the model or config already declares; a mock missing a field the code under test
reads; a hand-built fake of an internal structure that has been patched in several
fix-up PRs, which is the signal to construct it through the real constructor or a
shared helper. Not this pattern: a deliberately minimal fake that isolates one unit --
the signal is repeated patching, not the existence of a fake. The oracle is the one thing
that must stay independent; harnesses, fixtures and eval entry points are shared (C6).
Examples: #33615, #37339, #34100, #33509, #33179, #34746, #36424, #37148, #37182,
#38314, #38315, #38418, #39019, #39100, #39101.
**C2. The test, not a default or the environment, decides which path it exercises.**
Spot: a test that relies on defaults to select an execution mode, capture range or memory
fraction, so a default change silently moves what is covered; a job-level env var that
changes which strategy a class exercises, so the class tests the wrong thing on every run
of that lane; a filter or setting leaking from one suite into the next; cached process
state that an env pin cannot override. Not this pattern: a test that exists to check the
defaults work; pinning it would delete that coverage. Examples: #34146, #33776, #33847,
#38221, #33772, #34300, #39411.
**C3. The assertion matches the semantics under test.**
Spot: exit-code assertions on a process expected to abort; equality assertions where the
contract allows ties; an absolute tolerance on a value whose error scales with magnitude.
Examples: #34017, #37873, #32410, #34272, #35795.
**C4. Thresholds come from a measured distribution; derived metrics are gated on the primary one.**
Spot: round-number thresholds with no recorded basis; a derived quantity asserted without
the accuracy result it depends on. Not this pattern: a deliberately loose bound whose job
is "the model did not fall apart", which is a design choice; flag the ones with no margin
or that keep being adjusted. Examples: #36570, #34145, #31702, #31748, #38725, #36290.
**C5. Randomness is removed before a tolerance is loosened.**
Spot: a kernel test with unseeded random inputs whose bound was widened to make it pass;
a comparison that could be bit-exact but asserts within an epsilon. Seed the inputs, pin
the RNG, or make the guard exact. Not this pattern: a kernel that is itself
nondeterministic (atomic or split-K reduction order), where seeding does not help and a
tolerance is the correct assertion. Examples: #32126, #37343, #30026, #35787, #34356,
#34607.
**C6. Shared test infrastructure has one implementation.**
Spot: the same eval, mixin or fixture reimplemented locally in several files, each with
its own threshold or drift. Not this pattern: a reference implementation written
independently inside a test, which C1 requires; this is about harnesses, not oracles.
Examples: #34477, #36979, #39906.
---
## D. Is it stable?
**D1. Resources are reused only after they are actually released, not after release was requested.**
Spot: a launch that follows a kill without waiting for the device memory to come back; a
teardown that sends SIGKILL without a graceful shutdown first; a container sharing the
host IPC namespace so `/dev/shm` leaks across jobs. Examples: #39545, #37194, #31871,
#32829, #36605, #37485, #38244, #38638.
**D2. Each role in a test, and each concurrent run, has its own exclusive resources.**
Spot: two roles in one test sharing a device, NIC or port by default; concurrent runs
sharing a mount or namespace. Examples: #39584, #33044, #34755, #39611, #35500.
**D3. Side artifacts do not decide the correctness verdict.**
Spot: a run that fails because a profile, metrics upload or perf dump was missing or
unreadable; profiling enabled in a run whose purpose is pass/fail. Examples: #33832,
#35489, #38629.
---
## E. Pipeline structure
**E1. Jobs and reruns come from one declarative definition, not hand-written or duplicated ones.**
Spot: a workflow where adding a runner means editing a matrix by hand; sharding sized by
a constant rather than by measured partition data; a rerun workflow that declares its own
image, deps or env instead of reusing the stage's. Examples: #34186, #33329, #31792,
#36257, #36775, #34324, #34195.
**E2. An expensive artifact is built once per run and reused downstream.**
Spot: the same build or setup block appearing in several jobs of the same run; the same
compile happening in every job of a matrix. Examples: #33461, #33384, #33597, #33460,
#37258.
**E3. Runner-pool availability is a single configuration switch, not a recurring workflow edit.**
Spot: the same job disabled and re-enabled by separate PRs, each touching the workflow
files, several times in a quarter. Examples: #31764, #32719, #35627, #35607, #36981,
#37522, #38770, #38842, #39044.
**E4. Operational commands are gated on trust, scoped narrowly, and always answer.**
Spot: a command any commenter can trigger; a command that reruns far more than the caller
needs; an unrecognized command that is skipped silently; a reply that names the wrong
backend or run. Examples: #35750, #31980, #34057, #37618, #38734, #38736, #36778.
**E5. Fast-fail cascades link only jobs whose failures are correlated.**
Spot: a scheduled or manually dispatched run whose later jobs are cancelled by an earlier
unrelated failure; another platform's lane cancelled by a CUDA failure. Examples:
#35392, #35238, #36146.
**E6. Platform-specific CI triggers only on paths that affect it.**
Spot: a platform workflow that runs on every PR regardless of what changed; a lint stage
that runs a whole workspace's tests when none of its files moved. Examples: #36100, #34864.
---
## F. Install and cache cost
**F1. Build caches persist across jobs and runs, and every cache has a miss path.**
Spot: caches rebuilt per job; a cache miss that fails the job instead of falling back; a
cache key that does not change when its inputs do. Examples: #33361, #33619, #33460,
#34231, #33597, #35337, #33512, #32243.
**F2. The environment is pinned: toolchain and critical dependencies are fixed, survive later installs, and the checkout is what runs.**
Spot: build steps with no explicit toolchain, so an image bump silently changes output; a
transitive dependency that a later `pip install` upgrades or downgrades; an image built
from whatever main was at build time rather than the workflow commit; a site-packages
copy of the package under test taking precedence over the working tree. Examples:
#33437, #37508, #34768, #34322, #33738, #33441.
**F3. Repeated work inside the test process is cut.**
Spot: a checker that re-parses the whole package on every scan; a fixture that reloads a
tokenizer or model per case; independent subprocesses run serially; lint environments
rebuilt on every run. Examples: #36240, #36235, #36241, #37203, #34309.
**F4. Work whose precondition already holds is skipped: installs, disk reclamation, cache refreshes.**
Spot: unconditional installs; a skip check whose matching is too strict to fire on real
package name variants; unconditional disk reclamation; cache-base refreshes on every ref.
Examples: #33757, #37689, #33637, #33644, #33904.
**F5. A published package is preferred over a source clone; a required clone authenticates and retries.**
Spot: anonymous `git clone` in an install script; a dependency built from source that
ships a wheel. Examples: #37672, #37504, #37647, #34635.
**F6. Build outputs match the platforms and ABIs of the runner fleet.**
Spot: single-architecture images consumed by heterogeneous runners; an extension built
for one ABI when jobs run on two. Examples: #34276, #34253, #33619, #37820.
---
## G. Observability
**G1. Job names and status surfaces state the configuration that actually ran.**
Spot: an image version or platform variant only discoverable in the log; a status block
that omits a lane the PR gate depends on. Examples: #35686, #34813, #34877.
**G2. CI reporting carries queue time and runner health alongside pass/fail, and is shaped to be read.**
Spot: notifications that only say whether a run passed; a scheduled job missing from the
monitor that tracks its peers; a digest with no local timestamps, no filtering to
exceptions, and no trend over time. Examples: #37881, #33753, #37884, #38380.
---
## H. Guardrails
**H1. Every convention and every silent failure mode has a check that can fail.**
Spot: a rule that lives only in a review comment or a doc; a registered file whose tests
never run still reporting success; a suite whose job is never dispatched; a coverage job
that has failed on every run without anyone noticing; a sweep touching hundreds of test
files with no before/after registration count per backend. Silent non-execution is more
dangerous than a red test. Examples: #32735, #34074, #34147, #39697, #38581, #37436.