# /review-tech-docs — Review Technical Design Document

**Chế độ phân tích READ-ONLY — ghi file findings, KHÔNG sửa target.**
**Dùng `--resume` để áp dụng các finding được chấp nhận.**

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

*Lưu ý: Với lệnh này, target ở Bước 1 là **file tech-design gộp của PRD** `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` — MỘT doc full-stack phủ mọi UC của PRD (không còn per-UC / per-platform). Review chạy trên cả doc; findings gom theo từng UC (đọc §10 UC Coverage để biết finding thuộc UC nào).
Nếu `$ARGUMENTS` chứa `--resume` → bỏ qua sang Resume Mode bên dưới.*

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

---

## Nạp tài liệu Review

Sau khi nạp context nền, đọc các thứ sau theo thứ tự:

1. **Tech-doc target** — đọc đầy đủ. Trích từ header:
   - `@trace.ucs` → danh sách UC mà doc phủ (đối chiếu §10 UC Coverage)
   - `@trace.domain` → domain
   - `@trace.prd` → TICKET-ID của PRD nguồn
   - `@trace.platforms` → các platform có mặt (system / web / app)
   - `@trace.status` → status hiện tại (draft / in-review / approved)

2. **Các file BDD nguồn** — nạp **mọi** feature của PRD này: `{paths.specs_dir}/{domain}/{prd-slug}/bdd/**/*.feature` (system/web/app). Đây là nguồn đối chiếu cho T3/T7 — mỗi UC trong doc phải trace về scenario BDD tương ứng.

3. **Index endpoint cho T4 (KHÔNG nạp full doc khác)** — trích danh sách endpoint (§4.1) + entity chính của doc target. T4 sẽ `grep` các path đó trong `{paths.tech_docs_dir}/{domain}/*/tech-docs/*-tech-design.md` (các PRD khác) và chỉ nạp đoạn liên quan **khi có va chạm** — xem T4.

4. **Tham chiếu kiến trúc** — xác nhận lại CLAUDE.md §2: thứ tự layer, quy tắc kiến trúc.

5. **Core entities** — đã nạp trong context (Bước 6 của context-loader).

6. **PRD nguồn — §Business Rules (cho T8)** — nạp file PRD `{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md`, trích bảng Business Rule/Business Logic của các UC mà doc phủ. Đây là nguồn đối chiếu-ngược của **T8** (BR nào yêu cầu nguồn/sự kiện/generic-contract mà doc bỏ sót). Chỉ đọc §BR + §AC liên quan — KHÔNG cần toàn PRD.

Suy ra tên file findings (per-PRD):
`{paths.refinement_dir}/{TICKET-ID}-tech-review-findings.yaml`

---

## Review Dimensions

### T1 — Architecture Alignment *(luôn CRITICAL nếu vi phạm)*

Đối chiếu design đề xuất với quy tắc CLAUDE.md §2:

| Loại vi phạm | Severity |
|----------------|----------|
| Controller gọi Repository trực tiếp (skip layer) | Critical |
| Business logic trong Controller hoặc DTO | Critical |
| Pattern bị cấm từ §3 | Critical |
| Phụ thuộc đi ngược upstream (Service → Controller DTO) | Critical |
| Annotation transaction sai layer | Major |
| Thiếu tách lớp (không có Facade khi kiến trúc yêu cầu) | Major |

Với mỗi finding:
```
Component: {tên class hoặc method}
Violates:  "{rule text}" (CLAUDE.md §2)
Fix:       {layer/component nào nên sở hữu cái này}
```
→ **Không auto-fix.** Người phải quyết định fix cấu trúc. Bắt buộc note trong Review Board.

### T2 — Entity Consistency

Dùng catalog core-entities đã nạp:

| Vấn đề | Severity | Auto-fixable? |
|-------|----------|---------------|
| Entity được nhắc nhưng không có trong core-entities.md | Major | No — người confirm là DTO hay domain entity |
| Tên field khác core-entities.md | Major | Yes — đổi về canonical |
| Quan hệ được mô tả khác đi | Major | No — người quyết định |
| Entity mới được đưa ra nhưng chưa có trong core-entities.md | Minor | No — thêm vào core-entities trước |

