受け入れ検証の手順:AIがコードを書いた後、自らブラウザを開いて操作し、画面表示を検証して証拠を提出させる

変更点の確認からアプリ起動、操作、保存、再読み込み、修正・再テスト、証拠提出まで、一連の検証をAgentに委ねる。プロジェクトルール、Skill、任意ツールで再現可能な仕組みを作る。

著者:freeCodeCamp · Timothy Ogbemudia10-21 · 約 20 分

これは、AIコーディングエージェント(Coding Agent)がフロントエンドのコードを書いた後、実際のユーザーと同じ動線でブラウザを開き、クリック・入力・保存・再読み込みを行い、機能と画面表示を確認して再検証可能な証拠を残すための一連の受け入れプロセスです。

開発者の Timothy Ogbemudia 氏は、まずこれらの要求事項をプロジェクトルールと再利用可能な Skill としてまとめ、その後、検証の実行とレポート整理を行うオープンソースツール katalepsis を開発しました。前半で紹介する受け入れ手順は、ツールなしで単独で運用することも可能です。

Claude Code、Codex、Cursor などでウェブ画面を改修する際、こんな経験はないでしょうか。Agent が「実装完了、テストはすべて通過しました」と報告してきたので実際に画面を開いてみると、絞り込みをかけても古いデータが表示されたままだったり、設定を変更してページを再読み込みしたら元の値に戻ってしまったり、モーダル内の保存ボタンがスマートフォンの画面外にはみ出して押せなくなっていたりするケースです。

こうなると、開発者が自ら画面を操作し、スクリーンショットを撮り、エラーログをコピーして貼り付け、AI が修正するのを待ち、再び画面を開いて確認するという作業を繰り返すことになります。コードの生成自体は速くなったものの、検証のフェーズでは人間が伝言役として立ち回らざるを得ません。

Ogbemudia 氏が補おうとしたのは、まさにこのフィードバックループです。Agent がコードを修正したら、まず既存のチェックを走らせ、次にアプリを起動して改修したばかりの機能を実際に使ってみる。問題が見つかれば修正し、修正後に再度操作して確認する。既存のテストはそれがカバーするシナリオを担保し、ブラウザによる受け入れ検証は今回の改修箇所を起点として実際の稼働状態を検査します。この2つが組み合わさることで、「テストはすべてグリーンなのに、画面が壊れている」という事態を防ぎやすくなります。

受け入れ検証の流れ:設定変更・保存・再読み込み

ここでは理解を助けるためのオリジナル解説用シナリオを用います。設定画面に「表示名」と「週報通知」の保存機能を追加した場面を想定します。登場するルーティング、フィールド名、後述のレポートはすべて解説用のものであり、今回実際に稼働させたプロジェクトではありません。

1. 要件と差分を確認し、何を検証すべきか特定する

Agent はまず git status --short を確認し、既存の変更や未追跡ファイルを把握します。そのうえで、タスクが関係するコミット、未コミットの変更、新規ファイルを含む今回の実際のコード差分と要件を突き合わせます。単一の git diff HEAD だけでタスク全体の変更を網羅できていると思い込んではいけません。

今回の変更が設定画面と保存用 API に及んでいる場合、受け入れ計画には具体的な検証目標を落とし込みます:

  • /settings/profile を開き、名前とトグルスイッチの現在の状態を記録する。
  • 初期状態とは異なる新しい値に変更し、保存をクリックした後の結果を確認する。
  • ページを再読み込みし、データの読み込みが完了するのを待ってから、名前とスイッチの現在の値を再度読み取る。
  • 保存処理中の操作不能(disabled)状態が正しく解除されるか、保存失敗時にエラー表示が出るかを確認する。空データの入力や隣接する動線に影響がある場合はそれらも対象に含める。
  • 対象となるデスクトップ画面および狭小ビューポートでレイアウトを確認し、保存リクエストの成否と今回の一連の操作に伴う新規エラーログの有無を照合する。

ブラウザを起動する前に、プロジェクトに既存の関連テスト、および今回の変更に必要なビルドや型チェックを実行し、その結果を記録しておきます。ブラウザ検証は、これらのチェックでは拾いきれなかった疑問を埋めるために行います。

2. プロジェクト既存の方法で起動し、自身が作成したリソースを記録する

まず package.json の dev スクリプトを確認し、続いて README のローカル起動手順を参照します。コンテナが必須の場合は、プロジェクト既存の Docker 設定を使用します。実行コマンド、ポート番号、依存関係はすべて対象プロジェクトの流儀に合わせます。

起動時にはログを保存し、自身が立ち上げたプロセス PID、コンテナ、一時ディレクトリを記録します。サービスの準備が整うまで待機し、待機時間には必ずタイムアウトを設けます。起動に失敗した場合はログを確認して不足している前提条件を報告し、単にプロセスが作られたというだけでアプリが利用可能になったと判断してはなりません。

検証対象は、リリース準備が整った実際のアプリケーションです。ログインを簡単にするために認証を無効化したり、.env を書き換えたり、勝手なモックサービスをでっち上げたり、本来のフローを迂回する一時的なルーティングを追加したりしてはいけません。ドキュメントに記載されたテスト用アカウントやシードデータを使ってもログインできない場合は、ブロックされた旨とその原因を報告します。

3. 変更・保存・再読み込みを行い、最終状態を照合する

