# /generate-code — Sinh Implementation Code

## 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 ở Bước 1 là một file `.feature` hoặc UC-ID. Nếu `$ARGUMENTS` là UC-ID, tìm file feature khớp bằng cách glob `{paths.specs_dir}/{domain}/*/bdd/**/{UC-ID}*.feature` (wildcard `*` cho prd-slug chưa biết, `**` đệ quy để phủ các thư mục con platform `web/`·`app/`·`system/`); lấy `domain` + `prd_slug` từ path khớp. Cũng kiểm tra `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{@trace.platform}.tsv` tìm drift (new vs drifted vs synced).*

## 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.


---

## Scope Lock

Lệnh này giới hạn nghiêm ngặt trong **một file feature** được truyền qua `$ARGUMENTS`:

- File feature: `{path chính xác từ $ARGUMENTS}`
- UC: `{UC-ID}` (đọc từ `@trace.id` trong header file đó)

**KHÔNG đọc hay implement scenario từ bất kỳ file `.feature` nào khác** trong cùng folder domain, kể cả khi chúng dùng chung entity, khái niệm domain, hay tên service.

> **⚠️ Scope Lock CẤM implement UC khác — KHÔNG cấp phép XOÁ code UC khác.** Nhiều UC của cùng nghiệp vụ thường **dùng chung file code** (cùng Controller/Service). Khi một file như vậy đã tồn tại, **mọi member có sẵn là BẤT KHẢ XÂM PHẠM** — kể cả method mang `@trace.implements` của UC khác. Bảo toàn chúng là **YÊU CẦU BẮT BUỘC**, KHÔNG phải vi phạm scope. Bạn ĐƯỢC đọc các tag `@trace.implements` có sẵn trong file code (để biết member nào thuộc UC khác mà giữ) — điều bị cấm chỉ là **implement scenario** của `.feature` khác. Không bao giờ tái tạo file chung "chỉ gồm scenario của UC này" — đó là cách xoá nhầm nghiệp vụ UC trước.

---

## Context Load (bổ sung)

Đọc:
1. Chỉ file `.feature` đã giới hạn scope
2. Tech-doc gộp của PRD tại `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` (nếu tồn tại) — **tra §10 UC Coverage làm mục lục** để định vị scenario/section của UC đang sinh; **endpoint liên quan = §4.1 entries mà §5 lane của UC này gọi tới** (đừng lấy endpoint/section của UC khác). Từ đó đọc §4 API, §4.5 client, §5 flow của đúng UC này
3. CLAUDE.md §architecture + §coding_standards
4. **(chỉ FE/App)** Design Spec — nạp qua **Guard** bên dưới (gate approved/độ-tươi + sanity), là nguồn của màn hình, component inventory, và link Figma frame từng-màn.

---

## Guard — BDD & Design Spec đã sẵn sàng chưa *(cảnh báo MỀM — đồng bộ generate-bdd)*

**BDD (mọi platform) — DS1:** đọc `# @trace.status:` từ header `.feature` target.
- `approved` → tiếp tục. Khác `approved` → CHECKPOINT mềm:
  ```
  ⚠️  BDD {UC-ID} đang @trace.status: {status} (chưa duyệt). Code sinh từ BDD chưa chốt có thể phải làm lại.
     Vẫn sinh code bây giờ? (Y/N)
  ```
  Chỉ tiếp khi chọn Y.

**Design Spec (chỉ FE/App — `@trace.platform` = web/app) — DS2:** định vị `{paths.specs_dir}/{domain}/{prd-slug}/design-spec/{TICKET-ID}-design-spec-{@trace.platform}-{slug}.md`, đọc `| **Status** |` + `| **Built from PRD** |`:
- Không có file, HOẶC `Status ≠ approved`, HOẶC còn màn ❌ Missing, HOẶC `Built from PRD` ≠ `| **Version** |` PRD hiện tại (lỗi thời) → CHECKPOINT mềm (liệt kê lý do + "Vẫn dùng design-spec này? (Y/N)"). Nếu Y mà KHÔNG có design-spec → sinh UI từ BDD + Wireframe PRD (thiếu chi tiết visual).
- **Sanity-scan** (như generate-bdd bước 2.5): màn thiếu state loading/error/empty, AC-UI không testable, component `[NEW]`/`[TODO]` chưa chốt, còn ❌ Missing frame → cảnh báo trong cùng CHECKPOINT.
- `Status: approved` VÀ `Built from PRD` khớp PRD hiện tại VÀ sạch cờ đỏ → dùng làm nguồn màn hình / component / Figma.

**Tech-doc contract (chỉ backend/system) — DS3:** *áp dụng khi lần sinh này tạo code **backend** — `@trace.platform = system`, HOẶC chế độ default (không `--phase`) với `platform_type = backend`. Bỏ qua FE `--phase=ui`/`--phase=integration` (đã có nguồn shape riêng ở Phase Detection).*

Định vị tech-doc gộp `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`, kiểm phần backend của UC này (§4.1 Endpoints / §4.2 Request-Response / §4.3 Error):
- **Brownfield** — nếu PRD Metadata `API Source: existing`: contract nằm ở Appendix "Existing API Contract" của PRD → nguồn hợp lệ, **bỏ qua DS3** (không cảnh báo).
- **Thiếu tech-doc, HOẶC thiếu §4 contract cho UC này** → CHECKPOINT chặn mềm:
  ```
  ⚠️  Sinh code BACKEND cho {UC-ID} mà chưa có contract chốt (thiếu tech-doc §4 API).
     Code BE không có contract sẽ phải TỰ CHẾ shape API/DTO/error → rủi ro bịa, rework nặng.
     Khuyến nghị: /generate-tech-docs {system .feature} → /review-tech-docs (approved) rồi sinh code.
     Vẫn sinh bây giờ? (Y/N)
  ```
  Chỉ tiếp khi Y. *(Khác FE: FE degrade êm để prototype qua mock; BE thì contract là sản phẩm chính → chặn mềm.)*
