Skip to content

SDD 流程模擬

本文以對話方式模擬一個完整的規格驅動開發(SDD)流程,展示如何使用 Claude Code + OpenSpec 框架進行從需求到交付的端到端協作。

參與者

角色職責主要工具
PM需求定義、驗收標準OpenSpec proposal
UX使用者體驗設計、互動規格Figma + Figma Make + Claude Code
BEAPI 設計、後端實作審查OpenAPI + Claude Code
FE前端實作審查、元件整合Claude Code + Design System
ADM流程協調、品質關卡、跨角色協調OpenSpec + Claude Code
Agent規格生成、程式碼實作Claude Code
QA測試策略、驗收測試Test framework + Claude Code

備註: ADM(Agentic Development Master)是傳統 Scrum Master 角色的演進。在 SDD 工作流程中,ADM 專注於協調 AI-人類協作、確保規格品質,以及促進跨職能協調。

情境

功能需求: 在電商平台新增「商品願望清單」功能,讓使用者可以儲存商品並在個人檔案頁面查看願望清單。

階段 1:需求種子(PM)

背景

PM 從利益關係人處收到業務需求,並建立需求種子文件。種子應該由 PM 根據實際回饋撰寫,而不是由 AI 自動生成。

PM(Alice): 收集使用者回饋並撰寫種子文件

「根據客服工單和調查的使用者回饋,我已經為願望清單功能起草了需求種子。」

markdown
<!-- requirements/product-wishlist.md -->
---
title: 商品願望清單功能
author: PM-Alice
created: 2026-04-15
status: seed
---

# 商品願望清單功能

## 業務背景
使用者希望能夠儲存他們感興趣的商品,以便未來做購買決定。這是我們 Q1 使用者調查中第三大熱門需求(1,247 票)。

### 問題陳述
- 使用者經常在多次造訪後才決定購買,但無法追蹤先前瀏覽的商品
- 客服工單顯示每月約 200 件「如何找回之前看過的商品」詢問
- 競品分析:主要競爭對手均已提供此功能

### 預期效益
- 提升回訪率:預估增加 15% 回訪使用者
- 提升轉換率:願望清單使用者轉換率預估較一般使用者高 2.3 倍
- 降低客服負擔:減少相關工單約 80%

## 目標使用者
- 主要:已註冊會員,有明確購買意圖但需要時間考慮
- 次要:瀏覽型使用者,用於收集感興趣商品

## 使用情境

### 情境 1:加入願望清單
使用者 Amy 在瀏覽商品頁時看到一雙喜歡的鞋子,但想等薪水入帳再購買。她點擊愛心按鈕將商品加入願望清單,系統顯示「已加入願望清單」提示。

### 情境 2:查看願望清單
兩週後 Amy 登入帳號,進入個人檔案頁面點擊「我的願望清單」,看到之前收藏的鞋子。她注意到價格從 $2,800 降到 $2,380,決定立即購買。

### 情境 3:未登入使用者
訪客 Bob 瀏覽商品時想加入願望清單,點擊愛心後系統顯示登入提示。登入成功後,該商品自動加入願望清單。

### 情境 4:商品已下架
使用者 Carol 查看願望清單時,發現某商品顯示「已無法購買」標籤。她可以選擇移除該商品或保留作為參考。

## 業務規則
- 每位使用者願望清單上限:500 件商品
- 願望清單為私人資料,不對外公開
- 商品下架後保留在願望清單中 90 天,之後自動移除
- 價格變動通知:降價超過 10% 時發送 Email 通知(需使用者開啟通知設定)

## 驗收標準
- [ ] 未認證使用者點擊願望清單時導向登入,登入後自動加入
- [ ] 已認證使用者可以即時加入/移除願望清單
- [ ] 願望清單頁面支援分頁載入(每頁 20 件)
- [ ] 無法購買的商品在願望清單中以視覺標示區分
- [ ] 顯示商品加入時價格與目前價格的比較
- [ ] 願望清單為空時顯示引導文案