ブラウザの通信経路が開通したら(具体的な接続方法は後述)、Agent は対象のルーティングを開きます。この例では、初期の表示名が「元の名前」、週報スイッチがオフであると仮定します:

  1. 初期値を確認したうえで、名前を「小互」に変更し、スイッチをオンに切り替える。
  2. 保存をクリックし、成功メッセージの表示、ボタンの状態、関連リクエストの成功を確認する。
  3. ページを再読み込みし、設定データの再読み込み完了を待つ。
  4. 入力欄の現在値を読み取り、依然として「小互」になっているか確認する。スイッチの状態を読み取り、オンのままであるか確認する。

ここで読み取るべきは稼働中のコントロールの状態であり、入力欄の初期 HTML に書かれた value 属性だけを読んでも意味がありません。DOM はブラウザ内の要素構造であり、これやアクセシビリティスナップショットを介してコントロールを特定し、状態を取得できます。Playwright テストとして書く場合、名前は toHaveValue("小互") で確認でき、本物のトグルやチェックボックスなら toBeChecked() で検証できます。汎用的なブラウザツールを使う場合も、再読み込み後の新しいページから該当する状態を読み取る必要があります。

この一連の確認によって、「今回値を変更し、保存し、再読み込みした後の結果」が初めて実証されます。「ボタンをクリックした」ことや「成功トーストが一瞬出た」ことだけで最後の確認を省くことはできません。Playwright のウェブアサーションはタイムアウトまで条件を再試行するため、画面の非同期更新を待つのに適しています。ただし、設定するアサーション自体が実際のユーザー要件に対応している必要があります。

4. 機能は状態、配置は画面、API はレスポンスを見る

証拠の種類によって答えられる疑問は異なります:

  • 機能状態:DOM やアクセシビリティスナップショットを読み取り、名称、選択状態、ボタンの無効化、モーダルの開閉などを照合する。
  • 画面表示:例えば 375×812 と 1440×900 の2種類のビューポートでスクリーンショットを撮り、ボタンの見切れ、モーダルの枠外はみ出し、文字の重なりがないかを確認する。これらはブラウザのビューポート検証であり、実機スマートフォンでの検証と完全に同一ではありません。
  • API と新規エラー:保存処理が API に依存している場合、該当するリクエストのレスポンスや、今回の操作によって生じたコンソールの新規エラーを確認する。画面上に成功と表示されていても、裏で API が HTTP 500 を返しているなら報告が必要です。

スクリーンショットは要素の重なりを示せますが、それ単体で再読み込み後のフィールド値が正しいことの証明にはなりません。DOM にモーダルが存在するからといって、画面上で正しく操作できるとも限りません。検証スコープは今回の変更周辺に集中させます。過去からある警告や無関係なリクエストを勝手に別のバグ修正タスクに広げるべきではありませんし、フロントエンドのみの表示切り替えに対して不要な全ネットワーク監査を行う必要もありません。

同様の考え方は、絞り込みやリセット機能の検証にも当てはまります。Ogbemudia 氏が freeCodeCamp の記事で挙げた絞り込みの検証計画は次のようなものです。初期の行数を記録し、「Active」を選択し、さらに「Owner」を重ねがけし、条件をクリアした後に元の件数に戻るか確認する。そのうえでデフォルト以外の絞り込みを再設定し、URL や再読み込み後の条件・表示結果が維持されているかを確認する。最初から何の絞り込みもかかっていない状態で Reset を押し、「リセット機能は正常」と主張してはなりません。

5. 問題を見つけたら修正し、同じ動線を再検証する

もし再読み込み後に名前が元の表示に戻ってしまった場合は、その時点のリクエスト、状態、エラーログを保全し、コードに戻って原因を調査します。修正が完了したら影響を受けたテストを再実行し、再び「変更→保存→再読み込み→現在値の確認」というプロセスを通しでやり直します。コードを修正しただけで「これで直ったはずです」と報告を終わらせてはなりません。

プロジェクトで定められたテスト用ログイン情報、マイグレーション、プレビュー環境が利用できない場合は、検証が阻まれる(blocked)ことがあります。一方、すでに実行されたビジネスフローが本物の外部依存 API の障害によって失敗した場合は、実際の失敗内容とその原因を記録に残すべきであり、問題が現在のソースコード内にないからといって勝手に blocked に書き換えてはなりません。

6. 成果物の証拠を残し、自身が作成したリソースのみを片付ける

成功、失敗、あるいは途中でブロックされたかどうかにかかわらず、今回のために作成したブラウザを閉じ、自ら立ち上げたサービスやコンテナを停止し、提出不要な一時ファイルを削除します。起動時に記録したリソース情報に基づいてクリーンアップを行い、pkill node や git clean -fd といった大雑把なコマンドで他の作業プロセスを巻き込まないようにします。

改めて git status --short を確認します。元の変更や未追跡ファイルが保持されていること、今回の修正が説明可能であること、一時的な QA 成果物がコードコミットに混ざっていないことを確かめます。レポートとして提出するスクリーンショットやトレースファイルは別途保存し、その保存場所を明記します。不具合修正によってソースコード自体が変更されることはあるため、作業ツリーを開始時と完全に一致させる必要はありません。ただし、受け入れ検証の手順そのものが無断でのコミットやプッシュを許可するわけではありません。

7. 何を実施し、何を目撃したかを提出する

最終的な成果物として、確認したルーティング、実行した操作、期待された結果と実際の結果、証拠の保存場所、関連テストの実行結果、修正および再検証の内容、そしてブロックされた項目や未検証項目を報告します。これを受け取った開発者は、「結局どこをテストしたのか」をわざわざ聞き直すことなく、検証結果を再確認できるようになります。

