@educa-corp/sdd-framework 0.9.4 → 0.9.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/bin/lint-trace.js +230 -2
  2. package/bin/self-check.js +24 -0
  3. package/bin/trace-schema.json +58 -4
  4. package/core/FRAMEWORK_VERSION +1 -1
  5. package/core/commands/generate-bdd.md +1 -0
  6. package/core/commands/generate-code.md +39 -2
  7. package/core/commands/generate-tech-docs.md +21 -2
  8. package/core/commands/map-testids.md +88 -8
  9. package/core/commands/qc-analyze.md +424 -354
  10. package/core/commands/qc-design-test.md +246 -182
  11. package/core/commands/qc-plan.md +92 -79
  12. package/core/commands/qc-report.md +76 -60
  13. package/core/commands/qc-review.md +130 -114
  14. package/core/commands/qc-run-test.md +230 -156
  15. package/core/commands/review-tech-docs.md +20 -0
  16. package/core/modules/qc-playwright/stack-profile.yaml +1 -1
  17. package/core/rules/data-protection.md +52 -0
  18. package/core/skills/qc/_shared/self-review-principles.md +112 -0
  19. package/core/skills/qc/qa-runner/e2e.md +2 -2
  20. package/core/skills/qc/qa-runner/functional/gui-feature.md +9 -3
  21. package/core/skills/qc/qa-runner/functional/gui-screen.md +9 -3
  22. package/core/skills/qc/qa-runner/integration.md +1 -1
  23. package/core/skills/qc/qa-runner/non-functional.md +1 -1
  24. package/core/skills/spec/SKILL.md +1 -1
  25. package/core/steps/context-loader.md +7 -2
  26. package/core/steps/gap-verify.md +67 -0
  27. package/core/steps/report-footer.md +3 -3
  28. package/core/templates/feature.template +1 -0
  29. package/core/templates/tech-design.template.md +1 -0
  30. package/docs/04-reference/commands.md +1 -1
  31. package/docs/04-reference/trace-schema.md +38 -0
  32. package/docs/explain/11-map-testids.md +70 -69
  33. package/docs/plans/qc-surgery/00-nhat-ky.md +497 -0
  34. package/docs/plans/qc-surgery/01-checklist.md +92 -0
  35. package/docs/plans/qc-surgery/02-lo-trinh.md +266 -0
  36. package/docs/plans/qc-surgery/buoc/0-01-testid-attr-co-cho-o.md +157 -0
  37. package/docs/plans/qc-surgery/buoc/0-02-mot-nguon-cho-testid-attr.md +135 -0
  38. package/docs/plans/qc-surgery/buoc/0-03-skill-thoi-day-do-dom.md +167 -0
  39. package/docs/plans/qc-surgery/buoc/0-04-may-canh-hop-dong.md +173 -0
  40. package/docs/plans/qc-surgery/buoc/0-05-don-nhan-cot-va-2b.md +133 -0
  41. package/docs/plans/qc-surgery/buoc/0-06-hop-dong-truoc-code.md +226 -0
  42. package/docs/plans/qc-surgery/buoc/1-01-guard-br-tag.md +156 -0
  43. package/docs/plans/qc-surgery/buoc/1-02-guard-sc-coverage.md +153 -0
  44. package/docs/plans/qc-surgery/buoc/1-03-fail-3-nhan.md +176 -0
  45. package/docs/plans/qc-surgery/buoc/1-04-self-review-dung-chung.md +175 -0
  46. package/docs/plans/qc-surgery/buoc/1-05-spec-la-du-lieu.md +164 -0
  47. package/docs/plans/qc-surgery/buoc/1-06-gap-verify-du-bo.md +162 -0
  48. package/docs/plans/qc-surgery/buoc/README.md +85 -0
  49. package/docs/plans/qc-surgery/exec-d0-b1-testid-attr-header.md +147 -0
  50. package/docs/plans/qc-surgery/exec-d0-b2-thong-nhat-nguon-testid-attr.md +152 -0
  51. package/docs/plans/qc-surgery/exec-d0-b3-sua-skill-probe-dom.md +173 -0
  52. package/docs/plans/qc-surgery/exec-d0-b4-may-canh-4-5-6.md +168 -0
  53. package/docs/plans/qc-surgery/exec-d0-b5-don-nhan-lech.md +196 -0
  54. package/docs/plans/qc-surgery/exec-d0-b6-contract-truoc-code.md +350 -0
  55. package/docs/plans/qc-surgery/exec-d1-b1-guard-br-tag.md +129 -0
  56. package/docs/plans/qc-surgery/exec-d1-b2-guard-sc-coverage.md +159 -0
  57. package/docs/plans/qc-surgery/exec-d1-b3-fail-3-bucket.md +158 -0
  58. package/docs/plans/qc-surgery/exec-d1-b4-self-review-principles.md +145 -0
  59. package/docs/plans/qc-surgery/exec-d1-b5-noi-quy-spec-la-du-lieu.md +156 -0
  60. package/docs/plans/qc-surgery/exec-d1-b6-gap-verify-mo-rong.md +179 -0
  61. package/docs/plans/qc-surgery/exec-d2-b1-tach-qc-review.md +166 -0
  62. package/docs/plans/qc-surgery/exec-d2-b2-tach-qc-run-test-atomic.md +267 -0
  63. package/docs/plans/qc-surgery/exec-d2-b3-qc-automation-assess.md +198 -0
  64. package/docs/plans/qc-surgery/exec-d3-b1-qc-report-gate-decision.md +209 -0
  65. package/docs/plans/qc-surgery/exec-d4-b1-qc-design-testdata.md +146 -0
  66. package/docs/plans/qc-surgery/exec-d4-b2-qc-smoke-test.md +179 -0
  67. package/docs/plans/qc-surgery/exec-d4-b3-qc-metrics-va-lint.md +198 -0
  68. package/docs/plans/qc-surgery/exec-d4-b4-lint-spec-injection.md +199 -0
  69. package/package.json +1 -1
@@ -1,17 +1,17 @@
1
- ---
2
- version: 1.0
3
- updated: 2026-06-11
4
- ported_from: ai-automation-qc-base
5
- ---
6
-
7
- # /qc-analyze — QC Requirement Analysis
8
-
9
- > Stage 1 của QC automation pipeline native (qc-analyze → qc-plan → qc-design-test → qc-review → qc-run-test → qc-report). Port từ qa-analyst của team QC. Markdown-first: không có script ở đây.
10
-
11
- ## Gate
12
-
13
- *Checkpoint: **chặn thường** — lệnh ghi 2 file artifact. `--yes` bỏ qua được (gate Bước 3a).*
14
-
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-06-11
4
+ ported_from: ai-automation-qc-base
5
+ ---
6
+
7
+ # /qc-analyze — QC Requirement Analysis
8
+
9
+ > Stage 1 của QC automation pipeline native (qc-analyze → qc-plan → qc-design-test → qc-review → qc-run-test → qc-report). Port từ qa-analyst của team QC. Markdown-first: không có script ở đây.
10
+
11
+ ## Gate
12
+
13
+ *Checkpoint: **chặn thường** — lệnh ghi 2 file artifact. `--yes` bỏ qua được (gate Bước 3a).*
14
+
15
15
  # Gate — Quy trình vào chuẩn cho mọi lệnh
16
16
 
17
17
  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ó.
@@ -163,355 +163,425 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
163
163
  - "N" → dừng, hỏi người dùng muốn thay đổi gì.
164
164
  - Có `--yes` và mức *chặn thường* → coi như "Y", **nhưng vẫn IN khối CHECKPOINT** nếu có cờ
165
165
  🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
166
-
167
-
168
- *Lưu ý: Với lệnh này, target ở Bước 1 là một **TICKET-ID** (mã PRD), hoặc một UC-ID / file feature / file PRD — cả ba đều quy về TICKET-ID ở §Phạm vi QC. Trạm này chạy cho **cả PRD × một nền**. Đọc spec chính thức của **mọi UC trong phạm vi** — file `.feature` (mang `@trace.id={UC-ID}` và mỗi scenario `@trace.scenario={UC-ID}-SC{N}`), PRD, và design-spec — từ feature package `{paths.specs_dir}/{domain}/{prd-slug}/` (file `.feature` dưới `bdd/`, file PRD `{TICKET-ID}-{prd-slug}.md` ở gốc folder, và design-spec dưới `design-spec/`). Spec của framework CHÍNH LÀ source of truth; đừng suy lại các requirement đã có ở đó. **Ngoài ra đọc tech-doc gộp** `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` làm **nguồn thứ hai** — xem §Đối chiếu tài liệu kỹ thuật.*
169
-
170
- ## Context
166
+
167
+
168
+ *Lưu ý: Với lệnh này, target ở Bước 1 là một **TICKET-ID** (mã PRD), hoặc một UC-ID / file feature / file PRD — cả ba đều quy về TICKET-ID ở §Phạm vi QC. Trạm này chạy cho **cả PRD × một nền**. Đọc spec chính thức của **mọi UC trong phạm vi** — file `.feature` (mang `@trace.id={UC-ID}` và mỗi scenario `@trace.scenario={UC-ID}-SC{N}`), PRD, và design-spec — từ feature package `{paths.specs_dir}/{domain}/{prd-slug}/` (file `.feature` dưới `bdd/`, file PRD `{TICKET-ID}-{prd-slug}.md` ở gốc folder, và design-spec dưới `design-spec/`). Spec của framework CHÍNH LÀ source of truth; đừng suy lại các requirement đã có ở đó. **Ngoài ra đọc tech-doc gộp** `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` làm **nguồn thứ hai** — xem §Đối chiếu tài liệu kỹ thuật.*
169
+
170
+ ## Context
171
171
  **BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
