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
3 changes: 3 additions & 0 deletions openspec/changes/idd-ask-skill/.openspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
schema: spec-driven
created: 2026-07-18
created_by: che cheng <kiki830621@gmail.com>
37 changes: 37 additions & 0 deletions openspec/changes/idd-ask-skill/design.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Design: idd-ask-skill

## D1 — 與 find 的分工:lookup vs QA(防第 4 員定位稀釋)

| | `idd-find` | `idd-ask` |
|---|---|---|
| 問題 | 「有沒有處理過類似 X、在哪」 | 「當時為什麼這樣決定 / X 是怎麼運作的」 |
| 輸出 | ranked hits(人自己去讀) | **合成答案**(AI 讀完引用回答) |
| 讀取深度 | metadata + overlay(O(1)/hit) | top-N 命中的**全文**(body + comments) |
| Token 成本 | 低 | 高(有界:top-N 預設 5、`--limit` 可調) |

ask 的輸出**必附** `### Referenced Issues`,讓人可以 fall through 到 find 式的自行閱讀 — 兩員互補成「先問、不滿意再自己翻」的鏈。

## D2 — Retrieval delegate idd-find backend(family 慣例:不重造唯讀查詢)

Step「搜尋」直接沿用 `idd-find` 的 search backend 契約:`gh search issues`(relevance、`--state all` 全語料)主 + `gh issue list --search` fallback。**引用 idd-find SKILL 的 backend 段落,不內嵌分歧副本**(同 #137 反 typo-drift 紀律)。ask 疊加自己的第二步:對 top-N hits `gh issue view --json body,comments` 抓全文。跨措辭限制同界(詞法檢索),輸出尾端同樣揭露。

## D3 — Grounded 回答契約(spectra-ask 規矩移植)

1. 首行 blockquote 引用使用者原問題
2. 每個 claim 附來源(`#N` + comment 錨點或區段名);**語料裡沒有的不寫**(查無 → 誠實說查無 + 建議換 phrasing 或 `/idd-find` 自行翻)
3. **Source priority**:closed-with-PR > open > orphaned comment。衝突時取高優先源,並標注低優先源的分歧(「#A(closed)採 X;#B(open,進行中)傾向 Y」)
4. 結尾 `### Referenced Issues`:`#N (title) — URL`,只列實際引用的

## D4 — Decide-to-search gate(不是每個輸入都搜)

greeting / 純 meta-tool 問題(「idd-ask 怎麼用」)→ 不搜,直接答或引導。問題長得像 bug report → **不觸發 diagnose**,回答已知歷史後附一行「要立案 → `/idd-issue`」。無 question 且對話 context 可推 → 確認後搜;推不出 → 要求明確問題。

## D5 — #140 boilerplate checklist 逐項(第 4 員首戰)

Step 0 TaskCreate bootstrap ✓;config-protocol(`--repo` override;group 搜尋 = `--target group:<label>` 依 issue 需求,v1 先單 repo + 註記 group 為 residue)✓;unattended fallback(無互動 gate;decide-to-search 的「確認後搜」在 unattended 下改直接搜 + audit line)✓;bounded output(top-N=5、`--limit` 上限 10;讀全文的 token 成本是 ask 的本質成本,明文揭露)✓;read-only allowed-tools ✓;drift-guard ✓;CLAUDE.md + usecase-routing ✓。`references/surfacing-primitives.md` 成員表 +1(第 3 題弱命中入註記)。

## Alternatives considered

- **塞進 idd-find 當 `--answer` flag**:否 — I/O 形狀不同(hits vs 合成答案)、token 成本量級不同,正是 #139 Option A 拒絕合併的同一論證
- **建 local cache / embedding index**:否 — 基建成本不符 surfacing primitive 輕量定位;誠實邊界與 find 一致
- **先只寫 reference 文件(第 3 題弱)**:否 — spectra-ask 同構先例證明 ask 類 skill 的消費者是人,等 skill 消費者出現才開會讓「還原 decision rationale」繼續無入口
34 changes: 34 additions & 0 deletions openspec/changes/idd-ask-skill/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Proposal: idd-ask-skill

## Why

IDD 的知識庫(issues + comments + linked PRs:diagnoses、decisions、closing summaries)沒有 grounded 問答入口。查歷史只能手動 `gh issue list` + 逐一 view — 三個月後想還原「當時為什麼這樣決定」,AI 沒有 skill 層路徑(#72,鏡像 `/spectra-ask` 的空缺)。`/idd-find`(#139)解了「在哪裡」(query → ranked hits),沒解「為什麼」(question → 有引用的合成答案)。

## What Changes

1. **新 skill `/idd-ask <question>`**:surfacing family **第 4 員** — 自然語言問題 → decide-to-search gate → retrieval(**delegate `idd-find` 的 search backend**,不重造)→ 讀取 top-N 命中 issue 全文(body + comments)→ grounded 回答(首行 blockquote 原問題、每個 claim 附 issue/comment 引用、不腦補)→ `### Referenced Issues` 區段。
2. **Source priority**(類比 spectra-ask):closed-with-PR issue > open issue > orphaned comment — 已關已 ship 是 ground truth,open 標注「進行中、可能會變」。
3. **Read-only 鐵律**:不 create / edit / comment / close;問題長得像 bug report 也**不**觸發 diagnose(引導語提示 `/idd-issue`)。
4. **#140 增員程序首次實戰**:三題判準記錄 + boilerplate checklist 逐項 + `references/surfacing-primitives.md` 成員表同步(canonical 更新)。
5. **可發現性**:plugin CLAUDE.md 表 + usecase-routing 情境列;drift-guard suite `scripts/tests/idd-ask/`。

## 增員判準(#140 三題,誠實記錄)

1. **I/O 形狀與現任三員不同?** ✓ — list(filters→triage 表)/ clarify(內文→標注)/ find(query→ranked hits)/ **ask(question→合成答案+引用)**
2. **唯讀?** ✓(issue 明訂設計約束)
3. **有 delegate 消費者?** **弱命中** — 主要消費者是人(同 spectra-ask);潛在 skill 消費者(diagnose 查歷史 rationale)未接線。per family doc 此題弱時「先當 reference 文件」是選項,但本案有使用者裁決 + spectra-ask 同構先例(ask 類 skill 的消費者本來就是人),judgment:開 skill、把第 3 題弱命中記入 family doc 成員表註記。

## Non-Goals

- Embedding 語意搜尋(#72 Residue — `gh search` 詞法檢索是誠實邊界,與 idd-find 同界)
- Local cache / index 建置(retrieval 成本用 top-N + limit 控,不建基建)
- 觸發任何 lifecycle step(ask 是純問答)

## Impact

- Affected specs: `idd-ask`(ADDED,新 capability)
- Affected code: 新 `skills/idd-ask/SKILL.md`、`references/surfacing-primitives.md`(成員表 +1)、plugin `CLAUDE.md`、`references/usecase-routing.md`、新 suite

## Refs

Issue #72(Spectra opt-out → propose;「鏡像 /spectra-ask」定案);family 契約 #140;retrieval backend 先例 #139
35 changes: 35 additions & 0 deletions openspec/changes/idd-ask-skill/specs/idd-ask/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
## ADDED Requirements

### Requirement: Grounded question-answering over the issue corpus

`/idd-ask <question>` SHALL answer natural-language questions grounded exclusively in the target repo's issue corpus (issues, comments, linked PRs, open and closed): it SHALL retrieve candidates via the `idd-find` search backend contract (lexical relevance, full corpus), read the top-N hits' full body and comments (N default 5, `--limit` capped at 10), and compose an answer in which the first line blockquotes the user's question and every claim carries an issue/comment citation. Content absent from the corpus SHALL NOT be asserted — an empty or insufficient retrieval SHALL be reported honestly with a suggestion to rephrase or fall through to `/idd-find`. The answer SHALL end with a `### Referenced Issues` section listing only the actually-cited issues. Source priority SHALL be closed-with-PR > open > orphaned comment, with conflicts surfaced rather than silently resolved.

#### Scenario: decision-rationale question gets a cited answer

- **WHEN** `/idd-ask "為什麼 idd-verify 的 DA 改成 sequenced spawn?"` runs against a repo whose closed issue #130 records that decision
- **THEN** the answer opens by blockquoting the question, cites #130's diagnosis/closing content for each claim, and lists #130 under `### Referenced Issues`

#### Scenario: corpus silence is reported, not filled

- **WHEN** the question matches nothing in the corpus
- **THEN** the skill says so, suggests rephrasing or `/idd-find`, and fabricates no answer

#### Scenario: open-vs-closed conflict is surfaced

- **GIVEN** a closed-with-PR issue records decision X and an open issue is trending toward Y
- **WHEN** both are retrieved for the same question
- **THEN** the answer leads with X (higher priority) and explicitly notes the open issue's in-progress divergence

### Requirement: Surfacing-only fourth member obligations

`/idd-ask` SHALL be read-only (no issue create / edit / comment / close; a bug-report-shaped question SHALL NOT trigger any lifecycle step — the answer MAY append a one-line pointer to `/idd-issue`), SHALL follow the surfacing-primitives family boilerplate (Step 0 bootstrap, config-protocol resolution with `--repo`, unattended fallback that searches without the confirm gate and records an audit line, bounded output), and SHALL be registered in the family's canonical member table in `references/surfacing-primitives.md`.

#### Scenario: bug-shaped question stays read-only

- **WHEN** `/idd-ask "idd-edit 是不是會把 body 洗掉?"` runs
- **THEN** the skill answers from history (e.g., the #150 record) without creating or modifying anything, and at most appends a `/idd-issue` pointer

#### Scenario: family table registers the fourth member

- **WHEN** `references/surfacing-primitives.md` is inspected
- **THEN** its member table lists idd-ask alongside idd-list / idd-clarify / idd-find with its distinct I/O shape
9 changes: 9 additions & 0 deletions openspec/changes/idd-ask-skill/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
## 1. Tests first (RED)

- [x] 1.1 (Req: Grounded question-answering over the issue corpus; Req: Surfacing-only fourth member obligations) New drift-guard suite plugins/issue-driven-dev/scripts/tests/idd-ask/test.sh: SKILL 存在 + read-only 禁令 + blockquote 原問題 + claim 必附引用 + source priority + `### Referenced Issues` + top-N/limit 界限 + delegate idd-find backend 引用 + 不觸發 diagnose + family 文件成員表含 idd-ask + CLAUDE.md / usecase-routing 可發現性。Run: RED。

## 2. Implementation (GREEN)

- [x] 2.1 (Design D1 — 與 find 的分工; D2 — retrieval delegate idd-find backend; D3 — grounded 回答契約; D4 — decide-to-search gate; D5 — #140 boilerplate checklist 逐項) New skills/idd-ask/SKILL.md(frontmatter read-only allowed-tools、Step 0 bootstrap、gate、retrieval delegate、全文讀取 top-N、回答契約、source priority、unattended 行為、`--repo`/`--limit`、分工表)。
- [x] 2.2 references/surfacing-primitives.md 成員表 +1(第 3 題弱命中註記);plugin CLAUDE.md 輔助表加 idd-ask 列;references/usecase-routing.md 加情境列。
- [x] 2.3 Verify: suite GREEN; full plugin sweep 0 fail; spectra validate clean。
1 change: 1 addition & 0 deletions plugins/issue-driven-dev/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ skill source tree;既有 `SKILL.md` 可以繼續使用 Claude-native tool cont
| `idd-update` | Issue body 過時,要讀完所有 comments 才知道現狀 | 同步 body Current Status 區塊 |
| `idd-list` | 不知道有什麼要做、漏掉卡 verify 的 issue | 列出 open issues 含 IDD phase + 建議 next action |
| `idd-find` | 重複 diagnose 已解過的問題;忘記舊 fix 在哪 | 語意查找 open+closed 全語料(GitHub relevance + phase/PR overlay);surfacing-only、read-only(v2.97.0+,#139)|
| `idd-ask` | 三個月後沒人記得當時為什麼;AI 憑記憶腦補歷史 | issue 知識庫 grounded 問答(top-N 全文 + claim 必附引用 + source priority;鏡像 /spectra-ask);surfacing-only、read-only(v2.99+,#72)|
| `idd-report` | 進度不透明,stakeholder 看不到現況 | 產出進度報告到 GitHub Discussions |
| `idd-comment` | 非流程性決定 / 外部 context 散落在 chat | Template-guided comment(decision/note/question/correction/link/errata)|
| `idd-edit` | 手動 `gh api PATCH` 容易字串 escape 誤覆蓋 | 編輯既有 comment(append/replace/prepend-note 三種 mode)|
Expand Down
5 changes: 3 additions & 2 deletions plugins/issue-driven-dev/references/surfacing-primitives.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ IDD 的 skill 分兩類:**lifecycle skills**(issue → diagnose → implemen
|--------|-----|-----------|
| `/idd-list`(v2.51+) | structural triage | 「現在有什麼、在哪個 phase、哪個 PR」 |
| `/idd-clarify`(#135, v2.72+) | terminology / semantic accuracy | 「這個詞用得準不準、誰需要釐清」 |
| `/idd-find`(#139, v2.97+) | semantic lookup | 「之前是不是處理過類似的問題」 |
| `/idd-find`(#139, v2.97+) | semantic lookup | 「之前是不是處理過類似的問題、在哪」 |
| `/idd-ask`(#72, v2.99+) | grounded QA | 「當時為什麼這樣決定 / X 怎麼運作」→ **合成答案**+引用(鏡像 /spectra-ask) |

三員互補、零重疊:list 吃 filters、find 吃 free-text queryclarify 吃 issue 內文語意 — 輸入/輸出形狀不同是「該開新 skill 而非塞 flag」的判準(#139 Option A 裁決的一般化)。
四員互補、零重疊:list 吃 filters、find 吃 free-text query → ranked hits、ask 吃 question → 合成答案、clarify 吃 issue 內文語意 — 輸入/輸出形狀不同是「該開新 skill 而非塞 flag」的判準(#139 Option A 裁決的一般化)。ask 的 retrieval delegate find 的 backend(本檔「lifecycle 不重造唯讀查詢」鐵律同樣適用於 primitive 之間)。增員三題的誠實記錄(#72):第 3 題(delegate 消費者)**弱命中** — ask 類 skill 的消費者本來就是人(spectra-ask 同構先例),以此 judgment 開員;若未來第 3 題弱的候選無同構先例,先當 reference 文件

## D12 軸:Surfacing vs Lifecycle

Expand Down
1 change: 1 addition & 0 deletions plugins/issue-driven-dev/references/usecase-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
| **27** | N 個 unrelated open issues 想一次解(無 emergent 關係,backlog drain)| **`idd-all #A #B #C`**(v2.83.0+ multi-issue batch mode)— conflict-class-**ordered sequential** drain:依 `### Conflict Class` 排序(`E`/`D` 先、同資源 `B`/`C` 相鄰序列化、同檔成組、`A` 不拘),逐一跑完整 idd-all pipeline | (無 — 每 issue 讀 `### Conflict Class`,缺則預設 `D_diagnose_first` surfaced);**sequential**(真並發 stateful lane = deferred,見 worktree-isolation Case A);`A` 可選 worktree 隔離供事後手動並行;stop at verified | [parallel-orchestration.md](parallel-orchestration.md)(取代先前「無內建 bulk-solve」註記;conflict-class taxonomy = 並行安全契約;參 parked [#37](https://github.com/PsychQuant/issue-driven-development/issues/37))|
| **28** | 使用者回報以 **GitHub Discussion** 形式抵達(Q&A / Ideas),要納入 IDD intake | `idd-list --discussions`(surface actionable)→ 人判斷 → `idd-issue --from-discussion <url>`(seed + provenance + draft-and-confirm 回文)| `--discussions` / `--from-discussion` | [discussions-intake.md](discussions-intake.md) |
| **29** | 「之前是不是處理過類似的問題?」— 建案前查重、找舊案考古、引用過往結案紀錄(v2.97.0+,#139)| `idd-find "<free-text query>"`(open+closed 全語料,GitHub relevance 排序 + phase / PR overlay;surfacing-only、read-only)| `--repo` / `--limit N`(filter flags 拒收 → 導流 `idd-list`)| [surfacing-primitives.md](surfacing-primitives.md) |
| **30** | 「當時為什麼這樣決定?」「X 怎麼運作?」— 還原 decision rationale、查歷史脈絡、不想自己翻 issue(v2.99+,#72)| `idd-ask "<自然語言問題>"`(retrieval delegate idd-find backend → top-N 全文 → grounded 合成答案:claim 必附引用 + source priority + `### Referenced Issues`;surfacing-only、read-only、不觸發 diagnose)| `--repo` / `--limit N`(top-N 上限 10)| [surfacing-primitives.md](surfacing-primitives.md) |

> **Chain-solve vs bulk-solve semantic 差異**(per #49 cross-link):
> - **`/idd-all-chain #N`**(v2.55.0+, row 25, single-root):**hot-chain from active work** — 1 root + auto-emergent spawn(sister bug / verify follow-up / mid-plan tangential / sister concern detected via spawn manifest)。Bounded by hard caps per-root depth=3、global max-issues=10 (v2.60.0+, was 2/5)。Reviewer mental model:「this thread is complete」。
Expand Down
53 changes: 53 additions & 0 deletions plugins/issue-driven-dev/scripts/tests/idd-ask/test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#!/usr/bin/env bash
# test.sh — drift-guard for #72 /idd-ask (grounded QA over the issue corpus).
#
# WHY A CONTENT DRIFT-GUARD: the skill is prose. The falsifiable equivalent of
# "answers stay grounded and the skill stays a surfacing primitive" is
# asserting the SKILL carries the grounding contract verbatim (blockquote the
# question, cite every claim, source priority, Referenced Issues, honest
# silence), the family obligations (read-only, no-diagnose-trigger, bounded
# top-N, idd-find backend delegation — never a diverging retrieval copy), and
# the three discoverability surfaces. A missing citation-contract line is
# exactly how grounded QA silently degrades into hallucinated summaries.
#
# Usage: bash test.sh (exit 0 = all pass, 1 = any fail)

set -u

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PLUGIN_ROOT="$HERE/../../.."
SKILL="$PLUGIN_ROOT/skills/idd-ask/SKILL.md"
FAMILY="$PLUGIN_ROOT/references/surfacing-primitives.md"
PCLAUDE="$PLUGIN_ROOT/CLAUDE.md"
ROUTING="$PLUGIN_ROOT/references/usecase-routing.md"

HELPERS="$HERE/../../lib/assert-helpers.sh"
[ -f "$HELPERS" ] || { echo "✗ missing $HELPERS — cannot run suite" >&2; exit 1; }
. "$HELPERS"

# ── skill exists with the grounding contract ──
assert_file_exists "idd-ask SKILL.md exists" "$SKILL"
assert_output_grep "skill: blockquote the user's question" "blockquote 引用使用者原問題" "$SKILL"
assert_output_grep "skill: every claim carries a citation" "claim 必附引用" "$SKILL"
assert_output_grep "skill: source priority order" "closed-with-PR > open > orphaned comment" "$SKILL"
assert_output_grep "skill: Referenced Issues section" "### Referenced Issues" "$SKILL"
assert_output_grep "skill: honest silence on corpus miss" "查無" "$SKILL"
assert_output_grep "skill: conflicts surfaced not resolved" "分歧" "$SKILL"

# ── family obligations ──
assert_output_grep "skill: read-only prohibition" "不 mutate 任何 state" "$SKILL"
assert_output_grep "skill: bug-shaped question never triggers diagnose" "不觸發" "$SKILL"
assert_output_grep "skill: bounded top-N" "top-N" "$SKILL"
assert_output_grep "skill: delegates idd-find backend (no diverging copy)" '`idd-find` 的 search backend' "$SKILL"
assert_output_grep "skill: family membership" "surfacing-only" "$SKILL"
assert_output_grep "skill: cites family canonical" "references/surfacing-primitives.md" "$SKILL"

# ── family canonical registers the 4th member ──
assert_output_grep "family: member table lists idd-ask" "idd-ask" "$FAMILY"
assert_output_grep "family: ask's distinct I/O shape recorded" "合成答案" "$FAMILY"

# ── discoverability ──
assert_output_grep "plugin CLAUDE.md lists idd-ask" "idd-ask" "$PCLAUDE"
assert_output_grep "usecase-routing has an ask scenario" "idd-ask" "$ROUTING"

print_summary "idd-ask"
Loading
Loading