再確認可能な提出レポートの例

以下は前述のシナリオに対応した解説用のサンプルです。今回生成された実際のテスト結果や証拠ファイルではありません:

### 設定画面の受け入れ検証レポート(解説用サンプル)

- 環境:ローカル開発アプリ、対象ルート /settings/profile。
- 初期状態:表示名「元の名前」、週報通知はオフ。
- 事前検証:プロジェクト既存の設定画面関連テストを実行済み、すべて合格。

操作と結果:
1. 名前を「小互」に変更し、週報をオンにして保存をクリック。
   期待値:成功メッセージが表示され、保存ボタンが再度操作可能になること。
   実績値:期待どおり。保存リクエストは成功。
2. 再読み込みし、データのロード完了を待機。
   期待値:現在の名前が「小互」のままであり、週報もオンのままであること。
   実績値:入力欄の現在値とスイッチの状態を読み取り、期待どおりであることを確認。
3. デスクトップおよび狭小ビューポートを確認。
   期待値:ボタンが欠けずに表示され、ラベルの重なりがないこと。
   実績値:期待どおり。今回の一連の操作による新規の関連コンソールエラーなし。

発見事項と再検証:
- 初回検証時、保存後にボタンが無効化されたままになる不具合を発見。
- 送信ステータスの復帰ロジックを修正し、関連テストを再実行。そのうえで再度「操作→保存→再読み込み」を一通り実施し、合格を確認。

証拠(サンプルパス):
- evidence/settings-desktop.png:デスクトップ画面のキャプチャ。
- evidence/settings-mobile.png:狭小画面のレイアウトキャプチャ。
- evidence/settings-actions.log:操作、フィールド状態、リクエスト、エラーのログ。

未テスト/ブロック:実機スマートフォンは未テスト。今回ブロックされた項目はなし。
クリーンアップ:自身が作成したブラウザ、サービス、一時資材は停止・削除済み。上記の証拠は保持。
ワークスペース:元からあった変更と未追跡ファイルは保持。今回の修正内容は説明済み、不要なQA生成物がコミットに混入していないことを確認。

同じ改修でも、どんな証拠では不十分か?

以下は解説用のサンプルです。タスクを切り替えて、既存テスト・単なるページ読み込み・結果検証のそれぞれが何を証明できるかを比較します。

選んだ絞り込み条件が URL パラメータに書き込まれ、再読み込み後も有効なままであること。

一覧ページを開き、ステータスコード 200、エラーなし、検証合格。

既存の単体テスト
この経路をカバーしていない可能性あり

状態管理関数をテストしただけでは、URL パラメータと画面の同期不備を見落とす可能性があります。

ページを開いただけ
ページが開けたことしか証明していない

絞り込み条件を変更しておらず、再読み込み後の状態も確認していません。

本来検証すべき結果
タスクに応じた実証

絞り込み条件を変更して結果一覧を確認し、パラメータ付き URL を再読み込みして、絞り込み状態と結果がともに保持されていることを確認する。

プロジェクトへの定着:短いルール、オンデマンドSkill、独立レビュー

毎回人間が長文のプロンプトを再送していると、手順の抜け漏れが生じやすくなります。Ogbemudia 氏のアプローチは、完了基準をプロジェクトルールに明記し、具体的な操作手順は再利用可能なスキルとして切り出すというものでした。

常駐ルールで「完了の定義」を示し、Skill で「検証手順」を示す

対応する規約をサポートしているクライアントでは、AGENTS.md(Codex などで使用)や CLAUDE.md(Claude Code で使用)が、クライアントやディレクトリのスコープに応じて作業指示として読み込まれます。ここに膨大なブラウザ操作マニュアルを丸ごと入れてしまうと、ブラウザを必要としないタスクにまで余計なコンテキストを背負わせることになります。

Skill は SKILL.md を含むディレクトリであり、ファイルの先頭に name や description などのメタデータを備えています。スキル対応クライアントは通常、まず簡潔な説明文だけを把握し、タスクがマッチしたときや明示的に呼び出されたときに詳細な手順を読み込みます。そのため、以下のように役割を分担させます:

  • プロジェクトルール:ユーザーに見える挙動は稼働中のアプリで検証すること、および参照すべき Skill を指定する。
  • browser-review Skill:差分の確認から起動、操作、証拠収集、修正、クリーンアップに至る完全な手順を定義する。

これらのファイルは Agent に対する作業指示書であり、実際の読み込み方式はクライアントのサポート状況に依存します。原著者は Codex と Claude Code の両方を使用し、Claude の @ インポート構文を介してルールを共有していました。これは設定手法の一例であり、すべてのクライアントで自動的に共有されるわけではありません。

AGENTS.md をサポートしている環境では、以下のカスタムルールを追加できます。Claude Code を使用している場合は CLAUDE.md に直接記述するか、サポートされている形式でインポートします:

## 画面検証ルール

ユーザーに見える挙動の変更は、完了を宣言する前に、実際に稼働しているアプリ上で影響を受ける動線を検証すること。
具体的な手順は `.agents/skills/browser-review/SKILL.md` に従うこと。
ビルド、型チェック、既存テストの結果は、今回の実画面での検証結果と併せて報告すること。
完了できなかった手順はブロックされた理由を明記すること。実施していない検証は未テストとして扱い、合格とみなしてはならない。
自身が作成したリソースのみをクリーンアップし、引き渡し用の証拠を保持すること。一時的なQA生成物をコミットに混入させてはならない。