### T3 — BDD Traceability

Đối chiếu với **mọi** feature BDD của PRD (system/web/app). Với mỗi UC trong §10 UC Coverage, kiểm tra design khớp scenario **2 chiều**.

> ⚠ **SC scope theo platform:** `{UC}-SC{N}` chỉ unique trong (UC × platform) — `system UC1-SC1` và `web UC1-SC1` là **hai scenario khác nhau**. Khi đối chiếu, match SC **trong đúng lane platform** (system SC ↔ system BDD, web SC ↔ web BDD, app SC ↔ app BDD). KHÔNG so chéo platform. §5 phải để mỗi SC trong lane 5.A/5.B/5.C của nó; §10 mỗi dòng có cột Platform.

| Vấn đề | Severity | Auto-fixable? |
|-------|----------|---------------|
| Tech-doc đề xuất behavior không có trong scenario BDD nào (cùng platform) | Major | No — tạo scenario trước |
| Tech-doc mâu thuẫn một scenario BDD | Critical | No — giải quyết conflict trước |
| Scenario (platform, SC) không có design tương ứng (thiếu ở §5 lane hoặc §10) | Minor | Yes — thêm design note còn thiếu |
| §10 UC Coverage sót một (platform, SC) đã có BDD | Major | Yes — thêm dòng coverage + section tương ứng |
| SC ghi trong §5/§10 **không kèm platform** (bare `UC1-SC1`) → nhập nhằng | Major | Yes — gắn platform vào SC ref |

### T4 — Cross-PRD Endpoint Conflict Check *(targeted, load-on-hit)*

Mỗi PRD giờ chỉ 1 doc → xung đột TRONG doc đã do **T5** lo. T4 chỉ soi xung đột **liên-PRD** theo cách rẻ, KHÔNG nạp full doc khác:

1. Trích danh sách endpoint (method + path, §4.1) + entity chính của doc target.
2. `grep` từng path/entity đó trong `{paths.tech_docs_dir}/{domain}/*/tech-docs/*-tech-design.md` (trừ target) — chỉ đọc dòng match.
3. **Chỉ khi có va chạm** (một path/entity xuất hiện ở doc PRD khác) → nạp đúng đoạn §4.1/§4.2 (hoặc §3) của doc đó để so shape.

| Vấn đề *(chỉ khi grep dính)* | Severity | Auto-fixable? |
|-------|----------|---------------|
| Cùng endpoint path, request/response khác shape giữa 2 PRD | Critical | No — người giải quyết |
| Cùng service method với behavior khác | Critical | No — người giải quyết |
| Status transition của cùng entity khác nhau giữa các doc PRD | Critical | No — người giải quyết |
| Trách nhiệm chồng lấn (2 PRD cùng nhận sở hữu 1 endpoint/logic) | Major | No — người giải quyết |

Không có va chạm grep → T4 pass, không nạp thêm gì.

### T5 — Internal Consistency

Trong nội bộ tech-doc:

| Check | Severity | Auto-fixable? |
|-------|----------|---------------|
| Sequence diagram thể hiện flow khác phần mô tả viết | Major | No |
| API spec return type khác code sketch | Major | Yes — căn chỉnh cái này theo cái kia |
| Section tham chiếu một component/concept không bao giờ được định nghĩa sau đó | Minor | Yes — thêm định nghĩa |
| Assumption được nêu nhưng không design nào xử lý nó | Minor | Yes — thêm note hoặc bỏ assumption |

### T6 — Structural Completeness

Kiểm tra tất cả section chuẩn có mặt và không rỗng:

| Section | Missing severity |
|---------|-----------------|
| Header (`@trace.prd`, `@trace.ucs`, `@trace.domain`, `@trace.status`) | Major |
| Overview / Context | Major |
| Architecture Decision kèm lý do | Major |
| Component Diagram hoặc Layer Description | Major |
| Sequence Diagram hoặc Flow Steps | Major |
| API Contract (nếu hướng HTTP) | Major |
| Data Model Changes (nếu entity đổi) | Major |
| Error Handling Strategy | Major |
| Open Questions / Assumptions | Minor |

