# /generate-bdd — Sinh BDD Feature Files

## Gate
{{include:steps/gate.md}}

## Context
{{include:steps/context-loader.md}}

> **Proposal của tester (input tuỳ chọn):** trước khi sinh, quét `{paths.bdd_proposals_dir}/` (mặc định `{spec_source}/feedback/bdd-proposals/`) tìm `{UC-ID}-*.md`. Với mỗi proposal:
> - `Status: accepted` (PO/Dev đã duyệt) → chèn scenario vào `.feature` của UC (giữ `@trace`), rồi **lưu trữ**: chuyển file sang `{paths.bdd_proposals_dir}/archived/` + đặt `Status: incorporated`, và **commit + push** spec repo để gỡ khỏi feedback chung.
> - `Status: proposed`/`rejected` (hoặc thiếu `Status`) → **bỏ qua**, để nguyên cho PO/Dev xử lý (KHÔNG tự đoán, KHÔNG tự đưa vào).
> Bỏ qua sạch nếu folder rỗng.

---

## Sub-Agent Mode — dùng state từ payload *(nếu `_agent_mode`)*

*Chỉ khi Gate Step 0 phát hiện `_agent_mode: true` (đang chạy như sub-agent do orchestration spawn).* Orchestrator (session chính) đã chạy các Guard + chọn platform + nạp design-spec **một lần**; sub-agent KHÔNG lặp lại:
- `active_platform` = `payload.active_platform` → **bỏ qua** Platform Selection / Service Detection.
- `design_coverage` = `payload.design_coverage` → **bỏ qua** "Design Spec — Gate & Load"; nếu rỗng thì chỉ phủ Wireframe PRD.
- **Bỏ qua** Guard "PRD đã duyệt" + Guard Design-Spec bên dưới (orchestrator đã kiểm).
- Đi thẳng tới UC Decomposition + Generate cho `payload.uc_id`, dùng `design_coverage` để phủ Screen States + AC-UI.

---

## Guard — PRD đã duyệt chưa

Đọc `| **Status** |` từ bảng Metadata của PRD nguồn:
- `Status: approved` → tiếp tục bình thường.
- `Status: draft` (hoặc khác `approved`) → **CHECKPOINT cảnh báo mềm** (không chặn cứng — cho phép prototype):
  ```
  ⚠️  PRD đang ở Status: {status} (chưa duyệt). BDD sinh từ PRD chưa chốt có thể phải làm lại.
     Khuyến nghị: PO review xong đặt `| **Status** | approved |` rồi mới sinh BDD.
     Vẫn sinh BDD bây giờ? (Y/N)
  ```
  Chỉ tiếp tục khi người dùng chọn Y.

---

## Phát hiện Repo Mode

Sau khi nạp context, xác định chế độ hoạt động:

- **Spec repo mode**: `project-context.yaml` KHÔNG có section `services` HOẶC `setup.mode: spec`
- **Umbrella mode**: `project-context.yaml` CÓ section `services` VÀ `setup.mode: umbrella`

→ Spec repo mode → tới **Platform Selection** bên dưới (bỏ qua Service Detection)
→ Umbrella mode → tới **Service Detection** bên dưới (bỏ qua Platform Selection)

---

## Platform Selection (chỉ Spec Repo Mode)

*Bỏ qua section này nếu đang chạy umbrella mode.*

Hỏi người dùng chọn platform target:

```
BDD này dành cho platform nào?
  1. web    — FE/Web (React, Next.js, Angular, Vue, Nuxt)
  2. app    — Mobile (Flutter, React Native, iOS, Android)
  3. system — System/BE BDD (tổng hợp từ web + app BDD có sẵn)
```

Chờ người dùng chọn. Set `active_platform` = giá trị đã chọn.

**Output path (spec repo mode):**
`{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC{N}-{slug}.feature`

**Từ vựng theo platform:**