## 排除範圍(本次不做)
- 願望清單分享功能
- 願望清單分類/資料夾
- 多個願望清單
- 降價推播通知(僅 Email)

PM(Alice): 「我已經提交這份種子供團隊審查,再進行精煉會議。」

要點

種子文件應該由人類根據實際利益關係人回饋撰寫,而不是由 AI 從簡短提示自動生成。AI 傾向於幻覺出沒有被要求的「完整」功能集。PM 擁有需求定義的責任。

階段 1 產出工件:

工件位置負責人
需求種子文件requirements/product-wishlist.mdPM

階段 2:種子審查會議(ADM + 所有團隊成員)

背景

在精煉會議之前,ADM 召集所有團隊成員進行種子審查會議(實體或線上)。這確保每個人都理解範圍,並能當面提出疑慮。

ADM(Frank): 召開種子審查會議

「各位,我們開始願望清單功能的種子審查。請針對以下面向提出意見:

  • 需求的清晰度
  • 遺漏的驗收標準
  • 潛在的技術疑慮
  • 範圍的適當性」

BE(Bob):

「需求提到『價格變動』——我們需要即時價格追蹤,還是只顯示目前價格與加入時的價格比較?」

FE(Carol):

「對於『即時加入/移除』——我們應該澄清這是指 UI 上的樂觀更新嗎?」

UX(Diana):

「沒有提到空狀態。當使用者的願望清單是空的時候應該顯示什麼?」

QA(Eve):

「邊界案例:當願望清單中的商品從目錄中完全刪除時會發生什麼?」

ADM(Frank): 提出與現有功能的關係

「等一下,我們需要確認一件事。目前系統已經有『商品收藏』功能,這個願望清單跟收藏有什麼關係?」

PM(Alice):

「好問題。『商品收藏』是舊版功能,只是簡單的收藏列表。這次的願望清單有價格追蹤、降價提醒等進階功能。」

BE(Bob):

「那我們需要決定:是擴充現有收藏功能,還是建立全新的願望清單服務?如果是後者,舊的收藏功能要怎麼處理?」

ADM(Frank):

「這影響範圍可能超出本次需求。Alice,我們需要在提案中明確這點。選項有:

  1. 擴充現有收藏功能,加入新功能
  2. 建立新服務,保留舊收藏功能
  3. 建立新服務,逐步淘汰舊收藏功能並提供遷移路徑」

PM(Alice):

「我傾向選項 3。但需要跟產品團隊確認淘汰時程。我會在種子中加入這個決策點。」

ADM(Frank): 總結會議發現

「好的發現,團隊。Alice,請根據今天的討論更新種子,特別是與現有收藏功能的關係。我會將這些記錄為精煉會議的討論項目。」

PM(Alice): 根據會議回饋更新種子

markdown
<!-- requirements/product-wishlist.md(審查後更新)-->
---
title: 商品願望清單功能
author: PM-Alice
created: 2026-04-15
status: seed
reviewed: 2026-04-16
---

# 商品願望清單功能

## 業務背景
使用者希望能夠儲存他們感興趣的商品,以便未來做購買決定。這是我們 Q1 使用者調查中第三大熱門需求(1,247 票)。

### 問題陳述
- 使用者經常在多次造訪後才決定購買,但無法追蹤先前瀏覽的商品
- 客服工單顯示每月約 200 件「如何找回之前看過的商品」詢問
- 競品分析:主要競爭對手均已提供此功能

### 預期效益
- 提升回訪率:預估增加 15% 回訪使用者
- 提升轉換率:願望清單使用者轉換率預估較一般使用者高 2.3 倍
- 降低客服負擔:減少相關工單約 80%

## 目標使用者
- 主要:已註冊會員,有明確購買意圖但需要時間考慮
- 次要:瀏覽型使用者,用於收集感興趣商品

## 使用情境

### 情境 1:加入願望清單
使用者 Amy 在瀏覽商品頁時看到一雙喜歡的鞋子,但想等薪水入帳再購買。她點擊愛心按鈕將商品加入願望清單,系統顯示「已加入願望清單」提示。

