Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
schema: spec-driven
created: 2026-05-12
created_by: che cheng <kiki830621@gmail.com>
created_with: claude
207 changes: 207 additions & 0 deletions openspec/changes/multi-root-traversal-idd-all-chain/design.md

Large diffs are not rendered by default.

44 changes: 44 additions & 0 deletions openspec/changes/multi-root-traversal-idd-all-chain/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
## Why

`/idd-all-chain` 目前只接受**單一 root issue**,當 user 同時有多個獨立 root issue 要共開一個 review PR(例如 multi-source dogfood、cross-cutting refactor 撞到 3 個 root concern)時,只能跑 N 次獨立 chain → N 個 cluster branch → N 個 PR,reviewer 失去 holistic view。同時,既有的 `chain_max_depth=2 / chain_max_issues=5` hard cap 對 multi-root 場景太緊(3 roots × 平均 1.5 spawn 已 = 7.5 > 5),需 cap redesign 配套放寬。Spawn manifest schema v1 的 `root_issue: int` 在 multi-root 場景下無法區分某個 spawn 屬於哪個 root subtree,需 hard-break 到 v2。

## What Changes

- **Multi-root invocation**:`/idd-all-chain #44 #45 #50` 接受 ≥2 個 root issue(N=1 行為不變,backward compat)
- **Traversal mode**:default DFS(rich subtree first)+ opt-in `--bfs` flag(fairness — level-by-level across roots)
- **BREAKING**: spawn manifest schema v1 → v2 — `root_issue: int` 改 `root_issues: [int]`、top-level 加 `traversal: "dfs" | "bfs"`、每個 spawn entry 加 `root_id: int` 標明所屬 root subtree
- **Cap redesign**:`chain_max_depth` 2 → 3(primary cap),`chain_max_issues` 5 → 10(safety net);兩 cap 獨立 apply,whichever triggers first 勝
- **Verify FAIL halt scope**(Q4 Option C):per-root continue(非 global halt)— root #N 的 subtree 中 verify FAIL → 記入 `FAIL_ROOTS[]`,該 subtree 標 FAIL 並停,但其他 root 的 subtree 繼續;commits preserved;Phase 4 final report 印 per-root PASS/FAIL
- **Branch naming**:N=1 仍用 `idd/chain-<N>-<slug>`(backward compat);N>1 用 `idd/chain-multi-<hash8>-<root1-slug>`(hash = first 8 chars of `sha256sum` over joined root numbers,collision fallback to hash16)
- **DFS implementation**:current `QUEUE` 是 FIFO(pop-front + push-back = BFS)— DFS mode 改 push-spawns-to-front(`QUEUE=("$SPAWN_NUM" "${QUEUE[@]}")`);BFS mode 保留現行 push-back
- **Per-root depth counting**:每個 root 獨立 depth=0,`DEPTH_MAP[spawn] = DEPTH_MAP[parent] + 1`;`max_depth=3` 對每 root subtree 獨立 apply
- **PR title format**:N=1 維持 `chain: <root title>`;N>1 用 `chain (multi-root): N issues — <root#1 title>`
- **Phase 4 TaskList visualization**:forest tree printout — 每 root 印該 subtree 的 PROCESSED issues 縮排樹狀
- **Helper update**:`scripts/manifest-append.sh` 同步 bump `EXPECTED_SCHEMA_VERSION=2`,新增 `root_id` 參數位
- **4 sub-skill manifest writes**:`idd-implement`/`idd-verify`/`idd-plan`/`idd-diagnose` 的 manifest-append 呼叫需傳入 `root_id`(從讀 manifest 自己的 spawn parent 算出)

## Capabilities

### New Capabilities

(none)

### Modified Capabilities

- `idd-all-chain`: 接受多 root + 加 traversal mode + 新 cap 值 + 新 verify FAIL halt 語意 + 新 branch naming + 新 PR title
- `idd-spawn-manifest`: schema v1 → v2(BREAKING)— `root_issues: [int]`、top-level `traversal`、spawn entry `root_id`

## Impact