→ Mọi finding section-thiếu T6 đều **auto-fixable**: AI thêm skeleton section kèm prompt.

### T7 — Cross-Team API Contract Review

*Chỉ áp dụng khi TẤT CẢ điều sau đúng:*
*1. Doc có phần backend/API (`@trace.platforms` gồm `system`, tức PRD có System BDD).*
*2. Header tech-doc KHÔNG có `@trace.api_source: existing`.*

*Nếu `@trace.api_source: existing` → **skip T7 hoàn toàn**. Contract đã được PO xác định trong PRD — không có API design mới để đồng thuận.*

Dimension này đảm bảo team FE, App, và BE đều đồng thuận API contract trước khi bắt đầu implement.

**Step 1 — Check status sign-off trong header tech-doc:**

Đọc block `@trace.sign_off` trong header tech doc. Nếu vắng → thêm như một finding (auto-fixable: thêm skeleton).

```yaml
# @trace.sign_off:
#   be_team:  pending    # author — set "done" khi BE hài lòng với design
#   fe_team:  pending    # FE/Web — phải confirm contract khớp expectation của web BDD
#   app_team: pending    # App — phải confirm contract khớp expectation của app BDD (nếu áp dụng)
#   sa:       pending    # SA/Tech Lead — approval cuối
```

**Step 2 — Contract vs BDD cross-check:**

Nạp web và app BDD cho TICKET-ID này (từ `{paths.specs_dir}/{domain}/{prd-slug}/bdd/web/` và `{paths.specs_dir}/{domain}/{prd-slug}/bdd/app/` trong spec submodule hoặc spec repo).

Với mỗi platform BDD, kiểm tra API contract của tech doc có thoả các mệnh đề `Then` của BDD không:

| Check | Severity |
|---|---|
| Field response trong API contract không phủ những gì web BDD `Then` mong | Critical |
| Field response trong API contract không phủ những gì app BDD `Then` mong | Critical |
| Shape error response không khớp những gì các platform BDD mong | Major |
| Annotation `@system.resolution` của System BDD mâu thuẫn với design API contract | Critical |

**Step 3 — Report sign-off pending:**

Sau review, liệt kê các sign-off còn `pending`:

```
⏳ Sign-off pending trước khi tech docs được approve:
  fe_team  — team FE/Web phải confirm API contract khớp expectation web BDD
  app_team — team App phải confirm API contract khớp expectation app BDD
  sa       — SA/Tech Lead approval cuối

Khi thu đủ sign-off → cập nhật @trace.sign_off trong header tech doc, rồi chạy lại /review-tech-docs.
Tech docs không thể set "approved" khi còn bất kỳ sign-off bắt buộc nào pending.
```

**Approval gate:**
- Nếu `be_team: done` VÀ `fe_team: done` VÀ `app_team: done` (hoặc N/A) VÀ `sa: done` → tech docs có thể set `approved`
- Ngược lại → `@trace.status` giữ `in-review` — `generate-code` bị chặn

### T8 — Reconciliation & Completeness

*Bắt lỗi "đóng kín" + "coverage ≠ completeness" — cái mà Self-Review Gate của `generate-tech-docs` (Cổng 1/4) đáng lẽ chặn. Đối chiếu doc với PRD Business Rules, core-entities, và seam UC anh em (đọc-ngược có giới hạn — không kéo toàn bộ BDD của PRD).*

> **Nguyên tắc:** "mọi SC được map" (T3) là *cần*, KHÔNG *đủ*. T8 fail một doc dù T3 pass, nếu nó thiếu/mâu thuẫn ở tầng rộng hơn lát BDD.