### 情境 2:查看願望清單並發現降價
兩週後 Amy 登入帳號,進入個人檔案頁面點擊「我的願望清單」,看到之前收藏的鞋子。她注意到價格從 $2,800 降到 $2,380,決定立即購買。

### 情境 3:未登入使用者
訪客 Bob 瀏覽商品時想加入願望清單,點擊愛心後系統顯示登入提示。登入成功後,該商品自動加入願望清單。

### 情境 4:商品已下架
使用者 Carol 查看願望清單時,發現某商品顯示「已無法購買」標籤。她可以選擇移除該商品或保留作為參考。

### 情境 5:商品完全刪除
使用者 David 的願望清單中有一件商品被賣家完全刪除。系統自動移除該商品並在下次登入時顯示通知:「您的願望清單中有 1 件商品已不存在」。

## 業務規則
- 每位使用者願望清單上限:500 件商品
- 願望清單為私人資料,不對外公開
- 商品下架後保留在願望清單中 90 天,之後自動移除
- 商品完全刪除時立即從願望清單移除並通知使用者
- 價格變動通知:降價超過 10% 時發送 Email 通知(需使用者開啟通知設定)

## 驗收標準
- [ ] 未認證使用者點擊願望清單時導向登入,登入後自動加入
- [ ] 已認證使用者可以即時加入/移除願望清單(樂觀 UI 更新)
- [ ] 願望清單頁面支援分頁載入(每頁 20 件)
- [ ] 無法購買的商品在願望清單中以視覺標示區分
- [ ] 顯示商品加入時價格與目前價格的比較
- [ ] 已刪除的商品從願望清單中移除並通知
- [ ] 願望清單為空時顯示引導文案

## 與現有功能的關係
- 現有功能:商品收藏(簡單列表,無價格追蹤)
- 決策:建立新願望清單服務,逐步淘汰舊收藏功能
- 遷移計畫:待產品團隊確認淘汰時程

## 排除範圍(本次不做)
- 願望清單分享功能
- 願望清單分類/資料夾
- 多個願望清單
- 降價推播通知(僅 Email)

## 待討論問題(精煉會議)
- 空狀態設計細節
- 價格變動通知頻率上限
- 舊收藏功能遷移路徑與時程

要點

精煉前的種子審查會議能及早發現模糊和邊界案例。每個角色帶來他們的觀點:BE 問資料、FE 問互動、UX 問狀態、QA 問邊界案例。ADM 確保團隊考慮與現有功能的關係,避免功能重疊或遺漏淘汰計畫。同步會議讓團隊能即時討論和澄清,比非同步留言更有效率。

階段 2 產出工件:

工件位置負責人
更新後的種子文件requirements/product-wishlist.mdPM

階段 3:提案生成與審查(PM + ADM + 團隊)

背景

種子審查完成後,PM 使用種子作為 /openspec:proposal 的輸入,讓 Agent 生成正式提案。團隊隨後一起審查 Agent 的產出。

3.1 PM 生成提案

PM(Alice): 開啟 Claude Code

bash
claude "根據 requirements/product-wishlist.md 種子文件,
使用 /openspec:proposal 生成正式提案"

Agent: 生成提案草稿

markdown
<!-- openspec/changes/wishlist-feature/proposal.md -->
---
id: wishlist-feature
title: 商品願望清單功能
status: draft
owner: PM-Alice
created: 2026-04-16
---

# 商品願望清單功能

## 概述
為電商平台新增商品願望清單功能,讓使用者可以儲存感興趣的商品。

## 變更範圍

### 後端變更
| 檔案 | 變更類型 | 說明 |
|------|----------|------|
| `services/wishlist/` | 新增 | 願望清單微服務 |
| `api/wishlist.yaml` | 新增 | API 規格 |

### 前端變更
| 檔案 | 變更類型 | 說明 |
|------|----------|------|
| `components/WishlistButton/` | 新增 | 願望清單按鈕元件 |
| `pages/wishlist/` | 新增 | 願望清單頁面 |

## 驗收標準
(繼承自種子文件)

3.2 團隊逐行審查提案