- **Có §4 nhưng `@trace.status = draft/in-review`, HOẶC §12 GAP Register còn 🔴 blocker `open` chạm UC này** → WARN (không chặn): "contract chưa chốt / còn {n} blocker-GAP open — có thể phải rework khi contract đổi."
- **Có §4 + `@trace.status: approved` + 0 blocker-GAP** → dùng §4 làm nguồn contract (shape DTO/endpoint/error lấy nguyên văn từ đây, KHÔNG tự chế).

---

## Phase Detection

Parse `$ARGUMENTS` tìm flag `--phase`:

| Flag | Ý nghĩa |
|---|---|
| `--phase=ui` | FE Phase 1 — sinh UI + layer mock API từ System BDD contract |
| `--phase=integration` | FE Phase 2 — thay mock adapter bằng lời gọi API thật từ tech docs |
| *(không có)* | Default — full implementation (BE hoặc full-stack không tách mock) |

**Nếu `--phase` được set — xác nhận platform:**
Đọc `@trace.platform` từ header file feature.
- Nếu `system` → cảnh báo: "Flag `--phase` không áp dụng cho system BDD (hướng BE). Tiếp tục với chế độ default." Coi như không có flag.
- Nếu `web` hoặc `app` → tiếp tục logic phase bên dưới.

**Nếu `--phase=ui`:**
Phân giải **nguồn shape của mock** (hybrid — ưu tiên contract thật, fallback về System BDD):
- **BE contract** — tech-doc gộp `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`, phần backend của UC này (§4.1 Endpoints / §4.2 Request-Response / §4.3 Error). Nếu tồn tại, **shape** port/DTO của mock adapter (field request/response, type, error code) lấy từ đây → `mock_source = contract`. Chính xác nhất; không phải rework shape lúc integration.
- **System BDD** `{paths.specs_dir}/{domain}/{prd-slug}/bdd/system/{TICKET-ID}*.feature` — luôn nạp mệnh đề `Then` để lấy **behavior + giá trị fixture**. Nếu không có §4 API contract, shape cũng được infer từ đây → `mock_source = system-bdd`, và WARN:
  ```
  ⚠ Không tìm thấy §4 API contract — shape mock được infer chỉ từ System BDD.
    System BDD mô tả behavior, không phải full request/response shape — mock
    có thể khác API thật; dự kiến điều chỉnh ở --phase=integration.
    (Khuyến nghị: để BE publish {prd-slug}/tech-docs/{TICKET-ID}-tech-design.md (§4) trước để có mock chính xác.)
  ```
- Nếu System BDD cũng thiếu → cảnh báo "Không tìm thấy System BDD — layer mock sẽ dùng fixture placeholder." Tiếp tục.

Lưu `mock_source` (`contract` | `system-bdd`) cho các tag mock bên dưới.

**Rồi chạy Figma Dev Mode MCP Check bên dưới** trước khi sinh UI — link frame của Design
Spec là visual contract, và MCP local đọc chúng với độ trung thực cao hơn nhiều so với link web trần.

---

## Figma Dev Mode MCP Check *(chỉ sinh UI FE/App)*

*Chỉ chạy khi `platform` là `web`/`app` VÀ đang sinh UI (`--phase=ui`, hoặc chế độ default
cho feature FE/App). Bỏ qua hoàn toàn với BE / platform `system`.*

PO viết Design Spec từ **link web Figma** (read-only, giới hạn). Để codegen,
**Figma Dev Mode MCP server local** (tích hợp trong **app desktop** Figma) cho nhiều hơn
nhiều: layout chính xác, **variable/token** design, mapping component **Code Connect**,
selection context, và code snippet — những thứ một URL web đơn không trả về được.

**Step 1 — Phát hiện MCP local.** Kiểm tra xem Figma Dev Mode MCP server có kết nối không
(một Figma tool kiểu `get_design_context` / `get_code` sẵn có qua MCP).

**Step 2 — Nếu CHƯA kết nối → gợi ý dev bật nó, rồi chờ:**

```
🎨 Không phát hiện Figma Dev Mode MCP.
   Để có code FE chính xác (token, component, Code Connect thật), dùng server LOCAL:

   1. Mở app Figma DESKTOP (không phải browser)
   2. Mở file/frame của feature này
   3. Bật Dev Mode MCP server:
        Menu Figma → Preferences → "Enable Dev Mode MCP Server"
        (cần Dev hoặc Full seat; server chạy ở http://127.0.0.1:3845)
   4. Đảm bảo MCP server này đã được thêm vào config MCP của Claude Code
   5. Chọn frame của màn bạn đang implement, rồi tiếp tục

   Gõ C để tiếp tục khi đã bật, hoặc S để skip (fallback về link web + text spec).
```

- `C` → phát hiện lại; nếu giờ đã kết nối → tiếp tục dùng MCP local.
- `S` → tiếp tục ở **fallback mode**: chỉ dùng link frame web + text spec của Design Spec;
  thêm note ⚠️ trong report cuối rằng UI được sinh mà không có độ trung thực Figma local.

**Step 3 — Khi MCP local ĐÃ kết nối:** với mỗi màn đang implement, pull frame được chọn
qua Figma MCP và ground UI trên layout, variable, và mapping Code Connect trả về. Ưu tiên
component được map Code-Connect hơn là bịa markup; dùng tên token thật, không phải giá trị hardcode.

