← 기사 목록
日本語https://zenn.dev/topics/ai/feed

AI同士のhandoffを多層契約チェックリストにした

추출된 키워드

28
多層契約チェックリスト·5AI同士のhandoff·5Codex·4Claude Code·4目的契約·4範囲契約·4権限契約·4完成契約·4検証契約·4失敗契約·4AI エージェント·3no-regression·3クロスレビュー·3harness17/cross-agent-harness·3harness17/zenn-articles·3DB migration·2GitHub Releases·2front matter·2Markdown·2CI·2pre-commit hook·2PowerShell·2npm run build·2npm run test·2self verify·2published: true·2git commit·2git push·2

원문

10,230
AI同士のhandoffを多層契約チェックリストにした

AI同士のhandoffを多層契約チェックリストにした

はじめに

Codex と Claude Code を同じリポジトリで併用していると、作業の引き継ぎはすぐ曖昧になります。

「この差分をレビューして」「この計画で実装して」だけでも通じることはあります。ただ、実際に何度か回すと、次のような漏れが起きました。

  • どのファイルまで触ってよいかが曖昧で、別記事や別機能まで直し始める
  • published: true
    git push
    、release のような最終操作を AI 側が判断しそうになる
  • レビュー観点が抽象的で、事実確認・守秘・テスト不足のどれを優先するかがぶれる
  • 失敗時の止まり方が決まっておらず、権限やネットワークが必要な場面で作業を続けようとする

そこで、AI 間の handoff を単なる申し送りではなく、多層の作業契約として書くようにしました。

この記事では、Zenn 記事管理リポジトリ harness17/zenn-articles と、AI 共同作業用の harness17/cross-agent-harness で使っている考え方をもとに、handoff に何を書けば事故が減るかをチェックリストとして整理します。

関連する先行記事では、AI 2 台のクロスレビュー手順と、Codex と Claude Code を相互呼び出しする仕組みを扱いました。本記事では、その手前にある「handoff に何を書くと、別の AI へ安全に渡せるか」に絞ります。

handoffを1枚の依頼文にしない

最初は handoff を「次の人への依頼文」として書いていました。

Codexで記事を作成しました。
Claude Codeでレビューしてください。
問題なければ公開してください。

これでは足りませんでした。

レビュー担当から見ると、何を根拠に公開可と判断するのか、対象範囲はどこまでか、直してよいのか指摘だけなのか、公開操作までしてよいのかが分かりません。

人間同士なら文脈で補えますが、AI エージェントは「できそうなこと」を広く実行しがちです。特にローカルリポジトリを触れる状態だと、レビューのつもりで依頼しても、本文修正、関連ファイル更新、git 操作まで進みそうになります。

実際、記事レビューのつもりで渡した作業で、対象記事だけでなく別記事の表現や公開ログまで整えそうになったことがありました。実装タスクでも、UI の小修正を依頼したはずが、周辺コンポーネントの整理まで提案される場面がありました。どちらも悪意ではなく、依頼文に「ここから先は触らない」と書いていなかったことが原因です。

そのため、handoff は1枚の依頼文ではなく、次の層に分けて書くことにしました。

何を固定するか
目的契約何を達成したいか
範囲契約どこを触ってよいか、触ってはいけないか
権限契約編集、commit、push、publish を誰が判断するか
完成契約何を満たしたら完了か
検証契約どのコマンドやレビュー観点で確認するか
失敗契約どこで止まり、何を報告するか

この分け方にしてから、AI に渡す依頼の粒度が安定しました。

多層契約チェックリスト

実際に handoff へ書くときは、次のチェックリストを使います。

## YYYY-MM-DD HH:mm 追記(<主題> / <agent> 作成)

- 対象: <branch / worktree / path>
- 作成者: <Codex | Claude Code | user>
- 依頼先: <Codex | Claude Code>
- 主題: <一文で目的を書く>

目的契約:
- <この作業で達成すること>
- <達成しないこと>

範囲契約:
- 触ってよい範囲: <files / directories>
- 触ってはいけない範囲: <unrelated files / user changes>

権限契約:
- 編集: <可 / 不可>
- git add / commit / push: <可 / 不可 / user明示時のみ>
- publish / release / migration: <user明示時のみ>