ADM(Frank): 召開提案審查會議,投影提案內容

「各位,Agent 已經根據種子生成了提案草稿。我們現在逐行審查,有問題直接請 Agent 修改。」

ADM(Frank): 「先看『概述』區塊...」

PM(Alice):

「概述太簡略了。應該加上業務價值說明。」

ADM(Frank): 即時請 Agent 修改

bash
claude "更新提案的概述區塊,加入業務價值說明:
這是 Q1 調查第三大需求,預期提升使用者黏著度和轉換率"

Agent: 即時更新提案

ADM(Frank): 「好,繼續看『變更範圍』...」

BE(Bob):

「後端變更建議使用獨立的願望清單微服務,避免與商品服務耦合。請 Agent 補充這個架構決策。」

ADM(Frank): 即時請 Agent 修改

bash
claude "更新變更範圍的後端變更,
說明使用獨立微服務架構,並加入架構決策說明"

FE(Carol):

「前端變更缺少樂觀更新的技術需求說明。」

UX(Diana):

「我有個問題:願望清單動畫要用什麼模式?」

ADM(Frank): 「好問題。Carol 和 Diana,你們的意見?」

FE(Carol): 「建議遵循現有的『加入購物車』互動模式,保持一致性。」

ADM(Frank): 「還有一個重要議題:舊收藏功能的處理。」

BE(Bob):

「如果要淘汰舊收藏功能,我們需要在提案中加入:資料遷移計畫、API 相容性策略、淘汰時程。」

PM(Alice):

「我已經跟產品團隊確認過,他們同意 6 個月後淘汰舊功能。我們需要在這期間維持兩個系統並行。」

FE(Carol):

「前端需要加入遷移提示,引導使用者從舊收藏移動到新願望清單。這可能超出本次需求範圍。」

ADM(Frank): 「好,這是範圍外變動。我們記錄為後續任務,但提案需要說明相依性。」

ADM(Frank): 彙整修改請求

bash
claude "更新提案:
1. 前端變更加入樂觀更新技術需求
2. 新增技術決策區塊,記錄:
   - BE:獨立微服務架構
   - FE:樂觀更新模式
   - UX:遵循加入購物車動畫模式
3. 新增範圍確認區塊:
   - MVP 不包含:願望清單分類、分享願望清單
   - MVP 包含:加入/移除、清單檢視、降價提醒
4. 新增與現有功能關係區塊:
   - 舊功能:商品收藏(將於 6 個月後淘汰)
   - 資料遷移:自動將舊收藏匯入新願望清單
   - 範圍外後續任務:遷移提示 UI、舊 API 廢棄公告"

Agent: 更新所有規格

ADM(Frank): 「提案已更新完成。各位確認一下最終版本...沒問題的話,Diana 和 Bob 請在週末前完成你們的規格。」

要點

逐行審查讓團隊能即時發現問題並請 Agent 修正。審查時必須確認與現有功能的關係:是否擴充、是否淘汰、是否有範圍外的相依變動。這些決策都應記錄在提案中,範圍外任務則記錄為後續工作。

階段 3 產出工件:

工件位置負責人
OpenSpec 提案openspec/changes/wishlist-feature/proposal.mdPM + Agent

階段 4:API First 設計(BE + Agent + 團隊審查)

背景

API First 設計流程。根據階段 3 產生的 OpenSpec 提案,BE 依照 API 設計原則請 Agent 產生 OpenAPI 文件,並提交到 API 管理平台(Git-based)供團隊審查。

4.1 BE 產生 API 規格

BE(Bob): 開啟 Claude Code

「根據提案中的需求,我需要設計願望清單 API。」

bash
claude "根據 openspec/changes/wishlist-feature/proposal.md,
依照我們的 API 設計原則(RESTful、一致性命名、分頁標準)
建立 OpenAPI 3.0 規格,包含:
- GET /wishlist(分頁)
- POST /wishlist(加入商品)
- DELETE /wishlist/{productId}(移除商品)
包含帶有降價指示器的 WishlistItem schema。"

Agent: 生成 OpenAPI 規格

