Skip to content

spec-driven ​

預設工作流程的產出物:它們的順序、格式,以及所產生的變更資料夾。

spec-driven 是 OpenSpec 內建的預設 schema。schema.yaml 定義了它所設定的欄位。

產出物 ​

此工作流程會產生四種產出物:

產出物檔案用途
proposalproposal.md說明為何需要此變更
specsspecs/<capability-path>/spec.md,每個能力各一個說明哪些行為會改變
designdesign.md說明如何建置
taskstasks.md實作檢查清單

起草順序 ​

text
             ┌─ specs ──┐
proposal ────┤          ├── tasks ── apply
             └─ design ─┘

Proposal 先完成。Specs 和 design 可以任意順序接續,而 tasks 需要兩者都就緒。實作(apply)在 tasks.md 完成後開始。

有兩個產出可以省略:

  • design:當其條件皆不適用時,代理工具會略過它,直接起草 tasks。
  • specs:在變更的 .openspec.yaml 中設定 skip_specs: true。

範例變更資料夾 ​

名為 add-user-auth 的變更,包含所有已起草的產出:

text
openspec/changes/add-user-auth/
├── .openspec.yaml      變更中繼資料,在建立變更時寫入
├── proposal.md
├── specs/
│   └── user-auth/
│       └── spec.md     每個功能一個增量規格
├── design.md
└── tasks.md

proposal.md ​

說明為何需要此變更。

結構 ​

代理工具作為輸出格式接收的模板(templates/proposal.md):

md
# Proposal

## Why

<!-- 說明此變更的動機。解決什麼問題?為什麼是現在? -->

## What Changes

<!-- 描述將變更的內容。具體說明新增功能、修改或移除。 -->

## Capabilities

### New Capabilities
<!-- 正在引入的功能。為每個功能命名,代表系統可擁有相關需求的連貫行為,
     隨著系統演進而發展。不要命名實作任務或提案區段。避免籠統的包山包海名稱。
     對新增的路徑區段使用 kebab-case(例如 user-auth 或 identity/user-auth),
     遵循專案現有的規格組織。每個功能會建立
     specs/<capability-path>/spec.md。 -->
- `<capability-path>`: <簡要描述此功能涵蓋的範圍>

### Modified Capabilities
<!-- 需求正在變更的現有功能(不只是實作)。
     僅在規格層級行為變更時列出。每個都需要增量規格檔案。
     使用 openspec/specs/ 下現有的確切路徑。若無需求變更則留空。
     完全沒有功能的變更(純重構、工具、文件)
     必須在其 .openspec.yaml 中設定 `skip_specs: true` - openspec validate 會拒絕
     沒有該標記的零增量變更。不要為了通過驗證而憑空捏造需求。 -->
- `<existing-capability-path>`: <說明什麼需求正在變更>

## Impact

<!-- 受影響的程式碼、API、依賴項目、系統 -->

指示 ​

代理工具起草此產出時接收的指示(來自 schema.yaml):

md
建立 proposal 文件,說明 WHY 需要此變更。

區段:
- **Why**:用 1-2 句話說明問題或機會。解決什麼問題?為什麼是現在?
- **What Changes**:變更的條列清單。具體說明新增功能、修改或移除。將破壞性變更標記為 **BREAKING**。
- **Capabilities**:識別將建立或修改的規格:
  - **New Capabilities**:列出正在引入的功能。每個功能會成為新的 `specs/<capability-path>/spec.md`。為每個功能命名時,代表一種持久的系統行為(例如 `user-auth`),而非此變更中的工作(例如 `add-login-endpoint`)。選擇一個連貫的邊界,能隨著系統演進擁有相關需求;避免籠統的包山包海功能。對新增的路徑區段使用 kebab-case(例如 `user-auth` 或 `identity/user-auth`),並遵循專案現有的規格組織。
  - **Modified Capabilities**:列出需求正在變更的現有功能。僅在規格層級行為變更時納入(不只是實作細節)。每個都需要增量規格檔案。使用 `openspec/specs/` 下現有的確切路徑。若無需求變更則留空。