完成契約:
- 通常動作: <期待する動作>
- 前提条件: <認証 / 外部サービス / 実行環境>
- エラー処理: <失敗時の表示や停止条件>
- no-regression: <壊してはいけない既存動作>

検証契約:
- セルフ verify: <commands>
- レビュー観点: <correctness / security / tests / privacy>

失敗契約:
- 権限、ネットワーク、未確認事実、競合が出たら停止する
- どこまで完了し、何が未確認かを handoff に戻す

このテンプレートの目的は、長い依頼文を書くことではありません。AI が勝手に補完しやすい箇所を、先に埋めておくことです。

特に効いたのは「達成しないこと」「触ってはいけない範囲」「user 明示時のみ」の3つでした。

1. 目的契約: やることより、やらないことを書く

AI への依頼では「何をしてほしいか」は書きます。しかし、事故を減らすには「何をしないか」も同じくらい重要でした。

たとえば、記事レビューを依頼するときに次のように書きます。

目的契約:
- `articles/example.md` の公開前レビューを行う
- 重大指摘、軽微指摘、残リスクを分類して返す
- 本文修正、published変更、git操作は行わない

「レビューしてください」だけだと、AI は修正まで進めることがあります。レビュー専用なのか、修正込みなのかを目的契約で分けると、依頼先の振る舞いが安定します。

実装依頼でも同じです。

目的契約:
- 計画ファイルに書かれたチェックボックスを順に実装する
- 既存の設計方針に沿って最小差分にする
- 計画外のリファクタ、UI刷新、依存追加は行わない

「ついでに改善」を防ぐには、やらないことを明文化する必要がありました。

2. 範囲契約: 触ってよい場所を先に狭める

AI 同士の共同作業で一番怖いのは、片方が知らない差分を広げることです。

特に記事リポジトリでは、レビュー対象の記事だけを見るつもりが、README、公開ログ、別記事の表現まで整えたくなる場面があります。コードリポジトリなら、共通コンポーネントやテストヘルパーに広がります。

そこで、handoff には必ず範囲を書きます。

範囲契約:
- 触ってよい範囲: `articles/ai-handoff-multi-layer-contract-checklist.md`
- 触ってはいけない範囲: 他の記事、`README.md`、公開ログ、git履歴

実装タスクなら、ディレクトリ単位ではなくファイル単位で書けるならその方が安全です。

範囲契約:
- 触ってよい範囲:
  - `src/renderer/hooks/useSchedule.js`
  - `src/renderer/src/App.jsx`
- 触ってはいけない範囲:
  - 認証処理
  - migration
  - user が作業中の未コミット差分

ここで重要なのは、

git status
を見てから書くことです。未コミット差分がある状態で AI をもう一方に渡すなら、どれが自分の差分で、どれがユーザーや別エージェントの差分かを分けておかないと、レビューや修正の境界が壊れます。

3. 権限契約: 最終操作をAIに渡さない

handoff には、編集権限と公開権限を分けて書きます。

自分の運用では、次の操作は原則として user 明示時のみです。

  • published: true
  • git commit
  • git push
  • release
  • migration
  • 外部サービスへの投稿

AI が実装やレビューをできても、最後の公開判断まで渡す必要はありません。

Zenn 記事では、

published: true
に変えて main に push すると公開に進みます。アプリ開発では、push や release が利用者に影響します。DB なら migration がデータに影響します。

そのため、handoff では次のように明記します。

権限契約:
- 編集: 可
- git add / commit / push: 不可
- published変更: userが「公開して」と明示するまで不可
- 外部サービス投稿: 不可

この層を分けたことで、「レビューは通ったので公開しておきます」のような飛び越しを防ぎやすくなりました。

4. 完成契約: 完了条件を4種類に分ける

実装タスクでは、完成条件を「動けばよい」で済ませると漏れます。

自分は次の4種類に分けています。

種類確認すること
通常動作期待する主動線が動くか
前提条件認証、設定、外部サービス、権限が必要か
エラー処理失敗時に止まるか、分かる形で返すか
no-regression既存の動線やテストを壊していないか

記事の場合も同じ考え方が使えます。

完成契約:
- 通常動作: 読者が handoff チェックリストを自分のリポジトリへ移せる
- 前提条件: Codex / Claude Code を併用している読者を想定する
- エラー処理: 権限不足、未確認事実、対象外差分が出たときは止める方針を書く
- no-regression: 既存のクロスレビュー記事、相互呼び出し記事と主題が重複しない

