ある日、Antigravity に「ログイン画面のバリデーションを実装してほしい」と頼んだら、入力チェックの関数だけが返ってきました。エラーメッセージの表示も、フォーカス制御も、サーバー側のチェックとの整合も入っていません。私の頭の中の「バリデーションが完了している状態」とは、それらをすべて含んだ画面体験のことでしたが、エージェントが理解した「完了」は関数を1つ書き終えることだったのです。
このすれ違いは、プロンプトの書き方の問題ではありませんでした。「完了とは何か」が、私とエージェントの間で合意されていなかったことが原因です。今日はその気づきから整理してきた、Antigravity のエージェントと Definition of Done を交換するための実践をまとめます。
「Done」が共有されていないと、何度でも往復する
個人開発を長く続ける中で、外部の方に作業を切り出して頼む機会が少しずつ増えてきました。そこで痛感したのは、「完了の定義」を渡せていない発注は、ほぼ確実に再発注になるということです。エージェントに渡すタスクも、構造はまったく同じでした。
問題なのは、再発注のたびにコンテキストが薄れていくことです。最初の依頼に書き込まなかった条件を、2回目・3回目で説明しても、エージェントの思考はもう次の問題に向かっていて、最初の設計判断には戻ってきません。タスクの粒度を整えても、 Antigravity で AI に渡すタスクの粒度を設計する で書いたような「ちょうどいい大きさ」を意識しても、Done の定義が曖昧なら結局あとで揉めます。
対策はとてもシンプルです。「完了」の定義を、最初の依頼に書き込んでしまうこと。これだけで、再修正の往復は目に見えて減ります。
Definition of Done を3つの層で組み立てる
私が現場で使っているのは、Done を3つの層に分解する考え方です。
第1層:機能要件 — 何が動くと「動いた」と言えるか
「ユーザーがメールとパスワードを入れて Submit を押すと、サーバーに POST が飛び、レスポンスが OK ならホーム画面に遷移します。失敗ならエラーが表示される」のような、ふるまいの定義です。多くの方がここまでは書いているはずです。問題は、ここで止まることです。
第2層:観察可能な振る舞い — どう確認できれば「動いた」と判断できるか
「動くこと」と「動いていることを観察できること」は別物です。
- 開発者ツールのネットワークタブで POST リクエストの送信が確認できる
- エラー時には画面の入力欄の下に、赤色 14px のエラーメッセージが表示される
- 通信中はボタンが非活性になり、二重送信を防ぐ
ここを書かないと、エージェントは「動くコード」を書きますが、「私が動作を確認できるコード」にはなりません。完了報告を信頼するためには、第2層が言語化されている必要があります。
第3層:運用上の制約 — 何を壊してはいけないか
新しい機能が動くようになる代わりに、知らないところで何かが壊れる事故は、AIエージェントを使うようになってからむしろ増えました。だからこそ「触ってほしくないものリスト」を明示します。
- 既存のユーザー設定画面のバリデーションロジックは触らない(共通モジュールとして再利用する)
- スマートフォン横画面のレイアウト崩れを起こさない
- 追加機能には対応するテストも書く
この第3層は、エージェントの自由度を制約することで、結果としてレビュー時間を大きく短縮してくれます。
プロンプトに書き込むテンプレート
3層の考え方を、私はこのテンプレートにまとめてプロンプトに貼っています。
タスク: [何を作るか]
Definition of Done:
1. 機能要件
- [ユーザーが何をしたら、何が起きるか]
2. 観察可能な振る舞い
- [私が動作を確認する具体的な手順]
- [エラー時の表示仕様(場所・色・文言)]
3. 運用上の制約
- [触ってほしくないファイル・モジュール]
- [既存仕様に影響を与えてはいけない箇所]
- [追加すべきテスト]
完了報告の前に、上記 Definition of Done を1項目ずつ自己採点し、
満たせていない項目があれば「未達」として申告してください。最後の「自己採点して申告してください」の一文が、実はもっとも効きます。Antigravity のエージェントは、自分のアウトプットを Definition of Done と突き合わせるという形で内省してくれます。曖昧なまま「完了しました」と返してこなくなる感覚があります。
エージェント自身に検証させる「アクセプタンス・スクリプト」
さらに踏み込むなら、Definition of Done を実行可能なテストとして渡してしまうのが効果的です。Antigravity の検証ループとも相性がよく、 Antigravity でエージェントの完了検証を設計する のアプローチを発展させたものとも言えます。
// acceptance/login-validation.spec.ts
describe('Login Validation — Definition of Done', () => {
it('Done-1: 不正なメールでエラーが表示される', async () => {
await page.fill('#email', 'invalid');
await page.click('button[type=submit]');
await expect(page.locator('.email-error')).toHaveText('メールアドレスの形式が正しくありません');
});
it('Done-2: パスワード未入力時に該当フィールドにフォーカスが戻る', async () => {
await page.fill('#email', 'user@example.com');
await page.click('button[type=submit]');
await expect(page.locator('#password')).toBeFocused();
});
it('Done-3: 通信失敗時にネットワークエラーが表示される', async () => {
await page.route('**/api/login', route => route.abort());
await page.fill('#email', 'user@example.com');
await page.fill('#password', 'password');
await page.click('button[type=submit]');
await expect(page.locator('.network-error')).toHaveText('ネットワークに接続できません');
});
});このファイルを先に書いて「これらのテストがすべて通ることを Done とします」と Antigravity に渡すと、エージェントの動きが一段引き締まります。テストを通すという具体的なゴールがある状態と、抽象的に「ログイン画面を作って」と頼まれた状態では、出てくるコードの質がはっきり変わります。
Done が欠けているときに繰り返し起きる3つの症状
Definition of Done を書くようになる前、私は同じ失敗を3パターン繰り返していました。逆に言えば、この3つに思い当たるなら、Done の言語化を試す価値があります。
ひとつめは 「見た目だけ完成・動作は未完成」 の症状です。きれいに整った画面が返ってきて、ボタンの配置も間隔も完璧なのに、押しても何も起きありません。これは Layer 1 だけがプロンプトにあって Layer 2 が欠けている典型例です。「動くこと」が完成条件として書かれていなければ、エージェントには「見た目を整えること」が優先されてしまいます。
ふたつめは 「気づかないうちに範囲が広がっている」 症状です。小さな修正を頼んだはずが、共通ユーティリティの名前が変わっていたり、Lintの設定が「改善」されていたりします。Layer 3 が欠けていると、エージェントは「ついでの改善は歓迎されている」と暗黙に解釈します。Layer 3 を渡しておくと、範囲外への変更は事前に確認してくれるようになります。
みっつめは 「静かな未完成」 です。完了報告が返ってきて、見た目もよく、しかし1週間後にまったく試されていなかったエッジケースが見つかる。これが一番コストが高い失敗モードで、レビューの瞬間には表面化しません。プロンプト末尾の「Definition of Done を1項目ずつ自己採点してください」が効くのはここです。エージェントが黙って飛ばしていた未対応項目を、自分の言葉で「未達」と申告してくれるようになります。
これら3つの症状に心当たりがあるかどうかが、Done を言語化する価値のいちばん早い判定基準だと思っています。
3層をレビューのチェックポイントに変換する
3層は書いて終わりではなく、完了報告が返ってきたときのレビュー観点としてそのまま使えます。私は次の対応表を手元に置いて、報告を受け取るたびに上から順に確認しています。
| 層 | レビューで確認すること | 欠けたときに出る典型症状 |
|---|---|---|
| 第1層:機能要件 | 依頼したふるまいが実際に起きるか | そもそも要件を満たしていない |
| 第2層:観察可能な振る舞い | 自分の手で動作を再現できるか | 「動くはず」だが確認できない |
| 第3層:運用上の制約 | 触ってはいけない箇所が無傷か | 気づかないうちに別の場所が壊れる |
この表の右端の列こそが、Definition of Done を書く本当の理由だと感じています。左の2列は「うまくいったこと」を確認しますが、右の列は「静かに壊れたこと」を捕まえます。手戻りのコストが高いのは、いつも後者のほうでした。
曖昧な Done を具体的な Done に書き換える
抽象的な依頼を3層に沿って書き換えると、何がどう変わるのか。実際の例で示します。
【書き換え前】
ログイン画面のバリデーションを実装してほしい。
【書き換え後】
タスク: ログイン画面の入力バリデーションを実装する
Definition of Done:
1. 機能要件
- 不正な形式のメールで Submit すると、送信されずエラーが表示される
2. 観察可能な振る舞い
- エラーは入力欄の下に赤 14px で表示される
- 通信中は Submit ボタンが非活性になる
3. 運用上の制約
- 既存の共通バリデーション(settings 画面)は変更しない
- 追加分に対応する spec テストを書く書き換え前の一文には、私の頭の中にしかない条件が5つ以上省略されています。書き換え後は、その省略をすべて表に出しただけです。新しい情報を足したのではなく、暗黙だった前提を言語化しただけ。この違いが、そのまま手戻りの差になって返ってきます。
アート審査の世界で気づいた「言語化された基準」の力
余談ですが、私はアプリ開発と並行してアート活動も続けています。作品を公募展や国際的な審査会に出してきた中で、はっきり感じてきたことがありました。
審査基準が言語化されているコンペは、結果に納得感がありました。「概念の独自性 30%・技術的完成度 30%・社会的インパクト 40%」のような形で公開されていると、応募する側も審査する側も、同じ言語で対話できます。逆に審査基準が言語化されていないコンペは、応募する側も「何を磨けばよいか」が分からず、審査員の個人的な好みに左右されます。
エージェントへの依頼も、これと同じ構造に見えるのです。Done が言語化されていれば、エージェントは何を磨けばよいかを知っています。されていなければ、エージェントなりの最大公約数的な解釈で動くしかなく、結果として私たちが頭の中で描いていたものから少しずつズレていきます。
技術と表現は別世界に見えますが、「何をもって完成とするか」を相手と共有する作業は、実はとても似ています。
次のアクション
今あなたが書きかけている依頼の中で、Done の定義が曖昧なものを1つだけ選び、上のテンプレートに沿って3層の条件を書き加えてみてください。たったひとつのタスクから始めるだけで、再修正の往復の少なさを実感できるはずです。私自身、最初は「面倒だな」と感じていましたが、結局のところ手戻りに使う時間のほうが明確に長かったと、後で気づきました。