@educa-corp/sdd-framework 0.9.5 → 0.9.6

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 (55) hide show
  1. package/bin/build.js +11 -1
  2. package/bin/lint-trace.js +397 -28
  3. package/bin/self-check.js +183 -12
  4. package/bin/trace-schema.json +2656 -1981
  5. package/core/FRAMEWORK_VERSION +1 -1
  6. package/core/commands/dev-gen-test.md +62 -0
  7. package/core/commands/generate-code.md +1 -1
  8. package/core/commands/generate-tech-docs.md +3 -3
  9. package/core/commands/map-testids.md +88 -11
  10. package/core/commands/qc-analyze.md +509 -425
  11. package/core/commands/qc-design-test.md +475 -247
  12. package/core/commands/qc-plan.md +134 -93
  13. package/core/commands/qc-review.md +216 -131
  14. package/core/commands/qc-run-test.md +346 -231
  15. package/core/commands/validate-traces.md +17 -2
  16. package/core/rules/workflow.md +40 -0
  17. package/core/skills/qc/qa-analyst/DOC_GAP.template.md +9 -1
  18. package/core/skills/qc/qa-designer/shared/duplicate-check-procedure.md +33 -5
  19. package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +24 -0
  20. package/core/skills/qc/qa-planner/test-plan.md +7 -0
  21. package/core/skills/qc/qa-runner/functional/gui-feature.md +1 -1
  22. package/core/skills/qc/qa-runner/functional/gui-screen.md +1 -1
  23. package/core/steps/qc-scope.md +67 -11
  24. package/core/steps/qc-stamp.md +142 -0
  25. package/core/steps/report-footer.md +13 -5
  26. package/core/templates/tech-design.template.md +3 -3
  27. package/docs/01-getting-started/quickstart.md +4 -3
  28. package/docs/02-concepts/architecture.md +14 -0
  29. package/docs/02-concepts/glossary.md +8 -0
  30. package/docs/02-concepts/overview.md +3 -2
  31. package/docs/02-concepts/pipeline-steps/04-bdd.md +1 -1
  32. package/docs/02-concepts/pipeline-steps/05-tech-docs.md +21 -5
  33. package/docs/02-concepts/pipeline-steps/06-code.md +12 -2
  34. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +60 -12
  35. package/docs/02-concepts/pipeline-steps/README.md +4 -3
  36. package/docs/02-concepts/traceability.md +2 -2
  37. package/docs/03-guides/architect.md +2 -2
  38. package/docs/03-guides/developer.md +5 -2
  39. package/docs/03-guides/tester-qa.md +17 -5
  40. package/docs/04-reference/commands.md +6 -3
  41. package/docs/04-reference/trace-schema.md +1 -1
  42. package/docs/explain/07-generate-tech-docs.md +5 -3
  43. package/docs/explain/08-review-tech-docs.md +15 -3
  44. package/docs/explain/09-generate-code.md +30 -4
  45. package/docs/explain/10-review-code.md +1 -1
  46. package/docs/explain/11-map-testids.md +72 -70
  47. package/docs/explain/12-dev-gen-test.md +1 -1
  48. package/docs/explain/15-qc-analyze.md +14 -2
  49. package/docs/explain/16-qc-plan.md +5 -1
  50. package/docs/explain/17-qc-design-test.md +26 -3
  51. package/docs/explain/18-qc-review.md +6 -2
  52. package/docs/explain/19-qc-run-test.md +29 -6
  53. package/docs/explain/20-qc-report.md +5 -2
  54. package/docs/explain/README.md +4 -1
  55. package/package.json +1 -1
@@ -1,17 +1,19 @@
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 CỨNG** — ghi đè DOC_GAP.md đã có → mất cột Trạng thái/Câu trả lời (PO điền TAY, KHÔNG sinh lại được), ĐÁNH SỐ LẠI GAP-ID, phá 🚫 Block trong mọi .Test.md đã sinh. `--yes` KHÔNG bỏ qua được (gate Bước 3a).*
14
+
15
+ *Mức cứng chỉ áp khi `DOC_GAP.md` **đã tồn tại** — lần chạy đầu không ghi đè gì, đi thẳng. Xem §Chạy lại.*
16
+
15
17
  # Gate — Quy trình vào chuẩn cho mọi lệnh
16
18
 
17
19
  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,425 +165,507 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
163
165
  - "N" → dừng, hỏi người dùng muốn thay đổi gì.
164
166
  - 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