> **Thuật ngữ cross-service (đọc nhanh):** *dedup* = cùng event tới ≥2 lần chỉ xử lý 1 lần (idempotency key) · *ordering* = event đúng thứ tự phát ra, hoặc bên nhận chịu được lệch · *ack path* = bên nhận xong báo lại bên gửi để ngừng gửi lại (thiếu → mất event / gửi lại vô hạn) · *cross-field invariant* = ràng buộc luôn đúng giữa nhiều field (vd `paid ⇒ paid_at ≠ null`).

| Vấn đề | Severity | Auto-fixable? |
|---|---|---|
| Enum/trạng thái dùng trong doc nhưng **không có producer** (không luồng nào sinh giá trị đó) | Major | No — người xác định owner |
| Cột/field ghi nhưng **không có writer** (không luồng nào set) | Major | No |
| **PRD Business Rule** yêu cầu một nguồn/sự kiện mà doc **bỏ sót** | Critical | No — thêm design trước |
| PRD-BR đưa **hợp đồng chung** (generic envelope) nhưng doc tự làm **typed-per-thing** | Major | No |
| **Leak boundary** — kéo định danh nội bộ của service khác vào lookup của mình thay vì abstraction tầng mình | Major | No |
| Cross-service **không tách** bên nào own dedup/ordering, hoặc thiếu **ack path** | Major | No |
| **Cross-field invariant** giữa các field/entity không được nêu | Minor | Yes — thêm note |

### T9 — Gap Honesty & Constants

*Bắt "bịa lặng" + "hard-code" + "happy-only" — cái mà Self-Review Gate Cổng 2/3/4 đáng lẽ chặn.*

| Vấn đề | Severity | Auto-fixable? |
|---|---|---|
| Policy/type/giá trị nêu **như fact không nguồn** (bịa), lẽ ra phải là `[GAP]`/`[ASSUMPTION]` | Critical | No — người xác nhận nguồn hoặc giữ GAP |
| Chỗ đáng lẽ khai gap lại **bỏ trắng / chép hình dạng ở boundary** | Major | No |
| **Constant/literal inline** như luật (chưa vào catalog / chưa đặt tên) | Major | Yes — tách vào bảng constants/enum |
| **Happy-only** — API/flow thiếu partial + error case + rollback | Major | No — thiết kế nhánh lỗi trước |
| **Hàm cốt lõi** được đặt tên nhưng **không tả** điều kiện chọn / nhánh / kết quả (dừng ở tên hàm + sequence-diagram) | Major | No — cần người bổ sung impl-spec |
| BDD **mâu thuẫn** invariant kiến trúc mà doc **lặng chép** thay vì ghi conflict + escalate PO sửa `.feature` | Critical | No — escalate, không tự quyết |
| Citation/tham chiếu **không resolve** (trỏ tới thứ không được định nghĩa) / policy nêu như fact | Minor | Yes — thêm định nghĩa hoặc bỏ ref |
| `[GAP]`/`[ASSUMPTION]` inline **mồ côi** — không có dòng ở §12 GAP Register (hoặc dòng §12 không có marker inline) | Major | Yes — đồng bộ register ↔ marker |
| §12 GAP Register còn **🔴 blocker `open`** (chưa đóng) | Critical | No — chặn approve tới khi owner đóng. *(GAP đã khai đúng KHÔNG tính là "bịa" — đây là finding về gate, không phạt trung thực)* |

> **T8/T9 phản chiếu Self-Review Gate** (4 cổng) của `generate-tech-docs` và `project-lessons` **L-012** — nếu gen bỏ lọt, cổng review bắt lại. Đặt trong nguồn `.tmpl` nên bền qua `/update-framework`.

---

## Ghi File Findings

Sau khi chạy hết các check, ghi findings vào `{paths.refinement_dir}/{TICKET-ID}-tech-review-findings.yaml`:

