ブランチ修正を検証する
// GUIDE · BRANCH-FIX
候補の修正を end-to-end でテストする。
Vivarium はレシピ(バグ)と比較サーフェス(あなたの修正)と、修正が壊れた code path を実際に回避したときに反転する verdict を組み合わせている。本ガイドはそのループを Layer 1(ブラウザ内)と Layer 2/3(Docker)の両方で end-to-end に歩く。
// 0 · このループの目的
AI-slop 検証 — その修正、本当に動く?
AI エージェント(Claude Code、Cursor、Cline、Continue、…)が アップストリームのバグに対する候補の修正を提案してきた。素直な問いは 「本当にバグを直したのか、それとも修正に見えるだけか?」。Vivarium の branch-fix ループはこれに 機械的な verdict 反転 で答える: 修正をレシピのランタイムに通し、バグがまだ trigger するか を見るだけ。
verdict のセマンティクス。
reproducedは 「バグが trigger する(修正は回避できていない)」、unreproducedは「バグが trigger しない(修正が壊れた code path を回避できた)」。動く修正は verdict をreproducedからunreproducedに反転させる。
2 つのパスは同じワイヤ形(Contract v1)と同じ比較サーフェス (/repro/compare)を共有する:
レシピページに代替版の再現スクリプトをペーストする。レシピの WASM ランタイム(php-wasm、ruby.wasm、Pyodide)が同じタブで再走させ、verdict をキャプチャする。CI 不要、Docker 不要。
branch-fix Docker イメージを自分でビルドして push する。GitHub Actions ワークフローがそのイメージを pull して走らせ、verdict をキャプチャ、/repro/compare が描画するアーティファクトバンドルを公開する。
Layer ディスパッチは自動 — レシピのカタログエントリが既にどの Layer
かを知っているし、MCP の verify_branch_fix ツールは両方に
正しい足場を返す。
// 1 · レシピを選ぶ
カタログでバグを探す。
既知のレシピから入るか、エラー文から入るかで 2 通り:
ギャラリーを眺めて該当レシピのページに入る。slug をメモする(例: php-12167)。
/repro/match(エラーをペーストして候補をスコア順に表示)を使うか、AI エージェントから match_error を呼ぶ(同じスコアリング、同じサーフェス)。
// 2 · PATH A — LAYER 1
レシピページに修正をペースト、verdict をキャプチャ。
Path A に opt-in した Layer 1 レシピは、ベースライン出力の下に
「Try a fix」 パネルを描画する。現時点では
php-12167
に出荷済み、形が proven したら他のレシピも続く。
ベースラインの再現が先に走り、オリジナルの verdict(典型的には reproduced)がキャプチャされたあとパネルが描画される。
3 つのモード: (a) textarea にペースト、(b) 公開取得可能 URL(raw GitHub / Gist)を指定、(c) ディスクからファイル選択。URL 取得時の CORS エラーはペーストへフォールバック。
レシピが既にロード済みの WASM ランタイムが置換ソースを実行する。パネルは Contract v1 形の verdict バンドルをキャプチャする: branch-fix-verdict.json + original-verdict.json。
パネルは side ごとに 1 つずつダウンロードリンクを描画する。両ファイルは Contract v1 に準拠 — Layer 2/3 のワークフロー実行から /repro/compare が消費するのと同じ形。
/repro/compare を開いて両方の JSON ファイルをドロップゾーンにドラッグする。ページは side-by-side で evidence を描画し、divergent なフィールドをハイライトする。
エージェント駆動のショートカット。 AI エージェントから
verify_branch_fix(slug, fix_url)を呼ぶと、ツールは?fix_url=事前読み込み済みのcompare_urlを返す — その URL を開けばレシピページが自動で fix を走らせる。verify_branch_fix(slug, fix_source)も同様 (≤4 KiB inline、長い fix はfix_url経由)。
// 3 · PATH B — LAYER 2 / 3
イメージをビルド、ワークフローを実行、アーティファクトをドロップ。
Layer 2(Docker カタログ)と Layer 3(record-replay)はブラウザ タブでは走らせられない — バグは本物の OS、本物のソケット、 本物のファイルシステムを要求する。Path B はビルドをあなた自身の インフラに移し、push したイメージに対する verdict を GitHub Actions ワークフローでキャプチャする。
AI の候補修正を fork に当て、レシピの Dockerfile で Docker イメージをビルドし、GitHub Actions ランナーが pull できるレジストリ(公開 ghcr.io / Docker Hub repo)に push する。image-as-input の境界は contract — Vivarium はあなたのソースをビルドしない、あなたが行う。
gh workflow run branch-fix-verdict.yml --repo aletheia-works/vivarium -f slug=<slug> -f branch_image=<your-image-ref> を実行する。ワークフローはあなたのイメージを pull、その中で再現を走らせ、Contract v1 verdict をキャプチャ、branch-fix-verdict-<slug>-<run_id> をアーティファクトとしてアップロードする。
ワークフロー実行ページからアーティファクト zip を取得する。branch-fix-verdict.json と(デプロイ済みスナップショットがあれば)original-verdict.json が含まれる。
ページは zip をクライアントサイドでパースし、両 verdict を Contract v1 スキーマに照らして検証、side-by-side で evidence を描画する。
エージェント駆動のショートカット。 Layer 2/3 の slug に対する
verify_branch_fix(slug)は、コピペ可能なgh_commandを返す。エージェントが実際にそれを走らせるにはgh認証とレジストリアクセスが必要 — だがコマンド自体は 組み立て済み。
// 4 · VERDICT を読む
ここでの `reproduced` と `unreproduced` の意味。
branch-fix の verdict が伝えるのはちょうど 1 つ: 修正が当たった状態で バグは trigger したか?
オリジナル =
reproduced、branch-fix =unreproduced。 修正がバグを回避した。典型的な 「修正が動いた」結果。オリジナル =
reproduced、branch-fix =reproduced。 修正は slop — outcome を変えていない。 修正を反復して再走させる。オリジナル =
unreproduced、branch-fix =reproduced。 リグレッション — あなたの変更が バグを導入した。diff を巻き戻すか、何を変えたかを確認する。オリジナル =
unreproduced、branch-fix =unreproduced。 どちらも変化なし。レシピが 非アクティブ(アップストリームが既に修正済み)か、現在のランタイム に対してあなたの修正が no-op。
Path A は ユーザーランド の修正が壊れた code path を回避するか をテストする — アップストリームのインタプリタにパッチを当てる わけではない。Path B は 完全に再ビルドしたイメージ がバイナリレベルでバグを直すかをテストする。重みは違う、ワイヤ形は同じ。
// 5 · エッジケース
ループが計画通り進まないとき。
ランタイムが verdict 生成前にエラーした。 Path A ではパネルのステータスラインが赤になり、ランタイムエラー テキストが出る。よくある原因: ペーストした修正の構文エラー。 構文を直して Run をもう一度クリック。
スキーマが verdict 形を reject した。 /repro/compare は各 verdict を
verdict.schema.json(Contract v1 rev3)に照らして検証する。エラーリージョンは bad フィールドの JSON path を表示する。Path A は構造的に well-formed な verdict を生成するので、これが見えるなら 手編集ファイルをドロップしている可能性が高い。CORS で URL 取得がブロックされた。 公開 raw GitHub と Gist URL は CORS 対応ヘッダを返す。セルフホスト URL はしばしば 返さない。その場合はペーストモードへフォールバック。
Path B のプライベートレジストリイメージ。 ランナーは認証なしで pull する。プライベートレジストリは v1 の スコープ外。public ref に push するか、pull 認証情報フォロー アップを待つ。
修正が長すぎて
?fix=に入らない。 インライン URL パラメータの上限は 4 KiB。Gist や fork URL を 使った?fix_url=を使う。
// 6 · この先
このループの上に組めるエージェントループ。
パイプライン: match_error → slug に絞り込み → verify_branch_fix → compare_url を開く → verdict を読む。reproduced ならエージェントに別の候補を頼む。unreproduced ならマージへ。
カタログにまだあなたのバグが無いなら、レシピを書く。Path A は 1 行で opt-in できる。
自分のリポジトリに統合する パスは push のたびに verdict ドリフトを検出する — オンデマンドだけではない。
どこかで詰まったら、それは本ガイドのバグです。slug、Layer、path(A か B)、 詰まったステップ番号を Issue に残してください。