| Platform | "user action" | "type/input" | "observe" | "navigate" |
|---|---|---|---|---|
| web | "clicks" | "types into" / "enters" | "sees" / "the page shows" | "navigates to" / "goes to" |
| app | "taps" | "enters" / "inputs" | "sees" / "the screen shows" | "navigates to" / "opens" |
| system | N/A — dùng business event | — | "the system returns" / "receives response" | — |

---

## System BDD Synthesis (active_platform = system)

*Chỉ áp dụng khi platform = system. Bỏ qua với web và app.*

### Step S0 — Brownfield Check

Kiểm tra bảng Metadata của PRD nguồn tìm `| **API Source** | existing |`.

**Nếu `API Source: existing`:**
- API contract đã được PO ghi trong phần "Existing API Contract" của PRD.
- **Skip Steps S1–S3** — không cần scan FE/App BDD, không cần conflict resolution.
- Dùng bảng "Existing API Contract" trong PRD làm contract input cho Step S4.
- Set `# @trace.api_source: existing` trong header của file system BDD được gen.

**Nếu `API Source` không có hoặc không phải `existing`:**
- Tiếp tục Steps S1–S3 (normal synthesis flow).

---

### Step S1 — Scan các BDD FE/App có sẵn

Tìm các BDD có sẵn cho TICKET-ID này:
- Web BDD: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/web/{TICKET-ID}-*.feature`
- App BDD: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/app/{TICKET-ID}-*.feature`

Phân loại feature:

| Điều kiện | Mode |
|---|---|
| Tìm thấy cả web + app BDD | **Multi-platform** — tổng hợp từ cả hai |
| Chỉ tìm thấy web BDD | **Web-only** — tổng hợp từ web |
| Chỉ tìm thấy app BDD | **App-only** — tổng hợp từ app |
| Không tìm thấy FE/App BDD | **Backend-only** — gen trực tiếp từ PRD |

---

### Step S2 — Trích contract kỳ vọng theo từng platform

Với mỗi file BDD tìm thấy, trích:
- **Triggers**: hành động người dùng nào gọi backend? (map sang event "request" logic)
- **Expected response data**: mỗi mệnh đề `Then` cần field/shape gì từ hệ thống?
- **Error signals**: backend phải báo hiệu các trạng thái lỗi nào?
- **Business rules**: mỗi platform giả định hệ thống thực thi các invariant nào?

---

### Step S3 — Cross-Platform Conflict Check (chỉ multi-platform mode)

*Bỏ qua nếu web-only, app-only, hoặc backend-only.*

So sánh các contract đã trích giữa các platform. Gắn cờ conflict nếu bất kỳ cái nào khác nhau:

| Loại conflict | Ví dụ |
|---|---|
| **Response shape mismatch** | Web mong `{ token, redirect_url }`, App mong `{ token, user_profile }` |
| **Error semantics mismatch** | Web mong HTTP 423 cho lock, App mong custom error code `ACC_LOCKED` |
| **Business rule contradiction** | Web BDD nói "lock sau 5 lần", App BDD nói "lock sau 3 lần" |
| **Data field conflict** | Web mong `expires_in: seconds`, App mong `expires_at: ISO timestamp` |

**Nếu phát hiện conflict → CHECKPOINT (bắt buộc, không bỏ qua được):**

```
⚠️  CROSS-PLATFORM CONTRACT CONFLICT
──────────────────────────────────────────────────────────────────
Feature : {TICKET-ID} — {UC name}

Conflict 1: Response shape mismatch
  Web BDD (Then): user sees dashboard → implies { token, redirect_url }
  App BDD (Then): app navigates to HomeScreen → implies { token, user_profile }

Resolution options:
  A — Union response
      BE trả về tất cả field: { token, redirect_url, user_profile }
      Client bỏ qua field không dùng. Đơn giản, hơi over-fetch.
  B — Platform hint trong request
      Client gửi X-Platform: web|app trong header, BE tuỳ biến response.
      Response gọn hơn, nhiều BE logic hơn.
  C — Endpoint riêng
      POST /auth/login/web  và  POST /auth/login/app
      Linh hoạt tối đa, nhiều endpoint phải bảo trì hơn.
  D — Custom: mô tả cách của bạn
──────────────────────────────────────────────────────────────────
Chọn resolution cho mỗi conflict (A/B/C/D):
```