**Nếu `--phase=integration`:**
Phân giải design điều khiển adapter từ **tech-doc gộp của PRD** `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`:
- **Mapping port→endpoint→DTO→error** (ưu tiên): §4.5.4 (API Integration Layer của platform này) — mỗi client method → endpoint có thật.
- **Nguồn endpoint/shape**: §4.1 Endpoints + §4.2 Request-Response + §4.3 Error của cùng doc.

Đọc `@trace.status` của doc. Nếu `draft` hoặc `in-review` → cảnh báo:
```
⚠ Tech design {TICKET-ID} (UC {UC-ID} / {platform}) đang {status}.
  Contract / mapping adapter còn có thể đổi.
  Tiếp tục — đảm bảo BE endpoint đã deploy hoặc confirm mapping thủ công.
```
Nếu doc **thiếu §4.5.4** (client integration chưa được vẽ cho platform này) → cảnh báo: "Chưa có §4.5.4 cho {platform} — fallback map trực tiếp từ §4.1 endpoint (mapping adapter được infer). Khuyến nghị: chạy `/generate-tech-docs {web|app .feature}` để bổ sung §4.5 trước."
Định vị mock adapter có sẵn từ lần chạy `--phase=ui` (tìm `{UC-ID}MockApiAdapter` trong `{paths.src_dir}/{domain}/`).
Nếu không tìm thấy → cảnh báo: "Không tìm thấy mock adapter — sinh real API adapter từ đầu dùng contract tech-doc."

---

## Read Trace State

Đọc `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{@trace.platform}.tsv` nếu tồn tại. Với mỗi scenario row, ghi nhận `status` hiện tại:

| Status | Ý nghĩa | Hành động trong lần chạy này |
|--------|---------|-------------------|
| `UNTRACKED` | `implemented_by == —` | Generate — scenario chưa có code |
| `DRIFT` | `spec_ver != gen_ver` | Sửa **tại chỗ đúng method** của scenario đó (Edit) — KHÔNG viết lại cả file (file chung sẽ mất method UC khác) |
| `OK` | đã implement + test | Skip trừ khi gen lại tường minh |
| `GAP` | đã implement, chưa test | Skip codegen — đã code rồi; chạy `/dev-gen-test` thay vì |

Dùng các status này để điền số **Scenarios** trong plan CHECKPOINT (`{X} new, {Y} drifted, {Z} synced-skip`).
Nếu `.tsv` không tồn tại → coi mọi scenario là `UNTRACKED`.

---

## Package Placement *(đặt code đúng convention — chống phân mảnh package)*

Trước khi định vị file, chốt **package đích** theo **Package Layout của CLAUDE.md §2** (context-loader đã trích: `code_base_package` + `package_strategy`):

- **Path code = `{code_base_package}.{layer}`** — vd `vn.edupia.segment.service`, `.repository`, `.facade`. `{layer}` theo thứ tự layer §2 (dto/entity/repository/service/facade/controller…).
- **CẤM đưa `{prd-slug}` / tên feature / `{UC-ID}` vào package** (đây là lỗi hay gặp: map bố cục spec `specs/{domain}/{prd-slug}/` thành package code → đẻ `…{service}.{feature}.{layer}`, nhân bản layer). Feature/UC **chỉ** phân biệt ở **TÊN CLASS**: `EventCollectionService`, `IdentityResolutionService` **cùng nằm trong** `…segment.service`, KHÔNG phải `…segment.eventcollection.service`.
- *(Chỉ khi §2 khai `package_strategy = by-feature` mới đặt sub-package theo feature — mặc định là **by-layer**.)*

**Quét module TÁI DÙNG trước khi tạo:** với mỗi layer, kiểm `{code_base_package}.{layer}` đã tồn tại trên disk chưa. Nếu đã có class phục vụ cùng entity/nghiệp vụ → **đặt member mới vào class đó (EXTEND)**, KHÔNG dựng cây layer / class song song. Chỉ tạo mới khi thực sự chưa có.

---

## Seam & Stub Ledger — nối/lấp chỗ chưa implement *(chống mồ côi)*

*Chạy TRƯỚC khi tạo bất kỳ chỗ giả lập nào. Đây là chỗ vá hai lỗi kinh điển khi gen từng BDD: (a) **hàm trắng mồ côi** — callee để trắng vì logic thuộc BDD khác, ghép luồng thì chạy vào no-op; (b) **hàm thật mồ côi** — BDD sau đẻ hàm mới thay vì lấp hàm trắng cũ, thành ra hàm thật không ai gọi.*

Có **hai loại chỗ chưa implement**, ghi chung vào **một sổ** — cột `kind` phân biệt:

| kind | Là gì | Ví dụ |
|---|---|---|
| `seam` | Gọi ra một **port cross-UC** do UC khác sở hữu | `A` gọi `ScoreAccumulationPort` (UC scoring sở hữu) |
| `stub` | Một **method/hàm trắng nội-feature** — callee mà logic thuộc BDD **khác của chính feature này** | `SegmentService.calculateSegment()` để trắng vì logic thuộc BDD2 |

Scope Lock cấm **implement scenario** của `.feature` khác — nhưng KHÔNG cấm **nối vào / lấp code đã tồn tại** (đọc `@trace.implements` sẵn có; xem Scope Lock). Đó là chìa khoá để không đẻ mồ côi.

### Sổ chung `_seams.tsv`

`{paths.trace_dir}/{domain}/{prd-slug}/_seams.tsv` — tạo file + header nếu chưa có, tab-separated:

```
kind    name    consumer_uc    owner_uc    artifact    binding    status    last_updated
```

