@educa-corp/sdd-framework 0.9.2 → 0.9.3

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 (37) hide show
  1. package/core/FRAMEWORK_VERSION +1 -1
  2. package/core/commands/qc-analyze.md +189 -66
  3. package/core/commands/qc-design-test.md +141 -2
  4. package/core/commands/qc-plan.md +153 -7
  5. package/core/commands/qc-review.md +134 -1
  6. package/core/commands/qc-run-test.md +134 -1
  7. package/core/modules/qc-playwright/stack-profile.yaml +3 -3
  8. package/core/skills/qc/qa-analyst/DOC_GAP.template.md +47 -17
  9. package/core/skills/qc/qa-analyst/acceptance-criteria.md +1 -1
  10. package/core/skills/qc/qa-analyst/business-rules.md +2 -2
  11. package/core/skills/qc/qa-analyst/data-flow.md +2 -2
  12. package/core/skills/qc/qa-analyst/spec-breakdown.md +2 -2
  13. package/core/skills/qc/qa-analyst/spec-issue-reporter.md +14 -2
  14. package/core/skills/qc/qa-designer/e2e/journey.md +1 -1
  15. package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +1 -1
  16. package/core/skills/qc/qa-designer/functional/api.md +1 -1
  17. package/core/skills/qc/qa-designer/functional/gui-feature.md +1 -1
  18. package/core/skills/qc/qa-designer/functional/gui-screen.md +1 -1
  19. package/core/skills/qc/qa-designer/integration/api.md +1 -1
  20. package/core/skills/qc/qa-designer/integration/db.md +1 -1
  21. package/core/skills/qc/qa-designer/integration/gui.md +1 -1
  22. package/core/skills/qc/qa-designer/integration/kafka.md +1 -1
  23. package/core/skills/qc/qa-designer/non-functional.md +1 -1
  24. package/core/skills/qc/qa-planner/test-plan.md +24 -13
  25. package/core/skills/qc/qa-runner/exploratory/session.md +1 -1
  26. package/core/steps/context-loader.md +1 -1
  27. package/core/steps/qc-scope.md +119 -0
  28. package/core/templates/project-context.yaml +3 -1
  29. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +1 -1
  30. package/docs/04-reference/configuration.md +146 -146
  31. package/docs/explain/15-qc-analyze.md +1 -1
  32. package/docs/explain/16-qc-plan.md +1 -1
  33. package/docs/explain/17-qc-design-test.md +1 -1
  34. package/docs/plans/qc-implementation-log.md +145 -4
  35. package/docs/plans/qc-sync-command.md +2 -1
  36. package/package.json +1 -1
  37. package/scripts/migrate-qc-docs.js +261 -0
@@ -1 +1 @@
1
- 0.9.2
1
+ 0.9.3
@@ -165,7 +165,7 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
165
165
  🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
166
166
 
167
167
 
168
- *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID hoặc file feature/PRD. Đọc spec chính thức của UC đó — 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.*
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
169
 
170
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 đó**,
@@ -177,60 +177,155 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
177
177
 
178
178
  ---
179
179
 
180
- ## Platform Resolution *(thiết lập cho cả QC pass mọi stage sau kế thừa)*
180
+ ## Phạm vi QC PRD nào, nền nào, những UC nào
181
181
 
182
- > **PHẢI chạy TRƯỚC Guard bên dưới.** `{UC-ID}-SC{N}` chỉ độc nhất trong (UC × platform), nên
183
- > một UC đa nền có **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`, `bdd/system/` — và mỗi
184
- > file mang `@trace.status` **riêng**: bản web có thể `approved` trong khi bản app còn `draft`.
185
- > Guard đọc "file `.feature` của UC" khi chưa biết platform là đọc một file **bất kỳ trong ba**:
186
- > báo `approved` trong khi bản đang dùng còn nháp, hoặc chặn oan một bản đã duyệt.
187
- > Chốt platform trước thì Guard mới có đúng một file để đọc.
182
+ # QC Scope phân giải phạm vi cho mọi lệnh `qc-*`
188
183
 
189
- `{UC-ID}-SC{N}` chỉ độc nhất trong (UC × platform) — `web SC3` và `app SC3` là hai scenario khác nhau, và sổ trace tách theo `{UC-ID}-{platform}.tsv`. Nên **một QC pass khoá đúng MỘT platform**, và mọi artifact QC nằm dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/`.
184
+ **Chạy TRƯỚC phần logic riêng của lệnh, và SAU `steps/gate.md`.** Bước này chốt bốn thứ
185
+ cả 6 trạm QC đều cần, để chúng không tự suy mỗi trạm một kiểu:
190
186
 