yaml
# specs/api/wishlist-api.yaml
openapi: 3.0.3
info:
  title: Wishlist API
  version: 1.0.0
  description: 商品願望清單 API

paths:
  /api/v1/wishlist:
    get:
      summary: 取得願望清單
      operationId: getWishlist
      parameters:
        - name: page
          in: query
          schema:
            type: integer
            default: 1
        - name: limit
          in: query
          schema:
            type: integer
            default: 20
      responses:
        '200':
          description: 成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WishlistResponse'

    post:
      summary: 加入願望清單
      operationId: addToWishlist
      requestBody:
        content:
          application/json:
            schema:
              type: object
              required:
                - productId
              properties:
                productId:
                  type: string
      responses:
        '201':
          description: 加入成功

  /api/v1/wishlist/{productId}:
    delete:
      summary: 從願望清單移除
      operationId: removeFromWishlist
      parameters:
        - name: productId
          in: path
          required: true
          schema:
            type: string
      responses:
        '204':
          description: 移除成功

components:
  schemas:
    WishlistResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: '#/components/schemas/WishlistItem'
        pagination:
          $ref: '#/components/schemas/Pagination'

    WishlistItem:
      type: object
      properties:
        productId:
          type: string
        productName:
          type: string
        imageUrl:
          type: string
        currentPrice:
          type: number
        originalPrice:
          type: number
        priceDropped:
          type: boolean
        isAvailable:
          type: boolean
        addedAt:
          type: string
          format: date-time

4.2 提交到 API 管理平台

BE(Bob): 提交 API 規格到 Git-based API 管理平台

bash
git add specs/api/wishlist-api.yaml
git commit -m "feat(api): add wishlist API specification"
git push origin feature/wishlist-api

BE(Bob): 「API 規格已提交,請 ADM 安排審查會議。」

4.3 團隊逐行審查 API 規格

ADM(Frank): 召開 API 審查會議(實體或線上),投影 API 規格

「各位,包括 Alice,我們來逐行審查願望清單 API 規格。有問題直接請 Agent 修改。」

ADM(Frank): 「先看 GET /wishlist endpoint...」

PM(Alice):

「分頁參數沒問題。但我想確認,刪除商品的通知機制會在哪裡處理?」

BE(Bob):

「刪除商品的通知會透過事件驅動架構處理,不在這個 API 範圍內。我請 Agent 在 API 文件加上備註說明。」

ADM(Frank): 即時請 Agent 修改

bash
claude "在 wishlist-api.yaml 的 info.description 加入備註:
刪除商品通知透過事件驅動架構處理,不在此 API 範圍"

ADM(Frank): 「繼續看 WishlistItem schema...」

FE(Carol):

「priceDropped 布林值很好用。但 addedAt 欄位的格式需要明確說明是 ISO 8601。」

ADM(Frank): 即時請 Agent 修改

bash
claude "更新 WishlistItem schema 的 addedAt 欄位,
加入 example 說明 ISO 8601 格式"

QA(Eve):

「缺少錯誤回應的 schema,這會影響測試設計。」

ADM(Frank): 即時請 Agent 修改

bash
claude "新增 ErrorResponse schema,並在所有 endpoint 加入
400、401、404、500 的錯誤回應定義"

Agent: 更新所有規格

UX(Diana):

「API 能支援空狀態和分頁載入的 UI 需求。我這邊沒問題。」

ADM(Frank): 「API 規格更新完成。各位確認最終版本...」

BE(Bob): 確認後合併

bash
git commit -m "feat(api): finalize wishlist API after review"
git push && gh pr merge --squash

要點

逐行審查 API 規格讓團隊能即時發現問題並請 Agent 修正。PM 確保 API 滿足業務需求,FE 確保回應格式可用,QA 確保錯誤處理完整。所有修改在會議中完成,避免來回修改。

階段 4 產出工件:

工件位置負責人
OpenAPI 規格specs/api/wishlist-api.yamlBE + Agent

階段 5:UX 設計(UX + Agent)

背景