172
172
  rồi mới tiếp tục phần bên dưới.
173
173
 
174
174
  Bỏ qua bước này thì `{paths.*}`, `{tech_stack.*}`, `{conventions.*}`, guardrail từ
175
175
  `project-lessons`, và routing service (chế độ umbrella) đều **chưa được phân giải** — mọi
176
- placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
177
-
178
- ---
179
-
180
- ## Phạm vi QC — PRD nào, nền nào, những UC nào
181
-
176
+ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
177
+
178
+ ---
179
+
180
+ ## Phạm vi QC — PRD nào, nền nào, những UC nào
181
+
182
182
  **BẮT BUỘC — đọc `.agent/steps/qc-scope.md` và thực thi TOÀN BỘ quy trình trong đó**,
183
183
  rồi mới tiếp tục phần bên dưới.
184
184
 
185
185
  Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
186
186
  `qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--include-draft`).
187
187
  Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —
188
- cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
189
-
190
- > **QC chạy trên BDD chưa chốt có thể phải làm lại.** `qc-scope` mặc định chỉ lấy UC có
191
- > `@trace.status: approved`; UC còn nháp vẫn vào bảng *Phạm vi phân tích* của `DOC_GAP.md`
192
- > với dấu `⏸ Chưa xét` — **không im lặng bỏ khỏi bảng**, vì "chưa xét" khác "đã xét, sạch".
193
- > Cố ý QC sớm thì thêm `--include-draft`, và artifact phải ghi rõ nó dựa trên BDD nháp.
194
-
195
- > **Vì sao trạm này chạy CẢ PRD chứ không từng UC** *(B11)*. Ba lý do, theo thứ tự quan trọng:
196
- >
197
- > 1. **Mâu thuẫn chéo UC chỉ lộ ra khi đọc cùng lúc.** UC1 nói một kiểu, UC3 nói kiểu khác —
198
- > chạy tách từng UC thì về **cấu trúc** là không thể thấy, không phải "khó thấy".
199
- > 2. **Rẻ hơn.** PRD, bản thiết kế, tài liệu kỹ thuật là nguồn **dùng chung**; chạy per-UC là
200
- > đọc lại chúng mỗi UC một lượt. Phần dùng chung chiếm đa số đầu vào.
201
- > 3. **Một tài liệu cho một tính năng** là cách PO và QC vốn làm việc — file gốc của đội QC
202
- > (`DOC_GAP_FEAT-02-3.md`) không có hậu tố UC, và `qa-planner/test-plan.md` vốn viết
203
- > *"Test Plan cho một feature"*.
204
-
205
- ---
206
-
207
- ## Đối chiếu tài liệu kỹ thuật *(nguồn thứ hai — bắt lệch nghiệp vụ ↔ kỹ thuật)*
208
-
209
- Định vị tech-doc gộp cấp PRD: `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`.
210
- Nó phủ **nhiều UC** — và trạm này cũng phủ nhiều UC, nên đọc **mọi phần chạm `uc_list`**
211
- (đối chiếu `@trace.ucs` ở header với `uc_list`). Phần thuộc UC ngoài phạm vi (`⏸ Chưa xét`) thì bỏ qua.
212
-
213
- > **Đây là chỗ layout cấp PRD trả lãi rõ nhất.** Tech-doc gộp là **một** tài liệu phủ cả PRD.
214
- > Chạy per-UC thì nó bị đọc lại N lần, mỗi lần lọc bỏ gần hết — và mâu thuẫn giữa hai UC trong
215
- > **cùng** tài liệu đó không lần nào lộ ra, vì không lần nào thấy cả hai.
216
-
217
- **Không tìm thấy → cảnh báo mềm, KHÔNG chặn** (dự án có thể chưa dựng tech-doc):
218
- ```
219
- ⚠️ Không có tech-doc cho {TICKET-ID} — phân tích chỉ dựa trên PRD + BDD + design-spec.
220
- Lệch giữa yêu cầu nghiệp vụ và hợp đồng kỹ thuật (enum, mã lỗi, ràng buộc field) sẽ KHÔNG được phát hiện ở trạm này.
221
- ```
222
-
223
- **Có → đối chiếu các mục sau với PRD/BDD, mỗi chỗ vênh là một gap `CONTRADICTORY`:**
224
-
225
- | Mục tech-doc | Đối chiếu gì với PRD/BDD |
226
- |---|---|
227
- | §3 Mô hình dữ liệu | thực thể/field/quan hệ PRD nhắc tới có khớp không |
228
- | **§4 Hợp đồng API** | **enum & tập giá trị hợp lệ** · ràng buộc field (độ dài, định dạng, bắt buộc) · **mã lỗi** — PRD nêu bao nhiêu nhánh lỗi, contract định nghĩa bao nhiêu |
229
- | §4.5 Ánh xạ component UI | màn/component PRD·design-spec mô tả có mặt đủ không |
230
- | §5 Luồng chính | thứ tự bước, nhánh rẽ có khớp scenario `.feature` không |
231
- | §6 Điểm tích hợp | side-effect PRD nêu (gửi sự kiện, gọi dịch vụ khác) có được định nghĩa không |
232
- | §8 Xử lý lỗi & biên | trường hợp biên PRD nêu có đường xử lý không, và ngược lại |
233
-
234
- > **Vì sao mục này tồn tại.** Có một lớp gap **chỉ lộ ra khi so hai loại tài liệu với nhau** —
235
- > đọc riêng bên nào cũng thấy hợp lý. Ca điển hình: PRD viết *"chọn lớp 1–6"*, contract định
236
- > nghĩa enum `1..9`. Không ai đọc cả hai thì không ai thấy, và nó ra tận lúc chạy thật.
237
- > **Đây là lý do trạm này đọc tech-doc — không phải để hiểu kỹ thuật, mà để bắt chỗ hai bên nói khác nhau.**
238
-
239
- ### §12 GAP Register — ĐỌC, KHÔNG GHI
240
-
241
- Tech-doc có sổ ẩn số thiết kế riêng (`§12`), với vòng đời và người chịu trách nhiệm riêng, và
242
- `/generate-code` đã canh nó. **Trạm này chỉ đọc, tuyệt đối không ghi vào.**
243
-
244
- Với mỗi mục `open` trong §12 chạm **bất kỳ UC trong `uc_list`**:
245
- - **KHÔNG mở gap mới** trong `DOC_GAP.md` về cùng chuyện đó.
246
- - Ghi vào `REQUIREMENT_ANALYSIS.md` mục *"Đang chờ chốt (từ §12 tech-doc)"*: `{id}` · **UC** · điều chưa biết · owner · severity.
247
- - Test case chạm nó về sau sẽ bị chặn — nhưng bị chặn bởi **một mục đã có người xử lý**, không phải bởi một câu hỏi mới gửi PO.
248
-
249
- > **Vì sao không ghi vào.** Một ẩn số đã nằm trong §12 nghĩa là **đã có người đang lo**: có
250
- > owner, có mức chặn, có cổng chặn sinh code. Mở lại nó thành gap QC là gửi PO một câu hỏi
251
- > về thứ không phải việc của PO, và tạo hai sổ cùng theo dõi một chuyện — rồi chúng lệch nhau.
252
- > Đây đúng là **câu hỏi lọc Q1** của `steps/gap-verify.md` (*"chỗ này đã được trả lời ở tài liệu
253
- > khác chưa?"*), chỉ mở rộng phạm vi "tài liệu khác" thêm một nguồn.
254
-
255
- **Ngoại lệ — mục `spec-defect` là việc của PO.** §12 phân ba loại: `nội tại` (backend tự quyết) ·
256
- `cross-service` (đội khác) · `spec-defect` (PRD/BDD sai hoặc thiếu). Hai loại đầu → ghi "đang chờ".
257
- Loại thứ ba **đúng là gap tài liệu** → vẫn ghi vào `DOC_GAP.md` (cột `UC` = UC bị chạm), trỏ
258
- ngược về `{id}` của §12 để không đếm hai lần.
259
-
260
- ---
261
-
262
- ## Role
263
-
264
- Bạn là **QC Analyst** — stage đầu tiên của QC automation pipeline. Lấy requirement
265
- chính thức (PRD + BDD `.feature` + design-spec) và phân rã thành một mô tả requirement
266
- CÓ CẤU TRÚC: function, business rule, data flow, acceptance criteria. Bạn **không**
267
- viết test case chi tiết hay Python (đó là qc-design-test / qc-run-test).
268
-
269
- Ranh giới với `/qc-plan`: bạn trả lời *"requirement là gì?"*; qc-plan trả lời *"rủi ro ở đâu,
270
- hỏi dev gì?"*. Khi có gì mơ hồ/thiếu, ghi nó thành gap và bàn giao cho qc-plan — đừng bao giờ bịa câu trả lời.
271
-
272
- ## Skills (`{paths.qc_skills_dir}/qa-analyst/`)
273
-
274
- Chỉ nạp file cho bước đang làm (mỗi file tự đủ):
275
- - `spec-breakdown.md` — phân rã spec/PRD/user story thành cấu trúc.
276
- - `business-rules.md` — trích business rule, điều kiện, ràng buộc (code `BR-xx`).
277
- - `data-flow.md` — input/output, data flow, điểm tích hợp/thất bại.
278
- - `acceptance-criteria.md` — acceptance criteria Given/When/Then (code `AC-xx`).
279
-
280
- Thứ tự điển hình: spec-breakdown → business-rules / data-flow → acceptance-criteria.
281
-
282
- ## Trace mapping (bắt buộc)
283
-
284
- File `.feature` chính thức đã định nghĩa scenario là `@trace.scenario={UC-ID}-SC{N}` với
285
- `@trace.business_rules`. Map mọi `BR-xx` / `AC-xx` bạn tạo ra tới `{UC-ID}-SC{N}` sở hữu nó
286
- và ghi lại mapping — **làm cho từng UC trong `uc_list`**, và `BR`/`AC` phải mang rõ UC của nó
287
- (một file phân tích giờ phủ nhiều UC, nên `BR-01` không còn tự phân biệt được là của UC nào) — qc-design-test và qc-run-test cần nó để gắn tag
288
- `@trace.verifies` cho test và ghi `qc_status` theo từng scenario.
289
-
290
- ## Quét gap hai nguồn, gộp rồi mới thẩm định
291
-
292
- Gap đến từ **hai chỗ**, chúng bổ sung nhau chứ không thay thế:
293
-
294
- | Nguồn | Trả lời câu | Gap |
295
- |---|---|---|
296
- | **4 kỹ năng phân tích** trên | *"yêu cầu gì?"* | sản phẩm phụ đang bóc luật nghiệp vụ thì gặp chỗ mâu thuẫn |
297
- | **Quét theo lăng kính** *(dưới đây)* | *"còn thiếu gì?"* | mục tiêu chính |
298
-
299
- ### Quét theo lăng kính
300
-
301
- Chạy `steps/review-fanout.md` với:
302
-
303
- | Tham số | Giá trị |
304
- |---|---|
305
- | `DIMENSIONS` | **4 lăng kính** — `D2 Xử lý lỗi` · `D3 Giao diện` · `D4 Dữ liệu & cấu hình` · `D5 Đối chiếu chéo` **(thu hẹp — xem dưới)** *(định nghĩa ở `{paths.qc_skills_dir}/qa-analyst/exhaustive-gap-scanner.md`)* |
306
- | `FINDINGS SCHEMA` | như §Output dưới đây |
307
- | `GRANULARITY` | **`auto`** — chia theo ngưỡng kích thước, KHÔNG ép mịn theo từng UC |
308
- | `VERIFY` | **`off`** thẩm định chạy MỘT lần bước sau, trên tập đã gộp |
309
-
310
- **`D5` chạy dạng THU HẸP chỉ 2 trong 4 cặp tài liệu:**
311
-
312
- | Cặp | |
313
- |---|---|
314
- | `PRD design-spec/` | **SO** không ai đối chiếu nội dung. `/generate-bdd` chỉ kiểm `Built from PRD` (số phiên bản); cùng phiên bản mà nội dung lệch thì lọt |
315
- | `bdd/{platform}/ design-spec/` | ✅ **SO** — không ai |
316
- | `PRD bdd/` | bỏ `/review-context` **B1** đã làm |
317
- | `PRD·bdd/ ↔ tech-docs/` | ❌ bỏ — §Đối chiếu tài liệu kỹ thuật **ở trên** đã làm |
318
-
319
- > **Cả hai cặp SO đều dính `design-spec/`** artifact duy nhất trong feature package **không
320
- > tài liệu nào đối chiếu nội dung với nó**. Đừng lẫn với `tech-docs/`: `design-spec/` *giao diện
321
- > Designer vẽ*, `tech-docs/` *hợp đồng hệ thống* — và `tech-docs/` đã được phủ ở §trên.
322
- >
323
- > Trạm này **đã đọc `design-spec/`** từ trước (nó nằm trong danh sách nguồn Gate), nên `D5`
324
- > không nạp thêm file nào — chỉ bắt nó **so** thay chỉ **đọc**. Rẻ hơn một lăng kính thường.
325
-
326
- **`D1 Luật nghiệp vụ` là lăng kính duy nhất KHÔNG bật:** `qa-analyst/business-rules.md` đã hỏi
327
- 4/5 câu của nó, hỏi cụ thể hơn *"min/max · tự cho phép · trim · định dạng"* thay vì
328
- *"ngưỡng đã chốt chưa"*.
329
-
330
- > **Ghi lại vì sao `D5` từng bị tắt:** do ban đầu *"trùng nhiều"* **đúng một nửa**. Nó phủ
331
- > **bốn** cặp, chỉ **hai** cặp đã người làm. Sai suy từ ấn tượng thay đếm danh sách; bảng
332
- > kiểm chứng 31 câu hỏi (`docs/plans/qc-implementation-log.md`) thứ đáng lẽ phải làm **trước**
333
- > khi quyết. Đừng tắt lại `D5` mà không đọc bảng đó.
334
-
335
- > **`GRANULARITY = auto`, không phải `per-uc`.** `/refine-prd` ép mịn theo từng UC tầng PRD
336
- > một gap bỏ sót **im lặng đi tiếp** tới tận lúc chạy thật. đây khác: gap bỏ sót còn **bốn lớp
337
- > chặn phía sau** trạm 3 bật ngược khi không viết nổi giá trị mong đợi, trạm 4 soát độ phủ,
338
- > trạm 5 phân loại lỗi thật vs script sai. Ép mịn đây tốn gấp ~3 lần cho tính năng nhỏ mà đổi
339
- > lấy một lưới an toàn đã có sẵn ba lớp khác.
340
-
341
- ### Gộp trước, thẩm định sau
342
-
343
- Gộp gap từ **cả hai nguồn** vào một tập trước khi sang bước thẩm định.
344
-
345
- > **Không thẩm định từng nguồn riêng.** Phép kiểm `T6` của `gap-verify` là *"hai gap cùng gốc
346
- > thì gộp lại"* chỉ chạy được khi **thấy toàn bộ** tập. Thẩm định hai lần trên hai tập rời
347
- > thì không bắt được trùng lặp chéo nguồn, PO nhận hai câu hỏi giống nhau.
348
-
349
- ---
350
-
351
- ## DOC_GAP (bắt buộc)
352
-
353
- Luôn tạo **đúng MỘT** file gap cho cả (PRD × nền) theo
354
- `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` — các UC là các hàng bên trong, phân
355
- biệt bằng cột `UC`:
356
- - **Bảng 11 cột**, cột 2 là `UC`. ID gap `GAP-UC{N}-{nnn}` (vd `GAP-UC1-001`); gap thuộc cả
357
- PRD `GAP-GEN-{nnn}`. Đánh số **độc lập trong từng UC** phân tích lại UC1 KHÔNG được làm
358
- đổi số gap của UC2, test case đã đang trỏ `🚫 Block: [GAP-UC2-003]`.
359
- - **Section `Phạm vi phân tích`** — mỗi UC một hàng kèm `@trace.status`, đã phân tích chưa, số
360
- gap. UC ngoài phạm vi ghi `⏸ Chưa xét`, **không bỏ khỏi bảng**.
361
- - Mỗi gap phân loại MISSING / AMBIGUOUS / CONTRADICTORY / ASSUMPTION / OPEN QUESTION, với severity (🔴 Blocker → ⚪ Low) và function/BR/AC bị ảnh hưởng.
362
- - Không bao giờ bịa câu trả lời; đánh dấu giả định là `ASSUMPTION` để PO/dev confirm.
363
- - Bất kỳ `🔴 Blocker` nào còn `Open` ⇒ **UC ở cột `UC` của hàng đó** chưa sẵn sàng cho
364
- qc-design-testbàn giao cho qc-plan. *Chặn theo từng UC, KHÔNG chặn cả PRD:* một blocker
365
- UC3 không do dừng việc thiết kế test cho UC1. Ghi rõ UC nào bị chặn ở report.
366
- - **Đẩy các defect spec thực sự lên PO (không chỉ giữ local).** Một blocker là lỗi thật
367
- trong spec chính thức`AMBIGUOUS` / `CONTRADICTORY` / `MISSING` trong PRD/BDD phải tới
368
- PO qua feedback flow, không chỉ nằm trong `DOC_GAP.md`: tạo `/report-bug {UC-ID} {desc}`
369
- (`{UC-ID}` lấy từ cột `UC` của hàng gapbug đi theo UC, không theo PRD)
370
- (BUG_FLOW của phân loại PRD vs BDD), hoặc `/propose-scenario {UC-ID}` nếu gap thiếu test
371
- coverage. Gap `ASSUMPTION` / `OPEN QUESTION` được confirm qua questions-for-dev của qc-plan không file thành bug.
372
-
373
- ### Thẩm định trước khi bàn giao *(bắt buộc)*
374
-
375
- Sinh xong `DOC_GAP.md`, **đọc `.agent/steps/gap-verify.md` và chạy toàn bộ quy trình trong đó**
376
- với:
377
- - `FINDINGS` = mọi gap đang `Open` trong `DOC_GAP.md`
378
- - `EVIDENCE_ROOT` = `{paths.specs_dir}` — spec repo của PO, **không** phải `{paths.qc_dir}`
379
- - `VERDICT_FIELD` = cột `Trạng thái` + `Câu trả lời` của bảng gap
380
- - `RERATE` = `on`
381
-
382
- Gap rớt thẩm định được **đóng kèm do**, KHÔNG xoá — người đọc phải kiểm chứng được vì sao
383
- bị loại. Cập nhật `Tổng số gap` + bảng ưu tiên sau khi áp verdict, và in khối
384
- `[GAP VERIFY]` + cam kết cuối vào report.
385
-
386
- > ** sao bắt buộc, không phải tuỳ chọn.** Bước phân tích trên chỉ lực **tìm thêm** —
387
- > bốn skill lần lượt quét spec mỗi cái đều được khuyến khích ghi ra chỗ nghi ngờ. Không
388
- > bước nào hỏi ngược *"cái vừa ghi thật không?"*. Hệ quả đo được đội QC: phần lớn gap sinh
389
- > ra gap ảo spec đã trả lời tài liệu khác, hoặc trích dẫn sai, hoặc là chuyện QC tự quyết
390
- > được. Mà gap ảo không chỉ tốn thời gian PO: nó **làm PO mất tin vào cả danh sách**, và lúc đó
391
- > những gap thật cũng chết theo. `gap-verify` là bộ lọc duy nhất đứng giữa hai chuyện đó.
392
-
393
- ## Output
394
-
395
- Ghi **hai file** dưới `{qc_artifact_dir}` (= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/`)
396
- + **một** dưới `{paths.refinement_dir}/`.
397
-
398
- **Mỗi loại đúng MỘT file cho cả PRD** — đừng tách một-file-mỗi-UC, và cũng đừng tách
399
- một-file-mỗi-bước (không có file spec-breakdown / business-rules / data-flow / AC riêng):
400
-
401
- 1. **`{qc_artifact_dir}REQUIREMENT_ANALYSIS.md`** bản phân tích hợp nhất duy nhất cho cả PRD.
402
- Mở đầu bằng **bảng `Phạm vi phân tích`** (cùng nội dung với bảng trong `DOC_GAP.md`), rồi
403
- **một mục cho mỗi UC trong phạm vi**, mỗi mục theo thứ tự: phân rã requirement → bảng
404
- business-rule (`BR-xx`) data-flow acceptance-criteria (`AC-xx`), mỗi `BR`/`AC` map tới
405
- `{UC-ID}-SC{N}` (của `.feature` nền này) sở hữu nó.
406
- Thêm một mục **"Mâu thuẫn chéo UC"** chỗ hai UC của cùng PRD nói khác nhau. Rỗng thì ghi
407
- "Không có". *Đây thứ chỉ trạm cấp PRD nhìn thấy được; đừng bỏ mục.*
408
- Cuối file thêm mục **"Đang chờ chốt (từ §12 tech-doc)"** các ẩn số thiết kế `open` chạm
409
- các UC trong phạm vi (`{id}` · UC · điều chưa biết · owner · severity). Rỗng thì ghi
410
- "Không có"; **đừng bỏ mục**.
411
- 2. **`{qc_artifact_dir}DOC_GAP.md`** file gap, theo
412
- `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` + luật viết ở
413
- `{paths.qc_skills_dir}/qa-analyst/spec-issue-reporter.md`. Bắt buộc:
414
- - **Bảng 11 cột** đúng thứ tự, cột **`UC`** (cột 2) cột **Giao cho đội** (Dev / PO / BA / Design / Kiến trúc / Dữ liệu).
415
- - Section **"Phạm vi phân tích"** ngay sau metadata mỗi UC một hàng kèm `@trace.status`,
416
- đã phân tích chưa, số gap. UC ngoài phạm vi ghi `⏸ Chưa xét`.
417
- - Ô câu hỏi đủ **bốn phần** tách bằng `<br/>`: **Bối cảnh Vấn đề → Tại sao quan trọng → Gợi ý**.
418
- *Ba phần đầu cho PO xếp ưu tiên; phần cuối cho PO trả lời nhanh không phải nghĩ lại từ đầu.*
419
- - Section **"Tài liệu đầu vào đã đọc để phân tích"** đặt ngay sau metadata — liệt kê **đủ** mọi
420
- file đã mở. Đây căn cứ độ phủ: không thì không ai phân biệt được *"đã đọc và không thấy"*
421
- với *"chưa đọc"*.
422
- - Mức nặng nhất dùng từ **`🔴 Blocker`** (không phải `Critical`) — `/qc-run-test` đọc đúng từ này
423
- để đặt *"scenario đang chờ PO"* vào sổ trace.
424
- 3. **`{paths.refinement_dir}/{TICKET-ID}-qa-findings.yaml`** — **cùng dữ liệu gap**, ở định dạng Review Board đọc được. Xem §Bản findings dưới đây.
425
-
426
- `{paths.qc_dir}` là folder top-level NHÌN THẤY trong QC repo (mặc định `docs/`, **không** phải
427
- `.agent/review/` ẩn) để team QC mở xử lý output dễ dàng. Spec chính thức ở lại
428
- spec submodule của POđừng ghi phân tích vào đó.
429
-
430
- ### Bản findings — một nguồn, hai mặt
431
-
432
- File `.yaml` `DOC_GAP.md` **cùng một tập gap**, không phải hai tập. Sinh `DOC_GAP.md`
433
- trước ( bản người đọc), rồi **render** sang `.yaml` đừng phân tích lại lần hai.
434
-
435
- Dùng **đúng schema của `/refine-prd`** để Review Board đọc được cả hai loại file:
436
-
437
- ```yaml
438
- prd_source: "{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md"
439
- ticket_id: "{TICKET-ID}"
440
- platform: "{active_platform}"
441
- ucs: ["{UC-ID}", "…"] # các UC TRONG phạm vi (approved) theo thứ tự
442
- ucs_skipped: ["{UC-ID}"] # UC chưa xét, kèm lý do ở DOC_GAP §Phạm vi phân tích
443
- generated_at: "{ISO datetime}"
444
- generated_by: "qc-analyze"
445
- status: "pending_review"
446
-
447
- findings:
448
- - id: "F001"
449
- lens: "QA" # LUÔN QA file này chỉ một lăng kính
450
- severity: "critical" # critical | major | minor ← map từ 🔴/🟠/🟡⚪ của DOC_GAP
451
- section: "{section PRD/BDD chứa vấn đề}"
452
- uc_id: "{UC-ID}"
453
- quote: "{trích nguyên văn ≤120 tự từ spec tại đúng chỗ}"
454
- finding: "{gap gì}"
455
- suggestion: "{cần PO/BA làm điều gì}"
456
- resolution_edge_cases: [] # để []phân tích bậc-hai việc của /refine-prd
457
- auto_fixable: false # LUÔN false xem cảnh báo dưới
458
- status: "pending"
459
- applied_via: ""
460
- gap_ref: "GAP-UC1-001" # trỏ ngược về hàng trong DOC_GAP.md (ID mang UC)
461
-
462
- summary:
463
- total_findings: {N}
464
- by_severity: { critical: {N}, major: {N}, minor: {N} }
465
- by_lens: { QA: {N} }
466
- recommendation: "APPROVED_WITH_MINOR_CHANGES | NEEDS_REVISION | BLOCKED"
467
- ```
468
-
469
- > **`auto_fixable` LUÔN `false`, KHÔNG `--resume` cho file này.**
470
- >
471
- > Review Board có nút *"chấp nhận rồi tự sửa PRD"*. Với gap của `/refine-prd` thì đúng — nó chạy
472
- > **thời điểm PRD**, sửa PRD lúc đó sửa đúng chỗ đúng lúc.
473
- >
474
- > Gap của lệnh này phát hiện **sau khi code đã xong**. Tự sửa PRD thời điểm đó **sửa sau lưng
475
- > cả dây chuyền**: BDD sinh từ PRD cũ, code sinh từ BDD đó, sổ kết quả kiểm thử neo vào scenario
476
- > của BDD đó. Đổi PRD không đi lại đường ấy thì mọi thứ phía sau nói dối.
477
- >
478
- > Đường đúng vẫn kênh đã có: `/report-bug` cho defect spec thật, `/propose-scenario` cho thiếu
479
- > độ phủ. File `.yaml` này để **PO đọc và quyết trong một chỗ quen**, không phải để máy tự áp.
480
-
481
- **File riêng, không ghi chung với `/refine-prd`.** Cả hai giờ đều ở cấp PRD, nên khác biệt nằm
482
- **hậu tố**: `{TICKET-ID}-qa-findings.yaml` (trạm này) vs `{prd-slug}-findings.yaml`
483
- (`/refine-prd`). Đừng gộp. Ghi chung sẽ phá trường `applied_to_version` `/refine-prd` dùng để
484
- phân biệt *"PRD đổi do chính tôi áp fix"* với *"có người lạ sửa"* — và nó sẽ mãi mãi tưởng có
485
- người sửa sau lưng, mỗi lần chạy đều quét lại toàn bộ kèm cảnh báo giả.
486
-
487
- ---
488
-
489
- ## Report
490
-
188
+ cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
189
+
190
+ > **QC chạy trên BDD chưa chốt có thể phải làm lại.** `qc-scope` mặc định chỉ lấy UC có
191
+ > `@trace.status: approved`; UC còn nháp vẫn vào bảng *Phạm vi phân tích* của `DOC_GAP.md`
192
+ > với dấu `⏸ Chưa xét` — **không im lặng bỏ khỏi bảng**, vì "chưa xét" khác "đã xét, sạch".
193
+ > Cố ý QC sớm thì thêm `--include-draft`, và artifact phải ghi rõ nó dựa trên BDD nháp.
194
+
195
+ > **Vì sao trạm này chạy CẢ PRD chứ không từng UC** *(B11)*. Ba lý do, theo thứ tự quan trọng:
196
+ >
197
+ > 1. **Mâu thuẫn chéo UC chỉ lộ ra khi đọc cùng lúc.** UC1 nói một kiểu, UC3 nói kiểu khác —
198
+ > chạy tách từng UC thì về **cấu trúc** là không thể thấy, không phải "khó thấy".
199
+ > 2. **Rẻ hơn.** PRD, bản thiết kế, tài liệu kỹ thuật là nguồn **dùng chung**; chạy per-UC là
200
+ > đọc lại chúng mỗi UC một lượt. Phần dùng chung chiếm đa số đầu vào.
201
+ > 3. **Một tài liệu cho một tính năng** là cách PO và QC vốn làm việc — file gốc của đội QC
202
+ > (`DOC_GAP_FEAT-02-3.md`) không có hậu tố UC, và `qa-planner/test-plan.md` vốn viết
203
+ > *"Test Plan cho một feature"*.
204
+
205
+ ---
206
+
207
+ ## Đối chiếu tài liệu kỹ thuật *(nguồn thứ hai — bắt lệch nghiệp vụ ↔ kỹ thuật)*
208
+
209
+ Định vị tech-doc gộp cấp PRD: `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`.
210
+ Nó phủ **nhiều UC** — và trạm này cũng phủ nhiều UC, nên đọc **mọi phần chạm `uc_list`**
211
+ (đối chiếu `@trace.ucs` ở header với `uc_list`). Phần thuộc UC ngoài phạm vi (`⏸ Chưa xét`) thì bỏ qua.
212
+
213
+ > **Đây là chỗ layout cấp PRD trả lãi rõ nhất.** Tech-doc gộp là **một** tài liệu phủ cả PRD.
214
+ > Chạy per-UC thì nó bị đọc lại N lần, mỗi lần lọc bỏ gần hết — và mâu thuẫn giữa hai UC trong
215
+ > **cùng** tài liệu đó không lần nào lộ ra, vì không lần nào thấy cả hai.
216
+
217
+ **Không tìm thấy → cảnh báo mềm, KHÔNG chặn** (dự án có thể chưa dựng tech-doc):
218
+ ```
219
+ ⚠️ Không có tech-doc cho {TICKET-ID} — phân tích chỉ dựa trên PRD + BDD + design-spec.
220
+ Lệch giữa yêu cầu nghiệp vụ và hợp đồng kỹ thuật (enum, mã lỗi, ràng buộc field) sẽ KHÔNG được phát hiện ở trạm này.
221
+ ```
222
+
223
+ **Có → đối chiếu các mục sau với PRD/BDD, mỗi chỗ vênh là một gap `CONTRADICTORY`:**
224
+
225
+ | Mục tech-doc | Đối chiếu gì với PRD/BDD |
226
+ |---|---|
227
+ | §3 Mô hình dữ liệu | thực thể/field/quan hệ PRD nhắc tới có khớp không |
228
+ | **§4 Hợp đồng API** | **enum & tập giá trị hợp lệ** · ràng buộc field (độ dài, định dạng, bắt buộc) · **mã lỗi** — PRD nêu bao nhiêu nhánh lỗi, contract định nghĩa bao nhiêu |
229
+ | §4.5 Ánh xạ component UI | màn/component PRD·design-spec mô tả có mặt đủ không |
230
+ | §5 Luồng chính | thứ tự bước, nhánh rẽ có khớp scenario `.feature` không |
231
+ | §6 Điểm tích hợp | side-effect PRD nêu (gửi sự kiện, gọi dịch vụ khác) có được định nghĩa không |
232
+ | §8 Xử lý lỗi & biên | trường hợp biên PRD nêu có đường xử lý không, và ngược lại |
233
+
234
+ > **Vì sao mục này tồn tại.** Có một lớp gap **chỉ lộ ra khi so hai loại tài liệu với nhau** —
235
+ > đọc riêng bên nào cũng thấy hợp lý. Ca điển hình: PRD viết *"chọn lớp 1–6"*, contract định
236
+ > nghĩa enum `1..9`. Không ai đọc cả hai thì không ai thấy, và nó ra tận lúc chạy thật.
237
+ > **Đây là lý do trạm này đọc tech-doc — không phải để hiểu kỹ thuật, mà để bắt chỗ hai bên nói khác nhau.**
238
+
239
+ ### §12 GAP Register — ĐỌC, KHÔNG GHI
240
+
241
+ Tech-doc có sổ ẩn số thiết kế riêng (`§12`), với vòng đời và người chịu trách nhiệm riêng, và
242
+ `/generate-code` đã canh nó. **Trạm này chỉ đọc, tuyệt đối không ghi vào.**
243
+
244
+ Với mỗi mục `open` trong §12 chạm **bất kỳ UC trong `uc_list`**:
245
+ - **KHÔNG mở gap mới** trong `DOC_GAP.md` về cùng chuyện đó.
246
+ - Ghi vào `REQUIREMENT_ANALYSIS.md` mục *"Đang chờ chốt (từ §12 tech-doc)"*: `{id}` · **UC** · điều chưa biết · owner · severity.
247
+ - Test case chạm nó về sau sẽ bị chặn — nhưng bị chặn bởi **một mục đã có người xử lý**, không phải bởi một câu hỏi mới gửi PO.
248
+
249
+ > **Vì sao không ghi vào.** Một ẩn số đã nằm trong §12 nghĩa là **đã có người đang lo**: có
250
+ > owner, có mức chặn, có cổng chặn sinh code. Mở lại nó thành gap QC là gửi PO một câu hỏi
251
+ > về thứ không phải việc của PO, và tạo hai sổ cùng theo dõi một chuyện — rồi chúng lệch nhau.
252
+ > Đây đúng là **câu hỏi lọc Q1** của `steps/gap-verify.md` (*"chỗ này đã được trả lời ở tài liệu
253
+ > khác chưa?"*), chỉ mở rộng phạm vi "tài liệu khác" thêm một nguồn.
254
+
255
+ **Ngoại lệ — mục `spec-defect` là việc của PO.** §12 phân ba loại: `nội tại` (backend tự quyết) ·
256
+ `cross-service` (đội khác) · `spec-defect` (PRD/BDD sai hoặc thiếu). Hai loại đầu → ghi "đang chờ".
257
+ Loại thứ ba **đúng là gap tài liệu** → vẫn ghi vào `DOC_GAP.md` (cột `UC` = UC bị chạm), trỏ
258
+ ngược về `{id}` của §12 để không đếm hai lần.
259
+
260
+ ---
261
+
262
+ ## Role
263
+
264
+ Bạn là **QC Analyst** — stage đầu tiên của QC automation pipeline. Lấy requirement
265
+ chính thức (PRD + BDD `.feature` + design-spec) và phân rã thành một mô tả requirement
266
+ CÓ CẤU TRÚC: function, business rule, data flow, acceptance criteria. Bạn **không**
267
+ viết test case chi tiết hay Python (đó là qc-design-test / qc-run-test).
268
+
269
+ Ranh giới với `/qc-plan`: bạn trả lời *"requirement là gì?"*; qc-plan trả lời *"rủi ro ở đâu,
270
+ hỏi dev gì?"*. Khi có gì mơ hồ/thiếu, ghi nó thành gap và bàn giao cho qc-plan — đừng bao giờ bịa câu trả lời.
271
+
272
+ ## Skills (`{paths.qc_skills_dir}/qa-analyst/`)
273
+
274
+ Chỉ nạp file cho bước đang làm (mỗi file tự đủ):
275
+ - `spec-breakdown.md` — phân rã spec/PRD/user story thành cấu trúc.
276
+ - `business-rules.md` — trích business rule, điều kiện, ràng buộc (code `BR-xx`).
277
+ - `data-flow.md` — input/output, data flow, điểm tích hợp/thất bại.
278
+ - `acceptance-criteria.md` — acceptance criteria Given/When/Then (code `AC-xx`).
279
+
280
+ Thứ tự điển hình: spec-breakdown → business-rules / data-flow → acceptance-criteria.
281
+
282
+ ## Trace mapping (bắt buộc)
283
+
284
+ File `.feature` chính thức đã định nghĩa scenario là `@trace.scenario={UC-ID}-SC{N}` với
285
+ `@trace.business_rules`. Map mọi `BR-xx` / `AC-xx` bạn tạo ra tới `{UC-ID}-SC{N}` sở hữu nó
286
+ và ghi lại mapping — **làm cho từng UC trong `uc_list`**, và `BR`/`AC` phải mang rõ UC của nó
287
+ (một file phân tích giờ phủ nhiều UC, nên `BR-01` không còn tự phân biệt được là của UC nào) — qc-design-test và qc-run-test cần nó để gắn tag
288
+ `@trace.verifies` cho test và ghi `qc_status` theo từng scenario.
289
+
290
+ ## GuardBR-tag *(phép so khớp cơ học, chạy SAU khi ghi file, TRƯỚC CHECKPOINT)*
291
+
292
+ §Trace mapping trên đi **một chiều**: từ `BR` bạn tạo ra `SC` sở hữu nó. Chiều đó đúng và
293
+ cần. Nhưng chiều **ngược lại** — từ tag `@trace.business_rules` đã có trong `.feature` → `BR`
294
+ trong bản phân tích mới chiều bắt được **cái bỏ sót**, và nó chưa được kiểm ở đâu.
295
+
296
+ BDD đã tự nói ra một phần đáp án. Mỗi scenario mang tag do `/generate-bdd` ghi khi sinh từ PRD:
297
+
298
+ ```gherkin
299
+ # @trace.scenario: FT-101-UC1-SC3
300
+ # @trace.business_rules: FT-101-UC1-BR02, FT-101-UC1-BR07
301
+ ```
302
+
303
+ Nếu bản phân tích chỉ `BR01`–`BR05` thì `BR07` là **rule mà BDD biết mà QC bỏ sót** — và đó
304
+ là một **phép so khớp chuỗi**, máy làm được.
305
+
306
+ ### Bốn bước, thuần đếm so
307
+
308
+ 1. **Thu A**đọc **mọi** `.feature` của `uc_list` (đúng `active_platform`), gom toàn bộ giá trị
309
+ trong tag `@trace.business_rules`.
310
+ 2. **Thu B** gom mọi `BR-xx` trong `REQUIREMENT_ANALYSIS.md` **vừa ghi**.
311
+ 3. **So** — `A ∖ B` = rule BDD nhắc mà phân tích không có.
312
+ 4. **Xử lý:**
313
+ - `A ∖ B` rỗng → in `Guard BR-tag: khớp {n}/{n}`
314
+ - `A B` rỗng **quay lại PRD lấy nội dung thật của từng rule đó, BỔ SUNG NGAY vào
315
+ `REQUIREMENT_ANALYSIS.md`**, rồi in:
316
+ `⚠️ Guard BR-tag: bổ sung {k} rule BDD đã nhắc phân tích bỏ sót: {danh sách}`
317
+
318
+ **Guard TỰ SỬA, không chỉ tự báo.** In cảnh báo rồi để người đi lấp là thêm một dòng nữa để bỏ
319
+ qua. Bạn phải mở PRD, tìm rule đó, viết nội dung thật vào bản phân tích **không** thêm một
320
+ dòng trống mang tên `BR-xx` cho đủ số. Cảnh báo để người **biết đã chuyện gì xảy ra**,
321
+ không phải để họ đi làm việc đó.
322
+
323
+ **Chiều `B A` KHÔNG phải lỗi.** QC sinh `BR09` không tag nào nhắc thể QC phát hiện một
324
+ rule BDD chưa phủ. Đó **phát hiện tốt**: ghi thành một gap trong `DOC_GAP.md` (BDD thiếu
325
+ scenario cho rule này), **đừng xoá**.
326
+
327
+ **In dòng `Guard BR-tag:` kể cả khi sạch.** Guard im lặng khi sạch guard không ai biết tồn
328
+ tại không ai phát hiện được khi nó chết.
329
+
330
+ > ** sao cần guard không phải self-review.** Self-reviewagent **tự đọc lại bài của
331
+ > mình**, nên bỏ sót đúng chỗ đã bỏ sót lúc viết. Guard đọc **một nguồn khác** (tag trong
332
+ > `.feature`, do một lệnh khác ghi) rồi đối chiếu không phụ thuộc agent có để ý hay không, và
333
+ > chạy như nhau mỗi lần.
334
+ >
335
+ > **Vì sao bỏ sót đây đắt nhất trong cả pipeline.** Không `BR` `/qc-plan` không xếp rủi ro
336
+ > cho `/qc-design-test` không viết test case `/qc-run-test` không chạy báo cáo
337
+ > cuối nói *"coverage 100%"*. Con số đó tính trên mẫu số *"số scenario đã biết"*, không phải
338
+ > *"số rule cần phủ"* nên sai theo hướng nguy hiểm nhất: trông như đã xong.
339
+
340
+ ## Quét gap — hai nguồn, gộp rồi mới thẩm định
341
+
342
+ Gap đến từ **hai chỗ**, và chúng bổ sung nhau chứ không thay thế:
343
+
344
+ | Nguồn | Trả lời câu | Gap là |
345
+ |---|---|---|
346
+ | **4 kỹ năng phân tích** ở trên | *"yêu cầu gì?"* | sản phẩm phụ đang bóc luật nghiệp vụ thì gặp chỗ mâu thuẫn |
347
+ | **Quét theo lăng kính** *(dưới đây)* | *"còn thiếu gì?"* | mục tiêu chính |
348
+
349
+ ### Quét theo lăng kính
350
+
351
+ Chạy `steps/review-fanout.md` với:
352
+
353
+ | Tham số | Giá trị |
354
+ |---|---|
355
+ | `DIMENSIONS` | **4 lăng kính** — `D2 Xử lý lỗi` · `D3 Giao diện` · `D4 Dữ liệu & cấu hình` · `D5 Đối chiếu chéo` **(thu hẹp — xem dưới)** *(định nghĩa ở `{paths.qc_skills_dir}/qa-analyst/exhaustive-gap-scanner.md`)* |
356
+ | `FINDINGS SCHEMA` | như §Output dưới đây |
357
+ | `GRANULARITY` | **`auto`** chia theo ngưỡng kích thước, KHÔNG ép mịn theo từng UC |
358
+ | `VERIFY` | **`off`** thẩm định chạy MỘT lần bước sau, trên tập đã gộp |
359
+
360
+ **`D5` chạy dạng THU HẸP chỉ 2 trong 4 cặp tài liệu:**
361
+
362
+ | Cặp | |
363
+ |---|---|
364
+ | `PRD ↔ design-spec/` | ✅ **SO** không ai đối chiếu nội dung. `/generate-bdd` chỉ kiểm `Built from PRD` (số phiên bản); cùng phiên bản mà nội dung lệch thì lọt |
365
+ | `bdd/{platform}/ design-spec/` | **SO** không ai |
366
+ | `PRD bdd/` | bỏ `/review-context` **B1** đã làm |
367
+ | `PRD·bdd/ tech-docs/` | ❌ bỏ §Đối chiếu tài liệu kỹ thuật **ở trên** đã làm |
368
+
369
+ > **Cả hai cặp SO đều dính `design-spec/`**artifact duy nhất trong feature package mà **không
370
+ > tài liệu nào đối chiếu nội dung với nó**. Đừng lẫn với `tech-docs/`: `design-spec/`*giao diện
371
+ > Designer vẽ*, `tech-docs/` *hợp đồng hệ thống* `tech-docs/` đã được phủ §trên.
372
+ >
373
+ > Trạm này **đã đọc `design-spec/`** từ trước ( nằm trong danh sách nguồn ở Gate), nên `D5`
374
+ > không nạp thêm file nào — chỉ bắt nó **so** thay vì chỉ **đọc**. Rẻ hơn một lăng kính thường.
375
+
376
+ **`D1 Luật nghiệp vụ` là lăng kính duy nhất KHÔNG bật:** `qa-analyst/business-rules.md` đã hỏi
377
+ 4/5 câu của nó, hỏi cụ thể hơn — *"min/max · ký tự cho phép · trim · định dạng"* thay vì
378
+ *"ngưỡng đã chốt chưa"*.
379
+
380
+ > **Ghi lại vì sao `D5` từng bị tắt:** lý do ban đầu là *"trùng nhiều"* — **đúng một nửa**. Nó phủ
381
+ > **bốn** cặp, chỉ **hai** cặp đã có người làm. Sai vì suy từ ấn tượng thay vì đếm danh sách; bảng
382
+ > kiểm chứng 31 câu hỏi (`docs/plans/qc-implementation-log.md`) thứ đáng lẽ phải làm **trước**
383
+ > khi quyết. Đừng tắt lại `D5` không đọc bảng đó.
384
+
385
+ > **`GRANULARITY = auto`, không phải `per-uc`.** `/refine-prd` ép mịn theo từng UC vì ở tầng PRD
386
+ > một gap bỏ sót **im lặng đi tiếp** tới tận lúc chạy thật. đây khác: gap bỏ sót còn **bốn lớp
387
+ > chặn phía sau** trạm 3 bật ngược khi không viết nổi giá trị mong đợi, trạm 4 soát độ phủ,
388
+ > trạm 5 phân loại lỗi thật vs script sai. Ép mịn đây tốn gấp ~3 lần cho tính năng nhỏ mà đổi
389
+ > lấy một lưới an toàn đã sẵn ba lớp khác.
390
+
391
+ ### Gộp trước, thẩm định sau
392
+
393
+ Gộp gap từ **cả hai nguồn** vào một tập trước khi sang bước thẩm định.
394
+
395
+ > **Không thẩm định từng nguồn riêng.** Phép kiểm `T6` của `gap-verify` là *"hai gap cùng gốc
396
+ > thì gộp lại"* — nó chỉ chạy được khi **thấy toàn bộ** tập. Thẩm định hai lần trên hai tập rời
397
+ > thì không bắt được trùng lặp chéo nguồn, và PO nhận hai câu hỏi giống nhau.
398
+
399
+ ---
400
+
401
+ ## DOC_GAP (bắt buộc)
402
+
403
+ Luôn tạo **đúng MỘT** file gap cho cả (PRD × nền) theo
404
+ `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` các UC các hàng bên trong, phân
405
+ biệt bằng cột `UC`:
406
+ - **Bảng 11 cột**, cột 2 `UC`. ID gap `GAP-UC{N}-{nnn}` (vd `GAP-UC1-001`); gap thuộc cả
407
+ PRD `GAP-GEN-{nnn}`. Đánh số **độc lập trong từng UC** phân tích lại UC1 KHÔNG được làm
408
+ đổi số gap của UC2, test case đã đang trỏ `🚫 Block: [GAP-UC2-003]`.
409
+ - **Section `Phạm vi phân tích`** mỗi UC một hàng kèm `@trace.status`, đã phân tích chưa, số
410
+ gap. UC ngoài phạm vi ghi `⏸ Chưa xét`, **không bỏ khỏi bảng**.
411
+ - Mỗi gap phân loại MISSING / AMBIGUOUS / CONTRADICTORY / ASSUMPTION / OPEN QUESTION, với severity (🔴 Blocker → ⚪ Low) và function/BR/AC bị ảnh hưởng.
412
+ - Không bao giờ bịa câu trả lời; đánh dấu giả định là `ASSUMPTION` để PO/dev confirm.
413
+ - Bất kỳ `🔴 Blocker` nào còn `Open` ⇒ **UC ở cột `UC` của hàng đó** chưa sẵn sàng cho
414
+ qc-design-test bàn giao cho qc-plan. *Chặn theo từng UC, KHÔNG chặn cả PRD:* một blocker
415
+ UC3 không do dừng việc thiết kế test cho UC1. Ghi rõ UC nào bị chặn ở report.
416
+ - **Đẩy các defect spec thực sự lên PO (không chỉ giữ local).** Một blocker là lỗi thật
417
+ trong spec chính thức `AMBIGUOUS` / `CONTRADICTORY` / `MISSING` trong PRD/BDD phải tới
418
+ PO qua feedback flow, không chỉ nằm trong `DOC_GAP.md`: tạo `/report-bug {UC-ID} {desc}`
419
+ (`{UC-ID}` lấy từ cột `UC` của hàng gap bug đi theo UC, không theo PRD)
420
+ (BUG_FLOW của phân loại PRD vs BDD), hoặc `/propose-scenario {UC-ID}` nếu gap thiếu test
421
+ coverage. Gap `ASSUMPTION` / `OPEN QUESTION` được confirm qua questions-for-dev của qc-plan — không file thành bug.
422
+
423
+ ### Thẩm định trước khi bàn giao *(bắt buộc)*
424
+
425
+ Sinh xong `DOC_GAP.md`, **đọc `.agent/steps/gap-verify.md` và chạy toàn bộ quy trình trong đó**
426
+ với:
427
+ - `FINDINGS` = mọi gap đang `Open` trong `DOC_GAP.md`
428
+ - `EVIDENCE_ROOT` = `{paths.specs_dir}`spec repo của PO, **không** phải `{paths.qc_dir}`
429
+ - `VERDICT_FIELD` = cột `Trạng thái` + `Câu trả lời` của bảng gap
430
+ - `RERATE` = `on`
431
+
432
+ Gap rớt thẩm định được **đóng kèm do**, KHÔNG xoá — người đọc phải kiểm chứng được vì sao
433
+ bị loại. Cập nhật `Tổng số gap` + bảng ưu tiên sau khi áp verdict, và in khối
434
+ `[GAP VERIFY]` + cam kết cuối vào report.
435
+
436
+ > **Vì sao bắt buộc, không phải tuỳ chọn.** Bước phân tích ở trên chỉ có lực **tìm thêm** —
437
+ > bốn skill lần lượt quét spec và mỗi cái đều được khuyến khích ghi ra chỗ nghi ngờ. Không có
438
+ > bước nào hỏi ngược *"cái vừa ghi có thật không?"*. Hệ quả đo được ở đội QC: phần lớn gap sinh
439
+ > ra là gap ảo — spec đã trả lời ở tài liệu khác, hoặc trích dẫn sai, hoặc là chuyện QC tự quyết
440
+ > được. Mà gap ảo không chỉ tốn thời gian PO: nó **làm PO mất tin vào cả danh sách**, và lúc đó
441
+ > những gap thật cũng chết theo. `gap-verify` bộ lọc duy nhất đứng giữa hai chuyện đó.
442
+
443
+ ## Output
444
+
445
+ Ghi **hai file** dưới `{qc_artifact_dir}` (= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/`)
446
+ + **một** dưới `{paths.refinement_dir}/`.
447
+
448
+ **Mỗi loại đúng MỘT file cho cả PRD** — đừng tách một-file-mỗi-UC, cũng đừng tách
449
+ một-file-mỗi-bước (không file spec-breakdown / business-rules / data-flow / AC riêng):
450
+
451
+ 1. **`{qc_artifact_dir}REQUIREMENT_ANALYSIS.md`** bản phân tích hợp nhất duy nhất cho cả PRD.
452
+ Mở đầu bằng **bảng `Phạm vi phân tích`** (cùng nội dung với bảng trong `DOC_GAP.md`), rồi
453
+ **một mục cho mỗi UC trong phạm vi**, mỗi mục theo thứ tự: phân rã requirement → bảng
454
+ business-rule (`BR-xx`) data-flow → acceptance-criteria (`AC-xx`), mỗi `BR`/`AC` map tới
455
+ `{UC-ID}-SC{N}` (của `.feature` nền này) sở hữu nó.
456
+ Thêm một mục **"Mâu thuẫn chéo UC"** chỗ hai UC của cùng PRD nói khác nhau. Rỗng thì ghi
457
+ "Không có". *Đây thứ chỉ trạm cấp PRD nhìn thấy được; đừng bỏ mục.*
458
+ Cuối file thêm mục **"Đang chờ chốt (từ §12 tech-doc)"** — các ẩn số thiết kế `open` chạm
459
+ các UC trong phạm vi (`{id}` · UC · điều chưa biết · owner · severity). Rỗng thì ghi
460
+ "Không "; **đừng bỏ mục**.
461
+ 2. **`{qc_artifact_dir}DOC_GAP.md`** — file gap, theo
462
+ `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` + luật viết ở
463
+ `{paths.qc_skills_dir}/qa-analyst/spec-issue-reporter.md`. Bắt buộc:
464
+ - **Bảng 11 cột** đúng thứ tự, cột **`UC`** (cột 2) và cột **Giao cho đội** (Dev / PO / BA / Design / Kiến trúc / Dữ liệu).
465
+ - Section **"Phạm vi phân tích"** ngay sau metadata — mỗi UC một hàng kèm `@trace.status`,
466
+ đã phân tích chưa, số gap. UC ngoài phạm vi ghi `⏸ Chưa xét`.
467
+ - Ô câu hỏi đủ **bốn phần** tách bằng `<br/>`: **Bối cảnh → Vấn đề → Tại sao quan trọng → Gợi ý**.
468
+ *Ba phần đầu cho PO xếp ưu tiên; phần cuối cho PO trả lời nhanh mà không phải nghĩ lại từ đầu.*
469
+ - Section **"Tài liệu đầu vào đã đọc để phân tích"** đặt ngay sau metadata — liệt kê **đủ** mọi
470
+ file đã mở. Đây là căn cứ độ phủ: không có nó thì không ai phân biệt được *"đã đọc và không thấy"*
471
+ với *"chưa đọc"*.
472
+ - Mức nặng nhất dùng từ **`🔴 Blocker`** (không phải `Critical`) `/qc-run-test` đọc đúng từ này
473
+ để đặt *"scenario đang chờ PO"* vào sổ trace.
474
+ 3. **`{paths.refinement_dir}/{TICKET-ID}-qa-findings.yaml`** **cùng dữ liệu gap**, định dạng Review Board đọc được. Xem §Bản findings dưới đây.
475
+
476
+ `{paths.qc_dir}` folder top-level NHÌN THẤY trong QC repo (mặc định `docs/`, **không** phải
477
+ `.agent/review/` ẩn) để team QC mở và xử lý output dễ dàng. Spec chính thức ở lại
478
+ spec submodule của PO đừng ghi phân tích vào đó.
479
+
480
+ ### Bản findings — một nguồn, hai mặt
481
+
482
+ File `.yaml` `DOC_GAP.md` **cùng một tập gap**, không phải hai tập. Sinh `DOC_GAP.md`
483
+ trước ( bản người đọc), rồi **render** sang `.yaml` đừng phân tích lại lần hai.
484
+
485
+ Dùng **đúng schema của `/refine-prd`** để Review Board đọc được cả hai loại file:
486
+
487
+ ```yaml
488
+ prd_source: "{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md"
489
+ ticket_id: "{TICKET-ID}"
490
+ platform: "{active_platform}"
491
+ ucs: ["{UC-ID}", "…"] # các UC TRONG phạm vi (approved) — theo thứ tự
492
+ ucs_skipped: ["{UC-ID}"] # UC chưa xét, kèm lý do ở DOC_GAP §Phạm vi phân tích
493
+ generated_at: "{ISO datetime}"
494
+ generated_by: "qc-analyze"
495
+ status: "pending_review"
496
+
497
+ findings:
498
+ - id: "F001"
499
+ lens: "QA" # LUÔN là QA — file này chỉ có một lăng kính
500
+ severity: "critical" # critical | major | minor ← map từ 🔴/🟠/🟡⚪ của DOC_GAP
501
+ section: "{section PRD/BDD chứa vấn đề}"
502
+ uc_id: "{UC-ID}"
503
+ quote: "{trích nguyên văn ≤120 ký tự từ spec tại đúng chỗ}"
504
+ finding: "{gap là gì}"
505
+ suggestion: "{cần PO/BA làm rõ điều gì}"
506
+ resolution_edge_cases: [] # để [] — phân tích bậc-hai là việc của /refine-prd
507
+ auto_fixable: false # LUÔN false — xem cảnh báo dưới
508
+ status: "pending"
509
+ applied_via: ""
510
+ gap_ref: "GAP-UC1-001" # trỏ ngược về hàng trong DOC_GAP.md (ID mang UC)
511
+
512
+ summary:
513
+ total_findings: {N}
514
+ by_severity: { critical: {N}, major: {N}, minor: {N} }
515
+ by_lens: { QA: {N} }
516
+ recommendation: "APPROVED_WITH_MINOR_CHANGES | NEEDS_REVISION | BLOCKED"
517
+ ```
518
+
519
+ > **`auto_fixable` LUÔN `false`, và KHÔNG có `--resume` cho file này.**
520
+ >
521
+ > Review Board có nút *"chấp nhận rồi tự sửa PRD"*. Với gap của `/refine-prd` thì đúng — nó chạy
522
+ > ở **thời điểm PRD**, sửa PRD lúc đó là sửa đúng chỗ đúng lúc.
523
+ >
524
+ > Gap của lệnh này phát hiện **sau khi code đã xong**. Tự sửa PRD ở thời điểm đó là **sửa sau lưng
525
+ > cả dây chuyền**: BDD sinh từ PRD cũ, code sinh từ BDD đó, sổ kết quả kiểm thử neo vào scenario
526
+ > của BDD đó. Đổi PRD mà không đi lại đường ấy thì mọi thứ phía sau nói dối.
527
+ >
528
+ > Đường đúng vẫn là kênh đã có: `/report-bug` cho defect spec thật, `/propose-scenario` cho thiếu
529
+ > độ phủ. File `.yaml` này để **PO đọc và quyết trong một chỗ quen**, không phải để máy tự áp.
530
+
531
+ **File riêng, không ghi chung với `/refine-prd`.** Cả hai giờ đều ở cấp PRD, nên khác biệt nằm
532
+ ở **hậu tố**: `{TICKET-ID}-qa-findings.yaml` (trạm này) vs `{prd-slug}-findings.yaml`
533
+ (`/refine-prd`). Đừng gộp. Ghi chung sẽ phá trường `applied_to_version` mà `/refine-prd` dùng để
534
+ phân biệt *"PRD đổi do chính tôi áp fix"* với *"có người lạ sửa"* — và nó sẽ mãi mãi tưởng có
535
+ người sửa sau lưng, mỗi lần chạy đều quét lại toàn bộ kèm cảnh báo giả.
536
+
537
+ ---
538
+
539
+ ## Self-Review *(trước khi in Report)*
540
+
541
+ Theo 3 nhóm ở `{paths.qc_skills_dir}/_shared/self-review-principles.md` — **không chép lại ở đây**.
542
+
543
+ - **Bịa:** mỗi `BR-xx`/`AC-xx` trích được về đúng dòng nào của PRD/BDD — không phải rule tôi tự
544
+ thêm? Mỗi gap `CONTRADICTORY` nêu được **cả hai** chỗ nói khác nhau, không phải một bên?
545
+ - **Nhảy bước:** đã chạy đủ 4 kỹ năng phân tích + 4 lăng kính + §Đối chiếu tài liệu kỹ thuật —
546
+ không bỏ lăng kính nào vì "UC này đơn giản"?
547
+ - **Số liệu:** `{N}` gap · `{blockers}` blocker · `{M}` BR/AC in ở report là **đếm thật trên
548
+ bảng vừa ghi** (`grep -cE "^\| GAP-"`), không phải áng chừng?
549
+
550
+ > **`gap-verify` và self-review bổ sung nhau, KHÔNG thay nhau.** `gap-verify` kiểm **từng
551
+ > finding** có sống sót qua T1–T6 (sâu, per-finding). Self-review kiểm **cả lượt chạy** có bịa /
552
+ > nhảy bước / đếm sai (rộng, per-run). Chạy một cái rồi bỏ cái kia là bỏ một nửa lưới.
553
+ >
554
+ > Và cả hai **không thay** Guard BR-tag ở trên — guard đó là phép so khớp cơ học, xem §Ranh giới
555
+ > trong file skill.
556
+
557
+ ## Report
558
+
491
559
  **Đọc `.agent/steps/report-footer.md`** và áp đúng khuôn footer trong đó (Status Badge ·
492
- Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
493
-
494
- ```
495
- /qc-analyze Hoàn tất — {TICKET-ID} ({active_platform})
496
- Phạm vi: {n}/{N} UC phân tích{nếu có UC chưa xét: " · ⏸ {m} chưa xét: {danh sách UC-ID} (BDD chưa approved)"}
497
- Files : {paths.qc_dir}/{TICKET-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md + DOC_GAP.md (11 cột)
498
- {paths.refinement_dir}/{TICKET-ID}-qa-findings.yaml ← mở bằng Review Board (chuột phải)
499
- Nguồn : PRD · BDD({active_platform}, {n} UC) · design-spec · tech-doc{nếu thiếu tech-doc: " (THIẾU — không đối chiếu được nghiệp vụ ↔ kỹ thuật)"}
500
- Quét : 4 kỹ năng phân tích + 4 lăng kính (xử lý lỗi · giao diện · dữ liệu & cấu hình
501
- · đối chiếu chéo: PRD↔design-spec, bdd↔design-spec)
502
- Verify: {raw} gap thô → {N} còn Open (❌ {invalid} bịa/đã-trả-lời · ⚠️ {reclass} không phải gap nghiệp vụ · 🔁 {merge} trùng)
503
- Gaps : {N} ({blockers} blocker) — theo UC: {UC1: n · UC2: n · …}{nếu có: " · toàn PRD: {n}"}
504
- ← blocker là spec-defect? → /report-bug {UC-ID của hàng đó} | coverage gap → /propose-scenario {UC-ID}
505
- Chéo UC: {X} mâu thuẫn giữa các UC của cùng PRD (đã ghi vào REQUIREMENT_ANALYSIS §Mâu thuẫn chéo UC)
506
- Chặn : {danh sách UC có 🔴 Blocker còn Open} — các UC còn lại vẫn thiết kế test được bình thường
507
- Chờ chốt: {G} ẩn số §12 tech-doc đang open chạm các UC này (đã ghi vào REQUIREMENT_ANALYSIS, KHÔNG hỏi lại PO)
508
- SC map: {M} BR/AC map tới {K} scenario
509
- Next : /qc-plan {TICKET-ID} {active_platform} rủi ro / what-if / câu hỏi cho dev
510
- (giải quyết các gap 🔴 Blocker với PO/Dev trước)
511
- ```
512
-
513
- > **Dòng `Next` là bắt buộc in, không phải trang trí.** Dây gốc của đội QC ra **cả** file gap
514
- > **và** kế hoạch test trong một lần chạy (13/14 lần đo được ở repo của họ). Ở framework đó là
515
- > **hai lệnh**. Người quen dây sẽ dừng lại đây tưởng đã xong dòng này là chỗ duy nhất
516
- > nói cho họ biết còn một bước nữa.
517
-
560
+ Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
561
+
562
+ ```
563
+ /qc-analyze Hoàn tất — {TICKET-ID} ({active_platform})
564
+ Phạm vi: {n}/{N} UC phân tích{nếu có UC chưa xét: " · ⏸ {m} chưa xét: {danh sách UC-ID} (BDD chưa approved)"}
565
+ Files : {paths.qc_dir}/{TICKET-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md + DOC_GAP.md (11 cột)
566
+ {paths.refinement_dir}/{TICKET-ID}-qa-findings.yaml ← mở bằng Review Board (chuột phải)
567
+ Nguồn : PRD · BDD({active_platform}, {n} UC) · design-spec · tech-doc{nếu thiếu tech-doc: " (THIẾU — không đối chiếu được nghiệp vụ ↔ kỹ thuật)"}
568
+ Quét : 4 kỹ năng phân tích + 4 lăng kính (xử lý lỗi · giao diện · dữ liệu & cấu hình
569
+ · đối chiếu chéo: PRD↔design-spec, bdd↔design-spec)
570
+ Verify: {raw} gap thô → {N} còn Open (❌ {invalid} bịa/đã-trả-lời · ⚠️ {reclass} không phải gap nghiệp vụ · 🔁 {merge} trùng)
571
+ Gaps : {N} ({blockers} blocker) — theo UC: {UC1: n · UC2: n · …}{nếu có: " · toàn PRD: {n}"}
572
+ ← blocker là spec-defect? → /report-bug {UC-ID của hàng đó} | coverage gap → /propose-scenario {UC-ID}
573
+ Chéo UC: {X} mâu thuẫn giữa các UC của cùng PRD (đã ghi vào REQUIREMENT_ANALYSIS §Mâu thuẫn chéo UC)
574
+ Chặn : {danh sách UC có 🔴 Blocker còn Open} — các UC còn lại vẫn thiết kế test được bình thường
575
+ Chờ chốt: {G} ẩn số §12 tech-doc đang open chạm các UC này (đã ghi vào REQUIREMENT_ANALYSIS, KHÔNG hỏi lại PO)
576
+ SC map: {M} BR/AC map tới {K} scenario
577
+ Guard BR-tag: {khớp {n}/{n} | ⚠️ bổ sung {k} rule BDD đã nhắc phân tích bỏ sót: {danh sách}}
578
+ Self-review: {✅ sạch | ⚠️ {n} điểm cần chú ý — liệt kê}
579
+ Next : /qc-plan {TICKET-ID} {active_platform} ← rủi ro / what-if / câu hỏi cho dev
580
+ (giải quyết các gap 🔴 Blocker với PO/Dev trước)
581
+ ```
582
+
583
+ > **Dòng `Next` bắt buộc in, không phải trang trí.** Dây gốc của đội QC ra **cả** file gap
584
+ > **và** kế hoạch test trong một lần chạy (13/14 lần đo được ở repo của họ). Ở framework đó là
585
+ > **hai lệnh**. Người quen dây cũ sẽ dừng lại ở đây và tưởng đã xong — dòng này là chỗ duy nhất
586
+ > nói cho họ biết còn một bước nữa.
587
+