別の Agent に検証対象を独自に判断させる

コードの実装者は自分が何を変更しようとしたかを把握しているため、自らが置いた仮定に沿ってセルフチェックを設計してしまいがちです。そのため原著者は、重要な変更については実装セッションの文脈を引き継いでいない独立したレビュアー Agent に委ねることを推奨しています。元の要件定義、実際の diff、そして受け入れ Skill だけを渡し、実装者が残したスクリーンショットや QA メモ、合格判定に頼ることなく、独自に影響範囲を判断させます。

原文には非常に具体的な例が挙げられています。要件は「E2E テストが E2E_BASE_URL 経由でアプリのアドレスを設定できるようにすること」であり、実装者はデフォルトの 8080 ポートでテストを実行して合格させました。しかし、e2e/global-setup.ts 内にあるシード用画像の URL には、http://localhost:8080/test-media/... というハードコードが残ったままでした。

独立したレビュアー Agent は 8081 ポートでアプリを起動し、旧ポートへ向けて送られた3つの画像リクエストの失敗と、画面上にフォールバック用のカードが3枚表示されているのを発見しました。「カードが存在すること」だけを確認していれば合格になってしまいます。フォールバックカードもカードの一種だからです。正しい修正方法は、設定されたベースアドレスから画像 URL を生成するか、適切な相対パスを使用することでした。

この事例が示しているのは、別の検証スコープを持った視点が、デフォルトの経路で覆い隠されていた問題点をいかにあぶり出すかということです。もちろん、2人目の Agent がすべての欠陥を見つけ出せるという保証はありません。

再利用可能な検証スキル:browser-review Skill

以下は内容が同等な2つの標準化プロセス定義です。日本語版と英語版を用意しています。両方を同時に使う必要はなく、チームの作業言語に合わせてどちらか一方を選択して使用してください。

利用する際は、プロジェクトのルートディレクトリに .agents/skills/browser-review/SKILL.md を作成し、対応する内容をそのまま保存します(冒頭の YAML メタデータも含めて保持してください)。

日本語訳(本ガイド作成の中国語版に基づく)

---
name: browser-review
description: 今回のユーザーに見える変更が実際のアプリでどう動作するかを検証し、レビュー可能な証拠を提出する。
---

現在のタスクにおけるユーザーに見える変更に対してブラウザ受け入れ検証を実施してください:
1. 最初の要求と実際の git diff(今回の未コミットの変更および新規ファイルを含む)を読み、git status --short にある既存の作業内容を記録する。影響を受けるルート、関連するローディング/空データ/エラー/選択/無効化の各状態、隣接する動線、操作、期待される結果および検証方法を洗い出す。プロジェクトに既存の関連テストと、必要なビルドまたは型チェックをまず実行し、結果を記録する。
2. プロジェクトの dev スクリプト、README、または既存の Docker 設定に従ってアプリケーションを起動する。制限時間を設けてサービスの待機状態を確認した後にブラウザを開く。起動失敗時はログを保持して報告する。自身が起動したプロセス、コンテナ、ブラウザ、一時ファイルのみを追跡・記録する。検証を容易にする目的で認証を無効化したり、.env を書き換えたり、モックサービスを捏造したり、実際のフローを迂回するルートを追加したりしてはならない。
3. 影響を受ける一連のユーザー動線を通しで実行し、最終結果を確認する。保存時は初期値を記録したうえで異なる目標値に変更し、再読み込み後に現在の値を読み取って永続化を確認する。リセット時は事前に条件を変更したうえで、入力と結果がともに復元されるかを確認する。単にページが読み込めた、あるいはクリック時にエラーが出なかったことだけをもって成功とみなしてはならない。
4. 機能状態は DOM やアクセシビリティスナップショットを読み取って確認し、レイアウトの問題は該当するデスクトップおよび狭小ビューポートのスクリーンショットを撮って確認する。API を伴う場合は、関連するレスポンスと新規エラーを検査する。過去の警告や無関係なネットワークリクエストに今回の検証スコープを拡大してはならない。
5. 不具合を発見した場合は最小限の原因を修正し、関連テストを再実行したうえで、影響を受ける動線を再度通して検証する。ドキュメントに記載された認証情報の欠落やプレビュー環境自体の到達不能といった環境起因の障害は blocked と報告する。実行されたプロダクトフローが実依存 API の障害により失敗した場合は、失敗とその原因をそのまま残す。実施しなかった検証は未テストとして理由を明記する。
6. 成否にかかわらず、自身が起動・作成したプロセス、コンテナ、ブラウザ、一時的な資材のみをクリーンアップし、引き渡しに必要な証拠は保持する。再度 git status --short を確認し、既存の変更や未追跡ファイルを確実に残す。git add、commit、push、またはグローバルなクリーンアップコマンドを実行してはならない。
7. レポートには、対象ルート、実施した操作、期待される結果と実際の結果、対応する証拠、関連テストの結果、修正内容、blocked/未テスト項目、クリーンアップ状況を記載する。証拠のない検証を合格と主張してはならない。

英語版(本ガイド作成の原文)

---
name: browser-review
description: Verify this task's user-visible changes in the running app and report reviewable evidence.
---

