forge-queue: 自律タスクキュー設計
ステータス: draft v3 (2026-04-17)
概要
forge-queue はイシューベースのタスクを逐次バッチ実行する機能を提供します。 ユーザーはイシュー URL のリストを含む .specs/queue.yaml を作成し、MCP サーバーがキューの状態を管理しながら、既存の forge パイプラインが各タスクを処理します。
アーキテクチャ
SKILL.md (forge-queue)
│
│ queue_init(queue_path) ← parse + validate YAML
│ │
│ ▼
│ queue_next(queue_path) ← return next task + pre-generated workspace slug
│ │
│ ▼
│ claude -p "/forge {url} --auto" ← isolated subprocess, forge unchanged
│ │ SKILL.md passes workspace_slug in user_confirmation
│ ▼
│ queue_report(queue_path, index) ← find workspace by slug, read state.json
│ │
│ ▼
│ queue_next(queue_path) ← next task, or "all done"
│ │
│ ...4 つの新しい MCP ツールはシンプルな YAML I/O ラッパーです。すべてのパイプラインロジックは既存の pipeline_init / pipeline_next_action / pipeline_report_result チェーンに残ります。
スキル
責務が明確に分離された 2 つのスキル:
/forge-queue-create—.specs/queue.yamlを生成する/forge-queue—.specs/queue.yamlを実行する
/forge-queue-create
queue.yaml ファイルを生成します。2 つの入力モードをサポートします:
モード A: URL 直接指定
/forge-queue-create https://jira.example.com/browse/DEA-123 https://jira.example.com/browse/DEA-456スキルは queue_create MCP ツールで各 URL をバリデートし、ユーザーにエフォートレベルを確認(またはデフォルトを受け入れ)して YAML ファイルを書き込みます。
モード B: 検索ベースの収集
/forge-queue-create --jira-project DEA --jira-status "To Do"
/forge-queue-create --gh-label "bug" --gh-state "open"スキルは既存ツールを使ってイシューを検索します:
- Jira: Atlassian MCP ツール(利用可能な場合)または Jira REST API(forge の Jira 連携と同パターンで
curlを使用) - GitHub:
gh issue list --label <label> --state <state> --json url,title
スキルは一致するイシューを収集してユーザーに確認(選択/解除)を求め、タスクごとのエフォートを確認(またはデフォルト)してから queue_create を呼び出して YAML ファイルを書き込みます。
この分割の理由: モード A は決定論的(URL → YAML)であり、MCP ツールで完全に処理できます。モード B は外部 API 呼び出しとユーザー操作(イシュー選択)が必要であり、これはスキルレベルの関心事です — MCP ツールは Jira/GitHub への API 呼び出しやユーザーとのやり取りを行うべきではありません。
/forge-queue
キューを実行します。以下のスキル設計を参照してください。
MCP ツール
queue_create
目的: URL リストから新しい .specs/queue.yaml を生成する。
パラメーター:
queue_path(string, 必須): キュー YAML ファイルの出力パス。tasks(array, 必須): タスクオブジェクトのリスト。各オブジェクトは以下を含む:url(string, 必須): イシュー URL。effort(string, 任意):S、M、またはL。省略した場合、forge は推奨エフォートを自動選択する(--autoの動作)。
動作:
- 各エントリをバリデート:
urlが既知のソースタイプ(GitHub イシュー、Jira イシュー)と一致するか確認。effortが存在する場合、S、M、Lのいずれかであることを確認。
- ファイルが既に存在する場合、エラーを返す(誤った上書きを防止)。ユーザーは先に既存ファイルを削除またはリネームする必要があります。
- YAML ファイルを書き込む。
返り値:
{
"created": true,
"path": ".specs/queue.yaml",
"task_count": 3,
"errors": []
}queue_init
目的: .specs/queue.yaml を解析してバリデートする。
パラメーター:
queue_path(string, 必須): キュー YAML ファイルへのパス。
動作:
- YAML ファイルを読み込んで解析する。
- 全エントリをバリデート:
urlが存在し、空でなく、既知のソースタイプ(GitHub イシュー、Jira イシュー)と一致するか確認。effortが存在する場合、S、M、Lのいずれかであることを確認。
- サマリーを返す: 合計数、完了数、失敗数、保留数。
返り値:
{
"total": 4,
"completed": 1,
"failed": 0,
"pending": 3,
"errors": []
}errors が空でない場合、キューは無効であり処理すべきではありません。
queue_next
目的: キューから次の未処理タスクを返し、ワークスペーススラグを事前生成してワークスペースパスを決定論的にする。
パラメーター:
queue_path(string, 必須): キュー YAML ファイルへのパス。
動作:
- YAML ファイルを読み込んで解析する。
statusが存在しないまたはstatusがin_progress(割り込み後の再開)である最初のエントリを見つける。- 新規タスクの場合:
- URL からワークスペーススラグを事前生成する。スラグはイシュー識別子から導出される:
- Jira:
dea-123(https://jira.example.com/browse/DEA-123から) - GitHub:
42(https://github.com/org/repo/issues/42から) スラグは URL のみから導出された安定した決定論的な値です。
- Jira:
- queue.yaml に
status: in_progress、started_at: <ISO8601>、workspace_slug: <生成スラグ>を書き込む。in_progressエントリの場合: 変更なし(冪等 —started_atとworkspace_slugは前回の試行から保持される)。
- URL からワークスペーススラグを事前生成する。スラグはイシュー識別子から導出される:
workspace_slugを含むタスク詳細を返す。
ワークスペーススラグが forge に届く仕組み: forge サブプロセスの SKILL.md は pipeline_init_with_context への user_confirmation オブジェクトで workspace_slug を渡します。これは既存の機能です — pipeline_init_with_context はすでに user_confirmation で workspace_slug を受け入れ、applyWorkspaceSlug(pipeline_init_with_context.go の 277-283 行目)で適用します。forge のコード変更は不要です。
実際のワークスペースパスは forge が決定します: YYYYMMDD-{source_id}-{workspace_slug} または YYYYMMDD-{source_id}-{issue_title_slug}(外部コンテキストでスラグが精緻化された場合)。queue_report は日付 + source_id プレフィックスで .specs/ をスキャンしてワークスペースを特定します。
再開セマンティクス: in_progress エントリは前のセッションがパイプライン途中で割り込まれたことを意味します。queue_next はそれを次のタスクとして返し、forge パイプラインの既存の再開ロジック(pipeline_init の自動再開)が回復を処理します。特別なキューレベルの再試行ロジックは不要です。
返り値(エフォートありの新規タスク):
{
"has_next": true,
"index": 2,
"resuming": false,
"url": "https://github.com/org/repo/issues/42",
"effort": "S",
"workspace_slug": "42",
"forge_arguments": "https://github.com/org/repo/issues/42 --auto effort:S"
}返り値(エフォートなしの新規タスク):
{
"has_next": true,
"index": 3,
"resuming": false,
"url": "https://jira.example.com/browse/DEA-789",
"effort": null,
"workspace_slug": "dea-789",
"forge_arguments": "https://jira.example.com/browse/DEA-789 --auto"
}forge_arguments は pipeline_init(arguments=...) に直接渡せる事前構築済み文字列です。--auto フラグは常に含まれます。effort がない場合、effort: フラグは省略されます — forge の pipeline_init_with_context は --auto モードで推奨エフォートを自動選択します。
forge_arguments にワークスペーススラグは含まれません。スラグは別途渡されます — forge-queue の SKILL.md はそれを claude -p プロンプトに埋め込み、サブプロセスの forge SKILL.md が user_confirmation に含めます。
返り値(割り込まれたタスクの再開):
{
"has_next": true,
"index": 1,
"resuming": true,
"url": "https://jira.example.com/browse/DEA-456",
"effort": "S",
"workspace_slug": "dea-456",
"workspace": ".specs/20260417-dea-456-add-export-feature",
"forge_arguments": ".specs/20260417-dea-456-add-export-feature"
}resuming が true の場合、forge_arguments には URL の代わりにワークスペースパスが含まれます。forge の pipeline_init はこれを再開候補として検出し、自動再開で処理します。workspace フィールドは queue.yaml から読み込まれます(最初の試行後に queue_report が書き込む)。
返り値(タスクなし):
{
"has_next": false,
"summary": {
"total": 4,
"completed": 3,
"failed": 1,
"results": [
{"url": "...", "status": "completed", "pr": 2891},
{"url": "...", "status": "failed", "reason": "..."}
]
}
}queue_report
目的: 完了したタスクの結果を決定し、queue.yaml に記録する。呼び出し元がパイプラインの結果を解釈する必要はなく、このツールが直接 state.json を読み込んで結果を決定する(決定論的、LLM の判断不要)。
パラメーター:
queue_path(string, 必須): キュー YAML ファイルへのパス。index(number, 必須):queue_nextが返したタスクインデックス。workspace(文字列、オプション): スキルが forge のpipeline_init_with_contextレスポンスから渡すワークスペースディレクトリパス。指定された場合、.specs/スキャンを 完全にスキップします。
動作:
queue.yamlを読み込み、indexのエントリを見つける。- エントリから
workspace_slugを読み込む(queue_nextが書き込んだもの)。 .specs/でワークスペースディレクトリを特定する:- オプションの
workspaceパラメータが指定されている場合(スキルが forge のpipeline_init_with_contextレスポンスからパスをキャプチャして渡す): そのパスを直接使用する。スキャンは不要。 - フォールバック(クラッシュリカバリ — スキルが workspace を渡す前に中断された場合):
started_atから日付プレフィックス(例:20260417)と queue.yaml エントリからworkspace_slugを抽出し、.specs/で{date_prefix}-{workspace_slug}*を スキャンする。スラッグはパイプライン開始前に事前生成され queue.yaml に書き込まれるため、 このスキャンは決定論的です。 - 一致が見つからない場合: タスクを
failed(理由:"workspace not found")としてマークする。
- オプションの
{workspace}/state.jsonを読み込む。- 結果を決定論的に決定する:
currentPhase == "completed"→status: completed。- その他のフェーズ →
status: failed。 理由:state.jsonから"{currentPhase}: {error.message}"。state.Errorが nil の場合(エラーなしで放棄されたパイプラインなど)、理由は"{currentPhase}: abandoned"。
- ブランチ名のために
state.json.branchを読み込む。 - queue.yaml エントリを更新する:
status: completed または failedworkspace: 実際のディレクトリ名(例:20260417-dea-123-fix-login)branch: git ブランチ名(例:feature/20260417-dea-123-fix-login)reason: 失敗理由(failed のみ)finished_at: ISO8601 タイムスタンプ
queue.yamlをアトミックに書き込む。
返り値:
{
"status": "completed",
"branch": "feature/20260417-dea-123-fix-login-validation",
"workspace": "20260417-dea-123-fix-login-validation",
"remaining": 2
}queue_update_pr
目的: queue.yaml エントリに PR 番号を書き込む。gh pr list で PR を検索した後にスキルが呼び出す。
パラメーター:
queue_path(string, 必須): キュー YAML ファイルへのパス。index(number, 必須): タスクインデックス。pr(number, 必須): PR 番号。
動作:
queue.yamlを読み込み、indexのエントリを見つける。- エントリに
pr: <number>を書き込む。 queue.yamlをアトミックに書き込む。
返り値:
{
"updated": true
}設計の根拠: PR 番号の検索には gh pr list(シェルコマンド)が必要ですが、これは MCP ツール内で実行してはなりません(制約 #12)。スキルがシェルコマンドを実行し、結果をこのツールに渡してアトミックな YAML 書き込みを行います。これにより MCP ツールをピュアな Go で保ちつつ、queue.yaml の書き込みが常にアトミックであることを保証します(制約 #6)。
スキル設計
forge-queue は forge の内部を何も知らない独立したスキル(/forge-queue)です。各タスクは隔離された claude -p サブプロセスで実行され、タスクごとにクリーンなコンテキストウィンドウを確保してタスク間の汚染ゼロを実現します。
## Step 1: Initialize
1. Call `queue_init(queue_path=".specs/queue.yaml")`.
2. If errors: report and stop.
3. Report queue status (e.g. "4 tasks: 1 completed, 1 failed, 2 pending").
## Step 2: Process Loop
1. Call `queue_next(queue_path=".specs/queue.yaml")`.
2. If `has_next` is false: output summary and stop.
3. If NOT resuming (`resuming` is false):
Run `git checkout main && git pull --rebase`.
4. Run forge as a subprocess via Bash:
`claude -p "/forge {forge_arguments}" --allowedTools "Bash,Read,Write,Edit,Glob,Grep,Agent,Skill,mcp__plugin_claude-forge_forge-state__*"`
- For new tasks, append to the prompt:
"Use workspace_slug '{workspace_slug}' in user_confirmation."
- Each subprocess starts a fresh session with an empty context window.
- forge runs autonomously (--auto) and exits on completion or failure.
5. Call `queue_report(queue_path=".specs/queue.yaml", index=<index>)`.
6. If `status == "completed"` and `branch` is present:
a. Run `gh pr list --head {branch} --json number --jq '.[0].number'`
b. If PR number is found:
Call `queue_update_pr(queue_path, index, pr=<number>)`.
7. Return to step 1.サブプロセス隔離の理由
- コンテキスト分離: 各タスクはクリーンなコンテキストウィンドウを得る。前のタスクのコード、エラー、設計上の決定が次のタスクに漏れない。
- /clear 不要:
/clearは CLI のみのインタラクティブコマンドであり、プログラム的に呼び出すことができない。claude -pはタスクごとに新しいセッションを開始することで同じ効果を実現する。 - forge 変更なし: forge の観点から見ると、各サブプロセスの呼び出しは、ユーザーが新しいターミナルで
/forge {url} --autoと入力するのと同一です。
サブプロセスでの MCP サーバーの利用可能性(確認済み)
claude -p サブプロセスは、claude-forge がプラグインとしてインストールされているリポジトリのルートから実行された場合、forge-state MCP サーバーへの完全なアクセスを持ちます。確認済み: すべての 46 個の mcp__plugin_claude-forge_forge-state__* ツールが claude -p セッションで利用可能です(2026-04-17 にテスト済み)。
認証(gh CLI、Jira 認証情報)は親シェル環境から継承されます。
ワークスペーススラグのフロー
ワークスペーススラグは forge を変更することなくシステムを通じて流れます:
queue_next queue.yaml subprocess (forge)
│ │ │
│ pre-generate slug │ │
│ from URL (e.g. "dea-123") │ │
│──write workspace_slug───────▶│ │
│ │ │
│ return workspace_slug │ │
│◀──────────────────────────────│ │
│ │ │
│ embed slug in claude -p │ │
│ prompt instruction │ │
│──────────────────────────────────────────────────▶ │
│ │ forge SKILL.md │
│ │ passes slug in │
│ │ user_confirmation│
│ │ .workspace_slug │
│ │ │ │
│ │ ▼ │
│ │ pipeline_init_ │
│ │ with_context │
│ │ applies slug │
│ │ (existing code │
│ │ L277-283) │
│ │ │ │
│ │ ▼ │
│ │ workspace created│
│ │ .specs/20260417- │
│ │ dea-123-fix-login│
│ │ │
queue_report queue.yaml
│ │
│ read workspace_slug │
│◀──────────────────────────────│
│ │
│ scan .specs/ for │
│ {date}-{source_id}* │
│ → finds 20260417-dea-123-... │
│ │
│ read state.json │
│ determine status │
│──write workspace, branch─────▶│再開の動作
ユーザーがキューの実行を割り込んだ場合(Ctrl+C、ターミナルを閉じるなど):
- 現在のタスクの
statusはqueue.yamlでin_progressのまま残り、workspace_slugとstarted_atはすでに記録されています。 - 完了したタスクはすでに
completedまたはfailedです。 - 残りのタスクは
statusを持ちません。
再開するには、ユーザーが再び /forge-queue .specs/queue.yaml を実行するだけです:
queue_initが現在の状態を報告する(N completed、M failed、1 in progress、K pending)。queue_nextが既存のworkspace_slugを持つin_progressタスクを返す。workspaceが設定されている場合(最初の部分実行後にqueue_reportが書き込んだ場合):forge_argumentsにはワークスペースパスが含まれ、pipeline_initを通じて forge の自動再開をトリガーします。workspaceがまだ設定されていない場合(queue_reportが実行される前に割り込まれた場合):forge_argumentsには URL が含まれます。forge は新しいワークスペースを作成します。ワークスペーススラグにより同じスラグが使用されますが、pipeline_initは若干異なるワークスペース名を生成する可能性があります(日付が異なる場合があります)。queue_reportは日付プレフィックスのスキャンによってこれを処理します。- 再開したタスクが完了した後、
queue_reportが結果を記録し、ループは次の保留タスクで続きます。
関心の分離
forge-queue が知らないこと:
- forge のメインループの仕組み(
pipeline_next_actionのディスパッチ) - アクションタイプの種類(
spawn_agent、checkpoint、execなど) - フェーズ、リビジョン、レビューの仕組み
- PR 作成やソースへの投稿の仕組み
forge-queue が知っていること:
- YAML キューの読み込み/バリデーション方法(
queue_init) - 次のタスクを選択してスラグを生成する方法(
queue_next) forge_argumentsを使ってclaude -pサブプロセスを起動する方法- 特定の
workspace_slugを使用するようサブプロセスに指示する方法 - 結果を記録する方法(
queue_report) gh pr listを使って PR 番号を検索する方法(シェルコマンド)- PR 番号をアトミックに書き戻す方法(
queue_update_pr) - タスク間でメインブランチに戻る方法
queue.yaml スキーマ
tasks:
- url: https://jira.example.com/browse/DEA-123 # required
effort: M # optional: S | M | L (auto-selected if omitted)
# — fields below are managed by forge-queue —
status: completed # completed | failed | in_progress
workspace_slug: dea-123 # pre-generated slug (set by queue_next)
workspace: 20260417-dea-123-fix-login # actual .specs/ directory name (set by queue_report)
branch: feature/20260417-dea-123-fix-login # git branch name (set by queue_report)
pr: 2891 # PR number (set by skill via queue_update_pr)
reason: "phase-3: design rejected" # failure reason (set by queue_report)
started_at: "2026-04-17T10:30:00Z" # ISO8601 (set by queue_next)
finished_at: "2026-04-17T10:45:00Z" # ISO8601 (set by queue_report)設計上の制約
- 逐次のみ — 並列実行なし。ユーザーは並列処理のために複数のターミナルを開く。
--auto強制 — チェックポイントなし。各タスクは自律的に実行される。- リンクのみの入力 — タスクはイシュー URL(Jira、GitHub)でなければならない。フリーテキストのタスクはキューモードではサポートされない。
- forge の内部変更なし — 5 つのキューツールは追加的なものです。
pipeline_init、pipeline_next_action、pipeline_report_resultは変更されない。ワークスペーススラグは既存のuser_confirmation.workspace_slugフィールドを通じて伝達される(すでにpipeline_init_with_contextでサポート済み)。 - キューの状態は queue.yaml に保存 — YAML ファイルは入力と状態トラッカーの両方を兼ねる。別の状態ファイルはない。
- アトミックな書き込み — すべての queue.yaml の変更は MCP ツール(
queue_next、queue_report、queue_update_pr)を通じて行われる。スキルは直接 queue.yaml を書き込まない。 - ブランチ隔離 — 各タスクは独自のブランチを持つ。スキルはタスク間で
git checkout main && git pull --rebaseを実行する。 - フェイルフォワード — 失敗したタスクは放棄され、次のタスクが開始される。
- 決定論的な結果決定 —
queue_reportは直接 state.json を読み込む。SKILL.md はパイプラインの結果を解釈しない。 - 再開可能 —
queue_nextはin_progressエントリを候補として扱い、割り込まれたセッションからの回復を可能にする。 - セッション隔離 — 各タスクは別の
claude -pサブプロセスで実行される。コンテキストウィンドウはタスクごとにクリーン。タスク間の汚染なし。 - MCP ツールはピュアな Go — MCP ツール内で外部コマンド(
gh、curlなど)のos/exec呼び出しなし。シェルコマンドはスキルレイヤーのみで実行される。 - ワークスペーススラグはサブプロセスより先に判明 —
queue_nextがスラグを事前生成して queue.yaml に書き込む。サブプロセスは既存のuser_confirmation.workspace_slugメカニズムを通じてそれを forge に渡す。スキルはpipeline_init_with_contextレスポンスからワークスペースパスをキャプチャし、オプションのworkspaceパラメータとしてqueue_reportに渡す。フォールバックのクラッシュリカバリスキャンは{date_prefix}-{workspace_slug}*を使用する。
Go パッケージの配置
mcp-server/internal/queue/ ← YAML parse/validate/read/write + workspace scan
mcp-server/internal/handler/tools/
queue_create.go ← MCP handler (generate queue.yaml)
queue_init.go ← MCP handler (validate existing queue.yaml)
queue_next.go ← MCP handler (pick next task + slug)
queue_report.go ← MCP handler (record result)
queue_update_pr.go ← MCP handler (write PR number)
skills/
forge-queue/SKILL.md ← queue executor skill
forge-queue-create/SKILL.md ← queue generator skill依存関係の方向
queue パッケージは state.ReadState(読み取り専用)をインポートして queue_report でパイプラインの結果を決定します。これは一方向の依存関係です:
tools → queue → state (ReadState only)これは既存のレイヤリングルール(tools → ... → state)に従います。逆方向の依存関係は導入されません。queue パッケージは engine/orchestrator または handler/tools をインポートしません。
URL バリデーションの再利用
queue_create と queue_init の両方がソースタイプ検出を使用して URL をバリデートします。レイヤリング違反を避けるため(queue は handler/validation をインポートできません — ハンドラーパッケージはロジックパッケージより上位に位置します)、URL バリデーションロジックは pkg/validation に抽出されます — queue と handler/tools の両方がインポートできる低レベルの共有パッケージです:
tools → queue → pkg/validation(URL バリデーション)
tools → handlers → pkg/validation(既存の validate_input)pkg/validation は internal/ パッケージをインポートしてはなりません(pkg/maputil と同じ制約)。
テスト戦略
Go ユニットテスト(mcp-server/internal/queue/)
- YAML 解析/書き込みのラウンドトリップ(フィールド順を保持)
- バリデーション: URL 欠如、無効なエフォート、無効な URL フォーマット、重複 URL
queue_nextの状態遷移: 未設定 → in_progress、in_progress → 冪等queue_nextで全タスク完了 →has_next: falsequeue_nextスラグ生成: Jira URL → 小文字キー、GitHub URL → イシュー番号- ワークスペーススキャン: 候補が 0、1、複数ある場合の日付 + source_id プレフィックスマッチング
- state.json からの
queue_reportステータス決定:currentPhase == "completed"→ completedcurrentPhase != "completed"、Errorあり → failed with messagecurrentPhase != "completed"、Errorなし → failed with "abandoned"
queue_reportワークスペーススラグの精緻化: 事前生成スラグ vs 実際のディレクトリ- アトミック書き込み: 書き込み後のファイルの整合性を確認
MCP ハンドラーテスト(mcp-server/internal/handler/tools/)
queue_create: URL をバリデート、既存ファイルを拒否、有効な YAML を書き込むqueue_init: 混合ステータスキューの正しいカウントを返すqueue_next: エフォートあり/なしで正しいforge_argumentsを返すqueue_next: Jira と GitHub URL で正しいworkspace_slugを返すqueue_report: state.json を読み込み、正しいステータスとブランチを書き込むqueue_update_pr: 正しいエントリに PR 番号を書き込む
統合テスト(手動)
- エンドツーエンド: キュー作成 →
/forge-queue実行 → queue.yaml が更新されたことを確認 - タスク途中で割り込み → 再開 →
in_progressタスクが取得されることを確認 user_confirmationのworkspace_slugが期待されるワークスペースパスを生成することを確認
ツール数への影響
現在: 46 ツール。変更後: 51 ツール(+5: queue_create、queue_init、queue_next、queue_report、queue_update_pr)。 CLAUDE.md、scripts/README.md、README.md のカウントを更新してください。