# /generate-design-spec — Sinh Design Specification (FE / App)

## Gate
# Gate — Quy trình vào chuẩn cho mọi lệnh

Mọi lệnh PHẢI chạy gate này trước khi thực thi phần logic riêng của nó.

## Bước 0 — Kiểm tra chế độ Sub-Agent

Trước tiên, kiểm tra xem `$ARGUMENTS` có phải là payload JSON từ một orchestrator hay không:

1. Thử parse `$ARGUMENTS` dưới dạng JSON.
2. Nếu parse thành công **và** chứa `"_agent_mode": true`:
   - **Bỏ qua hoàn toàn Bước 1, 2 và 3 của Gate này.**
   - Đặt target file = `payload.target_file`
   - Đặt loaded context = `payload.context` (KHÔNG chạy context-loader.md)
   - Đặt phạm vi UC = `payload.uc_id` (chỉ xử lý UC này)
   - Đặt line range = `payload.uc_section` (chỉ đọc đúng section đó của PRD)
   - Đặt dimension = `payload.dimension` nếu có (lệnh review per-UC: chỉ review đúng lăng kính này)
   - Đi thẳng tới phần logic riêng của lệnh.
3. Nếu `$ARGUMENTS` không phải JSON hoặc không có `_agent_mode` → tiếp tục sang Bước 1 (chế độ thường).

## Bước 0-B — Kiểm tra Model

*Bỏ qua bước này nếu `_agent_mode: true` (sub-agent — orchestrator đã kiểm tra rồi).*

Các lệnh sinh nội dung và review phức tạp đòi hỏi khả năng suy luận mạnh.
Dùng model nhỏ hơn sẽ rủi ro: bỏ sót edge case, phân tích spec thiếu sót, vi phạm kiến trúc.

Hiển thị và chờ phản hồi:

```
⚙️  MODEL CHECK
──────────────────────────────────────────────────────────────────
  Recommended  : claude-opus-4 (hoặc model Opus mới nhất)
  Why needed   : Phân tích spec, review kiến trúc, sinh code đòi hỏi
                 suy luận sâu. Model nhỏ hơn dễ bỏ sót edge case.

  Cách đổi trong Claude Code:
    • Settings → Model → chọn "claude-opus"
    • hoặc: /model → chọn claude-opus

  Đang chạy claude-opus?
    Y — đúng, đang dùng claude-opus → tiếp tục
    S — bỏ qua kiểm tra (tôi chấp nhận rủi ro chất lượng thấp hơn với model hiện tại)
──────────────────────────────────────────────────────────────────
```

- "Y" → tiếp tục sang Bước 1.
- "S" → tiếp tục sang Bước 1 (người dùng chấp nhận rủi ro, thêm ⚠️ vào report cuối).
- "N" hoặc bất kỳ giá trị nào khác → **DỪNG.** Xuất: "Vui lòng chuyển sang claude-opus rồi chạy lại lệnh này."

## Bước 1 — Xác định Target File

1. Nếu `$ARGUMENTS` được cung cấp và trỏ tới một file tồn tại → dùng trực tiếp làm target.
2. Nếu `$ARGUMENTS` là một **UC-ID / ticket ID / tên rút gọn** (không có path) → phân giải thành file bằng cách glob theo bố cục feature-package. `{prd-slug}` lúc này **chưa biết**, nên dùng wildcard `*` cho segment đó, và `**` đệ quy dưới `bdd/` để phủ hết các thư mục con theo platform (`bdd/web/`, `bdd/app/`, `bdd/system/`):
   - **Lệnh BDD** (target là `.feature`): `{specs_dir}/{domain}/*/bdd/**/{UC-ID}*.feature` — hoặc `{specs_dir}/*/*/bdd/**/{UC-ID}*.feature` nếu domain cũng chưa biết. Nếu lệnh ngụ ý một platform/scope cụ thể (vd: system tech-doc cần BDD `system/`), ưu tiên kết quả trong thư mục con platform đó.
   - **Lệnh PRD** (target là file PRD `{TICKET-ID}-{prd-slug}.md` — file `.md` duy nhất ở gốc feature folder, cạnh `bdd/`): `{specs_dir}/{domain}/*/{TICKET-ID}*.md` nếu biết TICKET-ID; nếu không, `{specs_dir}/{domain}/*/*.md` (khớp feature folder có id tương ứng), hoặc `{specs_dir}/*/*/*.md` nếu domain cũng chưa biết. *(Glob `*/*.md` ở cấp gốc folder chỉ khớp PRD — tech-docs/design-spec `.md` nằm sâu hơn trong thư mục con.)*
   - **Lệnh tech-docs**: `{specs_dir}/{domain}/*/tech-docs/{UC-ID}*-tech-design*.md`.
   - **Lệnh design-spec**: `{specs_dir}/{domain}/*/design-spec/{TICKET-ID}*.md`.

   Khi một file khớp: đặt nó làm target **và** ghi lại `domain` + `prd_slug` từ path của nó (theo quy tắc trích xuất trong `context-loader.md` Bước 1 — `prd_slug` = segment đầu tiên sau `{specs_dir}/{domain}/`). Mọi path mà lệnh đọc/ghi về sau (BDD/tech-docs/design-spec/trace cùng cấp) đều dùng **`prd_slug` đã phân giải đó**, nên tất cả artifact nằm chung một feature package. Nếu nhiều file khớp (vd: nhiều platform), chọn theo platform/scope của lệnh hoặc liệt kê ra và hỏi.
3. Nếu `$ARGUMENTS` rỗng hoặc không tìm thấy file khớp:
   - Liệt kê các file trong thư mục liên quan của lệnh này (vd: `specs/*/*/*.md` — file PRD ở gốc mỗi feature folder — cho lệnh PRD, `specs/*/*/bdd/**/*.feature` cho lệnh BDD).
   - Hiển thị danh sách cho người dùng và hỏi: "Bạn muốn làm việc với file nào? (Nhập số thứ tự hoặc tên file)"
   - Chờ người dùng chọn rồi mới tiếp tục.

## Bước 2 — Chạy Context Loader

Nạp toàn bộ context của dự án bằng cách làm theo quy trình trong `steps/context-loader.md`.
Lưu toàn bộ context đã nạp vào bộ nhớ để dùng xuyên suốt phiên làm việc của lệnh.

## Bước 3 — CHECKPOINT

Sau khi hoàn thành Bước 1 và 2, hiển thị bản tóm tắt và chờ xác nhận:

```
CHECKPOINT
-----------
Target     : {resolved file path}
Project    : {project.name từ project-context.yaml}
Tech stack : {language} / {framework}
Module     : {module nếu có, else "not configured"}
Domains    : {danh sách domain, ngăn cách bởi dấu phẩy}

Tiếp tục? (Y/N)
```

Chờ người dùng trả lời rõ ràng "Y" hoặc "N" rồi mới tiếp tục.
- "Y" → tiếp tục sang các bước riêng của lệnh bên dưới.
- "N" → dừng lại và hỏi người dùng muốn thay đổi gì.


*Lưu ý: Với lệnh này, target file là một Business PRD (`{TICKET-ID}-{prd-slug}.md` — file `.md` ở gốc feature folder) dưới `{paths.specs_dir}/{domain}/{prd-slug}/`. Phân giải từ `$ARGUMENTS` hoặc liệt kê thư mục và hỏi. Chỉ hỗ trợ PRD của FE và mobile — PRD của BE sẽ bị từ chối ở bước Platform Check.*

## Context
# Context Loader — Nạp toàn bộ context dự án

Thực hiện các bước theo đúng thứ tự. Lưu mọi thứ vào bộ nhớ trong suốt phiên làm việc của lệnh.

**Hướng dẫn ưu tiên (chống lost-in-middle):**
- Bước 1–2 là PROJECT-CONFIG — nạp trước, phân giải mọi path và metadata.
- Bước 3 là CRITICAL — kiến trúc + coding standards, là các sự thật ưu tiên cao nhất khi sinh nội dung.
- Bước 4 là SAFETY — quy tắc bảo vệ dữ liệu, thực thi ngầm suốt cả phiên.
- Bước 5–6 là DOMAIN KNOWLEDGE — thuật ngữ và định nghĩa entity.
- Bước 7 là WORKING MEMORY RECAP — chốt các sự thật quan trọng lên đầu bộ nhớ làm việc.

---

## Bước 1 — [PROJECT-CONFIG] Nạp project-context.yaml

Đọc `.agent/project-context.yaml`. Trích xuất và lưu:

**Tech Stack:**
- `tech_stack.language` → ngôn ngữ đang dùng (vd: Java 17, TypeScript, C#, Go)
- `tech_stack.framework` → framework đang dùng (vd: Spring Boot 3.2, Angular 17, .NET 8)
- `tech_stack.build_tool` → build tool (vd: Maven, npm, dotnet, go)
- `tech_stack.test_framework` → test framework (vd: JUnit 5 + Mockito, Jest, xUnit)
- `tech_stack.database` → database (vd: PostgreSQL, MySQL, MongoDB)
- `tech_stack.module` → module profile đang dùng (vd: java-spring, angular, dotnet, golang, context-engineering)

**Conventions:**
- `conventions.build_command` → cách compile/build
- `conventions.test_command` → cách chạy test
- `conventions.service_run` → cách khởi động service
- `conventions.ticket_prefix` → tiền tố ticket ID (vd: PROJ, FEAT, UC)

**Domains:**
- `domains` → danh sách các business domain đang hoạt động

**Paths (nếu có):**
- `paths.specs_dir` → gốc của spec artifact — PRD, BDD, tech-docs, design-spec. Cấu trúc: `{specs_dir}/{domain}/{prd-slug}/{ {TICKET-ID}-{prd-slug}.md | bdd/ | tech-docs/ | design-spec/}` (file PRD đặt tên `{TICKET-ID}-{prd-slug}.md`, là file `.md` duy nhất ở gốc feature folder)
- `paths.refinement_dir` → thư mục output cho findings/review
- `paths.qc_dir` → gốc artifact QC automation (hiện ở top-level, mỗi UC một thư mục con: `{qc_dir}/{UC-ID}/`)
- `paths.qc_skills_dir` → nơi các lệnh qc-* nạp QC skill (mặc định bundled `.agent/skills/qc`; override sang repo/submodule riêng của team QC để bản nâng cấp framework không ghi đè)
- `paths.product_definitions_dir` → gốc product definition
- `paths.domain_knowledge_dir` → gốc domain knowledge
- `paths.business_dictionary` → path tới business-dictionary.md
- `paths.core_entities` → path tới core-entities.md
- `paths.tech_docs_dir` → gốc tài liệu kỹ thuật (gộp với specs_dir trong bố cục feature-package — tech-docs nằm dưới `{specs_dir}/{domain}/{prd-slug}/tech-docs/`)
- `paths.trace_dir` → thư mục trạng thái trace; cấu trúc: `.trace/{domain}/{prd-slug}/{UC-ID}-{platform}.tsv` (mỗi UC × platform một sổ)

Nếu không có section `paths`, dùng các giá trị mặc định:
- `specs_dir` = `specs`
- `refinement_dir` = `.agent/review`
- `qc_dir` = `docs`
- `qc_skills_dir` = `.agent/skills/qc`
- `product_definitions_dir` = `specs/product-definition`
- `domain_knowledge_dir` = `specs/domain-knowledge`
- `business_dictionary` = `specs/domain-knowledge/business-dictionary.md`
- `core_entities` = `specs/domain-knowledge/core-entities.md`
- `tech_docs_dir` = `specs`
- `trace_dir` = `.trace`

Lưu ý: Trong bố cục feature-package, `specs_dir` là gốc thống nhất. Mọi loại spec artifact (PRD, BDD, tech-docs, design-spec) đều nằm dưới `{specs_dir}/{domain}/{prd-slug}/`. `prd-slug` là tên folder feature-package, không phải một biến config riêng.

**Cách trích xuất `prd_slug` (đúng cho MỌI target file, bất kể độ sâu lồng nhau):** với một path target dạng `{specs_dir}/{domain}/{prd-slug}/...`, lấy **segment path đầu tiên sau `{specs_dir}/{domain}/`** — tức vị trí `{prd-slug}`. KHÔNG dùng folder cha trực tiếp của file, vì artifact BDD/tech-docs/design-spec lồng sâu hơn một hoặc hai cấp bên trong package. Ví dụ:
- `specs/payment/create-invoice/PAY01-create-invoice.md` → `prd_slug = create-invoice`
- `specs/payment/create-invoice/bdd/system/PAY-UC1.feature` → `prd_slug = create-invoice` *(KHÔNG phải `system`)*
- `specs/payment/create-invoice/bdd/web/PAY-UC1.feature` → `prd_slug = create-invoice` *(KHÔNG phải `web`)*
- `specs/payment/create-invoice/tech-docs/PAY01-tech-design.md` → `prd_slug = create-invoice` *(KHÔNG phải `tech-docs`)*
- `specs/payment/create-invoice/design-spec/PAY-design-spec-web.md` → `prd_slug = create-invoice`

Mọi artifact cùng cấp của một feature (PRD, BDD của từng platform, tech-docs BE + FE, design-spec, và trace TSV) đều phân giải về **cùng một `prd_slug`** — nên một BDD **system** hay tech-doc **system/BE** được tổng hợp sẽ nằm chung package `{specs_dir}/{domain}/{prd-slug}/` với các artifact web/app mà nó được suy ra từ đó.

Nếu `tech_stack.module` được đặt, đồng thời nạp `.agent/modules/{module}/stack-profile.yaml` nếu file tồn tại.

---

## Bước 1.5 — [SERVICE ROUTING] Phân giải path service (chế độ umbrella)

*Bỏ qua hoàn toàn bước này nếu `setup.mode` không phải `"umbrella"` và không có section `services` trong project-context.yaml.*

Nếu có section `services`:

**1. Phát hiện active domain** (theo thứ tự ưu tiên):
- Đọc `@trace.domain` từ frontmatter của target file (nếu Gate đã nạp một target file)
- Trích xuất từ path target file: `domain` = segment đầu tiên sau base path `specs_dir`; `prd_slug` = segment kế tiếp (folder feature-package). Điều này đúng ở mọi độ sâu target — xem quy tắc trích xuất `prd_slug` ở Bước 1.  
  *(vd: `specs/user/create-account/USR01-create-account.md` **và** `specs/user/create-account/bdd/system/UC1.feature` đều → domain = `user`, prd_slug = `create-account`)*
- Nếu `$ARGUMENTS` chứa một path, trích xuất segment domain sau `specs_dir`

**2. Route tới service** — nếu active domain khớp với một key trong `services`:
- Override `paths.specs_dir` → `services.{domain}.specs_dir` — **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, MỌI BDD (web/app/**system**) là artifact dùng chung liên team → để bước 4 route sang spec repo; KHÔNG pin theo service ở đây.
- Override `paths.tech_docs_dir` → `services.{domain}.tech_docs_dir` — **chỉ khi `setup.spec_source` KHÔNG được đặt.** Khi `spec_source` ĐƯỢC đặt, tech-design (API contract) là artifact liên team và phải nằm trong spec repo dùng chung (xử lý ở bước 4), nên để bước 4 route `tech_docs_dir` — KHÔNG pin theo service ở đây.
- Lưu `active_service` = `services.{domain}.path`
- Lưu `active_service_module` = `services.{domain}.module`
- Nếu service có `module` riêng → dùng nó làm `active_module` (override `tech_stack.module`)

**3. Fallback** — nếu không phát hiện được domain hoặc không có service key khớp:
- Giữ path mặc định từ Bước 1
- Đặt `active_service = unresolved`

**4. Tự động override theo spec source** — nếu `setup.spec_source` được đặt VÀ path tương ứng chưa được set tường minh trong `paths:`:
- Override `paths.specs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt.** Mọi spec artifact (PRD, BDD, tech-docs, design-spec) nằm dưới gốc spec thống nhất trong spec repo dùng chung theo bố cục feature-package: `{spec_source}/specs/{domain}/{prd-slug}/`. Mọi umbrella (FE/App/BE) đều đọc từ đây. *(`specs/` theo service chỉ khi không có `spec_source`.)*
- Override `paths.tech_docs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt** (bước 2 không còn pin tech-docs theo service trong trường hợp này). Tech-docs nằm tại `{spec_source}/specs/{domain}/{prd-slug}/tech-docs/`. Tech-design CHÍNH LÀ API contract liên team: BE viết ở đây, FE/App đọc nó từ cùng spec submodule tại `/generate-code --phase=integration`. *(tech-docs theo service chỉ xảy ra khi không có `spec_source` — repo BE thuần đa-service không có spec module dùng chung.)*
- Override `paths.domain_knowledge_dir` → `{spec_source}/specs/domain-knowledge`
- Override `paths.business_dictionary` → `{spec_source}/specs/domain-knowledge/business-dictionary.md`
- Override `paths.core_entities` → `{spec_source}/specs/domain-knowledge/core-entities.md`
- Override `paths.bug_reports_dir` → `{spec_source}/feedback/bug-reports`
- Override `paths.bdd_proposals_dir` → `{spec_source}/feedback/bdd-proposals`
- Override `paths.prd_change_requests_dir` → `{spec_source}/feedback/prd-change-requests`
- Override `paths.trace_dir` → `{spec_source}/.trace` — **luôn khi `spec_source` được đặt.** Trace TSV được gộp vào spec repo (một nơi authoritative duy nhất, không tách theo service) để PM/PO có một chỗ duy nhất quản lý trạng thái. Cấu trúc bên trong: `.trace/{domain}/{prd-slug}/{UC-ID}-{platform}.tsv`. Các lệnh phía code (`/generate-code`, `/dev-run-test`, `/qc-run-test`) chạy từ `service_root` nhưng **ghi trace row của chúng vào `{spec_source}/.trace/{domain}/{prd-slug}/`** — giống như chúng đã push `feedback/` vào đó. *(`.trace` theo service chỉ khi không có `spec_source`.)*
- Override `paths.refinement_dir` → `{spec_source}/.agent/review` — **luôn khi `spec_source` được đặt.** Findings review (`/refine-prd`, `/review-context`, `/review-tech-docs`) là artifact liên-team *về* tài liệu trong spec repo (PRD/BDD/tech-design) — thuộc cùng khu vực ghi với `.trace/` và `feedback/`. Các lệnh review chạy từ working dir của service (BE repo) nhưng **ghi findings vào `{spec_source}/.agent/review/`**, KHÔNG phải `.agent/review` của service repo. Bên trong flat, phân biệt bằng tên file đã prefix `{prd-slug}`/`{UC-ID}`/`{TICKET-ID}`. *(`.agent/review` theo service chỉ khi không có `spec_source`.)*

> **Vì sao đặt dưới `spec_source`:** PRD, BDD, tech-docs, design-spec, domain knowledge, feedback của tester, **trạng thái coverage `.trace/`**, **và findings review `.agent/review/`** đều là **artifact liên team** — chúng nằm trong **spec repo dùng chung** theo bố cục feature-package để mọi umbrella (FE/App/BE) và PM đọc từ một nguồn qua `/sync`. Trong bố cục feature-package, một folder `specs/{domain}/{prd-slug}/` gom tất cả loại artifact của một PRD, giúp spec repo tự đủ và dễ điều hướng theo feature. Service submodule chỉ chứa **code** (+ tooling build/test). `.trace/`, `.agent/review/` và `feedback/` là khu vực **ghi** của dev/QC/reviewer trong spec repo. Ở chế độ single-service (không có `spec_source`), mọi thứ mặc định dưới gốc repo — vẫn là một repo.

---

## Bước 1.6 — [SERVICE CONVENTIONS] Nạp convention riêng của service (chế độ umbrella)

*Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc context ở chế độ single-service.*

Khi `active_service` đã được phân giải thành một path thật ở Bước 1.5 (vd: `user-service/`):

**1. Định vị config của service** — thử theo thứ tự ưu tiên:
- `{active_service}/.agent/project-context.yaml`
- `{active_service}/project-context.yaml`

**2. Nếu tìm thấy, override bằng giá trị riêng của service:**

| Biến | Nguồn |
|----------|--------|
| `conventions.test_command` | `conventions.test_command` của service |
| `conventions.build_command` | `conventions.build_command` của service |
| `paths.trace_dir` | **Nếu `spec_source` được đặt → giữ route spec-repo của bước 4 (`{spec_source}/.trace`); bỏ qua mọi `trace_dir` cấp service.** Chỉ khi không có `spec_source`: `{active_service}/{service paths.trace_dir}` (mặc định `{active_service}/.trace`). |
| `paths.specs_dir` | **Nếu `spec_source` được đặt → giữ route spec-repo của bước 4 (`{spec_source}/specs`); bỏ qua mọi `specs_dir` cấp service** (mọi spec artifact đều liên team, không bao giờ theo service ở chế độ này). Chỉ khi không có `spec_source`: `{active_service}/{service paths.specs_dir}` nếu được set, else dùng override ở Bước 1.5. |
| `paths.refinement_dir` | **Nếu `spec_source` được đặt → giữ route spec-repo của bước 4 (`{spec_source}/.agent/review`); bỏ qua mọi `refinement_dir` cấp service** (findings review là artifact liên team). Chỉ khi không có `spec_source`: `{active_service}/.agent/review`. |

**3. Lưu** `service_root = {active_service}` làm mốc thư mục làm việc cho mọi lệnh phía sau:
- Các lệnh shell (`/dev-run-test`, `/dev-gen-test`) chạy **bên trong** `service_root`
- **File source/test** được ghi tương đối với `service_root`; **trace TSV** được ghi vào `{paths.trace_dir}` (là spec repo khi `spec_source` được đặt — một thao tác ghi liên-repo, commit/push vào spec submodule giống như `feedback/`).

**4. Nếu không tìm thấy config của service** — giữ mặc định umbrella, vẫn set `service_root = {active_service}` (luôn cần mốc path kể cả khi không có config override).

---

## Bước 2 — [PROJECT-CONFIG] Nạp module stack profile (có điều kiện)

Nếu `tech_stack.module` được đặt, đọc `.agent/modules/{module}/stack-profile.yaml`.
Merge các convention riêng của framework (layer pattern, test pattern, quy tắc đặt tên) vào context đã nạp.
Nếu file không tồn tại → bỏ qua âm thầm.

---

## Bước 3 — [CRITICAL] Nạp CLAUDE.md (phân tầng: root + service overlay)

*Đây là context ưu tiên cao nhất — nó định nghĩa CÁCH viết code và tài liệu cho dự án này.*

CLAUDE.md được nạp theo **hai tầng** để các quy tắc toàn-umbrella và kiến trúc/coding standards
riêng của service kết hợp đúng cách. Agent luôn đứng ở gốc umbrella, nhưng code triển khai nằm
trong một service submodule với stack, kiến trúc, và convention RIÊNG của nó — nên CLAUDE.md của
service phải thắng khi sinh code.

**Tầng 1 — [BASE] Root CLAUDE.md (toàn umbrella).**
Đọc `CLAUDE.md` ở gốc repo. Coi nội dung của nó là **nền tảng dùng chung** cho cả umbrella —
git convention, tư thế bảo vệ dữ liệu, quy tắc xuyên suốt, và (ở chế độ single-service) là
kiến trúc + coding standards duy nhất của dự án.

**Tầng 2 — [OVERLAY] Service CLAUDE.md (chỉ chế độ umbrella).**
*Chỉ chạy nếu `service_root` đã được set ở Bước 1.6 (tức đã route tới một service thật).*
Đọc `{service_root}/CLAUDE.md`. File này định nghĩa kiến trúc + coding standards của **stack
thực sự đang được triển khai** (vd: `user-service` = java-spring, `web` = nextjs).
Overlay nó lên trên Tầng 1: **khi có xung đột, giá trị của service THẮNG** cho kiến trúc,
coding standards, và error handling. Các giá trị Tầng 1 mà service không định nghĩa lại
(vd: git convention, banned pattern dùng chung toàn tổ chức) vẫn có hiệu lực.

Từ kết quả **đã merge**, trích xuất và lưu:

- **§1 Project Overview** → tên dự án, ngôn ngữ, framework, lệnh build/test, domains
- **§2 Architecture** → thứ tự layer (vd: Controller → Facade → Service → Repository), quy tắc kiến trúc — *service overlay thắng*
- **§2 Package Layout** → **base package** (vd `vn.edupia.{service}`) + **chiến lược đặt package** (by-layer / by-feature) + nơi code một domain sống. Đây là **quy ước đặt code trên đĩa**, PHẢI enforce khi sinh code. Nếu §2 chỉ nêu base package + thứ tự layer mà **không** nói tới sub-package theo feature → hiểu là **by-layer**: các layer đặt **TRỰC TIẾP** dưới base package (vd `vn.edupia.{service}.service`, `.repository`); feature/UC/prd-slug **KHÔNG** thành sub-package, chỉ phân biệt ở **tên class**. Lưu `code_base_package` + `package_strategy` — *service overlay thắng*.
- **§3 Coding Standards** → quy tắc đặt tên (class, method), kiểu response wrapper, pattern bị cấm — *service overlay thắng*
- **§5 Error Handling** → kiểu exception, mapping HTTP status code, tên class not-found exception — *service overlay thắng*
- **§7 Git Conventions** → pattern đặt tên branch, format commit message — *lấy theo root trừ khi service định nghĩa lại*

**Quy tắc phân giải:**
- Nếu cả hai tầng tồn tại → merge như trên; ghi `claude_md_source = root + {service_root}`.
- Nếu chỉ có service overlay (không có root CLAUDE.md) → dùng file service một mình; `claude_md_source = {service_root}`.
- Nếu `service_root` được set nhưng `{service_root}/CLAUDE.md` **thiếu** → fallback về root CLAUDE.md và gắn cờ ⚠️ trong recap Bước 7 (service không có định nghĩa kiến trúc/coding-standards — việc sinh code sẽ dùng mặc định umbrella, có thể sai stack).
- Nếu cả hai đều không tồn tại → ghi nhận CLAUDE.md thiếu và tiếp tục chỉ với dữ liệu từ project-context.yaml.

---

## Bước 4 — [SAFETY] Nạp quy tắc bảo vệ dữ liệu

Đọc `.agent/rules/data-protection.md` (hoặc `rules/data-protection.md` từ bản cài đặt framework).

Lưu các pattern file nhạy cảm — bạn **tuyệt đối không** đọc, ghi, hiển thị, hay tham chiếu nội dung từ các file khớp những pattern đó trong suốt cả phiên.

Nếu cả hai file đều không tồn tại → áp dụng mặc định built-in: không bao giờ truy cập `.env*`, `*.key`, `*.pem`, `*secret*`, `*password*`, `*credential*`.

---

## Bước 5 — [DOMAIN] Nạp Business Dictionary (có điều kiện)

Kiểm tra file business dictionary có tồn tại không (dùng `paths.business_dictionary` đã phân giải ở Bước 1).

Nếu tồn tại, đọc và trích xuất:
- **Canonical Terms** → danh sách đầy đủ các thuật ngữ chuẩn và định nghĩa
- **Banned Terms** → danh sách đầy đủ các thuật ngữ bị cấm và bản thay thế chuẩn
- **Status / Enum Registry** → các giá trị enum được phép theo từng entity

Lưu danh sách banned term để **thực thi chủ động** suốt phiên làm việc của lệnh:
- Khi sinh bất kỳ văn bản nào (PRD, BDD, comment code, tech docs), kiểm tra không có banned term nào xuất hiện
- Tự động thay banned term bằng bản chuẩn tương đương

Nếu file không tồn tại → bỏ qua âm thầm. Không cảnh báo hay chặn.

---

## Bước 6 — [DOMAIN] Nạp Core Entities (có điều kiện)

Kiểm tra file core entities có tồn tại tại `paths.core_entities` không (đã phân giải ở Bước 1).
Path mặc định: `specs/domain-knowledge/core-entities.md`.

Nếu tồn tại, đọc và lưu:
- **Entity catalog** → với mỗi entity: tên, mục đích, service sở hữu, các field chính (tên + kiểu), business invariant, và quan hệ
- **Field name registry** → tên field chuẩn dùng trong code và tài liệu được sinh ra
- **Relationship map** → cách các entity liên hệ với nhau (1:N, N:N, embedded, v.v.)

**Cách dùng catalog này:**
- Khi sinh code: dùng tên field, kiểu, và quan hệ định nghĩa ở đây — KHÔNG suy đoán từ code có sẵn
- Khi sinh PRD/BDD: tham chiếu tên entity từ catalog này để nhất quán
- Khi sinh tech-docs: dùng catalog này làm nguồn chân lý cho định nghĩa entity

Nếu file không tồn tại → bỏ qua âm thầm.

---

## Bước 6.5 — [PLATFORM] Suy ra active_module và platform_type

Dùng `tech_stack.module` đã nạp ở Bước 1, suy ra và lưu hai biến để mọi lệnh phía sau dùng:

```
active_module = tech_stack.module   (vd: "java-spring", "react", "flutter")
```

| `platform_type` | Modules |
|---|---|
| `backend` | `java-spring`, `golang`, `dotnet`, `php-laravel`, `context-engineering` |
| `web-frontend` | `react`, `nextjs`, `vue`, `nuxt`, `angular` |
| `mobile` | `flutter`, `react-native`, `ios-swiftui`, `android-compose` |

Nếu `tech_stack.module` rỗng hoặc không nhận diện được → set `platform_type = "unknown"` và gắn cờ ⚠️ trong recap Bước 7.

Hai biến này (`active_module`, `platform_type`) là nguồn chuẩn cho mọi logic rẽ nhánh trong các lệnh cần hành vi riêng theo platform (dev-gen-test, debug, fix-bug, dev-smoke-test).

---

## Bước 6.7 — [GUARDRAILS] Nạp Project Lessons (có điều kiện)

*Các lỗi tích luỹ mà AI không được lặp lại trong dự án này. Chúng được bổ sung dần qua `/learn`
hoặc được chấp nhận trong `/review-code`, `/fix-bug`, `/debug`.*

Phân giải path file lessons:
- Dùng `paths.lessons_file` nếu được set (có thể bị service override ở chế độ umbrella, Bước 1.6)
- Else mặc định `specs/domain-knowledge/lessons-learned.md`
- Ở chế độ umbrella/service (khi `service_root` được set), nếu `paths.lessons_file` chưa set, mặc định `{service_root}/.agent/project-lessons.md`

Nếu file tồn tại, đọc và lưu TẤT CẢ lesson làm **GUARDRAIL ĐANG HOẠT ĐỘNG** cho phiên:
- Coi **Rule** của mỗi lesson là ràng buộc cứng — cùng mức ưu tiên với coding standards trong CLAUDE.md (Bước 3).
- Trước khi sinh hoặc sửa bất kỳ artifact nào (PRD, BDD, tech-doc, code, test), đối chiếu output với mọi lesson có `category` khớp lệnh hiện tại VÀ `scope` khớp target (domain / file).
- Nếu output sinh ra vi phạm một lesson → sửa **trước khi** trình bày, và ghi rõ lesson nào (`L-NNN`) đã được áp dụng.

Nếu file không tồn tại → bỏ qua âm thầm (chưa có lesson nào được ghi nhận).

---

## Bước 7 — [RECAP] Working Memory Recap (chống lost-in-middle)

Sau khi nạp toàn bộ context, tổng hợp và xuất một khối tóm tắt gọn.
Recap này đảm bảo các sự thật quan trọng nhất được nêu ở CUỐI quá trình nạp context
(hiệu ứng recency — tươi mới nhất trong bộ nhớ làm việc khi bắt đầu task).

Xuất đúng khối này:
```
[CTX LOADED]
Stack     : {language} / {framework} / {database}
Platform  : {active_module} ({platform_type})
Layers    : {thứ tự layer từ CLAUDE.md §2 đã merge, vd: Controller → Facade → Service → Repository}
Package   : {code_base_package}.{layer} · {by-layer | by-feature}  ← feature/UC → tên class, KHÔNG thành package (nếu by-layer)
CLAUDE.md : {root + {service_root} | chỉ {service_root} | chỉ root | ⚠️ service overlay THIẾU — dùng root | missing}
Ticket    : {ticket_prefix}-
Dict      : {loaded — N canonical terms, M banned terms | missing}
Entities  : {loaded — EntityA, EntityB, EntityC | missing}
Lessons   : {loaded — N guardrails | chưa có}
Service   : {active_service} ({active_service_module}) | single-service
Svc Root  : {service_root} — đã nạp conventions + trace_dir từ config service | —
Status    : {FULL | PARTIAL — thiếu: CLAUDE.md / business-dict / core-entities | MINIMAL}
```

Nếu bất kỳ file CRITICAL nào thiếu (CLAUDE.md), gắn cờ rõ ràng để người dùng quyết định có tiếp tục hay không.

---

## Hoàn tất nạp Context

Sau khi hoàn thành tất cả các bước, bạn đã nạp:
- Định danh dự án, tech stack, convention module
- Quy tắc kiến trúc và thứ tự layer  ← **[CRITICAL — giữ trong bộ nhớ làm việc]**
- Coding standards và quy tắc đặt tên  ← **[CRITICAL — giữ trong bộ nhớ làm việc]**
- Quy tắc bảo vệ dữ liệu (pattern file nhạy cảm không bao giờ truy cập)
- Quy tắc thuật ngữ kèm danh sách banned term  ← **[DOMAIN — áp dụng cho mọi từ được sinh ra]**
- Entity catalog (tên field, kiểu, invariant)  ← **[DOMAIN — dùng khi sinh code]**
- Toàn bộ path đã cấu hình

Tiếp tục sang bước kế tiếp của lệnh đang gọi.


*Context bổ sung cho lệnh này: Đọc toàn bộ PRD target. Trích xuất: **TICKET-ID**, **domain**, **tên feature**, **Service** và **Module** từ metadata PRD (row `| **Service** |` và `| **Module** |`), User Flow (Section 4a), và tên màn hình Wireframe (Section 4b).*

*Quy tắc trích xuất Service (giống /generate-prd):*
- *Nếu metadata PRD có `| **Service** |` → dùng làm `active_service` và `| **Module** |` làm `active_module`.*
- *Nếu vắng VÀ `services` được định nghĩa trong `project-context.yaml` → hỏi: "Design Spec này dành cho service nào?" (chỉ liệt kê service FE/App, chờ chọn).*
- *Nếu dự án single-service → `active_service = "default"`, `active_module = tech_stack.module`.*

---

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

Đọc `| **Status** |` từ bảng Metadata của Business 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 song song):
  ```
  ⚠️  Business PRD đang ở Status: {status} (chưa duyệt). Design Spec dựng trên PRD chưa chốt có thể phải làm lại theo PRD.
     Khuyến nghị: PO duyệt PRD (đặt `| **Status** | approved |`) rồi mới sinh Design Spec.
     Vẫn sinh Design Spec bây giờ? (Y/N)
  ```
  Chỉ tiếp tục khi người dùng chọn Y.

---

## Platform Check

Dùng `active_module` và `platform_type` suy ra từ context loading:

1. Nếu `platform_type = "backend"` → **STOP**. Xuất:
   ```
   ❌ Design Spec chỉ dành cho platform FE và mobile.
   Với service BE, API contract thuộc về Business PRD (Use Case → section Business Logic).
   ```

2. Nếu `platform_type = "web-frontend"` → set `active_platform = "web"`.

3. Nếu `platform_type = "mobile"`:
   - `flutter` hoặc `react-native` → set `active_platform = "app"`
   - `ios-swiftui` → set `active_platform = "app-ios"`
   - `android-compose` → set `active_platform = "app-android"`

4. Nếu `platform_type = "unknown"` → hỏi: "Design Spec này dành cho platform nào?"
   ```
   Options:
     1 — web    (React / Next.js / Vue / Angular)
     2 — app    (Flutter / React Native)
     3 — app-ios     (iOS SwiftUI)
     4 — app-android (Android Compose)
   ```
   Chờ chọn. Map lựa chọn sang `active_platform` và suy ra `active_module` nếu được.

---

## Version Check (PRD drift)

Trước khi sinh, kiểm tra design-spec đã tồn tại cho platform này:
`{paths.specs_dir}/{domain}/{prd-slug}/design-spec/{TICKET-ID}-design-spec-{active_platform}-{slug}.md`
Đọc `| **Version** |` hiện tại của PRD từ Metadata (vd `1.3`).

- **Không tồn tại** → sinh mới; ghi `| **Built from PRD** | v{prd_version hiện tại} |`.
- **Tồn tại** → đọc row `| **Built from PRD** |` của nó, so với `| **Version** |` PRD hiện tại:
  - **Bằng nhau** → hỏi: "Design Spec đã dựng từ PRD v{x}. Sinh lại? (Y/N)".
  - **Khác** (PRD đã đổi) → CHECKPOINT drift:
    ```
    ⚠️  Phát hiện PRD version drift
    Design Spec dựng từ PRD v{old}; PRD giờ ở v{new}.
    Thay đổi kể từ v{old} (đọc # Change Log của PRD):
      {changelog rows}
    Options:
      Y — cập nhật phần ảnh hưởng (màn / flow đổi)
      F — sinh lại toàn bộ
      N — huỷ
    ```
- Khi sinh / sinh lại → cập nhật `| **Built from PRD** | v{prd_version hiện tại} |`, bump `| **Version** |` Design Spec, **reset `| **Status** | draft |`** (design đổi → sign-off lại), thêm row Changelog.

---

## Screen Discovery

Từ Section 4 của PRD (User Flow + Wireframe), trích xuất mọi tên screen / page / modal được nhắc tới.

Trình bày danh sách và hỏi PO xác nhận:

```
Screens detected from PRD:
  1. {Screen name 1}
  2. {Screen name 2}
  ...

Đây đã đủ các màn cho platform {active_platform} chưa?
Thêm màn còn thiếu, bỏ màn không áp dụng, hoặc xác nhận bằng Y.
```

Chờ xác nhận. Lưu danh sách đã xác nhận là `screen_list`.

---

## Figma Frame Links *(bắt buộc — một link node-level đọc được cho mỗi màn)*

Một Design Spec chỉ tốt ngang với design mà nó trỏ tới. AI **không đọc được link file
trần** (`figma.com/design/{fileKey}/...` không có `node-id`) — nó cần một
**link node-level tới từng frame cụ thể** để fetch layout, component, và token thật của
frame đó qua Figma MCP. Vậy nên thu thập một link **mỗi màn**, không phải một
link cho cả feature.

**Hỏi PO, liệt kê mọi màn trong `screen_list`:**

```
Dán link Figma frame cho từng màn bên dưới.

  Trong Figma: chọn frame → chuột phải → "Copy link to selection"
  (URL phải chứa  ?node-id=...  — đó là link per-frame mà AI đọc được)

  1. {Screen 1} : ____
  2. {Screen 2} : ____
  ...

Nếu một màn chưa có design, gõ  none  cho màn đó.
```

**Với mỗi câu trả lời:**

1. **Validate format** — URL phải khớp `figma.com/design/{fileKey}/...?node-id={nodeId}`.
   - Hợp lệ → lưu thành `figma_frames[{screen}] = {url}`, parse ra `fileKey` + `nodeId`.
   - Link file **không có `node-id`** → từ chối: "Link này trỏ tới cả file, không phải một frame. Copy lại qua chuột phải → Copy link to selection." Hỏi lại màn đó.
   - `none` → `figma_frames[{screen}] = "TBD"`, đánh dấu màn đó ❌ Missing.

2. **Fetch frame qua Figma MCP** (chỉ với link hợp lệ) — gọi `get_design_context`
   (và `get_screenshot` khi hữu ích) với `fileKey` + `nodeId` đã parse để đọc
   layout, tên component, và design token thật. Đặt mọi Screen Spec dựa trên dữ liệu
   đã fetch này; **đừng** bịa layout mà frame không thể hiện. Nếu fetch thất bại
   (permission / not found) → coi màn đó là ❌ Missing và ghi chú lỗi fetch.

3. Suy ra `figma_url` mức feature = link file (không có `node-id`) chung của các
   frame, cho row Metadata. Nếu các frame trải nhiều file, liệt kê từng cái.

**Gate bắt buộc (không abort — sinh ra draft):**
- Nếu **bất kỳ** màn nào ❌ Missing → spec được sinh dưới dạng **draft** với các màn đó
  được gắn cờ, `Status` giữ `draft` cho tới khi mọi màn có link frame đọc được, đã fetch.
  `/generate-bdd` FE/App sẽ **cảnh báo mềm** (đồng bộ Guard PRD) nếu sinh BDD khi design-spec chưa `approved`. Ghi `missing_frames = [screens]`.
- Thêm một AI Assumption cho mỗi màn thiếu: "Không có Figma frame đọc được cho {screen} — spec
  của màn này chỉ là text và không được sign off cho tới khi thêm link `node-id`."

---

## CHECKPOINT

```
CHECKPOINT — Design Spec
-------------------------
Target PRD  : {prd-file-path}
Platform    : {active_platform}
Module      : {active_module}
Service     : {active_service}
Domain      : {domain}
Screens     : {N} — {screen_list ngăn cách bởi dấu phẩy}
Figma       : {linked}/{N} màn có link frame đọc được{; missing: missing_frames ngăn cách bởi dấu phẩy}
Output path : {paths.specs_dir}/{domain}/{prd-slug}/design-spec/{TICKET-ID}-design-spec-{active_platform}-{slug}.md

{Nếu missing_frames khác rỗng}:
⚠️  {count} màn không có link Figma frame đọc được — các màn này sẽ được sinh dưới dạng
   draft chỉ-text và spec không thể sign off cho tới khi thêm link node-id.

Generate? (Y/N)
```

Chờ Y rõ ràng trước khi tiếp tục.

---

## Ngôn ngữ — Language Guard *(áp khi viết mọi section)*
# Business Language Guard — chặn thuật ngữ kỹ thuật rò vào tài liệu nghiệp vụ

Tài liệu nghiệp vụ (PRD, product-definition) mô tả **WHAT** — chỉ ngôn ngữ nghiệp vụ. Guard này chạy **mỗi khi viết hoặc sửa** prose (gen mới, áp fix `--resume`, hiệu chỉnh): **quét và xử lý** các thuật ngữ kỹ thuật/UI phổ thông bên dưới **trước khi ghi**.

> Guard này là **baseline framework**, chạy **song song** với Banned Terms của `business-dictionary.md` (cơ chế dictionary giữ nguyên; project vẫn bổ sung term đặc thù vào đó). Khi cả hai cùng áp, ưu tiên bản chuẩn của dictionary nếu có.

## Bản đồ xử lý (4 nhóm)

**Nhóm 1 — Tương tác/triển khai → DIỄN ĐẠT LẠI sang nghiệp vụ (giữ nguyên nghĩa):**

| Kỹ thuật/UI | Cách nói nghiệp vụ |
|---|---|
| re-render / render lại / reload / refresh (màn) | "hiển thị lại {tên màn}" |
| timeout | "quá thời gian chờ" |
| lỗi mạng / network error | "lỗi kết nối" |
| UI / giao diện (khi chỉ một màn) | "màn" / "màn hình" |
| click / tap | "bấm" / "chọn" |
| popup / modal (nếu chỉ là khái niệm hiển thị) | "hộp thoại" / "thông báo" |
| disable / enable (nút) | "khoá" / "mở" thao tác |
| redirect / navigate | "chuyển tới {màn}" |

**Nhóm 2 — Visual thuần → CHUYỂN Design Spec (bỏ khỏi PRD, ghi nhận lại):**
`spinner`, `loading indicator`, `animation`, `fade/slide`, màu sắc, font, layout pixel, micro-interaction → *"Chi tiết visual này thuộc Design Spec — ghi nhận để tạo Design Spec sau."*

**Nhóm 3 — Backend/contract thuần → BỎ khỏi PRD (thuộc Tech Docs):**
`API`, `endpoint`, `token/JWT`, `HTTP status`, tên class/bảng/cột DB, query, payload, header.
*(Ngoại lệ DUY NHẤT: Appendix "Existing API Contract" khi `API Source: existing` — xem Platform Strategy.)*

**Nhóm 4 — Ẩn dụ dữ liệu/cài đặt cho trạng thái nghiệp vụ → XÉT THEO NGHĨA (KHÔNG phải bảng thay thế):**

Các từ như `cờ / flag`, `biến / trường / field`, `giá trị / value`, `trả về / return`, `đọc / ghi (cờ)` **đa nghĩa** — kỹ thuật ở ngữ cảnh này, nghiệp vụ ở ngữ cảnh khác. **ĐỪNG thay máy móc.** Một từ chỉ là leak khi **cả hai** điều sau đúng:
1. Nó chỉ một **artifact lưu trữ/cơ chế** (cờ, biến, trường, giá trị-trả-về, đọc/ghi) đứng thay cho một **trạng thái/khái niệm nghiệp vụ**; VÀ
2. Khái niệm đó **đã có tên nghiệp vụ** (trong business-dictionary hoặc hiển nhiên).

→ Cả hai đúng: viết lại theo **tên nghiệp vụ**, ưu tiên term chuẩn trong business-dictionary.
→ Từ **tự nó là khái niệm nghiệp vụ**: **GIỮ NGUYÊN**.

| Reframe (là leak) | Giữ nguyên (nghiệp vụ thật) |
|---|---|
| "cờ tình trạng = chưa làm" → "con *chưa làm khảo sát*" (có term Tình trạng khảo sát) | "giá trị đơn hàng", "giá trị hợp đồng" |
| "cờ trả giá trị lạ" → "không đọc được tình trạng khảo sát" | "khách trả về sản phẩm" (hoàn hàng) |
| "đọc cờ thất bại" → "không xác định được tình trạng" | "trả kết quả học tập cho phụ huynh" |

**Neo an toàn:** lái theo business-dictionary — nếu đang diễn giải một khái niệm **đã có entry** thì dùng đúng term đó. Hỏi *"khái niệm này có tên nghiệp vụ chưa"*, KHÔNG hỏi *"từ này có bị cấm không"*.

**Luật code-format:** trong prose nghiệp vụ, **không bọc backtick/`code`** quanh giá trị/trạng thái nghiệp vụ (`chưa làm`, `đã nộp`) — code-format báo hiệu "token kỹ thuật". Dùng *nghiêng* hoặc "trong ngoặc kép". Backtick chỉ dành cho định danh code/kỹ thuật thật.

## Quy tắc áp dụng
- Quét toàn bộ text sắp ghi (User Story, AC, BR, Business Logic, Scope, Edge Cases, Assumptions…).
- Nhóm 1 → thay tại chỗ, giữ nguyên nghĩa nghiệp vụ. **Đồng bộ cách diễn đạt** với chỗ đã có sẵn trong cùng tài liệu (vd nếu "quá thời gian chờ" đã dùng ở một BR → dùng nhất quán ở mọi nơi).
- Nhóm 2 → gỡ khỏi prose nghiệp vụ + nhắc chuyển Design Spec.
- Nhóm 3 → gỡ khỏi PRD (trừ ngoại lệ brownfield).
- Nhóm 4 → **xét ngữ cảnh, KHÔNG thay máy móc**: chỉ reframe khi là ẩn dụ dữ liệu cho một khái niệm đã có tên nghiệp vụ (ưu tiên term dictionary); **giữ nguyên** khi từ mang nghĩa nghiệp vụ thật. Đồng thời bỏ backtick khỏi giá trị nghiệp vụ trong prose.
- Nếu term không có trong bản đồ nhưng rõ ràng là tên kỹ thuật/triển khai → vẫn diễn đạt lại theo tinh thần Nhóm 1, đừng để lọt.

**Checklist (dùng ở Quality Checklist của lệnh):** 0 thuật ngữ kỹ thuật/UI (re-render, UI, timeout, spinner, API/endpoint/token…) trong prose nghiệp vụ — đã diễn đạt lại (Nhóm 1) / chuyển Design Spec (Nhóm 2) / bỏ về Tech Docs (Nhóm 3); 0 ẩn dụ dữ liệu cho trạng thái đã có tên nghiệp vụ (cờ/giá trị/đọc-ghi khi là artifact — Nhóm 4) và 0 backtick bọc giá trị nghiệp vụ.


**Design Language Guard — phân tầng bề mặt (bổ sung cho guard trên):**

Design Spec là cầu nối PRD → code, có **hai loại bề mặt** với ngôn ngữ khác nhau. Nguyên tắc: **không xoá chi tiết kỹ thuật — dồn về đúng tầng** (giống altitude AC/BR ở PRD).

- **Tầng A — bề mặt đọc (BẮT ngôn ngữ nghiệp vụ/UX):** §1 Screen Inventory (mọi cột), §2 Layout, cột "Hành vi UI" của Screen States, Actions & Navigation, blockquote mục đích màn, §5 AC-UI. Mô tả theo **vai trò/ý đồ** ("thanh tiến độ", "nút chính", "trạng thái chưa chọn đáp án"). **CẤM ở tầng này:** tên layer/variant Figma (`Q1--Selected`, `Base Design`), mã token/màu hex/số đo px-pt (`Primary/500 #0876B1`, `padding 16px`), định danh code, và ẩn dụ dữ liệu (Nhóm 4 của guard trên: cờ/giá trị/đọc-ghi khi là artifact).
- **Tầng B — cột/phụ lục kỹ thuật chuyên dụng (được giữ code):** Component Inventory (`Code Component`, `Import Path`), cột **Figma Frame** (link), bảng **Design Token** (phụ lục). Định danh Figma/code/token **chỉ** được xuất hiện ở đây.

Một state mô tả ở tầng A bằng lời ("chưa chọn đáp án → nút vô hiệu"); tên variant Figma của state đó (nếu cần) nằm ở cột Figma Frame / Component Inventory, KHÔNG lẫn vào prose.

## Generation Rules

Áp dụng các quy tắc này nhất quán khi sinh mọi section:

**Component mapping (C.M — bắt buộc):**
- Với mỗi component được tham chiếu, kiểm tra `figma-components/{active_module}.md` (đã nạp trong context).
- ✅ Matched → dùng đúng `Code Component` và `Import Path` từ catalog.
- ⚠️ Matched nhưng `[TODO]` → đánh dấu ô component là `[TODO — implementation pending]`.
- ❌ Không có trong catalog → đánh dấu `[NEW — confirm with designer before generating code]`.
- Không bao giờ bịa tên component hay import path.

**Section thích ứng theo platform:**
- Section 3 (Interaction Patterns) và Section 4 (Platform Considerations) thích ứng theo `active_platform`:
  - `web` → gồm responsive breakpoint, trạng thái hover/focus, keyboard navigation, accessibility.
  - `app` / `app-ios` / `app-android` → gồm gesture, safe area, touch target tối thiểu, navigation pattern, deep link, permission, hành vi offline.
  - Chỉ sinh section liên quan tới `active_platform`. Bỏ hẳn section của platform kia.

**Figma grounding (bắt buộc):**
- Với mỗi màn có frame đã fetch (`figma_frames[screen]` là link hợp lệ), dựa Layout,
  Component Inventory, và Screen States trên **dữ liệu Figma đã fetch** — tên component thật,
  token thật, cấu trúc frame thật. Đừng mâu thuẫn hay bịa layout.
- Dùng đúng URL `figma_frames[screen]` từng-màn trong Screen Inventory, header mỗi Screen
  Spec, và Figma Summary — không bao giờ dùng fragment giả `{figma_url}#screen1`.
- Với màn ❌ Missing: sinh draft chỉ-text từ PRD, thêm tiền tố vào Screen Spec
  `> [DRAFT — no Figma frame; do not sign off]`, và để ô Figma là ❌ Missing.

**Screen states (bắt buộc mỗi màn):**
- Mỗi màn phải mô tả tối thiểu: `default`, `loading`, `error`.
- Thêm `empty` khi màn có thể hiển thị trạng thái không dữ liệu.
- Thêm `success` khi một action hoàn tất tạo ra trạng thái xác nhận riêng biệt.
- Nếu một state không áp dụng → đánh dấu `N/A` kèm lý do ngắn.

---

## Generate

Ghi `{paths.specs_dir}/{domain}/{prd-slug}/design-spec/{TICKET-ID}-design-spec-{active_platform}-{slug}.md`:

````markdown
# {TICKET-ID} {Feature Name} — Design Spec [{active_platform}]

---

## Metadata

| Field              | Value                                                         |
|--------------------|---------------------------------------------------------------|
| **Spec ID**        | {TICKET-ID}-DS-{active_platform}                              |
| **Version**        | 1.0                                                           |
| **Status**         | draft                                                         |
| **Platform**       | {active_platform}                                             |
| **Module**         | {active_module}                                               |
| **Service**        | {active_service}                                              |
| **Domain**         | {domain}                                                      |
| **Business PRD**   | [{TICKET-ID}](../{TICKET-ID}-{prd-slug}.md)                   |
| **Built from PRD** | v{prd_version — `\| **Version** \|` của PRD lúc sinh; dùng phát hiện drift} |
| **Figma**          | {figma_url — link file feature} ({linked}/{N} frame đã link)  |
| **Author**         | {tên PO hoặc "AI-assisted"}                                   |
| **Created**        | {YYYY-MM-DD}                                                  |
| **Updated**        | {YYYY-MM-DD}                                                  |

---

# 1. Danh mục màn hình (Screen Inventory)

> **Tầng A:** cột **Điểm vào** và **Ghi chú** viết bằng ngôn ngữ nghiệp vụ — mô tả điều kiện/ý đồ. KHÔNG liệt kê states, KHÔNG tên component/variant Figma (states thuộc §2; tên Figma thuộc cột Figma Frame / §2). Vd Điểm vào: "khi con *chưa làm khảo sát* và có ít nhất một câu hỏi" — KHÔNG "cờ tình trạng = `chưa làm`".

| # | Tên màn hình | Điểm vào | Figma Frame | Ghi chú |
|---|-------------|-------------|-------------|-------|
| 1 | {Màn hình 1}  | {điều kiện nghiệp vụ để vào màn — vd: "sau khi hoàn tất Nhập thông tin con và con chưa làm khảo sát"} | [Frame]({figma_frames[Screen 1]}) | {ghi chú nghiệp vụ nếu có — KHÔNG states/tên Figma} |
| 2 | {Màn hình 2}  | {điểm vào — ngôn ngữ nghiệp vụ} | [Frame]({figma_frames[Screen 2]}) / ❌ Missing | |

---

# 2. Đặc tả màn hình (Screen Specs)

<!--
  Lặp lại block này cho mỗi màn trong Screen Inventory.
  Mỗi màn phải có: Layout, Component Inventory, Screen States, Actions & Navigation.
-->

## Màn hình 1: {Tên màn hình}

**Figma**: [{Tên frame}]({figma_frames[Screen 1]})   <!-- ❌ Missing → thêm tiền tố cho màn này `> [DRAFT — no Figma frame; do not sign off]` -->

### Layout

{Mô tả layout theo CẤU TRÚC & Ý ĐỒ bằng lời: thứ tự section trên→dưới, vai trò từng vùng, hệ phân cấp thị giác, hành vi co giãn. **KHÔNG rải mã token / màu hex / số đo px** trong văn xuôi — số đo & token cụ thể đặt ở cột Component Inventory và bảng Design Token (phụ lục, Tầng B). Vd: "một cột dọc, thanh tiến độ ở đầu, khu nội dung chính giữa, nút hành động cố định đáy màn".}

### Component Inventory

| Component (Figma)   | Code Component  | Import Path            | States                          | Ghi chú            |
|---------------------|-----------------|------------------------|---------------------------------|--------------------|
| {Figma/Button/Primary} | Button       | @/components/ui/Button | default, loading, disabled      |                    |
| {Figma/Input/Text}  | TextInput       | @/components/ui/Input  | default, focus, error, disabled |                    |
| {Figma/Card/Order}  | OrderCard       | @/features/{domain}/components/OrderCard | default, skeleton |           |

### Screen States

<!-- Tầng A: Trigger & Hành vi UI mô tả bằng lời quan sát được. KHÔNG tên variant Figma (Q1--Selected), KHÔNG "API/render/spinner" (→ "đang tải / hiển thị lại / vùng chờ"). Tên component/variant nếu cần → cột Figma / Component Inventory. -->

| State     | Trigger                                     | Hành vi UI (mô tả quan sát được)                    |
|-----------|---------------------------------------------|------------------------------------------------------|
| default   | Màn đã hiển thị, có dữ liệu                   | {Mô tả giao diện người dùng thấy}                    |
| loading   | Đang tải dữ liệu                            | {Vùng chờ / vị trí báo đang tải}                     |
| error     | Tải dữ liệu thất bại / dữ liệu không hợp lệ  | {Thông báo lỗi + hành động khôi phục}                |
| empty     | Không có dữ liệu để hiển thị                 | {Trạng thái trống + lời mời hành động}               |
| success   | Thao tác hoàn tất (nếu có)                   | {Xác nhận / chuyển màn / đổi trạng thái}             |

### Actions & Navigation

| Action          | Trigger                        | Kết quả                                         |
|-----------------|--------------------------------|-------------------------------------------------|
| {Tên action}    | Tap/click {tên phần tử}        | Điều hướng tới {Màn hình N} / Mở {Tên modal}    |
| {Tên action}    | Swipe trái trên {list item}    | Hiện xác nhận xoá                               |
| {Back / Cancel} | Cử chỉ back / nút Cancel       | Quay lại {màn trước} mà không lưu               |

---

<!--  Lặp lại block ## Màn hình N cho mỗi màn bổ sung  -->

---

# 3. Pattern tương tác (Interaction Patterns)

<!--
  Platform web: gồm section A + B. Bỏ section C.
  Platform app: gồm section C. Bỏ section A + B.
-->

<!-- ═══════════════════════════ CHỈ WEB ═══════════════════════════ -->

## A. Hành vi Responsive  *(chỉ web)*

| Breakpoint | Width      | Thay đổi layout                                    |
|------------|------------|----------------------------------------------------|
| Mobile     | < 768px    | {1 cột, bottom navigation bar, CTA full-width}     |
| Tablet     | 768–1279px | {grid 2 cột, sidebar thu gọn, tab navigation}      |
| Desktop    | ≥ 1280px   | {layout đầy đủ, sidebar hiển thị, max-width 1440px}|

## B. Hover / Focus / Keyboard  *(chỉ web)*

| Phần tử        | Trạng thái Hover              | Trạng thái Focus                | Phím tắt          |
|----------------|-------------------------------|---------------------------------|-------------------|
| Primary button | Background → {color.hover}    | Outline 2px {color.focus}       | Enter / Space     |
| Text input     | Border → {color.border.hover} | Border → {color.primary}, label nổi | Tab to focus |
| Dropdown       | Highlight nền                 | Giống hover + ring              | Phím mũi tên điều hướng |

<!-- ═══════════════════════════ CHỈ APP ═══════════════════════════ -->

## C. Cử chỉ & Điều hướng  *(chỉ app)*

| Cử chỉ            | Màn / Phần tử             | Hành vi                                               |
|-------------------|---------------------------|-------------------------------------------------------|
| Cử chỉ back (iOS swipe-right / Android back) | Mọi màn | {Quay lại màn trước / Hiện dialog "Discard changes?"} |
| Pull-to-refresh   | {Tên màn}                 | Refresh dữ liệu, spinner ở trên cùng                  |
| Swipe trái trên row | {Tên list item}         | Hiện action {Delete / Archive}                        |
| Long press        | {Tên phần tử}             | {Context menu / chế độ chọn}                          |
| Pinch / zoom      | {Image viewer}            | Scale ảnh, double-tap để reset                        |

### Navigation Pattern  *(chỉ app)*

```
{Vẽ navigation stack cho feature này, vd:
  BottomTab(Home) → FeatureListPage → FeatureDetailPage → EditPage
  BottomTab(Home) → FeatureListPage ↘ (modal) CreatePage
}
```

Entry: {người dùng vào feature này thế nào — tab / deeplink / push từ màn khác}
Exit: {người dùng rời thế nào — back stack / chuyển tab / deeplink out}

### Platform Conventions  *(chỉ app)*

| Khía cạnh                | Hành vi iOS                             | Hành vi Android                         |
|--------------------------|-----------------------------------------|-----------------------------------------|
| Navigation bar           | {Nút back trên-trái, title canh giữa}   | {Mũi tên Up trên-trái, title canh trái} |
| Sheet / bottom modal     | {UISheetPresentation, hiện grabber}     | {BottomSheet, drag handle}              |
| Alert / confirm dialog   | {UIAlertController, action canh phải}   | {Material AlertDialog, action canh trái}|
| Loading indicator        | {UIActivityIndicatorView, center}       | {CircularProgressIndicator}             |
| Toast / snackbar         | {Custom toast, bottom center}           | {Material Snackbar, bottom}             |

---

# 4. Cân nhắc theo Platform (Platform Considerations)

<!--
  Web: gồm section A. App: gồm section B. Bỏ section không áp dụng.
-->

<!-- ═══════════════════════════ CHỈ WEB ═══════════════════════════ -->

## A. Accessibility  *(chỉ web)*

- [ ] Mọi phần tử tương tác tới được bằng phím Tab — không có keyboard trap
- [ ] Focus trap bên trong modal dialog (Tab chỉ chạy vòng trong modal)
- [ ] Nút chỉ-icon có `aria-label` mô tả hành động
- [ ] Cập nhật nội dung động (loading → loaded) thông báo qua `aria-live`
- [ ] Tương phản màu đạt WCAG AA: text ≥ 4.5:1, text lớn ≥ 3:1
- [ ] Input form có label hiển thị (không chỉ placeholder)
- [ ] Message lỗi liên kết với input qua `aria-describedby`

<!-- ═══════════════════════════ CHỉ APP ═══════════════════════════ -->

## B. Thiết bị & OS  *(chỉ app)*

- [ ] Áp dụng safe area insets ở mọi màn — trên (status bar) và dưới (home indicator)
- [ ] Touch target tối thiểu: 44×44pt (iOS) / 48×48dp (Android)
- [ ] Đã test trên màn nhỏ: rộng 375pt (iPhone SE) / rộng 360dp (Android phổ biến)
- [ ] Deep link entry: `{scheme}://{host}/{path}` → vào {tên màn} với {param} đã điền sẵn
- [ ] Permission gate: {liệt kê permission cần — Camera / Location / Notification}
  - {Permission}: yêu cầu ở {tên màn} với copy lý do: "{copy TBD}"
- [ ] Hành vi offline / không mạng:
  - {Tên màn}: hiện dữ liệu cache + banner offline
  - {Tên action}: disable nút, hiện tooltip "Requires connection"
- [ ] Dark mode: mọi màn đã test dark mode — không có màu hardcode

---

# 5. AC-UI — Tiêu chí chấp nhận về Design

> Được **PO + Designer** cùng review và sign off trước khi sinh BDD.
> Bổ sung cho (không thay thế) AC mức nghiệp vụ trong [Business PRD]({prd-path}).

| ID     | Tiêu chí chấp nhận                                                             | Verified by     |
|--------|--------------------------------------------------------------------------------|-----------------|
| AC-UI1 | Mọi màn khớp frame Figma đã duyệt trong dung sai design-system                  | Designer        |
| AC-UI2 | Trạng thái đang tải xuất hiện gần như tức thì (≤200ms) khi màn bắt đầu tải dữ liệu | QA              |
| AC-UI3 | Mọi message lỗi đều hiển thị, rõ ràng, và kèm action khôi phục                  | PO              |
| AC-UI4 | Empty state có illustration và call-to-action rõ ràng                          | PO + Designer   |
| AC-UI5 | {Riêng platform — vd web: "Mọi màn pass kiểm tra tương phản WCAG AA"}         | QA              |
| AC-UI6 | {Riêng platform — vd app: "Cử chỉ back ở mọi màn quay về đúng màn trước"}     | QA              |
| AC-UI7 | {Tiêu chí UI riêng của feature từ section wireframe của Business PRD}           | PO              |

---

# Appendix

## Tóm tắt Figma

| Màn hình        | Figma Frame                          | Trạng thái Link / Fetch              |
|-----------------|--------------------------------------|--------------------------------------|
| {Màn hình 1}    | [Link]({figma_frames[Screen 1]})     | ✅ Đã link & fetch                   |
| {Màn hình 2}    | —                                    | ❌ Missing — không có link node-id   |

## Design Token đã tham chiếu

| Token                 | Value         | Dùng ở                          |
|-----------------------|---------------|---------------------------------|
| `color.primary`       | {#hex}        | Primary button, link, active state |
| `color.surface`       | {#hex}        | Nền card                        |
| `spacing.md`          | {16px / 4}    | Khoảng cách dọc tiêu chuẩn      |
| `typography.heading2` | {font/size}   | Title màn hình                  |

## Tài liệu tham khảo

- [{TICKET-ID}]({prd-path}) — Business PRD (nguồn của AC, UC, BR)
- {[Design Spec khác](./other-ds.md) — nếu feature này dùng chung màn}

## Giả định AI

> Mỗi giả định dưới đây được đưa ra vì input PO chưa đầy đủ.
> PO phải review và confirm trước khi sign-off.

- {Giả định 1 — [AI DRAFT]}
- {Một dòng cho mỗi màn ❌ Missing: "Không có Figma frame đọc được cho {screen} — draft chỉ-text; chặn sign-off cho tới khi thêm link node-id."}

---

## Changelog

| Version | Date         | Changes         |
|---------|--------------|-----------------|
| 1.0     | {YYYY-MM-DD} | Initial version |

<!--
  NEXT STEPS:
  1. Điền các link Figma frame còn ❌ Missing (link node-id) — chạy lại để fetch & ground chúng.
  2. Chia sẻ với Designer — xác minh link Figma, cập nhật component inventory.
  3. PO + Designer sign off: đổi Status → "approved" (chỉ cho phép khi 0 màn ❌ Missing).
  4. Chạy /generate-bdd "{prd-file}" — BDD dùng AC-UI từ spec này cho FE scenario.
-->
````

---

## Self-Review Gate *(cổng tự-rà — bắt buộc chạy trước khi ghi)*

- [ ] Mọi màn trong Screen Inventory có Screen Spec đầy đủ ở Section 2
- [ ] Mọi màn có tối thiểu các state: default, loading, error
- [ ] Mọi component Figma đã map trong Component Inventory — chưa map thì gắn cờ `[NEW]` hoặc `[TODO]`
- [ ] Chỉ sinh section liên quan platform ở Section 3 (không có section web trong doc app, và ngược lại)
- [ ] Chỉ sinh section liên quan platform ở Section 4
- [ ] Các mục AC-UI testable (pass/fail rõ ràng, không phải "looks good")
- [ ] Link cross-reference Business PRD là relative path hợp lệ
- [ ] Mọi màn có link Figma frame node-level (`?node-id=`) — và các màn có link đã được fetch qua Figma MCP và dùng để ground spec
- [ ] Mỗi màn ❌ Missing được gắn cờ trong spec (`> [DRAFT — no Figma frame...]`), liệt kê trong Figma Summary, và có một AI Assumption
- [ ] Nếu bất kỳ màn nào ❌ Missing → Status giữ `draft` (generate-bdd FE/App cảnh báo mềm, không chặn cứng)
- [ ] **Ngôn ngữ Tầng A sạch:** không tên layer/variant Figma, mã token/hex/px, hay ẩn dụ dữ liệu (cờ/giá trị/đọc-ghi khi là artifact) lọt vào §1, Layout, Screen States, Actions, AC-UI — định danh kỹ thuật chỉ ở Component Inventory / Design Token / cột Figma. Backtick không bọc giá trị nghiệp vụ. Khái niệm đã có trong business-dictionary dùng đúng term chuẩn.

> **Đây là CỔNG, không phải nhắc nhở:** chạy từng mục trên TRƯỚC khi ghi file. Mục nào **FAIL** → **bắt buộc**:
> 1. ghi một dòng `⚠️ {mục thiếu — màn/section cụ thể}` vào section **"Giả định AI"** của file (đừng để lọt im lặng), VÀ
> 2. giữ `| **Status** | draft |` (chưa đủ điều kiện để PO+Designer sign-off `approved`).
> KHÔNG xuất design-spec như "hoàn chỉnh" khi còn mục fail chưa gắn cờ.

---

## Output

# Report Footer — Định dạng output chuẩn cho mọi lệnh

Mọi report của lệnh phải kết thúc bằng section footer chuẩn này.

## Status Badge

Chọn một theo kết quả:
- `✅ Complete` — mọi bước thành công, không có vấn đề
- `❌ Failed` — lệnh không hoàn thành được do lỗi chặn
- `⚠️ Warnings` — hoàn thành nhưng có vấn đề không chặn, nên review lại

## Output Artifacts

Liệt kê mọi file được tạo hoặc sửa bởi lệnh này:
```
Output Artifacts:
  {created|updated} {file-path} ({mô tả ngắn})
  {created|updated} {file-path} ({mô tả ngắn})
```

Nếu không ghi file nào (vd: lệnh review hoặc phân tích) → ghi `Output Artifacts: none (read-only)`.

## Pipeline Position

In một sơ đồ pipeline một dòng, đánh dấu phase của lệnh HIỆN TẠI bằng `◀ bạn ở đây`,
để người dùng luôn thấy lệnh này nằm ở đâu trong luồng end-to-end:

```
Discovery → PRD → [Design Spec] → BDD → Tech Design → Code → Dev Self-Check → QC → Trace Audit
```

Tìm lệnh hiện tại trong bảng phase dưới đây và đánh dấu **phase của nó** trong sơ đồ trên:

| Phase | Commands |
|-------|----------|
| Discovery | `/define-product` |
| PRD | `/generate-prd` · `/refine-prd` · `/review-context` (PRD) |
| Design Spec | `/generate-design-spec` |
| BDD | `/generate-bdd` · `/review-context` (BDD) |
| Tech Design | `/generate-tech-docs` · `/map-testids` · `/review-tech-docs` |
| Code | `/generate-code` · `/review-code` |
| Dev Self-Check | `/dev-gen-test` · `/dev-run-test` · `/dev-smoke-test` |
| QC | `/qc-analyze` · `/qc-plan` · `/qc-design-test` · `/qc-review` · `/qc-run-test` · `/qc-report` |
| Trace Audit | `/validate-traces` |

Với **lệnh review**, thêm vòng review 3 bước và đánh dấu bước hiện tại, vd:
`Vòng review: [① phân tích ◀] → ② Review Board → ③ --resume`.

**Lệnh xuyên suốt** (`/sync`, `/update-framework`, `/fix-bug`, `/debug`, `/learn`,
`/report-bug`, `/propose-scenario`, `/generate-spec-manifest`) nằm ngoài pipeline tuyến tính —
**bỏ hẳn dòng Pipeline** cho các lệnh này (đừng cố nhét chúng vào sơ đồ).

## Gợi ý lệnh tiếp theo

Gợi ý lệnh kế tiếp hợp lý theo phase của workflow:

| Lệnh hiện tại           | Gợi ý lệnh tiếp theo                          |
|-------------------------|-----------------------------------------------|
| /setup-ai-first         | `/define-product` để bắt đầu feature đầu tiên |
| /define-product         | `/generate-prd {product-definition-file}`     |
| /generate-prd           | `/refine-prd {prd-file}` rồi `/review-context {prd-file}` |
| /refine-prd             | Mở Review Board → cập nhật PRD → `/review-context {prd-file}` |
| /review-context (PRD)   | Khi 0 critical → PO đặt `Status: approved`, rồi FE/App: `/generate-design-spec {prd-file}` (→ design sign-off → BDD); BE: `/generate-bdd {prd-file}`. Còn critical/NEEDS_FIX → sửa PRD (giữ draft) |
| /generate-design-spec   | Designer review → xác nhận link Figma → PO + Designer sign-off → `/generate-bdd {prd-file}` |
| /generate-bdd           | `/review-context {feature-file}` để kiểm tra độ phủ |
| /review-context (BDD)   | `/generate-tech-docs {UC-ID}` nếu APPROVED; sinh lại nếu NEEDS_FIX |
| /qc-analyze             | `/qc-plan {UC-ID}` (xử lý các gap blocker 🔴 trước) |
| /qc-plan                | `/qc-design-test {UC-ID}`                     |
| /qc-design-test         | `/qc-review {UC-ID}` (review test-case)       |
| /qc-review (test-case)  | `/qc-run-test {UC-ID}` nếu APPROVED; sửa TC nếu NEEDS_FIX |
| /qc-run-test            | `/qc-report {UC-ID}` rồi `/qc-review {UC-ID}` (review script) |
| /qc-review (script)     | `/qc-report {UC-ID}` rồi tạo PR nếu APPROVED |
| /qc-report              | `/validate-traces {UC-ID}` để làm mới Living Docs (qc_status) |
| /generate-tech-docs     | `/review-tech-docs {tech-design-file}`        |
| /review-tech-docs       | `/generate-code {feature-file}` nếu APPROVED; sửa doc nếu NEEDS_FIX |
| /generate-code          | Lần gen đầu → `/review-code {UC-ID}`; gen lại → `/dev-gen-test {UC-ID}` |
| /dev-gen-test         | `/dev-run-test {UC-ID}`                          |
| /dev-run-test (passing)    | `/review-code {UC-ID}`                        |
| /dev-run-test (failing)    | `/fix-bug {ticket-id}` hoặc `/debug {error}`    |
| /review-code            | `/dev-smoke-test {UC-ID}` hoặc tạo PR            |
| /dev-smoke-test             | Tạo PR và link tới ticket                  |
| /validate-traces        | DRIFT/UNTRACKED → `/generate-code {UC-ID}`; GAP → `/dev-gen-test {UC-ID}`; tất cả OK → tạo PR |
| /fix-bug                | Tạo PR và link tới ticket                  |
| /debug                  | `/fix-bug {ticket-id}` nếu cần sửa          |
| /report-bug             | Gửi cho dev (`/fix-bug {BUG-ID}`); nếu thiếu coverage → `/propose-scenario {UC-ID}` |
| /propose-scenario       | Báo PO/Dev review proposal trong `feedback/bdd-proposals/` |
| /learn                  | Tiếp tục làm việc — lesson áp dụng ở lệnh kế tiếp |
| /sync                   | `/validate-traces` để xem độ phủ đầy đủ; xử lý mọi `📥 tester feedback` được nêu |
| /update-framework       | Review `git diff .agent/`, commit; `/sync` để đồng bộ nội dung dự án |

Định dạng footer như sau:
```
---
Status   : {badge}
{khối Output Artifacts}
Pipeline : Discovery → PRD → [BDD ◀ bạn ở đây] → Tech Design → Code → Dev Self-Check → QC → Trace Audit
           (lệnh review) Vòng review: [① phân tích ◀] → ② Review Board → ③ --resume
Next     : {lệnh gợi ý kèm ví dụ tham số}
```
*(Bỏ dòng `Pipeline` cho các lệnh xuyên suốt liệt kê ở trên.)*


{Nếu missing_frames rỗng}:
```
/generate-design-spec Hoàn tất — {TICKET-ID} [{active_platform}]
---
Status : ✅ Complete — cả {N} màn đã link & fetch từ Figma
Output Artifacts:
  created {paths.specs_dir}/{domain}/{prd-slug}/design-spec/{TICKET-ID}-design-spec-{active_platform}-{slug}.md  (v1.0)
Next   : Chia sẻ với Designer → PO + Designer sign-off (Status: approved)
         → /generate-bdd {prd-file}  (sinh BDD theo service; đọc AC-UI từ Design Spec)
```

{Nếu missing_frames khác rỗng}:
```
/generate-design-spec Hoàn tất (DRAFT) — {TICKET-ID} [{active_platform}]
---
Status : ⚠️ Warnings — {count} màn không có link Figma frame đọc được: {missing_frames ngăn cách bởi dấu phẩy}
Output Artifacts:
  created {paths.specs_dir}/{domain}/{prd-slug}/design-spec/{TICKET-ID}-design-spec-{active_platform}-{slug}.md  (v1.0, draft)
Next   : 🔒 Khuyến nghị hoàn tất sign-off trước (đủ link Figma node-id); /generate-bdd FE/App sẽ cảnh báo nếu design-spec chưa approved.
         1. Trong Figma: chọn mỗi frame còn thiếu → chuột phải → Copy link to selection
         2. Chạy lại /generate-design-spec {prd-file} → AI fetch & ground các frame mới
```