Chờ PO giải quyết từng conflict. Ghi mỗi quyết định thành annotation `# @system.resolution:` trong file system BDD được gen.

**Nếu không có conflict → tới Step S4 trực tiếp.**

---

### Step S4 — Sinh các scenario System BDD

Sinh scenario dựa trên mode và các conflict đã giải quyết:

- **Multi-platform**: tổng hợp từ cả contract web + app, áp dụng các resolution đã chọn
- **Web-only / App-only**: suy ra từ contract của một platform
- **Backend-only**: suy ra trực tiếp từ AC/BR của PRD dùng ngôn ngữ business event (không phải HTTP)

Từ vựng step của System BDD (luôn dùng — bất kể từ vựng FE/App):
- Triggers: "the system receives {event}" / "a {actor} submits {action}"
- Assertions: "the system returns {data}" / "the system signals {error}" / "the system stores {state}"
- KHÔNG dùng từ UI (click, tap, see, navigate) trong system BDD

**Nếu multi-platform với resolution A (union):**
- System BDD thể hiện contract response đầy đủ: tất cả field từ mọi platform
- Thêm comment: `# @system.resolution: union — clients receive all fields`

**Nếu resolution B (platform hint):**
- Viết `Scenario Outline` riêng dùng Examples table cho biến thể response `web` vs `app`
- Thêm comment: `# @system.resolution: platform-hint — X-Platform header determines response shape`

**Nếu resolution C (endpoint riêng):**
- Viết Scenario riêng cho mỗi endpoint
- Ghi rõ việc tách endpoint trong phần SCOPE

---

## Service Detection (chỉ Umbrella Mode)

*Bỏ qua section này nếu đang chạy spec repo mode.*

Routing service là **domain-keyed** và **context-loader (Bước 1.5) đã phân giải sẵn** từ `@trace.domain`/Domain của PRD — KHÔNG re-resolve ở đây, chỉ dùng lại các biến đã set:
- `active_service` = `services.{domain}.path` (path submodule, vd `user-service/`) — hoặc `"unresolved"` nếu domain không khớp entry nào, hoặc bỏ trống ở single-service.
- `active_module` = module của service (`services.{domain}.module`, đã override `tech_stack.module` ở Bước 1.5) — dùng cho từ vựng bên dưới.

Chỉ cần kiểm tra trạng thái đã phân giải:

| Trạng thái (từ context-loader) | Hành động |
|---|---|
| `active_service` đã phân giải thành path service | Tiếp tục với `active_module` đã set. |
| `active_service = "unresolved"` (có section `services` nhưng domain PRD không khớp entry nào) | **DỪNG**, báo: "Domain `{domain}` của PRD không khớp service nào trong `services:` của project-context.yaml — bổ sung mapping rồi chạy lại." (Không đoán/hỏi tay — domain là khoá định danh, lệch là lỗi cấu hình cần sửa ở SoT.) |
| Single-service (không có section `services`) | `active_module = tech_stack.module` (đã set ở Bước 6.5). Tiếp tục. |

**Output path (umbrella mode):** `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC{N}-{slug}.feature`

*(Không thêm subfolder theo service: feature-package đã domain-scoped sẵn ở `{domain}/`, mà service route 1-1 theo domain — nên thêm subfolder service sẽ chỉ lặp lại domain. `active_service` chỉ dùng cho `service_root`/từ vựng, KHÔNG vào path spec.)*

**Từ vựng theo platform** — điều chỉnh cách viết step BDD theo `active_module`:

