@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
@@ -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 artifact dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/`): nếu `$ARGUMENTS` nêu platform (web/app/system) dùng; else glob `{paths.qc_dir}/{UC-ID}/*/` đúng 1 folder platform → dùng nó, nhiều folder → hỏi platform nào. Đọc output của qc-analyze (`REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`) từ `{paths.qc_dir}/{UC-ID}/{active_platform}/` và file `.feature` của đúng platform đó.*
165
+ *Lưu ý: Với lệnh này, target ở Bước 1 là một **TICKET-ID** ( PRD)hoặc UC-ID / file feature, cả hai quy về TICKET-ID §Phạm vi QC. Đọc output của qc-analyze (`REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`) từ `{qc_artifact_dir}` và file `.feature` của đúng nền đó.*
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 đó**,
@@ -174,6 +174,140 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
174
174
 
175
175
  ---
176
176
 
177
+ ## Phạm vi QC
178
+
179
+ # QC Scope — phân giải phạm vi cho mọi lệnh `qc-*`
180
+
181
+ **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à
182
+ cả 6 trạm QC đều cần, để chúng không tự suy mỗi trạm một kiểu:
183
+
184
+ | Biến | Là gì |
185
+ |---|---|
186
+ | `TICKET-ID` | mã PRD — **thư mục artifact QC mang tên này** |
187
+ | `active_platform` | `web` \| `app` \| `system` \| … — một QC pass khoá đúng MỘT nền |
188
+ | `qc_artifact_dir` | `{paths.qc_dir}/{TICKET-ID}/{active_platform}/` |
189
+ | `uc_list` | các UC của (PRD × nền) này, kèm trạng thái BDD từng UC |
190
+
191
+ > **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ữ đã
192
+ > 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
193
+ > artifact vào sai thư mục **trong im lặng**.
194
+
195
+ ---
196
+
197
+ ## 1 — `TICKET-ID`
198
+
199
+ Artifact QC gom theo **PRD**, không theo UC. Nên mọi trạm phải quy được về `TICKET-ID`:
200
+
201
+ | `$ARGUMENTS` là | Cách lấy |
202
+ |---|---|
203
+ | **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 |
204
+ | **TICKET-ID** | dùng trực tiếp |
205
+ | 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 |
206
+
207
+ Đố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á
208
+ 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.)
209
+
210
+ ---
211
+
212
+ ## 2 — `active_platform`
213
+
214
+ > **PHẢI phân giải TRƯỚC mọi phép đọc `.feature`.** `{UC-ID}-SC{N}` chỉ độc nhất trong
215
+ > (UC × nền), nên một UC đa nền có **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`,
216
+ > `bdd/system/` — và mỗi file mang `@trace.status` **riêng**: bản web có thể `approved`
217
+ > 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
218
+ > 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
219
+ > một bản đã duyệt.
220
+
221
+ Theo thứ tự, dừng ở cái đầu tiên khớp:
222
+
223
+ 1. `$ARGUMENTS` nêu nền (`web`/`app`/`system`/…) → dùng.
224
+ 2. Target là một file `.feature` → đọc `# @trace.platform` của nó.
225
+ 3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/` — **đúng một** thư mục nền → dùng nó.
226
+ 4. Glob `{paths.qc_dir}/{TICKET-ID}/*/` — **đúng một** thư mục nền đã có artifact → dùng nó.
227
+ *(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.)*
228
+ 5. Nhiều nền mà không suy được → hỏi *"QC pass này cho nền nào? (web/app/system)"*.
229
+ **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
230
+ thư mục và ghi `qc_status` vào sai sổ trace:
231
+ ```
232
+ ❌ {TICKET-ID} có {n} nền ({list}) — không suy được nền nào cho QC pass này.
233
+ Chạy headless thì phải nêu tường minh: /{lệnh} {TICKET-ID} web --yes
234
+ ```
235
+
236
+ Lưu `active_platform`. Từ đây, **mọi** phép đọc `.feature` chỉ đọc thư mục
237
+ `bdd/{active_platform}/` — không trộn SC chéo nền.
238
+
239
+ ---
240
+
241
+ ## 3 — `qc_artifact_dir`
242
+
243
+ ```
244
+ qc_artifact_dir = {paths.qc_dir}/{TICKET-ID}/{active_platform}/
245
+ ```
246
+
247
+ Chứa: `REQUIREMENT_ANALYSIS.md` · `DOC_GAP.md` · `TEST_PLAN.md` · `test-cases/*.Test.md`
248
+ — **mỗi loại đúng MỘT file cho cả PRD**, các UC là mục/hàng bên trong.
249
+
250
+ `{paths.qc_dir}` là folder top-level **nhìn thấy** trong repo QC (mặc định `docs/`, **không**
251
+ phải `.agent/` ẩn) để đội QC mở và xử lý output dễ dàng. Spec chính thức ở lại spec submodule
252
+ của PO — đừng ghi artifact QC vào đó.
253
+
254
+ > **Sổ trace KHÔNG theo layout này.** Nó vẫn là `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv`
255
+ > — 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
256
+ > **cột `UC`** của bảng gap, không qua đường dẫn file.
257
+
258
+ ---
259
+
260
+ ## 4 — `uc_list`
261
+
262
+ Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/*.feature`. Mỗi file → một
263
+ UC: đọc `# @trace.id` (mã UC) và `# @trace.status` từ header.
264
+
265
+ Chia hai nhóm:
266
+
267
+ | Nhóm | Điều kiện | Xử lý |
268
+ |---|---|---|
269
+ | **Trong phạm vi** | `@trace.status: approved` | phân tích / thiết kế / chạy bình thường |
270
+ | **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 |
271
+
272
+ In bảng phạm vi ra trước khi làm gì:
273
+ ```
274
+ Phạm vi QC — {TICKET-ID} / {active_platform}
275
+ ✅ {UC-ID} {tên UC} approved
276
+ ⏸ {UC-ID} {tên UC} draft → chưa xét
277
+ → {n} UC trong phạm vi · {m} chưa xét
278
+ ```
279
+
280
+ **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
281
+ trong artifact là dựa trên BDD nháp.
282
+
283
+ **Không UC nào `approved` và không có `--include-draft` → DỪNG:**
284
+ ```
285
+ ❌ {TICKET-ID} ({active_platform}): 0/{n} UC có BDD approved — không có gì để chạy.
286
+ Cách đúng: người duyệt đặt `# @trace.status: approved` rồi chạy lại.
287
+ Muốn chạy sớm trên BDD nháp (prototype): thêm --include-draft
288
+ ```
289
+
290
+ > **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
291
+ > **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à
292
+ > 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
293
+ > thường gặp là cái an toàn, và cái sớm phải nói ra.
294
+
295
+ > **Vì sao `--yes` không thay được `--include-draft`.** `--yes` nghĩa *"tôi không ngồi đây để
296
+ > 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
297
+ > 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.
298
+
299
+
300
+ > **Trạm này chạy CẢ PRD, đúng như trạm 1** *(B11)* — một `TEST_PLAN.md` cho mỗi (PRD × nền).
301
+ > `qa-planner/test-plan.md` vốn viết *"Test Plan cho một **feature**"* và template của nó là
302
+ > `# Test Plan – <Feature>` với metadata `Feature / Project / Module`: đây là quay về đúng
303
+ > tầng mà skill gốc được viết cho.
304
+ >
305
+ > `uc_list` ở đây **phải khớp** bảng *Phạm vi phân tích* trong `DOC_GAP.md`. Lệch nhau nghĩa là
306
+ > BDD đã đổi trạng thái sau lần chạy trạm 1 → nêu ra và khuyên chạy lại `/qc-analyze`, đừng
307
+ > âm thầm lập plan cho một tập UC khác với tập đã phân tích.
308
+
309
+ ---
310
+
177
311
  ## Role
178
312
 
179
313
  Bạn là **QC Planner** — stage 2. Từ output của qc-analyze, tạo TEST PLAN:
@@ -192,9 +326,18 @@ thiết kế test case cụ thể (đó là qc-design-test).
192
326
 
193
327
  ## Output
194
328
 
195
- Ghi test plan vào `{paths.qc_dir}/{UC-ID}/{active_platform}/TEST_PLAN.md`. Giới hạn plan trong
196
- các scenario của UC (`{UC-ID}-SC{N}` từ `.feature` của platform này) để qc-design-test thiết kế
197
- case theo từng scenario.
329
+ Ghi **đúng MỘT** test plan cho cả PRD vào `{qc_artifact_dir}TEST_PLAN.md`
330
+ (= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/TEST_PLAN.md`). Giới hạn plan trong các
331
+ scenario của **các UC trong phạm vi** (`{UC-ID}-SC{N}` từ `.feature` của nền này) để
332
+ qc-design-test thiết kế case theo từng scenario.
333
+
334
+ Bắt buộc:
335
+ - **Bảng `§3 Test items` có cột `UC`** — một plan giờ phủ nhiều UC, không có cột đó thì không
336
+ ai biết vùng test nào thuộc UC nào.
337
+ - **`§2 Phạm vi`** liệt kê rõ UC `⏸ Chưa xét` ở phần *Out of scope*, kèm lý do (BDD chưa
338
+ approved). *Không có dòng này thì một UC bị bỏ trông giống một UC không có gì để test.*
339
+ - **`§5 Entry criteria` chặn theo từng UC**, không chặn cả PRD: gap 🔴 Blocker ở UC3 không
340
+ dừng việc thiết kế test cho UC1. Ghi `Ready` / `Blocked` cho **mỗi** UC.
198
341
 
199
342
  ## Report
200
343
 
@@ -202,8 +345,11 @@ case theo từng scenario.
202
345
  Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
203
346
 
204
347
  ```
205
- /qc-plan Hoàn tất — {UC-ID}
348
+ /qc-plan Hoàn tất — {TICKET-ID} ({active_platform})
349
+ Phạm vi: {n} UC trong plan{nếu có: " · ⏸ {m} chưa xét"}
206
350
  Plan: {risks} rủi ro · {questions} câu hỏi mở cho dev · layers: {list}
207
- Next: /qc-design-test {UC-ID} ← thiết kế test case
208
- (gửi questions-for-dev cho PO/Dev nếu còn blocker)
351
+ File: {paths.qc_dir}/{TICKET-ID}/{active_platform}/TEST_PLAN.md
352
+ Sẵn sàng: {danh sách UC Ready} | Chặn: {danh sách UC Blocked + GAP-ID chặn nó}
353
+ Next: /qc-design-test {UC-ID} ← thiết kế test case, chạy cho từng UC đã Ready
354
+ (gửi questions-for-dev cho PO/Dev cho các UC còn Blocked)
209
355
  ```
@@ -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. Phát hiện review mode từ `$ARGUMENTS`/context: review test-case `.Test.md` (sau design) hoặc review Python script (sau run). Đọc artifact từ `{paths.qc_dir}/{UC-ID}/{active_platform}/` và source test/page-object đã sinh.*
165
+ *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID (`active_platform` + `qc_artifact_dir` do §Phạm vi QC phân giải). Phát hiện review mode từ `$ARGUMENTS`/context: review test-case `.Test.md` (sau design) hoặc review Python script (sau run). Đọc artifact từ `{qc_artifact_dir}` và source test/page-object đã sinh.*
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
@@ -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. `active_platform` này CHÍNH LÀ platform của sổ trace sẽ ghi `qc_status` (`{UC-ID}-{active_platform}.tsv`) — nên `@trace.verifies={UC-ID}-SC{N}` resolve không nhập nhằng. Đọc `.Test.md` đã review từ `{paths.qc_dir}/{UC-ID}/{active_platform}/test-cases/`. Lệnh này dùng stack module **qc-playwright** (`.agent/modules/qc-playwright/stack-profile.yaml`) — Python + pytest-playwright + Page Object — độc lập với module implementation của dev.*
165
+ *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID (`active_platform` + `qc_artifact_dir` do §Phạm vi QC phân giải). `active_platform` này CHÍNH LÀ platform của sổ trace sẽ ghi `qc_status` (`{UC-ID}-{active_platform}.tsv`) — nên `@trace.verifies={UC-ID}-SC{N}` resolve không nhập nhằng. Đọc `.Test.md` đã review từ `{qc_artifact_dir}test-cases/`. Lệnh này dùng stack module **qc-playwright** (`.agent/modules/qc-playwright/stack-profile.yaml`) — Python + pytest-playwright + Page Object — độc lập với module implementation của dev.*
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 & stack (theo module qc-playwright)
@@ -19,11 +19,11 @@ architecture:
19
19
  - "Each test independent via pytest-playwright fixtures (page / logged_in_page / …)"
20
20
  - "Page Object extends slim BasePage; split 3 layers: locators _x(), actions verb_noun(), assertions assert_x() using expect()"
21
21
  - "Locator priority: data-testid → role → label/text → CSS → avoid XPath"
22
- - "test-id values come from the FE tech-design §2b Test Selectors contract ({UC-ID}-tech-design-{platform}.md) — prefer them (no runtime scan); fall back to role/text only when an actionable element has no test-id there, and note the gap"
22
+ - "test-id values come from the FE tech-design §2b Test Selectors contract (tech-doc gộp cấp PRD: {TICKET-ID}-tech-design.md, bảng Test Selectors §4.5.6 — lọc theo cột "Serves SC") — prefer them (no runtime scan); fall back to role/text only when an actionable element has no test-id there, and note the gap"
23
23
  - "Group tests by (role, account) so login/logout never interleaves across roles"
24
24
  - "Cover 100% of TCs in the .Test.md — every TC ends Pass/Fail/Skip, none left Draft"
25
25
  folder_structure: |
26
- {paths.qc_dir}/{UC-ID}/test-cases/ ← test-case Markdown (.Test.md) — source of truth (mặc định docs/, lộ ra ngoài)
26
+ {qc_artifact_dir}test-cases/ ← test-case Markdown (.Test.md) — source of truth (mặc định docs/, lộ ra ngoài)
27
27
  pages/ ← Page Object Model
28
28
  │ ├── base_page.py ← slim BasePage (click/fill/wait/screenshot)
29
29
  │ └── <feature>_page.py
@@ -42,7 +42,7 @@ coding_standards:
42
42
  test_function: "test_TC<NNN>_<snake_case>"
43
43
  page_object: "<feature>_page.py with <Feature>Page class extending BasePage"
44
44
  files:
45
- test_case_md: "{paths.qc_dir}/{UC-ID}/test-cases/TC_<FEATURE>.Test.md"
45
+ test_case_md: "{qc_artifact_dir}test-cases/TC_<FEATURE>.Test.md"
46
46
  page_object: "pages/<feature>_page.py"
47
47
  test_script: "tests/<project>/test_<feature>.py"
48
48
  patterns:
@@ -13,13 +13,18 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
13
13
  > **Một chỗ CỐ Ý khác upstream:** mức nặng nhất dùng từ **`Blocker`**, không phải `Critical`.
14
14
  > Lý do: `/qc-run-test` đọc `🔴 Blocker` để đặt *"scenario đang chờ PO"* vào sổ kết quả trace.
15
15
  > Đổi từ là đứt liên kết đó. Ba mức còn lại giữ nguyên upstream.
16
+ >
17
+ > **Phạm vi: MỘT file cho cả (PRD × nền)** *(B11)*, không phải một file mỗi UC. Đây là quay về
18
+ > đúng thiết kế upstream — chính ID `GAP-<UC>-…` của bản gốc chỉ có nghĩa khi một file chứa
19
+ > nhiều UC, và file thật của đội QC (`DOC_GAP_FEAT-02-3.md`) cũng không có hậu tố UC. Đường
20
+ > dẫn: `{paths.qc_dir}/{TICKET-ID}/{platform}/DOC_GAP.md`.
16
21
 
17
- # DOC GAP -- <UC>: <Tên UC>
22
+ # DOC GAP -- <TICKET-ID> / <nền>: <Tên feature>
18
23
 
19
24
  | Trường | Giá trị |
20
25
  |---|---|
21
- | Feature | `<FEATURE>` |
22
- | UC | `<UC> <Tên UC>` |
26
+ | Feature | `<TICKET-ID>` — `<Tên feature>` |
27
+ | Nền (platform) | `<web \| app \| system>` |
23
28
  | Tài liệu nguồn | `<đường dẫn PRD>` · `<đường dẫn BDD nếu có>` · `<các file inputs/ liên quan>` |
24
29
  | Ngày phân tích | `<YYYY-MM-DD>` |
25
30
  | Tổng số gap | `<N>` (Blocker: x · High: y · Medium: z · Low: w) |
@@ -27,6 +32,21 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
27
32
 
28
33
  ---
29
34
 
35
+ ## Phạm vi phân tích
36
+
37
+ > **Một file gap phủ CẢ PRD** — mỗi UC là các hàng trong bảng gap, phân biệt bằng cột `UC`.
38
+ > Bảng dưới là **căn cứ độ phủ**: nó phân biệt *"đã xét, không thấy gap"* với *"chưa xét"*.
39
+ > UC nào có BDD chưa `approved` thì ghi `⏸ Chưa xét` — **KHÔNG bỏ khỏi bảng**.
40
+
41
+ | UC | Tên UC | `@trace.status` | Đã phân tích? | Số gap |
42
+ |---|---|---|---|---|
43
+ | `<UC-ID>` | … | `approved` | ✅ | `<n>` |
44
+ | `<UC-ID>` | … | `draft` | `⏸ Chưa xét` | — |
45
+
46
+ **Trong phạm vi: `<n>`/`<N>` UC.** Chưa xét: `<danh sách UC-ID>` — chạy lại sau khi BDD được duyệt, hoặc `--include-draft` để xét luôn bản nháp.
47
+
48
+ ---
49
+
30
50
  ## Tài liệu đầu vào đã đọc để phân tích
31
51
 
32
52
  > Liệt kê **đầy đủ** mọi file đã đọc để dựng phân tích gap (spec chính + mọi ref-link + transitive 1-hop). Đây là căn cứ độ phủ — mọi file đã mở đều phải có mặt, KHÔNG bỏ sót. Đường dẫn tính từ `{paths.specs_dir}`.
@@ -41,9 +61,14 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
41
61
 
42
62
  ---
43
63
 
44
- | ID | Loại | Vấn đề cần confirm | Câu hỏi / Lý do cần confirm & Gợi ý | Trích đoạn tài liệu (Evidence) | Giao cho đội | Mức độ | Người trả lời | Trạng thái | Câu trả lời |
45
- |---|---|---|---|---|---|---|---|---|---|
46
- | GAP-<UC>-001 | MISSING / AMBIGUOUS / CONTRADICTORY / ASSUMPTION | Tiêu đề ngắn mô tả vấn đề | **Bối cảnh:** Ngữ cảnh dẫn đến gap này.<br/>**Vấn đề:** Điều gì chưa được spec hoặc mâu thuẫn.<br/>**Tại sao quan trọng:** Hậu quả nếu không làm rõ.<br/>**Gợi ý:** Ai cần làm để giải quyết. | `<đường dẫn file spec>` `<TÊN-BR hoặc AC gốc trong PRD, ví dụ FEAT-01-2-UC4-BR11>`: trích nguyên văn | Dev / PO / BA / Design | 🔴 Blocker | | Open | |
64
+ > **ID gap mang UC** `GAP-UC{N}-{nnn}` (vd `GAP-UC1-001`). Gap thuộc **cả PRD**, không riêng
65
+ > UC nào → `GAP-GEN-{nnn}`. Đánh số **độc lập trong từng UC**: chạy lại phân tích cho UC1
66
+ > KHÔNG được làm đổi số gap của UC2 test case đã đang trỏ `🚫 Block: [GAP-UC2-003]`, đổi
67
+ > số là đứt liên kết đó trong im lặng.
68
+
69
+ | ID | UC | Loại | Vấn đề cần confirm | Câu hỏi / Lý do cần confirm & Gợi ý | Trích đoạn tài liệu (Evidence) | Giao cho đội | Mức độ | Người trả lời | Trạng thái | Câu trả lời |
70
+ |---|---|---|---|---|---|---|---|---|---|---|
71
+ | GAP-UC1-001 | `<UC-ID>` | MISSING / AMBIGUOUS / CONTRADICTORY / ASSUMPTION | Tiêu đề ngắn mô tả vấn đề | **Bối cảnh:** Ngữ cảnh dẫn đến gap này.<br/>**Vấn đề:** Điều gì chưa được spec hoặc mâu thuẫn.<br/>**Tại sao quan trọng:** Hậu quả nếu không làm rõ.<br/>**Gợi ý:** Ai cần làm gì để giải quyết. | `<đường dẫn file spec>` `<TÊN-BR hoặc AC gốc trong PRD, ví dụ FEAT-01-2-UC4-BR11>`: trích nguyên văn | Dev / PO / BA / Design | 🔴 Blocker | | Open | |
47
72
 
48
73
  ---
49
74
 
@@ -51,13 +76,15 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
51
76
 
52
77
  | Mức độ | Gap ID |
53
78
  |---|---|
54
- | 🔴 Blocker | GAP-<UC>-00x · ... |
55
- | 🟠 High | GAP-<UC>-00x · ... |
56
- | 🟡 Medium | GAP-<UC>-00x · ... |
57
- | ⚪ Low | GAP-<UC>-00x · ... |
79
+ | 🔴 Blocker | GAP-UC1-00x · GAP-UC3-00x · ... |
80
+ | 🟠 High | GAP-UC2-00x · ... |
81
+ | 🟡 Medium | GAP-UC1-00x · ... |
82
+ | ⚪ Low | GAP-GEN-00x · ... |
83
+
84
+ *Xếp theo mức độ trước, KHÔNG theo UC — người đọc cần biết "chặn gì" trước "chặn ở đâu".*
58
85
 
59
86
  **Cần chốt trước khi viết test case:**
60
- - GAP-<UC>-00x: <lý do block>
87
+ - GAP-UC1-00x (`<UC-ID>`): <lý do block>
61
88
 
62
89
  ---
63
90
 
@@ -99,16 +126,19 @@ upstream_sha: c7ca6cfb798c609f18ffe20a38f64f95c76e1919
99
126
  Trước khi lưu file gap, kiểm tra **từng hàng** trong bảng gap:
100
127
 
101
128
  - [ ] **Có section `Tài liệu đầu vào đã đọc để phân tích`** – bảng liệt kê **đầy đủ** mọi file đã đọc (spec chính + ref-link + transitive 1-hop), có đường dẫn `{paths.specs_dir}`, vai trò, phiên bản; ghi tổng số + trạng thái lane API. KHÔNG bỏ sót file nào đã mở.
102
- - [ ] **10 cột đủ** đúng thứ tự: `ID | Loại | Vấn đề cần confirm | Câu hỏi / do cần confirm & Gợi ý | Trích đoạn tài liệu (Evidence) | Giao cho đội | Mức độ | Người trả lời | Trạng thái | Câu trả lời`
103
- - [ ] **Cột 3 = `Vấn đề cần confirm`** KHÔNG viết tắt thành `Vấn đề`
104
- - [ ] **Cột 4 = `Câu hỏi / do cần confirm & Gợi ý`** bắt buộcđủ 4 phần, tách bằng `<br/>`:
129
+ - [ ] ** section `Phạm vi phân tích`** bảng mỗi UC một hàng kèm `@trace.status` + Đã phân tích? + Số gap. UC chưa duyệt vẫn hàng, ghi `⏸ Chưa xét`. Thiếu bảng này thì không ai phân biệt được *"đã xét, không thấy gap"* với *"chưa xét"*.
130
+ - [ ] **11 cột đủ** – đúng thứ tự: `ID | UC | Loại | Vấn đề cần confirm | Câu hỏi / do cần confirm & Gợi ý | Trích đoạn tài liệu (Evidence) | Giao cho đội | Mức độ | Người trả lời | Trạng thái | Câu trả lời`
131
+ - [ ] **Cột 2 = `UC`** UC đầy đủ (`<TICKET-ID>-UC{N}`), hoặc `— (toàn PRD)` cho gap `GAP-GEN-`. Mọi UC gap phải khớp một hàng `✅` ở bảng *Phạm vi phân tích*.
132
+ - [ ] **ID dạng `GAP-UC{N}-{nnn}`** (hoặc `GAP-GEN-{nnn}`) – KHÔNG dùng `GAP-01` phẳng: số phẳng sẽ bị đánh lại khi phân tích lại một UC, làm đứt `🚫 Block: [GAP-xx]` trong test case đã có.
133
+ - [ ] **Cột 4 = `Vấn đề cần confirm`** – KHÔNG viết tắt thành `Vấn đề`
134
+ - [ ] **Cột 5 = `Câu hỏi / Lý do cần confirm & Gợi ý`** – bắt buộc có đủ 4 phần, tách bằng `<br/>`:
105
135
  ```
106
136
  **Bối cảnh:** ...<br/>**Vấn đề:** ...<br/>**Tại sao quan trọng:** ...<br/>**Gợi ý:** ...
107
137
  ```
108
138
  Không viết 4 mục liên tiếp trên cùng một dòng.
109
- - [ ] **Cột 5 = `Trích đoạn tài liệu (Evidence)`** – KHÔNG viết tắt thành `Evidence`; dẫn nguyên văn + đường dẫn file `{paths.specs_dir}`; **dùng PRD source ID** (ví dụ `FEAT-01-2-UC4-BR11`), KHÔNG dùng internal analysis ID (ví dụ `BR-UC4-12`)
110
- - [ ] **Cột Mức độ** (cột 7) – bắt buộc dùng emoji: `🔴 Blocker` / `🟠 High` / `🟡 Medium` / `⚪ Low`. Không được ghi text thuần.
111
- - [ ] **Cột Giao cho đội** (cột 6) – dùng: `Dev` / `PO` / `BA` / `Design` / `Architect` / `Analytics` hoặc kết hợp. Nếu thấy "Open" ở đây → đang bị lệch cột.
139
+ - [ ] **Cột 6 = `Trích đoạn tài liệu (Evidence)`** – KHÔNG viết tắt thành `Evidence`; dẫn nguyên văn + đường dẫn file `{paths.specs_dir}`; **dùng PRD source ID** (ví dụ `FEAT-01-2-UC4-BR11`), KHÔNG dùng internal analysis ID (ví dụ `BR-UC4-12`)
140
+ - [ ] **Cột Mức độ** (cột 8) – bắt buộc dùng emoji: `🔴 Blocker` / `🟠 High` / `🟡 Medium` / `⚪ Low`. Không được ghi text thuần.
141
+ - [ ] **Cột Giao cho đội** (cột 7) – dùng: `Dev` / `PO` / `BA` / `Design` / `Architect` / `Analytics` hoặc kết hợp. Nếu thấy "Open" ở đây → đang bị lệch cột.
112
142
  - [ ] **Cột Người trả lời = vai trò** (PO / BA / Dev / Design / ...). Nếu thấy "Open" ở đây → đang bị lệch cột.
113
143
  - [ ] **Cột Trạng thái** = `Open` / `Resolved` / `Out of Scope` / `Re-scoped → Covered`
114
144
  - [ ] **Trạng thái chung** trong metadata có emoji: `🔴 Blocked` / `🟠 Cần làm rõ` / `🟢 Đủ rõ để thiết kế TC`
@@ -53,7 +53,7 @@ Mỗi AC gắn mã trace: chức năng + BR-xx để TC sau này map 1-1.
53
53
 
54
54
  ## Output
55
55
 
56
- Ghi vào **mục Acceptance Criteria** của `{paths.qc_dir}/{UC-ID}/REQUIREMENT_ANALYSIS.md`
56
+ Ghi vào **mục Acceptance Criteria** của `{qc_artifact_dir}REQUIREMENT_ANALYSIS.md`
57
57
  (KHÔNG tạo file riêng — qc-analyze chỉ trả 2 file: `REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`):
58
58
 
59
59
  - Danh sách AC dạng Given/When/Then, có mã trace về chức năng, business rule (BR-xx)