- name
- issue-deps
- description
- open Issue の依存を棚卸しし、ブロッカー節と段番号を直して、着手できる Issue の一覧を出す。「issue-deps」「issueの依存を棚卸しして」「着手順を振り直して」と指示されたとき。
- argument-hint
- [フェーズ名(省略可)]
- disable-model-invocation
- true
- allowed-tools
- Bash, Read, Edit, Write
open Issue の依存を棚卸しし、`## ブロッカー` 節(先に終わっている必要がある Issue を書く欄)と段番号を直してください。**段**は着手順の層で、同じ段の Issue は互いに依存せず並行して着手できます。**段番号**はそれをタイトルの先頭に書いた数字です。
Issue の一覧を見ても、今日着手してよいものが分かりません。段番号は起票したときの見立てのままで、起点Issueを sub-issue(親を分解した子のIssue)に割ったあとに判明した順序が入らないからです。着手できない Issue に手を付ければ手戻りし、着手できる Issue を見落とせば手が止まります。
## 前提
- **始めるタイミングは人間が握ります。** 一度に多数の Issue のタイトルと本文を書き換えるため、いつ走らせるかは人間が決めます。
- **起動後は確認なしで最後まで走ります。** 誤った推定は次の棚卸しや着手時に人間が直せます。1件ずつ承認を取ると、Issue 数に比例して時間が伸びます。
- **リポジトリのファイルは変更しません。** 触るのは GitHub Issue のタイトルと `## ブロッカー` 節だけです。
- **段番号の規約は [Issueの階層ガイド](../../../docs/reference/issue-hierarchy.md) が正典です。** 以降の手順には計算に要る分だけを写しています(実行中にガイドを開かずに済ませるため)。規約と手順が食い違って見えたらガイドが勝ちます。
- **親の付け替えと親の close は扱いません。** 規約の改定を伴う不可逆な操作を、番号直しのついでに走らせないためです(#429 で扱います)。
## Phase 1: 対象を決める
open Issue を、親・本文・ラベル・子の状態つきで一度に取ります。
```bash
gh repo view --json owner,name --jq '.owner.login + "/" + .name'
```
```bash
gh api graphql -f query='
{ repository(owner:"<owner>", name:"<repo>") {
issues(first:100, states:OPEN) { nodes {
number title body
labels(first:20){nodes{name}}
parent{number title}
subIssues(first:100){nodes{state}}
} }
} }'
```
ブロッカーから段を計算する対象から、次の2つを外します。
- **`phase` ラベルの常設Issue**:分類であって着手する対象ではない
- **open な子を持つ Issue(`state` が `OPEN` の子が1件以上)**:親は子をまとめる入れ物で、着手単位ではない。段は子から範囲で付ける(Phase 4)
**`boy-scout` ラベルの Issue は外しません。** 正式フローを経ていなくても実際の実行単位なので、着手順が要ります。
**`subIssues.totalCount` では判定しません。** close 済みの子も数えるので、子を全部閉じた親(残りを割る次の着手単位)が一覧から消えます。
残った Issue を、親を上に辿ってグループへ分けます。段番号はグループごとに1から振るので、この分け方が計算の単位になります。
- **フェーズIssueに行き着く**:そのフェーズ
- **親を持たない**:「親なし」
- **辿れない(中間の親が結果に無い)**:どこにも入れず、段を振らずに報告
`parent` は1階層しか返らないので、`フェーズ → 起点Issue → スライスIssue` のように深い場合は、取得結果の中で親をもう一度引いて辿ります。中間の親が close 済みだったり100件の外に落ちていると辿れません。そのときは推測でグループへ入れず、最後の報告に「所属不明」として並べます。
引数の解釈は次のとおりです。
- **なし**:open な全グループ
- **フェーズ名**:そのフェーズのグループだけ
フェーズ名は `phase` ラベルの Issue のタイトルと照合します。同じフェーズ名が複数あるときは、いちばん番号が大きいものを使います(階層ガイドの世代交代の規約)。
取得件数がちょうど100件なら上限で切れている可能性があるので、その旨を伝えてから処理します。対象が0件なら、その旨を伝えて終了します。決めた対象リストを表示してから Phase 2 へ進みます。
## Phase 2: 依存を推定する
各 Issue の本文を読み、**同じグループ内**の他の Issue との依存を判定します。グループをまたぐ依存は数えません——フェーズ間の前後はフェーズ自体が表すからです。数えてよい依存は次の2つだけです。
| 依存 | 判定 | 例 |
| ------------------------------ | -------------------------------------------------------------- | ------------------------------------------------------ |
| 成果物が無いと動かない | A が作るファイル・関数・テーブル・Skill を、B が呼ぶ・編集する | B の対応方針が、A が新設するファイルの節を参照している |
| 決定が出るまで書き始められない | A が決める方針(ADR・ポリシー・規約)に、B の中身が従う | B が「A で決めた命名規約に合わせる」と書いている |
**定義を2つに絞るのは、推定結果を実行ごとにぶらさないためです。** 「関係がありそう」まで広げると、同じ本文から毎回違う依存が出て、番号が実行ごとに動きます。
**ファイル衝突(同じファイルを触る)は依存に含めません。** 同じファイルを2件が触ることは並行作業の調整であって、着手順ではありません。含めると、順序が無いところに順序が生まれて段番号が実態より長く伸び、並行して着手できる Issue が待ちに見えます。
既存の `## ブロッカー` 節の記述も、本文と突き合わせて**毎回再判定します。** 起票時に見えなかった依存は本文を読まないと見つからず、逆に、割った結果すでに解消した依存も残っています。
**既存のブロッカー節に書かれた Issue 番号は、状態を1件ずつ確かめます。** Phase 1 で取るのは open だけなので、取得結果に無い番号が close 済みなのか100件の外なのかは、引かないと区別できません。`(closed)` の付与(Phase 3)と段1の判定(Phase 4)がどちらもこの区別に依存します。
```bash
gh issue view <番号> --json state --jq .state
```
グループの外を指す番号(別フェーズの Issue・`phase` ラベルIssue)は、**消さずに残して報告します。** 段の計算には入れません。書いた人が意図して置いた可能性があり、消すと理由ごと失われます。
## Phase 3: `## ブロッカー` 節を書き戻す
**本文はこの節だけを差し替えます。** 本文全体を組み直すと、推定で他の節が書き換わって失われます。
```bash
gh issue view <番号> --json body --jq .body > <スクラッチパッド>/issue-<番号>.md
```
書き出したファイルを Read し、Edit で `## ブロッカー` 節の中身だけを差し替えてから反映します。節が無い Issue には新しく足します。**置く位置は `## 実装フロー(使用するSkill)` 節の直後です**。その節も無ければ本文の末尾に足します。
```bash
gh issue edit <番号> --body-file <スクラッチパッド>/issue-<番号>.md
```
書く形は次のとおりです。**Issue 番号のあとに、なぜそう判定したかを1文添えます。** 根拠が無いと、次に開いた人は推定を信じてよいか判断できません。
```markdown
## ブロッカー
- #123 — `/issue-deps` の SKILL.md を新設するIssue。本Issueの対応方針がその節を参照している
```
ブロッカーが無ければ「なし(すぐ着手できる)」と書きます。
**close 済みの Issue をブロッカーに持つときは、その行を残したまま `(closed)` を付けます。** 依存そのものは消えていないので、その事実を残します。段の計算では満たされた依存として扱います(Phase 4)。
## Phase 4: 段番号を振り直す
グループごとに、`## ブロッカー` 節から段を計算します。
- **ブロッカーが無い、または全部 close 済み**:1
- **open なブロッカーがある**:ブロッカーの段の最大値+1
**ブロッカーが無い Issue から順に確定させます。** 段の定義がブロッカーの段を使うので、先を確定させないと計算できません。例:`#A`(ブロッカーなし)は段1、`#B`(`#A` を待つ)は段2、`#C`(`#A` と `#B` を待つ)は段3。
**open な子を持つ親の段は、範囲で表します。** 範囲は、その親の下にいる段を持つ open Issue(孫以下も含む)の段の最小値〜最大値です。同じグループの親をブロッカーに持つ Issue は、その範囲の最大値を「ブロッカーの段」として使います——親を待つとは、中の子を全部待つことだからです。自分の祖先をブロッカーに持つ Issue は、循環として扱います。
タイトルは、先頭の `N. ` または `N〜M. ` を剥がしてから付け直します。
- **open な子を持たない**:`<段>. `
- **open な子を持つ親Issue**:`<最小>〜<最大>. `。最小と最大が同じなら `<段>. `。段を持つ子孫が無ければ番号を外す
- **close 済み Issue**:タイトルに触らない。他の Issue の段を計算するときは、満たされた依存として扱う
```bash
gh issue edit <番号> --title "<段または範囲>. <番号を除いたタイトル>"
```
**`gh` を並列で叩かないでください。** 1件ずつ順に処理します。同じ Issue へ複数の更新が同時に飛ぶと、あとから投げたほうが前の更新を消します。
## Phase 5: 報告
グループごとに次の形で出します。人間はこれを見て、今日どれに着手するかを決めます。
**着手できる(段1)**
- #123 …
**待ち**
| Issue | 段 | 待っているもの |
| ----- | --- | ------------------ |
| #124 | 2 | #123(成果物待ち) |
**親Issue(open な子を持つもの)**
| Issue | タイトル | 子の段 |
| ----- | ------------ | ------ |
| #120 | 全体構造設計 | 1〜3 |
グループに入れられなかった Issue(親を辿れなかったもの)と、グループの外を指すブロッカーが残っている Issue があれば、番号を並べて添えます。
末尾に、タイトルを書き換えた件数(親の範囲を付け直したものも数える)と `## ブロッカー` 節を直した件数を1行で添えます。
## エラーハンドリング
- **フェーズ名が `phase` ラベルのどのIssueにも一致しない**:存在するフェーズ名を並べて終了する
- **`## ブロッカー` 節が1つの本文に複数ある**:最初の1つだけを差し替え、その事実を報告する
- **依存が循環している**:その Issue 群には段を振らず、循環の経路を報告する
- **`gh issue edit` が失敗する**:その Issue の処理を中断し、エラーを表示して次の Issue へ進む
- **同じ Issue に別セッションの更新が入っている**:上書きせず中断し、その事実を報告する
## 使用方法
```
/issue-deps # open な全グループ
/issue-deps 設計・実装 # そのフェーズのグループだけ
```
引数: $ARGUMENTS
عرض على GitHub