Skip to content
Merged
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
2 changes: 1 addition & 1 deletion plugins/issue-driven-dev/references/pr-flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Refs #${N}
{follow-up issues, if any}

---
Generated by IDD. **Do NOT add 'Closes #${N}'** — IDD discipline requires manual /idd-close after merge to enforce checklist gate + closing summary.
Generated by IDD. **Do NOT add a GitHub close trailer** (Closes/Fixes/Resolves) — IDD discipline requires manual /idd-close after merge to enforce checklist gate + closing summary.
EOF
)

Expand Down
2 changes: 1 addition & 1 deletion plugins/issue-driven-dev/skills/idd-all-chain/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -554,7 +554,7 @@ $DETAILS_BLOCKS

---

🤖 Generated by /idd-all-chain. **Do NOT add 'Closes #N'** trailers — IDD discipline requires manual /idd-close per issue after merge to enforce checklist gate + per-issue closing summary.
🤖 Generated by /idd-all-chain. **Do NOT add GitHub close trailers** (Closes/Fixes/Resolves) — IDD discipline requires manual /idd-close per issue after merge to enforce checklist gate + per-issue closing summary.
EOF
)

Expand Down
2 changes: 1 addition & 1 deletion plugins/issue-driven-dev/skills/idd-all/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -749,7 +749,7 @@ Refs #${N}
{若有 follow-up issues,列出 #N #M ...}

---
🤖 Generated by /idd-all. **Do NOT add 'Closes #${N}'** — IDD discipline requires manual /idd-close after merge to enforce checklist gate + closing summary.
🤖 Generated by /idd-all. **Do NOT add a GitHub close trailer** (Closes/Fixes/Resolves) — IDD discipline requires manual /idd-close after merge to enforce checklist gate + closing summary.
EOF
)

Expand Down
2 changes: 1 addition & 1 deletion plugins/issue-driven-dev/skills/idd-implement/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ Refs #${NUMBER}
- [ ] **Pending: human review of this PR + /idd-close after merge**

---
Generated by /idd-implement on PR path. **Do NOT add 'Closes #${NUMBER}'** — IDD discipline requires manual /idd-close after merge to enforce checklist gate + closing summary.
Generated by /idd-implement on PR path. **Do NOT add a GitHub close trailer** (Closes/Fixes/Resolves) — IDD discipline requires manual /idd-close after merge to enforce checklist gate + closing summary.
EOF
)

Expand Down
38 changes: 38 additions & 0 deletions plugins/issue-driven-dev/skills/idd-verify/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ idd-verify #NNN
```
TaskCreate(name="resolve_input_source", description="Step 0.5: 解析 --pr / --commits / --branch / --since flag;都沒帶就跑 auto-detect(count Refs #N commits since origin/<default>,再 gh pr list 找 open PR),有歧義時 AskUserQuestion 確認")
TaskCreate(name="gate_pr_correspondence", description="Step 0.7: PR mode 下強制檢查 issue↔PR 對應 — gh pr view --json body 抓 Refs #N,跟 user 指定的 issue 比對;PR 沒任何 Refs 或 user issue 不在 set 內 → abort 並告訴使用者怎麼修")
TaskCreate(name="scan_pr_body_trailers", description="Step 0.8: PR mode 下用 gh pr view --json closingIssuesReferences 查本 PR 是否 linked-to-auto-close 任何 issue(GitHub 權威解析)— 非空則 warn(merge 時會 auto-close issue、bypass /idd-close gate)。Warn-only,不 abort")
TaskCreate(name="get_diff_and_issue", description="依 input source 取 diff(gh pr diff / git diff HEAD~N / git diff origin/<default>...<branch>) + gh issue view,存 diff 到 /tmp 供 agents 讀取;PR mode 額外做 gh pr checkout 並記住原 branch")
TaskCreate(name="check_attachments", description="確認 .claude/.idd/attachments/issue-NNN/ 存在,把 attachment 路徑塞進 reviewer agent prompt 作為 source-of-truth context。manifest 缺漏 → 警告繼續(reviewer 仍跑,但 verification 完整度受限)。依 rules/process-attachments.md。")
TaskCreate(name="launch_parallel_reviewers", description="6 個 tool calls 同一 message: 5 Agent(subagent_type=general-purpose) for requirements/logic/security/regression/devils-advocate + 1 Bash codex(run_in_background:true),prompt 中引用 attachment 路徑 + 強制 file-output rule (per #52 v2.59.0+,replaces TeamCreate model from #47 incident)")
Expand Down Expand Up @@ -211,6 +212,43 @@ EXTRA=$(comm -23 <(echo "$DISCOVERED") <(echo "$USER_ISSUES" | sort -u))
[ -n "$EXTRA" ] && AskUserQuestion "PR also refs $EXTRA — verify those too, or scope to $USER_ISSUES only?"
```