| Platform type | Modules | "click" | "type" | "see" | "navigate" |
|---|---|---|---|---|---|
| Web | react, nextjs, vue, nuxt, angular | "clicks" | "types into" / "enters" | "sees" / "the page shows" | "navigates to" / "goes to" |
| Mobile | flutter, react-native, ios-swiftui, android-compose | "taps" | "enters" / "inputs" | "sees" / "the screen shows" | "navigates to" / "opens" |
| Backend / API | java-spring, golang, dotnet, php-laravel | *(không có UI step — dùng)* "submits a request" / "calls the API" | — | "receives response" / "the system returns" | — |

Áp dụng từ vựng này âm thầm khi viết step Gherkin. KHÔNG trộn từ web và mobile trong cùng một file feature.

---

## Design Spec — Gate & Load (chỉ FE/App)

*Chỉ chạy khi target platform là FE/App — spec mode: `active_platform ∈ {web, app, app-ios, app-android}`; umbrella mode: `active_module` là module FE/App (react/nextjs/vue/nuxt/angular/flutter/react-native/ios-swiftui/android-compose). Bỏ qua HOÀN TOÀN với `system` và backend/brownfield.*

**1. Định vị design-spec của platform:**
`{paths.specs_dir}/{domain}/{prd-slug}/design-spec/{TICKET-ID}-design-spec-{active_platform}-{slug}.md`
- `app-ios`/`app-android` không có bản riêng → fallback bản `-app-`.

**2. Guard — sign-off & độ tươi (cảnh báo MỀM, đồng bộ Guard PRD — không chặn cứng):**
Đọc `| **Status** |` và `| **Built from PRD** |` từ Metadata design-spec.
- Không tìm thấy file, HOẶC `Status ≠ approved`, HOẶC design-spec còn màn ❌ Missing, HOẶC `Built from PRD` ≠ `| **Version** |` của PRD hiện tại (design-spec **lỗi thời** so với PRD) → CHECKPOINT:
  ```
  ⚠️  Design Spec cho {active_platform} chưa sẵn sàng (Status: {status} / không có / còn màn thiếu Figma / lỗi thời: dựng từ PRD v{old}, PRD giờ v{new}).
     BDD FE/App nên sinh từ design-spec đã approved & cập nhật để phủ đúng Screen States + AC-UI.
     Khuyến nghị: hoàn tất / cập nhật design-spec (chạy lại /generate-design-spec nếu PRD đã đổi) rồi mới sinh BDD.
     Vẫn sinh BDD bây giờ? (Y/N)
  ```
  Chỉ tiếp khi chọn Y. Nếu Y mà KHÔNG có design-spec → bỏ qua bước 3 (chỉ phủ Wireframe PRD §4b).
- `Status: approved` VÀ `Built from PRD` khớp PRD hiện tại → nạp design-spec, sang bước 2.5.

**2.5. Sanity-scan nội dung design-spec** (lớp soi độc lập — D1; soi nhanh ngay tại chỗ đã mở file, trước khi dùng):
Quét tìm cờ đỏ; nếu có → **cảnh báo mềm** (liệt kê + hỏi "Vẫn dùng design-spec này? (Y/N)"):
- Màn nào thiếu state `loading`/`error`/`empty`.
- AC-UI nào mơ hồ, không testable ("looks good" / "đẹp" / không pass-fail rõ).
- Component còn `[NEW]` / `[TODO]` (chưa chốt với designer).
- Còn `❌ Missing` frame (lẽ ra Status đã `draft` — approved mà vẫn Missing là bất thường).
Bắt lỗi design-spec **ngay trước khi nó lan xuống BDD**. Chọn N → quay lại hoàn thiện design-spec; chọn Y → sang bước 3.

