mutation-adequacy について
テストが注入されたコード変異を検出できるか評価するレビュー用スキルで、高リスク作業のレビュー段階で必須です。生き残った変異や未カバー箇所をテストの対応事項として示し、強制の有無はプロジェクト設定に従います。
主な機能
- 01差分範囲のミューテーション検査
- 02スコアと未カバー箇所の報告
- 03変異をテスト対応事項に変換
- 04設定された強制レベルの適用
活用シーン
- 01高リスク変更のテスト評価
- 02動作変更を見逃すアサーションの特定
- 03テスト未カバーの変更行の特定
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ lvlup-sw/ exarchos --skill mutation-adequacyインストール後、Claude Code で/mutation-adequacyを入力して読み込まれたか確認します。
同梱ファイルreferences/advisory-threshold.mdreferences/reading-the-carrier.md
詳細
ミューテーション適切性スキル
概要
mutation-adequacy レビューディメンションは、緩和された検証の組み合わせ(検証ラダーのスライス 3、R5)に対する 適切性の最後の砦 です。プランナーが、動作ごとに細かくレッド・グリーンを繰り返す代わりに、より低コストなテスト戦略(厳密な型 + インライン不変条件 + PBT 1 件 + 受け入れテスト 1 件)を採用する場合、ミューテーション適切性は、見落としが招きかねない空虚なテスト(および空虚な PBT プロパティ)を防ぐ機械的なガードです。テストスイートが実際に 注入されたミュータントを検出して失敗させる かどうかを評価します。これは、テストが正しい理由で失敗していることを示す最も強力なシグナルです。
このディメンションが適用されるのは 高リスク階層のみ で、/review の境界のみ です。これは、委任時の check_test_adequacy(R3)ゲートに対応するレビューフェーズのゲートです。高リスク階層との結び付きは review-contract.ts のポリシーデータ(ディメンション名 = このスキルのフォルダー名)で定義されており、このスキルで階層への所属を判断することはありません。このゲートは高リスク階層で必須のレビューディメンションです。これは、以下の重要度とは異なる、構造上の参加要件です。アクションテーブルでは [MUTATION_GATE_NAME]: adaptLadderGate(handleMutationAdequacy) として登録され、重要度ラッパー withGateSeverity は、登録時の gate メタデータからゲート名とディメンションを読み取ります。
直交する 2 つの軸と、2 つのデフォルト重要度。 ミューテーション スコア は、デフォルトでは引き続き参考情報です。しきい値未満の mutationScore は、残存ミュータントへの対応を促し、警告を出しますが、明示的な review.gates['mutation-adequacy'] 設定で重要度が引き上げられていない限り、処理をブロックしません。等価ミュータントが存在するため、100% のスコアは期待も必須もされません。
NoCoverage 軸は、DR-6 で追加された独立した 2 つ目のチェックであり、独自の実効性のある適用経路があります。review.mutationEnforcement: block の場合、ブロックモードのガード(workflow/guards.ts の Check 4b)は、差分対象の noCoverage 件数が設定された maxNoCoverage の上限(デフォルトは 0 — 変更行の未カバーは許容されません)を超えると、review → synthesize の遷移を失敗させます。これはスコアとは独立しています。これは先送りされた計画ではなく、稼働中のコードですが、オプトイン方式です。スコアの重要度と同様に、review.mutationEnforcement のデフォルトは advisory であり、mutation-adequacy のゲートごとの重要度も、プロジェクトのディメンションレベルのデフォルト設定によって降格されたゲートが意図せず再びブロックされないよう、意図的に advisory に設定されています。プロジェクトは、設定を 1 行追加して NoCoverage の適用を有効にできます(review.mutation-enforcement: block)。解決された設定に従い、このディメンションの結果を記録する際に「advisory」をハードコードしないでください。
CI 側は別の、より限定的な仕組みです。差分対象のミューテーションゲートはプルリクエストイベントで実行されますが、.exarchos.yml の設定にかかわらず、常に 監視モード で実行されます。判定結果をログに記録し、常に終了コード 0 を返します。現時点では CI におけるブロック用の最後の砦ではありません。切り替えは、サーバースイート全体でクリーンなランナー判定が得られるまで先送りされています。CI ゲートをマージブロッカーとして説明しないでください。NoCoverage の適用が有効な場合、その適用経路は上記のレビューであり、CI ではありません。
注:該当する場合の advisory は、しきい値未満のスコアや上限超過の NoCoverage 件数によって、実際に遷移が止められるかどうかを指します。いずれかの軸で解決された重要度にかかわらず、高リスク階層ではこのディメンションの 記録 が必須です(all-reviews-passed ガードは mutation-adequacy レビューがない場合に失敗するため)。そのため、毎回実行して記録してください。
トリガー
次の場合にこのスキルを有効にします。
- 高リスク階層の機能で、
reviewが品質ステージに到達した場合 - レビューワークフローの必須レビューとして、レビュー契約に
mutation-adequacyが記載されている場合 - (緩和されている可能性のある)テストの組み合わせが実際にミュータントを検出して失敗させるか評価する必要がある場合
中リスク/低リスク階層の作業、または /review の境界外では有効にしないでください。これらの経路では、このディメンションは必須ではありません。
実行
ステップ 1:差分スコープのミューテーションゲートを実行する
レビュー/PR のベース ref を指定してアクションを呼び出します。解決済みのミューテーションコマンドを
差分スコープで実行します(Stryker の --since、cargo-mutants の --in-diff、mutmut のパス制限。ツールチェーンの SoT から解決され、手動で組み合わせることはありません)。そのため、実行はフルツリーの
所要時間ではなく、< minutes で完了します。
exarchos:exarchos_orchestrate({
action: "mutation-adequacy",
featureId: "<featureId>",
base: "<review/PR base ref>", // e.g. "main" — reuse the same base the review diff uses
worktreePath: "<optional worktree>", // 'auto' resolves the calling delegation's worktree
operationId: "<optional idempotency key>"
// scope defaults to "diff"; do NOT pass scope:"full" here (see Anti-Patterns)
})
アクションは mutation.executing_started / mutation.executed(ライブネス、INV-10)と、mutationScore(INV-1)を含む折りたたみ可能な
gate.executed を自動的に出力します。これらのイベントを手動で出力しないでください。
ステップ 2:キャリアを読み取る
アクションは固定のキャリア(data)を返します。成功/失敗/縮退のいずれでも、構造は安定しています。
| フィールド | 意味 |
|---|---|
passed | mutationScore >= threshold && noCoverage <= maxNoCoverage — 2 つの軸と 2 つのデフォルトの重大度(ステップ 4 を参照) |
mutationScore | killed / (total − noCoverage) — Stryker の規約です。分母から noCoverage は除外されます |
killed | テストで検出されたミュータント(良好) |
survived | 逃げ切ったミュータント — テストは実行されましたが、検出できませんでした |
noCoverage | どのテストでもまったく実行されていないコード内のミュータント |
total | 差分スコープ内で生成されたミュータントの合計 |
threshold | 有効な助言用しきい値(オーバーライド > 設定 > ソフトデフォルト) |
report | 解析された Stryker の mutation-testing-report-schema |
next_actions | 「<file>:<line> を殺すテストを書く」というフォローアップ(ステップ 3) |
認識しておくべき縮退シグナル(いずれも passed: true を返すため、エラーがあってもゲートがブロックされることはありません):
skipped: true+reason— ミューテーションランナーを解決できませんでした。修正方法を報告し、失敗として扱わないでください。warning+warnings[]エントリ — ランナーが解析可能なレポートを生成しませんでした。記録し、例外をスローしないでください。deferred: true+scope: 'full'— 誤ってフルスコープを要求しました。デフォルトの差分スコープで再実行してください。
キャリアとレポートの全構造については、references/reading-the-carrier.md を参照してください。
ステップ 3:サバイバーを「このミュータントを殺す」フォローアップに変換する(INV-12)
アクションは、各 survived および NoCoverage ミュータントを、**「<file>:<line> を殺すテストを書く」**という形式の next_actions エントリにすでにマッピングしています。これらを、具体的で実行可能なレビュー指摘として提示してください。それぞれ、テストスイートに不足している特定のアサーションを示します。
- survived ミュータントは、その行を実行するテストはあるものの、ミューテーションを検出できるほど強いアサーションがないことを意味します。フォローアップ:変更後の動作を区別できるアサーションを追加します。
- NoCoverage ミュータントは、その行に触れるテストがまったくないことを意味します。フォローアップ:その行を実行するテストを追加し、観測可能な動作をアサートします。
これらは category: "test-quality" を指定した issues として記録し、他の評価項目と同じレビュー報告の契約に沿わせてください。一般的な「カバレッジを改善する」という助言を作らず、アクションが提示した file:line を引用してください。
ステップ 4:判定を適用する — それぞれ独自の解決済み重大度を持つ 2 つの軸
スコア軸は、デフォルトでは助言(警告)のままです。mutationScore がしきい値を下回る場合:
- 生き残ったケースへの対応事項(ステップ 3)を提示します。
- レビューレポートで警告します。
- 明示的な
review.gates['mutation-adequacy']設定の上書きによって重大度が引き上げられない限り、review → synthesizeの遷移をブロックしません。ハードコードせず、解決済みの重大度を適用してください。デフォルトがソフトなしきい値である理由と、スコアの推移から調整する方法については、references/advisory-threshold.mdを参照してください。
NoCoverage 軸は独立しており、実際にブロックモードを適用する経路を独自に備えています。review.mutationEnforcement: block の場合、集計後の noCoverage 件数が設定された maxNoCoverage の上限(デフォルトは 0)を超えると、スコアに関係なく、ブロックモードのガードが遷移を失敗させます。スコア軸と同様に、こちらもオプトインです(review.mutationEnforcement のデフォルトは advisory)。どちらの軸も有効または無効だと決めつけず、実際に設定されたモードを解決して適用してください。レビュー報告では、両方の軸を正確に記録してください。ブロック対象の NoCoverage の失敗を助言的なスコアの表現にまとめたり、どちらかの軸が CI によって適用されると説明したりしないでください(差分対象の CI 接続は observe モードで実行されます。「アンチパターン」を参照)。
必須の出力形式
レビュー状態にディメンションの結果を記録してください。レビューキーには kebab-case のディメンション名を指定する必要があります(このスキルのフォルダー名と同じです)。
exarchos:exarchos_workflow({ action: "update", featureId: "<id>", updates: {
reviews: { "mutation-adequacy": {
status: "pass", // "pass" is honest when both axes are advisory (the default); if either
// axis's severity is configured to block, report the real result instead
summary: "mutationScore 0.62 (threshold 0.40); 3 survivors surfaced as kill-test follow-ups",
issues: [
{ severity: "MEDIUM", category: "test-quality", file: "src/foo.ts", line: 42,
description: "surviving mutant — no assertion distinguishes the mutated branch",
required_fix: "write a test that kills src/foo.ts:42" }
]
} }
}})
all-reviews-passed ガードには、合格を示す status 値(pass | passed | approved | fixes-applied、大文字・小文字を区別しません)が必要です。単独の文字列は黙って無視され、遷移がブロックされます。
アンチパターン
| しないこと | 代わりにすること |
|---|---|
インラインで scope: "full" を実行する | ツリー全体のミューテーションは R10/v2.12 まで延期されている時間のかかる操作です。延期を示す助言を返すだけで、インラインでは実行されません |
--since / --in-diff を手作業で組み立てる | ツールチェーンの SoT からアクションに差分スコープを解決させます |
| 設定の上書きがないのに、しきい値未満のスコアを理由にマージをブロックする | デフォルトではスコア軸は助言です。対応事項を提示し、解決済みの重大度を適用してください |
| NoCoverage は常に助言、または常にブロックだと決めつける | 独自の実際のブロックモード経路(review.mutationEnforcement: block、デフォルトの上限は 0)があります。スコアの重大度と同様にオプトインです。実際に設定されたモードを解決して適用してください |
| CI のミューテーションゲートがマージをブロックすると主張する | 無条件で差分対象の observe モードで実行されます(判定をログに記録し、設定に関係なく常に 0 で終了します)。マージをブロックするものとして表現しないでください |
| Skipped/Warning のキャリアをハードエラーとして扱う | どちらも passed: true を返します。理由を報告し、例外をスローしないでください |
| 中・低ティアに対してこのディメンションを実行する | /review の境界で HIGH ティアのみをゲートします |
gate.executed を手動で出力する | アクションがライブネスと集計可能なゲートイベントを自動で出力します |
| 「テストをもっと追加してください」という一般的な助言をする | アクションが next_actions に提示した <file>:<line> を引用してください |
参考資料
references/reading-the-carrier.md— キャリアと Stryker のmutation-testing-report-schemaの読み方。references/advisory-threshold.md— しきい値がデフォルトではソフトである理由と、その調整方法。