- `kind` = `seam` | `stub`
- `name` = tên port (seam) | trách nhiệm/tên logic ngắn (stub) — "cái gì đang thiếu"
- `consumer_uc` = UC có call-site / để trắng
- `owner_uc` = UC sở hữu logic thật (`?` nếu tech-doc chưa nói rõ)
- `artifact` = `StubClass` (seam) | `ClassName#method` (stub) — nơi tìm ra hàng giả
- `binding` = cách stub được wire, vd bean/config (seam) | `—` (stub lấp tại chỗ, không qua binding)
- `status` = `PENDING` (chưa có hàng thật — bình thường) · `READY` (hàng thật đã có nhưng chưa nối/lấp — 🔴) · `RESOLVED` (đã nối/lấp xong)

*(Sổ sống cạnh TSV trace — cùng `trace_dir`, cùng luật ghi liên-repo khi có `spec_source`.)*

### Loại A — Seam (port ra ngoài, cross-UC)

Khi scenario của `{UC-ID}` gọi qua một **port do UC khác sở hữu** (vd `ScoreAccumulationPort`):

**A1. Xác định port + chủ sở hữu.** Từ tech-doc gộp — §5 flow lane của UC này (participant nào bị gọi tới), §6.2 Cross-Service Dependencies, §11 Out-of-Scope Reference — lấy tên port và **UC nào sở hữu hàng thật**. Tech-doc không nói rõ chủ → `owner_uc = ?`.

**A2. Ngó vào kho TRƯỚC khi dán giấy nợ.** Quét disk tìm một class **thật** implement port đó dưới `{code_base_package}` — class KHÔNG phải `*Stub*`/`*Mock*` và mang `@trace.implements` của UC khác:
- **CÓ hàng thật → nối thẳng, KHÔNG tạo stub.** Wire DI binding của `{UC-ID}` vào class thật (constructor injection / `@Primary` bean / config hiện có). Đây là **cấu hình wiring của chính UC này** → không vi phạm Scope Lock/EXTEND.
- **CHƯA có hàng thật → tạo stub, NHƯNG ghi nợ** (A3).

**A3. Ghi sổ nợ (chỉ khi phải tạo stub).**
- Tag class stub: `@trace.seam_pending={owner_uc | ?}` + `@trace.seam_port={PortName}`.
- Append 1 dòng: `kind=seam` · `name={PortName}` · `consumer_uc={UC-ID}` · `owner_uc={owner|?}` · `artifact={StubClass}` · `binding={cách wire}` · `status=PENDING`.

**A4. Khi CHÍNH `{UC-ID}` là chủ nợ** (lần gen này sinh **hàng thật** của một port UC khác đã stub trước đó):
- Sinh class thật bình thường (đúng layer).
- Đọc sổ: mọi dòng `kind=seam` `status=PENDING` có `owner_uc == {UC-ID}` (hoặc `owner_uc == ?` mà `name` khớp port) → đổi `status → READY`.
- **KHÔNG tự sửa binding của UC consumer** (file/scope UC khác — tránh clobber). IN cảnh báo nổi bật ở report cuối:
  ```
  🔌 SEAM READY — {port}: hàng thật {RealClass} vừa sinh, nhưng {consumer_uc} còn wire vào {stub_class}.
     Nối: trỏ binding của {consumer_uc} sang {RealClass} (xoá/thay stub), rồi build lại.
     (/validate-traces giữ cờ SEAM_UNWIRED tới khi nối xong.)
  ```

### Loại B — Stub (method/hàm trắng nội-feature) 🆕

Khi một scenario buộc gọi tới một callee mà **logic thuộc BDD khác của chính feature này** → KHÔNG để trắng vô hình. Làm hàm trắng thành **hiện hình + có sổ**:

**B1. Class thật GIỮ NGUYÊN TÊN** (vd `SegmentService` — KHÔNG đổi thành `StubSegmentService`; class là thật, chỉ **method** là placeholder).

**B2. Chữ ký suy từ CALL-SITE** — đúng cái caller cần (tên method, tham số, kiểu trả về). Thân method là placeholder rõ ràng: `throw new UnsupportedOperationException("stub: {trách nhiệm}")` (hoặc tương đương theo stack). **KHÔNG bịa logic** — chỉ dựng chữ ký.

**B3. Tag lên method trắng:**
```
@trace.stub={UC-ID}                (ai để trắng — consumer)
@trace.stub_owner={owner_uc | ?}   (BDD/UC sẽ điền logic thật)
@trace.stub_for={trách nhiệm ngắn} (để owner nhận ra đây là chỗ cần lấp)
```

**B4. Ghi sổ:** append 1 dòng: `kind=stub` · `name={trách nhiệm}` · `consumer_uc={UC-ID}` · `owner_uc={owner|?}` · `artifact={ClassName#method}` · `binding=—` · `status=PENDING`.

> *Phần lấp stub (khi BDD owner chạy) + cổng kiểm mồ côi nằm ở "Fill-before-create" và `/validate-traces` — xem các mục sau.*

> **Mẹo vận hành (né hẳn):** gen BDD **sở hữu logic TRƯỚC**, BDD **gọi SAU** — khi đó không bao giờ cần để trắng, không phát sinh nợ. Cơ chế trên chỉ để chịu được thứ tự ngược.

---

## File Scan

Trước khi sinh, xác định file nào cần cho các scenario của UC này — **path theo Package Placement ở trên**. Kiểm tra mỗi file đã tồn tại trên disk chưa.

Phân loại mỗi file:

| Status | Ý nghĩa | Hành động |
|--------|---------|--------|
| `CREATE` | File chưa tồn tại | Sinh file mới đầy đủ |
| `EXTEND` | File tồn tại, cần method mới | Chỉ thêm method mới — KHÔNG viết lại code có sẵn |
| `FILL` | File tồn tại, chứa **method stub trắng** mà `{UC-ID}` là chủ logic | **Lấp logic thật vào chính method đó** (Edit) — KHÔNG đẻ method song song (xem Fill-before-create) |
| `SKIP` | File tồn tại và đã phủ tất cả scenario của UC | Để nguyên |