Perform browser verification for this task's user-visible changes:
1. Read the original request and actual git diff, including this task's uncommitted changes and new files. Record pre-existing work with git status --short. Plan affected routes, relevant loading/empty/error/selected/disabled states, nearby flows, actions, expected outcomes and how to check them. Run existing relevant tests and any necessary build or type checks first, and record results.
2. Start the real app using its dev script, README or existing Docker configuration. Wait for readiness with a time limit; retain logs and report startup failure. Track only the processes, containers, browsers and temporary files you create. Do not disable authentication, edit .env, invent a fake service or add a bypass route to make verification easier.
3. Exercise each affected user flow and check the final result. Before saving, record initial values and set distinct target values; reload and read current values to check persistence. Before resetting, change the state, then check both inputs and results. Page loading or a click without an error is insufficient.
4. Use DOM/accessibility state for functional checks and relevant desktop/narrow viewport screenshots for layout. Inspect relevant responses and new errors when the flow uses an API. Do not expand scope to old warnings or unrelated requests.
5. Fix the smallest cause of a defect, rerun relevant tests and repeat the affected flow. Report missing documented credentials or an unreachable preview server as blocked. If an executed product flow fails because a real dependency API fails, keep the failure and its cause. List checks not performed and why.
6. Clean up your own processes, containers, browsers and temporary files on success or failure; preserve evidence needed for hand-off. Check git status --short again and preserve pre-existing edits and untracked files. Do not run git add, commit, push or global cleanup commands.
7. Report routes, actions, expected versus actual outcomes, supporting evidence, test results, fixes, blocked/untested items and cleanup. Do not claim a pass without evidence.

Agent にブラウザを接続する:Playwright MCP または CLI + Skill

上記のプロセスを実行するには、実際に操作可能なブラウザ接続経路が必要です。Playwright はブラウザを自動制御し、要素の特定や状態確認を行えるツールです。一方 MCP は、Agent が外部ツールを呼び出すための標準プロトコルです。Playwright MCP は、これらのブラウザ操作を Agent に提供するための仕組みであり、一般的なブラウザ拡張機能ではありません。

マイクロソフトのリポジトリには Playwright CLI + Skill というアプローチも記載されています。これはコマンドラインとオンデマンドの指示書によってブラウザを制御するもので、コーディングタスクにおけるコンテキスト消費量を抑えたい場合に適しています。一方 MCP は、持続的なツールインターフェースとブラウザコンテキストを提供します。どちらのアプローチを採用するにしても、前述の受け入れルールが必要であることに変わりはありません。

元記事の環境は Node.js 20+ を使用しており、確認時点での Playwright MCP 公式 README の最小要件は Node.js 18+ です。後述の katalepsis もインストールする場合は、Node.js 22.12+ が必要となります。

使用するクライアントに合わせて登録コマンドを選択します。Claude Code の場合:

claude mcp add playwright npx @playwright/mcp@latest

Codex の場合:

codex mcp add playwright npx "@playwright/mcp@latest"

クライアントの接続をリロードし、ブラウザツールが認識されていることを確認します。その後、Agent に TodoMVC デモページ を開かせ、「Browser QA」というタスクを追加してリスト上に現れるか確認させます。この小さなテストは接続経路の動作確認用であり、プロジェクト固有の受け入れ検証の代わりになるものではありません。

Agent はどのように操作対象の要素を見つけるか?

Playwright MCP はアクセシビリティスナップショットを返すことができます。これはページを button "保存"、textbox "メールアドレス" のようにロール、名前、状態を備えた構造として整理し、各要素に参照番号 ref を付与したものです。Agent はこのスナップショットを手がかりに操作対象を探すため、勘で CSS セレクタを推測する必要がありません。画面が遷移した後は再取得が必要であり、記事に書かれたサンプルの番号をそのまま使い回してクリックすることはできません。

セマンティックな位置特定は、ユーザーに見える挙動に基づいた検証を行ううえで有効です。また、各テストは自身のデータと状態を独立させ、直前のテストが残した状態に依存しないように設計すべきです。ただし、スナップショット上にボタンが存在するからといって、画面上でボタンが他の要素に覆い隠されていないとは限らないため、配置の確認には依然としてスクリーンショットが必要です。

オプションツール:katalepsis による検証と証拠の整理

前述の受け入れ手順を実践するだけであれば、katalepsis は必須ではありません。原著者はまず Skill を使って Agent にアプリを操作させましたが、その後 PR レビューに適したレポート出力を求め、MIT ライセンスのオープンソースツール katalepsis を開発しました。browser-review はプロセスを定めた Skill であり、katalepsis は検証を実行してレポートを整理するための CLI ツールです。

このツールは結果を artifacts/katalepsis/<run-id>/ に出力します。ブラウザで閲覧可能な HTML レポート、PR に貼り付けられる Markdown サマリー、そして構造化された実行結果と証拠ファイルの SHA-256 ハッシュを記録した manifest.json が生成されます。スクリーンショット、動画、Playwright の trace は、実行経路、設定、結果に応じて収集されるため、すべての検証ですべての証拠が揃うと決めつけることはできません。

ハッシュ値はファイルの一致確認には使えますが、改ざん耐性を保証するものではなく、テスト対象の選定が正しかったことを証明するものでもありません。一方 trace は、操作ステップ、その時点の DOM スナップショット、ネットワーク情報を順を追って再生できるため、失敗の経緯を突き止める際に役立ちます。詳細は Trace Viewer のドキュメント を参照してください。

2つの実行パス:既存テストを走らせる run と、自然言語で検査する verify

