AIコーディングエージェント(Claude Code、Codex、Cursorなど)が当たり前になった2025年。「AIが書いたコード、いつマージしていいの?」という問いに、多くのチームが明確な答えを持っていない。
人間がコードレビューする?AIが書いた大量のコードを?それこそ本末転倒だ。
MetricShipは、この問いに対する一つの回答である。AIに「どう実装するか」は指示しない。「いつ完了したと言っていいか」だけを定義する。
核心思想:判定は決定論、AIは入力データ
MetricShipの根本にある考え方はシンプルだ。
MetricShipはAIに「どう実装するか」を指示しない。
「いつ完了したと言っていいか」だけを定義し、
「作るAI」と「疑うAI」を分離して、
人間をコードレビューから基準最適化へ解放する。
AIが何を使おうが、どんなアーキテクチャで実装しようが、MetricShipは関与しない。ただ「この成果物は基準を満たしているか?」だけを判定する。
Goodhart's Lawとの戦い
ここで大きな問題がある。**Goodhart's Law(指標が目標になると、良い指標ではなくなる)**だ。
AIが「テストカバレッジ80%」を目標にすると何が起きるか?中身のないテストを大量生成する。「コードレビューで指摘0件」を目指すと?表面的な問題だけ直して本質的な設計問題はスルーする。
これを防ぐために、MetricShipは**「作るAI」と「疑うAI」を分離**する。
マルチエージェント構造
┌─────────────────────────────────────────────────────────────┐
│ 自律ループ (Autonomous Loop) │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Main Agent (Claude Code) │ │
│ │ ├─ コード実装 │ │
│ │ ├─ Unit Test Generator │ │
│ │ └─ Integration Test Generator │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ 成果物のみ渡す │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Audit Agent (別セッション・別モデル推奨) │ │
│ │ ├─ Test Validity Auditor │ │
│ │ ├─ AI Code Reviewer │ │
│ │ └─ Mutation Test Runner │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ metrics.json │
│ ↓ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ MetricShip Judge(DSL判定・決定論) │ │
│ └─────────────────────────────────────────────────────┘ │
│ ↓ │
│ PASS / BLOCK / HUMAN / ABORT │
└─────────────────────────────────────────────────────────────┘
重要なのは、Audit Agentは成果物のみを受け取ること。実装過程は知らない。だから「自分が書いたテストだから甘くなる」現象が起きない。
メタ・メトリクス:テストの質を測る
単純なテスト合格率だけでは不十分だ。MetricShipは「テスト自体の質」を測定する。
| メトリクス | 説明 | 狙い |
|---|---|---|
test_validity.score |
テストが実装の意図を正しく検証しているか | 中身のないテスト排除 |
mutation.score |
コードを壊した際にテストが失敗する割合 | テストの検知能力担保 |
coverage.negative_case_ratio |
異常系テストの割合 | ハッピーパスのみ防止 |
Mutation Testingの威力
例えば、こんなコードがあるとする。
function calculateDiscount(price: number, rate: number): number {
return price * (1 - rate);
}
テストはこう書かれている。
test('割引計算', () => {
expect(calculateDiscount(100, 0.1)).toBe(90);
});
一見OKに見える。でもMutation Testingは「*を+に変えても、このテストは失敗しないのでは?」と考える。実際、price + (1 - rate)にしたら100.9になるので、このテストは正しく失敗する。
しかし「1 - rateをrateに変えても失敗しない」テストがあったら?それは「表面的に動くけど本質を検証してないテスト」だ。Mutation Scoreはこれを暴き出す。
判定ステータス
MetricShipは4つのステータスを返す。
| ステータス | 意味 | 次のアクション |
|---|---|---|
| PASS | 基準をすべて満たした | 自動マージ・デプロイOK |
| BLOCK | 基準を満たしていない | AIが自動で修正ループに戻る |
| HUMAN | 自動判定できない | 人間が判断材料を見て決定 |
| ABORT | 最大試行回数超過 | 設計・要件の見直しが必要 |
BLOCKが出たら、Main Agentは自動で修正に入る。人間は介在しない。
while [[ $(metricship status) != "PASS" ]]; do
claudecode --task "Refine based on last failure"
metricship audit --target "./src"
RESULT=$(metricship judge)
if [ "$RESULT" == "ABORT" ]; then
echo "Max retries exceeded. Human intervention required."
exit 1
fi
done
criteria.yaml:基準の定義
基準はYAMLで宣言的に定義する。
version: 1
project: my-app
max_retry: 5
criteria:
# テスト系
- metric: unit_tests.pass_rate
condition: "== 100"
action: BLOCK
- metric: mutation.score
condition: ">= 80"
action: BLOCK
message: "Mutation score must be at least 80%"
# 品質系
- metric: ai_review.high_issues
condition: "== 0"
action: BLOCK
- metric: test_validity.score
condition: ">= 70"
action: HUMAN
message: "Test validity is borderline - human review recommended"
ポイントは、判定ロジックにLLMを使わないこと。条件式は完全に決定論的。AIの出力は「入力値」として扱う。
運用で守るルール
- BLOCK系メトリクスは5〜6個まで - 多すぎると何が問題かわからなくなる
- 同一理由で3回連続BLOCKしたら設計を見直す - AIの問題ではなく基準の問題
- 本番バグ発生時は必ず基準を見直す - 学習する基準
- KPI・事業指標は判定に使わない - それはMetricShipの責務ではない
期待される効果
| 指標 | 目標 |
|---|---|
| human_decisions_per_day | ≤ 3(HUMAN判定が1日3回以下) |
| auto_fix_success_rate | ≥ 80%(BLOCK後のAI自己修正成功率) |
| escaped_bug_rate | ≤ 1%(PASSしたが本番でバグが出た割合) |
| code_review_time_reduction | ≥ 70%(人間によるコードレビュー時間削減) |
まとめ
MetricShipは「AIハーネス」論争への思想的回答ではない。実装と運用で成立する現実解だ。
人間は「コードをレビューする作業」から「基準(Criteria)を最適化する作業」へシフトする。それがAI時代の開発者の新しい役割なのかもしれない。