> **Quy tắc EXTEND (phi-phá-huỷ — BẮT BUỘC ở mức thao tác):**
> 1. **Đọc lại file trên disk NGAY TRƯỚC khi ghi** (không dựa vào trí nhớ từ đầu phiên — file có thể đã đổi).
> 2. **CHỈ dùng thao tác chèn/sửa từng phần (Edit) để THÊM member mới.** **CẤM tuyệt đối ghi đè cả file (full Write)** cho file đã tồn tại — đây là nguyên nhân số 1 xoá nghiệp vụ UC trước.
> 3. Output PHẢI là **superset chặt** của nội dung cũ: **mọi** method, field, annotation, import, và `@trace.implements` cũ (kể cả của UC khác) **còn nguyên si**. Chỉ được **thêm**, không xoá/sửa member không thuộc `{UC-ID}` này.
> 4. Gắn `@trace.implements={UC-ID}-SC{N}` lên mỗi method **mới**.
>
> **Guard sau-ghi (lưới an toàn — chạy sau khi ghi mỗi file EXTEND):** đọc lại file vừa ghi, đối chiếu với bản trước khi sửa: **mọi `@trace.implements` và member cũ phải vẫn còn**. Nếu **mất bất kỳ member/tag cũ nào** (đặc biệt của UC khác) → **DỪNG NGAY, khôi phục file về bản cũ** (`git checkout -- {file}` nếu đã commit, hoặc hoàn tác edit), báo lỗi: *"EXTEND làm mất {member/tag} của {UC khác} — đã chặn clobber. Sửa lại theo add-only rồi chạy lại."* KHÔNG tiếp tục sinh các file sau khi chưa khôi phục.

> **Fill-before-create (chống hàm thật mồ côi — BẮT BUỘC trước khi tạo BẤT KỲ method mới nào):**
>
> Trước khi sinh một method mới cho scenario của `{UC-ID}`, phải kiểm tra: nó có phải là **logic thật cần lấp vào một stub đã tồn tại** không — thay vì đẻ một method song song mồ côi (nửa B của bug: BDD sau tạo hàm mới thay vì lấp hàm trắng cũ → hàm cũ được caller gọi mà rỗng, hàm mới không ai gọi).
>
> 1. **Quét chỗ chờ lấp** (hai nguồn):
>    - Sổ `_seams.tsv`: dòng `kind=stub` `status=PENDING` có `owner_uc == {UC-ID}` (hoặc `owner_uc == ?` mà `name`/trách nhiệm khớp scenario đang làm).
>    - Scan disk dưới `{code_base_package}`: method mang `@trace.stub_owner == {UC-ID}` (hoặc `?`) và `@trace.stub_for` khớp trách nhiệm.
> 2. **KHỚP → lấp TẠI CHỖ (Edit), KHÔNG tạo mới.** Ghi logic thật vào **đúng thân method stub đó**, **giữ nguyên chữ ký** (caller đang gọi vào đó — đổi chữ ký = gãy call-site). Gỡ 3 tag `@trace.stub*`, thay bằng `@trace.implements={UC-ID}-SC{N}`. **CẤM tạo method mới cùng trách nhiệm** — đó chính là hàm mồ côi.
> 3. **Cập nhật sổ:** dòng tương ứng `status → RESOLVED`, `owner_uc = {UC-ID}`, `last_updated`. *(Caller ở UC khác KHÔNG cần đổi — vẫn gọi đúng method cũ, giờ có logic; đây là điểm khác với seam: stub lấp tại chỗ, không cần rewire binding.)*
> 4. **Không khớp stub nào → tạo method mới bình thường** (theo EXTEND/CREATE ở trên).
>
> **Guard sau-lấp:** sau khi lấp, KHÔNG được còn method trắng cùng trách nhiệm, cũng KHÔNG được có method song song mới cùng trách nhiệm. Lỡ tạo song song → gộp về một, xoá method thừa (chỉ khi chắc chắn cùng trách nhiệm).
>
> *Nghi ngờ khớp mà không chắc (tên/chữ ký lệch nhiều) → KHÔNG tự ý xoá/gộp; giữ nguyên cả hai + để `/validate-traces` gắn cờ cho người soát.*

---

## CHECKPOINT — Code Generation Plan

Trước khi sinh code, hiện:

```
Code Generation Plan — {UC-ID}
──────────────────────────────────────────────────────
Feature  : {name}
Ticket   : {TICKET_ID nếu biết}
Domain   : {domain}
UC       : chỉ {UC-ID}  ← các file feature khác trong folder này KHÔNG được đọc
Tech     : {language} / {framework}
Package  : {code_base_package}.{layer} · {by-layer | by-feature}  ← feature/UC ở TÊN CLASS, KHÔNG thành package (nếu by-layer)
Phase    : {UI — mock layer | Integration — real API | Default — full}   ← bỏ nếu không có flag --phase
Scenarios: {N} total ({X} new, {Y} drifted, {Z} synced-skip)
Layer    : {từ CLAUDE.md §2}

Files:
  CREATE  {N} file mới
    + {path/FileName.ext}
  EXTEND  {M} file có sẵn  (ADD-ONLY — chỉ Edit thêm, CẤM full Write)
    ~ {path/FileName.ext}  — thêm: {methodA}, {methodB}
        ↳ GIỮ NGUYÊN (member có sẵn, gồm UC khác): {methodX [UC-other], methodY …}   ← sẽ không bị đụng
  FILL    {F} stub trắng  (lấp logic TẠI CHỖ — KHÔNG đẻ method song song)
    ⟲ {path/FileName.ext}#{method}  — lấp: {trách nhiệm}  (stub của {consumer_uc})
  SKIP    {K} file  (không cần đổi)
    = {path/FileName.ext}
──────────────────────────────────────────────────────
Proceed? (Y/N)
```

