OpenSpec 合併
將 openspec/specs/ 下多個相關的功能資料夾,透過兩階段的暫存/核准工作流程合併成一個主要功能規格。在人類明確核准之前,絕不會觸碰 openspec/specs/<capability>/ 下的任何內容。
適用時機
OpenSpec 的歸檔工作流程會將差異同步到 openspec/specs/<capability>/spec.md 中。當相關的變更以不同功能名稱歸檔時(通常是因為分階段建置,例如 user-show、user-detail、user-permissions),就會產生多個描述同一功能的規格資料夾。此技能透過一個需要人工簽核的暫存區將它們合併,在實際取代任何內容之前,都需經過人工核准。
兩個階段
- 階段 A — 暫存:讀取原始規格,將合併草稿寫入
openspec/specs/.merged-specs/<feature>/spec.md,並設定Approved-by-human: no。除此之外,不做任何其他變更。 - 階段 B — 套用:當使用者將 frontmatter 改為
Approved-by-human: yes時觸發。以核准的內容取代原始資料夾,並移除暫存檔案。
總是先判斷適用哪個階段:如果 openspec/specs/.merged-specs/<feature>/ 已存在,請先檢查其 Approved-by-human 欄位,而不是從頭開始重新暫存。
階段 A — 暫存
1. 識別候選資料夾
列出 openspec/specs/*,並尋找符合以下條件的資料夾:
- 共享共同名稱前綴或領域詞彙(
user-show、user-detail、user-permissions) - 在需求中引用相同的實體/資源(相同的模型、相同的 API 端點、相同的 UI 頁面)
- 歸檔時間相近(請檢查
openspec/changes/archive/的日期)
如果使用者已明確指定要合併的資料夾,請使用那些名稱,不要自行猜測。
如果存在多個可能的群組,請先停下來詢問使用者哪些資料夾應歸為一組,然後再進行暫存。
2. 完整讀取每個候選 spec.md
對於每個資料夾,讀取完整的 spec.md,而不僅僅是標題。注意需求名稱、情境、棄用標記,以及任何看起來會修改/取代另一個候選資料夾中需求的內容。
3. 推斷目標功能名稱
根據候選資料夾的共同前綴或共享領域名詞來推斷(例如 user-show + user-detail + user-permissions → user,或者如果更自然則可命名為 user-management)。
- 有明確的共同前綴/領域 → 直接使用,無需詢問。
- 沒有明顯的共享名稱(例如
checkout-flow和invoice-export僅因模糊的「帳務」主題相關)→ 在繼續之前請使用者指定目標功能名稱。
在輸出中明確陳述推斷的名稱,以便使用者可以覆寫。
4. 合併需求
規則(按順序):
- ADDED 需求的聯集:保留來自每個原始資料夾的每一個不同需求。
- 衝突/重複的需求(相同名稱,或相同行為被描述了兩次):保留最完整/最新的版本,合併非衝突的細節,而不是默默地刪除其中一個。
- REMOVED/已棄用的需求:從合併後的規格中排除。
- 絕不憑空創造需求。 以內聯方式標記模糊或衝突(例如
<!-- CONFLICT: source A says X, source B says Y — needs human decision -->),而不是默默地選擇。 - 盡可能保留原始情境文字;僅在將兩個重疊需求合併為一個時才改寫散文。
5. 寫入暫存檔案
寫入至:
openspec/specs/.merged-specs/<inferred-name>/spec.md
在檔案頂端加上此 frontmatter:
---
Approved-by-human: no
merged-from:
- user-show
- user-detail
- user-permissions
---
接著是合併後的規格內容。
6. 簡短通知使用者
- 哪些原始資料夾已被合併,以及推斷的目標名稱
- 任何已內聯標記並需要決策的衝突
- 下一步該做什麼:開啟
openspec/specs/.merged-specs/<inferred-name>/spec.md,檢閱它,如有需要則進行編輯,滿意後將 frontmatter 中的Approved-by-human設為yes openspec/specs/<capability>/下的任何內容都尚未被觸碰
階段 B — 套用
觸發條件:使用者要求套用/完成合併,或者你注意到 openspec/specs/.merged-specs/<feature>/spec.md 的 Approved-by-human: yes。
- 重新讀取暫存檔案並確認
Approved-by-human: yes。如果仍為no,請停止並告知使用者需要先核准 — 不要套用。 - 從 frontmatter 讀取
merged-from,以了解要取代哪些原始資料夾。 - 從暫存內容中移除 frontmatter(兩個欄位),留下一個沒有 frontmatter 的乾淨
spec.md。 - 以合併結果取代原始資料夾:
- 建立/覆寫
openspec/specs/<inferred-name>/spec.md,內容為移除 frontmatter 後的內容。 - 移除
merged-from中列出且非目標名稱本身的其他原始資料夾。
- 建立/覆寫
- 移除暫存資料夾
openspec/specs/.merged-specs/<inferred-name>/。 - 告知使用者哪些內容被取代/移除了,以便在提交前輕鬆比對 git diff。
在檔案中尚未明確出現 Approved-by-human: yes 之前,切勿執行步驟 4 — 不要要求使用者在聊天中以口頭方式核准作為替代;核准必須是已翻轉的 frontmatter 欄位本身。
範例
階段 A — 輸入:openspec/specs/user-show/、openspec/specs/user-detail/、openspec/specs/user-permissions/ — 全部描述同一個使用者設定檔功能的不同面向。
輸出:openspec/specs/.merged-specs/user/spec.md:
---
Approved-by-human: no
merged-from:
- user-show
- user-detail
- user-permissions
---
# 使用者
## 需求:...
...
傳送給使用者的訊息:已從 user-show、user-detail、user-permissions 合併為推斷的功能名稱 user(如果您偏好 user-management,可以重新命名資料夾);未發現衝突;請檢閱 openspec/specs/.merged-specs/user/spec.md,準備就緒後將 Approved-by-human: yes 設為 yes。
階段 B — 一旦使用者設定 Approved-by-human: yes 並要求套用:建立/覆寫 openspec/specs/user/spec.md 並帶有無 frontmatter 的合併內容,移除 user-show/、user-detail/、user-permissions/,並刪除 openspec/specs/.merged-specs/user/。