Metabase mutation-testing
Run mutation testing on a Clojure namespace, generate tests to kill surviving mutations, and open draft PRs with Linear issue tracking.
install
source · Clone the upstream repo
git clone https://github.com/metabase/metabase
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/metabase/metabase "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.claude/skills/mutation-testing" ~/.claude/skills/metabase-metabase-mutation-testing && rm -rf "$T"
manifest:
.claude/skills/mutation-testing/SKILL.mdsource content
Mutation Testing Skill
This skill runs mutation testing end-to-end: generates a coverage report, groups functions, shells out to
claude -p to write tests, verifies mutations are killed, and creates draft PRs with Linear issues.
Prerequisites
- A running nREPL connected to the Metabase dev environment
environment variable set with a valid Linear personal API keyLINEAR_API_KEY
CLI authenticated with GitHubgh
CLI available on PATHclaude
Reference Files
— mutation testing engine (generates reports, runs mutations)dev/src/dev/coverage.clj
— orchestration, Linear API, PR helpersdev/src/dev/mutation_testing.clj
Invocation
The argument is a Clojure namespace, optionally followed by a base branch:
/mutation-testing metabase.lib.order-by /mutation-testing metabase.lib.order-by --base-branch release-x.52.x /mutation-testing metabase.lib.order-by --project-id abc-123 /mutation-testing metabase.lib.order-by --base-branch release-x.52.x --project-id abc-123
: defaults to the value in config (set via--base-branch
), orset-config!
if not configured."master"
: if provided, issues are added to this existing Linear project instead of creating a new one.--project-id
Steps
1. Parse arguments
Parse
$ARGUMENTS to extract:
- namespace (required) — the first positional argument
(optional) — if present, the next argument is the base branch name--base-branch
(optional) — if present, the next argument is an existing Linear project ID--project-id
2. Load and configure
(require '[dev.mutation-testing :as mut-test] :reload) (require '[dev.coverage :as cov] :reload)
If this is the first invocation (or the REPL was restarted), set up the Linear team:
(mut-test/list-teams!) (mut-test/set-config! {:team-id "<id>"})
3. Run
(mut-test/run! '<target-ns>) ;; Or with options: (mut-test/run! '<target-ns> {:base-branch "release-x.52.x"}) (mut-test/run! '<target-ns> {:project-id "abc-123"}) (mut-test/run! '<target-ns> {:base-branch "release-x.52.x" :project-id "abc-123"})
Pass the opts map with
:base-branch and/or :project-id if those flags were provided in the arguments.
This will:
- Generate a baseline mutation testing report
- Create a Linear project for the namespace
- Group functions by coverage relationships
- For each group: create branch → invoke Claude to write tests → verify → retry if needed → commit & push → create Linear issue → create draft PR
- Print a summary with PR links
4. Handle failures
If a group fails mid-processing,
run! catches the error and continues to the next group. To retry a single group manually:
;; Get the parsed namespace info (def parsed (mut-test/parse-namespace '<target-ns>)) ;; Run coverage to get the data (def coverage-results (cov/test-namespace (:target-ns parsed) [(:test-ns parsed)])) ;; Group and find the one you want (def groups (mut-test/group-functions coverage-results)) ;; Process just that group (mut-test/process-group! parsed (nth groups <index>))
5. Verify the prompt (dry run)
To inspect what Claude will see without invoking it:
(def parsed (mut-test/parse-namespace '<target-ns>)) (def coverage-results (cov/test-namespace (:target-ns parsed) [(:test-ns parsed)])) (def groups (mut-test/group-functions coverage-results)) (println (mut-test/build-test-prompt (merge (select-keys parsed [:target-ns :test-ns :source-path :test-path]) (select-keys (first groups) [:fn-names :mutations]))))
Configuration
- Team ID: Set once per REPL session via
and(mut-test/list-teams!)(mut-test/set-config! {:team-id "..."}) - Project ID: Set automatically when
callsrun!create-project-for-namespace! - Base Branch: Defaults to
. Override via"master"
or pass(mut-test/set-config! {:base-branch "release-x.52.x"})
as the second arg to{:base-branch "..."}run! - Project ID: If set (via
orset-config!
opts),run!
reuses the existing Linear project instead of creating a new onerun!