Chờ "Y" rõ ràng trước khi sinh.

## Branch
```bash
git checkout -b feature/{TICKET_ID}-{slug}
```

## Generate (thứ tự layer từ CLAUDE.md §2)

Thứ tự mặc định (override từ CLAUDE.md nếu khác):
DTOs → Entity/Model → Repository → Service interface → Service impl → Facade (nếu áp dụng) → Controller

**Với file `CREATE`:** sinh file đầy đủ.

**Với file `EXTEND`:** áp **Quy tắc EXTEND phi-phá-huỷ** (xem File Scan) — đọc lại file trên disk → **chỉ Edit thêm** method mới cho `{UC-ID}` → **KHÔNG full Write**, không đụng member cũ → chạy **Guard sau-ghi** để chắc không mất member/tag của UC khác.

**Với file `FILL`:** áp **Fill-before-create** (xem File Scan) — Edit logic thật vào **đúng thân method stub** đã có, giữ nguyên chữ ký, gỡ `@trace.stub*` → `@trace.implements`, cập nhật sổ `RESOLVED`. **KHÔNG** đẻ method mới cùng trách nhiệm.

**Tag traceability trên controller/handler (theo cú pháp comment của ngôn ngữ bạn):**
```
@trace.implements={UC-ID}-SC{N}
@trace.prd_version={đọc @trace.prd_version từ header file .feature}
@trace.bdd_version={đọc @trace.bdd_version từ header file .feature}
@trace.tech_doc_revision={đọc @trace.revision từ header tech-doc, hoặc bỏ nếu không có tech-doc}
@trace.source={paths.specs_dir}/{domain}/{prd-slug}/bdd/{UC-ID}-{slug}.feature
```

`@trace.prd_version` ghi code này được viết theo version PRD nào.
`@trace.bdd_version` ghi code này được sinh từ version BDD nào.
`@trace.tech_doc_revision` ghi code này theo revision tech-design nào.
`/validate-traces` sẽ gắn cờ drift nếu bất kỳ artifact upstream nào được cập nhật lên version mới hơn.

> **Quy tắc entry-point:** `@trace.implements` phải xuất hiện ở **layer entry-point** như định nghĩa trong `CLAUDE.md §2`. Với REST API → Controller. Với module event-driven → event handler / consumer class. Với context-engineering → hàm orchestration prompt. Không bao giờ chỉ đặt ở layer trong.

> **Quy tắc nguồn giá trị (chống hard-code):** MỌI giá trị cụ thể (endpoint path, error code, tên field/DTO, enum, limit/timeout, header) phải lấy từ **nguồn đã chốt** — tech-doc §4 (contract) · `core-entities.md` (enum/field) · config/env — **KHÔNG bịa inline**. Nếu một hằng số nghiệp vụ lặp lại hoặc mang ý nghĩa (retry count, ngưỡng, key) → **đặt tên hằng số** (constant/config), không rải magic number/string trong code. Không có nguồn cho một giá trị → đây là GAP: dừng và hỏi, đừng chế bừa (đồng bộ Cổng 2 của generate-tech-docs). *(DS3 đã đảm bảo có §4 contract trước khi tới đây với BE.)*

### Test Selectors — emit element ID ổn định *(chỉ UI FE/App)*

*Áp dụng khi `platform` là `web`/`app` và đang sinh UI (`--phase=ui`, hoặc chế độ default FE/App). Bỏ qua với BE.*

Mỗi element **có action** (button, input, link, select, toggle, form-submit) PHẢI mang một **test-id ổn định** để QC định vị trực tiếp (không scan runtime):

1. **Nguồn id.** Nếu tech-doc gộp `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` có **§4.5.6 Test Selectors** cho platform này, lấy id **nguyên văn** từ bảng đó (contract). Nếu chưa có (vd `--phase=ui` trước khi §4.5 được vẽ), **sinh id theo quy ước** `{uc-lower}-{screen}-{element}-{type}` (vd `ft001-login-submit-btn`) để QC vẫn có handle ổn định — chúng sẽ được đối chiếu với §4.5.6 của tech-design lúc integration.
2. **Emit qua attribute platform** (từ `@trace.testid_attr`, hoặc theo module):
   - web (`react`/`nextjs`/`vue`/`angular`) → `data-testid="..."`
   - React Native → `testID="..."`
   - Flutter → `Key('...')` (+ `Semantics(identifier: '...')` khi action cần)
   - native iOS → `accessibilityIdentifier = "..."`
3. Chỉ element có action; đừng spam id lên text tĩnh. Giữ id giống hệt map của tech-design để QC Page Object khớp ngay lần đầu.
4. **Component catalog tái dùng?** Truyền id qua **forwarding prop** của nó (xem section catalog `## Test-ID Forwarding` — vd `<Button testId="ft001-login-submit-btn">`), không phải attribute thô. Nếu component không forward test-id, hoặc bạn đang backfill màn **existing/brownfield** (không phải sinh mới ở đây), đó là việc của `/map-testids {UC-ID}` — chạy nó thay vì sửa component dùng chung inline.

## Mock API Layer (chỉ `--phase=ui`)

*Bỏ qua hoàn toàn section này nếu `--phase` không phải `ui`.*

Dựng mock từ `mock_source` đã phân giải ở Phase Detection — **shape** từ §4 API contract (tech-doc gộp) khi có, **giá trị fixture + behavior** luôn từ mệnh đề `Then` của System BDD:

