patchをPR前に検証:変更前5回・変更後5回と判定不能の分離

AIコーディング支援が作ったpatchでテストが1回greenになっても、元の不具合が最初から再現していなければ、patchが直したとは判断できません。また、テストコマンドを起動できなかった結果を「patchが失敗した」と数えるのも別の誤りです。
そこで、標準Pythonだけの人工fixtureを作り、変更前5回すべてで同じ失敗を確認し、変更後5回すべてが成功した場合だけ verified_candidate にする小さな判定器を実行しました。タイトルの「5回」は、変更前5回と変更後5回の一組を意味します。
2026年10月2日の結果は、決定的な人工不具合が変更前5/5失敗・変更後5/5成功でした。一方、3失敗・2成功の混在fixtureは no_verdict_non_reproducible、存在しないコマンドを5回起動した条件は no_verdict_infrastructure で停止しました。verified_candidate は人がPRレビューへ進める候補という意味で、マージ可、安全、AIが正しいpatchを作ったという判定ではありません。
Sergeの公式説明と、今回の人工検証を分ける
Hugging Faceの一次記事は2026年9月29日公開です。同記事によると、Sergeは失敗の選別、再現、重複対応の確認、patch生成、検証、PR作成の6段階を通り、検証では対象テストを変更前と変更後に各5回実行します。runnerの異常終了、patch適用失敗、timeoutなどはpatchの良否ではなく基盤・検証上の失敗として扱い、PRを開かないと説明されています。
記事にある約80日で29件merge、直近2週間の86 LLM sessionで約250米ドル、distinct PR当たり約14米ドル、merge当たり約43米ドルという値は、Hugging Faceの特定期間・構成における自己報告です。今回の再現結果でも一般的な費用相場でもありません。本稿ではSerge、Transformers、GPU、Kubernetes、LLMを動かさず、同社の性能を評価していません。
今回取り出したのは「元の失敗を先に反復する」「前後を同じ回数で比べる」「実行不能をpatch評価から外す」という検証の境界だけです。人工patchは手書きで、AIに生成させていません。
検証条件:整数3個だけの人工不具合
環境はWindows、PowerShell、Python 3.14.2です。subprocess と unittest を含む標準ライブラリだけを使い、追加費用は0円でした。外部通信、API、credential、実リポジトリは使っていません。
fixtureは [2, 3, 4] の合計を9と照合します。変更前の関数だけは先頭要素を落として7を返し、失敗識別子 ASSERT_SUM_EXPECTED_9 を出します。変更後は3要素をすべて合計します。実サービスの障害ではなく、比較条件を固定するための小さな架空例です。
混在条件も乱数や時刻を使いません。実行番号1・3・5は失敗、2・4は成功する固定パターンで、flaky testに似た「観測が揃わない状態」だけを作りました。したがって、一般のflaky testを検出・解消できたという結果ではありません。
3条件を同じ表へ混ぜない
| 人工条件 | 変更前 | 変更後 | 判定 | 次の扱い |
|---|---|---|---|---|
| 決定的な不具合 | 同じ対象失敗 5/5 | 成功 5/5 | verified_candidate | 人のPRレビュー候補 |
| 混在fixture | 失敗3、成功2 | 未実行 | no_verdict_non_reproducible | 元の失敗条件を調べる |
| command not found | 基盤エラー 5/5 | 未実行 | no_verdict_infrastructure | 実行環境を直して最初から再試行 |
混在条件と基盤エラーは、変更前の再現条件を満たさないためpatch段階へ進みません。「greenが1回あったから元不具合なし」とも、「コマンドが失敗したからpatch不合格」とも数えていません。
図はJSON結果を読みやすく再構成したもので、端末画面のスクリーンショットではありません。各runのコマンド、対象版、exit code、失敗識別子、標準出力・標準エラーのSHA-256、停止理由は配布物の results.json にあります。
判定器はexit codeと失敗識別子を分ける
Python公式のsubprocess資料に沿って子プロセスのexit codeと出力を取得し、起動できない FileNotFoundError とtimeoutを別扱いにしました。中心の判定は次の順です。
if before["infrastructure_error"] or after["infrastructure_error"]:
return "no_verdict_infrastructure"
if before["target_failure"] != 5:
return "no_verdict_non_reproducible"
if after["pass"] != 5:
return "verification_failed"
return "verified_candidate"
単にexit 1だったrunは対象テストの失敗と決めません。標準出力に固定した失敗識別子がなければ unexpected_process_failure として基盤エラー側へ送ります。今回の有限なfixtureでは、別のテスト失敗や収集プログラムのクラッシュを「元不具合の再現」に混ぜないための境界です。
verification_failed は、変更前5/5失敗を満たしたあと、変更後5/5成功にならない場合の分類としてコードに残しました。今回用意した公開3条件では発生させず、13テストにもこの分岐単独の確認は含めていないため、表の実測件数には加えていません。
ZIPを別フォルダへ展開して追試する
判定器・人工fixture・13テスト・実測JSONをまとめたZIPを新しいフォルダへ展開し、READMEどおり次を実行します。
python -m unittest discover -s . -p 'test_*.py' -v
python .\patch_verifier.py --output results.json
Python公式のunittest資料のdiscoveryを使っています。元フォルダでは13テストを3回連続で実行し、各回13件成功・exit 0でした。公開ZIPも別の一時フォルダへ展開し、13件成功、判定器exit 0、同梱 results.json と再生成結果の一致を確認しました。
ZIPは10エントリ、8,507 bytes、SHA-256は D42200ADA79252C542D87236D49292F2FFB318E3713E539212F29FD05E64F765 です。内部の9ファイルは HASHES.sha256 でも照合できます。ハッシュ一致は同じ配布物であることの確認で、コードの正しさや安全性の証明ではありません。
制作途中で結果保存とZIP作成が2回止まった
最初の結果保存では、指定した raw/results.json の親フォルダを判定器が作らず、FileNotFoundError になりました。判定run自体は終わっていましたが、証拠を保存できなければ追試可能な成果物になりません。出力先の親フォルダを作る処理と、その動作を確認する13件目のテストを追加しました。
次のZIP作成では、実測JSONを raw/results.json に置いた一方、作成スクリプトが直下の results.json を探して停止しました。配布ZIPは生成されていません。入力元とZIP内の名前を別に指定できるよう直し、再作成後に10エントリ、内部hash、別展開テストを確認しました。期待値を実結果へ合わせる変更はしていません。
初回エラーの端末出力にはローカル絶対パスが含まれたため、その生ログは公開していません。原因、修正、追加テストを記録し、公開JSON・ZIP・画像から絶対パスとユーザー名を除きました。
限界:5回は正しさの証明ではない
5回という回数はSergeの公式説明を参考に今回の人工比較へ採用した条件で、統計的に十分な一般基準だとは検証していません。決定的な小さな関数で10回揃っても、別入力、回帰、性能、OS差、依存関係、競合、ネットワーク、権限、悪意ある変更は分かりません。
また、同じ対象テストだけをgreenにする変更は、要件を壊していても通る場合があります。今回は和の期待値を固定した1条件しかなく、仕様の正しさ、テスト自体の妥当性、patch差分のレビュー、全体回帰は別の確認です。実リポジトリでは対象テストに加え、関連テスト、lint、型検査、build、セキュリティ確認を選び、人が差分と要件を照合する必要があります。
実務への影響と次の行動:失敗・成功・実行不能を先に命名する
自分のリポジトリへ応用する前に、対象テストの名前、元の失敗識別子、反復回数、変更前に必要な失敗数、変更後に必要な成功数を固定します。次に、timeout、runner停止、依存取得失敗、patch適用失敗をpatchの結果とは別の no_verdict として記録します。条件を満たしたものも自動mergeせず、PR候補として人のレビューへ渡します。
本稿はpatch適用後・PR前の反証テストを扱います。ツールがファイル変更や外部送信を行う実行前に、宣言した操作とpolicyを照合したい場合は、AIエージェントの操作計画を実行前に検査する記事が関連します。runをまたいで訂正済みの値を古い入力から守る場合は、締切の訂正台帳を18テストした記事を参照できます。
表紙は検証ファネルを表す概念画像で、実行結果の証拠には数えていません。独自証拠は人工3条件のJSON、13テストの3回連続実行、公開ZIPの別展開追試です。