- Affected specs:
- Modified: `openspec/specs/idd-all-chain/spec.md`(multi-root + traversal + cap + halt + branch + PR title 五個 Requirements 更新)
- Modified: `openspec/specs/idd-spawn-manifest/spec.md`(schema v2 BREAKING,新欄位 + EXPECTED_SCHEMA_VERSION bump)
- Affected code:
- Modified: `plugins/issue-driven-dev/skills/idd-all-chain/SKILL.md`(Phase 0.1 多 root 解析、Phase 0.4 cluster branch 命名分支、Phase 1 init QUEUE/DEPTH_MAP 多 root、Phase 2 DFS/BFS 雙模式 + per-root halt、Phase 3 PR title + body、Phase 4 forest tree + per-root PASS/FAIL report)
- Modified: `plugins/issue-driven-dev/scripts/manifest-append.sh`(EXPECTED_SCHEMA_VERSION=2、新增 root_id 第 9 個位置參數)
- Modified: `plugins/issue-driven-dev/references/spawn-manifest.md`(schema v2 spec 描述 + example 更新)
- Modified: `plugins/issue-driven-dev/references/chain-flow.md`(DFS/BFS algorithm + per-root halt scope + cap interaction)
- Modified: `plugins/issue-driven-dev/skills/idd-implement/SKILL.md`(Step 5.7 manifest-append.sh 呼叫加 root_id 引數)
- Modified: `plugins/issue-driven-dev/skills/idd-verify/SKILL.md`(Phase 4 manifest-append.sh 呼叫加 root_id)
- Modified: `plugins/issue-driven-dev/skills/idd-plan/SKILL.md`(Step 2.5 manifest-append.sh 呼叫加 root_id)
- Modified: `plugins/issue-driven-dev/skills/idd-diagnose/SKILL.md`(Step 3.6 manifest-append.sh 呼叫加 root_id)
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
## MODIFIED Requirements

### Requirement: idd-all-chain skill SHALL drive root issue plus auto-emergent spawn through one cluster branch and one PR

The `idd-all-chain` skill SHALL accept one or more root issue arguments (`/idd-all-chain #N` or `/idd-all-chain #N #M #P [--bfs] [--cwd <path>]`) and run a chain-solve workflow that:

1. Creates a single cluster branch from the default branch. When N=1 the branch is named `idd/chain-<N>-<slug>` (backward compatible). When N>1 the branch is named `idd/chain-multi-<hash8>-<root1-slug>`, where `hash8` is the first 8 hex characters of `sha256` over the sorted-ascending root numbers joined by `-`, and `root1-slug` is the slug of the lowest root issue's title. On branch-name collision the chain shell SHALL retry with `hash16` (first 16 hex characters); on double collision the shell SHALL abort with a manual cleanup hint.
2. Recursively invokes `/idd-all #M --in-chain` for each root issue and each chain-eligible spawned issue, using either DFS or BFS traversal as specified below.
3. Stops processing the chain queue when the queue is empty, the per-root depth limit (`chain_max_depth=3`) is reached for the current subtree, or the total issues cap (`chain_max_issues=10`) is reached for the whole chain.
4. Opens exactly one pull request after the chain completes, covering all chained issues across all root subtrees.
5. Stops at verified state and SHALL NOT auto-close any issue (user retains close authority per IDD discipline).

The skill MUST NOT alter the existing `/idd-all` skill's single-issue lifecycle behavior. `/idd-all` invocations without `--in-chain` flag MUST behave identically to v2.46.0+ baseline.

The skill SHALL accept an optional `--bfs` flag that selects BFS traversal mode (level-by-level across all root subtrees). When `--bfs` is absent the skill SHALL use DFS traversal mode (process one root subtree fully before advancing to the next root). In DFS mode the chain queue SHALL push newly spawned issues to the **front** of the queue. In BFS mode the chain queue SHALL push newly spawned issues to the **back** of the queue. The default mode is DFS.

Each root issue SHALL have its own independent depth counter starting at zero. Spawn entries SHALL inherit `depth = parent_depth + 1` within their root subtree. The `chain_max_depth` cap applies per-root subtree. The `chain_max_issues` cap applies to the union of all root subtrees combined.

#### Scenario: single-root invocation is backward compatible

- **GIVEN** issue #28 is OPEN
- **WHEN** user invokes `/idd-all-chain #28`
- **THEN** the chain shell creates branch `idd/chain-28-<slug>` (single-root naming)
- **AND** initializes manifest with `root_issues=[28]` and `traversal="dfs"`
- **AND** processes the chain in identical observable behavior to v2.55.0 single-root chain runs

#### Scenario: multi-root invocation uses hash branch naming and DFS by default

