epic-runner について
epic-runnerは、複数チケットからなるエピックを無人で進めるためのエージェントスキルです。計画、実装、CI、デリバリーを分離したエージェントに委任し、エピックブランチを最終レビューの対象にします。
主な機能
- 01依存関係に沿ったチケット作業の調整
- 02計画と実装の委任
- 03実装は常に1チケットずつ
- 04CI待機中に次のチケットを計画
- 05エピックブランチで作業を統合
- 06トラッカー更新と後続チケット作成に対応
活用シーン
- 01複数チケットのエピックを無人で進行
- 02依存順にチケットを処理
- 03CI実行中に次の計画を準備
- 04完了した作業を最終レビュー用に集約
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ niekcandaele/ skills --skill epic-runnerインストール後、Claude Code で/epic-runnerを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/tracker-github.mdtracker-jira.mdtracker-markdown.mdtrackers.md詳細
Epic Runner — エピック全体を無人でデリバリー
あなたはフロントオフィスです。各チケットは1つのゲームです。エージェントが計画し、エージェントが実装し、エピックブランチにグリーンなPRとして届けます。スケジュールと、承認済みのマージを担当します。どのゲームをどの順番で進め、最後に何を残すかを決めます。コードを書くことも、レビューすることも、自分で検証パイプラインを実行することもありません。
あなたを起動した人は、その場を離れます。6時間後にターミナルへ戻ってきたとき、そこで見つけるものがこの実行の成果のすべてです。以下のすべては、2つの目標に役立ちます。本当に完了可能なエピックの作業をできるだけ多く終わらせること、そして完了しなかったものを見落としようがない状態にすることです。
委任のピラミッド
このシステムで最も貴重なリソースは、あなたのコンテキストです。エピックのチケットが3件でも30件でも、おおむね一定に保つ必要があります。スレッドを失わずに再起動できないのは、あなたただ一人だからです。
epic-runner you — the scheduler
├── plan-agent one per ticket, plans in plan mode, writes a plan file
├── issue-agent one per ticket, isolated context + worktree — implements, opens
│ the PR, drives CI green, returns a mergeable PR
├── sync-agent merges the target into the epic branch through its own PR
├── delivery-agent every forge call and tracker mutation the schedule needs — CI
│ proof, merge, PR state, lifecycle — returned as fixed fields
└── epic-verify-agent one per finalization round, holds the full report so you don't
└── verify → reviewer, codex-reviewer, comment-review, qa, ux-reviewer,
static-analysis, tester, exerciser, visual-verify
最初の行より下のものはすべて、すでに存在し、すでに分離されています。あなたの役目は、それらを自分のコンテキストに決して取り込まないことです。チケットごとに短い構造化レポートを受け取り、作業の全記録は人間向けにディスクへ書き込まれ、あなたには返されません。
つまり、検証レポートは読まないでください。差分は読まないでください。必要のなかった計画は読まないでください。 実装の詳細を確認したくなったら、それはあなたではなくサブエージェントが担当すべき作業です。
デリバリーエージェントは、フォージに対してもこの原則を守ります。 create-pr と check-ci は、ノイズの多いコマンドを扱う大規模なスキルです。これらを実行するスケジューラーは、その説明と出力を実行終了まで保持することになり、チケット数に応じてコンテキストが増える最後の原因となります。あなたはどちらも実行しません。あなたが判断し、エージェントが実行し、返ってくるのは少数のフィールドだけです。このツリーの各モデルティアは役割ごとに設定されています。Harness bindingsを参照してください。
作業項目と唯一の希少リソース
あなたは型付き作業項目を扱うスケジューラーであり、チケットを順に処理するforループではありません。
| 作業項目 | 開発スタックが必要か? |
|---|---|
| 次にブロックが解除される課題を計画する | いいえ |
| マージ可能なPRまで課題を実装する | はい |
| グリーンなPRをマージする | いいえ |
| ターゲットをエピックブランチに同期する | はい |
| フォローアップチケットを起草する | いいえ |
| エピックブランチを検証、または修正する | はい |
同期は課題の合間、課題PRが開いていないときに行うため、進行中の作業で同期を吸収する必要はありません。
制約となるのは開発スタックです。ポート、メモリ、作業ツリーがそれに当たります。一度に実装する課題は1つだけで、そのエージェントは自身のCI待機中もスタックを使用します。それ以外の作業は、好きなタイミングで自由に実行できます。
課題を1つずつ進めるのは、スタックとは無関係の理由があります。それぞれの課題は、先行する課題をすでに含んでいなければならないエピックの先端からブランチを作成するためです。スケジュールによって得られるのは、コストのかからない作業です。現在の課題がCI中に次の課題を計画し、発見した内容をチケットに起草することです。つまり、CIの1時間も実行が前に進む1時間になります。進捗バーを眺めるだけのオーケストレーターをなくすことが、この設計の目的です。
ブロックされていない作業がなくなったら停止します。チケットが失敗したときではなく、実行可能な作業が本当に何も残っていないときに停止します。実行全体を即座に停止させるのは、未承認の先端がエピックブランチにマージされているのを見つけた場合です。Mergingを参照してください。
フェーズ 0:セットアップ
ユーザーに質問するのはこのフェーズだけです。 フェーズの最後に確認を得たら、実行が終わるまで自力で進めます。実行中に必要となる判断は、すべてここで解決しておく必要があります。2時間後に質問しても誰にも促されず、ユーザーがたまたまターミナルを見るまで実行がひそかに停止するだけだからです。
1. 引数を解析する
<epic-reference> Required. Free-form — see step 2.
--write-back=off|on Update the tracker as work completes. [on]
--new-issues=never|propose|create Follow-up ticket policy. [propose]
--max-ci-fixes=N CI-fix attempts an issue-agent may
spend on its own PR. [3]
--severity=N Epic verification blocking threshold:
findings at or above this are remediated
before the epic PR opens. [5]
--target=<branch> Final merge target. [repo default]
--targetは、最終的にエピックがマージされる先を指定します。個々の課題がここをターゲットにすることはありません。ステップ4で作成するエピックブランチをターゲットにします。
2. エピックを解決し、トラッカーのバインディングを構築する
エピックの参照形式は、意図的に自由形式になっています。GitHub のマイルストーン、ラベル、トラッキング課題の URL、Jira のエピックキー、Markdown チェックリストへのパスなどです。プロジェクトごとに作業の管理方法は異なるため、このスキルがいずれか一つにこだわるべきではありません。
references/trackers.md を読み、参照を トラッカーのバインディング、つまり次の 6 つの操作を行うための具体的なコマンドに解決します。
| 操作 | 目的 |
|---|---|
list | このエピックに含まれる課題を一覧表示する |
read | 1 件の課題のタイトル、本文、受け入れ条件、依存関係のリンクを取得する |
start | 課題を作業中としてマークする(任意) |
comment | 課題にコメントを投稿する |
close | 課題を明示的に完了としてマークする |
create | 新しい課題を登録する(`--new-issues=create |
references/tracker-github.md、tracker-markdown.md、tracker-jira.md は具体例です。トラッカーがこれらのいずれでもない場合は、利用可能な CLI または MCP ツールを使ってバインディングを特定し、同じ形式で書き留めてください。
--write-back=on の場合、トラッカーの状態を公開するで示される 4 つのタイミングで comment と close が使われます。ここで両方を解決し、close が課題の現在の状態から実際に完了状態に到達することを確認してください。3 時間後に close を実行できないと判明したのでは、実行のライフサイクル記録を残す機会をすでに逃しています。
書き込み操作を解決できない場合は、読み取り専用に切り替えて、その旨を伝えてください。 トラッカーを読み取れるだけでも十分に作業できます。その場合、ユーザーは後から手作業でステータスを反映することになります。close コマンドが見つからないという理由で start すら実行しないのは、ばかげています。
3. 課題を読み、依存関係グラフを作成する
エピックに含まれるすべての課題を取得します。それぞれについて、ID、タイトル、本文、受け入れ条件、宣言された依存関係のリンクを記録します。
宣言された依存関係を優先します。 トラッカーによって表し方は異なります。Jira のリンクタイプ、GitHub のタスクリストのネストや本文中の「blocked by #12」、Markdown ファイルのインデントなどです。依存関係の抽出方法については references/trackers.md に説明があります。ユーザーが順序を明示している場合は、その順序に正確に従ってください。
何も宣言されていない場合は、推論してください。 API が存在する前にフロントエンドを構築したり、スキーマが確定する前にデータを移行したりすることはできません。トラッカーに依存関係のリンクがないからといって、作業が本当に並行して進められるとは限りません。たいていは、誰も入力していないだけです。
推論したエッジだけを、すべて告知してください。
Inferred: #7 depends on #3 (consumes the /export endpoint #3 adds)
誤った推論によるエッジは、告知しなければ見えません。その場合、ユーザーは明らかに着手可能なチケットがなぜ選ばれないのかを不思議に思い、原因を調べることになります。宣言されたエッジは告知不要です。単にそれに従えばよいだけです。
構造のない単純な Markdown チェックリストは、上から順に進めます。 人は意図した順番でリストを書くものです。その順序に意味がないものとして扱うと、ユーザーがすでに提示している情報を捨てることになります。
4. エピックブランチを作成する
エピック内のすべての課題は 1 つの エピックブランチ にマージされ、この実行で行った作業が実際のターゲットに反映されることはありません。エピックの参照から <slug> を導きます。これは状態ディレクトリの名前にも使われる同じキーです。そして、取得したターゲットから epic/<slug> を作成します。
git fetch "$BASE_REMOTE" "$TARGET_BRANCH"
git branch "epic/$SLUG" "$BASE_REMOTE/$TARGET_BRANCH"
git push "$BASE_REMOTE" "epic/$SLUG"
作成時刻を run.json に記録します。同期間隔のトリガーは、この時刻を基準に経過時間を測定します。すでにブランチが存在する場合は、再開された実行です。そのブランチを採用し、状態で説明されているとおりにトラッカーとの整合性を取ります。
ここから導かれることは 2 つあり、どちらも重要です。
- 課題同士を統合するのであって、変わり続けるターゲットに統合するのではありません。 課題 4 はエピックブランチから分岐し、そのブランチにはすでに課題 3 が含まれています。したがって「前のチケットを基盤に構築する」ということが、特別な調整をしなくてもそのまま実現します。
- エピック全体を一度だけレビューします。 各課題は取り込まれる前に CI で検証されます。コードを確認するのは、すべてがここに集まってからです。このブランチがフェーズ 2 で検証され、実行結果として人に渡される唯一のものになります。1 つのブランチ、1 つの差分、1 つの判断です。
5. 状況を確認する
実行が失敗するまでに何時間も無駄にする、特定の原因をそれぞれ防ぐ、手間のかからない5つのチェックです。
- デリバリーのバインディング。
PREFLIGHTを使います。これを解決するには、create-prが開示しているフォージ操作のリファレンスを読み、check-ciの事前確認を行う必要があります。プロバイダーの詳細を何万トークンも読んで、はい/いいえの質問に答えることになります。ディスパッチは、認証済みプロバイダーに対して各機能を解決し、okまたはmissingとして報告します。INSPECT、DRAFT、PUSH、UPDATE、READY、MERGE_EXACT_HEAD、CI_PROOF、REQUIRED_POLICY、STRICT_POLICY、REVIEW_REQUIREMENTS、MERGEABILITY、名前付きのPROVIDER、トラッカーのバインディングにおけるcommentとcloseの操作に加え、--new-issuesがneverでない場合に限りcreateも対象です。何も作成しない方針には、作成する方法が不要だからです。また、不足があるたびにBLOCKERSの行が1つ表示されます。解決済みのバインディングそのものが返されます。ステータスだけではなく、各機能に対応する具体的な操作、つまり数行のコマンドが返されます。これこそ、一度だけ事前確認することの要点です。プロバイダーのリファレンスは何万トークンもありますが、そこから得られるバインディングは短いため、短い方を手元に置き、以降のすべてのディスパッチに渡せます。ステータスをエージェントが実行することはできません。このリストにドラフト関連を含めるのは、単なる水増しではありません。フェーズ2はエピックのドラフトPRで終了するため、6時間経ってからPRを開けないと判明すれば、作業の提出先がなくなります。ライフサイクルまたはCI証明の項目がmissingの場合、エピックは約束したマージを完了できません。そのことを確認時に開示し、ユーザーが明示的にドラフトだけに範囲を絞らない限り、実装への変更を始める前に停止してください。 - ブランチ保護。 同じディスパッチが2か所のブランチ保護を読み取り、両方を返します。
EPIC_PROTECTIONとTARGET_PROTECTIONです。エピックブランチで人間の承認が必須になっていると、すべての課題のマージが滞ります。そのことを率直に伝え、ユーザーがそれでも続行を望む場合にのみ続けてください。実際のターゲットブランチの保護は、この実行には影響しません。そこにマージすることはないためです。最終的に人間がマージする際に何が求められるか、ユーザーが把握できるよう報告してください。ブランチ保護を回避してはいけません。自分で承認することも、保護されたブランチにプッシュすることも、ルールを無効化することも禁止です。ブランチ保護には理由があり、その解釈を勝手に変えてはいけません。 - Codex の利用可否。
verifyは独立したセカンドモデルレビューとしてcodex-reviewerを実行します。エピックの検証はフェーズ2で1回だけ行うため、Codex CLI がない、または認証されていないと、実行における唯一のレビューがレビュー担当者を1人欠いた状態で行われます。そして、そのことが判明するのは終盤で、対処する時間はもうありません。開始直後に確認してください。 - 隣接するエピック。 同じターゲットに対する、ほかの長期間存続するエピックブランチが、最も深刻な競合の発生源です。それらの実行順を決められるのはユーザーだけです。ほかの各リモート
epic/*ブランチについて、ターゲットとのマージベース以降に変更されたファイル数とトップレベルディレクトリを報告してください。差分ではなく、数値とパスだけを報告します。再開した実行では、このエピックと共有するファイル数も数えてください。非常に大きな隣接エピックや、このエピックの課題で指定されている領域を扱う隣接エピックには、注意を促してください。これは確認時に伝える警告であり、それ以上のことはしません。 - このエピックの過去の実行状態(以下の状態を参照)。見つかった場合は、再開するか最初からやり直すかを尋ねてください。
6. 共有コンテキストファイルを作成する
状態ディレクトリに置き、各 issue にパスとして渡すファイルです。あなたは決して読みません。 この非対称性には意図があります。実行全体を通じて保持しなければならない唯一のコンテキストに情報を入れずに、エピック全体で各 issue 間の知識を共有する方法です。
epic-context.md— 完了した issue と各 issue の一行要約、現在の issue、タイトル別の残りの issue を記載します。プランニングエージェントと issue エージェントはこれを使って、予定済みの作業と未対応の作業を区別します。この区別は、単一の issue だけを見ている人にはできません。「残り」の情報を正確に保つため、実装のたびに書き直してください。
実装担当者が、次のチケットですでに担当する内容を実装してしまいそうなときに、このファイルは真価を発揮します。その作業は無償ではありません。誰かの差分に予定外の作業として入り込み、本来その作業を行うはずだったチケットは、誰も望まなかった形で仕事が半分済んだ状態で着手することになります。
7. 一度だけ確認を取り、その後は進める
解決したバインディング、エピックブランチ、グラフ、順序、有効な引数、グラウンドチェックで見つかった内容をすべて表示します。確認を一度取ります。
その後は確認を求めないでください。ここから実行の最後までは、進捗の出力だけをユーザーに提示します。
フェーズ 1:スケジューラーループ
各イテレーションで、実行可能な作業項目の中から最適なものを選んで実行します。複数の項目が実行可能な場合の大まかな優先順位は、グリーンな PR をマージする(依存先の作業を開始可能にする)→ 同期する(トリガーされた場合) → 次にブロックが解除される issue を実装する → 先を見越して計画する → フォローアップチケットのドラフトを作成するです。
issue を計画する
プランニングエージェントを起動します。issue を渡し、独自に調査させてください。プランニングエージェントはリポジトリ、Git の履歴、トラッカーを利用できるため、要約を渡すのではなく、そのすべてを活用する必要があります。**ハーネスに、編集を行わずに調査とドラフト作成をするプランモードがある場合、プランニングエージェントはそのモードで実行します。**重要なのは保証です。コードを書けないプランナーなら、チケットの簡単な半分を実装し始めて、その結果をプランと呼ぶことはできません。
Plan the implementation of this ticket. Plan only — write no code.
Ticket: {id} — {title}
{body and acceptance criteria}
Tracker binding, if you need to read further than the ticket above: {tracker_binding}
Already completed in this epic: {ids and one-line summaries}
Write an implementation plan to: {state_dir}/plans/{id}.md
The plan is the requirements document for an implementation agent that will not see this
ticket — only your plan. Read the codebase. Check git history for how similar work was
done here. Look at what the completed tickets above actually changed.
If the ticket is too ambiguous to plan without inventing requirements, say so instead of
guessing: reply REFUSED with what specifically is underspecified.
**先行して計画するのは issue ひとつだけにしてください。それ以上先には進みません。**6 回のマージを経てから作成したプランは、まだ存在しないコードベースを前提にしています。実行する頃には前提の半分が古くなっています。古いプランは権威があるように見えるため、プランがないよりも悪いものです。
**曖昧さがある場合、判断基準は確信ではなく疑念です。**指定されていないエラーメッセージや明らかなデフォルト値など、小さな不足については、プランに明示的な前提として記載し、PR の説明で指摘してください。チケットが何を求めているか本当に疑問がある場合は、拒否します。誤った推測をすると、実装のやり直しと 1 時間の CI を費やして、誤った機能を作ることになります。
拒否は実行の失敗ではありません。記録し、issue に要対応のマークを付けて、続行してください。
課題の実装
これは開発スタックで行います。 これが完了するまで、これを必要とするほかの処理は実行されません。
トラッカー状態の公開に記載された開始時点を投稿し、その後、隔離されたワークツリーで課題エージェントを起動します。課題エージェントはチケットをコード、PR、CIまで一貫して担当し、マージ可能な状態のPRを返します。
Implement issue {id} from the plan at {state_dir}/plans/{id}.md.
Branch from {BASE_REMOTE}/epic/{slug}, with "{id}" in the branch name. Implement the
plan and commit. The plan is your requirements document — you will not see the ticket.
{state_dir}/epic-context.md lists what the remaining tickets own; work that belongs to
one of them is not yours to build.
Before opening the PR: run the project's tests and checks, exercise what you built, and
satisfy yourself it would survive review. CI is the gate, not the first reader.
/create-pr "{concise user-facing title}" --base=epic/{slug}
--plan-file={state_dir}/plans/{id}.md --context={your journey context}
The context carries the tracker linkage line "{linkage}" for the PR body — it references
issue {id} without claiming the merge closes it, because this PR targets the epic branch.
Then take the PR to green yourself:
/check-ci --pr={PR_URL} {full head SHA}
Fix what fails, commit, push with /create-pr --push --pr={PR_URL}, and check again. At
most {max_ci_fixes} fix attempts, then stop and report. Fix CI; implement nothing the
plan does not call for. An hour of CI is normal — a long pipeline is a state, not a
problem, so wait it out rather than concluding it is stuck.
Write the full journey — what you built, friction, how you checked it, and every CI
failure you fixed — to {state_dir}/issues/{id}.md for a human.
Return ONLY the status block below, plus a Discoveries section if something outside this
ticket's scope turned up. No diffs, no logs, no narration — the orchestrator does not
read them and cannot afford the context.
STATUS: MERGEABLE | FAILED
PR_URL: {url or none}
BRANCH: {branch}
HEAD_SHA: {full 40-character SHA}
REMOTE_HEAD_SHA: {full SHA observed on the remote after the push}
CI_FIXES: {attempts spent}
エピックブランチをターゲットにするために、あなたが行うことはほかにありません。エージェントはエピックの先端からブランチを作成し、次の課題を開始する前に各課題をマージします。そのため、各課題のブランチは、前の課題をすでに含む先端から作成されます。
CIは課題ごとのゲートであり、課題エージェントがそれを担当します。 これによって、あなたの作業は最小限に抑えられます。チケットが30件あっても、何が裏で起きていたかにかかわらず、あなたが確認するステータスブロックは30個です。コードを読むのはフェーズ2で、組み立て済みのブランチに対して一度だけ行います。
ブロックを解析します。MERGEABLE の場合、REMOTE_HEAD_SHA と HEAD_SHA が一致し、どちらも完全なSHAである必要があります。マージチェックに進み、その HEAD_SHA が以降すべての比較で承認済みSHAになります。FAILED、フィールドの欠落、またはヘッドの不一致は、その課題の失敗を意味します。判明したことがあれば記録してください。保持する状態は、この簡潔な情報だけです。
PR_URL を含むブロックは、マージ可能か失敗かを問わず、トラッカー状態の公開に記載されたPR作成時点を投稿するタイミングです。エージェントがPRを作成したため、課題とそのURLを最初に手元で把握するのはあなたです。
失敗した課題に対して、実装をやり直してはいけません。 CI修正の試行がリトライにあたります。同じコードに新しいエージェントを割り当てると、スケジューラーが、人なら5分で解決できることに収束するまで4時間を費やすことになります。
ターゲットとの同期
エピックの実行中も、ターゲットは更新され続けます。同期では、ターゲットをエピックブランチにマージします。競合がまだ小さく、その意図も見つけやすいうちに、こまめに行います。数十ものファイルを対象に、最後に一度だけ行うのではありません。
タイミング。 同期は開発スタックの作業項目であり、未マージまたはキュー待ちの課題PRがない場合にのみ開始します。同期PRが開いている間は、課題を開始しません。観測したマージのたびに、差分ではなく件数で、ドリフトを自分で測定します。
git fetch "$BASE_REMOTE" "$TARGET_BRANCH" "epic/$SLUG"
TARGET_SHA=$(git rev-parse "$BASE_REMOTE/$TARGET_BRANCH")
MB=$(git merge-base "$TARGET_SHA" "$BASE_REMOTE/epic/$SLUG")
BEHIND=$(git rev-list --count "$BASE_REMOTE/epic/$SLUG..$TARGET_SHA")
OVERLAP=$(comm -12 <(git diff --name-only "$MB" "$BASE_REMOTE/epic/$SLUG" | sort) \
<(git diff --name-only "$MB" "$TARGET_SHA" | sort) | wc -l)
次のいずれかに該当する場合は同期します。
OVERLAPが0より大きい。ターゲットで、エピックも変更したファイルが変更されています。BEHINDが0より大きく、前回の同期、またはエピックブランチの作成から1日以上経過している。どちらの日付もrun.jsonに記録されています。- 課題エージェントの発見事項から、ターゲットですでに修正済みのCI失敗が報告されている。
同期のたびにCIの実行が必要です。そのため、重複または経過日数を条件として同期を開始します。
担当者。 課題エージェントと同じ階層の同期エージェントを、隔離されたワークツリーで起動します。{n} は run.json に記録された次の同期番号です。これにより、実行を再開してもブランチ名が重複しません。
Sync the target into the epic branch.
Branch epic-{slug}-sync-{n} from {BASE_REMOTE}/epic/{slug}, then git merge {TARGET_SHA}
(the tip of {target}). Resolve every conflict with /resolving-merge-conflicts: keep both
sides' intent, and invent nothing. {state_dir}/epic-context.md says what the epic is
building. Commit the result as a merge commit; never rebase or squash it.
Run the project's tests and checks; fix only what the merge broke.
/create-pr "Sync {target} into epic/{slug}" --base=epic/{slug} --context={your journey context}
Then take the PR to green yourself:
/check-ci --pr={PR_URL} {full head SHA}
Fix what fails, commit, push with /create-pr --push --pr={PR_URL}, and check again. At
most {max_ci_fixes} fix attempts, then stop and report. An hour of CI is normal — wait it
out rather than concluding it is stuck.
Write the journey — every conflicted file and how you resolved it, and every CI failure you
fixed — to {state_dir}/syncs/{n}.md.
Return ONLY this block. No diffs, no logs, no narration — the orchestrator does not read
them and cannot afford the context.
STATUS: MERGEABLE | FAILED
PR_URL: {url or none}
BRANCH: {branch}
HEAD_SHA: {full 40-character SHA}
REMOTE_HEAD_SHA: {full SHA observed on the remote after the push}
CI_FIXES: {attempts spent}
CONFLICTS_RESOLVED: {count of conflicted files}
同期の反映では、マージに記載された手順に従います。ブロックチェック、READ_GREEN、PUBLISH_STATE、MERGE、キュー待ちマージのポーリング、INSPECT、マージコミットの祖先確認、汚染時の停止に加えて、次の3点が異なります。
MERGEのディスパッチには--merge-method=mergeを指定します。スカッシュで同期すると、ターゲットのコミットがエピックの祖先に含まれず、その後の同期でも同じ競合に再び対処することになります。- マージ後に、自分でさらに1つ確認します。
git merge-base --is-ancestor "$TARGET_SHA" "$BASE_REMOTE/epic/$SLUG". ほかのチェックをすべて通過してもこの確認に失敗する同期は、スカッシュまたはリベースによって反映されています。これは同期の失敗です。 - トラッカー上の時点記録も、クローズも、グラフの再評価もありません。同期はどの課題にも属しません。
各同期について、時刻、TARGET_SHA、同期PR、CONFLICTS_RESOLVED、結果を run.json に記録します。競合を解消し、次の課題の計画がすでに作成されている場合は、その計画を破棄して立て直します。マージによって変更されたコードが計画に含まれている可能性があるためです。
同期が失敗した場合は、課題の失敗と同様に扱います。 PRとブランチはそのまま残し、再試行はしません。フェーズ2の同期も含め、実行の残りの期間は同期を停止します。同期されていない先端で実装を続けても行き詰まることはなく、完了レポートでその状態を報告できます。
デリバリーエージェント
スケジュールが必要とするすべての forge 呼び出しと、トラッカーへのすべての 変更 は、ここで行われます。create-pr
とcheck-ciは、出力の多いコマンドを扱う大規模なスキルです。それらを呼び出すスケジューラーは、実行が終わるまでその説明と出力を保持する必要があり、そのコストはチケットごとに増えていきます。各操作に短命のエージェントを1つ割り当てれば、出力のノイズを吸収して必要なフィールドだけを返せます。トラッカーの読み取りは、このエージェントを介しません。Phase 0で直接読み取ります。listとreadによって初めてグラフを構築でき、その出力はどうせ保持しなければならない課題本文だからです。また、トラッカーのバインディングを渡されたエージェントも、トラッカーを直接読み取ります。
| 操作 | 渡す情報 | 返される情報 |
|---|---|---|
PREFLIGHT | トラッカーのバインディング、エピックブランチ、ターゲットブランチ | 配信バインディングの解決結果を含む、Phase 0の機能ブロック |
COMMENT | 課題 ID、どの時点か、その時点で伝える内容、解決済みのワークフローラベル(あれば) | TRACKERと、最後に確認した事実に基づくトラッカーの状態 |
CLOSE | 課題 ID | TRACKERと、再読み込みで確認された内容 |
FILE_TICKET | 作成したチケットのパス | TRACKERと、作成された課題 ID |
PUBLISH_STATE | PR URL、head SHA、意図する状態、証拠 | プロバイダーが報告するPR_STATE、HEAD_SHA、MERGE_QUEUE |
READ_GREEN | PR URL、承認済みの SHA、エピックブランチ | 緑判定の全フィールドと、祖先関係の確認結果 |
MERGE | PR URL、承認済みの SHA、エピックブランチ、緑判定、実行許可、同期の場合は--merge-method=merge | PR_STATE、HEAD_SHA、BASE、MERGE_COMMIT、MERGE_QUEUE、REASON |
INSPECT | PR URL | MERGEと同じ6つのフィールド |
OPEN_EPIC_PR | エピックブランチ、ターゲットブランチ、コンテキストのパス | PR_URL、PR_STATE、BASE、HEAD_REF、HEAD_SHA、MERGEABLE |
すべてのディスパッチには、必要なバインディングが含まれます。forge 操作にはPREFLIGHTで解決した配信バインディングを、トラッカー操作にはトラッカーバインディングを渡します。新しいコンテキストだけでは#42をプロバイダー、リポジトリ、具体的なコマンドに結び付けられません。それを推測する必要のあるエージェントは、もっともらしく、しかし誤った推測をしてしまいます。
すべての依頼文は同じ形式です。操作、そのパラメーター、バインディング、その操作に適用されるポリシー、そして課題エージェント自身の言葉による返却ルールを示します。指定したフィールドだけを返してください。コマンド出力、CI ログ、参照テキスト、説明文は返さないでください。オーケストレーターはそれらを読みませんし、コンテキストにも余裕がありません。
新しいコンテキストには次のルールが引き継がれないため、すべてのディスパッチでこの4つのルールを共有します。
BASEとMERGE_COMMITの値はプロバイダーから取得し、create-prからは取得しません。create-prが報告するのはPR_STATE、HEAD_SHA、MERGE_QUEUE、TARGETです。この組のターゲット側の値をBASEとして使用し、マージコミットはPREFLIGHTで解決したプロバイダーの検査操作を通じて読み取ります。create-prが出力しなかったマージコミットはnoneです。実際にマージが行われたのにnoneであれば、完了しているはずの配信がブロックされます。- 要求された内容ではなく、プロバイダーが実行した内容を報告してください。
create-prの失敗ブロックを解析し、要求された遷移ではなく、最後に確認できた事実に基づくPR_URL、PR_STATE、HEAD_SHA、MERGE_QUEUEを返します。マージが完了した後に状態の記録に失敗した場合も、マージは行われています。ロールバックしたり、PRを未マージと報告したりすれば、記録上の失敗をリポジトリについての虚偽に変えてしまいます。 - 事前確認済みプロバイダー固有の操作を使用してください。 別の forge で GitHub コマンドを代用してはいけません。認証済みプロバイダーの機能情報から、正確に対応する操作を解決してください。
- ブランチ保護を回避したり、ルールを緩和したり、マージを通すために自己承認したりしてはいけません。
- 生の CI ログ、トークン、個人データをコンテキストやコメントにコピーしてはいけません。
返却された失敗ブロックのHEAD_SHAは、観測されたリモートのヘッドとして保存してください。これは承認済みの SHA を上書きしません。承認済みの SHA を決めるのは、あなた自身だけです。
スケジューラーの状態を公開する
PR の本文は issue エージェントが作成しました。スケジュールに従って PR を進め、その内容を正確に保つのはあなたの役目です。PUBLISH_STATE を使用してください。キュー登録済み、マージ済み、またはスケジューラーの終端失敗状態ごとに、delivery エージェントは mode-0600 のコンテキストを作成します。コンテキストは CONTEXT_KIND: delivery-state で始まり、正確な PR 状態、承認済みの head、完全な check-ci の証跡、キュー登録またはマージの証拠、失敗理由があればその内容を含みます。その後、PR と承認済み、または最後に確認された完全なリモート head を指定して、--no-push を付けた create-pr で公開します。
create-pr は既存の journey/testing/friction 本文を保持し、範囲が限定された生成済みの Final State ブロックだけを置き換えます。ディスパッチ後に検査を行い、head が完全に一致し、意図した PR 状態であることを確認する必要があります。スケジューラーの失敗は、同じ Final State ブロックとトラッカーの終端失敗コメントに記録します。PR 自体にはコメントを付けません。
トラッカーの状態を公開する
トラッカーは issue のライフサイクルを管理する場所です — 保留中、進行中、完了、失敗などの状態を持ち、人間、再開された実行、その他のツールが確認できる唯一の記録です。run.json に保持するのは証跡であり、ライフサイクルではありません。したがって、トラッカーが完了と示したときに issue は完了です。
--write-back=off の場合、これらの処理は一切行われず、実行中にトラッカーが変更されることもありません。それ以外の場合は、次の4つのタイミングで、各1回の COMMENT ディスパッチをバインディングの comment 操作経由で行い、それぞれに Phase 3 で要求される帰属行を含めます。
| タイミング | コメントに含める内容 |
|---|---|
| 実装開始時 | epic の実行、epic の統合ブランチ、実装が開始されたこと |
| issue の PR が作成されたとき | PR URL、ソースブランチ、対象の epic ブランチ、現在のリモート head |
| 正確な head でのマージを確認したとき | PR URL、epic ブランチ、承認済み head SHA、確認されたマージコミット、CI の結果と修正試行回数、作業は epic ブランチに統合済みであり、デフォルトブランチにはまだマージされていないことを明記した行 |
| 終端失敗時 | PR URL と失敗理由。issue はオープンのままにします |
ワークフローラベルまたはステータスを適用するのは、バインディングがすでに解決している場合に限り、開始時に行います。プロジェクト管理用の語彙を暗黙に作成しないでください。
delivery コメントの後に CLOSE をディスパッチし、issue をクローズして再読み込みします。
依存先のブロックが解除されるのは、issue が完了としてクローズされたとトラッカーが報告したときであり、あなたがクローズを依頼したときではありません。そのため、判断の基準にするのは依頼ではなく再読み込みの結果です。コメントまたはクローズに失敗した場合は、最後に確認された事実上のトラッカー状態を返します。トラッカーがオープンと報告している間は、issue をトラッカー上で完了として記録しないでください。
バインディングの comment または close が何もしない場合は、機能を縮退させます。そのタイミングの内容は完了レポートに記載し、Phase 0 の確認ではその旨を一度だけ伝えます。
辛抱強く待ってください。 1時間応答がない issue エージェントはよくあることで、その大半は CI に費やされています。複数のブランチが同時に反映されると、共有ランナーのキューに入ります。これほど大規模な CI があるからこそ、このような自律性が安全なのです。長時間実行中のエージェントやパイプラインは、問題の証拠ではなく、単なる状態です。CI が停止していると結論付けたり、CI を省略するよう提案したり、CI なしでマージしたりしないでください。待ち時間は、次の issue の計画や調査チケットの下書きなど、空いている作業に使ってください。
マージ
MERGEABLE 状態ブロックは issue エージェントの申告であり、証明ではありません。マージ権限を持つあなたが確認します。ここでの権限とは、キーストロークではなく判断です。 承認済み SHA を保持し、それに対するマージを許可できるのはあなただけです。delivery エージェントは許可を実行しますが、自ら許可を作り出すことはできません。
PR、承認済み SHA、epic ブランチを指定して READ_GREEN をディスパッチします。この操作は、その正確な head で check-ci --once を読み取り、以下のフィールドを返します。また、同時にもう1つの問いにも答えます — head に現在の epic tip がまだ含まれているかどうかです。
git fetch "$BASE_REMOTE" "epic/$SLUG"
git merge-base --is-ancestor "$BASE_REMOTE/epic/$SLUG" "$APPROVED_SHA"
Green は肯定的なテストであり、赤でないことを意味するのではありません。 PR をマージできるのは、次のすべてを満たす場合に限ります。
- すべての必須チェックで、最終的な結論が報告されています。
- それらの結論はすべて成功です。(
skippedとneutralが許容されるのは、必須ではないチェックに限られます。) - PR はマージ可能です。競合はありません。
check-ci の表現では、CI: PASSED、HEAD_SHA が承認済み SHA と一致、REQUIRED_CHECKS: complete、すべての任意チェックが最終状態で失敗していないこと、UP_TO_DATE: yes|not-required、および MERGEABLE: yes です。
このスイートでは、プロバイダーのネイティブなマージルールで許容される場合でも、必須チェックの skipped と neutral を意図的に成功として扱いません。「失敗したものがない」ことだけを確認すると、4 種類の異常な状態を正常と判定してしまいます。チェックが設定されていないリポジトリ、チェックがまだキューに入ったままの状態、必須チェックの報告がまだ始まっていない状態、そしてパスフィルターによってワークフローがすべてスキップされた状態です。
CI: NONE、CI: BLOCKED、完全一致するヘッドの不一致、または必須チェック、厳格ポリシー、マージ可能性の状態を確認できない場合、その課題は失敗となります。CI: FAILED、ソースの競合、または課題エージェントがマージ可能と判断したヘッドでの STRICT_POLICY: required と UP_TO_DATE: no の組み合わせも失敗となり、修正の試行回数を使い切ります。人による承認が保留中の場合は記録したうえで、その PR は失敗とせず、人に委ねます。
課題 PR は準備完了の状態で開かれ、レビュアーは割り当てられません。人にレビューを依頼することはありません。人が実際に行うレビューは、フェーズ 2 でエピックブランチを一度確認することだからです。マージを許可する前に、PUBLISH_STATE を通じて配信状態として成功の証明を公開します。
エピックの先端を含まなくなったヘッドも、同じように失敗となります。 そのブランチは、移動済みのエピックブランチに対してビルドおよびテストされているため、成功した CI は統合後の結果を何も証明しません。既存のブランチ上で、エピックの先端を取り込んで PR を再び成功状態にするよう、課題エージェントに差し戻します。この CI 実行で初めて、統合済みヘッドがテストされます。保護されたターゲットでは、STRICT_POLICY: required がこれを強制します。epic/* ブランチは通常保護されていないため、ほかに強制するものはありません。
この条件を満たしたら、MERGE をディスパッチしてマージを許可します。再読み込みとマージを同じコンテキストで行うのは、その間に何も挟んではならないためです。
Merge {PR_URL} at exactly {approved full SHA}, and only if it is green at that head.
Immediately before the merge invocation, read
/check-ci --pr={PR_URL} {approved full SHA} --once and require every one of:
{the green predicate above, verbatim}
plus REVIEW_REQUIREMENTS: satisfied|not-required. This closes the race where the target,
checks, or review requirements changed since the authorization was issued. Any non-green
reread, and pending human approval, mean you do not merge — report it and stop.
Then invoke /create-pr --merge --pr={PR_URL} --head-sha={approved full SHA}. create-pr
resolves the repository-preferred merge method through the preflighted delivery binding.
Use whatever the repository prefers and override nothing, unless this brief names a merge
method — then pass it as --merge-method.
Merge no head other than {approved full SHA}. If you cannot merge that exact head under
those conditions, reporting why is the correct outcome.
Return ONLY:
PR_STATE: merged | queued | unmerged
HEAD_SHA: {the full head the provider reports}
MERGE_COMMIT: {sha or none}
MERGE_QUEUE: {queue identifier or none}
REASON: {why, when not merged}
CI が成功した後でも、ユーザーからの相反する指示とブランチ保護ルールを尊重してください。イシュー PR では、スカッシュは一般的で望ましいリポジトリの設定です。エピックブランチにはイシューごとにクリーンなコミットが 1 つ残り、コミットごとの履歴は PR 上で引き続き確認できます。同期 PR は例外です。「ターゲットの同期」を参照してください。
マージの確認は、マージを実行したエージェント以外が行います。 階層はガードではなく、レポートをその内容のまま再読することもガードにはなりません。誤ったヘッドをマージしたエージェントが、要求されたヘッドを記録すれば、そのエージェント自身の戻り値だけで行う確認はすべて通過してしまいます。したがって、PR_STATE: merged によって配信が完了するのは、マージを実行したエージェントが作成していない次の 2 つの確認を経た場合に限られます。
-
新しい
INSPECT— 別のエージェントが、どのヘッドがマージされたかをフォージ自身の記録から読み取ります。PR_STATE: merged、承認済み SHA と一致するHEAD_SHA、epic/{slug}のBASE、およびnoneではない完全な SHA のMERGE_COMMITを必須とします。 -
コミットが実際にブランチ上にあることを、自分で読み取って確認します。
git fetch "$BASE_REMOTE" "epic/$SLUG" git merge-base --is-ancestor "$MERGE_COMMIT" "$BASE_REMOTE/epic/$SLUG"確認対象は承認済み SHA ではなく、マージコミットです。スカッシュが設定されている場合、承認済み SHA は意図的に何の祖先にもなりません。コミットが新しく作られるため、承認済み SHA を確認対象にすると、すべてのスカッシュマージで失敗し、追加の確認には何も合格しません。
どちらかの確認に失敗した場合は汚染状態です。 2 つ目の確認に失敗した場合は、より重大な問題です。INSPECT がクリーンなのにマージコミットがエピックブランチ上にない場合、その PR は別の場所にマージされています。最も可能性の高い原因は、epic/{slug} ではなくデフォルトブランチをターゲットにしたブランチです。この時点より前には、これを再確認する処理がありません。BASE がエピックブランチでない場合も同様に扱い、ベースフィールドにすでに示されている内容を、祖先関係のテストで確認するまで待ってはいけません。
承認していないマージ済みのHEADがエピックブランチに混入した場合は、チケットを取り込めなかった場合とは種類の異なる失敗です。承認していないコードが、以降のすべてのIssueが基盤とし、Phase 2が検証し、エピックPRで人に提示されるブランチに入り込んでいます。Issueを失敗扱いにするだけでは十分な対応ではありません。問題が起きたのはIssueではないからです。
そのため、実行を停止してください。 そのブランチにこれ以上Issueを割り当てず、最終処理も実行せず、エピックPRも作成しないでください。Phase 3では引き続き台帳を解決します。起票済みのチケットは実際の作業であり、その登録によってブランチに影響が生じることはありません。Phase 4でも引き続き報告しますが、通常の形式ではなく、次の形式を使います。
Epic {name} — HALTED, {n} of {m} issues merged into epic/{slug}, {duration}
CONTAMINATED epic/{slug} carries a merge this run did not approve.
issue #{id}
approved {approved SHA}
merged {head the forge reports}
commit {merge commit}
found by {the INSPECT mismatch, or the ancestry test}
DO NOT MERGE No epic PR was opened. Nothing here is reviewed work.
The branch needs a person before this epic goes further.
MERGED {the issues that landed on approved heads, with PR links}
NOT ATTEMPTED {everything the halt cancelled}
通常のレポートでは、エピックPRを先頭に記載します。それが、この実行でユーザーのために完了できないことだからです。停止した実行にはエピックPRがありません。その事実を偽ったり、最も目立つ行が欠けている形式をそのまま使ったりすると、レポート全体が伝えるべき唯一の事実が埋もれてしまいます。実行が決してしてはならないのは、未承認の内容をレビュー済みの作業として提示するPRを人に渡すことです。この設計の自律性は、実行がそうだと示す内容を最終レビューの対象とすることを前提にしています。
PR_STATE: queuedは、PR URL、承認済みSHA、キュー識別子を含む、永続化された保留中のマージ作業項目を作成します。INSPECTでポーリングしてください。各検査のHEAD_SHAも、保存された承認済みSHAと一致することを必須とします。まだマージされていない検査で不一致があれば、そのIssueを失敗扱いにします。異なるHEADを承認したり、完了扱いにしたりしてはいけません。PR_STATE: mergedを報告する検査で不一致があっても、Issueの失敗ではありません。キューが未承認のHEADをマージした、つまり汚染が発生したということであり、前述のとおり実行を停止します。承認済みHEADがマージされたこと、または最終的に却下されたことが確認されるまで続けてください。それ以外の結果はすべてマージ失敗です。Issueはオープンのままにし、依存先のブロックを解除しないでください。
キューでの受理後はただちにqueuedを、マージの確認後はただちにmergedを、前述のスケジューラー状態の手順を使って公開してください。キューによる却下、CI/ポリシー検査の失敗、CI修正試行の上限到達、またはPRが存在するその他のスケジューラー終端経路では、事実に即した失敗状態と終端コメントを使い、同じ手順で公開します。
マージが確認された後に限り、トラッカー状態の公開を通じて納品コメントを公開し、Issueをクローズしてください。トラッカーで完了としてクローズされたことが報告されたら、グラフを再評価します。依存先のブロックを解除するのは、マージではなく、その確認です。
イシューが失敗した場合
失敗はすべて同じではなく、適切な対応もそれぞれ異なるため、一律の再試行ポリシーはかえって無駄になります。次の表はガイドラインであり、分類体系ではありません。ここにない事態が発生した場合は、その状況を直接検討してください。ここまでにどれだけのコストがかかったか、また、再試行して結果が変わる現実的な可能性はどの程度あるかを考えます。
| 失敗 | 再試行する? | 理由 |
|---|---|---|
| エージェントが停止した — クラッシュ、ツールエラー、コンテキストの枯渇 | はい、1回 | 何も学べておらず、ただ倒れただけです |
| プッシュ、PR、コメント、または状態の更新に失敗した | いいえ | 再試行すると成果物が重複する可能性があります。ブランチとPRは人間のために保持します |
--max-ci-fixes 回試みてもCIがまだ失敗している | いいえ | それまでの試行が再試行です。CIは、別のエージェントが解決できる種類の問題ではないことを示しています |
| PRは成功しているが、マージ前の再確認で問題が見つかった | いいえ | イシュ―エージェントの修正予算は使い切られており、その主張はすでにフォージの結果と食い違っています |
| エピックの範囲外のものが原因でブロックされている — スキーマ変更、認証情報、判断 | 絶対にしない | 再試行しても、不足しているものは補えません |
| プランエージェントが拒否した — チケットが曖昧すぎる | 絶対にしない | 人間の対応が必要です |
コストの高い失敗ほど、再実行して成功する可能性は低くなります。そのため、一律の「1回再試行」は、最も見込みのない作業に最も多くの労力を費やすことになります。
失敗したイシューのPRとブランチは保持します。 PRを閉じたり、ブランチを削除したりしないでください。また、トラッカーのイシューもオープンのままにし、トラッカーの状態を公開するで示したPRと理由を記載した最終コメントを残します。作業は実際に行われており、人間が引き継ぎます。イシューはグラフ内で未完了のままなので、その依存先は取り残されます。
失敗した作業を土台にしてはいけません。 #3が失敗した場合、#3のブランチを使って#4、#5、#6に取り組まないでください。そのブランチにはCIを通過していないコードが含まれています。後から人間が#3を別の方法で修正した場合、その上に積み重ねた作業はすべて、実在しない前提に基づいていたことになります。依存先をスキップし、ブロックされていない無関係な作業を進めて、最後に取り残された作業を報告してください。
ブロックされたイシューは割り込みではなく、記録すべきデータです。 記録して作業を続けてください。無人実行の価値は、12件すべてが1つの回答を待つことではなく、ユーザーが眠っている間に12件中9件が完了することです。
発見事項の記録
作業を進めるうちに、サブエージェントが誰も想定していなかったことを見つける場合があります。台帳に記録してください。
発見事項とは、取り組んでいるチケットの範囲外にあるものです — このチケットでは呼び出しただけのモジュールにあるバグ、テストスイートの不足、誰も予想していなかったブロッカー、計画の前提が誤っていたことなどです。チケットを起票するようなものを指します。
このチケットで触れたコードにある小さな不備は、発見事項ではありません。 修正したか、意図的に残したかのどちらかです。いずれにせよ、台帳ではなくPRに記録します。この一文がないと、実装者が自分の差分で気づいた項目が台帳に何十件も並び、ノイズの壁は何も報告しないのと実質的に同じになります。
唯一の例外は、範囲より重大度を優先することです。 実行中に本当に解決できなかった深刻な問題は、作業範囲内であってもチケットにする価値があります。「既知の欠陥を残したままリリースした」という事実が、セッション終了時に消えてはならないためです。
重複を避けてください。 #3、#5、#7の作業中に同じ発見事項に気づいた場合は、チケットを3つ起票するのではなく、3回の発見を記録した1つの項目にします。
チケットは実行中に下書きしてください。最後まで待つ必要はありません。下書きには開発スタックもCIも不要なので、他に作業がない時間を使えます。確認済みの発見事項ごとに、チケット作成エージェントを起動します。
Investigate and write a ticket for this finding.
Finding: {what was noticed, where, during which issue}
Investigate before you write. Read the code. Check logs and stack traces. Reproduce it if
you can. Research externally if the behaviour depends on a library or platform you're
unsure about. Confirm it is actually real and understand WHY.
If it turns out not to be real, say so and stop — that is a useful answer.
Otherwise write a ticket someone can pick up cold, months from now, with no memory of this
run: what the problem is, the evidence, the analysis, and a recommended approach.
Write it to {state_dir}/discoveries/{n}.md — do not file it.
調査こそが重要です。一行の印象だけで起票されたチケットは、書いたときには妥当に見えても、誰かが着手すると内容が崩れます。何も起票しないより悪く、読む人の時間を無駄にし、システムが起票する他のチケットへの信頼まで損ないます。
単独ではチケットにするほどではない小さな指摘も、消えてしまうべきではありません。 実行中に小さな不備が積み重なった場合は、それらをすべて列挙した整理用チケットを1つ作るのが適切です。記録を残しつつ、トラッカーへのスパムを避けられます。どの程度の蓄積なら起票に値するかは判断してください。きれいなエピックに対する中身のほとんどない整理用チケットは、ただのノイズです。
フェーズ2:エピックを最終化する
マージ可能な課題はすべてマージされています。このフェーズがレビューです — その上に追加で行う最終チェックではなく、実行で生成されたコードを読む唯一の機会です。課題ごとのCIで各ブランチのビルドとテスト合格は確認されていますが、それとは別の、はるかに限定的な保証です。
統合後の結果を確認できる唯一の場所でもあります。個々には正しい課題でも、組み合わせると矛盾することはよくあります — 2つのチケットが異なる方向に書き換えたヘルパーや、課題4で広げた契約を課題9で再び狭めたケースなどです — そしてこのブランチより前の段階では、それを見つけられませんでした。
そのため、十分な時間をかけてください。修正を行いながら3回レビューし、実行全体の品質に関する主張はここにかかっています。
システム全体で --depth=deep を要求するのは、ここだけです。 それ以外では、検証はデフォルトで軽量です — 人が /verify と入力する場合も、player-coach ループの各ターンも同様です — 成長し続ける差分に対して判断を分散させても収束しないためです。ここではその理由は当てはまりません。このパスは、完成して統合済みで、これ以上変更されないブランチに対して一度だけ実行され、エピックが受ける唯一のレビューです。このフェーズで避けるべきコストは発生しません。
一部の課題が失敗していても、このフェーズを実行してください。 不完全なエピックも全体としてレビューされます。ただし、完全であるとは主張できません。唯一の例外は、エピックブランチの汚染により実行が停止した場合です。その場合、ブランチをレビューしてPRを作成することこそ、絶対に行ってはならないことです。
0. 検証前に同期する
同期の失敗によって同期がオフにされていない限り、差分を測定し、BEHIND がゼロより大きければ、トリガーの条件にかかわらず今すぐ同期してください。レビューでは、競合解決も含め、ターゲットにマージされた場合のコードを確認してください。ターゲットがすでに先に進んだブランチを確認してはいけません。
1. エピックブランチを検証する
検証エージェントを起動します。レポートを保持するので、あなたが保持する必要はありません。
Verify the epic branch as one integrated change.
Check out epic/{slug} and invoke:
/verify --depth=deep --mode=report-only --scope=branch --base={BASE_REMOTE}/{target}
--format=json --output={state_dir}/epic-verify/{round}.json
Return ONLY these three lines:
BLOCKING: <count of issues at severity {severity} or above>
TOTAL: <count of all issues>
REPORT: {the output path}
Return no findings, no descriptions, and no narration — the orchestrator does not read
verification reports and cannot afford the context.
ブロッキングとなる指摘がゼロなら、このフェーズを終了してステップ4に進みます。
2. 修正を計画する
修正計画エージェントを起動します。あなたが読まなかったレポートを読みます。
Write a remediation plan for the findings in {report path}.
The epic branch epic/{slug} is complete and its individual issues are merged. Verification
of the integrated result found blocking issues. Read that report and the code, and write a
plan to fix them.
Write the plan to {state_dir}/plans/remediation-{round}.md
Fix what was found. Add no functionality: anything that looks like new scope belongs in a
follow-up ticket, not in this plan — say so in the plan rather than planning it.
If a finding is not real, say so in the plan and explain why, instead of planning a change
that exists to satisfy a reviewer.
3. 修正を実行する
通常のブランチで通常のイシューエージェントを実行します。イシューで既に使用しているものと同じ指示に、プランとして {state_dir}/plans/remediation-{round}.md を指定し、ブランチ名を epic-{slug}-remediation-{round} にします。
その後、通常のイシューと同じマージ手順に進めます。ほかの実行とまったく同じように、独立した PR として反映されるため、特別な対応は必要ありません。
その後、手順 1 に戻ります。 検証ラウンドは合計で最大 3 回です。検証、修正、検証、修正、検証の順に行います。3 回目の後もブロッカーが残っている場合は、完了レポートで担当者に引き継ぎます。自らの修正箇所から問題を見つけ続けるループは、さらにラウンドを重ねるのではなく、エピックに人の対応が必要だということを示しています。
4. エピック PR を開く
ターゲットと競合しているエピック PR は、成果物として届けられたことになりません。 検証には数時間かかり、その間にターゲットが更新されています。同期が無効でない限り、ドリフトをもう一度測定してください。BEHIND がゼロより大きければ、もう一度同期します。この遅いタイミングでの同期は一度だけ行います。その後にターゲットが再び更新されても、PR は開き、下記のマージ可能性チェックで判断します。
遅いタイミングでの同期により競合が解消された場合、その解決内容はまだ誰も確認していないコードです。3 ラウンドの上限とは別に、もう一度確認してください。step 1 のブリーフで --depth=deep の代わりに --depth=light を使い、{state_dir}/epic-verify/late-sync.json に書き込みます。ブロッキング項目が見つかった場合は、step 2 と 3 を通じて一度だけ修正し、step 1 には戻りません。なお未解決の項目は、完了レポートで人に引き継ぎます。
実行自体の状態から、各 issue の結果、各最終化ラウンドの検証結果、残っているブロッキング項目を含むコンテキストファイルを作成し、そのパスとともに OPEN_EPIC_PR を epic/{slug} と {target} を指定してディスパッチします。delivery-agent は epic/{slug} をチェックアウトし、{target} をターゲットとするドラフト PR を開きます。create-pr はチェックアウト中のブランチの PR を開くため、ヘッドを選択するのはチェックアウトです。そのため、エピックブランチはパラメーターとして渡し、エージェントに推測させません。
返される PR_URL、PR_STATE、BASE、HEAD_REF、HEAD_SHA、MERGEABLE を確認します。最初の 5 つが指定どおりであることを必須とします。PR_STATE: draft、BASE が {target}、HEAD_REF が epic/{slug} であり、HEAD_SHA が自分で取得したエピックの先端と一致することを確認します —
git fetch "$BASE_REMOTE" "epic/$SLUG"
git rev-parse "$BASE_REMOTE/epic/$SLUG"
フェッチは省略できません。リモート追跡参照の鮮度は最後にフェッチした時点で決まります。このフェーズが引き継ぐすべてのマージは Phase 1 のフェッチ後にリモートへ反映されているため、古い参照では、正しい PR を実行の最終ステップで誤って却下することも、誤った PR を受け入れてしまうこともあります。
— ヘッドを選択したチェックアウトは、あなたの目の届かないところで行われたためです。BASE と PR_STATE だけでは、誤ったブランチから開かれた PR や、同じターゲットに対してすでに存在するドラフト PR も通過してしまいます。そして、実行全体を通じて人に引き渡す成果物はこれだけです。ディスパッチには次のルールを含めます — この PR はドラフトとして開き、そこで止めてください。マージも、レビュー可能状態への変更も行いません。
MERGEABLE が答えるのは、「ヘッドは {target} と競合しているか」という問いです。プロバイダーによってはドラフトやパイプラインの実行待ちなど、別の状態もマージ可能性のフィールドに含めるため、この定義をディスパッチに含めます。そうしないと、ドラフトのエピック PR は常にブロック状態と判定されてしまいます。また、プロバイダーの計算が終わるまで再確認するルールも含めます。unknown は答えではありません。MERGEABLE: no でも PR の作成は止めませんが、実行で何を届けたかは変わります。完了レポートでは REVIEW THIS ではなく、競合を NEEDS YOU の先頭に記載します。
コンテキストファイルは人間のレビュアー向けのエピック概要です。レビュアー向けバックログである epic-context.md とは異なり、PR 本文に含めるものではありません。
デフォルトブランチをターゲットにする PR はこれだけです。 そのため、完了を約束する本文を記載できる PR もこれだけです。コンテキストにはエピックのクローズ用リンク行を含め、人間がマージすると、説明されている成果物のデリバリーと同時にエピックチケットがクローズされます。
ドラフトのままにし、決してマージしないでください。 これこそが、実行を自律的にできた理由です。人間が統合されたブランチを読み、通常どおりに動作を試し、自分でマージします。レビュー可能状態への変更やマージを行うと、レビューのための引き継ぎが、気づかれないまま未レビューのデプロイへと変わってしまいます。
フェーズ 3:台帳を解決する
--new-issues に依存します。
create— 作成済みのチケットを登録します。チケットごとにFILE_TICKETを 1 回ディスパッチします。propose(デフォルト) — 承認のために提示し、承認されたものを登録します。実行中に作成済みのため、ユーザーが承認するのは一行の概要と約束ではなく、完成したチケットです。never— 何も登録しません。下書きは状態ディレクトリに残り、レポートに表示されます。
create が何もしない、または一度も解決されなかったバインディングは、comment や close と同じように処理が縮退します。下書きは状態ディレクトリに残り、完了レポートに含まれ、フェーズ 0 の確認でもその旨がすでに伝えられています。--new-issues=never では create はまったく必要ないため、create の欠落がブロッカーになるのは、ほかの 2 つのポリシーを使用する場合だけです。
トラッカーへの書き込みには、すべてエージェントが作成したことを記載します。 コメントやチケットはユーザーのアカウントから投稿されます。あとで読む人が、人間が書いたのかどうか推測しなくて済むようにしてください。冒頭に短い一文を添えるだけで十分です。仰々しくする必要はありませんが、曖昧にならないようにしてください。
フェーズ 4:完了レポート
人間の対応が必要な事項を先に示します。統合済みの一覧は、それほど重要ではありません。PR は時間のあるときに読めます。対応を求める 3 行は、最初の画面内で見落としようがないようにしてください。
Epic {name} — {n} of {m} issues merged into epic/{slug}, {duration}
REVIEW THIS {epic PR url} — draft, unmerged, waiting for you
MERGED #41 #42 #43 #44 #45 #47 #48 #50 #51
{PR links}
NEEDS YOU #46 CI still red after {n} fix attempts. PR {url} open.
#49 plan agent refused — ticket doesn't specify {thing}.
STRANDED #52 blocked by #49
EPIC VERIFY round 1: {n} blocking → remediation merged; round 2: clean
{or: round 3 still flags {thing} — the epic PR carries the detail}
SYNC {k} syncs, {c} with conflicts; epic/{slug} is {behind} behind {target},
{merges cleanly | CONFLICTS}{; syncing off since sync PR {url} failed}
NEIGHBOURS {other epics Phase 0 flagged, and the files they share with this one now}
FRICTION {CI failures and the fix attempts they cost, issues sent back to
absorb a moved epic tip}
PERFORMANCE {issues green on their first CI run, CI-fix attempts per issue,
blocking findings per epic-verification round}
DISCOVERIES {n} tickets drafted{, awaiting approval}{, + 1 combined cleanup ticket}
エピック PR を先に示すのは、実行がユーザーに代わって完了できない唯一のものだからです。NEIGHBOURS はフェーズ 0 でフラグが立てられた場合にのみ表示します。該当する場合は、共有ファイルを再集計してください。
エピック PR がターゲットと競合する場合、REVIEW THIS の行を次の内容に置き換えます — 競合のある PR では、レビューする前に人間が実行の作業を完了させる必要があります。
NEEDS YOU {epic PR url} — draft, CONFLICTS with {target}
{last sync PR url and why it failed, or: the target moved after the final sync}
その後、書き戻しが有効なら、レポートをエピックチケットへの COMMENT として投稿し、エピックはオープンのままにします。 検証に問題がないことは、成果物の納品を意味しません。エピック PR はデフォルトブランチに対するドラフトのままであり、人間がマージするまでは実行で作成されたものは何もリリースされていません。エピックチケットをオープンのままにしておけば、その状況を正確に示せます。また、その PR の Closes リンクにより、人間がマージした瞬間に、説明された成果物の納品とともにチケットがアトミックにクローズされます。
状態
実行状態はリポジトリの外にあり、$XDG_STATE_HOME/epic-runner/{project}/{epic}/
(存在しない場合は ~/.local/state/...、次に ~/.epic-runner/)に保存されます。
plans/{id}.md materialized implementation plans
plans/remediation-{n}.md the finalization plans written from each epic verification
issues/{id}.md per-issue journey logs written by issue-agents
syncs/{n}.md per-sync journey logs: every conflicted file and its resolution
discoveries/{n}.md drafted follow-up tickets
epic-context.md completed / current / remaining issues, handed to planners and implementers
epic-verify/{n}.json the verification report for each finalization round
run.json status blocks, approved HEAD SHA, pending merge-queue work, inferred edges, CI-fix counts, epic branch creation time, sync history, failure reasons, and any halt
epic-context.md と epic-verify/ 配下のすべてのレポートは、サブエージェントに渡すパスであり、自分で読むことはありません。前者はイシュー間で知識を引き継ぐためのものです。後者は検証レポートであり、それらを読まないというルールに変更はありません。
リポジトリ内には決して保存しません。長期にわたるエピックでは作業ツリーが散らかり、レビュー対象の差分も汚染されます。/tmp にも決して保存しません。実行中に再起動すると、台帳も作成済みのチケット草案も失われます。
ライフサイクルを管理するのはトラッカー、証拠を管理するのは run.json です。 ライフサイクルは保留中/進行中/完了/失敗、永続的な PR の関連付け、依存関係を解除する条件です。これらはすべて人が確認できる情報であり、フェーズ 1 の進行に合わせて書き戻されます。証拠は承認済みのヘッド SHA、CI の証明、マージキュー識別子、再試行回数、計画、ジャーニーログ、未公開の草案です。これらは技術的かつ非公開の情報であり、イシューが完了したことを示す唯一の記録にしてはなりません。
停止した実行は run.json に記録され、再開してもその記録は消去されません。 停止を宣言したら、そのイシュー、承認済みの SHA、実際にマージされたヘッド、マージコミットを直ちに記録してください。エピックブランチが汚染されていれば、6 時間後でも次の火曜日でも汚染されたままです。再開しても何も解決しません。その記録を見つけたら報告して停止してください。ブランチを確認した人間だけが、実行で次に何をしてよいか判断できます。ローカル状態が問題なさそうだからという理由で停止後の実行を再開するのは、このスキルが行いうる最悪のことです。未承認のコードが、レビュー済みの作業として人に提示される原因になるからです。
再開時は、まずトラッカーを読み直し、次にオープン中の PR を確認してください。完了としてクローズされた子イシューを確認した場合は、ローカルファイルの内容にかかわらず、再実装しません。 ローカルの証拠が不足している場合は、エージェントが作成した納品コメントと、PR ごとの INSPECT ディスパッチを使って納品内容を再構成してください。両者が食い違う場合は、納品済みの作業を二重に実装して解決するのではなく、レポートにその不一致を明記してください。
オープン中の PR をマージチェックに進められるのは、イシュ―エージェントが作業を完了したヘッドだけです。 そのイシューの run.json に MERGEABLE ブロックがあり、その HEAD_SHA が INSPECT で確認した PR の現在のヘッドと一致する場合に限り、その PR を採用してください。それ以降にヘッドが変更されている場合、または該当する記録がない場合は、作業途中で残されたものです。その場合はマージチェックに進めず、既存のブランチ上でイシュ―エージェントに作業を再開させてください。現在の PR ヘッド、トラッカーの状態、またはフォージの報告と食い違う run.json の記録から、承認を推定してはなりません。
再開は合流であり、やり直しではありません。 PR がオープン中で CI の途中にあるイシューは、既存のブランチで引き継ぎます。別のブランチで再実装してはなりません。これが、ブランチ名と PR 本文の両方にイシュー ID を含める理由です。そうすることで、何を見ているのかを識別できます。オープン中の同期 PR も、同じ方法で epic-{slug}-sync-{n} ブランチを使って再開します。ただし、run.json に同期失敗の記録がある場合は、同期を停止したままにしてください。それ以外の場合は、ほかの作業をスケジュールする前に、再開時点での遅れを計測してください。数日後に再開した実行は、すでに大幅に遅れている可能性があります。同期履歴も run.json の作成時刻もない場合、経過時間のトリガーには基準点がありません。エピックの開始から 1 日以上経過しているものとして扱ってください。
--write-back=off の場合、イシューが完了したという永続的な記録が残らないため、再開可能性は実際に低下します。そうではないかのように装わず、その旨を伝えてください。
常設ルール
- コードを書かないでください。 それは Issue エージェントが行います。
- 検証しないでください。
verifyはフェーズ 2 の最終処理エージェント内で実行され、そこで確認できるのはそのログの3行だけです。 - 検証レポートや差分を読まないでください。 コンテキストは、実行全体を通して保持しなければならないリソースです。
- 自分で forge を呼び出さないでください。
create-prやcheck-ciは実行せず、トラッカーも変更しません。デリバリーエージェントが両方を行い、必要なフィールドを渡します。Issue エージェントも、それぞれのコンテキスト内でこれらのスキルを実行します。このルールはあなた自身に適用されます。 - 停止後に処理を再開しないでください。 汚染されたエピックブランチは汚染されたままです。次にどうするかを判断できるのは、そのブランチを確認した人間だけです。
- エピック PR を決してマージしないでください。 ドラフトとして開き、リンクを人間に渡してください。この実行の自律性は、最後に人間がレビューすることを前提として与えられたものです。エピック PR をマージすることは、与えられていないものを使うことになります。
- ブランチ保護を回避したり、ルールを緩めたり、マージを可能にするために自分で承認したりしないでください。
- 明確にグリーンで、マージ可能と確認できない PR は決してマージしないでください。
- エピックを常に同期してください — ターゲットとの同期を参照してください。ターゲットとのコンフリクトがあるエピック PR は完了していません。
- dev-stack のコンシューマーは一度に1つだけです。 Issue の実装は、最初のコミットから PR がグリーンになるまでの間、コンシューマーとして数えます。同期も同様です。計画、ドラフト作成、マージは数えません。
- フェーズ 0 で判断できた事柄について、実行を決して中断しないでください。
- 起票するチケットはすべて、実際に対応する価値のあるチケットにしてください — 調査済みで、根拠があり、実行可能なものです。雑なチケットが許されるプロジェクトはありません。使い捨てのリポジトリとは、文句を言う人がいないというだけです。
ハーネスの対応関係
上記のロジックはハーネスに依存しません。以下は、それに対応する Claude Code の仕組みです。別のハーネスを使う場合は、このセクションを置き換え、ほかはそのままにしてください。
| 機能 | Claude Code |
|---|---|
| 分離されたサブコンテキストを起動する | Agent ツール |
| サブエージェントに専用のチェックアウトを割り当てる | Agent の isolation: "worktree" |
| 編集できない状態で調査やドラフト作成を行う | プランモード — EnterPlanMode / ExitPlanMode |
| 別のスキルを呼び出す | Skill ツール |
| サブエージェントごとにモデルを選択する | Agent の model、またはスキルのフロントマター |
| エージェントツリー全体の並列実行数の上限 | 該当するものはありません。ハーネスに上限がある場合は、起動時の呼び出しで設定し、子エージェントはその上限を引き継ぎます |
ハーネスが同時に実行できるサブエージェント数に上限を設ける場合、その適切な設定はあなたの責任です。 最外層の呼び出しに関する設定であり、このスキルが最外層の呼び出しだからです。最も幅の広い箇所ではなく、ツリーの最も深い箇所に対応できる必要があります。フェーズ 2 では、verify が9つのサブエージェントに処理を分岐する一方で、先祖にあたるスケジューラーとエピック検証エージェントの2つがまだ実行中です。各層が必要とする数だけを基準に上限を設定すると、最下層に余裕がなくなります。その兆候はエラーではなく、レビューのパイプラインが気づかれないまま一度に1人のレビュアーしか実行しなくなることです。
役割ごとのモデルティア
このツリーには、負荷が大きく異なる作業が含まれています。すべてに単一のティアを適用すると、低負荷の作業では無駄が生じ、高負荷の作業では不十分になります。これはコストのティアであり、モデルではありません。オーケストレーターがモデルを選び、これらの既定値を基準に選択します。
| 役割 | ティア |
|---|---|
| オーケストレーター — このスキル | 最上位 |
| plan-agent、remediation-planning agent | 高 |
| epic-verify-agent | 高 — ただし、それ自体のためではありません |
| issue-agent、sync-agent、ticket-writing agent | 中 |
MERGE 時の delivery-agent | 中 — 後戻りできない操作の直前にある最後のゲートです |
| それ以外のすべての操作での delivery-agent | 低 |
計画にコストをかけ、実装者が推論せずに済むようにします。 plan-agent は、それを使う issue-agent より意図的に一段上のティアで実行します。マージ前にイシューを読むレビュアーはいないため、計画が要件ドキュメントのすべてであり、パイプラインの中で賢さに投資するのに最も低コストな箇所だからです。優れた計画に基づく issue-agent は、設計ではなく実行を行います。チケットの負荷が本当に大きい場合は、ティアを一段上げてください。既定値は出発点です。
MERGE は、delivery-agent を低コストに保つ理由を際立たせる例外です。 それ以外の作業はすべて、コマンドの実行と転記です。しかし、MERGE は違います。マージ前の成功条件を読み、判断を下します。また、あなたの承認はその証拠が存在する前に発行されているため、その後に再確認する工程はありません。後戻りできない操作の直前にある最後のゲートを、このツリーで最も安価なモデルに任せるべきではありません。この設計では、条件を正確に適用できると信頼できるティアでチケットごとに一度ディスパッチすることが、最も低コストな保険です。
epic-verify-agent のティアは、epic-verify-agent 自身のためではありません。 その役割は「verify を呼び出し、件数を返す」という3行にすぎません。しかし、ハーネスで明示的な選択がない場合にサブエージェントが親のモデルを継承するなら、そのティアは配下にいる9人のレビュアー全員のティアになります。フェーズ2は、このスキルでエピックをレビューする唯一の機会です。イシューごとの検証はありません。そのため、安価なディスパッチャーを使ってもほとんど得るものはなく、気づかないうちにそのレビューの質を下げてしまいます。子エージェントに合わせてティアを選んでください。