有了確定的 API 契約,UX 設計使用者體驗,並建立詳細的互動規格,這些規格將指導 AI 實作和人工審查。

5.1 UX 設計線框圖

UX(Diana): 設計願望清單功能的 UI

「根據 API 規格和提案需求,我需要設計:

  1. 商品卡片上的願望清單按鈕(愛心圖示)
  2. 個人檔案中的願望清單頁面(格狀佈局)
  3. 空狀態畫面」

Diana: 完成線框圖設計後,記錄互動規格

5.2 UX 標註驗收標準

UX(Diana): 開啟 Claude Code

「根據我的線框圖和已確定的 API 規格,我需要建立帶有驗收標準的互動規格。」

bash
claude "為願望清單按鈕和格狀元件建立 UX 互動規格。
參考 specs/api/wishlist-api.yaml 的回應格式。
包含 Given/When/Then 情境:
- 加入願望清單(已認證)
- 加入願望清單(未認證)
- 從願望清單移除"

Agent: 生成互動規格

markdown
<!-- specs/ux/wishlist-interaction.md -->
---
title: 願望清單互動規格
type: ux-spec
api-ref: specs/api/wishlist-api.yaml
components:
  - WishlistButton
  - WishlistGrid
  - EmptyState
---

# 願望清單互動規格

## 願望清單按鈕互動

### 狀態定義
| 狀態 | 視覺效果 | 行為 |
|------|----------|------|
| 未儲存 | 空心愛心 | 點擊時填滿動畫 |
| 已儲存 | 實心愛心(紅色) | 點擊時縮小淡出動畫 |
| 載入中 | 愛心 + 轉圈 | 點擊停用 |

### 驗收標準(Given/When/Then)

**情境:使用者將商品加入願望清單**
- Given:使用者已認證且商品不在願望清單中
- When:使用者點擊願望清單按鈕
- Then:按鈕立即顯示已儲存狀態(樂觀更新)
- And:「已加入願望清單」吐司訊息出現
- And:背景呼叫 POST /api/v1/wishlist

**情境:未認證使用者嘗試儲存**
- Given:使用者未認證
- When:使用者點擊願望清單按鈕
- Then:登入模態視窗出現
- And:成功登入後,商品自動儲存

UX(Diana): 「好的。讓我對照線框圖審查這份規格...狀態定義相符,API 參考也正確。我會把這份規格提交到儲存庫。」

要點

UX 規格參考已確定的 API 規格,確保前端互動與後端契約一致。Given/When/Then 格式使其可被機器解析,並可直接用於測試生成。

階段 5 產出工件:

工件位置負責人
線框圖 / 設計稿FigmaUX
UX 互動規格specs/ux/wishlist-interaction.mdUX + Agent

階段 6:實作(Agent + FE/BE 審查)

背景

有了核准的提案,Agent 根據規格實作程式碼。人工審查者(BE/FE)驗證是否符合規格和最佳實踐。

6.1 後端實作

ADM(Frank): 「Bob,提案已核准。請指導 Agent 進行後端實作。」

BE(Bob): 開啟 Claude Code

bash
claude "根據 openspec/changes/wishlist-feature/proposal.md
使用 /openspec:apply 實作後端 API"

Agent: 生成後端實作

typescript
// services/wishlist/wishlist.service.ts
import { Injectable } from '@nestjs/common';
import { WishlistRepository } from './wishlist.repository';

@Injectable()
export class WishlistService {
  constructor(private readonly repository: WishlistRepository) {}

  async getWishlist(userId: string, page: number, limit: number) {
    const items = await this.repository.findByUserId(userId, {
      skip: (page - 1) * limit,
      take: limit,
    });

    const total = await this.repository.countByUserId(userId);

    return {
      items: items.map(this.toWishlistItem),
      pagination: {
        page,
        limit,
        total,
        totalPages: Math.ceil(total / limit),
      },
    };
  }

  async addToWishlist(userId: string, productId: string) {
    const existing = await this.repository.findOne(userId, productId);
    if (existing) {
      return existing;
    }
    return this.repository.create({ userId, productId });
  }

