auto-dev — Issue を実装しマージする継続的なフリート
機能概要
implement-issue は計画された 1 つの Issue を準備完了の PR にし、merge-pr は 1 つの PR をマージします。auto-dev はこれらをスケールして統括します:N 個のバックグラウンドワーカーがそれぞれ 1 つの Issue をエンドツーエンドで担当し(implement-issue <N> → merge-pr <PR>)、ワーカーの PR がマージされると即座にそのワーカーは退役し、キューから次の Issue に対して新しいワーカーが起動されます。これにより、ユーザーが監視しなくても N 個の Issue が同時に進行します。
この価値は、単純な「ループで実行する」方法にはないオーケストレーションにあります:
- 競合回避 — 各同時ワーカーは異なるコード領域の Issue を受け取るため、ブランチが衝突することはほとんどありません。
- 優先順位付け — 最初に小規模で自己完結した Issue(迅速な成果、競合が少ない)、次に中規模 Issue。L/XL および手動 QA はデフォルトで除外されます。
- 強制マージ — ワーカーは「PR 準備完了」で停止する傾向があります(仕事の半分)。auto-dev はすべてのシグナルで GitHub から実際のマージ状態を検証し、停滞したワーカーを再駆動します。
- スコープ外の捕捉 — 関連のないバグに遭遇したワーカーは、
implement-issueの特別枠(自身の PR が既に変更するファイルにローカルで、かつ小規模で、独自のコミット内)に従ってインラインで修正し、それ以外の場合はcreate-issue経由で Issue を作成し、共有のファイリング基準(../_shared/filing-bar.md)に従ってドロップしないようにします。これにより、N 個のワーカーのフリートが N 通りの異なる基準でファイリングすることがなくなります。バックログは正確で、かつ消化可能な状態を保ちます(#410)。 - ライフサイクルの衛生 — 完了したエージェントはシャットダウンされ、状態ファイルは再起動後も存続するため、フリートは再開可能です。
自律契約
ユーザーはこれを開始したら離れます — 各ステップを承認するのではなく、バックログが消化されるのを見守ります。ハンズオフで実行します:合理的なデフォルトを選択し、それを明示し、続行します。真のブロッカーの場合のみ停止します:
ghが認証されていない、またはプッシュ/マージ権限がない。- 対象となる Issue がない(フィルタリング後キューが空)— バックログが消化されたことを報告します。
- ワーカーの正直なハードブロッカー(テストがグリーンにできない、両側で同じロジックを書き換えた競合、自分で付与できない必要な承認)。それを表面化します;マージを強制しないでください。
進行状況を偽装しないでください:チェックボックス、「準備完了」フリップ、マージは作業が 完了した と主張します — 子スキルが要求するように、それぞれを証拠で裏付けてください。スカッシュマージは唯一不可逆的なアクションです — それは merge-pr を通じてグリーンでマージ可能な PR に対してのみ行われ、ここで --admin オーバーライドを使用することはありません。
入力
- 同時実行数
N— 同時ワーカー数。デフォルト 3(3~4 が最適値;それ以上は競合が増え、領域を分離しにくくなります)。 - 優先順位付け — デフォルトは小→中規模の努力。ユーザーの指定を尊重します。
- 対象セット — デフォルト:オープンな Issue で、(a) 実装計画があり、(b) コードタスクであり(手動の「QA」/「手動確認」ではない)、(c) 努力上限内(デフォルト ≤ 中)。絞り込みを尊重します(「
studioの Issue のみ」、「priority: highラベル」)。 - ハートビート — 通常は動的モードで
loopを介して起動されるため、ループの自己ペースのウェイクアップがハートビートとなります。直接起動された場合は、独自のウェイクアップを設定します。 - モデルティア — デフォルトは 階層化、すべて Opus ではない(トークン経済学を参照):機械的なタスク/ドキュメントには最も安価な有能なモデル、典型的なバグにはミッドレンジ、重要な横断的/難しい作業および障害エスカレーションにはトップモデルを予約。単一最大のコストレバー。オーバーライドを尊重します(「すべて Opus で」、「すべて Sonnet で」)。
トークン経済学 — フリートを安価に運用する
長時間の実行コストは 約 83% がターンごとのコンテキストキャッシュ読み取り、約 16% が出力 です — あなたが支払っているのは コンテキスト容量 × ターン数 であり、思考に対してではありません。以下のルールを適用してください。完全な根拠と測定結果は references/token-economics.md にあります。
実際の 19 マージ実行(.NET/Blazor リポジトリ、N=3、21 ワーカーセッション、863M トークン)での測定: 224 ターン/セッション · コンテキスト 30K → 350K · ターンあたり平均コンテキスト 181K · ターンあたりツール呼び出し 0.55 回。キャッシュ読み取りはトークンの 98.3% でした。コスト = ターン数の合計に対するコンテキストサイズの総和 であるため、後のターンは初期のターンの約 10 倍のコストがかかり、ターン数は超線形 です — 両方の要素を同時に押し上げます。レバーは直感ではなく、これに基づいてランク付けしてください。
その見出しは ワーカー セッションをカウントしています — そしてオーケストレーターは請求額の 3 分の 1 でした。 スーパーバイザー自身のトランスクリプトを同じ集計に含めると:1 セッション、551 のアシスタントメッセージ、メッセージあたり平均コンテキスト約 210K、実行リスト換算コストの 33% — トップティアのワーカーセッションすべてを合わせたよりも多く — そして 19 回のマージすべてを通じて 一度もコンパクト化されませんでした。ワーカーの支出も同じように偏っています:37 のワーカーセッションのうち上位 3 つで全ワーカーコストの 32% を占め、最悪のケースはミッドティアの
effort: mediumIssue であり、ラベルもティアもそれを予測しませんでした。両方の測定と、それらを制限する 2 つのカウントされた予算は、references/token-economics.md § セッション長 にあります。
1. 各ワーカーを 2 つのサブエージェントに分割:実装、そしてマージ — 測定可能な効果があった唯一のレバー。A/B 検証済み、全ワーカートークンの約 11%。 フェーズ 1(/auto-dev-worker <N>)は準備完了の PR で停止します;フェーズ 2(/auto-dev-merge <PR>)はそれを新しいコンテキストでマージします。
| マージフェーズ | ターン数 | 平均 ctx/ターン | cacheRead |
|---|---|---|---|
| 前(18 ワーカー、実装セッション内) | 平均 27 | 247K | 6.6M ワーカーあたり |
| 後(v2 フェーズ 2、新しいセッション) | 24 | 71K | 1.7M |
ほぼ同一のターン数であるため、ターンあたりのコンテキストがすべての違いです:マージフェーズで -74%、ワーカー総支出の約 11%。フェーズ 1 のプロンプトで指定されたファイルを介して(またはレポート行、次に gh pr list --head にフォールバックして)フェーズ間で PR 番号を渡します。ボーナス:ベースライン実行で観測された両方のポスト完了ウェッジは、長時間セッションの 後半 のターンで発生しました — 短いセッションはウェッジが少なくなります。
2. ツール呼び出しのバッチ処理 — 試したが効果なし。ここで効果を期待しないでください。 例を含むハードなバッチルールがワーカープロンプトに追加され、A/B 測定されました:密度は 0.556 → 0.537 呼び出し/ターン(フェーズ 1)— すなわち変化なし。ルールは維持します(コストはかかりません)が、節約効果を予算化しないでください。診断:1.0 未満の密度は、バッチ処理 できる 呼び出しがバッチ処理されていないことを意味するわけでは ありません。ターンの約 45% は ツール呼び出しをまったく行いません — それらは推論/ナレーションのターンであり、残りのほとんどは真に逐次的です(編集 → ビルド → エラー読み取り → 修正)。回収可能な無駄は ツール呼び出しを生成しないターン であり、これはバッチ処理の問題ではなく、出力の冗長性の問題です。これに取り組む場合は、冗長性(思考の長さ、ステップ間のナレーション)に取り組み、呼び出し/ターンを測定して改善を確認してください。
3. 大きなコマンド出力をコンテキストから遠ざける — 測定値 4,262 トークン → 完全なテスト実行あたり約 2。完全な dotnet test 出力は約 17K 文字で、後続のすべてのターンで再読み取りされます。必須:ファイル にログし、EXIT + カウントのみをコンテキストに配置し、失敗時のみファイルを grep します。コマンドを tail/head にパイプすることを明示的に禁止します — それは証拠を切り詰めながら、依然としてターン数を消費します。
正直な範囲: 実行ごとの削減量は正確で検証済みですが、全体的な利得はワーカーがテストスイートを実行する頻度に依存します。A/B で最大の単一 Bash 結果は 25.2K → 10.5K 文字に減少しましたが、ターンあたりのツール結果総量はほとんど変化しませんでした(722 → 710 文字)— A/B の Issue はドキュメントタスクであり、テストをほとんど実行しなかったためです。テストの多い Issue で真の効果を期待してください。
4. モデルをタスクに階層化する — 実際に効果はあるが ティア依存、token-economics.md によると: ミッドとスモールの差は弱い($0.419/Mtok vs $0.168/Mtok ≈ 2.5 倍); トップとミッドの差は強い($2.376/Mtok vs $0.419/Mtok ≈ 5.7 倍)。 横断的な作業では優先する価値あり;ルーチンバグでは効果は低い。Issue ラベルでルーティングします(ランタイムの small/mid/top トリオに名前を適応させます。例:Haiku/Sonnet/Opus):
| Issue の形状(ラベル別) | ティア |
|---|---|
ドキュメント/テンプレート/マニフェスト、フォーマット&スナップショット再生成、priority:low+effort:S の一行ガード | スモール(例:Haiku) |
ほとんどのバグ:エミッター/バリデーター/パーサーガード、studio TS、CLI、LSP — effort:S/M;大規模 Issue はデフォルト | ミッド(例:Sonnet) |
| 下位ティアがこの Issue でグリーンに失敗した | トップ(例:Opus)、リアクティブエスカレーション(1 回) |
| 横断的(多くの領域/エミッター)、曖昧/設計 — メンテナーが難しいと知っている作業 | トップ(例:Opus)、予測的、最後の手段 |
オーケストレーター(あなた)はトップモデルに留まりますが、その ターンあたりのコンテキスト は小さく保ちます(レバー 2)。
5. 毎ターン再読み取りされるものを削減する。
- 固定プリアンブルをトリミングする — これはすべてのワーカーの すべての ターンで支払われます。44KB(約 11K tok)の
CLAUDE.mdは 1 つのファイルから 1 回の実行で 52M トークン のコストがかかりました;デプロイ/シークレット/kubectl の参考資料をリンクされたドキュメントに移動し、ポインタだけを残します。 - Issue ごとの TaskList は不要 — それは無制限に成長し、毎ターン再注入されます。状態ファイルだけが唯一のワーキングメモリです。
- 計画的に、カウントされたリズムでコンパクト化する — 感覚的に行わない。 リズムの整数は 1 か所にあります。references/token-economics.md § 2 つの予算;ステップ 4 はそれを状態ファイルのマージカウンターから起動します。これは再調査リズムが既に使用している同じカウントフィールドです。ここで数値を再記述しないでください —
tests/auto-dev-cost-budgets/test.shはそうするとビルドに失敗します。なぜカウントベースか: コストは Σ(コンテキスト × ターン) であるため、実行の末尾は超線形であり、このルールが置き換えるもの — コンテキストのパーセンテージ または 19 マージの実行内で一度も発動しないほど緩いリズム — は何も制限せず、1 回のセッションで 213 ドルのコストがかかりました。常に フォーカスディレクティブを付けて コンパクト化します。例:/compact keep the slot→issue/PR map, merge counter, queue order, filed follow-ups。コンパクト化 //clear/loopの再発火後の最初のアクション:状態ファイルを再読み取りする。 - ワーカーの最終レポートは簡潔に保つ — それらは後続のすべての調整ターンで再読み取りされます。
- 重い読み取りは使い捨ての
Exploreサブエージェントに委譲する — ファイルダンプはサブエージェントと共に消え、あなたのコンテキストに残りません。 - スーパーバイザーセッションをリーンに起動する — サブエージェントはスーパーバイザーの MCP セットを継承するため、あなたの セッションに接続されたすべてのサーバーがすべてのワーカーのすべてのターンに乗ります。ワーカーごとの MCP ストリッピングは利用できなくなりました(Agent ツールには spawn ごとの MCP 設定がありません);一度、起動時にトリミングします。
6. より少ないターンを取る(各ラウンドトリップがコンテキスト全体を再読み取りする)。
- 独立したツール呼び出しを 1 つのターンにバッチ処理する(並列読み取り/grep/
gh)。 - スクリプトにクエリ+分類を任せる —
scripts/survey.sh、scripts/reconcile.sh。 - マージが差し迫っていない限り、短いリズムでポーリングしない — 不要なウェイクはすべてを再読み取りし、約 5 分のキャッシュ TTL を過ぎると完全なキャッシュ 書き込み(1.25 倍)を支払い、読み取り(0.1 倍)ではありません。
- ターンだけでなく、セッションを制限する。 バッチ処理は A/B で効果がなく(レバー 2)、したがってターンごとの最適化はほぼ使い果たされており、セッション長が残された変数です — 37 のワーカーセッションのうち上位 3 つで全ワーカーコストの 32% を占め、努力ラベルもティアもどの 3 つかを予測しませんでした。したがって、フェーズ 1 のワーカーは ターン予算 を持ち(整数はここではなく references/token-economics.md § 2 つの予算 にあります)、
STATUS: PARTIALでそれを引き継ぎます;ステップ 4 はそれらを 新しい サブエージェントで再開します。これが節約の仕組みです —SendMessageの再開は、予算が破棄しようとしているコンテキストを保持します。これはレバー 1 のメカニズムであり、このセクションの唯一検証された成果であり、フェーズ の継ぎ目ではなく 長さ の継ぎ目で適用されます。
測定する — A/B なしでレバーが機能すると主張しないでください。3 つのスクリプト。すべてセッション .jsonl トランスクリプトのディレクトリを取ります:
scripts/usage_report.py— モデル別のトークン + ドル換算。トークン/マージ と $/マージ を追跡します。⚠️ これは過去の実行を含むプロジェクトディレクトリ全体を集計します — 最初に 1 つの実行を一時ディレクトリにシンボリックリンクし、レイアウトを維持します:スーパーバイザーセッションの<sid>.jsonlとその<sid>/subagents/ディレクトリ(<proj>/<sid>/subagents/agent-*.jsonlはすべてのワーカートランスクリプトが存在する場所です。したがって、1 回の実行のフリートは 1 つの ID の下にあります — 状態ファイルまたはghタイムスタンプから ID を選んでください;mtime は信頼できません)。フラットなagent-*.jsonlシンボリックリンクの山は「0 サブエージェント」として読み取られ、何もペアリングしません。scripts/analyze_cache.py— ターン/セッション、開始時とピーク時のコンテキスト、ターンあたりの平均コンテキスト、ツール別ツール結果ボリューム。これにより、セッションが高コストである 理由 がわかります。scripts/measure_phase2.py— 各 Issue のフェーズ 1 とフェーズ 2 のサブエージェントトランスクリプトを(レポート行のISSUE:によって)ペアリングし、コストを実装フェーズとマージフェーズに分割します;2.0 より前のトランスクリプトでは、代わりにセッション内のマージハンドオフターンを見つけます。これがレバー 1 を証明したものです。
2 つのメトリクスがほとんどのシグナルを伝えます:ターンあたりの平均コンテキスト(後のターンが肥大化しているか?)と ターンあたりのツール呼び出し数(ターンが何らかの作業をしているか?ベースライン約 0.55 — レバー 2 を参照)。
動作の仕組み
┌─────────────────────── auto-dev(あなた、スーパーバイザー) ───────────────────────┐
│ 状態ファイル:キュー(小→中)、実行中のスロット、完了、ファイリング済み │
│ すべてのシグナルで → GitHub と調整 → マージ終了 + 補充 → N を実行し続ける │
└───────────────────────────────────────────────────────────────────────────────────────┘
│ ディスパッチ(領域分離) ▲ 構造化レポート / アイドル通知
▼ │
ワーカーA ─ implement-issue → [準備完了] ⇢ 新しいサブエージェント ─ merge-pr → レポート ┐
ワーカーB ─ implement-issue → merge-pr → レポート ─┤ N 個のバックグラウンドエージェント、各 1 Issue、
ワーカーC ─ implement-issue → merge-pr → レポート ─┘ マージ時に退役、キューから補充
ワーカーは Agent ツールで生成されたバックグラウンドサブエージェント です。それらの構造化レポートを介して通信します — サブエージェントの最終メッセージ が そのレポートであり、戻ってきたときにあなたに配信されます — アイドル通知を介して、および SendMessage でまだ実行中のものに送信します;それらの生のトランスクリプトファイル(<proj>/<your-session>/subagents/agent-*.jsonl)は絶対に読み取らないでください — ワーカーの出力は巨大なログであり、あなたのコンテキストを吹き飛ばします。レポートに頼ってください。