**3. Trích coverage từ design-spec** (lưu thành `design_coverage`, dùng ở UC Decomposition + Coverage Matrix):
- **Screen States** ≠ `default` cho mỗi màn: `loading`, `error`, `empty`, `success` (cái nào có).
- **AC-UI behavioral**: giữ AC-UI mà cột `Verified by` là **PO/QA** và mô tả outcome quan sát được (lỗi + đường khôi phục, empty state + CTA, gesture điều hướng, có loading state). **LOẠI AC-UI visual thuần** (khớp Figma trong dung sai, tương phản WCAG, màu/pixel/animation — thường `Verified by: Designer`): Designer/QA review riêng, KHÔNG đưa vào Gherkin (giữ R3/R5/R6).
- **Dedup**: nếu một Screen State / AC-UI đã trùng một AC nghiệp vụ của PRD → không tạo SC mới, chỉ ghi nhận đã phủ.

---

## Orchestration Check

*Bỏ qua section này nếu đã ở sub-agent mode (Step 0 của Gate đã kích hoạt).*

Sau khi nạp context, kiểm tra PRD target có đủ lớn để cần sub-agent không:

1. Đếm các heading `#### {TICKET-ID}-UC` trong PRD → **UC count**.
2. Đếm tổng số dòng trong PRD → **line count**.
3. Nếu **UC count > 3** HOẶC **line count > 300**:
   - Chuyển sang orchestration mode — theo `steps/spawn-agent.md`.
   - Session chính trở thành orchestrator: spawn 1 sub-agent cho mỗi UC.
   - Mỗi sub-agent chạy `/generate-bdd` với payload `_agent_mode: true`.
   - Thu thập kết quả và hiện report đã merge.
   - **KHÔNG tiếp tục các bước bên dưới.**
4. Nếu UC count ≤ 3 VÀ line count ≤ 300 → tiếp tục Version Check bên dưới (single-session mode).

---

## Sub-Agent Return Format

*Section này áp dụng khi chạy như sub-agent (Gate Step 0 phát hiện `_agent_mode: true`).*

Sau khi sinh tất cả file `.feature` và `.tsv` cho UC được giao, trả về JSON kết quả có cấu trúc (theo `steps/spawn-agent.md` Step E):

```json
{ "uc_id": "{TICKET-ID}-UC{N}", "files_created": ["path/to/file1", "path/to/file2"], "status": "success | error", "errors": [] }
```

---

## Version Check

Trước khi sinh, kiểm tra các file `.feature` có sẵn cho PRD này:

1. Phân giải search path theo mode:
   - **Spec repo mode**: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC*.feature`
   - **Umbrella mode**: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC*.feature`
2. Đọc `| **Version** |` hiện tại của PRD từ metadata (vd: `1.2`).

**Nếu không có file feature nào** → gen mới, tiếp tục bình thường. Dùng version PRD làm `@trace.prd_version`.

**Nếu tìm thấy file feature có sẵn**:
- Đọc `# @trace.prd_version:` từ header file feature có sẵn.
- So với version PRD hiện tại.
- Nếu **giống** → hỏi: "BDD đã sinh từ PRD v{version}. Gen lại? (Y/N)"
- Nếu **khác** (PRD đã cập nhật):
  1. Đọc `# Change Log` từ PRD — trích tất cả row mới hơn `@trace.prd_version` của BDD hiện có. **Nếu `@trace.prd_version` của BDD CŨ HƠN row cũ nhất còn trong bảng** (lịch sử đã bị cắt sang `changelog/`) → không thấy đủ diff → **khuyến nghị F** (gen lại toàn bộ), đừng tin Y một phần.
  2. Hiện CHECKPOINT:
     ```
     ⚠️  Phát hiện PRD version drift
     BDD được sinh từ PRD v{old}
     PRD giờ ở v{new}

     Thay đổi kể từ v{old}:
       {changelog rows}

     Options:
       Y — chỉ cập nhật các scenario bị ảnh hưởng
       F — gen lại toàn bộ scenario
       N — huỷ
     ```
  3. Tiếp tục theo lựa chọn của người dùng. **Nếu changelog row không nêu rõ UC/AC/BR bị đổi (mơ hồ) → khuyến nghị F** (gen lại toàn bộ) thay vì Y, để khỏi sót scenario bị ảnh hưởng (lưới an toàn — không chắc đổi ở đâu thì quét rộng).