```yaml
source_file: "{absolute path to tech-doc}"
prd_id: "{TICKET-ID}"
ucs: [{UC-ID list phủ bởi doc}]
domain: "{domain}"
generated_at: "{ISO datetime}"
review_type: "tech-design"
status: "pending_review"
is_system_bdd: {true | false}  # true nếu doc có phần backend (@trace.platforms gồm system)

sign_off:                       # chỉ có khi is_system_bdd: true
  be_team:  pending             # đọc từ @trace.sign_off trong header tech-doc
  fe_team:  pending
  app_team: pending             # "n/a" nếu dự án không có platform app
  sa:       pending
sign_off_gate: blocked          # blocked | ready — "ready" chỉ khi tất cả bắt buộc là "done"

findings:
  - id: "F001"
    check_id: "T1"           # T1–T9
    severity: "critical"     # critical | major | minor
    section: "{section heading hoặc tên component nơi tìm thấy lỗi}"
    uc_id: "{UC-ID}"         # UC mà finding này thuộc về (một trong `ucs`; đọc §10 để xác định)
    quote: "{trích đoạn nguyên văn copy CHÍNH XÁC từ tech-doc tại vị trí lỗi, ≤120 ký tự}"
    finding: "{mô tả rõ ràng vi phạm hoặc gap}"
    suggestion: "{bản fix cụ thể — AI áp dụng khi --resume nếu được chấp nhận}"
    auto_fixable: false      # true = AI áp dụng được; false = người phải ghi quyết định trong note
    status: "pending"        # pending | accepted | modified | rejected | deferred

summary:
  total_findings: {N}
  by_severity: { critical: {N}, major: {N}, minor: {N} }
  auto_fixable: {N}
  requires_human_decision: {N}
  recommendation: "APPROVED | NEEDS_REVISION | BLOCKED"
  sign_off_gate: "{blocked — pending: fe_team, app_team, sa | ready}"
```

> **Field định vị (`quote` + `uc_id`) — bắt buộc cho source-jump của Review Board.**
> Với mỗi finding, copy một đoạn `quote` **nguyên văn** thẳng từ tech-doc tại đúng chỗ
> lỗi xảy ra — KHÔNG diễn giải lại; nó được so khớp với tài liệu để định vị dòng.
> Field này cho phép reviewer click một finding trong Review Board và nhảy tới đúng vị trí nguồn.

## Report

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

```
/review-tech-docs Hoàn tất — {target file}
PRD: {TICKET-ID} | UCs: {UC list} | Domain: {domain}
Findings: {total} | 🔴 Critical: {N} | 🟡 Major: {N} | 🟢 Minor: {N}
Auto-fixable: {N} | Needs human decision: {N}

GAP Register (§12): {open_blocker} 🔴 blocker open / {total_open} open / {total} tracked
  {🔒 còn blocker open → chặn approve | ✅ 0 blocker open}

Sign-off gate (chỉ system BDD):
  be_team  : {done | pending}
  fe_team  : {done | pending}   ← {name / "needs sign-off" }
  app_team : {done | pending | n/a}
  sa       : {done | pending}
  Gate     : {🔒 BLOCKED — pending: fe_team, sa | ✅ READY}

File findings: {paths.refinement_dir}/{TICKET-ID}-tech-review-findings.yaml
Next: Mở trong Review Board → Accept/Modify/Reject từng finding
      Rồi chạy: /review-tech-docs --resume {tech-design-file}
      Sau khi thu đủ sign-off → cập nhật @trace.sign_off trong tech doc, chạy lại review
```

---

## Resume Mode — Áp dụng các Finding được chấp nhận

*Kích hoạt khi `$ARGUMENTS` chứa `--resume`.*
*Ví dụ: `/review-tech-docs --resume {paths.tech_docs_dir}/payment/{prd-slug}/tech-docs/PAY-123-tech-design.md`*

### Phase 1 — Đọc các finding được chấp nhận

1. Suy ra file findings từ target: `{paths.refinement_dir}/{TICKET-ID}-tech-review-findings.yaml`
2. Đọc file. Gom các finding có `status: "accepted"` hoặc `status: "modified"`.
3. Nếu không có → báo "No accepted findings. File unchanged." và dừng.

### Phase 2 — Áp dụng fix

Áp dụng theo thứ tự: critical → major → minor.