katalepsis run katalepsis verify
入力 プロジェクト既存の Playwright テスト 操作意図と成功条件を明記した checks.json
モデル モデルの API キーは不要 Anthropic、OpenAI、OpenRouter のいずれかのキーを各自用意
実行 プロジェクト自身の Playwright ランナー モデルが内部の Playwright ブラウザツールを呼び出し(Playwright MCP には非依存)
判定 Playwright の元のアサーション結果をそのまま保持し、分類・整理 モデルが結論とアサーション番号を提出し、プログラム側で証拠しきい値を検証

verify が提供するアサーションの範囲は通常の Playwright テストよりも狭く、前述のサンプルにあるようなフィールド値の詳細、データの永続化、レイアウト検証のすべてを直接カバーできるわけではありません。具体的な境界については後述します。

インストールと初期設定

katalepsis は本稿執筆時点で npm には公開されていません。インストーラーはリポジトリからソースコードを取得してビルドするため、Node.js 22.12+、Git、pnpm 10(または利用可能な corepack)が必要です。以下は公式のインストール手順ですが、本稿においてローカル環境へのインストールや実行を代行しているわけではありません:

curl -fsSL https://www.katalepsis.glamboyosa.xyz/install.sh | sh

インストーラーは katalepsis と katalepsis-mcp を ~/.local/bin に配置するため、このディレクトリがターミナルの PATH に通っていることを確認してください。

既存の Playwright テストがある場合は、プロジェクトの既存 playwright.config.ts の use 設定に以下のフィールドをマージし、他の設定はそのまま維持します:

use: {
  baseURL: process.env.KATALEPSIS_BASE_URL ?? "http://localhost:3000",
  screenshot: "only-on-failure",
  video: "retain-on-failure",
  trace: "retain-on-failure",
},

プロジェクトに @playwright/test と実際のテストコードが存在することを確認してください。アプリを起動した状態で、プロジェクトルートから実行します。サンプルの URL は実際にローカルやプレビューで稼働しているアドレスに置き換えてください:

katalepsis run --cwd . --url http://127.0.0.1:3000

ブラウザが起動せずバージョンが見つからない旨のエラーが出た場合は、同じコマンドに --install-browsers を付与することで、プロジェクトの Playwright に適合するブラウザをインストールさせることができます。なお、verify は初回実行時に自身が使用する Chromium を自動でダウンロードします。

アドレスの配線は実際の利用方法を確認する必要があります。 設定ファイルが KATALEPSIS_BASE_URL を参照していない場合、ツールは警告を出しますが、環境変数の記述が存在していても個々のテストがそれを使っているとは限りません。テストコード内に page.goto("http://localhost:8080/login") のようにハードコードされていると、意図しないサーバーに対してテストが実行されてしまう可能性があります。原著者は自身の2つのプロジェクトで実際にこの問題に遭遇しました。

既存のテストがない場合は、プロジェクトルートに checks.json を配置します。以下は解説用に作成した空フォーム送信の検証例です。/signup、ボタンの説明、成功条件は実際のアプリに合わせて変更してください:

{
  "checks": [
    {
      "id": "empty-signup-validation",
      "name": "未入力フォーム送信チェック",
      "startPath": "/signup",
      "intent": "入力項目を記入せず、登録送信ボタンをクリックする。",
      "passCondition": "必須項目のエラーメッセージが表示され、登録成功メッセージが表示されていないこと。"
    }
  ]
}

現れるべき結果と現れてはならない結果の双方を明記することで、受け入れの目標が明確になります。ただしレポートを確認する際は、モデルが提示したアサーションが本当にこの検証目標に答えているかを照合する必要があります。

データの取り扱いについても把握しておく必要があります。verify はページの構造とテキストをモデルプロバイダーに送信します。スクリーンショットはモデルには送られず、レポート用の証拠としてローカルに保存されます。テキストレポートでは URL のクエリ値はマスキングされますが、スクリーンショット、動画、trace は自動でマスキングされません。そのため、外部送信や共有に適したテストデータを使用する必要があります。原著者は実際の作業ブランチにおいて、合成シードデータに差し替え、自身の許可ルールを守ったうえでモデル検証を行っていました。

OpenRouter を利用する場合を例にとると、API キーを保存し、プロバイダーを選択し、利用可能なモデル ID を確認します:

katalepsis auth set openrouter
katalepsis config provider openrouter
katalepsis models openrouter
katalepsis config model 'ここに上記で出力されたモデルIDを指定'
katalepsis verify --cwd . --url http://127.0.0.1:3000 --checks checks.json

ここでも事前にアプリを起動し、URL を書き換えておく必要があります。auth set はキーをシステムのキーチェーンに保存しますが、ツールが対応している環境変数で設定することも可能です。API キーをコマンドライン引数や MCP のツール引数に直接含めることは避けてください。

Coding Agent から検証を呼び出し、レポートを読み戻す

katalepsis の MCP サーバーを利用することで、コーディング Agent が修正完了後に自ら検証を実行し、レポートを読み取れるようになります。Codex を例にすると、--root の後ろのプレースホルダーをプロジェクトの実際の絶対パスに置き換えます:

codex mcp add katalepsis -- katalepsis-mcp --root /path/to/your-app

クライアント接続をリロードし、3つのツール(verification_run_tests:既存テストの実行、verification_check_browser:自然言語による検査、verification_get_report:レポートの取得)が表示されることを確認します。サーバーが対象とするプロジェクトの境界は --root で指定し、モデル認証は前述の auth であらかじめ設定しておきます。