- **Impact**:受影響的程式碼、API、依賴項目或系統。

重要:Capabilities 區段至關重要。它在
proposal 和 specs 階段之間建立契約。在填寫前請先研究現有規格:
執行 `openspec list --specs` 取得專案的功能清單,然後
對任何看起來相關的執行 `openspec show "<spec-id>" --type spec --json --no-scenarios` - 這會傳回功能的用途和需求文字,
而不會將整個規格檔案載入上下文。僅對已註冊的獨立存放區在兩個命令後附加 `--store "<id>"`,
並保留 `--type spec`:否則變更和規格共用名稱會產生模糊項目錯誤。
未使用 `--specs` 的 `openspec list` 列出進行中的變更,而非
規格 - 它不會顯示專案已涵蓋的內容。重複使用現有
功能的確切路徑,而非引入近乎重複的名稱。
篩選後的讀取僅是概述。在決定什麼已涵蓋或什麼應變更前,
使用 `openspec show "<spec-id>" --type spec` 完整閱讀每個相關規格,包括
場景(同樣的 `--store` 規則)。
此處列出的每個功能都需要對應的規格檔案。

每個變更必須聲明至少一個功能(新增或
修改)或明確選擇不使用規格:`openspec validate` 會拒絕
零增量的變更,除非變更的 `.openspec.yaml` 設定
`skip_specs: true`。僅在沒有規格層級行為變更時(純重構、工具、文件)使用 `skip_specs: true` - 規格描述
行為,所以如果行為不變,規格也不應變。
不要為了通過驗證而憑空捏造需求。

保持精簡(1-2 頁)。聚焦於「為什麼」而非「如何」-
實作細節屬於 design.md。

這是基礎 - specs、design 和 tasks 都建立在此之上。

增量規格 (spec.md) ​

定義行為變更,提案中列出的每個功能各一個增量規格。

每個增量規格是其功能資料夾中的 spec.md。openspec validate 和 openspec archive 會拒絕在 specs/ 下任何其他檔案中撰寫的增量區段,例如 specs/user-auth.md,因為歸檔永遠不會合併它們。

結構 ​

代理工具作為輸出格式接收的模板(templates/spec.md):

md
# Spec Delta

## Purpose
<!-- 僅限新功能:用一兩句話(50+ 字元)說明此功能的用途。若為現有功能則刪除此區段。 -->

## ADDED Requirements

### Requirement: <!-- requirement name -->
<!-- requirement text -->

#### Scenario: <!-- scenario name -->
- **WHEN** <!-- condition -->
- **THEN** <!-- expected outcome -->

指示 ​

代理工具起草此產出時接收的指示(來自 schema.yaml):

md
建立定義系統應該做什麼的規格檔案。

規格是行為契約,而非實作計畫。

好的規格內容:
- 使用者或下游系統依賴的可觀察行為
- 輸入、輸出和錯誤條件
- 外部限制(安全、隱私、可靠性、相容性)
- 可測試或明確驗證的情境

在規格中避免:
- 內部類別/函式名稱
- 程式庫或框架選擇
- 逐步實作細節
- 詳細的執行計畫(這些屬於 design.md 或 tasks.md)

快速測試:如果實作可以變更而不改變外部
可見行為,它可能不屬於規格。

為提案 Capabilities 區段列出的每個功能建立一個規格檔案。
`<capability-path>` 是相對於 `specs/` 的規格目錄(例如
`user-auth` 或 `identity/user-auth`)。保留完整路徑:
- 新功能:在 `specs/<capability-path>/spec.md` 使用提案中的確切路徑。提案中新增的任何路徑區段必須是 kebab-case。遵循專案現有的組織;當專案使用扁平結構時不要新增領域層級。
- 修改的功能:在 `specs/<capability-path>/spec.md` 建立增量時,使用 `openspec/specs/<capability-path>/` 中現有的確切路徑。執行 `openspec list --specs` 在寫入增量前確認該路徑,僅對已註冊的獨立存放區附加 `--store "<id>"` - 拼錯或捏造的路徑會指向不存在的功能而非您意指的功能。不要移動或重新命名功能。

