Skip to content
Back to skills

Setup

ASecurity

Verify and configure the mutation-testing plugin for this repository. check inspects the ecosystem's mutation tool (installed, runnable, supported test runner), the baseline suite's health, and the tracked .claude/mutation-testing.md config across its merge layers, read-only; apply detects the ecosystem, installs or names the tool, interviews for the diff target and operator set, and writes the config plus an empty arid-node suppression record. Use when: 'set up mutation testing', 'is mutatio...

  • 13 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 2, 2026
ai-agentspythongophpbashnodetestinggit

Works with

  • claude code
  • cli

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned October 4, 2026

npx -y skills add melodic-software/claude-code-plugins --skill setup --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Setup?

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

Security grade badge for Setup
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/melodic-software-setup-049033f2/badge)](https://www.skillsdirectory.com/skills/melodic-software-setup-049033f2)

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
---
description: "Verify and configure the mutation-testing plugin for this repository. check inspects the ecosystem's mutation tool (installed, runnable, supported test runner), the baseline suite's health, and the tracked .claude/mutation-testing.md config across its merge layers, read-only; apply detects the ecosystem, installs or names the tool, interviews for the diff target and operator set, and writes the config plus an empty arid-node suppression record. Use when: 'set up mutation testing', 'is mutation testing configured', 'which mutation tool for this repo', 'mutation-testing setup', or the audit skill reports missing config or an unavailable tool. Re-runnable. Safe to invoke again to reconfigure."
argument-hint: "[check|apply]"
user-invocable: true
disable-model-invocation: true
---

## Purpose

Establish the four things `/mutation-testing:audit` cannot infer safely at run time: **which tool**,
**which diff target**, **which operators**, and **where arid-node suppressions live**.

Configuration is required rather than optional here, which differs from a plugin whose config merely
speeds up inference. A mutation run drives the project's own test runner against deliberately broken
source; guessing the tool or the diff target either does nothing or does something expensive.
Check-centric per the uniform setup contract (`docs/plugin-philosophy.md`
"Setup is explicit and repeatable" in the marketplace repository): `check` inspects read-only;
`apply` interviews and writes, then re-runs `check`. No argument or `check` runs the check; `apply`
runs the check first, then the write flow.

## The config merge model

`.claude/mutation-testing.md` resolves across the documented cascade layers. User-global
(`~/.claude/mutation-testing.md`) → team (`${CLAUDE_PROJECT_DIR}/.claude/mutation-testing.md`) →
local overlay (`.claude/mutation-testing.local.md`), later layers overriding scalars and unioning
lists. This skill writes only the **team** file; a higher overlay is the user's to change. Report
explicitly when an overlay changes the team file's effect, and warn when a layer cannot be read,
rather than silently presenting the team file as effective.

The arid-node suppression record is a **separate** surface, `.claude/mutation-testing-arid.md`,
shaped by the finding-suppression convention. Keeping it separate from the config keeps the config
reviewable: a config diff is a policy change, a suppression diff is an accepted finding.

**Its layering is not the config's, and the difference is the whole point.** The config's later
layers override; the suppression record sits in the cascade's policy-floor precedence-inversion
class, so on a conflict for the same `finding_id` the **team** layer wins, and **a personal-layer
entry for an id the team layer does not carry does not suppress at all**. It is reported
`personal-only, not applied`. A personal layer is a draft surface here. Treating it as "layered the
same way" would let one developer silently hide a finding the team never accepted. The full
contract, including id derivation and the four dispositions, is owned by the audit skill's
[`${CLAUDE_PLUGIN_ROOT}/skills/audit/context/suppression.md`](../audit/context/suppression.md).

## `check` (read-only)

Report a PASS / FAIL / INFO table with one remediation line per FAIL. Modify nothing, and do **not**
run a mutation analysis. That is `/mutation-testing:audit`.

1. **Ecosystem detected**. Identify the stack from manifests that actually exist (`package.json`,
   `*.csproj`/`*.sln`, `pom.xml`/`build.gradle`, `composer.json`, `pyproject.toml`). Report what was
   found and which mutation tool it implies. No recognized ecosystem → INFO naming the manual path
   in the `principles` skill's `tooling.md`, not FAIL.
2. **Tool present and runnable**. Invoke the tool's own version command. Absent → FAIL with the
   install line for this ecosystem. Present but erroring → FAIL quoting the error; a tool that
   installs but cannot start is the common case and is worth distinguishing.
3. **Test runner supported**. Confirm the project's runner is one the tool drives. Report the
   detected runner and the tool's support status. Unsupported → FAIL; this blocks every later step,
   so report it before anything about scores.
4. **Baseline suite is green**. Run the project's test command once, unmutated, and report the
   result. Red → FAIL. A red baseline kills every mutant and reports a perfect score, so no mutation
   result is meaningful until this passes. Record the wall-clock; it is the input to the timeout
   setting and to whether diff-scoping is sufficient.
5. **Known flakiness**. Ask, and check for a documented flaky-test list or retry configuration.
   Flakiness present → INFO with the consequence stated plainly: mutants killed by a flaky failure
   inflate the score by an unknown margin.
6. **Config presence (effective, across layers)**. Report the merged result and which layer
   contributes what: tool, diff target, operator set, timeout, paths to mutate. Nothing readable →
   FAIL; unlike an inference-speeding config, this one is required.
7. **Diff target resolves**, the configured target must resolve in this repository
   (`git rev-parse --verify <target>`). Unresolvable → FAIL naming it; a stale default here silently
   scopes a run to nothing or to everything. A `test-command` without `{tests}` as a standalone
   word (whitespace or the end of the string on both sides, unquoted) → FAIL: `audit --exercised`
   replaces that word with the test paths, one argument each. Without it every mutant would run
   against the whole suite; inside quotes (`"{tests}"`) or a larger word (`--files={tests}`) the
   paths would merge into one argument or split wrongly. Absent `test-command` → INFO; only the exercised scope
   under the manual protocol needs it.
8. **Suppression record**. Report presence and entry count of `.claude/mutation-testing-arid.md`
   across layers. Absent is a valid state (no suppressions) → INFO. Present → validate **every**
   entry against the full contract in
   [`${CLAUDE_PLUGIN_ROOT}/skills/audit/context/suppression.md`](../audit/context/suppression.md), not a subset of it.

   Grade the entries with the lint rather than deriving anything by hand, passing every layer that
   exists:

   ```bash
   bash "${CLAUDE_PLUGIN_ROOT}/scripts/suppression-lint.sh" .claude/mutation-testing-arid.md
   ```

   It prints `record <path>` and then one line per entry, and exits 0 when every entry passes, 1
   when any entry fails, and 2 when it cannot grade a record at all: no top-level `suppressions:`
   mapping, an unreadable node-kind table, or no digest tool. An absent record and an empty
   `suppressions:` mapping are both exit 0. Its three failure verdicts are FAIL conditions here; the
   two items after them are the probe's own, and the lint decides neither:
   - **`malformed <id>`**. A required key is missing (`check`, `claim`, `sites` with a `surface` and
     an `anchor/v<N>`, `reason`, `date`), `date` is present but is not a real calendar day written `YYYY-MM-DD`,
     or `claim` is not the canonical form. FAIL, naming the entry and the key the lint names. A partial parse is not offered: an entry with `sites`,
     `reason`, and `date` but no `check` or `claim` is malformed and must not be reported as usable,
     because the audit would otherwise suppress a mutant on a location match the contract never
     authorized.
   - **`mismatch <id>`**. The entry's `(check, claim, sites)` do not hash to the key it is filed
     under, and the line names the id they do hash to. FAIL. This is the case a hand-edited
     constituent beside a stale key produces, and it silently stops suppressing if unchecked.
   - **`unknown-kind <id>`**. `claim` binds `arid(kind=<node-kind>)` with a kind that is not a
     **member of the enumerated table** in
     `${CLAUDE_PLUGIN_ROOT}/skills/principles/reference/scaling-and-suppression.md`
     ("The node-kind vocabulary"), which the lint reads at run time. FAIL, reporting the accepted
     kinds the lint prints beneath the failing entries. Membership is the test, not shape: a
     single-word kind that is not in the table is indistinguishable from prose that happens to be
     one word, and passing it would make every suppression self-justifying.
   - **Personal-only entries**. Any id present in a `.local.md` layer but absent from the team layer
     is reported `personal-only, not applied`, with promotion to the team layer named as the remedy.
     This is INFO, not FAIL: the entry is legal, it simply does not suppress.
   - Report the contributing layer for every entry. A malformed layer degrades soft. Report it and
     continue, never fail the whole read.
9. **Tracked, not ignored**. Both the config and the suppression record must be committed to be
   team-shared, and *not ignored* is only half of that. Run **both** probes per file:
   `git ls-files --error-unmatch <path>` (is it tracked?) and `git check-ignore -v <path>`
   (is it ignored?). Untracked → FAIL, naming the `git add` that fixes it; ignored → FAIL with the
   matching pattern. Checking only `check-ignore` is the trap: immediately after `apply` writes them
   the files are untracked but not ignored, so `check-ignore` is silent and the probe would report
   success on two files no teammate will ever receive. The `.local.md` overlays are expected to be
   both untracked and ignored. INFO, not FAIL.

## `apply` (idempotent)

Run `check`, then interview and write. Proceed non-interactively where the repository makes a value
unambiguous; ask only where the answer is genuinely the user's.

1. **Read the effective config first, across all layers**, and summarize it before proposing
   changes. Nothing is dropped without the user confirming.
2. **Detect and propose the tool.** From the ecosystem, name the tool and the exact install command,
   and ask before installing anything. Installing a dependency into the consumer's project is the
   user's decision, not this skill's. Propose, never install unprompted.
3. **Settle the diff target.** Default to the repository's own default branch as resolved from
   `origin/HEAD`, not a hardcoded `main` or `master`. Confirm it resolves.
4. **Settle the operator set.** Default to the tool's defaults, and say why: optional and
   experimental operators raise the mutant count and the unproductive rate together. Offer the
   narrowed set only if the user asks for a cheaper run.
5. **Settle the timeout** from the measured baseline suite time in `check`, not from a guess.
6. **Settle the mutate paths and the test command.** Propose source roots, excluding generated
   code, vendored directories, and test code itself. Mutating tests measures nothing. Then propose
   `test-command` for the detected ecosystem when its runner takes test file paths, with `{tests}`
   where the paths go: `python -m pytest {tests}`, `python -m unittest {tests}`,
   `npx vitest run {tests}`, `npx jest --runTestsByPath {tests}`, `vendor/bin/phpunit {tests}`. The
   audit fills `{tests}` with each path quoted as its own argument. Propose nothing for a runner
   that only filters by name (`dotnet test --filter`, Maven `-Dtest`, Gradle `--tests`) and say
   that `audit --exercised` refuses for that ecosystem. The forms and their sources are in the
   audit skill's [`tool-test-restriction.md`](../audit/context/tool-test-restriction.md).
7. **Write the config** following
   [`${CLAUDE_PLUGIN_ROOT}/skills/setup/templates/config-template.md`](templates/config-template.md).
8. **Create the suppression record empty**, with its header comment and an empty `suppressions:`
   mapping, a **mapping, never a list**, since a list is taken whole and one personal entry would
   discard the team's entire accepted set. An empty record makes the first suppression an edit to a
   reviewed file rather than the creation of a new one.
9. **Get both files tracked and committed, and say so.** They are team-shared surfaces; leaving them
   untracked is the failure probe 9 exists to catch, and `apply` is where it is cheapest to fix.
   Offer the `git add` and the commit that follows it, and run each only on the user's explicit
   acceptance; never commit unasked. Do not stop at staging as a matter of course: staged and
   unstaged both leave a dirty working tree, and a session-end gate that refuses a dirty tree holds
   the session open until these two files are committed and pushed. Say so when the user declines,
   so the tree they are left with is a choice rather than a surprise.
10. **Verify after remediation.** Re-run the `check` probes on what was written, including both
    halves of probe 9, tracked *and* not-ignored, then offer the overlay convention: personal
    config overrides in `.claude/mutation-testing.local.md`, and recommend the recursive
    `.claude/**/*.local.*` line in `.gitignore` if not already covered. State plainly that the same overlay pattern does **not**
    give personal suppressions effect: an arid entry in a `.local.md` is a draft until promoted to
    the team layer.

Re-running `apply` after everything passes changes nothing and reports "already configured".

## Output

A tracked `.claude/mutation-testing.md` and `.claude/mutation-testing-arid.md` in the consuming repo,
plus a one-paragraph summary of what was written, the measured baseline suite time, and how to
re-run this setup to reconfigure.

## What this skill does NOT do

- Run a mutation analysis. That is `/mutation-testing:audit`. `check` runs the *unmutated* suite
  once to establish the baseline, and nothing more.
- Install anything without asking.
- Write the plugin cache, Claude Code user settings, or `pluginConfigs`.
- Write machine-local state. Configuration lives in the consumer's tracked files, never in the
  plugin directory or the plugin data directory.
- Set a score threshold. The config template deliberately has no such field; see the `principles`
  skill's `scaling-and-suppression.md` for why gating on the score inverts the incentive.

Files in this skill

  • SKILL.md11.4 KB
  • evals/evals.json4.3 KB
  • templates/config-template.md3.3 KB

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…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.