From jaan-to
Runs mutation testing on test suites using stack-specific tools like Stryker (JS), Infection (PHP), Mutmut (Python), and go-mutesting (Go) to validate test quality. Use for verifying test effectiveness.
npx claudepluginhub parhumm/jaan-to --plugin jaan-toThis skill is limited to using the following tools:
> Mutation testing is the only reliable quality metric for AI-generated test suites.
Creates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Mutation testing is the only reliable quality metric for AI-generated test suites.
$JAAN_CONTEXT_DIR/tech.md - Tech stack context (CRITICAL -- determines mutation framework)
#current-stack, #frameworks, #constraints$JAAN_LEARN_DIR/jaan-to-qa-test-mutate.learn.md - Past lessons (loaded in Pre-Execution)$JAAN_TEMPLATES_DIR/jaan-to-qa-test-mutate.template.md - Output template${CLAUDE_PLUGIN_ROOT}/docs/extending/language-protocol.md - Language resolution protocol${CLAUDE_PLUGIN_ROOT}/docs/research/76-tdd-bdd-ai-orchestration.md (Section 5)Test Suite Source: $ARGUMENTS
Input modes:
/jaan-to:qa-test-generate)IMPORTANT: The input above is your starting point. Determine mode and proceed accordingly.
MANDATORY -- Read and execute ALL steps in: ${CLAUDE_PLUGIN_ROOT}/docs/extending/pre-execution-protocol.md
Skill name: qa-test-mutate
Execute: Step 0 (Init Guard) -> A (Load Lessons) -> B (Resolve Template) -> C (Offer Template Seeding)
Also read context files if available:
$JAAN_CONTEXT_DIR/tech.md -- Know the tech stack for mutation framework selectionIf files do not exist, continue without them.
Read and apply language protocol: ${CLAUDE_PLUGIN_ROOT}/docs/extending/language-protocol.md
Override field for this skill: language_qa-test-mutate
Language exception: Generated code output (config files, JSON reports) is NOT affected by this setting and remains in English/code.
Iterative feedback loop (Steps 5-8 of Phase 2) requires Claude Code
Tasktool for sub-agent delegation. In Codex runtime, run single mutation pass only -- skip iterative feedback loop.
ultrathink
Use extended reasoning for:
Read $JAAN_CONTEXT_DIR/tech.md for stack detection. Select mutation framework:
Reference: See
${CLAUDE_PLUGIN_ROOT}/docs/extending/qa-test-mutate-reference.mdsection "Multi-Stack Framework Table" for the supported stacks table (JS/TS, PHP, Go, Python), framework names, key CI features, and config files.
Unsupported stack fallback: If tech.md reports a stack not in the supported table (e.g., Java, Ruby, C#, Rust):
mutation_score: null in output, DO NOT failIf tech.md unavailable, use AskUserQuestion:
**/*.test.ts, **/*.spec.ts, **/*_test.go, **/test_*.py, **/Test*.php)npx --no-install stryker --version (never bare npx)vendor/bin/infection --versiongo-mutesting --versionmutmut --versionPresent discovery summary:
MUTATION TESTING ANALYSIS
-------------------------------------------------------------
Tech Stack: {detected stack}
Mutation Framework: {framework name} ({available/unavailable})
Test Runner: {runner name}
Test Files: {count} files
Test Commands: {detected test command}
Mutation Scope:
Source Files: {count} files to mutate
Test Files: {count} test files
Estimated Time: {rough estimate based on file count}
Based on stack and framework availability:
mutation_score: nullConfigure mutation run parameters:
Scope Decision:
Incremental Mode (apply when scope = incremental):
| Stack | Incremental Flag | Effect |
|---|---|---|
| JS/TS (StrykerJS) | incremental: true, incrementalFile: '.stryker-tmp/incremental.json' | Reuses ~94% of previous results via diff-match-patch |
| PHP (Infection) | --git-diff-lines --git-diff-base=main --only-covering-test-cases | Mutates only touched lines, runs only covering tests (3.3x speedup) |
| Go | N/A | No incremental support -- full runs only |
| Python | N/A | No incremental support -- full runs only |
Concurrency (always apply):
| Stack | Concurrency Flag | Notes |
|---|---|---|
| JS/TS (StrykerJS) | concurrency: N, coverageAnalysis: 'perTest' | Per-test analysis runs only tests covering each mutant |
| PHP (Infection) | --threads=max | Uses all available CPU cores |
| Go | N/A | Sequential execution only |
| Python | N/A | Sequential (community fork only) |
Reference: See
${CLAUDE_PLUGIN_ROOT}/docs/extending/qa-test-mutate-reference.mdsections "Mutation Run Commands" and "CI Integration Patterns" for per-stack commands and config examples.
Thresholds (from jaan-to/config/settings.yaml or defaults):
Reference: See
${CLAUDE_PLUGIN_ROOT}/docs/extending/qa-test-mutate-reference.mdsection "Scoring Rubric and Thresholds" for configurable threshold table.
Show complete plan before executing:
MUTATION TESTING PLAN
-------------------------------------------------------------
Framework: {mutation framework}
Available: {yes/no}
Scope: {incremental/full}
Source Files: {count} to mutate
Test Files: {count} test files
Test Command: {command}
Thresholds: Break={break}%, Target={target}%, Critical={critical}%
Feedback Loop: {enabled/disabled (Codex: disabled)}
Max Iterations: {2-3}
Output Folder: $JAAN_OUTPUTS_DIR/qa/test-mutate/{id}-{slug}/
Use AskUserQuestion:
Do NOT proceed to Phase 2 without explicit approval.
If mutation framework is unavailable for the detected stack:
mutation_score: null, empty survivors: []Execute the appropriate mutation command:
Reference: See
${CLAUDE_PLUGIN_ROOT}/docs/extending/qa-test-mutate-reference.mdsection "Mutation Run Commands" for per-stack execution commands and configuration.
Extract mutation score from mutation tool output only (never conflate with code coverage):
reports/mutation/mutation.json -> mutationScore fieldinfection-log.json -> stats.msi field (Mutation Score Indicator)killed/total ratio (no native JSON output)mutmut results CLI output -> parse survived/killed/total counts (NOT .mutmut-cache SQLite, which is unstable internal format)Write surviving mutants to handoff contract file:
Path: {output-folder}/{id}-{slug}-survivors.json
{
"schema_version": "1.0",
"tool": "{framework-name}",
"run_timestamp": "{ISO-8601}",
"mutation_score": {score or null},
"total_mutants": {count},
"killed": {count},
"survived": {count},
"survivors": [
{
"id": "mutant-{nnn}",
"file": "{relative/path/to/source.ext}",
"line": {line_number},
"original": "{original code snippet}",
"mutated": "{mutated code snippet}",
"mutator": "{MutatorName}",
"status": "Survived"
}
]
}
Contract rules:
tool field: free-form string (not enum). Known values: "stryker", "infection", "go-mutesting", "mutmut".file, line, original, mutated are REQUIRED per survivor.mutation_score: null (JSON null, not 0).If survivors artifact is empty OR mutation run failed:
Codex runtime: Skip this entire section. Single mutation pass only.
For each iteration (max 2-3, stop if delta < 5 points or delta < 2 points on iteration 2+):
Use Task tool to spawn sub-agent:
/jaan-to:qa-test-generate --from-mutants {survivors-json-path}original line and asserts behavior the mutated version would breakExecute mutation framework again with new tests added.
Calculate improvement delta:
delta = new_score - previous_scoreTrack iteration history:
Iteration Score Delta Survivors New Tests
--------- ------ ------ --------- ---------
1 72.5% -- 55 --
2 81.3% +8.8 38 17
3 84.1% +2.8 32 6 (stopped: delta < 5)
Compare final mutation score against configured thresholds:
| Threshold | Value | Result |
|---|---|---|
| Break CI | {break}% | {PASS/FAIL} |
| Target (new code) | {target}% | {PASS/FAIL} |
| Critical paths | {critical}% | {PASS/WARN if applicable} |
Before preview, validate:
file, line, original, mutated fieldssource "${CLAUDE_PLUGIN_ROOT}/scripts/lib/id-generator.sh"
SUBDOMAIN_DIR="$JAAN_OUTPUTS_DIR/qa/test-mutate"
mkdir -p "$SUBDOMAIN_DIR"
NEXT_ID=$(generate_next_id "$SUBDOMAIN_DIR")
Generate slug from feature/project name, lowercase-kebab-case, max 50 chars.
Template:
Mutation testing report for {project/feature} using {framework}. Mutation score: {score}%
({killed}/{total} mutants killed, {survived} survivors). {iterations} feedback iterations
performed. Threshold evaluation: Break CI {PASS/FAIL}, Target {PASS/FAIL}.
Top survivor locations: {top 3 file paths}.
MUTATION TESTING RESULTS
-------------------------------------------------------------
ID: {NEXT_ID}
Folder: $JAAN_OUTPUTS_DIR/qa/test-mutate/{NEXT_ID}-{slug}/
Framework: {framework}
Mutation Score: {score}% ({killed}/{total})
Survivors: {count}
Iterations: {count}
Threshold Results:
Break CI (60%): {PASS/FAIL}
Target (80%): {PASS/FAIL}
Critical (90%): {PASS/WARN/N/A}
Top 5 Survivor Locations:
1. {file}:{line} - {mutator}
2. {file}:{line} - {mutator}
...
Files:
{id}-{slug}.md (Mutation report)
{id}-{slug}-survivors.json (Survivors handoff contract)
Use AskUserQuestion:
If approved:
OUTPUT_FOLDER="$JAAN_OUTPUTS_DIR/qa/test-mutate/${NEXT_ID}-${slug}"
mkdir -p "$OUTPUT_FOLDER"
Path: $OUTPUT_FOLDER/${NEXT_ID}-${slug}.md
Sections:
Path: $OUTPUT_FOLDER/${NEXT_ID}-${slug}-survivors.json
Per contract defined in Step 4.3.
source "${CLAUDE_PLUGIN_ROOT}/scripts/lib/index-updater.sh"
add_to_index \
"$SUBDOMAIN_DIR/README.md" \
"$NEXT_ID" \
"${NEXT_ID}-${slug}" \
"{Feature/Project} Mutation Report" \
"{Executive Summary}"
MUTATION TESTING COMPLETE
-------------------------------------------------------------
ID: {NEXT_ID}
Folder: $JAAN_OUTPUTS_DIR/qa/test-mutate/{NEXT_ID}-{slug}/
Index: Updated $JAAN_OUTPUTS_DIR/qa/test-mutate/README.md
Score: {score}%
Survivors: {count}
Iterations: {count}
Next Steps:
- Review survivors and strengthen tests manually
- Run /jaan-to:qa-test-generate --from-mutants {survivors-json-path} for targeted tests
- Integrate mutation CI stage via /jaan-to:devops-infra-scaffold
Use AskUserQuestion:
If "Learn from this": Run /jaan-to:learn-add qa-test-mutate "{feedback}"
tech.md detection with unsupported-stack fallback$JAAN_OUTPUTS_DIR path{id}-{slug}/{id}-{slug}.md{id}-{slug}-survivors.json