  async removeFromWishlist(userId: string, productId: string) {
    await this.repository.delete(userId, productId);
  }
}

6.2 BE 審查

BE(Bob): 審查 Agent 產出的程式碼

「讓我看看 WishlistService 的實作...getWishlist 方法有正確實作分頁邏輯,addToWishlist 有處理重複加入的情況。不過我注意到缺少快取策略,這在高流量時會有效能問題。」

BE(Bob): 「核心實作符合 API 規格。我會開一個後續任務處理快取優化。核准合併。」

6.3 前端實作

ADM(Frank): 「Carol,後端準備好了。請指導 Agent 進行前端實作。」

FE(Carol): 開啟 Claude Code

bash
claude "根據 specs/ux/wishlist-interaction.md
使用設計系統的 Button 和 Card 元件實作前端元件"

Agent: 生成前端實作

tsx
// components/WishlistButton/WishlistButton.tsx
import { useState, useCallback } from 'react';
import { Heart } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useWishlist } from '@/hooks/useWishlist';
import { useAuth } from '@/hooks/useAuth';
import { cn } from '@/lib/utils';

interface WishlistButtonProps {
  productId: string;
  className?: string;
}

export function WishlistButton({ productId, className }: WishlistButtonProps) {
  const { isAuthenticated, openLoginModal } = useAuth();
  const { isInWishlist, toggle, isLoading } = useWishlist(productId);
  const [isAnimating, setIsAnimating] = useState(false);

  const handleClick = useCallback(async () => {
    if (!isAuthenticated) {
      openLoginModal({ onSuccess: () => toggle() });
      return;
    }

    setIsAnimating(true);
    await toggle();
    setTimeout(() => setIsAnimating(false), 300);
  }, [isAuthenticated, toggle, openLoginModal]);

  return (
    <Button
      variant="ghost"
      size="icon"
      onClick={handleClick}
      disabled={isLoading}
      className={cn(
        'transition-transform',
        isAnimating && 'scale-125',
        className
      )}
      aria-label={isInWishlist ? '從願望清單移除' : '加入願望清單'}
    >
      <Heart
        className={cn(
          'h-5 w-5 transition-colors',
          isInWishlist ? 'fill-red-500 text-red-500' : 'text-gray-400'
        )}
      />
    </Button>
  );
}

6.4 FE 審查

FE(Carol): 對照 UX 規格審查程式碼

「WishlistButton 的狀態切換邏輯正確,樂觀更新有實作。aria-label 也有加上,符合無障礙標準。不過我發現缺少 API 失敗時的錯誤狀態處理,這會影響使用者體驗。」

FE(Carol): 「核心互動符合 UX 規格。我請 Agent 補上錯誤處理後就可以合併。」

要點

人工審查是必要的。Agent 快速生成程式碼,但人類驗證是否正確符合規格,並捕捉 Agent 可能遺漏的邊界案例。

階段 6 產出工件:

工件位置負責人
後端程式碼services/wishlist/Agent + BE
前端程式碼components/WishlistButton/Agent + FE

階段 7:測試(QA + Agent)

背景

QA 從規格生成測試案例並執行。UX 規格中的 Given/When/Then 格式直接對應到測試情境。

7.1 QA 請求 Agent 生成測試

ADM(Frank): 「Eve,實作完成了。請生成並執行 E2E 測試。」

QA(Eve): 開啟 Claude Code

bash
claude "根據 proposal.md 和 wishlist-interaction.md
生成 E2E 測試案例"

Agent: 生成測試案例

typescript
// tests/e2e/wishlist.spec.ts
import { test, expect } from '@playwright/test';