---

## BDD Writing Rules (R1-R10 — enforce nghiêm)

| Rule | Name | Yêu cầu |
|------|------|-------------|
| R1 | Given/When/Then Semantics | Given=state, When=action, Then=outcome. Mỗi SC cần đủ G/W/T. |
| R2 | One Behavior Per Scenario | 1 SC = 1 behavior. KHÔNG chain When→Then→When→Then. |
| R3 | Ubiquitous Language | KHÔNG dùng UI selector / tên API / tech term trong step. |
| R4 | Outside-in Naming | Tên SC mô tả business outcome. Không "click" / "(Case X)" / tên component. |
| R5 | Declarative over Imperative | Mô tả WHAT (ý định nghiệp vụ), KHÔNG phải HOW (cơ chế UI). |
| R6 | Observable Outcomes Only | Then khẳng định outcome quan sát được. Không phải trạng thái UI trung gian / internal state. |
| R7 | Key Examples / Concrete | Dùng giá trị cụ thể. Không "valid data" mơ hồ. |
| R8 | Independence | SC chạy độc lập. Không phụ thuộc state từ SC khác. |
| R9 | Test Data Completeness | Data table có đủ field để suy ra Then kỳ vọng. |
| R10 | Scope Boundary Explicit | Cross-UC reference dùng cách diễn đạt navigation + comment Note. |

## Project Compliance (fail review nếu thiếu — C.1-C.5)

| Check | Rule |
|-------|------|
| C.1 Wireframe Coverage | Mỗi component/action trong Wireframe (PRD §4b) có ≥1 SC. **FE/App: mỗi Screen State (≠default) và mỗi AC-UI behavioral của design-spec (`design_coverage`) cũng phải có ≥1 SC** — dedup với AC nghiệp vụ PRD; bỏ AC-UI visual thuần. |
| C.2 PRD Traceability | Mỗi AC và mỗi BR (gồm từng bullet logic) map tới ≥1 SC. |
| C.3 Business Dictionary | Dùng đúng canonical term từ business-dictionary.md. |
| C.4 Banned Terms | 0 banned term trong file — grep trước khi gen. |
| C.5 NHÓM Grouping | Feature ≥3 SC → PHẢI có NHÓM grouping theo business theme. |

---

## NHÓM Grouping Convention (C.5 — bắt buộc cho ≥3 scenario)

Gom theo business theme, KHÔNG theo happy/negative/edge.

Format header (thụt 2 space, cùng cấp với Background):
```
  # ==========================================================
  # NHÓM N: <Business theme> (<BR refs nếu áp dụng>)
  # ==========================================================
```

Rules:
- Đánh số tuần tự NHÓM 1 → N. SC ID tuần tự xuyên suốt lifecycle (không reset theo từng NHÓM).
- Mỗi NHÓM có thể chứa @happy + @edge + @negative cùng theme.
- SC trong NHÓM không cần theo thứ tự ID (NHÓM 2 có thể chứa SC4, SC8, SC11 nếu cùng theme).

Pattern gợi ý (điều chỉnh theo UC):
- `Init / Save success — valid data combinations`
- `Validation / Block when invalid`
- `Error handling — API fail / system error`
- `Cancel changes / Close modal without saving`
- `Cross-system / Downstream effects`
- `Idempotency & Concurrency`

---

## UC Decomposition

Với mỗi UC trong PRD, trình bày outline SC **trước khi sinh**:
```
{TICKET-ID}-UC1: {Use Case Name}
  NHÓM 1: {Theme} (BR1, BR2)
    SC1 [@happy]:              {business outcome}
    SC2 [@happy @alternative]: {variant outcome}
  NHÓM 2: {Theme} (BR2, BR3)
    SC3 [@edge]:               {edge case}
    SC4 [@negative]:           {error handling}
  ACs covered: AC1, AC2
  BRs covered: {TICKET-ID}-UC1-BR1, BR2, BR3
```