ブラウザの検証は通常のツール呼び出しよりも時間がかかる場合があります。Codex では、~/.codex/config.toml 内の既存の [mcp_servers.katalepsis] セクション内に tool_timeout_sec = 600 を追加します。グローバル設定に記述したり、該当セクションの既存の起動パラメータを上書きしたりしないよう注意してください。その他のクライアントについては 公式の設定ドキュメント を参照してください。

レポートにおける4つの状態を区別する

  • passed:該当する実行パスの合否判定ルールを満たしていること。既存の Playwright テストがリトライを経て合格した場合は passed に分類されつつ、flaky フラグと各試行履歴が保持されます。
  • failed:検証が実際に失敗したこと。本物の外部 API 障害によってビジネスロジックが失敗した場合も failed として残します。これは当該環境で処理が失敗したことを意味し、原因が今回のコード変更にあると自動的に断定するものではありません。
  • blocked:有効な結論を下せない状態。例えばプレビューのヘルスチェックでネットワークエラーが発生した、502/503/504 が返された、ブラウザが起動できなかった、あるいはモデル検証において証拠不足、ツールエラー、ステップ数/トークン数/時間の上限超過が発生した場合などが該当します。
  • not tested:実行されなかった検証(フィルタリングやスキップなど)。ツール上は notTested として理由とともに別枠で一覧化され、合格数に合算することはできません。

プレビュー自体にアクセスできない場合、ツールはブラウザテストを開始する前に該当チェックをブロックし、環境の停止を複数の機能欠陥として誤報することを防ぎます。一方、実行後に確認されたビジネスロジックの失敗については、実際の証拠に基づいて判断する必要があります。

モデルが「合格」と言ったとき、プログラムはどう検証するか?

verify において、モデルは navigate、snapshot、click、fill、press、assert、finish を呼び出すことができます。assert は実際のブラウザ上でツールによって実行され、結果、スクリーンショット、そして a1 のような番号が記録されます。モデルは最後に finish を用いて結論を宣言し、アサーション番号を参照します。

モデル自身のテキストによる判断だけで合格のお墨付きを与えることはできません。プログラム側の関数 decideOutcome が、申告内容と実際の実行ログを照合します:

  1. 参照は、実際に実行されたアサーションと一致していなければならない。完了の結論がない場合、参照がない場合、未知の番号を参照している場合は、いずれも合格にはならない。
  2. 合格を主張する場合、参照されたアサーションがすべて成功していなければならず、少なくとも1つの可視性アサーション(visible)を含んでいる必要がある。URL の変化や特定要素の非表示(hidden)を証明しただけでは不十分。
  3. 参照されたアサーションの後に、未参照のアサーションの失敗が発生している場合、その新しい矛盾する証拠を無視して合格を報告することはできない。
  4. 失敗を主張する場合も、実際に失敗したアサーションを参照する必要がある。失敗の証拠がない失敗宣言も同様にブロックされる。

これらの証拠基準を満たさない宣言は blocked: no-evidence として分類されます。これはモデルが提出した証拠の妥当性をチェックするものであり、ビジネス要件のすべてを自動監査するものではありません。

元からページにあった要素は、単体で操作成功の証拠にはならない

原著者は、モデルが「メールアドレス入力欄のラベルが表示されている」ことを根拠にフォーム送信の成功を証明しようとしたケースに遭遇しました。しかしそのラベルはクリック前から存在していたものであり、アサーションが真であったとしても、送信によって何が生じたのかは何一つ説明していません。

そのため各検証では、分離されたブラウザコンテキストが使用されます。モデルが操作を行うページとは別に、同一 URL を読み込みつつ一切の操作を行わないページを操作前の対照群として用意します。モデルが navigate で直接開いた新しい URL についても、同様に対照用の複製が作成されます。

可視性アサーションが実行されると、ツールはその同一要素が操作前の対照ページにおいても成立しているかを確認し、trueAtStart を記録します。通常の操作検証では、成功した可視アサーションのうち少なくとも1つは、この対照群において元々は成立していなかったものでなければなりません。参照された可視要素がすべて最初から存在していた場合、それを根拠に合格することはできません。

「ページの初期状態」を確認するテストケースでは、checks.json の該当オブジェクトに "observeOnly": true を追加することで、この新規変化の要件をスキップできます。ただし、実際に実行されて合格した可視性アサーションは依然として必須です。「初期状態の確認」は、単にファーストビューだけを確認すればよいという意味ではありません。

モデルが合格と言っても、証拠ルールは受け入れるか?

申告内容とアサーションの条件を変更し、passed・failed・blocked がどう分岐するか観察します。これは証拠のしきい値を示すものであり、ユーザー要件の充足判断を代替するものではありません。

判定エンジンの裁定ステータス: passed
【証拠しきい値通過】参照されたアサーションは合格し、少なくとも1つの可視結果が操作前のページ複製には存在しませんでした。ただし、これだけでユーザーの要求を満たしているとは証明できません。

対照ページは URL に対して再度リクエストを発行するため、ブラウザコンテキストの分離はバックエンドデータの分離を意味するわけではありません。一度しか使えないアクティベーションリンクや、アクセスしただけでサーバー側の状態が変化するページの場合、2回目の読み込みによって挙動が変わる可能性があります。また、対照ページの読み込み自体が失敗した場合も、検証がブロックされることがあります。

アサーションが真であっても、要件に答えていない場合がある

