Skip to content
Back to skills

Test Audit

ASecurity

Decide whether a Pulp test earns its place — before writing it, when reviewing one, or when sweeping a subsystem for low-value, duplicated, or mis-routed tests. Applies a four-question authoring gate, a junk-pattern list, a retention bar, and an evidence template for any deletion, and routes kept tests to the right lane (required gate, pr-fast, slow, nightly). Handles "should I add a test for this", "is this test useful", "audit these tests", "reduce test time", "which tests can go", and "mov...

  • 22 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 30, 2026
testinggoshellgitapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Scanned September 30, 2026

npx -y skills add danielraffel/pulp --skill test-audit --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Test Audit?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Test Audit
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/danielraffel-test-audit/badge)](https://www.skillsdirectory.com/skills/danielraffel-test-audit)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: test-audit
description: >-
  Decide whether a Pulp test earns its place — before writing it, when reviewing
  one, or when sweeping a subsystem for low-value, duplicated, or mis-routed
  tests. Applies a four-question authoring gate, a junk-pattern list, a
  retention bar, and an evidence template for any deletion, and routes kept
  tests to the right lane (required gate, pr-fast, slow, nightly). Handles
  "should I add a test for this", "is this test useful", "audit these tests",
  "reduce test time", "which tests can go", and "move toward functional tests".
---

# Test Audit

The goal is **confidence per minute of gate time**, not a deletion count and
not a coverage number. Most of Pulp's suite is real tests; its waste is mostly
duplication across layers, oversized grids, and tests running on a lane that
does not need them. Audit with that prior.

Adapted from OpenClaw's `test-audit` skill
(<https://github.com/openclaw/openclaw/blob/main/.agents/skills/test-audit/SKILL.md>),
with Pulp's lanes, harnesses, and retention rules.

This complements, and does not relax, CLAUDE.md's "Tests ship with fixes": a
fix still lands with its test. This skill decides **which** test — one, at the
boundary that owns the behavior, functional by default.

## Modes

| Mode | When | Output |
|---|---|---|
| **Authoring** | before adding or changing any test | pass the gate below, or don't add it |
| **Audit** | reviewing a PR's tests, or a focused sweep | a short list of high-confidence candidates with evidence |
| **Campaign** | pruning one subsystem's suite | one owner-boundary PR per subsystem, evidence per deletion |

Audit and campaign start **read-only**: record evidence before editing anything.

## Authoring gate — answer all four before adding a test

1. **What behavior, invariant, or contract does it protect?** Name it in the
   test name as an observable outcome ("the tom's pitch dive finishes before the
   note does"), not as the function it calls.
2. **What realistic regression makes it fail?** If you cannot describe the bug,
   the test is decoration.
3. **Why doesn't existing coverage already catch that?** Each contract has one
   primary owner at the strongest boundary. A second layer needs its own
   distinct risk. Prefer adding a row to an existing table-driven case over a
   new case.
4. **Does it need a seam no real caller uses?** Then test at the real boundary
   instead of adding a test-only accessor.

Then prove it can fail: break the fix and watch the test go red with
`tools/scripts/confirm_failure.sh` (see CLAUDE.md, "Confirm the failure").

## Junk patterns

A match fails the gate unless the test names an independent contract the
retention bar keeps.

- No assertions, or assertions that cannot fail.
- Self-comparison: two identically configured objects compared to each other
  (the guard under test is never exercised).
- Expected values produced by the code under test (goldens regenerated from the
  renderer, impulse values snapshotted from the filter itself) — change
  detection, not correctness.
- A test-local subclass or mock that implements the behavior being asserted.
- Capability tests that restate declared flags or ID constants instead of
  exercising delivery.
- Exact string greps over source, docs, or generated code, where a
  behavior-level assertion (layout geometry, render output, round trip) exists.
- The same contract re-asserted at every layer (proof, authoring, control,
  runtime) with no layer-specific risk.
- Near-identical cases that differ by one input — merge into a table.
- Names that promise more than the inputs exercise.
- Per-element `REQUIRE` inside a sample loop — compare whole buffers once.
- Phase or ticket breadcrumbs in names or tags (already forbidden; see the
  `code-comments` skill).

A test that breaks under a behavior-preserving refactor is implementation-
coupled. In authoring, rewrite it at the owning boundary. In an audit it is
suspect, not automatically deletable.

## Retention bar — keep these

- Public API, plugin-format adapter, protocol, state serialization / migration,
  and file-format contracts.
- Real-time safety, threading and lock-free handoff invariants.
- Platform behavior (clipboard, dialogs, audio devices, screenshots).
- Credible regressions — a bug that actually shipped or nearly did.
- Functional end-to-end behavior (see templates below).
- Static drift / registry guards that are the cheapest independent check of a
  real invariant (two real sources agree). Being static or slow is not a reason
  to delete.

A retained test that fails on the base branch is a possible product bug: fix it
at the owner, don't delete it.

## Prefer functional tests

Drive the public path, observe the outcome a user or host would observe, and
assert a stated law with a derived tolerance and a built-in control.

| Area | Shape | Existing example |
|---|---|---|
| Format adapters | render directly and through the adapter over a sample-rate × block-size sweep; require equality | `test/test_vst3_audio_parity.cpp` |
| Widgets | `simulate_click` / `simulate_drag` → state change + callback | `test/test_widgets.cpp` |
| Design import | live source vs baked native layout snapshot | `test/test_design_import_native_materializer.cpp` |
| DSP | render through `process_block` or `RenderScenario`; measure with the shared analyzers; include a no-effect control | FDN reverb, drum-voice, and kick suites |
| CLI | shell out to the built binary; assert exit code + output | `test/test_control_sample_region_e2e.cpp` |

Load the `audio-harness` skill before choosing DSP acceptance gates.

## Grid economics

Most DSP runtime sits in a few cases with oversized grids. Before accepting a
slow test, ask whether a smaller grid catches the same regression:

- One point per behavior regime, not every sample rate × every setting.
- Short renders: reset leaks and block-partition dependence show up in seconds.
- Build expensive fixtures (baked graphs, baselines) once per case, not per probe.
- Keep an exhaustive sweep if it has value — tag it `[slow]` so it leaves the
  required gate.

## Routing: which lane a kept test belongs in

Labels decide lanes; `docs/guides/test-lanes.md` is the source of truth.

- **no label** — required `macos` gate (merge queue runs the full suite).
- **`pr-fast`** — deterministic static contracts that also run on the PR head.
- **`slow` / `performance` / `bench` / `quality-lab` / `validation`** — off the
  required gate. Before moving a test there, name what still enforces it: the
  nightly is informational, not a gate, so a test that must block needs an
  affected-path selector (see "Affected slow proofs" in `test-lanes.md`).

`RUN_SERIAL` and `PROCESSORS 8` tests run alone and set the floor on the test
step's wall-clock. Don't add either without a stated reason, and don't strip an
existing one without reading why it was set.

Measure against real gate timing, not a local Debug run. The `macos` job log
prints every test's time (`Test #N: <name> ... Passed <s> sec`); local Debug
costs can be an order of magnitude higher and misrank tests.

## Candidate evidence (required before any deletion)

Record for each candidate; a missing field blocks the deletion.

- Test name and `file:line`.
- The failure it can detect.
- Non-test callers of the covered code.
- The stronger remaining proof at the owner boundary (or why none is needed).
- Why the test exists — `git log -L` or the originating commit.
- What production or test-support code the deletion unlocks.
- Risk, and the focused validation command.

## Edit shape

- One coherent owner-boundary batch per PR.
- Delete test-only accessors and wrappers along with the tests that kept them
  alive.
- Move a retained regression to its canonical owner rather than keeping copies.
- Don't add replacement tests that restate the implementation, and don't
  delete uncertain candidates to inflate a count.
- Report test LOC and production LOC separately in the PR description.

## Validation

1. Build and run the owner's and siblings' test targets
   (`ctest --test-dir build -R <pattern>`; pair any "0 tests" result with a
   positive control — `ctest -R` exits 0 on no match).
2. For a removed string grep, run the tool or check that owns the real contract.
3. `tools/scripts/gates.sh origin/main`.
4. Run the inline architectural + adversarial review before the PR.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…