*(FE/App: nếu đã nạp design-spec (xem "Design Spec — Gate & Load"), đưa Screen State ≠default (loading/error/empty) + AC-UI behavioral của `design_coverage` vào outline — dedup với AC nghiệp vụ, đừng tạo SC trùng.)*

CHECKPOINT: "Outline này đúng chưa? Bạn muốn thêm hay bớt SC nào không?" → **Chờ confirm trước khi sinh.**

---

## Generate

**Output path theo mode:**
- **Spec repo mode**: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC{N}-{slug}.feature`
- **Umbrella mode**: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC{N}-{slug}.feature` *(service route 1-1 theo domain → không thêm subfolder service)*

Với mỗi UC, ghi vào path đã phân giải ở trên. Dùng từ vựng cho active platform (từ Platform Selection hoặc Service Detection).

```gherkin
{{include:templates/feature.template}}
```

*(Template `.feature` là **single-source** ở `templates/feature.template` — sửa file đó để đổi cấu trúc mọi `.feature` sinh ra. Coverage Matrix + Pre-merge Checklist nằm ở **cuối** template, thêm vào cuối mỗi file.)*

---

## Write Trace State

Sau khi sinh tất cả file `.feature`, tạo hoặc cập nhật **sổ trace theo platform** `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv` cho mỗi UC — một sổ riêng cho `system` / `web` / `app`. Vì `sc_id` = `{UC-ID}-SC{N}` chỉ độc nhất trong (UC × platform) (mỗi platform tự đánh số SC từ 1), **mỗi platform một file** để scenario platform này không đè/xoá platform khác. Lệnh luôn biết `active_platform` (từ Platform Selection / Service Detection) nên chỉ ghi đúng sổ của platform đang gen.

> **Umbrella + `spec_source`:** cả file `.feature` **và** trace `.tsv` đều ghi vào **spec repo** (`{spec_source}/specs/{domain}/{prd-slug}/bdd/…` và `{spec_source}/.trace/{domain}/{prd-slug}/…`, do context-loader phân giải) — một thao tác ghi **single-repo**, commit/push vào spec submodule. (Trace được gộp trong spec repo để PM quản lý mọi status ở một chỗ; các lệnh phía code cập nhật liên-repo sau.)

**Cột TSV (tab-separated, một header row + một data row cho mỗi scenario):**
```
sc_id\tsc_title\tspec_ver\tgen_ver\timplemented_by\ttest_count\ttest_classes\tdev_selftest\tdev_selftest_at\tqc_status\tqc_run_at\tqc_owner\tqc_blocked_by\tprd_version\tbdd_version\ttech_doc_revision\tfe_tech_doc_revision\tprd_status\tuc_status\tfe_phase\tstatus\tlast_updated
```

**Rules:**
- Nếu file chưa tồn tại → tạo với header row + tất cả scenario row.
- Nếu file tồn tại (gen lại) → với mỗi SC trong `.feature` mới:
  - SC đã có trong `.tsv` VÀ `spec_ver` không đổi → chỉ cập nhật: `sc_title`, `prd_version`, `bdd_version`, `prd_status`, `uc_status`, `last_updated`. Giữ nguyên các cột khác.
  - SC đã có trong `.tsv` VÀ `spec_ver` đổi (scenario bị sửa) → cập nhật: `sc_title`, `spec_ver`, `prd_version`, `bdd_version`, `prd_status`, `uc_status`, `last_updated` VÀ set `status = DRIFT` ngay (để TSV phản ánh drift mà không cần đợi `/validate-traces`). Giữ nguyên `gen_ver`, `implemented_by`, `test_count`, `test_classes`, `tech_doc_revision`, `fe_tech_doc_revision`.
  - SC mới (thêm trong lần gen lại này) → append row mới với `gen_ver`, `implemented_by`, `test_count`, `test_classes`, `dev_selftest`, `dev_selftest_at`, `qc_status`, `qc_run_at`, `qc_owner`, `qc_blocked_by`, `tech_doc_revision`, `fe_tech_doc_revision` đều set `—`.
  - SC không còn trong `.feature` (bị xoá) → xoá row của nó. *(An toàn: sổ này chỉ chứa scenario của `{active_platform}`, so với `.feature` của chính platform đó — không bao giờ đụng scenario platform khác.)*