例えば要件が「空のフォームを送信したらエラーを表示する」だったとします。モデルはエラーを確認する代わりに、一般的なリンクをクリックして「会社概要」ページへ遷移し、新しい見出しが表示されたことを証明しました。その見出しは実際に新しく現れたコンテンツであり、可視性や変化のしきい値を満たすかもしれませんが、フォームのバリデーションとは何の関係もありません。

したがって、人間がレポートを確認する際は、成功条件、実際の操作、参照されたアサーションを突き合わせて確認する必要があります。実際に実行されたアサーションは「証拠のない申告」を排除できますが、そのアサーションが要件と合致しているかまでを自動で保証することはできません。

現在 verify が提供しているモデル向けアサーションは、visible、hidden、URL contains の3種類のみです。前述の設定保存の例で挙げたような入力欄の現在値、スイッチの状態、再読み込み後の永続性、さらにはコンソールログ、ネットワーク、マルチビューポートのレイアウト検証などは、通常のブラウザツールや Playwright テストによって補完する必要があります。これはツールのラッパー層における現在の仕様範囲であり、Playwright 自体に能力がないわけではありません。

固定されているのは証拠の判定ルールであり、モデルが選択する操作内容はモデルや実行ごとに変動します。すぐに消えてしまうトースト通知などはアサーションの実行前に消えてしまうこともあるため、失敗時にはスクリーンショット、操作履歴、trace を確認し、プロダクト自体の不具合なのか、単に状態をキャッチできなかっただけなのかを見極める必要があります。リポジトリの実装状況ドキュメントには、外部開発者による独立したインストール検証やクロスプラットフォーム対応がまだ途上である旨が記載されています。原著者の試行結果をそのまま一般的な信頼性の保証とみなすことはできません。

正しい URL をテストした後、プレビューがどのコミットのものか確認する

コードを変更しても、プレビュー環境の更新が追いついていない場合があります。古いプレビュー環境でのテスト合格は、今回の変更の証明にはなりません。katalepsis はプレビューのコミット識別子を以下の優先順位で読み取り、現在チェックアウトされているコードの HEAD と比較します:

  1. レスポンスヘッダー x-commit-sha
  2. HTML の <head> の先頭 512KB 内に含まれる <meta name="commit-sha">
  3. 呼び出し元から渡された --preview-commit <sha>

3番目の方法は、実際にデプロイされたことが判明している SHA のみを渡すべきであり、デプロイされるはずのプッシュ済み SHA や GITHUB_SHA を代用してはなりません。比較結果は3通りに分かれます:

  • confirmed:チェックアウトされた HEAD と一致していること(7文字以上の有効な短縮 SHA プレフィックスも許容)。ただし、作業ツリー内の未コミットの変更がプレビューに反映されていることまでは証明できません。
  • unknown:バージョンを確認できない状態。レポートには不明と明記され、検証自体は実行可能です。
  • mismatched:明確に不一致である状態。検証は実行されますが、本来合格となるはずの実行は blocked に変更され、すでに発生した failed はそのまま維持されます。

操作は順次実行され、各チェックの消費リソースは制限される

原著者は試行中、並行して走った2つの fill がフォーカスを奪い合い、名前用の入力欄にメールアドレスが入力されてしまう問題を目撃しました。そのため本ツールでは、ブラウザの操作を直列キューに通し、前の操作が完了してから次の操作を実行するようにしています。これはページ内のすべてのネットワークリクエストが直列化されるという意味ではありません。

モデルによる各チェックは独立したブラウザコンテキストを使用し、trace を有効化します。デフォルトの上限は最大15ステップ、200,000トークン、120秒に設定されており、設定された予算に達した場合は blocked とその理由が報告されます。トップレベルのナビゲーションは、プレビューの origin および明示的に --allow-origin で指定された origin(プロトコル、ホスト、ポート)のみに制限されます。外部のサブリソース自体は読み込めるため、完全なネットワークサンドボックスではありません。

完了を宣言する前に、実働結果を提示する

この一連のプロセスは、フォーム、絞り込み、モーダル、ページ遷移、レスポンシブレイアウト、再読み込み後の状態保持など、ユーザーに見える実行時の挙動を対象としています。ドキュメントの誤字修正や、画面表示に影響しないバックエンドの変更のためにブラウザを立ち上げる必要はありません。変更内容に応じて適切な検証手段を選択します。

既存の自動テスト、ブラウザによる受け入れ検証、コードレビュー、そして重要な変更に対する人間の目視確認には、それぞれ固有の役割があります。最初の設定画面の話に戻るなら、Agent が提出すべきなのは、どの値を変更し、どのように保存と再読み込みを行い、最終的に何を読み取り、どのような問題に遭遇し、どこで証拠を再確認できるかという情報です。こうした引き渡しが行われて初めて、人間が何度もスクリーンショットを撮って伝言し、再試行を促すという不毛なやり取りを減らすことができます。

出典
How to Make Your Coding Agent Check Its Work in the BrowserTimothy Ogbemudia·原文を見る
編集部注
本文は元記事の手法、公式ドキュメント、オープンソース実装をもとに解説したものです。原著者のツール実行結果を今回の実測値として扱っているわけではありません。
↑ 本文ここまで
✓ この記事は全文無料です

メンバーは全文 Markdown のコピー・保存に加え、自分の AI に購読・質問させることができます。

原文を読む ↗
📮 新着解説をメールでいち早く

役に立ちましたか?メールを登録すると、新着解説を公開と同時にお届けします。解除はいつでも。