- **GIVEN** issues #44, #45, #50 are all OPEN
- **WHEN** user invokes `/idd-all-chain #44 #45 #50`
- **THEN** the chain shell creates branch `idd/chain-multi-<hash8>-<root-44-slug>` where `hash8` is computed from `sha256` of `44-45-50`
- **AND** initializes manifest with `root_issues=[44,45,50]` and `traversal="dfs"`
- **AND** processes root #44's full subtree (including any DFS-eligible spawns) before advancing to root #45
- **AND** advances to root #50 only after #45's subtree completes

#### Scenario: multi-root with explicit BFS flag

- **GIVEN** issues #44, #45, #50 are all OPEN
- **WHEN** user invokes `/idd-all-chain #44 #45 #50 --bfs`
- **THEN** the manifest records `traversal="bfs"`
- **AND** the chain queue uses push-back semantics
- **AND** roots #44, #45, #50 are processed in input order at the top level before any spawns are processed

##### Example: DFS vs BFS queue order with a single spawn

| Mode | Initial queue | Pop #44 | Spawn #X (from #44) added | Next pop |
| ---- | ------------- | ------- | ------------------------- | -------- |
| DFS | [44, 45, 50] | [45, 50] (current=44) | push-front: [X, 45, 50] | X |
| BFS | [44, 45, 50] | [45, 50] (current=44) | push-back: [45, 50, X] | 45 |

#### Scenario: per-root depth cap enforced

- **GIVEN** chain max-depth cap is 3
- **AND** root #44 has a chain of spawns: #44 → #X (depth 1 in #44 subtree) → #Y (depth 2) → #Z (depth 3) → #W would be depth 4
- **WHEN** the chain shell processes #Z
- **THEN** #W is filed as a follow-up issue with manifest entry (audit preserved)
- **AND** #W is NOT added to the chain queue (per-root depth limit enforced)
- **AND** the chain continues processing root #45's subtree independently

#### Scenario: total max-issues cap caps the whole chain

- **GIVEN** chain max-issues cap is 10
- **AND** roots #44 and #45 between them produce 10 processed issues in their combined subtrees
- **WHEN** an 11th spawn is filed
- **THEN** the 11th spawn is recorded in the manifest with its `root_id` set
- **AND** the 11th spawn is NOT added to the chain queue (total cap enforced)
- **AND** Phase 4 report lists the 11th spawn under "filed only, not chained (max-issues cap)"

### Requirement: idd-all-chain SHALL halt the chain on verify failure and preserve partial commits

When any chained `/idd-all #M --in-chain` invocation completes with verify FAIL state, the chain shell MUST scope the halt to the failing root's subtree only (not the entire queue), preserve all commits already made on the cluster branch, and continue processing the chain queue for other root subtrees whose work is independent. Specifically the shell MUST:

1. Identify the `root_id` of the failing issue (from the manifest entry, or from `root_issues[0]` if the failing issue is a root itself)
2. Add that `root_id` to the `FAIL_ROOTS[]` set
3. Remove from the chain queue all pending issues whose `root_id` matches the failing root's `root_id` (the failing subtree is halted)
4. Continue processing the chain queue for other root subtrees (their work is not affected)
5. Preserve all commits already made on the cluster branch (the shell MUST NOT rebase, revert, or modify existing commits)

The shell MUST emit a final report in Phase 4 listing per-root PASS / FAIL / SKIPPED status.

When verify FAIL occurs on the only root subtree of the chain (single-root invocation or all other root subtrees already completed), behavior is equivalent to halting the entire queue.

#### Scenario: verify FAIL in one root subtree halts only that subtree

- **GIVEN** the chain queue contains pending issues from root #44 subtree and root #45 subtree
- **AND** root #44's spawn #X reaches verify FAIL
- **WHEN** the chain shell observes the FAIL
- **THEN** `FAIL_ROOTS` contains 44
- **AND** all pending issues with `root_id=44` are removed from the queue
- **AND** the queue continues processing issues with `root_id=45`
- **AND** the cluster branch retains all commits from both #44's partial work and #45's complete work
- **AND** Phase 4 report shows root #44 as `FAIL (verify FAIL at #X)` and root #45 as `PASS`

#### Scenario: single-root verify FAIL still halts the whole queue

- **GIVEN** `/idd-all-chain #28` is invoked (single root)
- **AND** `/idd-all #28 --in-chain` reaches Phase 4 verify and reports blocking findings
- **WHEN** the chain shell observes the FAIL
- **THEN** the chain queue is fully halted (no other root subtrees exist)
- **AND** the cluster branch retains partial commits made so far
- **AND** Phase 4 report shows root #28 as `FAIL (verify FAIL at #28)`

