objectstack-pm-dispatch について
objectstack-pm-dispatchは、プロジェクトメンテナー向けのマルチエージェント納品スキルです。GitHubのバックログから作業を選び、開発エージェントに割り当て、結果をレビューします。プロジェクトの規約はリポジトリ内のファイルから読み、状態はGitHubに保存するため、新しいセッションから再開できます。
主な機能
- 01GitHubの課題選択と分類
- 02課題の確保と開発エージェントへの割り当て
- 03複数リポジトリとバッチ数の設定
- 04依存関係、重複、古い前提の確認
- 05エージェント報告の収集とレビュー
- 06ワークフロー状態をGitHubに保存
活用シーン
- 01キュー対象の未割り当て課題を処理
- 02リポジトリ横断の変更を振り分け
- 03依存順に複数リポジトリの作業を計画
- 04新しいセッションから納品作業を再開
MCP 対応
CLIを利用できない場合、GitHub APIまたはMCPツールで同じ状態操作を実行できます。このスキルはMCPサーバーのインターフェースを定義しません。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ objectstack-ai/ objectstack --skill objectstack-pm-dispatchインストール後、Claude Code で/objectstack-pm-dispatchを入力して読み込まれたか確認します。
同梱ファイルrules/dev-template.md
詳細
PM ディスパッチ — あらゆるプロジェクトに対応するマルチエージェント配信ループ
このスキルを使うと、1つのセッションがPM エージェントになります。PM エージェント自身がコードを書くことはありません。 GitHub のバックログから作業を選び、各イシューを構造化レポートを返す開発者エージェントに割り当て、その後、成果をレビューして取り込みます。
選択 → 確保 → ディスパッチ → 収集 → レビュー → 報告 → 次のバッチ
メンテナーがループに関与するのは、各バッチ後に出力されるラウンドレポートと、人間の判断が本当に必要な場合に起票される判断イシューの2つの場面だけです。
このスキルは特定のコードベースに依存しません。ゲート、リリースノートの成果物、ブランチの命名、レビュー要件など、プロジェクト固有の情報はすべて、プロジェクト独自の規約ファイル(AGENTS.md、CLAUDE.md、CONTRIBUTING.md、…)から読み取ります。このスキルとそのファイルの内容が異なる場合は、プロジェクトのファイルが優先されます。
クイックスタート
1. インストール
npx skills add objectstack-ai/objectstack/skills --skill objectstack-pm-dispatch
(--all を指定して ObjectStack バンドル全体をインストールすると、このスキルも含まれます。)
2. 設定 — 任意です。 設定がない場合、ループは現在のリポジトリを対象に実行されます。現在のリポジトリが唯一のシャードであり、バックログでもあります。複数のリポジトリがある場合、バックログ用に別のリポジトリを使う場合、またはデフォルトを変更する場合にのみ、.claude/pm-dispatch.json を追加してください。
3. 実行
/pm-dispatch # drain the pm:queue backlog, 3 agents at a time
/pm-dispatch batch:5 # wider batch
/pm-dispatch #<n> #<n> # two named issues, nothing else
/pm-dispatch rounds:1 # one round, then stop and report
最初のラウンドで必要なラベルが作成され(冪等)、バックログが一通り確認され、バッチ完了時にラウンドレポートが出力されます。
設定
ループは、セッションの開始時にいるリポジトリから .claude/pm-dispatch.json を読み取ります。このファイルは任意です。 どのキーにもデフォルト値があるため、単一リポジトリのプロジェクトであれば、設定なしで動作します。
{
"backlogRepo": "acme/hotcrm",
"repos": ["acme/hotcrm", "acme/hotcrm-web"],
"batch": 3,
"mode": "subagent",
"conventionsFile": "AGENTS.md",
"routingLabelPrefix": "repo:"
}
| キー | 型 | デフォルト | 意味 |
|---|---|---|---|
backlogRepo | "owner/name" | 現在のリポジトリ | イシューをキューとして扱う唯一のリポジトリです。スケジュールに関する権限はすべてここにあります。 |
repos | string[] | [backlogRepo] | 作業の反映先となるすべてのリポジトリです。ラベルの設定とルーティングラベルの検証に使います。 |
batch | number | 3 | 同時に作業できる開発者エージェントの最大数です。 |
mode | "subagent" | "cloud" | "subagent" | ディスパッチのバックエンドです。ディスパッチバックエンドを参照してください。 |
conventionsFile | string | AGENTS.md、CLAUDE.md、CONTRIBUTING.md のうち最初に見つかったファイル | ゲート、ブランチのルール、リリースノートの成果物、レビューポリシー、機能拡張に関する方針を定めたファイルへの、リポジトリ相対パスです。パスを指定してすべてのディスパッチに含めます。 |
routingLabelPrefix | string | "repo:" | イシューをデフォルト以外のリポジトリにルーティングするラベルの接頭辞です。例:repo:hotcrm-web。repos の項目が1つの場合は機能しません。 |
不明なキーはエラーであり、ヒントではありません。 ファイルにこの表にないキーが含まれている場合は、停止してその旨を伝えてください。意図を推測したり、黙って無視したりしてはいけません。宣言された設定の半分が気づかれないまま無視される状態を防ぐことこそ、このループの目的です。
設定なしの場合の動作。 ファイルがない場合、現在のリポジトリがバックログであり、唯一の対象リポジトリです。batch は 3、ディスパッチはセッション内で行い、ルーティングラベルは適用されません。規約ファイルは自動検出されます。3つのファイルがどれも存在しない場合は、最初のラウンドレポートでその旨を伝えてください。ループ自体は実行されますが、すべてのディスパッチでプロジェクトのゲートが欠落します。
引数
/pm-dispatch [args] — 自由形式で、すべて省略可能です。引数は、その実行に限って設定ファイルを上書きします。
| 引数 | 意味 | デフォルト |
|---|---|---|
label:<name> | バックログのフィルターラベル。label:all は未割り当てのオープンイシューすべてを対象にします | pm:queue |
repo:<owner/name> | スキャンするバックログのリポジトリ | backlogRepo |
batch:<n> | 同時に作業できる開発者エージェントの最大数 | batch |
rounds:<n> | N ラウンド後に停止します | キューが空になるまで |
mode:subagent | mode:cloud | ディスパッチのバックエンド | mode |
#12 #34 … | 明示的なイシュー一覧。ラベルによるクエリを完全に置き換えます | — |
状態モデル — すべての状態は GitHub にあり、ローカルにはありません
ループは、ローカル状態がまったくない新しいセッションから再開できなければなりません。状態の読み書きには、次のシグナルのみを使用してください。
| issue のシグナル | 意味 |
|---|---|
| open + queue ラベル + 未アサイン | ディスパッチ可能 |
| アサイン済み | 確保済み/進行中 — アサイン先があなたでなければ、別のエージェントか人間が担当しています。決して触れないでください |
ラベル pm:dispatched | このループによってディスパッチ済み(確保コメントにラウンドが記録されています) |
ラベル needs-user-decision | メンテナーの判断待ち — 決してディスパッチせず、自動回答もしないでください |
| issue を参照する open PR | 実装済み、レビュー中 |
Fixes #n を含むマージ済み PR | 完了 — GitHub が issue をクローズします(同一リポジトリのみ) |
ラベルの初回セットアップ(冪等 — repos のすべてのエントリーに対して、最初のラウンドの開始時に実行してください)。
for R in $REPOS; do
gh label create pm:queue -R "$R" -c 0e8a16 -d "Ready for the PM dispatch loop" || true
gh label create pm:dispatched -R "$R" -c 1d76db -d "Dispatched to a developer agent by /pm-dispatch" || true
gh label create needs-user-decision -R "$R" -c d93f0b -d "Blocked on a maintainer decision — do not dispatch" || true
done
# Routing labels exist only on the backlog repository, one per non-default target:
gh label create "repo:hotcrm-web" -R "$BACKLOG_REPO" -c fbca04 -d "Lands in hotcrm-web" || true
CLI が利用できない場合は、gh の代わりに GitHub API/MCP ツールを使用してください。プロトコルは同じです。
ボードは1つ、別のトラッカーは不要です。 これらのラベルが状態マシンです。repos の各エントリーから issue と PR を取り込む GitHub Project を使えば、メンテナーが一元的に確認できます。PM は GitHub の外に追跡状態を一切保持しません。この不変条件によって、ループを再開でき、ボードの内容も正確に保たれます。
ラウンドループ
0. バックログの確認 — 分類は継続的な責務であり、依頼を受けて行うものではありません
メンテナーがバックログを事前に整理することはありません。毎回のラウンド(および待機中の確認)で、次の3つの条件のいずれかに該当するすべての issue を確認し、それぞれを分類してください。
pm:*/needs-user-decisionラベルがなく、分類ラベルもないもの — つまり、プロジェクトが「すでに振り分け済み」とみなすラベル(領域、コンポーネント、担当チーム)のいずれも付いていないものです。基本ルールです。pm:queueが付いていて、分類ラベルがないもの — 分類ラベルを実際に使用しているリポジトリに限ります(注意事項を参照)。- 分類ラベルが付いているものの、キュー状態(
pm:queue、pm:dispatched、セットアップで定義するその他のpm:*状態、またはneeds-user-decision)がないもの。
条件2と3では、updated_at が1、2分以上前のカードのみを対象にしてください。条件1にはそのような時間の下限は不要です。
「ラベルなし」のフィルターを1つ使うのではなく、なぜ選言条件なのか。 振り分け状態とキュー状態は独立した軸です。その片方だけを持つカードは、両方のビューで同時に見えなくなります。誰がその状態を作ったかにかかわらず、判定条件自体でその形を拾う必要があります。
⚠️ ラベルの欠如を条件とする選言条件は、そのラベルが存在するリポジトリに限定してください。 そうしないと、それ以外の場所ではラベルが常に存在しないため、確認処理自体がノイズの発生源になります。プロジェクトで確認対象から除外しているもの(保留中の作業、追跡用 issue など)は、引き続き除外してください。保留中のカードは通常、「分類済み、キュー状態なし」という形です。
⏳ 経過時間の判定には created_at ではなく updated_at を使ってください。確認処理によるラベルの逐次書き込みでは、数秒間だけラベルが半端に付いた状態になります。この発生源をカバーできるのは updated_at だけです。
- 自動でキューに追加(
pm:queue) — 場所または再現手順が明示された具体的な不具合、範囲が明確なツールまたはゲートの修正、復元時の不変条件に関する指摘、テスト専用の固定。確認すべきことはありません。ラベルを付ければディスパッチ可能になります。 - メンテナーの確認(
needs-user-decision) — 設計に関するカード、機能または契約の形状についての提案、意向や優先順位付けが必要な数週間規模の取り組み、保存データの移行方法に関わるもの、またはリリース済み機能の削除を伴うもの。ラベルを付けるだけで受信箱に登録できます。詳細な4軸分析は、カードに実際に着手する際に記述します。 - まず修復 — GitHub のサニタイザーによって本文が切り詰められた issue はディスパッチできません。修復方法をコメントに記載して、次に進んでください。
1. 候補を取得する
フィルターに一致するオープンなイシューを一覧表示し、担当者または
needs-user-decision ラベルが設定されたものを除外します。一致する親のオープンなサブイシューも候補です。親のキュー登録を引き継ぐため、サブイシュー自体にラベルは必要ありません。
各候補の本文全体とコメントを読みます。コメントには、作業の半分がすでにリリース済みであることが記録されている場合があります。読まずに割り当てると、エージェントの実行を丸ごと無駄にします。
割り当てる前に毎回、前提が古くなっていないか確認します。 イシューには起票日時点のリポジトリの状況が記載されていますが、アクティブなデフォルトブランチは急速に更新されます。割り当てる前に、指定されたファイルとサブシステムを最近の履歴と照らし合わせて確認します。
git log --oneline -20 origin/main -- <paths named in the issue>
gh pr list --state merged --search "<the issue's keywords>" --limit 10
「これは今も正しいか?」から始める割り当てなら、数分で済みます。確認せずに始めると、エージェントの実行をひとつ消費し、すでに解決済みの問題に対して二度目の競合する修正を行ってしまうこともあります。
2. トリアージ — 振り分けは PM の仕事であり、メンテナーの仕事ではありません
メンテナーからの入力は、イシューと pm:queue(またはチャットでのタスク名の指定)だけです。変更がどのリポジトリに反映されるかを把握している必要はありません。通常、その答えこそが分析です。
-
実際の
repos内にある各リポジトリのコードとイシューを照らし合わせ、変更がどこに反映されるかを判断します。{routingLabelPrefix}<name>を自分で設定します。振り分けラベルがなければ、デフォルト(バックログ)リポジトリが対象です。人間がすでに設定した振り分けラベルは、そのまま尊重します。 -
複数リポジトリにまたがる作業を、ひとつの割り当てにしてはいけません。 分割します。親イシューを作り、リポジトリごとにサブイシューをひとつずつ作成します。また、契約(スキーマ、API の形、共有型)を修正するサブイシューを最初にします。後続のサブイシューには、本文に
Blocked-by: <owner/repo>#<n>の行を記載します。Blocked-byの参照先がクローズまたはマージされていないイシューは、決して割り当ててはいけません。記憶に頼らず、選定時に GitHub で確認します。 -
親イシューにすでにサブイシューがある場合、キューラベルが必要なのは親だけです。トリアージ時に内容を展開し、各サブイシューをトリアージして振り分けます。メンテナーが示した依存関係の順序を維持し、順序が示されていない場合は契約を先にする順序を推定し、自分で
Blocked-by:の行を記載します。親イシューは調整用のノードであり、開発者に割り当ててはいけません。進捗確認用としてオープンのままにし、最後のサブイシューがクローズされたら、要約コメントを付けてクローズします。 -
メンテナーが簡単に却下できるよう、イシューにコメントをひとつ残し、監査記録とします。「Triage: lands in
hotcrm-web; reason: …」のように記載します。 -
振り分けは技術的な判断です。「どのリポジトリか」を尋ねてエスカレーションしてはいけません。 コードを読んでも本当に判断できない場合、そのイシューは要件が不明確です。振り分けではなく、根底にあるプロダクトに関する問いをエスカレーションします(ステップ 8)。
-
割り当てる前に、リポジトリをまたいで重複を確認します。 イシューの本文とタイムラインにあるリポジトリ間の参照をすべてたどり、他のリポジトリのオープンなイシューと PR をキーワード検索します(モジュール名、エラー文字列など)。
- 重複する項目が担当者あり/PR 作業中の場合 → 割り当てません。
Blocked-by: <repo>#<n>とコメントを追加し、完了後に残りの作業を再確認します。 - 重複する項目がオープンで担当者なしの場合 → まず一本化します。相互リンクを追加し、どちらかをもう一方のサブイシューにする(または一方を重複としてクローズする)ことで、ひとつの作業につき割り当て先がひとつになるようにしてから、通常どおりキューに登録します。
- 重複する項目がすでに完了している場合 → バックログのイシューが古くなっている可能性があります。残っている作業を確認し、何も残っていなければクローズを推奨します。
- 該当なしの場合 → 通常どおり割り当てます。
スケジュールを決定するのはバックログリポジトリだけです。ふたつのキューが同じ作業を割り当ててはいけません。
- 重複する項目が担当者あり/PR 作業中の場合 → 割り当てません。
3. バッチを選択する
batch 件までの、相互に独立した課題を選びます。同じバッチ内の2つの課題が、同じパッケージ、レジストリ、バレルファイル、またはスキーマに影響する可能性が少しでもあってはなりません。同じ共有ファイルを編集する2つの開発者エージェントはマージ競合を起こし、逐次処理するよりも多くのコストがかかります。判断に迷ったら、逐次処理してください。2つ目の課題は次のラウンドに回します。
小さく、仕様が明確な課題を優先します。受け入れ条件を1文で示せない課題は、ディスパッチではなく、エスカレーション(ステップ8)の候補です。バッチの独立性はリポジトリをまたいで確認します。Blocked-by で関連付けられている課題や、親課題を共有する課題は、同じバッチに含めてはいけません。
4. 担当する
コードを書く前に担当を確定することで、2つのエージェントが同じ課題に取り組むのを防ぎます。
GitHub アカウントをまたぐ場合、担当者欄ですでに「誰が」分かります。「担当者が自分ではない → 対応済み、絶対に触らない」が、アカウント間のプロトコルのすべてです。
1つのアカウント内(複数のセッションで同じ ID を共有している場合)では、担当者欄が示すのは「何らかのエージェントがこの課題を担当した」ということだけです。担当を示すコメントには ID が記載されます。選択した各課題について、ディスパッチする前に次の順序で実行します。
-
割り当て:課題を自分に割り当て、
pm:dispatchedを追加します。ステップ1以降に担当者が設定された課題はスキップし、バッチから外します。 -
担当コメント:次の固定形式で記載します。セッション ID とブランチ名を組み合わせて ID とします。後続のすべての成果物(worktree、push、PR)はブランチに紐づき、セッションの行によって、同じ課題から同じブランチ名を導き出した2つのセッションを区別できます。
Claim: PM loop round N Session:
session_<id>Branch:claude/issue-<n>-<slug>Worktree:<repo>-issue-<n> -
競合チェック:割り当ては冪等なので、2つのエージェントがどちらも「成功」することがあります。コメントを再度読み込み、異なるセッション ID またはブランチを記載した先行の担当コメントがあれば、あなたは負けています。そのコメントには一切触れず、辞退する旨を返信し、別の課題を選んでください。先に投稿されたコメントが優先です。
担当コメントを読み返してください。GitHub の本文サニタイザーは短い <…> の範囲をその場で削除し、バッククォートで囲んでも保護されません(実測:HTML コメントをコードスパンで囲んだものが完全に消え、本文の残りはそのままで、API は成功を報告しました)。上記の形式はプレースホルダーで構成されており、競合チェックと、後述する期限切れの担当の再取得では、その中の ID 行を参照します。
- 山括弧は HTML エンティティ(
</>)として記述するか、<の後にスペースを入れてください。コードスパンで囲んでも保護されません。 - 担当コメント、レポートマーカーを含むクラウドモードのディスパッチ用プロンプト、引き継ぎメモなど、内容が重要な本文を書いた後は、読み返して、それぞれの範囲が残っていることを確認してください。書き込み後に読み返す必要があるのは、読み取りに2回の確認が必要なのと同じ理由です。「API が 200 を返した」ことは、「保存された内容が正しい」ことを意味しません。
開発者エージェントは早い段階でブランチを push します。リモートブランチは作業中であることを示す最も確かな証拠であり、「担当済み」から「PR が存在する」までの空白を埋めます。
期限切れの担当の再取得。 約24時間以上前の担当で、約束されたブランチがリモートに存在せず、PR もない場合は、作業が放棄されたものとみなします。確認のコメントを投稿し、さらに一定期間応答がなければ、理由を記録したうえで担当者を解除し、課題をキューに戻します。コミットのある有効なブランチが存在する担当を、絶対に再取得してはいけません。
5. ディスパッチ
課題ごとに1つのエージェントを割り当て、バックグラウンドで並列実行します。各エージェントのプロンプトの先頭には、開発者エージェントの運用テンプレートを付けます。使用しているハーネスにカスタムエージェントタイプがあれば、それを使ってください。なければ、テンプレートを使う汎用エージェントでも同等です。契約となるのはテンプレートであり、エージェントタイプではありません。
すべてのプレースホルダーを埋め、要約ではなく課題本文の全文を貼り付けます。
Your task is issue {backlog_repo}#{n}. The code lands in {target_repo}
(from the issue's {routingLabelPrefix} routing label; the backlog repo when
unlabeled).
ISSUE TITLE: {title}
ISSUE BODY:
{body}
{on rework rounds only:}
PREVIOUS ATTEMPT REVIEW — fix all of these before returning:
{feedback}
--- OPERATING PROCEDURE (binding) ---
{the developer-agent operating template, verbatim}
--- END OPERATING PROCEDURE ---
Non-negotiables for this dispatch:
- Work in {target_repo}: branch claude/issue-{n}-{slug} off origin/{default_branch},
in a DEDICATED worktree of that repository.
- Deliver a DRAFT PR in {target_repo}.
ディスパッチのバックエンド
どちらのモードでも、レポートは 2 回配信されます。まず GitHub に投稿します。エージェントは、1 行目にプレーンテキストのマーカー dev-report が記載された issue に JSON をコメントとして投稿し(HTML コメントにはしません。サニタイザーによって削除されるためです)、それを読み返した後、最終メッセージとして返します。コメントが記録であり、最終メッセージは処理を早めるためのものです。メッセージの途中でエージェントが停止しても、情報は失われません。
mode:subagent(デフォルト)。 PM 自身のセッション内でサブエージェントを実行します。レポートは各エージェントの最終メッセージとしても返されます。こちらのモードを推奨します。よりシンプルです。
mode:cloud。 各 issue は独立したセッションとなり、それぞれ専用のコンテナと新しいクローンを持ち、PM セッションの存続期間から切り離されます。タスクで 1 つのコンテナを超えるリソースや実行時間が必要な場合、またはメンテナーから指定された場合に使います。ハーネスにセッションを開始するツールが必要です。それがない場合は、その旨を伝えて mode:subagent に切り替えてください。
独立したセッションから PM にメッセージを返すことはできないため、ディスパッチプロンプトは完全に単独で成立する必要があります(会話のコンテキストがゼロの状態から始まります)。issue のコメントに投稿されるレポートが唯一の連絡手段です。
6. 収集
サブエージェントモード: エージェントからの応答を待ちます。ポーリングはせず、保留中のエージェントの結果を決して捏造しないでください。エージェントが停止した場合、または不正な形式の出力を返した場合は、issue の dev-report コメントを読み取ります。コメントもない場合は、エージェントを status: "blocked" として扱い、未加工の出力を添付します。
クラウドモード: 直接応答を受け取る手段はありません。GitHub 経由で収集してください。約 15 分後に確認を設定します。確認のたびに、ディスパッチした issue を調べてレポートコメントとリンクされた PR を確認し、そのラウンドのすべてのディスパッチから報告が届くまで、または報告のないディスパッチが約 2 時間を超えるまで、何も通知せずに次の確認を設定します。報告のないディスパッチは blocked として処理し、先へ進みます。
レポートがないことを成功とみなしてはいけません。
確認の文面はすべて結論ではなく条件として書いてください。予定されたテキストが届くのは、あなたには見えない未来です。 今設定した確認は、コンテキストを失ったセッションによって、状況が変化した後に読み取られる可能性があります。また、キャンセルしたタイマーでも通知が届くことがあります。古いテキストを無害にするには、次の 2 つのルールに従ってください。
- すべての確認を「冪等 — 操作の前に状態を再確認する」で始め、再確認なしで実行できる命令は含めないでください。タイマーが発火した後、プラットフォーム側が前提を再確認してくれることはありません。文面に再確認を含めることだけが、古くなった指示を無効にする手段です。
- 結論(「今すぐ Y を実行する」)ではなく、条件(「X なら Y」)を示してください。 条件は届いた時点で改めて判断できますが、結論は、それに至った状況をすでに捨てています。「⇒ 再ディスパッチする」「⇒ 信頼性がないと判断する」のような形式は避けてください。記述した時点では正しくても、届いた時点では正しいとは限りません。キャンセルしたと考えているタイマーにも同じことが当てはまります。キャンセルしても、通知が届かない保証にはなりません。
無応答のしきい値は収集を打ち切る境界であり、エージェントが停止したという判定ではありません。 上記の約 2 時間は「このラウンドでは待つのをやめる」という意味であり、「エージェントは停止した」という意味ではありません。もう 1 ラウンド待つコストは 1 ラウンド分ですが、停止したと判断すると、まだ稼働中かもしれない作業ツリーに重複してディスパッチすることになります。ディスパッチしたエージェントが停止したと判断する前に、次のいずれかを確認してください。エージェントが停止していることを示す直接的なステータス照会、セッションが停止したというホストからの報告、または自身のプロジェクトで測定した完了時間のベースラインを超える無応答(同程度のカードを複数件使い、ディスパッチから最初のブランチまたは PR がプッシュされるまでの時間を測定します)。ベースライン内の無応答は、証拠にはなりません。
7. 各レポートをレビューする
あなたが記録上のレビュー担当者です。各レポートについて、レポート自体の主張では なく、GitHub を基準に確認してください。
- PR が存在し、ドラフトで、デフォルトブランチを対象としており、本文で
Fixes #{n}を参照していること。 - PR の変更ファイルを取得します。スコープを確認します。課題と明らかに無関係な 内容がなく、プロジェクトの規約ファイルで必須とされる成果物(リリースノート/ changeset エントリー、生成ファイルの再生成、移行に関する注記)がすべて 含まれていること。
- レポートのテスト証拠に、単に「テストに合格」とあるのではなく、実際の コマンドと合格した出力が示されていること。
- 差分内の拒否クラスのテスト、つまり不正な入力が拒否されることを確認する
テストでは、単に例外がスローされたことだけでなく、エラーの識別情報(
codeとstatus、またはプロジェクトのエラーエンベロープで定義されている フィールド)を検証していること。例外がスローされることだけを検証する アサーションは、すでに通常のエラーをスローするあらゆる処理で成功します。 これは修正前の処理でよくある挙動です。そのため、カバレッジがあるように 見えても、名前が示す不具合を検出できません。 - 差分が課題の受け入れ基準を妥当に満たしていること。
課題ごとの判定:
- ACCEPT — PR へのリンクと実装内容の要約を記載したコメントを課題に投稿します。 その後、プロジェクトのマージポリシーに従ってマージまで進めます。マージ キューがある場合は、すべてのチェックが成功したら PR をレビュー可能にし、 キューに追加します(キューは現在のデフォルトブランチを基準に再ビルドするため、 これが認められた手順です)。マージキューがない場合は、リモート CI が完全に 成功してから、かつプロジェクトが PM によるマージを許可している場合に限り、 1 件ずつマージします。
- REWORK — 具体的で項目別のフィードバックを提示し、フィードバック欄を埋めた 状態で同じ課題を再度割り当てます(主張は同じ、エージェントは新しくします)。 課題ごとの手戻りは最大 2 回です。3 回目の失敗はエスカレーションします。
- ESCALATE — ステップ 8 を参照してください。
8. 不確実な点はメンテナーにエスカレーションする
まずエスカレーションの基準を適用してください。決定が必要に思えることの多くは、実際にはそうではありません。 次のいずれかに当てはまる場合に限り、エスカレーションしてください。
- 選択肢によってプロダクトの意味や公開契約の形が実質的に異なり、課題、規約ファイル、プロジェクトの記録済みの決定事項(ADR)、既存のコード規約のいずれからも答えを導けない場合。
- 破壊的な操作、または取り消しが困難な操作が必要な場合。保存データの移行方法、リリース済み機能の削除、強制操作などです。
それ以外はすべて PM の判断です。決定して作業を割り当て、許可を待つのではなく、拒否権を行使できる猶予期間をメンテナーに設けてください。課題へのコメントとラウンドレポートに、決定内容と理由を記載します。メンテナーは停止できますが、その返答を待つ必要はありません。
エスカレーションしないケースの例 — すぐに対応してください。
- 不変条件を回復する修正。 プロジェクトがすでに不変条件(関連する一連の機能で契約バージョンが統一されている、宣言内容と実際の強制内容が一致している、ゲートがチェックすると主張する内容を実際に実行する)を定めている場合、その不変条件が破られているという発見には、それ自体が決定を含んでいます。「不変条件を回復してもよいですか?」と尋ねるのはアンチパターンです。
- 技術タスク間の順序付けと依存関係の整理。
- 検証戦略 — リスクはあるものの方針が決定済みの変更に、どの回帰テストを実施する必要があるか。これは作業範囲の設定であり、決定ではありません。
- 確認した結果、上記のケースに該当する開発者エージェントの
needs_decision。決定内容と理由を示して、自分で回答してください。上位に取り次がないでください。
エスカレーションの基準を満たす場合:
- カードの前提を確認し直してから作成し、再エスカレーションする前にも再確認してください。 決定カードに記載する前提(進行中の変更がまだマージされていない、機能がまだ存在しない、ファイルが依然としてこの形であるなど)は、すべて有効期限のある読み取り情報です。活発なメインブランチには、1 日におよそ 12 件以上のマージが入り、リポジトリ間の事実は時間単位で変わります。カードを投稿する直前、さらに古いカードを再びメンテナーの前に出す直前に、すべての前提を確認し直し、成立しなくなったものは書き換えるか取り下げてください。一晩放置されたカードは、単に返答待ちなのではなく、前提が未検証のカードです。 前提が失効していることは、カードがないことよりも悪い状態です。メンテナーはもはや存在しない状況について判断することになり、カードにはそのことを示すものが何もありません。
- 決定は、それが属する課題上に記録してください。新しい課題は作成しないでください。 分析をその課題にコメントとして投稿し、
needs-user-decisionを追加して、課題をアクティブキューから外します。このラベルがメンテナーの受信トレイになります(label:needs-user-decisionでフィルター)。メンテナーが回答したらラベルを外し、課題をキューに戻します。決定に自然な紐付け先がない場合(複数の課題にまたがる場合、または課題がない状態で発生した場合)に限り、別の課題を作成してください(タイトルは[Decision] <決定が必要な内容を一行で記述>、ラベルは同じもの)。 - 分析には、背景、具体的な質問、選択肢、推奨案、および関連する課題/PR/ブランチを記載してください。さらに、以下に定める4つの軸すべてについて、各選択肢を分析してください。
- セッションが対話型の場合は、これに加えてメンテナーに直接質問してください。ラベル付きの課題は、いずれにしても永続的な記録として残します。プロダクトやアーキテクチャに関する質問に、メンテナーの代わりに答えてはいけません。
4 つの軸による判断フレーム(必須)
エスカレーションの各選択肢は、4 つすべての軸で分析します。このフレームはエスカレーションの中核であり、飾りではありません。
軸 ① — 実際のビジネスニーズ。 この選択肢は、実際に存在するビジネスシナリオに役立つものですか。それとも、実現するかどうかも定かでない機能のためのものですか。アーキテクチャの議論に入る前に、まずすべての選択肢について問いかけてください。答えを出すのではなく、問い自体を不要にできる場合があるためです。根拠は推測ではなく、測定されたものでなければなりません。誰がこのキーを書き込むのか、誰がこの機能を読み取るのか、プロジェクトのサンプルアプリや実際のデプロイで現在どのように使われているのかを確認します。「役に立ちそうに思える」では根拠になりません。この軸は判断を飾るのではなく、判断を変えます。技術的な形が同じ 2 つの指摘でも、この軸だけで正反対の結論になり得ます。利用の裏付けがない宣言済みサーフェスは廃止し、実際のアプリで方向性が実証されたものは残して、明示的にエラーにすることができます。他の軸では同じ判断になるとしても、それでは誤った答えです。
軸 ② — このプロジェクトにとっての長期的なアーキテクチャの健全性。 どの選択肢が、今日もっとも安く済むかではなく、ワークアラウンドを使わず、コントラクトを最優先にする持続可能なアーキテクチャとプロジェクトの進む方向に合っていますか。パッチ的な選択肢の長期的なコストは、必ず明示してください。「ここで特例扱いできます」という案が妥当なのは、将来それを取り除くコストを明記している場合に限ります。
軸 ③ — AI が作成したコードを、構造的に誤りにくくすること。特に、AI が作成した ObjectStack のメタデータが対象です。作成時点で誤りを防げる選択肢、つまり厳格なスキーマや、公開時に明示的なエラーで拒否する検証など、宣言した内容を確実に適用するものを優先してください。コンシューマー側で寛容に処理する方法(?? によるフォールバック、別名の受け入れ、暗黙の型変換)よりも優先します。AI が生成した誤りが隠れて増殖するのは、まさに寛容なコンシューマーです。1 つの寛容な読み取り側が、誤ったメタデータを一世代分まるごと「動作する」状態にしてしまい、動かなくなるまで気づかれません。エージェントがランタイムで適用されない機能を宣言することは、決して許可しないでください。
軸 ④ — スタートアップ時のスコープ規律。 コアがまだ形成途上である間は、宣言するサーフェスを拡大しないでください。「すでにリリース済み」は理由になりません。リリース済みでも利用されていない機能に、サンクコストの免除はありません。利用の裏付けがない宣言済みサーフェスには、実装より先に対処します。宣言と適用を一致させるため、宣言の範囲を狭めてください(廃止するか、用語だけを保留し、実装とともに再導入します)。誰も求めていない宣言を正当化するために実装を構築してはいけません。この既定の判断をどれほど厳しくするかは、このスキルではなく、プロジェクトで決めてください。規約ファイルに機能拡張の方針を明記します。コアのサーフェスが形成途上なら厳格に、安定した後はより柔軟にします。この軸は、他のプロジェクト固有のルールと同様に、そこに記載された方針を参照します。
推奨案は4 つすべての軸に基づいて正当化してください。軸同士が対立する場合は、トレードオフを正直に示し、メンテナーに判断を委ねてください。判断が分かれた場合は、推奨順位を実際の利用の裏付けを優先する形にします。実測された利用の裏付けがある場合は、長期的に望ましい形を優先します。利用の裏付けがゼロの場合は、増殖させないことを優先します(延期または削除し、記録を残し、いつでも再検討できるようにします)。AI の安全性に関する軸は、なお判断が分かれる場合に、明示的にエラーにする選択肢を優先するために使います。セキュリティと破壊的操作の判断は、人間に委ねます。この順序が定めるのは推奨の順位だけです。判断が分かれた場合は、引き続きエスカレーションが必要です。
軸 ② の重みが最も大きく、少なくとも 50% とします。
長期的な観点から判断を始めてください。軸同士が対立する場合、他の軸すべてを合わせても、軸 ② の判断を覆すことはできません。この重みは、特例やコントラクトの継ぎ足しを増やすのではなく、減らすものとして解釈してください。そのため、根拠のない拡張を正当化することは決してありません。この重みが定めるのは推奨の順位だけであり、決定権ではありません。
9. ラウンドの報告と次のラウンド
ラウンドの報告をメンテナーに提示します。指摘 → 判断 → PR リンク → 注記をまとめた表に加え、エスカレーションした事項と、拒否権の行使期間中に決定した事項を記載してください。その後、ステップ 1 から次のラウンドを開始します。再作業の再ディスパッチも、次のラウンドの batch 予算に含めます。
停止条件
次のいずれかに該当したらループを停止して報告します。
- キューが空になった、または
roundsを使い切った場合。 - 1 ラウンドのディスパッチの半数以上が失敗またはエスカレーションされた場合 — これはシステム上の問題(キューの管理不備、デフォルトブランチの破損、ツールの誤り)であり、その状態で残りのバックログに取り組むと、残りのディスパッチがすべて無駄になります。
- メンテナーが中断した場合。
開発者エージェントの運用テンプレート
rules/dev-template.mdをそのまま各ディスパッチプロンプトに貼り付けます。PMが{…}のプレースホルダーを埋めます。{decision_frame}には、以下の停止トリガーに続けて、上記の#### The four-axis decision frame (binding)セクション全体を含めます。このフレームはこのスキル内に1つだけあり、ディスパッチ時にPMが貼り付けるため、開発者側のコピーとの食い違いは生じません。
When to STOP instead of coding. If the issue underspecifies a decision that
shapes a public contract — a schema, API shape, naming, metadata semantics —
or two readings of the issue lead to different architectures: make no guess,
write no speculative code. Return status "needs_decision" with each question,
the options, their costs, and your recommendation in open_questions. A wrong
guess shipped is far more expensive than a round-trip to the maintainer.
報告に関する契約。 テンプレートの最終メッセージブロックにあるJSONが契約全体です。statusがneeds_decisionの場合、open_questionsは空であってはならず、各項目はエスカレーション分析への入力となります。out_of_scope_findingsは、開発者エージェントがすでに担当者なしのイシューとして登録している必要があります。PMは登録されていることだけを確認します。premise_still_valid: falseは、エージェントによる検証でイシューが誤りと判明したことを意味します。根拠はsummaryに記載し、prはnullにするか、残存する範囲に限定します。PRをレビューするのではなく、カードを再トリアージしてください。
上流への報告 — アプリの構築中に見つかったプラットフォームの不具合
方針はobjectstack-platformの**「アプリとプラットフォームの境界」**に記載されています。修正は上流で行い、アプリ内で回避策を講じてはいけません。このループでは、次の作業を追加します。
- 問題のある依存関係から上流リポジトリを特定する:依存関係の
package.jsonにあるrepositoryフィールドを確認し、現在のデフォルトブランチにも不具合が残っていることを確認します。すでに修正済みの場合、アプリ側のタスクはアップグレードです。 - 自分のアプリがなくても対処できる形で報告する:単独で実行できる最小限の再現手順、正確に固定したバージョン、期待される動作と実際の動作、根拠として引用する契約を記載します。
- スケジューラーではなくゲストとして登録する:
Part of <app-owner>/<app-repo>#<n>のバックリンクを付けます。上流側のキューラベルは適用せず、担当者を割り当てず、上流側のボードにも追加しないでください。 - アプリ側のイシューは正直に保留する:
Blocked-by: <upstream>#<n>を記載するか、ブロック解除条件を明記してバージョンを固定します。イシューはオープンのままにしてください。「回避策を講じた」は決して解決にはなりません。
リソース管理(PM 側)
メモリ使用量がピークに達するのは編集時ではなく、ビルドとテスト時です。そのため、並列性を下げるのではなく、負荷の高いフェーズを直列化します。開発者エージェントのテンプレートでは、コンテナー全体を対象とする検証ロック、ヒープ上限、対象を絞ったビルド、ワークツリーのクリーンアップを必須としています。
PM 側では、設定済みのbatchは通常規模のタスクを想定したものとして扱います。ビルド負荷の高いタスク(依存関係ファミリーのアップグレード、完全な回帰テスト)ではbatch:2に減らすか、そのイシューをmode:cloudでディスパッチして専用コンテナーで実行します。エージェントがメモリ不足を示すシグネチャで終了した場合は、フルバッチに含めず、単独で再ディスパッチしてください。
ガードレール(遵守必須)
- PM はファイルを書き込みません。マージが許可されるのは、レビュー済みで すべてのチェックが成功した開発エージェントの PR のみで、プロジェクト公認の 手順に従う必要があります。PM 自身の PR や、チェックに失敗した PR、未レビューの PR はマージできません。
- 強制プッシュは禁止です。デフォルトブランチへのプッシュ、他の人が担当として
確保した Issue の担当者変更、
needs-user-decisionIssue のディスパッチも 禁止です。 - 各開発エージェントは、リポジトリごとに独自のワークツリーで作業します。
- 並列実行数は
batchを上限とし、同じバッチ内のエージェントは、仕組みによって 担当ファイルが重複しないようにします。 - バックログリポジトリを唯一のスケジューリング権限元とします。進行中の バッチを確認できないリポジトリには、ディスパッチしないでください。
- キューごとに PM は 1 人です。
batchとmode:cloudで規模を拡大し、 1 つのキューに 2 人目のスケジューラーを置いてはいけません。2 人目の PM は、 別のリポジトリ全体を自身の担当範囲とし、すべての担当確保コメントにその旨を 記載したうえで、担当範囲外の Issue を確保してはいけません。担当範囲をまたぐ 作業は Issue として扱います。対象リポジトリにキューラベル付きのカードを作成し、Part of <owner/repo>#<n>の行と、待機側にBlocked-by:を記載します。 後続の作業項目は、記憶に頼らず、作業を引き継ぐリポジトリに登録します。 - このルールとプロジェクトの規約ファイルが矛盾する場合は、規約ファイルを 優先します。
このループをプロジェクトに適用する
このスキルには、プロジェクトごとのゲートを意図的に含めていません。これらを
規約ファイル(AGENTS.md / CLAUDE.md / …)に記載すれば、このループが各
ディスパッチに反映します。
| ループが把握する必要のある情報 | 情報の参照先 |
|---|---|
| ブランチの命名規則、デフォルトブランチ、PR の言語 | 規約ファイル |
| 必須のリリースノート成果物(changeset、CHANGELOG エントリ、なし) | 規約ファイル |
| コード PR が変更してはならない、リリースプロセスが所有するファイル | 規約ファイル |
| パッケージごとのテスト/型チェック/Lint コマンド | 規約ファイル |
| 共有の高負荷検証ロックの取得方法(ラッパー、ロックパス、取得予算) | 規約ファイル |
| マージポリシー(マージキュー、順次マージ、メンテナーのみ) | 規約ファイル |
| 能力拡張に関する方針の軸④が参照する内容(デフォルトで厳格、または寛容) | 規約ファイル |
| 存在するリポジトリとバックログの所在 | .claude/pm-dispatch.json |
| エスカレーション基準が従う、記録済みのアーキテクチャ上の決定 | プロジェクトの ADR ディレクトリ |
プロジェクトのルールが開発エージェントから見えずにディスパッチが失敗した場合、 修正するのは規約ファイルです。ディスパッチプロンプトを長くしてはいけません。