実践ガイド

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/5verified_candidate人のPRレビュー候補
混在fixture失敗3、成功2未実行no_verdict_non_reproducible元の失敗条件を調べる
command not found基盤エラー 5/5未実行no_verdict_infrastructure実行環境を直して最初から再試行

混在条件と基盤エラーは、変更前の再現条件を満たさないためpatch段階へ進みません。「greenが1回あったから元不具合なし」とも、「コマンドが失敗したからpatch不合格」とも数えていません。

決定的な人工不具合は変更前5回失敗・変更後5回成功、混在fixtureと基盤エラーは判定不能として停止した集計図

図は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の別展開追試です。

一次情報・出典

  1. Hugging Face:Anatomy of a bug-fixing agent
  2. Python公式:subprocess
  3. Python公式:unittest