### Step 0.8: PR auto-close detection(PR mode only,#87/#74)

`/idd-close` 的 checklist gate + closing summary 只在實際跑 `/idd-close` skill 時生效。若 PR 帶 GitHub auto-close 連結(PR body 內 `Closes #N` 等 trailer),GitHub 在 merge 時會直接 close 對應 issue,**完全 bypass** `/idd-close` — gate 沒跑、closing summary 沒寫,audit trail 斷裂。本 step 在 verify 時偵測,命中就 warn,讓使用者在 merge 前 strip。

偵測用 GitHub 自己的 `closingIssuesReferences` —— 這是 GitHub 對「本 PR merge 後會 auto-close 哪些 issue」的**權威解析**,已涵蓋所有 GitHub 承認的 trailer 形式(`Closes #N`、colon form `Closes: #N`、cross-repo、issue URL 等)。**不自寫 regex** 掃 PR body:自寫 regex 必然是 GitHub keyword parser 的脆弱近似(`/idd-verify --pr 94` R1 verify 實證 regex 漏了 colon form `Closes: #87`)。`closingIssuesReferences` 對 GitHub **已 settle 的 close-link 狀態**零誤判、零漏判。注意它是 eventually-consistent —— PR 剛建立的短暫傳播窗內可能尚未算出;verify 通常在 implement 完成數十秒以上才跑,屆時已 settle,此窗在實務上不構成問題。

這是**防禦縱深**:真正的修法是各 skill 的 PR-body template 不嵌 trailer(idd-implement / idd-all / idd-all-chain / pr-flow.md 已於 #87/#74 cluster 修正);本 gate 是第二層 —— 即使未來某個 template regression 漏掉、或使用者手動貼了 trailer,verify 仍能在 merge 前抓到。

```bash
# PR mode only — skip in local / branch / commits mode.
# closingIssuesReferences = GitHub's own authoritative parse of which issues this
# PR auto-closes on merge. Covers every trailer form GitHub honors; no
# parser-reimplementation risk. Query .url (not bare .number) so a cross-repo
# close ref is unambiguous.
# The `if CMD; then` form distinguishes a gh failure (auth/network/old CLI →
# else branch, surface a note) from a successful query that found nothing
# (then branch, $CLOSING_REFS empty → clean PR, stay silent). A bare
# `2>/dev/null || true` would conflate the two into a silent fail-open.
if CLOSING_REFS=$(gh pr view "$PR" --repo "$GITHUB_REPO" \
--json closingIssuesReferences \
-q '.closingIssuesReferences[].url' 2>/dev/null); then
if [ -n "$CLOSING_REFS" ]; then
echo "⚠️ WARNING: PR #$PR is linked to auto-close the following issue(s):"
printf ' %s\n' $CLOSING_REFS
echo " On merge GitHub will auto-close these, bypassing /idd-close's checklist"
echo " gate + closing summary. Strip the close trailer from the PR body before merge:"
echo " gh pr edit $PR --repo $GITHUB_REPO --body '<body without the Closes/Fixes/Resolves #N trailer>'"
fi
else
echo "note: Step 0.8 skipped — 'gh pr view --json closingIssuesReferences' failed"
echo " (auth / network / old gh CLI). Could not check PR #$PR for auto-close links;"
echo " verify continues. Re-check manually: gh pr view $PR --json closingIssuesReferences"
fi
```

**Warn-only,不 abort**:gate 的價值是把風險在 merge 前 surface 給使用者,由使用者決定是否 `gh pr edit`。語意同 `idd-close` Step 1.6 semantic gate(也是 warn-only)。`/idd-close #N` 這類 skill invocation 不會出現在 `closingIssuesReferences`(hyphenated token,GitHub 不當 close keyword),因此天然零誤判 —— 用 GitHub 權威 field 而非自寫 regex 的好處之一就是不需任何 keyword 排除邏輯。

### Step 1: 取得 diff 和 issue

依 Step 0.5 resolved source:
Expand Down