完成契約を先に書くと、レビュー側も見やすくなります。レビュー担当は「文章として良いか」ではなく、「完成契約を満たしているか」で判断できます。

5. 検証契約: コマンドと観点を両方書く

verify コマンドだけでは、レビュー観点が足りません。一方で、観点だけだと確認が曖昧になります。

そのため、検証契約ではコマンドと観点を分けます。

検証契約:
- セルフ verify:
  - `npm run test`
  - `npm run build`
- レビュー観点:
  - correctness: 中心主張が実コードや実運用と矛盾していないか
  - security: 秘密情報、個人情報、過剰な内部パスがないか
  - tests: 変更範囲に対してテストが足りるか
  - no-regression: 既存UIや既存記事の前提を壊していないか

記事リポジトリでは

npm run test
のようなテストがない場面もあります。その場合は、文体、必須要素、守秘、リンク、front matter を検証契約に入れます。
検証契約:
- セルフ verify:
  - front matterに `published: false` が残っている
  - topicsが5個以内
  - GitHubリンクがある
  - 5行以上のコードブロックがある
- レビュー観点:
  - 体験ベースになっているか
  - AI的な締め文がないか
  - 守秘リスクのある固有名詞がないか

「何を見たら OK か」を書いておくと、別 AI に渡してもレビュー結果の形式が揃います。

6. 失敗契約: 続けるより止まる条件を書く

AI エージェントは、失敗時に別ルートを探して進もうとします。これは便利な一方で、権限、ネットワーク、外部サービス、破壊的操作が絡むと危険です。

そこで、handoff には止まる条件を書きます。

失敗契約:
- ネットワークアクセスが必要になったら停止する
- 未確認の中心主張が出たら断言せず、確認待ちとして戻す
- userまたは別エージェントの未コミット差分と衝突したら停止する
- 破壊的操作、公開操作、migrationが必要なら実行せず理由を報告する
- 完了済み、未完了、未確認を分けて handoff に戻す

この層を入れてから、「何とか進める」より「安全に止める」結果が増えました。

特に、外部 API、公開記事、DB migration、GitHub Releases のような外部影響がある作業では、失敗契約がないと AI が代替手段を探しすぎます。止まり方を書いておくと、次の人間判断に渡しやすくなります。

簡易チェックをスクリプト化する

6 層を毎回手で確認するだけでは、忙しいときに抜けます。内容の正しさはレビューで見るとして、構造の抜け漏れだけは機械的に拾えるようにしました。

handoff の品質は、全部を自然言語レビューに任せるより、最低限の構造だけでも機械的に見た方が安定します。

たとえば、Markdown の handoff に必要な見出しがあるかだけなら、次のような PowerShell で確認できます。

$path = "CLAUDE_CODE_HANDOFF.md"
$content = Get-Content -Raw -Path $path
$required = @("目的契約:", "範囲契約:", "権限契約:", "完成契約:", "検証契約:", "失敗契約:")

foreach ($section in $required) {
    if ($content -notmatch [regex]::Escape($section)) {
        throw "Missing handoff contract section: $section"
    }
}

Write-Host "handoff contract sections found"

これは内容の正しさまでは見ません。それでも、「権限契約を書き忘れた」「失敗契約がない」という事故は拾えます。

throw
で止めているのは、CI や pre-commit hook に組み込んだときに、契約セクションのない handoff をそのまま通さないためです。ローカルで手動確認するだけなら、
Write-Warning
に変えて警告表示に留めてもよいです。

実運用では、このような軽い構造チェックと、AI による意味レビューを組み合わせるのがちょうどよいと感じています。

まとめ

AI 同士の handoff は、会話の続きとして書くと曖昧になります。

自分の運用では、handoff を次の6層に分けることで、作業境界がかなり明確になりました。

  • 目的契約: 何を達成し、何をしないか
  • 範囲契約: どこを触ってよいか
  • 権限契約: 編集、commit、push、publish を誰が判断するか
  • 完成契約: 通常動作、前提条件、エラー処理、no-regression
  • 検証契約: verify コマンドとレビュー観点
  • 失敗契約: どこで止まり、何を報告するか

Codex と Claude Code をつなぐほど、必要になるのは強い自動化よりも、止まる条件まで含めた作業契約でした。

参考リンク

GitHubで編集を提案