Fleet
FleetのYAMLで依存関係付きの複数エージェント処理を定義し、ライブグラフで監督し、停止や失敗から安全に復旧する方法を説明します。
Fleetは、複数のAIエージェント、コマンドによる検証、人の承認を依存関係グラフとしてYAMLに定義し、一つのRunとして実行する機能です。各エージェントノードは通常のCockpitタスクとして動き、Cockpitが実行順、並列数、待機、再開、履歴を管理します。
単発の仕事を一つだけ別タスクへ渡す場合はcockpit task runを使います。依存順や並列処理を繰り返し再現したい場合、途中にテストや承認を置きたい場合、アプリ再起動後も同じ処理を復旧したい場合にFleetを選びます。
FleetとRunを区別する
| 用語 | 意味 |
|---|---|
| Fleet | ノード、依存関係、引数、既定のエージェント設定を記述した再利用可能なYAML |
| Run | そのFleetを一度実行した記録。固有のrunId、作業場所、イベント履歴を持つ |
| ノード | エージェントタスク、既存タスクへのメッセージ、command gate、human gate、bounded loopのいずれか |
| Fleetパネル | Runの依存グラフ、各ノードの状態、タスク、レポート、イベントを表示するライブ画面 |
Fleetの定義を変更しても、開始済みRunの定義と履歴は書き換わりません。変更後の定義を最初から試す場合は、既存Runのretryではなくrerunで新しいRunを作ります。
保存場所を選ぶ
| スコープ | 保存場所 | 向いている用途 |
|---|---|---|
| Project | <repo>/.cockpit/fleets/<name>.yaml |
一つのリポジトリで繰り返す開発・検証。RunごとにGit Worktreeを作る |
| Global | ~/.agi-tools/data/cockpit/fleets/<name>.yaml |
複数リポジトリをまたぐ処理、またはリポジトリを必要としない処理 |
Project FleetはCI設定と同じくリポジトリのワークフローです。チームで再利用する定義はコミットします。Global Fleet自体にはリポジトリがないため、必要なノードごとにdirectoryを指定します。
最小のFleetを作る
次のProject Fleetは、調査、実装、テスト、要約を順に進めます。defaultsはエージェントノードへ継承され、needsが依存関係を作ります。command gateはエージェントを起動せず、コマンドの終了コード0だけを成功として扱います。
.cockpit/fleets/check-and-fix.yaml
version: 1
name: check-and-fix
description: Investigate one problem, implement the fix, and verify it
args:
target:
required: true
description: What to fix
defaults:
agent: codex
effort: medium
max_parallel: 2
nodes:
investigate:
prompt: |
Investigate {{args.target}}. Change nothing.
Report the cause, affected files, and a concrete fix plan.
implement:
needs: [investigate]
prompt: |
Implement the fix and verify the focused behavior.
Investigation: {{needs.investigate.report}}
tests:
type: gate
gate: command
needs: [implement]
run: pnpm test
summarize:
needs: [tests]
prompt: |
Review the completed change and report the result, changed files,
and verification evidence.
versionは現在1だけを使用します。nameとノードIDには英数字、_、-を使用できます。未知のキー、存在しない依存先、循環、解決できない展開式、エージェントが対応しない明示設定は検証エラーになります。
検証してから実行する
YAMLを保存したら、タスクを一つも作る前に検証します。
cockpit fleet validate .cockpit/fleets/check-and-fix.yaml --directory /path/to/repo
初回は--max-parallel 1で順番に動かし、ノードの責務、受け渡すレポート、変更範囲が意図どおりか確認します。問題がなければ並列数を上げます。
cockpit fleet run check-and-fix \
--directory /path/to/repo \
--arg target="search indexing" \
--max-parallel 1
runとrerunはRunを作る前に、固定指定、--set、Autoを含む全エージェントノードのアカウントをauthStateで確認します。一つでも期限切れ、サインアウト済み、または利用可能なAuto候補がない場合は、ノードとアカウントを列挙してRunを作成しません。対象へ再ログインしてから再実行します。先行ノードの実行中にログインする設計など、意図的に確認を省く場合だけ--skip-account-checkを使います。
runは処理完了を待たず、すぐにrunIdを返します。runへ--waitを付けず、続けてFleetパネルを開き、必要なときだけ待機します。
cockpit side-panel fleet <runId>
cockpit fleet wait <runId>
waitはRunが完了・失敗・停止・一時停止したとき、human gateが回答待ちになったとき、またはノードが中断されたときに返ります。監視プロセスが終了した場合は、直前のlatestSeqから再開できます。
cockpit fleet wait <runId> --since <latestSeq>
タイムアウトはRunの失敗ではありません。継続監視にはwait、一回だけ現在値を読む場合はstatusを使い、短い間隔でstatusを繰り返さないでください。
waitはRunを開始したタスクを自動再開しません。開始元へ完了を返したい場合は、そのタスクからfleet.run.* Hookを登録し、--caller-task selfでそのタスクが開始したRunだけに絞ります。
cockpit hooks add --event fleet.run.completed \
--caller-task self \
--run 'cockpit task send "${COCKPIT_FLEET_CALLER_TASK_ID}" --text "Fleet ${COCKPIT_FLEET_NAME} finished: ${COCKPIT_FLEET_RUN_STATUS}"'
--caller-task selfはHookの追加または更新時に呼び出し元のtask IDへ解決され、呼び出し元タスクがない環境では失敗します。同じタスクからCLIでrerunしたRunも一致しますが、UIの「Run this Fleet again」には呼び出し元タスクがないため一致しません。
ライブグラフを読む
Fleetパネルでは、依存関係と次の状態を同時に確認します。
- どのノードが実行中、待機中、完了、失敗、スキップ、除外、承認待ちか
- 並列ブランチのどこまでが終わり、どの依存先が後続を止めているか
- 各エージェントノードが使う実タスク、作業場所、ブランチ、試行回数
- ノードのレポート、構造化出力、command gateの結果、Runイベント
フローの線は依存元から後続ノードへ向かい、処理中の線はその方向へ動きます。OSで動きを減らす設定が有効な場合、Cockpitはこの動きを止めます。Fleet一覧もフロー図と同じノード・Runの状態表示を使うため、Runを開く前後で状態の意味が変わりません。
ノードの「イベント」を選ぶとevent timelineを確認できます。eventは試行ごとにまとまり、時刻とevent間の経過時間が表示されます。実行中の試行では現在までの経過時間も更新されます。待ち時間の位置をtimelineで見つけ、原因はレポート、タスク、gate出力で確認してください。
Runの見出しにある進捗バーは、全ノードを完了、実行中、失敗、中断、停止、スキップ、待機中の状態別に色分けし、完了数と総数を表示します。処理時間や完了予定時刻ではなく、現在のノード状態の内訳です。
実タスクを持つ各ノードでは、cockpit fleet status <runId>のprogressから、最後にruntime eventを受け取った時刻、実行中ターンの開始時刻と経過時間、そのターンのtool call数を確認できます。長いターンではlastActivityAtやCPU使用率ではなく、progress.lastRuntimeEventAtが更新されているかで動作中かを判断します。この値は照会時に計算され、Run履歴には保存されません。同じ値はcockpit task get <taskId>でも確認できます。
agent nodeへidle_timeoutまたはtimeoutを設定すると、runtime eventが指定時間届かない場合、またはnodeの総実行時間を超えた場合にタスクを停止し、idle_timeoutまたはtimeoutとして失敗させられます。値は20m、2h30mのように指定し、最短は1分です。Ask、承認、質問への回答待ちはidle_timeoutへ数えません。失敗後はcockpit fleet retryで復旧します。
これらを設定していなくても、Grok Build、Cursor、Qoderのターンが、承認、質問、background処理、tool callを待っていない状態で18分間まったく応答しない場合、Cockpitはstalledとして停止します。Fleetは一時的なruntime errorとしてbackoff付きで再試行し、同じ会話から再開します。1ターンの12時間上限に達した場合は自動再試行しません。
Antigravityの予約された継続処理やバックグラウンド継続処理がタイムアウトすると、出力を提出済みでも、FleetはerrorReason: continuation_timeoutとしてノードを失敗させます。一時的なプロバイダーエラーとして扱わず、自動再送もしません。cockpit fleet retryで再試行するか判断する前に、タスクへ遅れて届いた最終応答と、バックグラウンド処理、サブエージェント、ツールによる変更や外部操作を確認してください。
gateノードを選ぶと、command gateでは実際の終了コード、human gateでは承認または却下と回答本文を詳細画面で確認できます。終了コード0は成功、それ以外は失敗です。Run全体の状態だけで判断せず、該当gateの詳細と出力を確認してください。
PWAでは、Runに属するタスクから「タスクパネル → Fleet」を開きます。このタブはDesktopのグラフの代わりに、依存順のステップ一覧でRunの状態と進捗を表示します。ノードを選ぶと、依存関係、エージェントとモデル、状態、attempt、runtime、時刻、所要時間、loopまたはforeachのcontext、gateの結果、失敗の詳細を確認できます。PWAで開けるタスクがノードにある場合は、「このノードのタスクを開く」で移動できます。loopは現在のiterationを最初に開き、大きなグループは一定件数ずつ表示します。
PWAのFleetタブは監督用です。一時停止、再開、再試行、停止、gateへの回答には、DesktopのFleetパネルまたはcockpit fleetコマンドを使います。表示中のタブはRunの変更に合わせて更新されます。
CLIで詳しく確認する場合は次を使います。
| 確認したいこと | コマンド |
|---|---|
| 現在状態 | cockpit fleet status <runId> |
| 全ノード、解決済みランタイム、branch、task ID | cockpit fleet show <runId> |
| Runの判断経緯 | cockpit fleet logs <runId> |
| 一つのノードの判断経緯 | cockpit fleet logs <runId> --node <nodeId> |
| agent / messageノードの現在のレポート、状態、失敗理由 | cockpit fleet output <runId> --node <nodeId> |
| command gateの全量ログ | cockpit fleet output <runId> --node <gateId> [--attempt <n>] |
| ノードの実タスク | cockpit task get <taskId> |
agentノードのビジュアル実行が失敗した場合も、現在の試行のレポート、status、errorはRunへ残り、Fleetパネルとfleet outputから確認できます。agent / messageノードの以前の試行はこのコマンドに保持されず、--attemptは現在の試行番号だけを受け付けます。履歴上の判断経緯はfleet logsで確認します。
実行中ノードのタスクへ直接Steerを送る、turnをキャンセルする、タスクを完了・削除する操作は、そのノードを中断してRunを一時停止させます。一時停止後は新しいノードを開始しませんが、すでに並行実行中のノードは完了まで継続します。loop bodyの一つへ割り込んだ場合も兄弟ノードを中止せず、それらが終わるとloopはinterruptedになります。resumeは中断されたノードだけを同じiterationで再実行し、完了した兄弟の結果を保持します。実際の失敗では、従来どおりそのiterationの残りを中止します。意図的に割り込む場合を除き、ノードタスクではなくFleetパネルとfleetコマンドから監督します。
Fleet定義を読み込めない場合、パネルにはファイルが見つからない、YAML構文、スキーマ検証、その他の読み込み失敗の区別と、対象パス、検証項目が表示されます。パスが分かる場合は「ファイルを開く」で修正し、「再読み込み」で同じ定義を読み直します。ファイルが存在しない場合は表示されたパスを確認して作成してから再読み込みします。
別のコンピューターのFleetを操作する
すべてのcockpit fleetコマンドは、登録済みCockpitコンピューターを--host <host-or-alias>で指定できます。接続先では会員向けRemote Accessを有効にしておく必要があります。リモートFleet操作はリモートのタスクと同じ方法で認証します。同じtailnetで、Tailscaleの所有者が同じコンピューターにはtokenは不要で、それ以外の接続にはペアリング済みBearer tokenが必要です。Tailscale限定モードでは、Tailscale peerでもloopbackでもない接続を拒否します。
cockpit fleet list --runs --host build-host
cockpit fleet status <run-id> --host build-host
cockpit fleet retry <run-id> --node <node-id> --host build-host
Fleet定義、file selector、--directoryは接続先コンピューター上のパスです。ローカルのYAMLファイルはuploadされません。名前付きFleetは接続先のglobal定義から解決され、project Fleetでは接続先のproject directoryを明示します。リモート応答には接続先deviceの情報が含まれます。
ノードへ役割を割り当てる
エージェントノードにはagent、model、effort、account、browser_identity、service_tier、workspace、approvalを設定できます。defaultsは多数派の設定に使い、違うノードだけ上書きします。実行時の一時的な変更には--set '<node>.<field>=<value>'または--set '*.<field>=<value>'を使います。
一つのRunでClaudeに設計、Codexに実装、Grokに調査を割り当てられます。ただし、エージェントが対応しない設定をノードへ直接書くと検証に失敗します。defaultsまたは*から広く継承された非対応設定だけは、そのノードから除外されます。
approvalはsupervised、accept-edits、auto、full-accessを指定できます。auto(自動審査)はClaude、Codex、GrokのネイティブUIで利用できます。非対応エージェントのノードへ直接指定すると検証エラーになります。defaultsや*.approvalから継承したautoは、承認モードに対応する他のエージェントではsupervisedになり、承認モード自体に非対応なら除外されます。エージェントとモデルで自動審査の境界も確認してください。
max_parallelはグラフの幅ではなく、同時にアクティブにできるノード数です。既定値は4、範囲は1〜32です。マシンの容量と利用できるエージェントアカウント数に合わせます。
子タスクの承認待ちと解除の通知は、ノード完了や成果として扱いません。Fleetは通知の後も通常の成果レポートを待ち続けます。承認要求そのものは、タスク詳細で内容を確認して判断してください。
結果を後続へ渡す
自由文のレポートは{{needs.<id>.report}}、変更ファイルは{{needs.<id>.files}}、分離Worktreeのブランチは{{needs.<id>.branch}}で参照します。引数は先にargsへ宣言し、{{args.<key>}}で使います。依存関係にない兄弟ノードや後続ノードは参照できません。
機械的な判定へ値を渡す場合は、レポート文を解析せずoutput_contractを使います。JSON Schemaで必須フィールドと型を宣言すると、ノードは完了前にcockpit fleet outputで値を提出し、Fleetがその場で検証します。
inspect:
prompt: Count unresolved problems and report the evidence.
output_contract:
type: object
required: [issues]
properties:
issues: { type: integer, minimum: 0 }
clean:
type: gate
gate: command
needs: [inspect]
run: test "{{needs.inspect.output.issues}}" -eq 0
自由文は説明、構造化出力は条件や数値の受け渡しに使い分けます。command gateへエージェントが書いた文字列を直接展開せず、数値・真偽値またはFLEET_NEEDS_<ID>_OUTPUTとして渡される検証済みJSONを使います。
並列編集とWorktreeを設計する
Project FleetはRunごとにfleet/<name>-<runId>ブランチの共有Worktreeを作ります。workspace: sharedが既定で、直列ノードは前のノードがディスクへ残した変更を読めます。
同時にコードを編集するノードにはworkspace: isolatedを指定します。各ノードはfleet/<name>-<runId>-<nodeId>ブランチの別Worktreeで動くため、互いの変更は見えません。分離ノードにはコミットまで依頼し、後続の一つのintegrateノードで{{needs.<id>.branch}}を参照してマージします。
Fleetランタイムはブランチを自動マージしません。どの変更を採用し、競合をどう解くかは判断を持つintegrateノードの責務です。並列編集へsharedを使うと同じファイルを同時に変更できるため、読み取り専用または明確に分離した範囲だけに限定します。
Global Fleetでworkspace: isolatedを使う場合は、そのノードへリポジトリルートのdirectoryが必要です。指定先がGitリポジトリでなければ、通常のフォルダーへ黙って切り替えずノードが失敗します。
分岐、展開、反復を使う
| 機能 | 用途 | 重要な挙動 |
|---|---|---|
when |
上流のpass・approvalに基づく実行時分岐 | falseのノードはskippedになり、その下流にもskipが伝播する |
enabled |
Run引数で高コストな任意ノードを外す | Run作成時に一度評価され、falseはexcluded。依存順を保ったまま後続は動く |
foreach |
調査で見つけた一覧を一件ずつ並列処理する | 各項目がmax_parallelを一枠使い、全項目の結果を後続へまとめる |
| bounded loop | 修正と検証を上限回数まで繰り返す | max_iterationsが無限反復を防ぎ、until.runの終了コード0で抜ける |
| message node | Fleet外の既存Cockpitタスクへ指示を送り、そのreportを待つ | 外部タスクを所有せず、Runを停止・削除してもそのタスクは完了・停止しない |
enabledを使うFleetは、実行前に同じ引数を付けてvalidateし、どのノードがexcludedになるか確認します。foreachで書き込みを並列化する場合はworkspace: isolatedを使います。loopには必ず現実的な上限を置き、上限到達後にさらに反復すべきかは保存済みの証拠を見て判断します。
gateで事実と承認を置く
| gate | 成功条件 | 使う場面 |
|---|---|---|
| command | 非対話コマンドが終了コード0で終わる | テスト、lint、型検査、公開状態の機械確認 |
| human | Cockpit Askで利用者が承認する | push、公開、削除、送信、課金など外部に見える、または不可逆な操作の直前 |
command gateは30分でタイムアウトし、同じGitリポジトリのWorktreeを使う別Runのcommand gateとは直列化されます。retriesは0〜3ですが、自動再試行はVitest形式で1〜2件の失敗テストを特定できる負荷依存flakeだけが対象です。型エラー、ビルド失敗、タイムアウト、同じテストの連続失敗はすぐに失敗として扱います。
各command gateのstdout / stderr全量は、Runのfleet-runs/<runId>/gates/へ試行ごとの.logとして保存されます。自動再試行やfleet retryでも以前のfileを上書きしません。20 MBを超えるlogは先頭と末尾を残し、省略したbyte数を中央に記録します。status --jsonとshowは最新試行のoutputs.logPathを返し、Vitest形式のFAIL行がある場合は全streamから抽出した重複なしのoutputs.failedFilesも返します。Fleetパネルでは失敗fileを確認し、全量ログを開くでサイドパネルへ表示できます。CLIではcockpit fleet output <runId> --node <gateId> --attempt <n>を使います。loopのuntilではattempt番号がそのloopのuntil実行回数です。
Vitestのcommand gateがタイムアウトした場合、gateの出力と失敗テストには、タイムアウト前に失敗したテスト、出力を開始したまま完了しなかったテストファイル、30秒以上かかった完了済みテストのうち遅い順に最大5件が記録されます。要約だけで原因を判断せず、保存済みの全量logも確認します。
human gateのAskは、グラフを見なくても判断できるよう、完了したこと、確認した証拠、承認後に起きる外部操作を本文だけで説明します。却下は下流をskipし、Askを閉じただけの場合はノードが中断されてRunが一時停止します。
一時停止と失敗から復旧する
| 状況 | 操作 | 引き継ぐもの |
|---|---|---|
| 新しいノードの開始だけ止めたい | cockpit fleet pause <runId> |
実行中ノードは継続する |
| 一時停止、アプリ再起動、中断から続けたい | cockpit fleet resume <runId> |
同じRun、同じ作業場所、完了済みノード |
| 一つの失敗原因を直して下流だけやり直したい | cockpit fleet retry <runId> --node <nodeId> |
無関係な完了・pass済みノード |
| 定義変更後に最初から試したい | cockpit fleet rerun <runId> |
同じFleetと引数。新しいrunIdと作業場所を作る |
| 実行中タスクとgateを止めたい | cockpit fleet stop <runId> |
保存済みRunは残り、後でresumeできる |
| 不要な履歴を完全に消したい | cockpit fleet remove <runId> |
何も引き継がない。終端Runだけ削除できる |
失敗ノードの下流はskipされますが、独立したブランチは最後まで動きます。最初にlogs --nodeで原因を確認し、実際に誤っている最も上流のノードをretryします。下流だけをretryしても、誤った上流出力は直りません。
アプリ再起動後は、生きているタスクへ再接続し、それ以外の実行中ノードをinterruptedとして保存します。resumeすると通常のエージェントノードは同じ作業場所の新しいタスクで続くため、各promptは現在のファイルを再確認してから作業できる内容にします。
Autoアカウントの切り替えが進行中の間、Fleetはノードをrunningのまま維持し、切り替え後の同じタスクのreportを待ちます。利用可能な別アカウントがなく復旧できなかった場合にだけノードをinterruptedにします。その場合は、別のサインイン済みprofileへ切り替えて再開できます。
cockpit fleet resume <runId> --set '*.account=<profile>'
cockpit fleet retry <runId> --node <nodeId> --set '<nodeId>.account=<profile>'
Cockpitは直近200件の終端Runを保持します。removeはイベント履歴を恒久的に削除するため、必要なレポート、差分、公開URLを保存し、実行中または一時停止中なら先にstopしてから行います。
Runに残るタスクをまとめて完了する
完了、失敗、停止、一時停止したRunでは、FleetパネルのRunメニューまたはCLIから、Runが作成して未完了のまま残したタスクをまとめて完了できます。実行中のRunは対象外です。
cockpit fleet complete-tasks <runId> --dry-run
cockpit fleet complete-tasks <runId>
最初に--dry-runで、完了対象、完了済み、処理中のためスキップ、未完了loopが再利用するためスキップ、見つからないタスクを確認します。共有される同じタスクは一度だけ処理され、messageノードの外部タスクはFleetの所有物ではないため対象になりません。実行後もRunとタスクの履歴、およびGit Worktreeは保持されます。一時フォルダのタスクだけは完了時に作業場所が削除されるため、必要なファイルを先に保存してください。
完了を判定する
FleetパネルでRunがcompletedになったことだけでなく、次を確認します。
- 必須ノードが完了またはpassし、意図しない
skipped、excluded、interruptedがない - 分離ブランチをintegrateノードが取り込み、競合解決とコミットを報告している
- command gateが対象の最終作業場所で成功している
- human gate後の外部操作は承認された範囲だけで実行され、結果URLやIDを確認できる
- 最終レポートが変更内容、検証結果、残った制約をまとめている
構文と全オプションはcockpit fleet Reference、一件の委任と親子タスクはタスク管理(CLI)、分解方法そのものを任せる場合はMaster Agentを参照してください。