test.describe('商品願望清單功能', () => {
  test.beforeEach(async ({ page }) => {
    await page.goto('/products/test-product-1');
  });

  test('已認證使用者可以將商品加入願望清單', async ({ page }) => {
    // Given:使用者已認證
    await page.evaluate(() => {
      localStorage.setItem('token', 'test-token');
    });
    await page.reload();

    // When:點擊願望清單按鈕
    const wishlistBtn = page.getByLabel('加入願望清單');
    await wishlistBtn.click();

    // Then:按鈕顯示已儲存狀態
    await expect(wishlistBtn).toHaveAttribute('aria-label', '從願望清單移除');

    // And:成功吐司訊息出現
    await expect(page.getByText('已加入願望清單')).toBeVisible();
  });

  test('未認證使用者點擊願望清單時看到登入模態視窗', async ({ page }) => {
    // Given:使用者未認證
    await page.evaluate(() => {
      localStorage.removeItem('token');
    });

    // When:點擊願望清單按鈕
    await page.getByLabel('加入願望清單').click();

    // Then:登入模態視窗出現
    await expect(page.getByRole('dialog')).toBeVisible();
    await expect(page.getByText('請登入')).toBeVisible();
  });

  test('願望清單顯示降價指示器', async ({ page }) => {
    // Given:使用者有已降價的願望清單商品
    await page.goto('/wishlist');

    // Then:降價標籤可見
    await expect(page.getByText('價格已降')).toBeVisible();
  });
});

7.2 QA 執行測試

QA(Eve): 「執行 E2E 測試中...」

bash
npx playwright test tests/e2e/wishlist.spec.ts

# 結果
Running 3 tests using 1 worker
 已認證使用者可以將商品加入願望清單 (1.2s)
 未認證使用者點擊願望清單時看到登入模態視窗 (0.8s)
 願望清單顯示降價指示器 (0.9s)

3 passed (3.5s)

QA(Eve): 「所有測試通過。功能符合驗收標準。」

ADM(Frank): 「太好了。讓我們進行部署。」

要點

測試從規格生成,確保它們驗證的是實際需求。Given/When/Then 格式從需求到測試建立了直接的可追溯性。

階段 7 產出工件:

工件位置負責人
E2E 測試案例tests/e2e/wishlist.spec.tsAgent + QA
測試報告CI/CD 系統QA

階段 8:歸檔(PM + ADM + Agent)

背景

成功部署後,提案被歸檔。這更新了單一事實來源並建立了審計軌跡。

8.1 PM 請求歸檔

PM(Alice): 「功能已上線並在生產環境驗證完成。是時候歸檔了。」

bash
claude "願望清單功能通過所有測試並已部署,
使用 /openspec:archive 歸檔這個變更"

8.2 Agent 執行歸檔

Agent: 執行歸檔流程

bash
# Agent 執行歸檔流程
1. 更新提案狀態為 archived
2. 將規格合併到 specs/ 目錄
3. 更新 CHANGELOG.md
4. 建立 git tag
markdown
<!-- openspec/changes/wishlist-feature/proposal.md -->
---
status: archived
archived_at: 2026-04-26
deployed_version: v2.3.0
---

ADM(Frank): 「歸檔完成。團隊辛苦了。願望清單功能現在是我們生產規格的一部分了。」

階段 8 產出工件:

工件位置負責人
歸檔後的提案openspec/archive/wishlist-feature/Agent
合併後的規格specs/ 目錄Agent
CHANGELOG 更新CHANGELOG.mdAgent

完整工件清單

階段工件位置負責人
1需求種子文件requirements/PM
2更新後的種子文件requirements/PM
3OpenSpec 提案openspec/changes/PM + Agent
4OpenAPI 規格specs/api/BE + Agent
5線框圖 / 設計稿FigmaUX
5UX 互動規格specs/ux/UX + Agent
6後端程式碼services/Agent + BE
6前端程式碼components/Agent + FE
7E2E 測試案例tests/e2e/Agent + QA
8歸檔後的提案openspec/archive/Agent
8合併後的規格specs/Agent

流程摘要

Agentic Development Master(ADM)的角色

ADM 作為 SDD 交響樂團的指揮:

傳統 Scrum MasterAgentic Development Master
主持儀式協調 AI-人類協作
移除阻礙確保規格品質與完整性
追蹤速度監控 AI 產出品質關卡
指導團隊 Scrum指導團隊脈絡工程
保護團隊免受干擾協調跨角色交接

相關文件