1. **Định nghĩa shape port** `{UC-ID}ApiPort` (DTO request/response + error code):
   - `mock_source = contract` → tên field / type / error code lấy **nguyên văn từ tech-doc gộp** §4.1 (Endpoints) / §4.2 (Request-Response) / §3 (Data Model) — shape thật.
   - `mock_source = system-bdd` → shape **infer** từ mệnh đề `Then` của System BDD (tạm — xem cảnh báo ở trên).
2. **Trích dữ liệu fixture** theo từng scenario từ mệnh đề `Then` của System BDD — response success + error (BDD là source of truth cho *giá trị / behavior*, bất kể nguồn shape).
3. **Sinh mock adapter** tại `{paths.src_dir}/{domain}/{UC-ID}MockApiAdapter.{ext}`:
   - Implements interface `{UC-ID}ApiPort` (cùng interface mà real adapter sẽ implement)
   - Mỗi method trả về fixture data khớp mệnh đề `Then` của BDD, theo shape của port
   - Gồm cả trạng thái success và error (map sang các error scenario trong BDD)
   - Tag traceability:
     ```
     @trace.mock_for={UC-ID}
     @trace.mock_source={contract | system-bdd}
     @trace.system_bdd={paths.specs_dir}/{domain}/{prd-slug}/bdd/system/{UC-ID}*.feature
     {@trace.be_contract={TICKET-ID}-tech-design.md   # chỉ khi mock_source=contract}
     ```
4. **Wire vào layer service/hook** qua environment flag hoặc DI:
   ```
   const adapter = IS_MOCK ? new {UC-ID}MockApiAdapter() : new {UC-ID}ApiAdapter()
   ```
   - `IS_MOCK` mặc định `true` ở môi trường development/test cho tới khi real adapter được sinh.

> Tester dùng mock adapter để test mọi FE scenario mà không cần đợi BE **deploy**.
> Shape lấy từ §4 API contract (tech-doc gộp) khi có (chính xác, không rework integration); else từ System BDD (tạm — điều chỉnh ở `--phase=integration`). Giá trị fixture luôn từ System BDD — BDD là source of truth cho behavior.

---

## Integration Phase (chỉ `--phase=integration`)

*Bỏ qua hoàn toàn section này nếu `--phase` không phải `integration`.*

1. **Đọc integration design.** Trong tech-doc gộp `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`: ưu tiên §4.5.4 (mapping port→endpoint→DTO→error của platform), dùng §4.1/§4.2/§4.3 làm nguồn endpoint / request-response / error-code. Nếu doc chưa có §4.5.4 cho platform này, trích endpoint + shape + error code trực tiếp từ §4.1–§4.3.
2. **Đọc mock adapter có sẵn** interface (`{UC-ID}ApiPort`) từ output `--phase=ui`.
3. **Sinh real API adapter** tại `{paths.src_dir}/{domain}/{UC-ID}ApiAdapter.{ext}`:
   - Implements cùng interface `{UC-ID}ApiPort` như mock adapter
   - Gọi HTTP thật tới endpoint từ contract tech-doc
   - Map field response sang cùng shape mà mock adapter trả về
   - Tag traceability:
     ```
     @trace.implements={UC-ID}-SC{N}
     @trace.tech_doc_revision={đọc từ header tech-doc}
     ```
4. **Lật wire-up**: chuyển DI binding / env flag để service/hook dùng `{UC-ID}ApiAdapter` (thật) thay vì mock.
5. **KHÔNG xoá mock adapter** — giữ lại cho unit test.

---

## Self-Review (3 vòng)
- [ ] Mỗi scenario có endpoint tương ứng
- [ ] @trace.implements trên mọi endpoint
- [ ] Tôn trọng quy tắc layer kiến trúc (CLAUDE.md §2)
- [ ] Error handling khớp CLAUDE.md §5
- [ ] **Không hard-code:** giá trị cụ thể lấy từ tech-doc §4 / core-entities / config; hằng số nghiệp vụ được đặt tên; 0 magic number/string inline; không debug logging
- [ ] **Add-only cho file EXTEND:** đọc lại file trên disk; mọi member + `@trace.implements` cũ (gồm UC khác) còn nguyên; Guard sau-ghi đã chạy, không mất member nào
- [ ] **Seam/Stub ledger:** không tạo giả lập khi hàng thật đã tồn tại (đã nối/lấp thẳng); mọi seam mới có `@trace.seam_pending` + dòng `kind=seam PENDING`; mọi method trắng có `@trace.stub`/`@trace.stub_owner`/`@trace.stub_for` + dòng `kind=stub PENDING` trong `_seams.tsv`; nếu UC này là chủ nợ (seam) → đã set `READY` + in cảnh báo
- [ ] **Fill-before-create:** trước khi tạo method mới đã quét sổ + `@trace.stub` — có stub khớp thì **lấp tại chỗ** (giữ chữ ký, gỡ `@trace.stub*` → `@trace.implements`, sổ `RESOLVED`); KHÔNG còn method song song cùng trách nhiệm

## Build Verify
```bash
{conventions.build_command}   # từ project-context.yaml, tối đa 3 retry
```

## Write Trace State

Cập nhật `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{@trace.platform}.tsv` — với mỗi scenario đã implement, tìm row có sẵn theo `sc_id` và chỉ cập nhật các cột sau. *(Umbrella + `spec_source`: `trace_dir` phân giải về `{spec_source}/.trace` — lệnh này chạy từ `service_root` nhưng ghi trace row vào **spec repo** (liên-repo); commit/push spec submodule cho lần cập nhật trace, cùng với push code 2 tầng.)*