### Requirement: idd-all-chain SHALL produce a cluster PR with collapsed per-issue sections

After the chain queue is processed (full success, per-root partial failure, or any combination), `idd-all-chain` Phase 3 SHALL open exactly one pull request whose body contains:

1. PR title:
- When N=1: `chain: <root title>` (backward compatible)
- When N>1: `chain (multi-root): N issues — <root#1 title>` where `<root#1 title>` is the title of the lowest-numbered root issue
2. `Refs #<root_1> #<root_2> ... #<chained_1> #<chained_2> ...` listing all chained issue numbers (all roots first, then their spawns)
3. A `## Cluster overview` section with a table summarizing each issue (issue number, `root_id` it belongs to, spawn source, phase, head commit)
4. A `## Per-issue details` section using collapsed `<details>` HTML elements per issue
5. A `## Pending review` checklist where the final box reads `Pending: human review of cluster PR + /idd-close <issue list> after merge`

The PR body SHALL NOT contain `Closes #N` / `Fixes #N` / `Resolves #N` trailers (per existing IDD discipline against auto-close).

#### Scenario: single-root cluster PR uses chain prefix

- **GIVEN** chain solved root #28 with spawn #34
- **WHEN** Phase 3 opens the cluster PR
- **THEN** the PR title is `chain: <#28 title>`
- **AND** the body contains `Refs #28 #34`

#### Scenario: multi-root cluster PR uses chain (multi-root) prefix

- **GIVEN** chain solved roots #44, #45, #50 with one additional spawn #X from root #44
- **WHEN** Phase 3 opens the cluster PR
- **THEN** the PR title is `chain (multi-root): 4 issues — <#44 title>`
- **AND** the body contains `Refs #44 #45 #50 #X`
- **AND** the cluster overview table includes a `root_id` column showing #X belongs to root_id=44

## ADDED Requirements

### Requirement: idd-all-chain SHALL emit a Phase 4 final report with forest-tree visualization for multi-root chains

When the chain shell completes the queue (success or per-root failure), Phase 4 SHALL emit a final report containing:

1. A traversal mode line indicating the chosen mode (`Forest summary (traversal: dfs)` or `Forest summary (traversal: bfs)`).
2. A forest visualization: one tree per root issue. Each node shall display the issue number, depth within its root subtree, spawn source (sub-skill + spawn kind) for non-root nodes, and a status icon (`✓` for PASS, `✗` for FAIL, `⊘` for filed-but-not-chained).
3. A per-root summary listing each root's final status: `PASS (N spawn processed)` / `FAIL (verify FAIL at #X — subtree halted)` / `SKIPPED (max-issues cap)` / `SKIPPED (root not OPEN)`.
4. A flat list of filed-only-not-chained issues (those that hit a cap or eligibility filter).

For single-root chains (N=1), the forest visualization SHALL contain exactly one tree, and the per-root summary SHALL contain exactly one entry.

#### Scenario: multi-root forest report shows per-root status

- **GIVEN** roots #44 (PASS, 2 spawn processed), #45 (FAIL at #48), #50 (filed but unprocessed due to max-issues cap)
- **WHEN** Phase 4 emits the report
- **THEN** the report contains a `Forest summary (traversal: dfs)` header
- **AND** lists a `✓` node for root #44 with its two `✓` descendant nodes
- **AND** lists a `✗` node for root #45 with the failing spawn #48 shown as `✗`
- **AND** lists a `⊘` node for root #50 with annotation `(max-issues cap)`
- **AND** the per-root summary lists `#44: PASS (2 spawn processed)`, `#45: FAIL (verify FAIL at #48 — subtree halted)`, `#50: SKIPPED (max-issues cap)`

##### Example: forest tree output for the scenario above

```
Forest summary (traversal: dfs):

✓ root #44 (depth 0)
✓ #34 (depth 1, idd-implement Step 5.7 sister-bug)
✓ #41 (depth 2, idd-verify Phase 4 follow-up-finding)
✗ root #45 (depth 0) — FAIL at #48
✗ #48 (depth 1, idd-plan Step 2.5 tangential)
⊘ root #50 (depth 0) — filed but unprocessed (max-issues cap)

Per-root PASS/FAIL:
#44: PASS (2 spawn processed)
#45: FAIL (verify FAIL at #48 — subtree halted)
#50: SKIPPED (max-issues cap)
```
Loading