name: mutation-audits
description: >-
Owns ${CLAUDE_PLUGIN_DATA}/mutation-audits/<consumer-slug>/audits.json —
the durable, per-module record of mutation-testing runs produced by
base:mutation-testing and consumed by base:next-mutation,
/base:orient, and the AC-linkage step of base:property-based-testing.
Single authority for the file format
(plugins/base/schemas/mutation-audit.schema.json) and the canonical
write path (scripts/). Operations: add-audit, get, list, query.
Sibling concern to base:learnings (technical knowledge) and
base:metrics (per-epic workflow rollups); mutation audits are
per-module diagnostic state used to phase coverage toward the full
project and to derive spec-gap findings from unspecced behavior.
user-invocable: true
argument-hint: " [op-specific args] — ops: add-audit | get | list | query "
allowed-tools: Read, Bash
Format authority
The canonical format of audits.json is defined by the JSON Schema at
plugins/base/schemas/mutation-audit.schema.json. All readers and
writers cite that schema rather than restating the rules. Every write
script in scripts/ validates against the schema before committing a
mutation — the file cannot land in a malformed state.
The semantic policy that does not fit in a JSON Schema — replace-in-place
semantics for module records, AC-linkage convention, dep-snapshot
discovery — lives in references/format.md.
Where the writes happen
All mutations go through scripts in ${CLAUDE_SKILL_DIR}/scripts/:
| Operation | Script | Purpose |
|---|---|---|
add-audit |
add-audit.sh |
Insert or replace a module record (path is the identity key); lazily bootstraps audits.json on first call |
get |
get.sh |
Fetch a single module record (or one field) as JSON or raw |
list |
list.sh |
List module records with optional filters (--stale-days N, --changed-since <sha>, --tool <name>) |
query |
query.sh |
jq passthrough across the current consumer's audits.json |
Every script:
- Refuses to invoke
claude,Skill, or any LLM-backed runtime. - Uses
--help/-hto print its header comment block. - Sources
lib/common.shfor shared helpers. - Uses atomic tmp+rename so concurrent runs cannot tear the file.
- Validates against the schema after every write.
- Stamps
updated_atwith an ISO 8601 timestamp. - Requires
jq(a hard dependency; checked at startup).
Consumers (base:mutation-testing's diagnostic loop, base:next-mutation
walks, /base:orient stale-audit surfacing, base:property-based-testing
AC-linkage step) invoke this skill via the Skill tool — they do
not shell out to the underlying scripts directly. Direct shell-outs
to plugins/base/skills/mutation-audits/scripts/*.sh from outside this
skill are a contract violation: the relative path only resolves when
cwd is the claude-plugins source repo, so consumer-project invocations
silently fail. The schema, atomic-write, and validation guarantees only
hold when every writer takes the
Skill("base:mutation-audits", args: "<op> …") path.
File layout
${CLAUDE_PLUGIN_DATA}/
├── retros/<consumer>/... # sibling — workflow friction (existing)
├── meta-retros/<consumer>/... # sibling — meta-level findings (existing)
├── learnings/<consumer-slug>/... # sibling — technical knowledge (existing)
├── metrics/<consumer-slug>/... # sibling — per-epic workflow rollups (existing)
└── mutation-audits/ # this skill
└── <consumer-slug>/
└── audits.json # one structured file per consumer
<consumer-slug> is resolved by resolve_consumer_slug() in
scripts/lib/common.sh — same algorithm as the retros / backlog /
learnings skills (lowercase basename of git repo root, alphanumeric +
hyphens). In plugin-dev mode this resolves to claude-plugins; in
consumer mode it resolves to the consumer project's directory name.
Dispatch
Read $ARGUMENTS. The first whitespace-separated token is the
operation; the rest are op-specific arguments passed through to the
script. Run the matching script with bash, capture output, surface
to the user.
add-audit [args...] → scripts/add-audit.sh [args...]
get <path> [args...] → scripts/get.sh <path> [args...]
list [args...] → scripts/list.sh [args...]
query <jq-expr> → scripts/query.sh <jq-expr>
anything else (or empty) → list ops with one-line summaries, exit
The skill is a thin dispatcher: it does not encode validation, format
rules, or routing in prose. Each script is self-documenting via
--help. Run a script with --help to see its arguments.
Operation summaries
add-audit
Insert-or-replace a module record. Module path is the identity key —
a second add-audit against the same path overwrites the prior record
in place. Lazily bootstraps audits.json if it doesn't exist yet.
Required flags: --path <repo-relative-module-path>, --tool <name>,
--killed <int>, --survived <int>, --no-coverage <int>,
--timeout <int>, and --survivors-json <path> (a file containing a
JSON array of survivor objects matching the schema's survivor
definition).
Optional flags: --tool-version <string>, --consumer-sha <sha>
(defaults to git rev-parse HEAD), --deps-lockfile <path>,
--deps-sha <sha> (paired — provide both or neither).
The script computes score = killed / (killed + survived + no_coverage)
itself; callers don't pass it. Validation against the schema runs
before write.
get
Fetch one module record by path. Signature: get <path> [--field <jq-path>] [--raw]. With --field, drill into a sub-field (e.g.
--field .stats.score). With --raw, emit unquoted strings. Exit 1
when the path has no record.
list
List module records with optional filters. Signature: list [--stale-days <N>] [--changed-since <sha>] [--changed-since-audit] [--tool <name>] [--format brief|paths|json]. Default format brief
emits one line per module with path, score, last-audit date,
survivor counts.
--stale-days N filters to modules whose last_audit is older than N
days. --changed-since <sha> runs git diff --name-only <sha>..HEAD
once and keeps modules whose path overlaps the changed file set
(useful for "changed since one branch ref"). --changed-since-audit
runs git diff --name-only per module against each module's own
last_audit.consumer_sha and keeps modules whose own files have
moved since their own last audit — the correct signal for "what
should I re-audit." Modules with consumer_sha == "unknown" or an
unresolvable sha count as changed (broken provenance). The two
--changed-since* flags are mutually exclusive. --tool <name>
filters by audit tool. Filters compose (AND).
query
Raw jq over audits.json. Signature: query <jq-expr>. No
post-processing — caller writes their own jq.