
Summary
この文書の要点
- テスト成功、要件網羅、主要シナリオ保証、未知のバグ不在を同一視せず、修正完了にはテスト証拠の品質を要求します。
- AIが推測した期待値はprovisionalとして扱い、人間が承認するまで強制品質ゲートの証拠に昇格させません。
- retry後の成功をPASSで隠さず、結果の共通形式とfailure fingerprintによって再現可能・Flaky・環境障害を分けます。
- 証拠が不足する場合は失敗やバグゼロではなく、insufficient_test_evidenceとしてキャンペーンを保留します。
テストが緑でも、自動品質保証の精度は決まらない
AIによる不具合探索では、品質ゲートの成功がキャンペーンの完了条件になります。しかし、重要な業務シナリオがテストされていない、失敗がretryで隠れている、期待値を実装コードから推測している、テスト自体が修正と同時に弱められている場合、緑の結果は修正完了の証拠になりません。
カバレッジ率を上げても、この問題は解決しません。実行された行が増えたことと、要件の重要な分岐、権限境界、競合、異常系が検証されたことは別です。自動化の精度を上げるには、テスト結果だけでなく、何を根拠に、どの環境で、どの強さのassertionを使い、どの要件を検証したかを管理する必要があります。
そこでテストを、仕様書、センサー、証拠、停止条件の四つの役割として扱います。テストコードを増やすことより、テストが何を観測でき、どの条件で信頼でき、いつ証拠として使えないかを定義することが、この品質管理基盤の目的です。
- テスト成功は、要件網羅や未知のバグ不在を意味しない。
- テスト数とLine Coverageを、単独の品質KPIにしない。
- AIが修正できる範囲と、AIが期待値を確定できる範囲を分ける。
Requirement・Test・Environmentの三つのカタログを結ぶ
証拠品質を追跡するため、Requirement Catalog、Test Catalog、Test Environment Catalogを分けて持ちます。Requirementには重要度、承認状態、受入条件を、Testには安定したTest ID、対象要件、レベル、所有者、決定性、実行コマンド、品質ゲートを、Environmentには時刻、timezone、乱数seed、DB分離、外部通信、実行バージョンを記録します。
Test IDはファイル名や行番号ではなく、対象領域、操作、振る舞い、条件を組み合わせた安定した識別子にします。ファイル移動やテストの分割でIDが変わると、過去のFlaky履歴、failure fingerprint、Requirementとの対応が切れてしまうためです。
既存テストを最初から手作業で全登録する必要はありません。ファイル、package、test titleから暫定エントリを自動生成し、statusをdiscoveredにします。AIはdomain、severity、Requirement対応の候補を提案できますが、Critical Gateで証拠として使うのは、人間がapprovedへ昇格したテストだけです。
- Requirement Catalog: 何を保証するかと、その承認根拠。
- Test Catalog: どのテストが、どの要件を、どのゲートで検証するか。
- Environment Catalog: 同じ結果を再現するための実行条件。
- 既存Debtはdiscoveredとして可視化し、重要領域から段階的に承認する。
異なるテスト結果をCanonical Test Resultへ正規化する
Vitest、Playwright、Shell Script、Integration Test、Security Scan、Coverage、Mutation Testingは、それぞれ異なる形式で結果を出します。品質ゲートがframework固有のログを直接読むと、失敗分類と証拠項目がばらばらになります。既存Runnerへ大きく侵入せず、Reporterまたは後処理Adapterで共通形式へ正規化します。
Canonical Test Resultには、run ID、commit SHA、Test ID、Requirement ID、attempt、status、duration、environment fingerprint、failure fingerprint、expected、actual、error type、sanitize済みstack、関連ファイル、artifactを持たせます。AIへ渡す前にこの形式へ変換することで、ログ全体を送らず、必要な証拠だけで原因分析できます。
状態はPASSとFAILだけにしません。再現可能な失敗、Flaky、環境、依存サービス、timeout、assertion、setup、未承認skip、Quarantine、未実行を区別します。自動修正へ送るのは原則としてFAIL_REPRODUCIBLEだけです。環境障害をコード修正へ誤分類することを防ぎます。
- FAIL_REPRODUCIBLE: 同じ条件で同じ失敗が再現し、自動修正候補になる。
- FAIL_FLAKY: 成功と失敗が混在し、まず決定性の改善が必要。
- FAIL_ENVIRONMENT・FAIL_DEPENDENCY: ソース修正ではなく実行環境の問題として扱う。
- SKIPPED_UNAPPROVED・QUARANTINED・NOT_RUN: 成功証拠として数えない。
retryを成功変換ではなく、再現性診断に使う
Playwrightなどのretryで、1回目が失敗し2回目が成功した結果をPASSにすると、Flaky Testは緑のゲートへ隠れます。retryは不安定さを消す仕組みではなく、同じ失敗が再現するかを調べる診断手段として扱います。成功と失敗が混在すればFAIL_FLAKY、同じfingerprintで複数回失敗すればFAIL_REPRODUCIBLE候補です。
failure fingerprintは、Test ID、正規化したエラー種別、assertion message、expected、actual、主要stack frame、HTTP status、DB error codeなどから生成します。timestamp、UUID、一時パス、port、Runner ID、request ID、軽微な行番号変化は除外し、同じ原因を同じ識別子に収束させます。
環境条件もfingerprintとして保存します。同じcommit、同じTest ID、同じenvironment fingerprintで最大数回診断し、同じ失敗が繰り返されるかを見ます。コード、環境、失敗原因を別の軸で固定しなければ、再現可能性は判断できません。
時刻、乱数、DB、外部APIをテストの管理対象にする
テストの揺れはコードだけから生まれません。timezone、locale、現在時刻、乱数、DBの残存データ、test fileの実行順、port衝突、外部API、Docker image、Node、browser、CPU制約が結果へ影響します。これらを暗黙の前提にせず、Environment Catalogと結果のfingerprintへ残します。
時間依存コードにはClock injectionを使い、テスト実行時刻を固定します。ランダムな入力を使う場合はseedを必ず記録し、同じseedで失敗を再現できるようにします。DBはtransaction rollback、test fileごとのschema、runごとのdatabaseなど、規模に合う方式でテスト間の状態共有を断ちます。
外部APIは、Unit・通常IntegrationではFakeやMock Server、ContractではOpenAPIやJSON Schema、Sandboxでは限定的な疎通、Production-likeでは明示承認という階層に分けます。外部サービスの停止を、アプリケーションコードのバグとしてAIに修正させてはいけません。
- fixed clock、timezone、locale、random seedを実行結果へ記録する。
- DB、temp directory、port、test dataをrun単位で隔離する。
- 外部ネットワークをdeny-by-defaultにし、許可したテストだけ疎通する。
- Node、pnpm、browser、Docker image digest、feature flagを環境証拠に含める。
Flaky Testを隠さず、期限付きで隔離する
Flaky Testは、単なるテスト運用上のノイズではありません。AIが失敗ログをソースバグと誤認し、意味のない修正と再試行を続ける原因になります。検出したFlakyはTest IDとfingerprintで重複排除し、成功・失敗回数、環境、trace、推定原因、所有者を一つのIssueで追跡します。
すぐに直せないFlakyはQuarantineできますが、無視にはしません。隔離理由、証拠Issue、開始・期限、所有者、承認者を登録し、初期方針では期限を最大14日とします。Quarantine中も低頻度で実行し、安定した成功が続けば解除候補を作ります。
Critical Testは自動Quarantineを禁止し、期限切れQuarantineはGate failureとします。隔離中のテストは、たとえ成功してもバグゼロの証拠には数えません。RatchetではQuarantine件数の増加自体を品質悪化として検出します。
実装修正と同時に、テストが弱くなっていないかを見る
AIが修正とテストを同時に変更できると、自身のpatchを通すために期待値を緩める余地が生まれます。意図的でなくても、assertionを部分一致へ変える、retryやtimeoutを増やす、Mockを広げる、Snapshotを一括更新することで、表面上は成功する弱いテストを作れます。
PRのbaseとheadを比較するTest Change Guardを置き、assertion削除・減少、skip・todo・only、retry・timeout増加、Coverage・Mutation Score閾値低下、Snapshot大量更新、Mock・network interception拡大、エラー無視、テスト削除、exclude追加、権限・異常系テスト削除を検出します。
検出結果はLOWからCRITICALのTest Change Riskへ分類します。Critical Scenario、認証・認可、金額、状態遷移のテスト弱体化はHard Gateにし、AI生成PRでは人間のPRより厳しい閾値を使います。HIGH以上はDraft PRをneeds-humanへ移し、自動修正ループから外します。
- テストの変更行数だけでなく、assertionの意味が弱くなったかを見る。
- CIだけのskipやFeature Flagによる回避を検出する。
- Test Change RiskをAIの説明ではなく、ASTとdiffによる決定的検査から算出する。
Regression Testを修正より先に置き、独立して検証する
バグ修正では、まず不具合を再現する最小のRegression Testを作り、修正前commitで失敗することを確認します。その後に実装修正を適用し、同じテストとaffected regressionを通します。修正後の実装だけを見てテストを書くと、現在のコードを追認するテストになりやすいためです。
AIの役割もTest Architect、Test Implementer、Independent Verifierに分けます。ArchitectはRequirement、境界値、状態遷移、authorityを整理し、実装コードを変更しません。Implementerは承認済み設計からテストだけを生成します。Verifierは期待値の根拠、Mock、Flaky要因、修正前の失敗を独立に確認します。
検出力が不明な場合は、negative controlやMutation Testingを使います。実装へ意図的な小さな変異を入れてテストが落ちなければ、そのテストはコードを通過していても重要な振る舞いを観測できていません。テスト作成者と検証者を同じ判断文脈に置かないことが、AI生成テストでは特に重要です。
- 修正前に失敗し、修正後に成功することを証拠として残す。
- Regression Testを確認済みBugと承認済み期待動作へ関連付ける。
- Fixer、Test Implementer、Verifierを別Job・別プロンプトに分ける。
Coverageを多次元化し、現在値から悪化させない
品質の可視化ではLine、Branch、Functionだけでなく、Requirement、Business Scenario、State Transition、Permission Matrix、Error Path、Contract、Mutation Scoreを分けます。たとえばLine Coverageが高くても、deny testや競合テストがなければ、認可や整合性の証拠は不足しています。
既存の低い数値を一度に最低基準へ引き上げると、導入時点で全CIが止まります。現在値をBaselineとして保存し、PRで悪化した時だけ拒否するRatchetを使います。全体率に加えてChanged Code Coverageを測り、変更行へ対応するテストが一つも実行されていない場合はTest Gapとして扱います。
Mutation Testingも全リポジトリへ毎回実行しません。PRでは変更関数、夜間はaffected package、週次はCritical componentに絞ります。生き残ったmutantは機械的なテスト大量追加へつなげず、重要度を付けたTest Gapとして扱います。Mutation Scoreは検出力の一つの指標であり、単独の合否基準ではありません。
Test Evidence Gateを、バグゼロ判定の手前に置く
Test Quality Scoreは、Requirement対応、Critical Scenario、決定性、Flaky、assertion強度、Changed Code Coverage、Mutation、所有者を人が把握するには有用です。しかし平均点だけでゲートを通すと、Critical Test failureを別項目の高得点で相殺できてしまいます。スコアとHard Gateは分けます。
AIバグ探索の完了には、既存Quality Gateに加えてTest Evidence Gateを要求します。実行対象がCatalogにあり、Critical Testがapprovedで、Critical Requirementとの対応があり、未隔離Flakyと期限切れQuarantineがなく、Test Change Riskが許容範囲で、修正BugにRegression Testがあることを確認します。
さらにCanonical Test Resultとenvironment fingerprintが保存され、同じSHAでFinal Gateが2回成功し、その間にコード・テスト・設定変更がなく、provisional testだけで成功判定していないことを要求します。足りない場合はfailureでもバグゼロでもなく、insufficient_test_evidenceという状態で止めます。
- Critical failure、未承認skip、期限切れQuarantineはスコアで相殺しない。
- Requirement authority不明、Critical Scenario未実行、再現確認不足をHard Gateにする。
- Evidence不足はソースバグと区別し、テスト設計または人間承認へ戻す。
バグがゼロでも、証拠が弱ければ実行頻度を下げすぎない
適応型スケジューラが新規バグ数だけを見ると、テストが弱いため何も検出できない状態を「安定」と誤認します。頻度判断に、承認済みCritical Coverage、未対応Requirement、Flaky数、期限切れQuarantine、Evidence Gate、provisional test数、Test Change Riskを追加します。
再現可能バグがあればACTIVE、バグはないが証拠不足ならTEST_EVIDENCE_IMPROVEMENTへ遷移します。改善可能なTest Gapがあれば15〜45分間隔でCatalog、Regression Test、決定性を改善し、Critical Requirementの期待値が不明ならneeds-humanまたはテスト設計Draft PRへ移します。
証拠改善タスクがない時はSpot Runnerを起動しません。Test Evidence Gateを通過して初めてFINAL_VERIFYへ進み、同一SHAに対する時間を置いた最終検証を行います。テスト品質の低さをRunnerの大量起動で埋め合わせず、必要な証拠が何かを状態として表現します。
Observe、Ratchet、Enforceの三段階で導入する
既存テストへ最初から強制ルールを適用すると、過去からあるDebtだけでCIが止まり、品質改善基盤そのものが外されやすくなります。初期値はObserveとし、テストを自動発見し、Catalog、Flaky候補、Requirement未対応、Test Change Riskを記録します。この段階では原則としてCIを失敗させません。
次のRatchetでは現状をBaselineにし、Coverage低下、skip増加、Flaky・Quarantine増加など、品質の悪化だけを拒否します。新規Critical機能には承認済みRequirementとScenario Testを要求しても、既存Debtを一括修正させません。改善を積み上げながら、守る範囲を徐々に広げます。
Enforceは、Catalogと所有者が整ったCritical領域から限定的に有効化します。承認済みRequirement、Critical Scenario、Flaky policy、Test Change Guard、Test Evidence Gateを強制し、ReleaseではFull Test Quality Gateを要求します。modeは設定で切り替え、component単位で移行できるようにします。
- Observe: 測定とDebt可視化。既存CIを止めない。
- Ratchet: 現在値を基準に、悪化だけを禁止する。
- Enforce: 証拠が整ったCritical領域から強制する。
この品質基盤自身も、誤判定しないことを検証する
Unit TestではSchema、test discovery、stable Test ID、result normalization、fingerprint、Flaky分類、Quarantine期限、traceability、Coverage ratchet、Test Change Risk、Evidence Gate、Secret redactを検証します。特にassertion削除、skip追加、retry・timeout増加、Snapshot一括更新を検出できることを固定します。
Integration Testでは、Playwrightのretry後成功がFLAKYになること、provisional testだけではEvidence不足になること、Critical Testの自動Quarantineを拒否すること、Regression TestのないBug Fixを止めること、AIが弱めたテストをPR Gateが拒否することを確認します。
導入はtest discovery、Canonical Result、Observe report、1 workspaceのFlaky診断、1 Requirement登録、neutralなChange Guard、限定Ratchet、Evidence Gate接続、限定Enforceの順に進めます。品質管理基盤の判定ミスはAIの修正ループ全体へ波及するため、本番コードと同じか、それ以上に段階的な検証が必要です。
この設計の限界と運用上の注意
Requirement Catalogは、正しさを自動生成する仕組みではありません。承認されていない要件、部門間で解釈が違う期待動作、実際の業務と古い文書のずれは、人間が解消する必要があります。Catalogを作っただけで、要件品質が上がるわけではありません。
Test Change GuardやMutation Testingにも誤検知と見逃しがあります。assertion数が同じでも意味は弱くなり得ますし、survived mutantが必ず重大なTest Gapとは限りません。HIGH・CRITICALを機械的に全面拒否するのではなく、対象領域とauthorityに応じて人間確認へ移す設計が必要です。
Canonical Resultやartifactを詳しく残すほど調査しやすくなりますが、ログに個人情報、Token、Secret、本番データが混ざる危険も増えます。共通sanitizer、非公開GCS、保持期間、AI送信前のredactを、証拠保存と同じ設計範囲に含めます。
AIの品質保証は、テストコードより証拠の制度で決まる
自動修正基盤では、テストはAIが目標とする評価関数になります。弱いテスト、揺れるテスト、誤った期待値を与えれば、AIはその評価関数に忠実に、望ましくない方向へ最適化します。だからこそ、期待値のauthority、実行環境、再現性、要件対応、変更リスクをテストコードの外側で管理する必要があります。
重要なのは、テストを増やした件数ではありません。どの要件を、どの根拠で、どの環境において、どの強さで検証し、修正前後の差を再現できたかです。Test Evidence Gateは、その問いへ機械可読な形で答えられない限り、AIに完了を宣言させないための境界です。
AIが実装とテストを高速に生成できるほど、人間の仕事は期待値と証拠の承認へ移ります。自動化の信頼性を決めるのは、モデルが何件のテストを書けるかではなく、機械が自分に都合のよい合格条件を作れない制度を設計できるかです。