191
- Phân giải `active_platform` theo thứ tự, dừng ở cái đầu tiên khớp:
192
- 1. `$ARGUMENTS` nêu platform (`web`/`app`/`system`) → dùng.
193
- 2. Target Bước 1 một file `.feature` đọc `# @trace.platform` của nó.
194
- 3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/`**đúng một** thư mục platform tồn tại dùng nó.
195
- 4. Nhiều platform mà không suy được → hỏi *"QC pass này cho platform nào? (web/app/system)"*.
196
- **Có `--yes`:** không hỏi dừng với lỗi ràng, đoán bừa platform ghi artifact vào sai
187
+ | Biến | |
188
+ |---|---|
189
+ | `TICKET-ID` | PRD **thư mục artifact QC mang tên này** |
190
+ | `active_platform` | `web` \| `app` \| `system` \| … — một QC pass khoá đúng MỘT nền |
191
+ | `qc_artifact_dir` | `{paths.qc_dir}/{TICKET-ID}/{active_platform}/` |
192
+ | `uc_list` | các UC của (PRD × nền) này, kèm trạng thái BDD từng UC |
193
+
194
+ > **Vì sao gom về một chỗ.** Luật phân giải nền từng được copy-paste ở 5 lệnh và câu chữ đã
195
+ > lệch nhau. Năm bản của một luật là nơi drift sống: sửa bốn, quên một, và trạm bị quên ghi
196
+ > artifact vào sai thư mục **trong im lặng**.
197
+
198
+ ---
199
+
200
+ ## 1 — `TICKET-ID`
201
+
202
+ Artifact QC gom theo **PRD**, không theo UC. Nên mọi trạm phải quy được về `TICKET-ID`:
203
+
204
+ | `$ARGUMENTS` là | Cách lấy |
205
+ |---|---|
206
+ | **UC-ID** (`{TICKET-ID}-UC{N}`) | phần **trước** `-UC` — đúng luật `steps/gate.md` Bước 1 dùng để tìm tech-doc gộp |
207
+ | **TICKET-ID** | dùng trực tiếp |
208
+ | một **path file** (`.feature` / PRD / design-spec) | phân giải `{domain}` + `{prd-slug}` theo luật `context-loader` Bước 1, rồi lấy `TICKET-ID` từ tên file PRD `{TICKET-ID}-{prd-slug}.md` — file `.md` duy nhất ở gốc feature folder |
209
+
210
+ Đối chiếu: `TICKET-ID` suy ra phải khớp tên file PRD thật. Lệch → **DỪNG**, in cả hai giá
211
+ trị. (Suy sai `TICKET-ID` là ghi cả một PRD vào sai thư mục — không có bước nào phía sau bắt được.)
212
+
213
+ ---
214
+
215
+ ## 2 — `active_platform`
216
+
217
+ > **PHẢI phân giải TRƯỚC mọi phép đọc `.feature`.** `{UC-ID}-SC{N}` chỉ độc nhất trong
218
+ > (UC × nền), nên một UC đa nền có **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`,
219
+ > `bdd/system/` — và mỗi file mang `@trace.status` **riêng**: bản web có thể `approved`
220
+ > trong khi bản app còn `draft`. Đọc "file `.feature` của UC" khi chưa biết nền là đọc một
221
+ > file **bất kỳ trong ba**: báo `approved` trong khi bản đang dùng còn nháp, hoặc chặn oan
222
+ > một bản đã duyệt.
223
+
224
+ Theo thứ tự, dừng ở cái đầu tiên khớp:
225
+
226
+ 1. `$ARGUMENTS` nêu nền (`web`/`app`/`system`/…) → dùng.
227
+ 2. Target là một file `.feature` → đọc `# @trace.platform` của nó.
228
+ 3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/` — **đúng một** thư mục nền → dùng nó.
229
+ 4. Glob `{paths.qc_dir}/{TICKET-ID}/*/` — **đúng một** thư mục nền đã có artifact → dùng nó.
230
+ *(chỉ dùng cho trạm 2–6; trạm `/qc-analyze` là trạm tạo ra thư mục đó nên không có gì để soi.)*
231
+ 5. Nhiều nền mà không suy được → hỏi *"QC pass này cho nền nào? (web/app/system)"*.
232
+ **Có `--yes`:** không hỏi — DỪNG với lỗi rõ ràng, vì đoán bừa nền là ghi artifact vào sai
197
233
  thư mục và ghi `qc_status` vào sai sổ trace:
198
234
  ```
199
- ❌ {UC-ID} có {n} platform ({list}) — không suy được platform nào cho QC pass này.
200
- Chạy headless thì phải nêu tường minh: /qc-analyze {UC-ID} web --yes
235
+ ❌ {TICKET-ID} có {n} nền ({list}) — không suy được nền nào cho QC pass này.
236
+ Chạy headless thì phải nêu tường minh: /{lệnh} {TICKET-ID} web --yes
201
237
  ```
202
238
 
203
- Lưu `active_platform`. Đọc **đúng file `.feature` của platform đó** (`{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{UC-ID}*.feature`) làm nguồn SC — không trộn SC chéo platform.
239
+ Lưu `active_platform`. Từ đây, **mọi** phép đọc `.feature` chỉ đọc thư mục
240
+ `bdd/{active_platform}/` — không trộn SC chéo nền.
241
+
242
+ ---
243
+
244
+ ## 3 — `qc_artifact_dir`
245
+
246
+ ```
247
+ qc_artifact_dir = {paths.qc_dir}/{TICKET-ID}/{active_platform}/
248
+ ```
249
+
250
+ Chứa: `REQUIREMENT_ANALYSIS.md` · `DOC_GAP.md` · `TEST_PLAN.md` · `test-cases/*.Test.md`
251
+ — **mỗi loại đúng MỘT file cho cả PRD**, các UC là mục/hàng bên trong.
252
+
253
+ `{paths.qc_dir}` là folder top-level **nhìn thấy** trong repo QC (mặc định `docs/`, **không**
254
+ phải `.agent/` ẩn) để đội QC mở và xử lý output dễ dàng. Spec chính thức ở lại spec submodule
255
+ của PO — đừng ghi artifact QC vào đó.
256
+
257
+ > **Sổ trace KHÔNG theo layout này.** Nó vẫn là `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv`
258
+ > — một sổ cho mỗi (UC × nền), vì mỗi hàng là một scenario. Liên kết giữa hai bên đi qua
259
+ > **cột `UC`** của bảng gap, không qua đường dẫn file.
204
260
 
205
261
  ---
206
262
 
207
- ## GuardBDD đã duyệt chưa
263
+ ## 4`uc_list`
264
+
265
+ Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/*.feature`. Mỗi file → một
266
+ UC: đọc `# @trace.id` (mã UC) và `# @trace.status` từ header.
267
+
268
+ Chia hai nhóm:
269
+
270
+ | Nhóm | Điều kiện | Xử lý |
271
+ |---|---|---|
272
+ | **Trong phạm vi** | `@trace.status: approved` | phân tích / thiết kế / chạy bình thường |
273
+ | **Chưa xét** | khác `approved` | **KHÔNG** phân tích; vẫn ghi một hàng vào bảng phạm vi kèm trạng thái thật |
274
+
275
+ In bảng phạm vi ra trước khi làm gì:
276
+ ```
277
+ Phạm vi QC — {TICKET-ID} / {active_platform}
278
+ ✅ {UC-ID} {tên UC} approved
279
+ ⏸ {UC-ID} {tên UC} draft → chưa xét
280
+ → {n} UC trong phạm vi · {m} chưa xét
281
+ ```
282
+
283
+ **Cờ `--include-draft`:** phân tích cả UC chưa duyệt, nhưng **vẫn in bảng trên** và đánh dấu
284
+ trong artifact là dựa trên BDD nháp.
285
+
286
+ **Không UC nào `approved` và không có `--include-draft` → DỪNG:**
287
+ ```
288
+ ❌ {TICKET-ID} ({active_platform}): 0/{n} UC có BDD approved — không có gì để chạy.
289
+ Cách đúng: người duyệt đặt `# @trace.status: approved` rồi chạy lại.
290
+ Muốn chạy sớm trên BDD nháp (prototype): thêm --include-draft
291
+ ```
292
+
293
+ > **Vì sao có `--include-draft` chứ không chặn cứng.** QC sớm trên BDD nháp là một cách dùng
294
+ > **cố ý được cho phép** từ trước (guard cũ là cảnh báo mềm, không phải chặn). Bỏ hẳn nó là
295
+ > lấy đi một năng lực đang có mà không ai khai. Còn để mặc định `approved`-only thì cái
296
+ > thường gặp là cái an toàn, và cái sớm phải nói ra.
297
+
298
+ > **Vì sao `--yes` không thay được `--include-draft`.** `--yes` nghĩa *"tôi không ngồi đây để
299
+ > trả lời"*; `--include-draft` nghĩa *"tôi biết BDD còn nháp và vẫn muốn chạy"*. Gộp hai cái
300
+ > là để một lần chạy headless âm thầm phân tích spec chưa chốt rồi bàn giao như thể đã chốt.
208
301
 
209
- *Chạy SAU Platform Resolution — xem lý do ở khối trên.*
210
302
 
211
- Đọc `# @trace.status:` từ header file `.feature` **của `{active_platform}` đã phân giải**:
212
- - `approved` tiếp tục bình thường.
213
- - `draft` (hoặc khác `approved`) **CHECKPOINT cảnh báo mềm** (không chặn cứng cho phép QC sớm/prototype):
214
- ```
215
- ⚠️ BDD của {UC-ID} ({active_platform}) đang ở @trace.status: {status} (chưa duyệt). QC chạy trên BDD chưa chốt có thể phải làm lại.
216
- Khuyến nghị: review-context (BDD) sạch + người duyệt đặt `# @trace.status: approved` rồi mới chạy QC.
217
- Vẫn chạy QC bây giờ? (Y/N)
218
- ```
219
- Chỉ tiếp khi chọn Y — **trừ khi `$ARGUMENTS` có `--yes`**: coi như Y, **nhưng vẫn IN khối cảnh
220
- báo** (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy QC đã chạy trên BDD nháp).
303
+ > **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ó
304
+ > `@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`
305
+ > với dấu `⏸ Chưa xét` **không im lặng bỏ khỏi bảng**, "chưa xét" khác "đã xét, sạch".
306
+ > Cố ý QC sớm thì thêm `--include-draft`, và artifact phải ghi rõ nó dựa trên BDD nháp.
221
307
 
222
- > **Vì sao `--yes` phải phủ cả guard mềm này.** `steps/gate.md` mở đường chạy headless
223
- > (`claude -p "… --yes"`), nhưng `--yes` chỉ khai là bỏ qua CHECKPOINT của gate. Guard mềm ở đây
224
- > một `(Y/N)` thứ hai nên lệnh vẫn treo hạn chế độ không người trả lời, và đường
225
- > headless gate hứa bị bít cho đúng lệnh này. Một cổng chỉ chặn được khi có người ngồi đó
226
- > thì chế độ headless không phải cổng, treo.
308
+ > **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:
309
+ >
310
+ > 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
311
+ > 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".
312
+ > 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à
313
+ > đọc lại chúng mỗi UC một lượt. Phần dùng chung chiếm đa số đầu vào.
314
+ > 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
315
+ > (`DOC_GAP_FEAT-02-3.md`) không có hậu tố UC, và `qa-planner/test-plan.md` vốn viết
316
+ > *"Test Plan cho một feature"*.
227
317
 
228
318
  ---
229
319
 
230
320
  ## Đối chiếu tài liệu kỹ thuật *(nguồn thứ hai — bắt lệch nghiệp vụ ↔ kỹ thuật)*
231
321
 
232
322
  Định vị tech-doc gộp cấp PRD: `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`.
233
- Nó phủ **nhiều UC** — lọc theo `@trace.ucs` header, chỉ đọc phần chạm `{UC-ID}` đang xét.
323
+ Nó 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`**
324
+ (đố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.
325
+
326
+ > **Đâ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.
327
+ > 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
328
+ > **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.
234
329
 
235
330
  **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):
236
331
  ```
@@ -259,9 +354,9 @@ Nó phủ **nhiều UC** — lọc theo `@trace.ucs` ở header, chỉ đọc ph
259
354
  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à
260
355
  `/generate-code` đã canh nó. **Trạm này chỉ đọc, tuyệt đối không ghi vào.**
261
356
 
262
- Với mỗi mục `open` trong §12 chạm `{UC-ID}`:
357
+ Với mỗi mục `open` trong §12 chạm **bất kỳ UC trong `uc_list`**:
263
358
  - **KHÔNG mở gap mới** trong `DOC_GAP.md` về cùng chuyện đó.
264
- - Ghi vào `REQUIREMENT_ANALYSIS.md` mục *"Đang chờ chốt (từ §12 tech-doc)"*: `{id}` · điều chưa biết · owner · severity.
359
+ - 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.
265
360
  - 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.
266
361
 
267
362
  > **Vì sao không ghi vào.** Một ẩn số đã nằm trong §12 nghĩa là **đã có người đang lo**: có
@@ -272,8 +367,8 @@ Với mỗi mục `open` trong §12 chạm `{UC-ID}`:
272
367
 
273
368
  **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) ·
274
369
  `cross-service` (đội khác) · `spec-defect` (PRD/BDD sai hoặc thiếu). Hai loại đầu → ghi "đang chờ".
275
- Loại thứ ba **đúng là gap tài liệu** → vẫn ghi vào `DOC_GAP.md`, trỏ ngược về `{id}` của §12 để
276
- không đếm hai lần.
370
+ 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ỏ
371
+ ngược về `{id}` của §12 để không đếm hai lần.
277
372
 
278
373
  ---
279
374
 
@@ -301,7 +396,8 @@ Thứ tự điển hình: spec-breakdown → business-rules / data-flow → acce
301
396
 
302
397
  File `.feature` chính thức đã định nghĩa scenario là `@trace.scenario={UC-ID}-SC{N}` với
303
398
  `@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ó
304
- và ghi lại mapping — qc-design-testqc-run-test cần để gắn tag
399
+ và ghi lại mapping — **làm cho từng UC trong `uc_list`**, `BR`/`AC` phải mang UC của nó
400
+ (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
305
401
  `@trace.verifies` cho test và ghi `qc_status` theo từng scenario.
306
402
 
307
403
  ## Quét gap — hai nguồn, gộp rồi mới thẩm định
@@ -367,13 +463,23 @@ Gộp gap từ **cả hai nguồn** vào một tập trước khi sang bước t
367
463
 
368
464
  ## DOC_GAP (bắt buộc)
369
465
 
370
- Luôn tạo một file gaps theo `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md`:
371
- - Mỗi gap `GAP-xx`, phân loại MISSING / AMBIGUOUS / CONTRADICTORY / ASSUMPTION / OPEN QUESTION, với severity (🔴 Blocker → 🟢 Low) và function/BR/AC bị ảnh hưởng.
466
+ Luôn tạo **đúng MỘT** file gap cho cả (PRD × nền) theo
467
+ `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` các UC các hàng bên trong, phân
468
+ biệt bằng cột `UC`:
469
+ - **Bảng 11 cột**, cột 2 là `UC`. ID gap `GAP-UC{N}-{nnn}` (vd `GAP-UC1-001`); gap thuộc cả
470
+ 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
471
+ đổi số gap của UC2, vì test case đã có đang trỏ `🚫 Block: [GAP-UC2-003]`.
472
+ - **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ố
473
+ gap. UC ngoài phạm vi ghi `⏸ Chưa xét`, **không bỏ khỏi bảng**.
474
+ - 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.
372
475
  - Không bao giờ bịa câu trả lời; đánh dấu giả định là `ASSUMPTION` để PO/dev confirm.
373
- - Bất kỳ `🔴 Blocker` nào còn `Open` ⇒ UC chưa sẵn sàng cho qc-design-test bàn giao cho qc-plan.
476
+ - Bất kỳ `🔴 Blocker` nào còn `Open` ⇒ **UC cột `UC` của hàng đó** chưa sẵn sàng cho
477
+ qc-design-test — bàn giao cho qc-plan. *Chặn theo từng UC, KHÔNG chặn cả PRD:* một blocker ở
478
+ UC3 không có lý do gì dừng việc thiết kế test cho UC1. Ghi rõ UC nào bị chặn ở report.
374
479
  - **Đẩy các defect spec thực sự lên PO (không chỉ giữ local).** Một blocker là lỗi thật
375
480
  trong spec chính thức — `AMBIGUOUS` / `CONTRADICTORY` / `MISSING` trong PRD/BDD — phải tới
376
481
  PO qua feedback flow, không chỉ nằm trong `DOC_GAP.md`: tạo `/report-bug {UC-ID} {desc}`
482
+ (`{UC-ID}` lấy từ cột `UC` của hàng gap — bug đi theo UC, không theo PRD)
377
483
  (BUG_FLOW của nó phân loại PRD vs BDD), hoặc `/propose-scenario {UC-ID}` nếu gap là thiếu test
378
484
  coverage. Gap `ASSUMPTION` / `OPEN QUESTION` được confirm qua questions-for-dev của qc-plan — không file thành bug.
379
485
 
@@ -399,18 +505,28 @@ nó bị loại. Cập nhật `Tổng số gap` + bảng ưu tiên sau khi áp v
399
505
 
400
506
  ## Output
401
507
 
402
- Ghi **hai file** dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/` + **một** dưới `{paths.refinement_dir}/` — **đừng** tách phân tích
403
- thành một-file-mỗi-bước (không có file spec-breakdown / business-rules / data-flow / AC riêng):
404
-
405
- 1. **`{paths.qc_dir}/{UC-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md`**bản phân tích hợp nhất duy nhất.
406
- Section theo thứ tự: phân requirement → bảng business-rule (`BR-xx`) data-flow
407
- acceptance-criteria (`AC-xx`), mỗi `BR`/`AC` map tới `{UC-ID}-SC{N}` (của `.feature` platform này) sở hữu nó.
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 UC này
409
- (`{id}` · điều chưa biết · owner · severity). Rỗng thì ghi "Không có"; **đừng bỏ mục**.
410
- 2. **`{paths.qc_dir}/{UC-ID}/{active_platform}/DOC_GAP.md`** file gap, theo
508
+ Ghi **hai file** dưới `{qc_artifact_dir}` (= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/`)
509
+ + **một** dưới `{paths.refinement_dir}/`.
510
+
511
+ **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
512
+ một-file-mỗi-bước (không file spec-breakdown / business-rules / data-flow / AC riêng):
513
+
514
+ 1. **`{qc_artifact_dir}REQUIREMENT_ANALYSIS.md`** bản phân tích hợp nhất duy nhất cho cả PRD.
515
+ 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
516
+ **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
517
+ business-rule (`BR-xx`) → data-flow → acceptance-criteria (`AC-xx`), mỗi `BR`/`AC` map tới
518
+ `{UC-ID}-SC{N}` (của `.feature` nền này) sở hữu nó.
519
+ 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
520
+ "Không có". *Đây là thứ chỉ trạm cấp PRD nhìn thấy được; đừng bỏ mục.*
521
+ 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
522
+ các UC trong phạm vi (`{id}` · UC · điều chưa biết · owner · severity). Rỗng thì ghi
523
+ "Không có"; **đừng bỏ mục**.
524
+ 2. **`{qc_artifact_dir}DOC_GAP.md`** — file gap, theo
411
525
  `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` + luật viết ở
412
526
  `{paths.qc_skills_dir}/qa-analyst/spec-issue-reporter.md`. Bắt buộc:
413
- - **Bảng 10 cột** đúng thứ tự, có cột **Giao cho đội** (Dev / PO / BA / Design / Kiến trúc / Dữ liệu).
527
+ - **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).
528
+ - Section **"Phạm vi phân tích"** ngay sau metadata — mỗi UC một hàng kèm `@trace.status`,
529
+ đã phân tích chưa, số gap. UC ngoài phạm vi ghi `⏸ Chưa xét`.
414
530
  - Ô 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 ý**.
415
531
  *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.*
416
532
  - Section **"Tài liệu đầu vào đã đọc để phân tích"** đặt ngay sau metadata — liệt kê **đủ** mọi
@@ -418,7 +534,7 @@ thành một-file-mỗi-bước (không có file spec-breakdown / business-rules
418
534
  với *"chưa đọc"*.
419
535
  - Mức nặng nhất dùng từ **`🔴 Blocker`** (không phải `Critical`) — `/qc-run-test` đọc đúng từ này
420
536
  để đặt *"scenario đang chờ PO"* vào sổ trace.
421
- 3. **`{paths.refinement_dir}/{UC-ID}-qa-findings.yaml`** — **cùng dữ liệu gap**, ở định dạng Review Board đọc được. Xem §Bản findings dưới đây.
537
+ 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.
422
538
 
423
539
  `{paths.qc_dir}` là folder top-level NHÌN THẤY trong QC repo (mặc định `docs/`, **không** phải
424
540
  `.agent/review/` ẩn) để team QC mở và xử lý output dễ dàng. Spec chính thức ở lại
@@ -433,8 +549,10 @@ Dùng **đúng schema của `/refine-prd`** để Review Board đọc được c
433
549
 
434
550
  ```yaml
435
551
  prd_source: "{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md"
436
- uc_id: "{UC-ID}"
552
+ ticket_id: "{TICKET-ID}"
437
553
  platform: "{active_platform}"
554
+ ucs: ["{UC-ID}", "…"] # các UC TRONG phạm vi (approved) — theo thứ tự
555
+ ucs_skipped: ["{UC-ID}"] # UC chưa xét, kèm lý do ở DOC_GAP §Phạm vi phân tích
438
556
  generated_at: "{ISO datetime}"
439
557
  generated_by: "qc-analyze"
440
558
  status: "pending_review"
@@ -452,7 +570,7 @@ findings:
452
570
  auto_fixable: false # LUÔN false — xem cảnh báo dưới
453
571
  status: "pending"
454
572
  applied_via: ""
455
- gap_ref: "GAP-xx" # trỏ ngược về hàng trong DOC_GAP.md
573
+ gap_ref: "GAP-UC1-001" # trỏ ngược về hàng trong DOC_GAP.md (ID mang UC)
456
574
 
457
575
  summary:
458
576
  total_findings: {N}
@@ -473,8 +591,9 @@ summary:
473
591
  > Đường đúng vẫn là kênh đã có: `/report-bug` cho defect spec thật, `/propose-scenario` cho thiếu
474
592
  > độ phủ. File `.yaml` này để **PO đọc và quyết trong một chỗ quen**, không phải để máy tự áp.
475
593
 
476
- **File riêng, không ghi chung với `/refine-prd`.** Tên `{UC-ID}` nên không đụng
477
- `{prd-slug}-findings.yaml`. Ghi chung sẽ phá trường `applied_to_version` mà `/refine-prd` dùng để
594
+ **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
595
+ ở **hậu tố**: `{TICKET-ID}-qa-findings.yaml` (trạm này) vs `{prd-slug}-findings.yaml`
596
+ (`/refine-prd`). Đừng gộp. Ghi chung sẽ phá trường `applied_to_version` mà `/refine-prd` dùng để
478
597
  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ó
479
598
  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ả.
480
599
 
@@ -486,17 +605,21 @@ người sửa sau lưng, mỗi lần chạy đều quét lại toàn bộ kèm
486
605
  Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
487
606
 
488
607
  ```
489
- /qc-analyze Hoàn tất — {UC-ID} ({active_platform})
490
- Files : {paths.qc_dir}/{UC-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md + DOC_GAP.md (10 cột)
491
- {paths.refinement_dir}/{UC-ID}-qa-findings.yaml ← mở bằng Review Board (chuột phải)
492
- Nguồn : PRD · BDD({active_platform}) · design-spec · tech-doc{nếu thiếu tech-doc: " (THIẾU không đối chiếu được nghiệp vụ ↔ kỹ thuật)"}
608
+ /qc-analyze Hoàn tất — {TICKET-ID} ({active_platform})
609
+ 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)"}
610
+ Files : {paths.qc_dir}/{TICKET-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md + DOC_GAP.md (11 cột)
611
+ {paths.refinement_dir}/{TICKET-ID}-qa-findings.yaml ← mở bằng Review Board (chuột phải)
612
+ 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)"}
493
613
  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
494
614
  · đối chiếu chéo: PRD↔design-spec, bdd↔design-spec)
495
615
  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)
496
- Gaps : {N} ({blockers} blocker) blocker spec-defect? /report-bug {UC-ID} | coverage gap /propose-scenario {UC-ID}
497
- Chờ chốt: {G} ẩn số §12 tech-doc đang open chạm UC này (đã ghi vào REQUIREMENT_ANALYSIS, KHÔNG hỏi lại PO)
616
+ Gaps : {N} ({blockers} blocker) theo UC: {UC1: n · UC2: n · …}{nếu có: " · toàn PRD: {n}"}
617
+ blocker spec-defect? /report-bug {UC-ID của hàng đó} | coverage gap /propose-scenario {UC-ID}
618
+ 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)
619
+ 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
620
+ 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)
498
621
  SC map: {M} BR/AC map tới {K} scenario
499
- Next : /qc-plan {UC-ID} {active_platform} ← rủi ro / what-if / câu hỏi cho dev
622
+ Next : /qc-plan {TICKET-ID} {active_platform} ← rủi ro / what-if / câu hỏi cho dev
500
623
  (giải quyết các gap 🔴 Blocker với PO/Dev trước)
501
624
  ```
502
625
 
@@ -162,7 +162,7 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
162
162
  🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
163
163
 
164
164
 
165
- *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID. **Phân giải `active_platform`** (QC pass khoá 1 platform): nếu `$ARGUMENTS` nêu platform → dùng; else glob `{paths.qc_dir}/{UC-ID}/*/` — đúng 1 folder platform → dùng nó, nhiều folder → hỏi. Đọc output của qc-analyze + qc-plan (`REQUIREMENT_ANALYSIS.md`, `DOC_GAP.md`, `TEST_PLAN.md`) từ `{paths.qc_dir}/{UC-ID}/{active_platform}/` và file `.feature` của đúng platform đó (với `@trace.scenario` mỗi scenario). Với layer GUI, cũng đọc bảng **Test Selectors** §4.5.6 (block platform) của tech-doc gộp tại `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` (nếu có — bảng gộp mọi UC của platform, **lọc theo cột "Serves SC" khớp SC của UC này**) — các test-id ổn định mà QC sẽ định vị theo.*
165
+ *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID. Đọc output của qc-analyze + qc-plan (`REQUIREMENT_ANALYSIS.md`, `DOC_GAP.md`, `TEST_PLAN.md`) từ `{qc_artifact_dir}` — **cả ba đều ở cấp PRD, lọc theo cột `UC`** để lấy phần của UC này — và file `.feature` của đúng platform đó (với `@trace.scenario` mỗi scenario). Với layer GUI, cũng đọc bảng **Test Selectors** §4.5.6 (block platform) của tech-doc gộp tại `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` (nếu có — bảng gộp mọi UC của platform, **lọc theo cột "Serves SC" khớp SC của UC này**) — các test-id ổn định mà QC sẽ định vị theo.*
166
166
 
167
167
  ## Context
168
168
  **BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
@@ -172,6 +172,139 @@ Bỏ qua bước này thì `{paths.*}`, `{tech_stack.*}`, `{conventions.*}`, gua
172
172
  `project-lessons`, và routing service (chế độ umbrella) đều **chưa được phân giải** — mọi
173
173
  placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
174
174
 
175
+ ## Phạm vi QC
176
+
177
+ # QC Scope — phân giải phạm vi cho mọi lệnh `qc-*`
178
+
179
+ **Chạy TRƯỚC phần logic riêng của lệnh, và SAU `steps/gate.md`.** Bước này chốt bốn thứ mà
180
+ cả 6 trạm QC đều cần, để chúng không tự suy mỗi trạm một kiểu:
181
+
182
+ | Biến | Là gì |
183
+ |---|---|
184
+ | `TICKET-ID` | mã PRD — **thư mục artifact QC mang tên này** |
185
+ | `active_platform` | `web` \| `app` \| `system` \| … — một QC pass khoá đúng MỘT nền |
186
+ | `qc_artifact_dir` | `{paths.qc_dir}/{TICKET-ID}/{active_platform}/` |
187
+ | `uc_list` | các UC của (PRD × nền) này, kèm trạng thái BDD từng UC |
188
+
189
+ > **Vì sao gom về một chỗ.** Luật phân giải nền từng được copy-paste ở 5 lệnh và câu chữ đã
190
+ > lệch nhau. Năm bản của một luật là nơi drift sống: sửa bốn, quên một, và trạm bị quên ghi
191
+ > artifact vào sai thư mục **trong im lặng**.
192
+
193
+ ---
194
+
195
+ ## 1 — `TICKET-ID`
196
+
197
+ Artifact QC gom theo **PRD**, không theo UC. Nên mọi trạm phải quy được về `TICKET-ID`:
198
+
199
+ | `$ARGUMENTS` là | Cách lấy |
200
+ |---|---|
201
+ | **UC-ID** (`{TICKET-ID}-UC{N}`) | phần **trước** `-UC` — đúng luật `steps/gate.md` Bước 1 dùng để tìm tech-doc gộp |
202
+ | **TICKET-ID** | dùng trực tiếp |
203
+ | một **path file** (`.feature` / PRD / design-spec) | phân giải `{domain}` + `{prd-slug}` theo luật `context-loader` Bước 1, rồi lấy `TICKET-ID` từ tên file PRD `{TICKET-ID}-{prd-slug}.md` — file `.md` duy nhất ở gốc feature folder |
204
+
205
+ Đối chiếu: `TICKET-ID` suy ra phải khớp tên file PRD thật. Lệch → **DỪNG**, in cả hai giá
206
+ trị. (Suy sai `TICKET-ID` là ghi cả một PRD vào sai thư mục — không có bước nào phía sau bắt được.)
207
+
208
+ ---
209
+
210
+ ## 2 — `active_platform`
211
+
212
+ > **PHẢI phân giải TRƯỚC mọi phép đọc `.feature`.** `{UC-ID}-SC{N}` chỉ độc nhất trong
213
+ > (UC × nền), nên một UC đa nền có **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`,
214
+ > `bdd/system/` — và mỗi file mang `@trace.status` **riêng**: bản web có thể `approved`
215
+ > trong khi bản app còn `draft`. Đọc "file `.feature` của UC" khi chưa biết nền là đọc một
216
+ > file **bất kỳ trong ba**: báo `approved` trong khi bản đang dùng còn nháp, hoặc chặn oan
217
+ > một bản đã duyệt.
218
+
219
+ Theo thứ tự, dừng ở cái đầu tiên khớp:
220
+
221
+ 1. `$ARGUMENTS` nêu nền (`web`/`app`/`system`/…) → dùng.
222
+ 2. Target là một file `.feature` → đọc `# @trace.platform` của nó.
223
+ 3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/` — **đúng một** thư mục nền → dùng nó.
224
+ 4. Glob `{paths.qc_dir}/{TICKET-ID}/*/` — **đúng một** thư mục nền đã có artifact → dùng nó.
225
+ *(chỉ dùng cho trạm 2–6; trạm `/qc-analyze` là trạm tạo ra thư mục đó nên không có gì để soi.)*
226
+ 5. Nhiều nền mà không suy được → hỏi *"QC pass này cho nền nào? (web/app/system)"*.
227
+ **Có `--yes`:** không hỏi — DỪNG với lỗi rõ ràng, vì đoán bừa nền là ghi artifact vào sai
228
+ thư mục và ghi `qc_status` vào sai sổ trace:
229
+ ```
230
+ ❌ {TICKET-ID} có {n} nền ({list}) — không suy được nền nào cho QC pass này.
231
+ Chạy headless thì phải nêu tường minh: /{lệnh} {TICKET-ID} web --yes
232
+ ```
233
+
234
+ Lưu `active_platform`. Từ đây, **mọi** phép đọc `.feature` chỉ đọc thư mục
235
+ `bdd/{active_platform}/` — không trộn SC chéo nền.
236
+
237
+ ---
238
+
239
+ ## 3 — `qc_artifact_dir`
240
+
241
+ ```
242
+ qc_artifact_dir = {paths.qc_dir}/{TICKET-ID}/{active_platform}/
243
+ ```
244
+
245
+ Chứa: `REQUIREMENT_ANALYSIS.md` · `DOC_GAP.md` · `TEST_PLAN.md` · `test-cases/*.Test.md`
246
+ — **mỗi loại đúng MỘT file cho cả PRD**, các UC là mục/hàng bên trong.
247
+
248
+ `{paths.qc_dir}` là folder top-level **nhìn thấy** trong repo QC (mặc định `docs/`, **không**
249
+ phải `.agent/` ẩn) để đội QC mở và xử lý output dễ dàng. Spec chính thức ở lại spec submodule
250
+ của PO — đừng ghi artifact QC vào đó.
251
+
252
+ > **Sổ trace KHÔNG theo layout này.** Nó vẫn là `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv`
253
+ > — một sổ cho mỗi (UC × nền), vì mỗi hàng là một scenario. Liên kết giữa hai bên đi qua
254
+ > **cột `UC`** của bảng gap, không qua đường dẫn file.
255
+
256
+ ---
257
+
258
+ ## 4 — `uc_list`
259
+
260
+ Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/*.feature`. Mỗi file → một
261
+ UC: đọc `# @trace.id` (mã UC) và `# @trace.status` từ header.
262
+
263
+ Chia hai nhóm:
264
+
265
+ | Nhóm | Điều kiện | Xử lý |
266
+ |---|---|---|
267
+ | **Trong phạm vi** | `@trace.status: approved` | phân tích / thiết kế / chạy bình thường |
268
+ | **Chưa xét** | khác `approved` | **KHÔNG** phân tích; vẫn ghi một hàng vào bảng phạm vi kèm trạng thái thật |
269
+
270
+ In bảng phạm vi ra trước khi làm gì:
271
+ ```
272
+ Phạm vi QC — {TICKET-ID} / {active_platform}
273
+ ✅ {UC-ID} {tên UC} approved
274
+ ⏸ {UC-ID} {tên UC} draft → chưa xét
275
+ → {n} UC trong phạm vi · {m} chưa xét
276
+ ```
277
+
278
+ **Cờ `--include-draft`:** phân tích cả UC chưa duyệt, nhưng **vẫn in bảng trên** và đánh dấu
279
+ trong artifact là dựa trên BDD nháp.
280
+
281
+ **Không UC nào `approved` và không có `--include-draft` → DỪNG:**
282
+ ```
283
+ ❌ {TICKET-ID} ({active_platform}): 0/{n} UC có BDD approved — không có gì để chạy.
284
+ Cách đúng: người duyệt đặt `# @trace.status: approved` rồi chạy lại.
285
+ Muốn chạy sớm trên BDD nháp (prototype): thêm --include-draft
286
+ ```
287
+
288
+ > **Vì sao có `--include-draft` chứ không chặn cứng.** QC sớm trên BDD nháp là một cách dùng
289
+ > **cố ý được cho phép** từ trước (guard cũ là cảnh báo mềm, không phải chặn). Bỏ hẳn nó là
290
+ > lấy đi một năng lực đang có mà không ai khai. Còn để mặc định `approved`-only thì cái
291
+ > thường gặp là cái an toàn, và cái sớm phải nói ra.
292
+
293
+ > **Vì sao `--yes` không thay được `--include-draft`.** `--yes` nghĩa *"tôi không ngồi đây để
294
+ > trả lời"*; `--include-draft` nghĩa *"tôi biết BDD còn nháp và vẫn muốn chạy"*. Gộp hai cái
295
+ > là để một lần chạy headless âm thầm phân tích spec chưa chốt rồi bàn giao như thể đã chốt.
296
+
297
+
298
+ > **Trạm này vẫn gọi theo TỪNG UC** *(B11)* — thiết kế và chạy test **thật sự** làm tăng dần
299
+ > theo UC, nên giữ khả năng làm UC1 khi UC3 chưa xong là đúng. Chỉ **chỗ đọc/ghi** đổi: mọi
300
+ > artifact nằm chung ở `{qc_artifact_dir}` cấp PRD, không còn một thư mục mỗi UC.
301
+ >
302
+ > Nên `DOC_GAP.md` / `TEST_PLAN.md` đọc được ở đây phủ **cả PRD**: **lọc theo cột `UC`** để lấy
303
+ > phần của UC đang làm. Đừng coi toàn bộ bảng gap là của UC này — sẽ chặn oan.
304
+
305
+ ---
306
+
307
+
175
308
  ---
176
309
 
177
310
  ## Role
@@ -211,7 +344,13 @@ qc-run-test ghi `qc_status` theo từng scenario.
211
344
 
212
345
  ## Output
213
346
 
214
- Ghi các file `.Test.md` dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/test-cases/`.
347
+ Ghi các file `.Test.md` dưới `{qc_artifact_dir}test-cases/`
348
+ (= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/test-cases/`) — **một thư mục test-case dùng
349
+ chung cho cả PRD**. Tên file mang `<FEATURE>` nên các UC không đâm nhau; cột **Trace SC** là
350
+ chỗ phân biệt TC thuộc UC nào.
351
+
352
+ Dòng `🚫 Block: [GAP-UC{N}-{nnn}](DOC_GAP.md)` trỏ **liên kết tương đối một cấp lên**
353
+ (`DOC_GAP.md` nằm ở thư mục cha của `test-cases/`) — đúng như trước, layout đổi không ảnh hưởng.
215
354
 
216
355
  ## Report
217
356