# SpawnSubTask

タスクを並列サブタスクに分解して実行する。各サブタスクは独立した worker（ジョブ）で動き、完了後に親タスクが結果を集約する。

> **デフォルト無効（opt-in）**。SpawnSubTask は Bash などと同じセンシティブ扱いのツールで、初期状態ではエージェントに提示されない。使うにはワークスペースの **設定 → ツール** タブで明示的に有効化する。有効化していない場合、SpawnSubTask は呼べず、分解は [`Delegate`](./delegate.md)（直列・インプロセス）で行う。
>
> タスク分解の既定手段は Delegate（直列）。research / general / brainstorming などのピースは Delegate でサブ調査を 1 件ずつこなす。SpawnSubTask は「壁時計時間の短縮のために独立テーマを本当に並列で走らせたい」ワークスペースだけが有効化する。

## 基本

```js
SpawnSubTask({
  title: "ローカル LLM 比較調査",
  instruction: "Ollama, vLLM, llama.cpp の最新性能ベンチマークを比較する。各ツールについて: 1) 直近6ヶ月の主要ベンチマーク, 2) ハードウェア要件, 3) 対応モデル一覧 を output/report.md にまとめる。",
  piece: "research"   // 任意。指定しないと自動分類
})
```

呼び出すと `subtasks/{index}/` にサブタスクのワークスペースが作られ、結果はそこに集約される。

## いつ使うか

まず Delegate（直列）で足りるかを検討する。SpawnSubTask は opt-in で有効化したうえで、**並列の別ジョブで壁時計時間を縮めたい**場合に限って使う。

### 並列分解が効果的なケース

- 2 つ以上の **独立したテーマ**（互いに参照しない）
- 各テーマが軽くなく、調査・処理に時間がかかる
- 分解後の各タスクが単独でも意味を持つ成果物になる
- 壁時計時間の短縮が GPU・実行枠の追加消費に見合う

例:
- 「3 つの製品比較レポート」→ 製品ごとに 3 サブタスク
- 「複数 PDF の OCR 処理」→ ファイルごとに分解
- 「複数 SNS の情報収集」→ プラットフォーム別に分解

### 分解しないほうがよいケース

- 単一テーマで論理的に連続する処理（A→B→C のように依存）
- サブタスクが極端に小さい（オーバーヘッドの方が大きい）
- 全体像を見ながら判断する必要がある作業（対話的タスク等）

## instruction の書き方

- **完結した依頼文**で書く（親タスクの文脈を持たないので、サブタスクは instruction だけで判断する）
- 期待する成果物（出力ファイル名・場所）を明示
- 必要な前提情報があれば文中に展開

❌ 「これと同じ調査を別キーワードでやって」
✅ 「キーワード『A』『B』『C』について、各々のメリット・デメリットを比較する独立した調査を行い、output/A-vs-B.md にまとめる」

## piece の指定

- 省略時: 親と同じ classifier ロジックで自動選択
- 明示する場合: `research`, `general`, `office-process` 等の piece 名を指定

## WaitSubTask（完了待ち）

`SpawnSubTask` でサブタスクを起動したあとは、**`WaitSubTask`（引数なし）を 1 回呼んで全サブタスクの完了を待つ**。

```js
SpawnSubTask({ title: "A の調査", instruction: "..." })
SpawnSubTask({ title: "B の調査", instruction: "..." })
SpawnSubTask({ title: "C の調査", instruction: "..." })
WaitSubTask()   // ここで全部の完了を待つ
// 再開後、subtasks/*/result.md を Read して集約 → complete
```

- `WaitSubTask` は **同期的にブロックしない**。呼ぶとジョブはいったん停止（park）して worker を解放し、全サブタスクが終わると **同じ movement のまま再開** する
- 再開時、各サブタスクの結果は `subtasks/{index}/result.md` に書き出されている。Read で読んで集約レポートを作る
- 起動した分の成果物は `subtasks/{index}/output/` 以下にも残る

### ワークフロー

1. `SpawnSubTask` を必要な数だけ呼ぶ（並列に起動される）
2. `WaitSubTask()` を 1 回呼ぶ
3. 再開後、`subtasks/*/result.md` を読んで集約する
4. `complete` で終了する

### auto-park（取りこぼし防止）

サブタスクがまだ走っている最中に `complete`（success）を呼んでも、エンジンが**偽の完了を返さず、自動で park して完了を待つ**。`WaitSubTask` を呼び忘れても結果を取りこぼさない安全網。とはいえ意図を明示するため、原則 `WaitSubTask` を明示的に呼ぶこと。

### orphan の自動キャンセル

親ジョブが**失敗・キャンセル・リトライ**したとき、まだ走っているサブタスクは**自動でキャンセル**される。親が消えたのに子だけ GPU を使い続ける「ゾンビサブタスク」は発生しない。

ただし **ASK（ユーザー回答待ち）で停止した場合は子を残す**。親があとで再開して結果を使うため。

### delegate との違い

| | delegate | SpawnSubTask + WaitSubTask |
|---|---|---|
| 既定 | **デフォルト有効**（分解の既定手段） | **デフォルト無効**（要 opt-in 有効化） |
| 実行 | 直列・インプロセス | 並列・別ジョブ |
| 待機 | 自動（1 ツール呼び出しで完結） | `WaitSubTask` で待つ |
| 負荷 | GPU・実行枠が軽い | 並列ぶん多く消費する |
| 向く用途 | 逐次の分解・重い委譲全般 | 独立テーマを並列で短時間化したいとき |

## 起動の間隔（spawn_stagger_ms）

短時間に大量の `SpawnSubTask` を呼ぶと、その一団が GPU スロットの優先順位を一気に奪ってしまう。これを避けるため、`SpawnSubTask` のキュー投入の間に既定で 1 秒の間隔を入れる。

```yaml
subtasks:
  spawn_stagger_ms: 1000   # SpawnSubTask 投入間の間隔（ミリ秒、デフォルト 1000、0 で無効）
```

- `0` にすると間隔を入れず即時に投入する
- エージェントから見た呼び出し方は変わらない（透過的に間隔が入るだけ）

## 結果の参照

サブタスク完了後、親タスクは:
- `subtasks/{index}/result.md` にサブタスクの要約結果がある（Read で読む）
- `subtasks/{index}/output/` 以下にサブタスクの成果物がある

## 制限

- ネスト深さは `subtasks.maxDepth`（デフォルト 2）まで
- サブタスクが waiting_human 等で停止すると親もブロックされる