一次寫一個規格檔案,讓使用者看到進度,且中斷的執行會保留已寫入的檔案:
- 在寫入任何內容前,將提案列出的功能與
  已存在的 `specs/<capability-path>/spec.md` 檔案進行比較,
  從第一個沒有檔案的功能開始。不要重寫
  現有檔案,除非使用者要求。
- 在每個檔案前,用一行說明您正在撰寫哪個功能。
- 每當您起草完一個檔案就立即寫入。不要計畫所有檔案
  後才開始寫第一個。
- 當提案列出的每個功能都有其檔案時停止。

必須至少有一個規格檔案,除非變更的 `.openspec.yaml`
設定 `skip_specs: true`(沒有規格層級行為變更)- `openspec validate`
在沒有該標記時拒絕零增量變更。如果提案未列出任何
功能且未設定 `skip_specs`,請先重新檢視提案。

增量操作(使用 ## 標題):
- **ADDED Requirements**:新功能
- **MODIFIED Requirements**:變更的行為 - 必須包含完整的更新內容
- **REMOVED Requirements**:已棄用的功能 - 必須包含 **Reason** 和 **Migration**
- **RENAMED Requirements**:僅名稱變更 - 使用 FROM:/TO: 格式

格式要求:
- 每個需求:`### Requirement: <name>` 後接描述
- 使用 SHALL/MUST 表示規範性需求(避免 should/may)
- 每個情境:`#### Scenario: <name>` 使用 WHEN/THEN 格式
- **關鍵**:情境必須恰好使用 4 個井字號(`####`)。使用 3 個井字號或項目符號會靜默失敗。
- 每個需求必須至少有一個情境。
- 每個情境必須有 WHEN/GIVEN 步驟(觸發條件)和 THEN 步驟(結果)。只有 AND 行或純文字的情境仍算是一個情境,但 `openspec validate` 會將其標記為警告:一般驗證仍會通過,但 `openspec validate --strict` 會失敗。
- 將每個需求的描述(`### Requirement:` 和其第一個情境之間的文字)保持在 500 字元以內。`openspec validate` 會標記 ADDED 需求和主規格中較長的描述。這是警告:一般驗證仍會通過,但 `openspec validate --strict` 會失敗。撰寫新需求時,每個需求陳述一個行為:將範例和邊界案例移到情境中,並將涵蓋多個行為的需求拆分為個別的 `### Requirement:` 區塊,各有其情境。在 MODIFIED 下,保持現有需求區塊完整;絕不要僅為了符合長度而拆分、裁剪或重寫現有文字。僅在專門為此目的所做的變更中,當使用者要求時才拆分現有的長需求:在 MODIFIED 下,保留其標題和每個情境,並將其描述縮減為一個行為而不改變其含義,然後將每個移除的行為作為其專屬的 ADDED 需求,各有其情境。

僅限新功能:增量規格的第一個區段是 `## Purpose` -
用一兩句話(50+ 字元,否則 `openspec validate --strict` 會報告太短)描述功能的用途。歸檔時
會將其複製到它建立的主規格中;沒有它,新的主規格會
留下 `TBD ... Update Purpose after archive` 佔位符供手動填寫。不要為現有功能的增量新增 `## Purpose` -
該規格已有 Purpose 且增量中的會被忽略。要變更
現有功能的 Purpose - 包括遺留的 `TBD` 佔位符 -
直接編輯 `<planningHome.root>/openspec/specs/<capability-path>/spec.md`。
`planningHome.root` 來自 `openspec instructions ... --json` 回應。一律使用它而非儲存庫相對路徑:
每當變更位於存放區時,它會解析到存放區 - 無論是來自 `--store`、專案 `store:` 指標或全域預設
存放區 - 否則會解析到目前儲存庫。不要嘗試推斷
適用哪種情況;該欄位已有答案。

MODIFIED 需求工作流程:
1. 在 `<planningHome.root>/openspec/specs/<capability-path>/spec.md` 中定位現有需求(與上述相同的存放區感知根目錄)
2. 複製整個需求區塊(從 `### Requirement:` 到所有情境)
3. 貼在 `## MODIFIED Requirements` 下並編輯以反映新行為
4. 確保標題文字完全符合(空白不敏感)

常見陷阱:使用 MODIFIED 搭配部分內容會在歸檔時遺失細節。
如果要新增關切而不變更現有行為,改用 ADDED。

範例(一個新功能,所以其第一個區段是 `## Purpose`):
```
# Spec Delta

## Purpose

讓使用者能夠以可攜式格式將其資料從產品中匯出。

## ADDED Requirements

### Requirement: User can export data
The system SHALL allow users to export their data in CSV format.

#### Scenario: Successful export
- **WHEN** user clicks "Export" button
- **THEN** system downloads a CSV file with all user data

## REMOVED Requirements

### Requirement: Legacy export
**Reason**: Replaced by new export system
**Migration**: Use new export endpoint at /api/v2/export
```

Specs should be testable - each scenario is a potential test case.

design.md ​

說明如何實作此變更。僅在變更需要時起草。

Structure ​

代理作為輸出格式收到的範本(templates/design.md):

md
# Design

## Context

<!-- 目前狀態與限制方法之限制條件。動機請見 proposal.md - 勿重述 -->

## Goals / Non-Goals

**Goals:**
<!-- 此設計旨在達成之目標 -->

**Non-Goals:**
<!-- 明確排除之範圍 -->

## Decisions

<!-- 關鍵設計決策,附理由與替代方案考量 -->

## Risks / Trade-offs

<!-- 已知風險與權衡 -->

Instructions ​

代理起草此文件時收到的指示(來自 schema.yaml):

md
建立說明如何實作此變更的設計文件。

何時納入 design.md(僅在任一條件適用時建立):
- 跨領域變更(多個服務/模組)或新架構模式
- 新的外部依賴或重大資料模型變更
- 安全性、效能或遷移複雜度
- 在編碼前因技術決策而受益之模糊地帶

章節:
- **Context**:僅限說明方法所需的目前狀態與限制。關於動機,請參考 proposal 而非重述(例如:"See proposal.md - Why")。
- **Goals / Non-Goals**:此設計達成與明確排除之內容。勿重述 proposal 的範圍 - 僅新增設計層面的界線。
- **Decisions**:關鍵技術選擇附理由(為何選擇 X 而非 Y)。包含每個決策考慮的替代方案。
- **Risks / Trade-offs**:已知限制、可能出錯的事項。格式:[風險] → 緩解措施
- **Migration Plan**:部署步驟、回滾策略(如適用)
- **Open Questions**:可安全延後回答而不影響規格、方法或任務分解的未知事項。若無則省略。

Open questions 用於真正可延後的未知事項,而非您跳過的決策。若問題會改變規格、所選方法或任務分解,請立即解決 - 詢問使用者而非猜測。

聚焦於架構與方法,而非逐行實作。proposal 涵蓋為什麼與做什麼;design 涵蓋怎麼做。關於動機請參考 proposal,一旦撰寫完成則參考 specs 的需求 - 若某章節僅會重述它們,請指向它們。

好的設計文件會解釋技術決策背後的「為什麼」。

tasks.md ​

將實作分解為可檢查的任務。[apply](#apply)在此追蹤進度。

Structure ​

代理作為輸出格式收到的範本(templates/tasks.md):

md
# Tasks

## 1. <!-- Task Group Name -->

- [ ] 1.1 <!-- Task description -->
- [ ] 1.2 <!-- Task description -->

## 2. <!-- Task Group Name -->

- [ ] 2.1 <!-- Task description -->
- [ ] 2.2 <!-- Task description -->

Instructions ​

代理起草此文件時收到的指示(來自 schema.yaml):

md
建立分解實作工作的任務清單。

撰寫任務前,檢查 design.md 的 Open Questions。若其中任一會改變要建置的內容,請先與使用者解決 - 不要將未聲明的假設帶入任務清單。

**重要:追蹤的任務請遵循以下範本。** apply 階段會解析
核取方塊格式以追蹤進度。僅含 `x` 的方塊視為完成,
不分大小寫且任何間距皆可,因此 `- [ x]` 也算完成。其他
任何標記,包含 `- [~]`、`- [-]` 和空的 `- []`,皆視為
未完成。沒有核取方塊的行完全不被追蹤。

指引:
- 將相關的追蹤任務分組於 ## 編號標題下
- 每個追蹤任務必須是核取方塊:`- [ ] X.Y Task description`
- 任務應該足夠小,能在一次工作階段中完成
- 按依賴順序排列任務(什麼必須先完成?)
- 追蹤可在封存前完成的實作與驗證工作。若要求的工作流程包含封存或需要此變更被封存後才能進行的工作,請將這些步驟保留為 tasks.md 末尾選擇性 `## Workflow follow-up` 區塊中的純項目符號。這些項目符號是追蹤任務進度之外的參考資訊。
- 每個任務必須說明如何驗證完成(測試、命令、可觀察行為或交付物)。將驗證內容放入該任務的核取方塊描述中。僅當檢查跨越多個實作任務的更廣泛整合或系統行為時,才使用單獨的驗證任務。
- 每個任務群組必須交付其自身工作所需的測試與文件。不要將測試或文件收集到最後一個群組中 - 當後面的群組首次執行前面群組的工作時,失敗會級聯回中間的每個群組並迫使重工。一個若其工作既不要求測試也不要求文件的群組(例如鷹架或依賴設定),則不包含兩者。最後一個群組僅用於整合檢查,不用於前面群組所欠的測試和文件。

範例:
```
# Tasks

## 1. Setup

- [ ] 1.1 Create new module structure and verify expected files are present
- [ ] 1.2 Add dependencies to package.json and verify package installation succeeds

## 2. Core Implementation

- [ ] 2.1 Implement data export function and verify the export test passes
- [ ] 2.2 Add CSV formatting utilities and verify unit tests cover quoting and delimiters
- [ ] 2.3 Document the export API in docs/export.md and verify the documented command runs as written
```

當適用時,附加工作流程後續追蹤作為純項目符號,例如:
```
## Workflow follow-up

- Archive the change after the project's review requirements are satisfied.
- Verify the archived result.
```

關於需要建置什麼,請參考 specs;關於如何建置,請參考 design。

Apply ​

從規劃到實作的交接。apply 是處理 tasks.md 的階段,而非文件。

  • 開始時機:一旦 tasks.md 存在且列出至少一個任務。
  • 追蹤:tasks.md 中的核取方塊。勾選它們即為進度記錄。
  • 結束時機:所有核取方塊皆已勾選。OpenSpec 接著會建議封存此變更。

Settings ​

apply 設定(來自 schema.yaml):

yaml
apply:
  requires: [tasks]
  tracks: tasks.md
  # instruction: shown below

Instructions ​

實作開始時發送給代理的指示(來自 schema.yaml):

md
Read context files, work through pending tasks, mark complete as you go.
Pause if you hit blockers or need clarification.

schema.yaml ​

完整的 schema.yaml,指示內文已省略。每個皆在其上方章節中完整顯示。

yaml
name: spec-driven
version: 1
description: Default OpenSpec workflow - proposal → specs → design → tasks
artifacts:
  - id: proposal
    generates: proposal.md
    description: Initial proposal document outlining the change
    template: proposal.md
    # instruction: shown in full under proposal.md above
    requires: []

  - id: specs
    generates: "specs/**/*.md"
    description: Detailed specifications for the change
    template: spec.md
    # instruction: shown in full under Delta specs above
    requires:
      - proposal

  - id: design
    generates: design.md
    description: Technical design document with implementation details
    template: design.md
    # instruction: shown in full under design.md above
    requires:
      - proposal

  - id: tasks
    generates: tasks.md
    description: Implementation checklist with trackable tasks
    template: tasks.md
    # instruction: shown in full under tasks.md above
    requires:
      - specs
      - design

apply:
  requires: [tasks]
  tracks: tasks.md
  # instruction: shown in full under Apply above