| check_id | Làm gì |
|----------|-----------|
| T1 (Architecture) | Áp dụng fix cấu trúc từ note finding — chuyển logic về đúng layer, cập nhật mô tả component |
| T2 (Tên field) | Đổi field về tên canonical từ core-entities.md xuyên suốt tài liệu |
| T3 (Thiếu design note) | Thêm design decision note cho scenario chưa phủ |
| T5 (Internal inconsistency) | Căn chỉnh các section mâu thuẫn theo quyết định nêu trong note |
| T6 (Thiếu section) | Thêm skeleton section với prompt placeholder cho tech lead điền |
| T8 (cross-field invariant) | Thêm note invariant còn thiếu *(chỉ mục minor auto-fixable; enum mồ côi / PRD-BR bỏ sót / leak boundary cần người)* |
| T9 (constant / citation) | Tách constant inline vào bảng catalog/enum; thêm định nghĩa cho citation không resolve |

**Finding T1, T2, T4 và các mục Critical/Major của T8/T9 có `auto_fixable: false`:** cần một resolution do người viết trong
note "Modify" của Review Board (enum mồ côi, PRD-BR bỏ sót, leak boundary, bịa-lặng, happy-only, BDD↔kiến trúc mâu thuẫn — không tự đoán). Áp dụng đúng những gì note nói. Đừng bịa fix.

### Phase 3 — Cập nhật header + TSV + Report

Sửa file tech-doc trực tiếp:
1. Tìm `@trace.revision:` trong header — tăng giá trị integer lên 1.
2. Tìm `@trace.status:` trong header. Set `approved` **chỉ khi CẢ HAI**:
   - (a) sign_off_gate = `ready` (tất cả sign-off done; hoặc doc không có phần system → không cần sign-off), **VÀ**
   - (b) §12 GAP Register **không còn 🔴 blocker nào ở trạng thái `open`** (đếm ở T9).
   Thiếu (a) hoặc (b) → set `in-review` (chặn `/generate-code`); ghi rõ lý do vào report (sign-off pending / còn N blocker-GAP open).
3. Nếu block `@trace.sign_off` vắng và đây là tech doc system BDD → thêm nó với tất cả giá trị `pending`.

Ghi cả hai thay đổi vào file.

Rồi cập nhật TSV cho **mọi UC mà doc phủ** (`@trace.ucs`) — doc gộp có một `@trace.revision` chung cho cả BE và client:
- Với mỗi UC trong `@trace.ucs`: glob **mọi sổ platform** `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-*.tsv` (system/web/app), và set `tech_doc_revision` thành integer `@trace.revision` mới cho mọi row trong từng sổ đó.
- **KHÔNG** đụng `fe_tech_doc_revision` ở đây — cột đó do `/generate-code --phase=integration` ghi khi FE thực sự wire adapter theo §4.5.4 (drift-detect riêng cho FE integration).
- Set `last_updated` thành ngày hôm nay (`YYYY-MM-DD`) cho các row vừa chạm.

In report sau khi hoàn tất mọi lần ghi file.

```
/review-tech-docs --resume Đã áp dụng — {target file}
PRD: {TICKET-ID} | UCs: {UC list}

Applied  : {N} findings ({critical} critical, {major} major, {minor} minor)
Skipped  : {N} rejected/deferred

Changes:
  - {change 1}
  - {change 2}

Revision : {old} → {new}
Status   : {approved | in-review}

Sign-off : {✅ Tất cả done — status set approved
           | 🔒 Pending: fe_team, sa — status set in-review
              Cập nhật @trace.sign_off trong tech doc khi mỗi team confirm, rồi chạy lại /review-tech-docs}

Chạy lại /review-tech-docs {file} để xác nhận 0 finding critical còn lại.
Next: {/generate-code {feature-file}  ← chỉ khi status = approved
      | Thu các sign-off pending → cập nhật @trace.sign_off → chạy lại /review-tech-docs}
      → nếu tech-doc sống trong spec repo dùng chung: commit + push lên spec submodule để FE/App `/sync` contract đã cập nhật
```