167
  🔴/⚠️ (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
168
+
169
+
170
+ *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.*
171
+
172
+ ## Context
171
173
  **BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
172
174
  rồi mới tiếp tục phần bên dưới.
173
175
 
174
176
  Bỏ qua bước này thì `{paths.*}`, `{tech_stack.*}`, `{conventions.*}`, guardrail từ
175
177
  `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
-
178
+ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
179
+
180
+ ---
181
+
182
+ ## Phạm vi QC — PRD nào, nền nào, những UC nào
183
+
182
184
  **BẮT BUỘC — đọc `.agent/steps/qc-scope.md` và thực thi TOÀN BỘ quy trình trong đó**,
183
185
  rồi mới tiếp tục phần bên dưới.
184
186
 
185
187
  Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
186
- `qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--include-draft`).
188
+ `qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--force`).
187
189
  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** 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 nguồn **dùng chung**; chạy per-UC
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ônghậu tố UC, `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
- phủ **nhiều UC** 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 chỗ layout cấp PRD trả lãi nhất.** Tech-doc gộp **một** tài liệu phủ cả PRD.
214
- > Chạy per-UC thì bị đọc lại N lần, mỗi lần lọc bỏ gần hết — 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 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 một gap `CONTRADICTORY`:**
224
-
225
- | Mục tech-doc | Đối chiếu với PRD/BDD |
226
- |---|---|
227
- | §3 hình dữ liệu | thực thể/field/quan hệ PRD nhắc tới 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 tả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 định nghĩa không |
232
- | §8 Xử lỗi & biên | trường hợp biên PRD nêu đường xử không, 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 sổ ẩn số thiết kế riêng (`§12`), với vòng đời 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 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 **đã người đang lo**:
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, 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) phân 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 (đó qc-design-test / qc-run-test).
268
-
269
- Ranh giới với `/qc-plan`: bạn trả lời *"requirement gì?"*; qc-plan trả lời *"rủi ro đâu,
270
- hỏi dev gì?"*. Khi 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
- ghi lại mapping — **làm cho từng UC trong `uc_list`**, `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 của UC nào) — qc-design-test và qc-run-test cần nó để gắn tag
288
- `@trace.verifies` cho test 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 là 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ỉ có `BR01`–`BR05` thì `BR07` là **rule mà BDD biết mà QC bỏ sót** — và đó
304
- một **phép so khớp chuỗi**, máy làm được.
305
-
306
- ### Bốn bước, thuần đếmso
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 mà 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 đã có chuyện gì xảy ra**,
321
- không phải để họ đi làm việc đó.
322
-
323
- **Chiều `BA` KHÔNG phải lỗi.** QC sinh `BR09` không tag nào nhắc → có thể QC phát hiện một
324
- rule BDD chưa phủ. Đó là **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 là guard không ai biết nó tồn
328
- tại không ai phát hiện được khi 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để ý hay không, và
333
- > chạy như nhau mỗi lần.
334
- >
335
- > ** 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 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ỗ**, chúng bổ sung nhau chứ không thay thế:
343
-
344
- | Nguồn | Trả lời câu | Gap |
345
- |---|---|---|
346
- | **4 kỹ năng phân tích** ở trên | *"yêu cầu là 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 **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/` là *hợp đồng hệ thống* — và `tech-docs/` đã được phủ ở §trên.
372
- >
373
- > Trạm này **đã đọc `design-spec/`** từ trước (nó 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ă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 đã người làm. Sai suy từ ấn tượng thay đế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 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` *"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, 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 là 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 là `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 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 gapbug đ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` 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 lý do**, KHÔNG xoá — người đọc phải kiểm chứng được vì sao
433
- nó 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 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 thật không?"*. Hệ quả đo được ở đội QC: phần lớn gap sinh
439
- > ra gap ảo — spec đã trả lời ở tài liệu khác, hoặc trích dẫn sai, hoặc chuyện QC tự quyết
440
- > được. gap ảo không chỉ tốn thời gian PO: **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, và 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 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 có"; **đừ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}` là 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ử 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` và `DOC_GAP.md` là **cùng một tập gap**, không phải hai tập. Sinh `DOC_GAP.md`
483
- trước (nó 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 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 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 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 đó **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 kênh đã có: `/report-bug` cho defect spec thật, `/propose-scenario` cho thiếu
529
- > độ phủ. File `.yaml` này để **PO đọc 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` `/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"* sẽ mãi mãi tưởng
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 "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` 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 bỏ một nửa lưới.
553
- >
554
- > cả hai **không thay** Guard BR-tag ở trên guard đó phép so khớp cơ học, xem §Ranh giới
555
- > trong file skill.
556
-
557
- ## Report
558
-
190
+ cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
191
+
192
+ ---
193
+
194
+ ## Stamp phiên bản nguồn
195
+
196
+ **BẮT BUỘC — đọc `.agent/steps/qc-stamp.md` và thực thi phần áp cho lệnh này**,
197
+ rồi mới tiếp tục phần bên dưới.
198
+
199
+ **hai vế**: §1 **ghi** khối `Nguồn & phiên bản` vào artifact lệnh này sinh ra ·
200
+ §2 **so** stamp của artifact lệnh này ĐỌC với version hiện tại của spec.
201
+ Bỏ vế ghi thì trạm sau không để so; bỏ vế so thì stamp thành một con số không ai
202
+ đọc một bộ TC lỗi thời sẽ chạy xanh rồi ghi `pass` **hợp lệ theo mọi phép kiểm**.
203
+
204
+ > **QC chạy trên BDD chưa chốt thể phải làm lại.** `qc-scope` mặc định chỉ lấy UC có
205
+ > `@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`
206
+ > 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".
207
+ > Cố ý QC sớm thì thêm `--force`, và artifact phải ghi rõ nó dựa trên BDD nháp.
208
+
209
+ > **Vì sao trạm này chạy CẢ PRD chứ không từng UC** *(B11)*. Ba do, theo thứ tự quan trọng:
210
+ >
211
+ > 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 —
212
+ > chạy tách từng UC thì về **cấu trúc** không thể thấy, không phải "khó thấy".
213
+ > 2. **Rẻ hơn.** PRD, bản thiết kế, tài liệu kỹ thuật nguồn **dùng chung**; chạy per-UC là
214
+ > đọc lại chúng mỗi UC một lượt. Phần dùng chung chiếm đa số đầu vào.
215
+ > 3. **Một tài liệu cho một tính năng** cách PO QC vốn làm việc file gốc của đội QC
216
+ > (`DOC_GAP_FEAT-02-3.md`) không hậu tố UC, và `qa-planner/test-plan.md` vốn viết
217
+ > *"Test Plan cho một feature"*.
218
+
219
+ ---
220
+
221
+ ## Đối chiếu tài liệu kỹ thuật *(nguồn thứ hai bắt lệch nghiệp vụ ↔ kỹ thuật)*
222
+
223
+ Định vị tech-doc gộp cấp PRD: `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`.
224
+ 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`**
225
+ (đố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.
226
+
227
+ > **Đây là chỗ layout cấp PRD trả lãi rõ nhất.** Tech-doc gộp **một** tài liệu phủ cả PRD.
228
+ > 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
229
+ > **cùng** tài liệu đó không lần nào lộ ra, không lần nào thấy cả hai.
230
+
231
+ **Không tìm thấy cảnh báo mềm, KHÔNG chặn** (dự ánthể chưa dựng tech-doc):
232
+ ```
233
+ ⚠️ Không tech-doc cho {TICKET-ID} phân tích chỉ dựa trên PRD + BDD + design-spec.
234
+ Lệch giữa yêu cầu nghiệp vụ hợp đồng kỹ thuật (enum, lỗi, ràng buộc field) sẽ KHÔNG được phát hiện ở trạm này.
235
+ ```
236
+
237
+ **Có đối chiếu các mục sau với PRD/BDD, mỗi chỗ vênh một gap `CONTRADICTORY`:**
238
+
239
+ | Mục tech-doc | Đối chiếu với PRD/BDD |
240
+ |---|---|
241
+ | §3 hình dữ liệu | thực thể/field/quan hệ PRD nhắc tới có khớp không |
242
+ | **§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 |
243
+ | §4.5 Ánh xạ component UI | màn/component PRD·design-spec tả mặt đủ không |
244
+ | §5 Luồng chính | thứ tự bước, nhánh rẽ có khớp scenario `.feature` không |
245
+ | §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 |
246
+ | §8 Xử lỗi & biên | trường hợp biên PRD nêu có đường xử lý không, và ngược lại |
247
+
248
+ > **Vì sao mục này tồn tại.** một lớp gap **chỉ lộ ra khi so hai loại tài liệu với nhau** —
249
+ > đọ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
250
+ > 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.
251
+ > **Đây do trạm này đọc tech-doc không phải để hiểu kỹ thuật, để bắt chỗ hai bên nói khác nhau.**
252
+
253
+ ### §12 GAP Register ĐỌC, KHÔNG GHI
254
+
255
+ Tech-doc sổ ẩn số thiết kế riêng (`§12`), với vòng đời người chịu trách nhiệm riêng,
256
+ `/generate-code` đã canh nó. **Trạm này chỉ đọc, tuyệt đối không ghi vào.**
257
+
258
+ Với mỗi mục `open` trong §12 chạm **bất kỳ UC trong `uc_list`**:
259
+ - **KHÔNG mở gap mới** trong `DOC_GAP.md` về cùng chuyện đó.
260
+ - 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.
261
+ - 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.
262
+
263
+ > **Vì sao không ghi vào.** Một ẩn số đã nằm trong §12 nghĩa là **đã có người đang lo**: có
264
+ > 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
265
+ > 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.
266
+ > Đâ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
267
+ > khác chưa?"*), chỉ mở rộng phạm vi "tài liệu khác" thêm một nguồn.
268
+
269
+ **Ngoại lệ mục `spec-defect` việc của PO.** §12 phân ba loại: `nội tại` (backend tự quyết) ·
270
+ `cross-service` (đội khác) · `spec-defect` (PRD/BDD sai hoặc thiếu). Hai loại đầu → ghi "đang chờ".
271
+ Loại thứ ba **đúng gap tài liệu** vẫn ghi vào `DOC_GAP.md` (cột `UC` = UC bị chạm), trỏ
272
+ ngược về `{id}` của §12 để không đếm hai lần.
273
+
274
+ ---
275
+
276
+ ## Role
277
+
278
+ Bạn **QC Analyst** stage đầu tiên của QC automation pipeline. Lấy requirement
279
+ chính thức (PRD + BDD `.feature` + design-spec) phân thành một tả requirement
280
+ CẤU TRÚC: function, business rule, data flow, acceptance criteria. Bạn **không**
281
+ viết test case chi tiết hay Python (đó là qc-design-test / qc-run-test).
282
+
283
+ Ranh giới với `/qc-plan`: bạn trả lời *"requirement là gì?"*; qc-plan trả lời *"rủi ro ở đâu,
284
+ 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.
285
+
286
+ ## Skills (`{paths.qc_skills_dir}/qa-analyst/`)
287
+
288
+ Chỉ nạp file cho bước đang làm (mỗi file tự đủ):
289
+ - `spec-breakdown.md` phân spec/PRD/user story thành cấu trúc.
290
+ - `business-rules.md` trích business rule, điều kiện, ràng buộc (code `BR-xx`).
291
+ - `data-flow.md` — input/output, data flow, điểm tích hợp/thất bại.
292
+ - `acceptance-criteria.md`acceptance criteria Given/When/Then (code `AC-xx`).
293
+
294
+ Thứ tự điển hình: spec-breakdown business-rules / data-flowacceptance-criteria.
295
+
296
+ ## Trace mapping (bắt buộc)
297
+
298
+ File `.feature` chính thức đã định nghĩa scenario là `@trace.scenario={UC-ID}-SC{N}` với
299
+ `@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ó
300
+ 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ó
301
+ (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
302
+ `@trace.verifies` cho test và ghi `qc_status` theo từng scenario.
303
+
304
+ ## Guard — BR-tag *(phép so khớp cơ học, chạy SAU khi ghi file, TRƯỚC CHECKPOINT)*
305
+
306
+ §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à
307
+ cần. Nhưng chiều **ngược lại** — từ tag `@trace.business_rules` đã có trong `.feature` → `BR`
308
+ trong bản phân tích mới là chiều bắt được **cái bỏ sót**, nó chưa được kiểm ở đâu.
309
+
310
+ BDD đã tự nói ra một phần đáp án. Mỗi scenario mang tag do `/generate-bdd` ghi khi sinh từ PRD:
311
+
312
+ ```gherkin
313
+ # @trace.scenario: FT-101-UC1-SC3
314
+ # @trace.business_rules: FT-101-UC1-BR02, FT-101-UC1-BR07
315
+ ```
316
+
317
+ Nếu bản phân tích chỉ có `BR01`–`BR05` thì `BR07` là **rule mà BDD biết mà QC bỏ sót** — và đó
318
+ một **phép so khớp chuỗi**, máy làm được.
319
+
320
+ ### Bốn bước, thuần đếm so
321
+
322
+ 1. **Thu A** đọc **mọi** `.feature` của `uc_list` (đúng `active_platform`), gom toàn bộ giá trị
323
+ trong tag `@trace.business_rules`.
324
+ 2. **Thu B** — gom mọi `BR-xx` trong `REQUIREMENT_ANALYSIS.md` **vừa ghi**.
325
+ 3. **So** `AB` = rule BDD nhắc phân tích không có.
326
+ 4. **Xử lý:**
327
+ - `A B` rỗng → in `Guard BR-tag: khớp {n}/{n}`
328
+ - `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
329
+ `REQUIREMENT_ANALYSIS.md`**, rồi in:
330
+ `⚠️ Guard BR-tag: bổ sung {k} rule BDD đã nhắc mà phân tích bỏ sót: {danh sách}`
331
+
332
+ **Guard TỰ SỬA, không chỉ tự báo.** In cảnh báo rồi để người đi lấp thêm một dòng nữa để bỏ
333
+ 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
334
+ dòng trống mang tên `BR-xx` cho đủ số. Cảnh báo để người **biết đãchuyện xảy ra**,
335
+ không phải để họ đi làm việc đó.
336
+
337
+ **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
338
+ rule BDD chưa phủ. Đó **phát hiện tốt**: ghi thành một gap trong `DOC_GAP.md` (BDD thiếu
339
+ scenario cho rule này), **đừng xoá**.
340
+
341
+ **In dòng `Guard BR-tag:` kể cả khi sạch.** Guard im lặng khi sạch là guard không ai biết nó tồn
342
+ tại không ai phát hiện được khi chết.
343
+
344
+ > **Vì sao cần guard không phải self-review.** Self-review agent **tự đọc lại bài của
345
+ > mình**, nên nó bỏ sót đúng chỗ nó đã bỏ sót lúc viết. Guard đọc **một nguồn khác** (tag trong
346
+ > `.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à
347
+ > chạy như nhau mỗi lần.
348
+ >
349
+ > ** sao bỏ sót đây đắt nhất trong cả pipeline.** Không `BR` → `/qc-plan` không xếp rủi ro
350
+ > cho nó → `/qc-design-test` không viết test case → `/qc-run-test` không có gì chạy → báo cáo
351
+ > cuối nói *"coverage 100%"*. Con số đó tính trên mẫu số *"số scenario đã biết"*, không phải
352
+ > *"số rule cần phủ"* — nên nó sai theo hướng nguy hiểm nhất: trông như đã xong.
353
+
354
+ ## Quét gap — hai nguồn, gộp rồi mới thẩm định
355
+
356
+ Gap đến từ **hai chỗ**, và chúng bổ sung nhau chứ không thay thế:
357
+
358
+ | Nguồn | Trả lời câu | Gap là |
359
+ |---|---|---|
360
+ | **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 |
361
+ | **Quét theo lăng kính** *(dưới đây)* | *"còn thiếu gì?"* | mục tiêu chính |
362
+
363
+ ### Quét theo lăng kính
364
+
365
+ Chạy `steps/review-fanout.md` với:
366
+
367
+ | Tham số | Giá trị |
368
+ |---|---|
369
+ | `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`)* |
370
+ | `FINDINGS SCHEMA` | như §Output dưới đây |
371
+ | `GRANULARITY` | **`auto`** chia theo ngưỡng kích thước, KHÔNG ép mịn theo từng UC |
372
+ | `VERIFY` | **`off`** thẩm định chạy MỘT lần bước sau, trên tập đã gộp |
373
+
374
+ **`D5` chạy ở dạng THU HẸP — chỉ 2 trong 4 cặp tài liệu:**
375
+
376
+ | Cặp | |
377
+ |---|---|
378
+ | `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 |
379
+ | `bdd/{platform}/ design-spec/` | **SO**không ai |
380
+ | `PRD ↔ bdd/` | ❌ bỏ — `/review-context` **B1** đã làm |
381
+ | `PRD·bdd/ ↔ tech-docs/` | ❌ bỏ — §Đối chiếu tài liệu kỹ thuật **ở trên** đã làm |
382
+
383
+ > **Cả hai cặp SO đều dính `design-spec/`** artifact duy nhất trong feature package **không
384
+ > 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
385
+ > Designer vẽ*, `tech-docs/` *hợp đồng hệ thống* — và `tech-docs/` đã được phủ §trên.
386
+ >
387
+ > Trạm này **đã đọc `design-spec/`** từ trước (nó nằm trong danh sách nguồn Gate), nên `D5`
388
+ > không nạp thêm file nào chỉ bắt **so** thay chỉ **đọc**. Rẻ hơn một lăng kính thường.
389
+
390
+ **`D1 Luật nghiệp vụ` lăng kính duy nhất KHÔNG bật:** `qa-analyst/business-rules.md` đã hỏi
391
+ 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ì
392
+ *"ngưỡng đã chốt chưa"*.
393
+
394
+ > **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ủ
395
+ > **bốn** cặp, chỉ **hai** cặp đã người làm. Sai suy từ ấn tượng thay vì đếm danh sách; bảng
396
+ > kiểm chứng 31 câu hỏi (`docs/plans/qc-implementation-log.md`) là thứ đáng lẽ phải làm **trước**
397
+ > khi quyết. Đừng tắt lại `D5` không đọc bảng đó.
398
+
399
+ > **`GRANULARITY = auto`, không phải `per-uc`.** `/refine-prd` ép mịn theo từng UC tầng PRD
400
+ > 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
401
+ > 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ủ,
402
+ > 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
403
+ > lấy một lưới an toàn đã có sẵn ba lớp khác.
404
+
405
+ ### Gộp trước, thẩm định sau
406
+
407
+ Gộp gap từ **cả hai nguồn** vào một tập trước khi sang bước thẩm định.
408
+
409
+ > **Không thẩm định từng nguồn riêng.** Phép kiểm `T6` của `gap-verify` *"hai gap cùng gốc
410
+ > 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
411
+ > 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.
412
+
413
+ ---
414
+
415
+ ## DOC_GAP (bắt buộc)
416
+
417
+ Luôn tạo **đúng MỘT** file gap cho cả (PRD × nền) theo
418
+ `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` các UC các hàng bên trong, phân
419
+ biệt bằng cột `UC`:
420
+ - **Bảng 11 cột**, cột 2 `UC`. ID gap `GAP-UC{N}-{nnn}` (vd `GAP-UC1-001`); gap thuộc cả
421
+ 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
422
+ đổi số gap của UC2, test case đã đang trỏ `🚫 Block: [GAP-UC2-003]`.
423
+ - **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ố
424
+ gap. UC ngoài phạm vi ghi `⏸ Chưa xét`, **không bỏ khỏi bảng**.
425
+ - 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.
426
+ - Không bao giờ bịa câu trả lời; đánh dấu giả định là `ASSUMPTION` để PO/dev confirm.
427
+ - Bất kỳ `🔴 Blocker` nào còn `Open` **UC cột `UC` của hàng đó** chưa sẵn sàng **để nghiệm
428
+ thu** — bàn giao cho qc-plan. *Chặn theo từng UC, KHÔNG chặn cả PRD:* một blocker ở UC3 không
429
+ do dừng việc thiết kế test cho UC1. Ghi rõ UC nào bị chặn ở report.
430
+
431
+ > **"Chưa sẵn sàng" "chưa được thiết kế"** *(G66)*. `/qc-design-test` **vẫn chạy được và nên
432
+ > chạy** trên UC bị chặn — TC chạm gap mang dấu `🚫 Block: [GAP-UC{N}-{nnn}]` trỏ về hàng gap,
433
+ > vẫn được `Guard SC coverage` đếm là đã phủ, và **chưa chạy** tới khi gap `Answered`.
434
+ > Cái bị chặn **chạy test**, không phải **viết test**.
435
+ >
436
+ > *Mã gap mang UC (`GAP-UC1-003`) luật "đánh số độc lập trong từng UC" ở trên **chỉ có nghĩa
437
+ > vì** test case đang trỏ vào các mã đó. Hiểu "chưa sẵn sàng" thành "đừng thiết kế" là giết cả
438
+ > chế đó.*
439
+ - **Đẩy các defect spec thực sự lên PO (không chỉ giữ local).** Một blocker lỗi thật
440
+ trong spec chính thức `AMBIGUOUS` / `CONTRADICTORY` / `MISSING` trong PRD/BDD phải tới
441
+ PO qua feedback flow, không chỉ nằm trong `DOC_GAP.md`: tạo `/report-bug {UC-ID} {desc}`
442
+ (`{UC-ID}` lấy từ cột `UC` của hàng gap bug đi theo UC, không theo PRD)
443
+ (BUG_FLOW của phân loại PRD vs BDD), hoặc `/propose-scenario {UC-ID}` nếu gap thiếu test
444
+ coverage. Gap `ASSUMPTION` / `OPEN QUESTION` được confirm qua questions-for-dev của qc-plan — không file thành bug.
445
+
446
+ ### Thẩm định trước khi bàn giao *(bắt buộc)*
447
+
448
+ Sinh xong `DOC_GAP.md`, **đọc `.agent/steps/gap-verify.md` và chạy toàn bộ quy trình trong đó**
449
+ với:
450
+ - `FINDINGS` = mọi gap đang `Open` trong `DOC_GAP.md`
451
+ - `EVIDENCE_ROOT` = `{paths.specs_dir}` spec repo của PO, **không** phải `{paths.qc_dir}`
452
+ - `VERDICT_FIELD` = cột `Trạng thái` + `Câu trả lời` của bảng gap
453
+ - `RERATE` = `on`
454
+
455
+ Gap rớt thẩm định được **đóng kèm lý do**, KHÔNG xoá người đọc phải kiểm chứng được vì sao
456
+ bị loại. Cập nhật `Tổng số gap` + bảng ưu tiên sau khi áp verdict, in khối
457
+ `[GAP VERIFY]` + cam kết cuối vào report.
458
+
459
+ > **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** —
460
+ > 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 có
461
+ > 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
462
+ > 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
463
+ > được. 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 đó
464
+ > những gap thật cũng chết theo. `gap-verify` bộ lọc duy nhất đứng giữa hai chuyện đó.
465
+
466
+ ## Chạy lại `DOC_GAP.md` đã tồn tại *(THÊM, không THAY)*
467
+
468
+ Chạy lại trạm này chuyện bình thường: spec đổi thì phải phân tích lại. Nhưng `DOC_GAP.md` có
469
+ **hai phần khác hẳn nhau**, chỉ một phần sinh lại được:
470
+
471
+ | Phần | Ai tạo | Sinh lại được? |
472
+ |---|---|:---:|
473
+ | tả gap · phân loại · severity · UC | lệnh này | ✅ |
474
+ | **Cột `Trạng thái`** (`Open`/`Answered`) · **cột `Câu trả lời`** | **PO điền TAY** | **không bao giờ** |
475
+ | **Mã `GAP-UC{N}-{nnn}`** | lệnh này, nhưng **`.Test.md` đang trỏ vào** | ❌ đổi là phá |
476
+
477
+ **Đọc file cũ TRƯỚC khi ghi.** Với mỗi gap:
478
+
479
+ | Tình huống | Xử|
480
+ |---|---|
481
+ | Gap cũ, phân tích lại **vẫn thấy** | **Giữ nguyên** mã · `Trạng thái` · `Câu trả lời`. Chỉ cập nhật phần mô tả nếu spec đổi |
482
+ | Gap cũ, phân tích lại **không thấy nữa** | **KHÔNG xoá hàng.** `Trạng thái` → `Stale`, ghi lý do *"lần phân tích {ngày} không còn thấy"* |
483
+ | Gap **mới** | Cấp mã **tiếp theo** trong UC đó |
484
+
485
+ **Đánh số: CHỈ CẤP MỚI, không bao giờ tái dùng.** Mốc là ** cao nhất từng cấp** cho UC đó kể cả
486
+ khi gap mang mã ấy đã `Answered` hoặc `Stale`. Không dồn số, không lấp chỗ trống.
487
+
488
+ > **Tái dùng một mã là kịch bản tệ nhất của cả mục này.** `🚫 Block: [GAP-UC1-003]` trong một
489
+ > `.Test.md` cũ vẫn **đúng cú pháp**, vẫn resolve được, và giờ trỏ vào **một câu hỏi hoàn toàn khác**.
490
+ > Không lint nào bắt, không người nào nhìn ra. Liên kết **chết** thì còn thấy được; liên kết **trỏ
491
+ > sai nội dung** thì không.
492
+
493
+ **Gap `Stale` KHÔNG xoá hàng** một `.Test.md` thể đang trỏ vào nó. Xoá là biến `🚫 Block` thành
494
+ liên kết chết, `/qc-design-test` §Guard vòng đời `🚫 Block` sẽ không phân giải được.
495
+
496
+ ### Lập lại từ trắng — phải nói ra
497
+
498
+ Có ca hợp lệ: bản phân tích cũ sai hẳn, muốn bỏ. Cờ là **`--force`** (`rules/workflow.md` §Cờ bỏ qua
499
+ điều kiện). Không có cờ → **DỪNG** và nêu rõ cái giá:
500
+
501
+ ```
502
+ DOC_GAP.md đã tồn tại ({n} gap · {k} đã Answered).
503
+ Mặc định: HOÀ vào bản cũ — giữ mã gap, giữ Trạng thái/Câu trả lời của PO.
504
+ Muốn bỏ hẳn bản cũ, lập lại từ trắng: thêm --force
505
+ ⚠️ --force sẽ XOÁ {k} câu trả lời của PO, làm mọi 🚫 Block trong .Test.md trỏ sai.
506
+ ```
507
+
508
+ `--force` report **bắt buộc** khai:
509
+ ```
510
+ ⚠️ --force: bỏ qua luật hoà — đã xoá {k} câu trả lời của PO và {n} mã gap cũ.
511
+ Mọi 🚫 Block trong .Test.md của {TICKET-ID} giờ trỏ sai → chạy lại /qc-design-test cho các UC đó.
512
+ ```
513
+
514
+ > **Vì sao hoà là MẶC ĐỊNH còn lập-lại phải xin.** Cái thường gặp phải là cái an toàn; cái phá huỷ
515
+ > phải nói ra — cùng lập luận `steps/qc-scope.md` dùng cho `approved`-only.
516
+ >
517
+ > **Vì sao gap ảo làm hỏng nhiều hơn một file.** §Thẩm định ở trên đã ghi: *"gap ảo … làm PO **mất
518
+ > tin vào cả danh sách**, và lúc đó những gap thật cũng chết theo"*. Gửi lại PO một câu hỏi **họ đã
519
+ > trả lời rồi** gây đúng thiệt hại đó — và nó còn tệ hơn, vì nó chứng minh hệ thống không nhớ.
520
+
521
+ ## Output
522
+
523
+ Ghi **hai file** dưới `{qc_artifact_dir}` (= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/`)
524
+ + **một** dưới `{paths.refinement_dir}/`.
525
+
526
+ **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
527
+ một-file-mỗi-bước (không file spec-breakdown / business-rules / data-flow / AC riêng):
528
+
529
+ 1. **`{qc_artifact_dir}REQUIREMENT_ANALYSIS.md`** — bản phân tích hợp nhất duy nhất cho cả PRD.
530
+ 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
531
+ **một mục cho mỗi UC trong phạm vi**, mỗi mục theo thứ tự: phân requirement bảng
532
+ business-rule (`BR-xx`) → data-flow → acceptance-criteria (`AC-xx`), mỗi `BR`/`AC` map tới
533
+ `{UC-ID}-SC{N}` (của `.feature` nền này) sở hữu nó.
534
+ 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
535
+ "Không có". *Đây thứ chỉ trạm cấp PRD nhìn thấy được; đừng bỏ mục.*
536
+ 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
537
+ các UC trong phạm vi (`{id}` · UC · điều chưa biết · owner · severity). Rỗng thì ghi
538
+ "Không có"; **đừng bỏ mục**.
539
+ 2. **`{qc_artifact_dir}DOC_GAP.md`** — file gap, theo
540
+ `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` + luật viết ở
541
+ `{paths.qc_skills_dir}/qa-analyst/spec-issue-reporter.md`. Bắt buộc:
542
+ - **Bảng 11 cột** đúng thứ tự, có cột **`UC`** (cột 2) và cột **Giao cho đội** (Dev / PO / BA / Design / Kiến trúc / Dữ liệu).
543
+ - Section **"Phạm vi phân tích"** ngay sau metadata mỗi UC một hàng kèm `@trace.status`,
544
+ đã phân tích chưa, số gap. UC ngoài phạm vi ghi `⏸ Chưa xét`.
545
+ - Ô 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 ý**.
546
+ *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.*
547
+ - Section **"Tài liệu đầu vào đã đọc để phân tích"** đặt ngay sau metadata liệt **đủ** mọi
548
+ file đã mở. Đây là căn cứ độ phủ: không thì không ai phân biệt được *"đã đọc không thấy"*
549
+ với *"chưa đọc"*.
550
+ - Mức nặng nhất dùng từ **`🔴 Blocker`** (không phải `Critical`) `/qc-run-test` đọc đúng từ này
551
+ để đặt *"scenario đang chờ PO"* vào sổ trace.
552
+ 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.
553
+
554
+ `{paths.qc_dir}` folder top-level NHÌN THẤY trong QC repo (mặc định `docs/`, **không** phải
555
+ `.agent/review/` ẩn) để team QC mở và xử lý output dễ dàng. Spec chính thức ở lại
556
+ spec submodule của POđừng ghi phân tích vào đó.
557
+
558
+ ### Bản findings — một nguồn, hai mặt
559
+
560
+ File `.yaml` và `DOC_GAP.md` là **cùng một tập gap**, không phải hai tập. Sinh `DOC_GAP.md`
561
+ trước (nó là bản người đọc), rồi **render** sang `.yaml` — đừng phân tích lại lần hai.
562
+
563
+ Dùng **đúng schema của `/refine-prd`** để Review Board đọc được cả hai loại file:
564
+
565
+ ```yaml
566
+ prd_source: "{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md"
567
+ ticket_id: "{TICKET-ID}"
568
+ platform: "{active_platform}"
569
+ ucs: ["{UC-ID}", "…"] # các UC TRONG phạm vi (approved) — theo thứ tự
570
+ ucs_skipped: ["{UC-ID}"] # UC chưa xét, kèm lý do ở DOC_GAP §Phạm vi phân tích
571
+ generated_at: "{ISO datetime}"
572
+ generated_by: "qc-analyze"
573
+ status: "pending_review"
574
+
575
+ findings:
576
+ - id: "F001"
577
+ lens: "QA" # LUÔN là QA — file này chỉ có một lăng kính
578
+ severity: "critical" # critical | major | minor ← map từ 🔴/🟠/🟡⚪ của DOC_GAP
579
+ section: "{section PRD/BDD chứa vấn đề}"
580
+ uc_id: "{UC-ID}"
581
+ quote: "{trích nguyên văn ≤120 ký tự từ spec tại đúng chỗ}"
582
+ finding: "{gap là gì}"
583
+ suggestion: "{cần PO/BA làm rõ điều gì}"
584
+ resolution_edge_cases: [] # để [] — phân tích bậc-hai là việc của /refine-prd
585
+ auto_fixable: false # LUÔN false — xem cảnh báo dưới
586
+ status: "pending"
587
+ applied_via: ""
588
+ gap_ref: "GAP-UC1-001" # trỏ ngược về hàng trong DOC_GAP.md (ID mang UC)
589
+
590
+ summary:
591
+ total_findings: {N}
592
+ by_severity: { critical: {N}, major: {N}, minor: {N} }
593
+ by_lens: { QA: {N} }
594
+ recommendation: "APPROVED_WITH_MINOR_CHANGES | NEEDS_REVISION | BLOCKED"
595
+ ```
596
+
597
+ > **`auto_fixable` LUÔN `false`, và KHÔNG có `--resume` cho file này.**
598
+ >
599
+ > 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
600
+ > ở **thời điểm PRD**, sửa PRD lúc đó là sửa đúng chỗ đúng lúc.
601
+ >
602
+ > Gap của lệnh này phát hiện **sau khi BDD đã `approved` và tech-doc đã duyệt** — ở luồng song song,
603
+ > thường là lúc `/generate-code` đang chạy ở nhánh bên kia. Tự sửa PRD ở thời điểm đó là **sửa sau
604
+ > lưng cả dây chuyền**: BDD sinh từ PRD cũ, tech-doc và hợp đồng test-id §4.5.6 chốt theo BDD đó,
605
+ > code đang được sinh từ chúng, và sổ kết quả kiểm thử neo vào scenario của BDD đó. Đổi PRD mà không
606
+ > đi lại đường ấy thì mọi thứ phía sau nói dối.
607
+ >
608
+ > *(Bản trước viết tiền đề là **"sau khi code đã xong"**. Kết luận đúng, tiền đề **hết đúng** từ khi
609
+ > `a3a5f30` mở luồng FE ∥ QC song song — trạm này giờ chạy khi code còn chưa xong. Sửa tiền đề chứ
610
+ > không sửa kết luận: cái chặn không phải là **code**, mà là **BDD + tech-doc đã đóng băng**.)*
611
+ >
612
+ > Đường đúng vẫn là kênh đã có: `/report-bug` cho defect spec thật, `/propose-scenario` cho thiếu
613
+ > độ phủ. File `.yaml` này để **PO đọc và quyết trong một chỗ quen**, không phải để máy tự áp.
614
+
615
+ **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
616
+ ở **hậu tố**: `{TICKET-ID}-qa-findings.yaml` (trạm này) vs `{prd-slug}-findings.yaml`
617
+ (`/refine-prd`). Đừng gộp. Ghi chung sẽ phá trường `applied_to_version` mà `/refine-prd` dùng để
618
+ 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ó
619
+ 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ả.
620
+
621
+ ---
622
+
623
+ ## Self-Review *(trước khi in Report)*
624
+
625
+ Theo 3 nhóm ở `{paths.qc_skills_dir}/_shared/self-review-principles.md` — **không chép lại ở đây**.
626
+
627
+ - **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ự
628
+ 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?
629
+ - **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 —
630
+ không bỏ lăng kính nào vì "UC này đơn giản"?
631
+ - **Số liệu:** `{N}` gap · `{blockers}` blocker · `{M}` BR/AC in ở report là **đếm thật trên
632
+ bảng vừa ghi** (`grep -cE "^\| GAP-"`), không phải áng chừng?
633
+
634
+ > **`gap-verify` và self-review bổ sung nhau, KHÔNG thay nhau.** `gap-verify` kiểm **từng
635
+ > 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 /
636
+ > 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.
637
+ >
638
+ > 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
639
+ > trong file skill.
640
+
641
+ ## Report
642
+
559
643
  **Đọc `.agent/steps/report-footer.md`** và áp đúng khuôn footer trong đó (Status Badge ·
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 mà 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` là 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
-
644
+ Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
645
+
646
+ ```
647
+ /qc-analyze Hoàn tất — {TICKET-ID} ({active_platform})
648
+ 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)"}
649
+ Files : {paths.qc_dir}/{TICKET-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md + DOC_GAP.md (11 cột)
650
+ {paths.refinement_dir}/{TICKET-ID}-qa-findings.yaml ← mở bằng Review Board (chuột phải)
651
+ 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)"}
652
+ 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
653
+ · đối chiếu chéo: PRD↔design-spec, bdd↔design-spec)
654
+ 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)
655
+ Gaps : {N} ({blockers} blocker) — theo UC: {UC1: n · UC2: n · …}{nếu có: " · toàn PRD: {n}"}
656
+ ← blocker là spec-defect? → /report-bug {UC-ID của hàng đó} | coverage gap → /propose-scenario {UC-ID}
657
+ 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)
658
+ 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
659
+ 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)
660
+ SC map: {M} BR/AC map tới {K} scenario
661
+ Guard BR-tag: {khớp {n}/{n} | ⚠️ bổ sung {k} rule BDD đã nhắc mà phân tích bỏ sót: {danh sách}}
662
+ Self-review: {✅ sạch | ⚠️ {n} điểm cần chú ý — liệt kê}
663
+ Next : /qc-plan {TICKET-ID} {active_platform} ← rủi ro / what-if / câu hỏi cho dev
664
+ (giải quyết các gap 🔴 Blocker với PO/Dev trước)
665
+ ```
666
+
667
+ > **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
668
+ > **và** kế hoạch test trong một lần chạy (13/14 lần đo được ở repo của họ). Ở framework đó là
669
+ > **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
670
+ > nói cho họ biết còn một bước nữa.
671
+