| Cột | Giá trị |
|--------|-------|
| `gen_ver` | copy `spec_ver` từ row `.tsv` hiện tại (= version scenario tại thời điểm codegen) |
| `implemented_by` | `{ControllerClass}.{methodName}` |
| `bdd_version` | `@trace.bdd_version` từ header `.feature` |
| `tech_doc_revision` | `@trace.revision` từ tech-doc gộp `{TICKET-ID}-tech-design.md` (§4 backend đã điều khiển codegen của UC này), hoặc `—` nếu chưa có doc |
| `fe_tech_doc_revision` | `@trace.revision` của cùng tech-doc gộp, ghi khi sinh FE với `--phase=integration` (adapter được wire theo §4.5.4); `—` cho BE, hoặc cho FE `--phase=ui` / chưa có §4.5.4 |
| `fe_phase` | `ui` nếu `--phase=ui` \| `integrated` nếu `--phase=integration` \| `—` nếu không có flag phase |
| `last_updated` | hôm nay `YYYY-MM-DD` |

Giữ nguyên mọi cột khác (`sc_title`, `spec_ver`, `prd_version`, `prd_status`, `uc_status`, `test_count`, `test_classes`, `dev_selftest`, `dev_selftest_at`, `qc_status`, `qc_run_at`, `qc_owner`, `qc_blocked_by`).
`status` được tính bởi `/validate-traces` — không set ở đây.

**Reset test khi lấp stub (Fill-before-create).** Nếu lần gen này **lấp** một/nhiều method stub (dòng sổ `→ RESOLVED`): logic vừa đổi thật → test cũ viết trên hàm trắng đã cũ (nó có thể "xanh" chỉ vì hàm trắng `throw`/trả rỗng). Với **mọi scenario chạy qua method vừa lấp** — gồm cả scenario của **consumer_uc** (UC đã để trắng, thường nằm ở file TSV khác `{consumer_uc}-{platform}.tsv`):
- `dev_selftest → not_run`, `dev_selftest_at → —` (ép chạy lại self-test).
- **CHỈ** đụng 2 cột test này — ngoại lệ có kiểm soát của luật "giữ nguyên cột khác"; là thao tác an-toàn (không sửa code UC khác, chỉ hạ cờ test đã cũ).
- Gom danh sách `{consumer_uc}` bị ảnh hưởng để in ở "Next".

## Refresh Panel Mirror
# Làm mới panel mirror của Living Docs *(local, chế độ umbrella)*

*Bỏ qua hoàn toàn ở chế độ single-service (không có `services` và không có `setup.spec_source`) — ở đó
`.trace/` của chính repo CHÍNH LÀ vị trí panel, nên không có gì để mirror.*

Sau khi cập nhật TSV authoritative tại `{paths.trace_dir}`:

**Khi `setup.spec_source` được đặt (trace gộp — trường hợp phổ biến):**
`{paths.trace_dir}` phân giải về `{spec_source}/.trace` — vị trí authoritative duy nhất.
Lệnh này chạy từ `service_root`, nên thao tác ghi là **liên-repo vào spec submodule**;
commit/push spec submodule cho lần cập nhật trace (giống như `feedback/`).
1. Phân giải `panel_mirror = ./.trace` tại **gốc workspace hiện tại**.
2. Nếu `panel_mirror` phân giải ra path khác với `{paths.trace_dir}`, copy mỗi
   `{UC-ID}-{platform}.tsv` vừa cập nhật → `{panel_mirror}/{UC-ID}-{platform}.tsv` (tạo thư mục; ghi đè).
   Không namespace theo service — chỉ có một bộ trace; service sở hữu được mang trong
   `@trace.service` của từng row.

**Legacy (không có `spec_source` — trace theo service):**
Copy mỗi `{UC-ID}-{platform}.tsv` vừa cập nhật → `{panel_mirror}/{service-name}/{UC-ID}-{platform}.tsv`
(namespace theo `active_service`).

Cách này giữ panel Living Docs của workspace đang mở luôn mới **giữa các lần sync** — nó chỉ là
một **mirror tiện lợi cục bộ**. File `trace-report.json` đã merge (canonical, trong
`{spec_source}/.living-docs/`) được build lại bởi `/sync` hoặc `/validate-traces`. Với các lệnh
được orchestrate, làm việc này một lần trong orchestrator sau khi tất cả sub-agent trả về — không phải
bên trong từng sub-agent.


## Commit
```bash
git add {files}
git commit -m "{commit_format}: {description}"
```

## 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.)*


```
/generate-code Hoàn tất — {UC-ID}
Files: created={N}, extended={M}, filled={F} stub, skipped={K} | Build: SUCCESS
Branch: feature/{TICKET_ID}-{slug}
Phase    : {UI (mock layer) | Integration (real API) | Default (full)}
fe_phase : {ui | integrated | —}
Figma    : {Dev Mode MCP local (grounded) | ⚠️ chỉ link web + text spec (không có MCP local) | n/a cho BE}   ← chỉ UI FE/App

Next:
  --phase=ui xong:
    → Báo tester: FE test được qua mock adapter
    → Thu sign-off BE → /review-tech-docs {tech-design-file}
    → Khi BE sẵn sàng → /generate-code {feature-file} --phase=integration

  --phase=integration xong:
    → /review-code {UC-ID}   ← cần code review
    → /dev-gen-test {UC-ID}  ← bộ integration test

  Default (không có flag phase):
    → /review-code {UC-ID}   ← cần code review trước khi test
    → /dev-gen-test {UC-ID}

  Nếu lần này LẤP stub (filled > 0):
    → /dev-gen-test {owner_uc}      ← test cho logic vừa lấp
    → /dev-gen-test {consumer_uc}   ← RE-GEN: test cũ của {consumer_uc} viết trên hàm trắng, giờ đã cũ
    (dev_selftest các scenario này đã bị reset → not_run; /validate-traces sẽ hiện chúng là GAP tới khi test lại)

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