はじめに
個人AI秘書であるJARVISと、専門業務を担うエージェント基盤MiTiRは、役割も実行環境も異なる独立プロジェクトである。
- JARVIS:Windows上で動作し、ユーザーとの対話、予定、記憶、PC操作、外部サービスとの連携を担当する
- MiTiR:Mac mini上で動作し、調査、解析、専門エージェント、各種業務サービスを提供する
両者はTailscale経由のAPIで接続できる。しかし、APIは実行時のタスク処理には向いていても、「相手側の実装を変更してほしい」「契約仕様を更新したので追従してほしい」といった開発上の依頼を安全かつ監査可能な形で交換する仕組みとしては十分ではない。
そこで考えたのが、Gitのhandoff文書をメッセージキューとして使い、各マシンの常駐runnerがメッセージを検出し、Codexを起動して自律的に設計・実装・テストを進める仕組みである。
本稿では、この構想のアーキテクチャ、メッセージプロトコル、安全設計、実装方法、段階的な導入計画をまとめる。
1. なぜGitを使うのか
通常、システム間の非同期通信にはRabbitMQ、Kafka、SQSなどのメッセージキューを利用する。しかし今回交換するのは、高頻度のセンサーデータではなく、数分から数時間かけて処理する開発依頼である。
Gitには、開発handoffに適した特徴がそろっている。
- メッセージと変更履歴が同じ場所に残る
- 誰が、いつ、何を依頼したか追跡できる
- commit SHAで依頼時点の状態を特定できる
- pull requestやbranchによるレビュー工程を利用できる
- 新しいサーバー基盤を追加しなくても始められる
- 障害後もリポジトリとローカル状態から復旧できる
一方で、Gitは本来のメッセージキューではない。そのため、排他制御、重複防止、再試行、メッセージ状態、無限ループ防止をこちらで明示的に設計する必要がある。
2. 双方向handoffの基本構成
通信方向は明確に分離する。
| 方向 | 受信文書 | 書き込む主体 |
|---|---|---|
| JARVIS → MiTiR | MiTiRリポジトリの docs/from-Jarvis.md | JARVIS |
| MiTiR → JARVIS | JARVISリポジトリの docs/from-MiTiR.md | MiTiR |
原則は次のとおりである。
- 相手リポジトリは読み取り専用で参照する
- 相手リポジトリ内で書き込めるのは、自分専用のhandoff文書だけとする
- ソースコードの変更は、受信側が自分のリポジトリで行う
- 受信済みメッセージを書き換えない
- 受付、進捗、完了、失敗は新しい応答メッセージとして追記する
これにより、JARVISがMiTiRのコードを直接変更したり、MiTiRがJARVISの作業中ファイルを壊したりすることを防ぐ。
flowchart LR
J["JARVIS runner\nWindows"] -->|"from-Jarvis.md"| MR["MiTiR Git"]
MR --> M["MiTiR runner\nMac mini"]
M -->|"from-MiTiR.md"| JR["JARVIS Git"]
JR --> J
3. 開発作業の自動実行フロー
各マシンには常駐runnerを置く。JARVIS側はWindows Task Scheduler、MiTiR側はmacOSのlaunchdで起動する。
runnerの処理は次の流れになる。
- 監視専用cloneで
git fetch origin mainを実行 origin/mainのcommit SHAを前回値と比較- 更新があった場合だけhandoff文書を解析
- 未処理メッセージを抽出
- Schema、送信元、期限、実行レベル、禁止操作を検証
- SQLiteで重複や競合を確認
- 受信側リポジトリに専用worktreeとtask branchを作成
- 検証済みフィールドから固定形式のプロンプトを生成
codex execでCodexを非対話実行AGENTS.mdとSDDに従って実装・テスト- 許可レベルに応じて停止、commit、branch pushを実施
- 結果を相手側handoff文書へ返信
開発者が使用中の作業ディレクトリで30秒ごとにgit pullする方式は採用しない。未commitの変更やbranch切替と衝突するためである。監視専用cloneでfetchし、リモート上の文書を読む方式にする。
既定のポーリング間隔は60秒とし、設定で30秒まで短縮可能とする。変更がない限りCodexは起動しない。
4. Handoff Protocol v1.0.0
Markdownは人間が読みやすいが、自由記述だけでは自動処理に向かない。そこでMarkdownの見出しの下に、閉じたYAML envelopeを埋め込む。
protocol_version: "1.0.0"
message_id: "JARVIS-20260816-0001"
correlation_id: null
created_at: "2026-08-16T10:30:00+09:00"
sender: "jarvis"
recipient: "mitir"
type: "implementation_request"
status: "requested"
reply_to: "1002_Jarvis/docs/from-MiTiR.md"
execution_level: "L2"
requires_approval: false
expires_at: "2026-08-17T10:30:00+09:00"
max_hops: 4
payload:
title: "Synchronize Integration API v0.2.0"
requirements:
- "Preserve existing read capabilities"
- "Add research.select_candidates"
prohibited_actions:
- "live_api_mutation"
- "trading_mutation"
- "push_to_main"
artifacts:
openapi_sha256: "..."
必須要素は以下である。
- 一意な
message_id - 元依頼と応答を結ぶ
correlation_id - タイムゾーン付きISO 8601時刻
senderとrecipient- メッセージ種別と状態
- 実行レベル
- 承認要否
- 有効期限
- 最大往復回数
- 検証対象となるpayload
- 明示的な禁止操作
- 関連するSHAや成果物
未知のフィールドや未定義のメッセージ種別は、将来バージョンとして合意されていない限り拒否する。曖昧な入力を「たぶんこういう意味だろう」とエージェントに解釈させないことが重要である。
5. メッセージ種別と状態遷移
想定するメッセージ種別は次のとおりである。
implementation_requestdocumentation_requestreview_requeststatus_responseresult_responseapproval_requestapproval_responsecancellation_requesterror_response
処理状態は以下に限定する。
requested → accepted → in_progress → completed
├── blocked
├── approval_required
├── failed
└── cancelled
completed、failed、cancelled、rejectedは終端状態である。状態更新のために元のメッセージを書き換えるのではなく、新しい相関メッセージを発行する。これにより履歴が消えず、障害解析や監査が容易になる。
6. 自動化レベルと人間の承認
自律化は一気に全面開放しない。操作の影響に応じて5段階に分ける。
| レベル | 許可する処理 | 停止点 |
| L0 | 調査、読取り、報告 | 結果返信 |
| L1 | SDD・文書変更 | 実装開始前 |
| L2 | ソース変更とテスト | commit前 |
| L3 | task branchへのcommit・push | merge前 |
| L4 | merge、デプロイ、実API mutation、外部影響操作 | 人間の明示承認 |
初期リリースはL0からL2までとする。安定性を確認した後、L3を有効にする。L4はrunnerが勝手に実行できない固定ゲートとする。
特に次の操作はL4として扱う。
mainへのmerge- 本番デプロイ
- 実MiTiR APIへのmutation
- RM-T10の実行
- Trading mutation
- データ削除
- 認証・ネットワーク・セキュリティ設定の変更
Git上の「実装してよい」という依頼は、実サービスへmutationしてよいという承認と同一ではない。
7. 重複実行と競合の防止
Gitの更新通知は、通信障害や再起動によって何度も観測される。そのため、処理済みIDをローカルSQLiteへ保存する。
保存する主な情報は以下である。
message_idcorrelation_id- 受信commit SHA
- 正規化payloadのSHA-256
- 処理状態
- 試行回数
- worktreeとbranch
- 結果commit SHA
- テスト結果
- 開始・終了時刻
- エラー分類
同じmessage_idと同じpayload SHAが再度届いた場合はexact replayと判断し、コードを再実行しない。安全であれば前回結果だけを再通知する。
同じIDなのにpayload SHAが異なる場合は、単なる再送ではなく競合または改変として拒否する。これが「少なくとも1回届く可能性がある通信」を安全な「実質1回実行」に近づける中核となる。
取消も冪等にする。開始前なら取消、実行中なら安全に停止要求、すでに完了していれば「完了済みであり取消によって元に戻してはいない」と応答する。
8. worktreeによる作業分離
1メッセージにつき1 worktree、1 branchを割り当てる。
worktrees/
├── JARVIS-20260816-0001/
├── JARVIS-20260816-0002/
└── MITIR-20260816-0003/
branch名の例は次のようになる。
handoff/JARVIS-20260816-0001
この方式には以下の利点がある。
- 人間がVS Codeで作業中のリポジトリを汚さない
- タスク同士の変更が混ざらない
- タスク単位でテスト、破棄、再実行できる
- branchとmessage IDを対応付けられる
- mainへの誤pushを防ぎやすい
初期段階では、各マシンで同時に1件だけ実行する。並列化は状態管理、CPU・メモリ使用量、Git競合を複雑にするため、安定後の拡張項目とする。
9. handoff文書をそのままプロンプトにしない
最大の注意点は、handoff文書も外部入力であるという事実だ。Markdown本文をそのまま強い権限を持つCodexへ渡すと、意図しない命令やプロンプトインジェクションが実行される可能性がある。
安全な境界は次のように作る。
- MarkdownからYAML envelopeだけを抽出
- 閉じたSchemaで検証
- 許可された送信元とmessage typeを確認
- execution levelと禁止操作をPolicyEngineで判定
- allowlistされたpayload項目のみ取り出す
- runner側の固定テンプレートへ埋め込む
- Codexをsandbox付きで起動
生成するプロンプトは概ね次の形になる。
Process validated handoff JARVIS-20260816-0001.
Read AGENTS.md and the relevant SDD documents.
Implement only the approved scope.
Execution level: L2
Prohibited actions:
- live API mutation
- Trading mutation
- commit or push
- changes outside this worktree
- reading or outputting secrets
Run the complete test suite and return a structured result.
Stop when approval is required.
APIキー、GitHub token、秘密鍵、認証ヘッダーなどは、handoff、プロンプト、テストfixture、ログ、Git履歴へ入れない。OSのCredential Manager、Keychain、または安全な環境注入を使用する。
10. runnerの内部構造
runnerを一枚岩にせず、責務ごとに分割する。
| コンポーネント | 責務 |
| Poller | 定期監視と更新検出 |
| GitTransport | fetch、文書取得、branch push |
| HandoffParser | MarkdownとYAMLの解析 |
| SchemaValidator | 閉じた契約の検証 |
| StateStore | SQLiteによる冪等性と復旧 |
| PolicyEngine | 実行レベル、承認、禁止操作の判定 |
| PromptBuilder | 検証済み入力から固定プロンプトを生成 |
| WorktreeManager | worktreeとtask branchの管理 |
| AgentExecutor | codex execの起動、監視、timeout |
| ResultPublisher | 応答メッセージの生成と送信 |
| SecretRedactor | ログと結果の秘密情報除去 |
| RecoveryManager | 再起動後のロック・途中状態復旧 |
JARVISとMiTiRは共通プロトコルを持つが、OS常駐設定やリポジトリパスは異なる。共通ロジックとplatform adapterを分離することで、将来別のプロジェクトへも展開できる。
また、このGit handoff runnerはMiTiR HTTP APIのリクエスト処理へ埋め込まない。APIと開発自動化を独立プロセスにしておけば、handoff側の障害が専門業務APIへ波及しにくい。
11. 障害、再試行、無限ループへの対策
自律エージェント同士の通信では、正常系より停止条件が重要になる。
Git障害
- fetchやpushは回数制限付きで再試行
- exponential backoffとjitterを利用
- force pushは禁止
- mainへの直接pushは禁止
- 解釈が必要な競合は自動解決せず
blockedにする
プロセス障害
- wall-clock timeoutを設定
- 起動中message IDをロック
- stale lockを検出
- 再起動後はSQLiteから状態を復旧
- 中途半端な完了報告を送らない
無限応答ループ
max_hopsを設けるstatus_responseやresult_responseから新たな実装を自動起動しない- exact replayに繰り返し返信しない
- 相手宛てのメッセージを処理しない
- 相関チェーンの最大メッセージ数を制限する
緊急停止
kill switchを用意し、メッセージの検出と表示は続けながら、Codexの起動だけを無効化できるようにする。
12. テスト戦略
この仕組みは実GitHubや本番APIを使わなくても大部分をテストできる。
一時ディレクトリへJARVISとMiTiRを模したローカルGitリポジトリを作り、fake agent executorを使って次を検証する。
- 正常なメッセージ受信と応答
- 必須フィールド不足
- 未知フィールド
- 壊れたMarkdownまたはYAML
- exact replay
- 同一ID・異なるpayload
- 同時実行の防止
- 期限切れ
- 開始前・実行中・完了後の取消
- runner強制終了後の復旧
- Git fetch/push失敗
- branch名衝突
- dirty worktree保護
- timeout
- 秘密情報のredaction
- 悪意のある命令を含むpayload
max_hopsとループ防止- mainへの直接pushがないこと
- live mutationがないこと
- Trading mutationがないこと
- 相手リポジトリの許可外ファイルを変更しないこと
その後、dry-runで実GitHub上のhandoffを検出し、Codexを起動せずに処理計画だけを表示する。最後にL2の実装・テストまでを受入試験する。
13. 段階的な導入計画
実装は次の順序が安全である。
Phase A:SDDと共通契約
- Spec、Structure、Task、Testingを両プロジェクトへ追加
- Handoff Protocol v1.0.0を固定
- 両側の正規化Schema SHA-256を一致させる
Phase B:L0ポーラー
- fetchと更新検出
- parserとSchema検証
- SQLiteへの処理済み記録
- メッセージを表示するだけでCodexは起動しない
Phase C:L1文書処理
- worktree作成
- 固定プロンプト生成
- SDD更新
- commit前で停止
Phase D:L2コード実装
- コード変更
- 単体・契約・回帰テスト
- 結果返信
- commit前で人間確認
Phase E:L3 task branch連携
- 自動commit
- task branchへのpush
- Draft PRまたは結果通知
- merge前で停止
Phase F:限定的なL4
十分な運用実績と追加承認を経た後にのみ検討する。merge、デプロイ、実API mutationは用途別の追加ゲートを必要とする。
14. モデルの使い分け
初期設計と中核実装は、長いSDD、複数リポジトリ、Git安全性、状態遷移を同時に理解する必要がある。そのため、知能とコストのバランスがよいGPT-5.6 Terraを中心に使う。
- SDDと全体構造:Terra / high
- PolicyEngine、復旧、Git競合:Terra / high
- 定型的なモデル、CLI、テスト追加:Luna / medium
- 結合時の複雑な不具合:Terra / high
- 最終的な高リスク設計レビュー:必要な場合のみSol
運用が安定した後は、通常のL0~L2メッセージをLunaで処理し、失敗、競合、セキュリティ関連だけTerraへ昇格させる方式が費用対効果に優れる。
15. 構築工数の目安
既存のJARVIS・MiTiRリポジトリとSDDを利用できる場合、概算は以下となる。
| 到達レベル | 内容 | 目安 |
| L1 | 検出、解析、文書処理 | 6~10時間 |
| L2 | 自動コーディング、テスト、commit前停止 | 12~18時間 |
| L3 | task branchへのcommit/pushと結果返信 | 20~30時間 |
| L4 | merge、実API、デプロイ | 追加10~20時間以上 |
最初の実用目標は約15時間でL2を完成させること。その後、複数回の相互handoff試験を行い、追加約8時間でL3へ進むのが現実的である。
16. この仕組みの本質
この構想の本質は、二つのAIが勝手にコードを書くことではない。
重要なのは、次の要素をコードと同じレベルで明示的に管理することである。
- 誰が依頼したか
- 何が許可されているか
- 何をしてはいけないか
- どの仕様に従うか
- 同じ依頼を再実行しないか
- どこで人間へ戻すか
- 失敗後にどう復旧するか
- 相手側へ何を返したか
Gitは履歴を残し、SDDは判断基準を与え、runnerは継続性を担い、Codexは限定された作業を実行する。これらを組み合わせることで、JARVISとMiTiRは独立性を保ちながら協調できる。
将来的には、この構成をタービン統合設計、調査システム、複数アプリの保守、企業内エージェント連携などへ展開できる。プロジェクトごとに業務ロジックは変わっても、Gitを監査可能なhandoff層として用い、SDDと承認ゲートの内側でAIエージェントを動かすという基本構造は再利用できる。
まとめ
JARVISとMiTiRの自律開発連携は、次の構成で実現する。
- Gitのhandoff文書を低頻度・監査可能なメッセージキューとして利用
- 双方向の書込み先を一つずつに限定
- 常駐runnerが
fetchで変更を検出 - 閉じたYAML契約でメッセージを検証
- SQLiteでexact replayと競合を管理
- 1依頼1 worktree・1 branchで変更を隔離
- 固定プロンプトからCodexを非対話実行
- L0~L4の実行レベルで権限を制御
- merge、実API mutation、Tradingなどは人間承認を維持
- ローカルGitとfake executorで異常系までテスト
目指すのは無制限な完全自動化ではない。安全に停止でき、履歴を説明でき、必要な場所で人間が判断できる自律開発システムである。

コメント