**Giá trị ghi cho mỗi scenario:**

| Cột | Giá trị |
|--------|-------|
| `sc_id` | `{UC-ID}-SC{N}` |
| `sc_title` | text title của scenario |
| `spec_ver` | `@trace.sc_version` của scenario này |
| `gen_ver` | `—` (chưa gen) |
| `implemented_by` | `—` |
| `test_count` | `—` |
| `test_classes` | `—` |
| `dev_selftest` | `—` (chưa chạy test) |
| `dev_selftest_at` | `—` |
| `qc_status` | `—` (kết quả QC automation chính thức — set bởi `/qc-run-test`) |
| `qc_run_at` | `—` |
| `qc_owner` | `—` (SC chưa pass đang chờ ai: `dev` / `po` — set bởi `/qc-run-test` + `/report-bug`) |
| `qc_blocked_by` | `—` (`BUG-{id}` / `GAP-{id}` liên kết — set bởi `/qc-run-test` + `/report-bug`) |
| `prd_version` | `@trace.prd_version` từ header `.feature` |
| `bdd_version` | `@trace.bdd_version` từ header `.feature` |
| `tech_doc_revision` | `—` (revision tech-doc gộp `{TICKET-ID}-tech-design.md` — set bởi `/generate-code` + `/review-tech-docs`) |
| `fe_tech_doc_revision` | `—` (revision cùng tech-doc gộp, ghi khi FE `--phase=integration` wire theo §4.5.4 — set bởi `/generate-code`) |
| `prd_status` | đọc `\| **Status** \|` từ metadata PRD |
| `uc_status` | `draft` cho UC mới; giữ giá trị hiện có khi gen lại |
| `fe_phase` | `—` (set bởi `/generate-code --phase` khi FE implement) |
| `status` | `UNTRACKED` |
| `last_updated` | hôm nay `YYYY-MM-DD` |

## Refresh Panel Mirror
{{include:steps/trace-mirror.md}}

## Output

{{include:steps/report-footer.md}}

```
/generate-bdd Hoàn tất

[Spec repo mode — platform: {active_platform}]
Files:
  {paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC1-{slug}.feature ({N} scenarios)
  {paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{TICKET-ID}-UC2-{slug}.feature ({N} scenarios)
Trace:
  {paths.trace_dir}/{domain}/{prd-slug}/{TICKET-ID}-UC1-{active_platform}.tsv ({N} rows)
  {paths.trace_dir}/{domain}/{prd-slug}/{TICKET-ID}-UC2-{active_platform}.tsv ({N} rows)
Next (spec repo):
  → Chạy /generate-bdd lại cho các platform khác (web → app → system)
  → Sau khi gen hết platform: commit + push + báo team dev
  → Team dev đọc BDD từ spec submodule — không chạy /generate-bdd ở phía họ

[Umbrella mode — service: {active_service}]
Files:
  {paths.specs_dir}/{domain}/{prd-slug}/bdd/{TICKET-ID}-UC1-{slug}.feature ({N} scenarios)
Trace:
  {paths.trace_dir}/{domain}/{prd-slug}/{TICKET-ID}-UC1-{active_platform}.tsv ({N} rows)
Next (umbrella):
  → /review-context {feature-file} để kiểm tra coverage
  → /generate-tech-docs {feature-file}
  → /generate-code {feature-file}

📊 Living Docs: chạy /validate-traces (hoặc /sync) để push trace này lên dashboard spec-module.
```
