@educa-corp/sdd-framework 0.9.3 → 0.9.4

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 (59) hide show
  1. package/bin/build.js +11 -0
  2. package/bin/qc-base-map.json +119 -49
  3. package/bin/self-check.js +30 -0
  4. package/core/FRAMEWORK_VERSION +1 -1
  5. package/core/commands/qc-analyze.md +6 -119
  6. package/core/commands/qc-design-test.md +123 -143
  7. package/core/commands/qc-plan.md +6 -119
  8. package/core/commands/qc-review.md +59 -125
  9. package/core/commands/qc-run-test.md +6 -119
  10. package/core/commands/setup-ai-first.md +5 -5
  11. package/core/commands/update-framework.md +1 -1
  12. package/core/commands/validate-traces.md +1 -1
  13. package/core/rules/workflow.md +1 -1
  14. package/core/skills/qc/qa-analyst/DOC_GAP.template.md +1 -1
  15. package/core/skills/qc/qa-analyst/spec-breakdown.md +2 -2
  16. package/core/skills/qc/qa-designer/api/auth-chain.md +155 -0
  17. package/core/skills/qc/qa-designer/api/auth-sequence.md +75 -0
  18. package/core/skills/qc/qa-designer/api/common-headers.md +61 -0
  19. package/core/skills/qc/qa-designer/api/crud-sequence.md +122 -0
  20. package/core/skills/qc/qa-designer/api/endpoint.md +231 -0
  21. package/core/skills/qc/qa-designer/api/http-status-codes.md +102 -0
  22. package/core/skills/qc/qa-designer/e2e/journey.md +13 -8
  23. package/core/skills/qc/qa-designer/exploratory/charter.md +2 -0
  24. package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +7 -4
  25. package/core/skills/qc/qa-designer/functional/api.md +87 -18
  26. package/core/skills/qc/qa-designer/functional/gui-feature.md +12 -9
  27. package/core/skills/qc/qa-designer/functional/gui-screen.md +12 -10
  28. package/core/skills/qc/qa-designer/integration/api.md +12 -5
  29. package/core/skills/qc/qa-designer/integration/db.md +12 -6
  30. package/core/skills/qc/qa-designer/integration/gui.md +12 -5
  31. package/core/skills/qc/qa-designer/integration/kafka.md +12 -5
  32. package/core/skills/qc/qa-designer/non-functional.md +12 -5
  33. package/core/skills/qc/qa-designer/shared/action-keywords-glossary.md +91 -0
  34. package/core/skills/qc/qa-designer/shared/duplicate-check-procedure.md +105 -0
  35. package/core/skills/qc/qa-designer/shared/implicit-scenarios.md +22 -0
  36. package/core/skills/qc/qa-designer/shared/precision-rules.md +198 -0
  37. package/core/skills/qc/qa-designer/shared/read-doc-gap-inputs.md +25 -0
  38. package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +93 -0
  39. package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +243 -0
  40. package/core/skills/qc/qa-planner/risk-model.md +1 -1
  41. package/core/skills/qc/qa-reviewer/script/e2e.md +9 -1
  42. package/core/skills/qc/qa-reviewer/script/exploratory.md +9 -1
  43. package/core/skills/qc/qa-reviewer/script/functional.md +9 -1
  44. package/core/skills/qc/qa-reviewer/script/integration.md +9 -1
  45. package/core/skills/qc/qa-reviewer/script/non-functional.md +9 -1
  46. package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +26 -0
  47. package/core/skills/qc/qa-reviewer/shared/review-check-groups.md +207 -0
  48. package/core/skills/qc/qa-reviewer/shared/review-file-template.md +228 -0
  49. package/core/skills/qc/qa-reviewer/test-case/e2e.md +71 -13
  50. package/core/skills/qc/qa-reviewer/test-case/exploratory.md +53 -4
  51. package/core/skills/qc/qa-reviewer/test-case/functional.md +63 -15
  52. package/core/skills/qc/qa-reviewer/test-case/integration.md +64 -12
  53. package/core/skills/qc/qa-reviewer/test-case/non-functional.md +72 -13
  54. package/core/skills/qc/qa-runner/e2e.md +1 -1
  55. package/docs/02-concepts/pipeline-steps/09-validate-traces.md +1 -1
  56. package/docs/04-reference/trace-schema.md +1 -1
  57. package/docs/explain/00-setup-ai-first.md +1 -1
  58. package/docs/plans/qc-implementation-log.md +145 -3
  59. package/package.json +1 -1
@@ -174,126 +174,13 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
174
174
 
175
175
  ## Phạm vi QC
176
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.
177
+ **BẮT BUỘCđọc `.agent/steps/qc-scope.md` thực thi TOÀN BỘ quy trình trong đó**,
178
+ rồi mới tiếp tục phần bên dưới.
296
179
 
180
+ Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
181
+ `qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--include-draft`).
182
+ Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —
183
+ cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
297
184
 
298
185
  > **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
186
  > 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
@@ -318,16 +205,61 @@ tồn tại cho UC và mới hơn → review script. Nếu mơ hồ, hỏi.
318
205
 
319
206
  ## Skills (`{paths.qc_skills_dir}/qa-reviewer/`)
320
207
 
321
- Chọn theo mode + layer, nạp MỘT file:
322
- - Review test-case: `test-case/{functional,e2e,integration,non-functional,exploratory}.md`
323
- - Review script: `script/{functional,e2e,integration,non-functional,exploratory}.md`
208
+ **Nạp trước nhưng đúng vai:**
209
+
210
+ | File | Vai nào | Cho gì |
211
+ |---|---|---|
212
+ | `shared/read-doc-gap-inputs.md` | **cả hai** | Nạp **đủ** mọi tài liệu nguồn theo bảng *Tài liệu đầu vào đã đọc* của `DOC_GAP.md` — soát mù thì kết luận "0 lỗi" là vô nghĩa |
213
+ | `shared/review-file-template.md` | **cả hai** | Khuôn file kết quả · thang điểm `XX/100` · luật ghi file · nguyên tắc *mặc định hoài nghi* |
214
+ | ↳ §*Kiểm tra cấu trúc TC* của file đó | **chỉ vai 1** | Tầng kiểm các luật của trạm 3: ATOMIC 1-bullet · không ký tự `\|` · cụm từ mơ hồ · teardown đúng chỗ · marker ≠ oracle |
215
+ | `shared/review-check-groups.md` | **chỉ vai 1** | **7 nhóm kiểm tra** chạy tuần tự · registry 46 nhãn lỗi · **7 mẫu-hay-thiếu** của Nhóm 6 · nền tảng ISTQB · anti-pattern |
216
+
217
+ > ⚠️ **Vai 2 (soát code) KHÔNG nạp 7 nhóm kiểm tra.** Chúng kiểm **cấu trúc file test case** —
218
+ > áp vào file Python là bảo reviewer đi tìm `#### Expected Result` trong một Page Object, rồi
219
+ > gắn nhãn lỗi cho thứ đáng lẽ không có ở đó. Vai 2 dùng bộ tiêu chí riêng ở `script/*.md`.
220
+
221
+ **Rồi chọn theo vai + tầng, nạp MỘT file:**
222
+ - Soát test-case: `test-case/{functional,e2e,integration,non-functional,exploratory}.md`
223
+ - Soát script: `script/{functional,e2e,integration,non-functional,exploratory}.md`
324
224
 
325
225
  ## Review focus
326
226
 
327
227
  - **Test-case:** mọi `{UC-ID}-SC{N}` đã phủ? happy + negative + boundary? expected cụ thể? trace (`BR-xx` + `@trace.verifies` SC) có mặt? không có TC orphan?
328
228
  - **Script:** khớp `.Test.md` 1-1? Page Object 3 lớp + BasePage gọn? `expect()` không phải bare assert? không hard-code URL/cred/timeout, không `time.sleep`, không Allure? selector theo priority (data-testid→role→…)? 100% TC đã script (không còn Draft)?
329
229
 
330
- Sinh findings (severity + vị trí + fix). Verdict APPROVED hoặc NEEDS_FIX.
230
+ Sinh findings (mức độ + vị trí + cách sửa), chấm điểm, ra verdict.
231
+
232
+ **KHÔNG tự sửa** file TC hay code. Chỉ nhận xét và chấm điểm — người sửa là `/qc-design-test`
233
+ (vai 1) hoặc `/qc-run-test` (vai 2). *Reviewer tự sửa rồi tự duyệt là bỏ mất cái cổng.*
234
+
235
+ ## Output
236
+
237
+ Ghi vào **`{qc_artifact_dir}test-cases/REVIEW_<FEATURE>.md`** — đứng **cạnh** file TC vừa soát:
238
+
239
+ ```
240
+ {qc_artifact_dir}test-cases/
241
+ ├── TC_<FEATURE>.Test.md
242
+ ├── REVIEW_<FEATURE>.md ← soát file trên
243
+ ├── TC_<FEATURE>_API.Test.md
244
+ └── REVIEW_<FEATURE>_API.md ← soát file trên
245
+ ```
246
+
247
+ ⚠️ **Tên file review KHÔNG có đuôi `.Test.md`.** Đuôi đó là của file test case; gắn vào file
248
+ review sẽ làm `/qc-run-test` nhặt nó lên như một file test case rồi cố sinh script từ một bảng điểm.
249
+
250
+ Khuôn đầy đủ + quy tắc ghi: `shared/review-file-template.md`. Ba điều bắt buộc:
251
+
252
+ - **Điểm `XX/100`** — trừ 5đ mỗi `FAIL`, 2đ mỗi `WARN`. Verdict **suy ra được**:
253
+ `≥80` VÀ không còn `FAIL` chặn → `APPROVED`; ngược lại `NEEDS_FIX`.
254
+ - **Bảng Tổng quan THÊM một hàng mỗi vòng**, không ghi đè — đó là cách duy nhất thấy được điểm
255
+ có tăng không. Các bảng chi tiết thì ghi đè phần của tầng mình.
256
+ - **Hai vai ghi vào CÙNG một file**, phân biệt bằng cột `Tầng`. Chúng cách nhau một trạm, nên
257
+ hàng của vai sau **không được ghi đè** hàng của vai trước.
258
+
259
+ > **Vì sao cổng này phải ghi ra file** *(B13)*. Trước đây lệnh chỉ nói *"sinh findings"* — không
260
+ > nói ghi đâu, dạng gì, và điểm là `A/B/C/D` **không có luật chấm**. Hệ quả: hai người soát ra
261
+ > hai kết quả, và **vòng 2 không so được với vòng 1** — tức không ai biết sửa xong có tốt lên
262
+ > không. Một cổng không để lại dấu vết đo được thì không phải cổng, nó là một lượt đọc.
331
263
 
332
264
  ## Report
333
265
 
@@ -335,8 +267,10 @@ Sinh findings (severity + vị trí + fix). Verdict APPROVED hoặc NEEDS_FIX.
335
267
  Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
336
268
 
337
269
  ```
338
- /qc-review Hoàn tất — {UC-ID} ({test-case | script} review)
339
- Verdict: {APPROVED | NEEDS_FIX} {N} findings ({crit} critical)
270
+ /qc-review Hoàn tất — {UC-ID} ({soát test-case | soát script}) vòng #{N}
271
+ Điểm : {XX}/100 ({fail} FAIL × −5đ · {warn} WARN × −2đ){nếu có vòng trước: " ← vòng #{N-1}: {YY}/100"}
272
+ Verdict: {APPROVED | NEEDS_FIX} — {n} findings ({crit} chặn)
273
+ File : {qc_artifact_dir}test-cases/REVIEW_<FEATURE>.md (thêm 1 hàng vào bảng Tổng quan)
340
274
  Next (test-case APPROVED): /qc-run-test {UC-ID}
341
275
  Next (script APPROVED) : /qc-report {UC-ID} rồi tạo PR
342
276
  (NEEDS_FIX → fix artifact bị gắn cờ, rồi chạy lại /qc-review {UC-ID})
@@ -174,126 +174,13 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
174
174
 
175
175
  ## Phạm vi QC
176
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.
177
+ **BẮT BUỘCđọc `.agent/steps/qc-scope.md` thực thi TOÀN BỘ quy trình trong đó**,
178
+ rồi mới tiếp tục phần bên dưới.
296
179
 
180
+ Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
181
+ `qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--include-draft`).
182
+ Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —
183
+ cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
297
184
 
298
185
  > **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
186
  > 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
@@ -448,13 +448,13 @@ Hướng dẫn: "Mở `specs/domain-knowledge/core-entities.md` và định ngh
448
448
 
449
449
  ## Step 6 — Cài VS Code Extension (Khuyến nghị)
450
450
 
451
- Khuyến nghị user cài extension VS Code **Spec Driven Docs Tools** — nó cung cấp panel Review Board + Living Documentation tích hợp với workflow này.
451
+ Khuyến nghị user cài extension VS Code **SDD Board** — nó cung cấp panel Review Board + Living Documentation tích hợp với workflow này.
452
452
 
453
453
  ```bash
454
- code --install-extension SpecDrivenDocsTools.spec-driven-docs-tool
454
+ code --install-extension EducaCorp.sdd-board
455
455
  ```
456
456
 
457
- Hoặc: VS Code → `Ctrl+Shift+P` → **"Extensions: Install from Marketplace"** → tìm **Spec Driven Docs Tools**.
457
+ Hoặc: VS Code → `Ctrl+Shift+P` → **"Extensions: Install from Marketplace"** → tìm **SDD Board**.
458
458
 
459
459
  **Nó làm gì:**
460
460
  - 📋 **Review Board** — UI trực quan để review findings từ `/refine-prd`, `/review-context`, `/review-tech-docs`
@@ -557,7 +557,7 @@ Next:
557
557
  4. Điền specs/domain-knowledge/core-entities.md
558
558
  5. git add và commit 4 file đó
559
559
  6. Cài VS Code extension:
560
- code --install-extension SpecDrivenDocsTools.spec-driven-docs-tool
560
+ code --install-extension EducaCorp.sdd-board
561
561
  7. /define-product để bắt đầu feature đầu tiên
562
562
  ```
563
563
 
@@ -591,7 +591,7 @@ Next:
591
591
  3. Điền specs/domain-knowledge/core-entities.md ← entity glossary
592
592
  4. git add và commit các file đó
593
593
  5. Cài VS Code extension:
594
- code --install-extension SpecDrivenDocsTools.spec-driven-docs-tool
594
+ code --install-extension EducaCorp.sdd-board
595
595
  6. /define-product để bắt đầu feature đầu tiên
596
596
 
597
597
  ⚠️ Nhắc handoff team dev:
@@ -193,7 +193,7 @@ Your content was preserved:
193
193
  Review & commit:
194
194
  git diff .agent/
195
195
  git add .agent/ .claude/commands/
196
- git commit -m "chore: upgrade spec-driven-docs v{current} → v{latest}"
196
+ git commit -m "chore: upgrade sdd-framework v{current} → v{latest}"
197
197
  {umbrella mode: this is the umbrella root — service submodules need no framework update}
198
198
 
199
199
  ---
@@ -1223,7 +1223,7 @@ Schema:
1223
1223
  - `test_classes`: dùng `[]` (không phải `"—"`) khi không có test class
1224
1224
  - `tech_doc_revision` / `fe_tech_doc_revision`: dùng integer; `0` nếu chưa sinh
1225
1225
  - `code_coverage_pct` / `test_coverage_pct`: làm tròn về integer gần nhất (0–100)
1226
- - **`status` trong JSON CỐ TÌNH chỉ có 4 giá trị** `OK`/`DRIFT`/`GAP`/`UNTRACKED` — KHÔNG ghi `ORPHANED` vào field này. VS Code extension "Spec Driven Docs Tools" (sống **ngoài** repo này) switch trên `status`; thêm giá trị thứ 5 sẽ rơi vào nhánh không khớp và có thể làm row mất khỏi panel.
1226
+ - **`status` trong JSON CỐ TÌNH chỉ có 4 giá trị** `OK`/`DRIFT`/`GAP`/`UNTRACKED` — KHÔNG ghi `ORPHANED` vào field này. VS Code extension "SDD Board" (sống **ngoài** repo này) switch trên `status`; thêm giá trị thứ 5 sẽ rơi vào nhánh không khớp và có thể làm row mất khỏi panel.
1227
1227
  Row `ORPHANED` xuất ra JSON là: `"status": "DRIFT"` + `"orphaned": true`. Panel chưa hỗ trợ vẫn hiện nó như `DRIFT` — đủ đúng về nghĩa ("code không khớp spec, cần xử lý") và **không im lặng**; panel có đọc `orphaned` thì hiện nhãn riêng. Chi tiết đầy đủ luôn có ở `orphaned[]` và ở report terminal.
1228
1228
  **TSV giữ nguyên chữ `ORPHANED`** trong cột `status` — TSV là nguồn-sự-thật, JSON chỉ là bản xuất cho panel.
1229
1229
  - `orphaned` (boolean): `true` chỉ khi cột `status` của TSV là `ORPHANED`; mọi row khác ghi `false` (đừng bỏ trống — panel đọc field vắng dễ ra `undefined`).
@@ -1,6 +1,6 @@
1
1
  # Workflow Rules
2
2
 
3
- > General AI behavior rules for all spec-driven-docs commands.
3
+ > General AI behavior rules for all SDD Framework commands.
4
4
  > Loaded by `steps/context-loader.md` at the start of every command.
5
5
 
6
6
  ---
@@ -129,7 +129,7 @@ Trước khi lưu file gap, kiểm tra **từng hàng** trong bảng gap:
129
129
  - [ ] **Có 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 có 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
130
  - [ ] **11 cột đủ** – đúng thứ tự: `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`
131
131
  - [ ] **Cột 2 = `UC`** – mã UC đầy đủ (`<TICKET-ID>-UC{N}`), hoặc `— (toàn PRD)` cho gap `GAP-GEN-`. Mọi UC có 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ó.
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-UC1-001]` trong test case đã có.
133
133
  - [ ] **Cột 4 = `Vấn đề cần confirm`** – KHÔNG viết tắt thành `Vấn đề`
134
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/>`:
135
135
  ```
@@ -29,7 +29,7 @@ Bóc tách spec/PRD/user story thô thành mô tả yêu cầu có cấu trúc c
29
29
  vào phân tích/BR/AC. Với file Confluence/HTML/MHTML: phát hiện qua thẻ `<s>`,
30
30
  `<strike>`, `<del>` hoặc style `text-decoration: line-through`.
31
31
  3. Xác định: feature name, actor/role, mục tiêu nghiệp vụ, phạm vi (in/out scope).
32
- 4. Đánh dấu phần MƠ HỒ / THIẾU → ghi vào `DOC_GAP.md` (gap GAP-xx).
32
+ 4. Đánh dấu phần MƠ HỒ / THIẾU → ghi vào `DOC_GAP.md` (gap `GAP-UC{N}-{nnn}`; gap thuộc cả PRD → `GAP-GEN-{nnn}`).
33
33
 
34
34
  ---
35
35
 
@@ -58,6 +58,6 @@ Phần spec-breakdown là **mục đầu tiên** của `REQUIREMENT_ANALYSIS.md`
58
58
  - Danh sách giả định và câu hỏi mở (đánh dấu rõ điều CHƯA chắc)
59
59
 
60
60
  Đồng thời ghi mọi khoảng trống phát hiện vào `{qc_artifact_dir}DOC_GAP.md`
61
- (theo `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md`), mỗi gap có ID `GAP-xx`.
61
+ (theo `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md`), mỗi gap có ID `GAP-UC{N}-{nnn}` — mã mang UC để phân tích lại một UC không đánh số lại UC khác.
62
62
 
63
63
  Kết thúc bằng gợi ý: feature đã đủ rõ để chuyển sang `qa-planner` (phân tích rủi ro) chưa.
@@ -0,0 +1,155 @@
1
+ ---
2
+ version: 1.0
3
+ updated: 2026-09-04
4
+ ported_from: ui-automation-testing
5
+ upstream_path: skills/qa-tc-designer/api-testcase-designer/api/templates/auth-chain.md
6
+ upstream_sha: ef8f9761f1b53d0ccd752f8fe0c13a427fa15f45
7
+ ---
8
+
9
+ > **VIẾT LẠI, không copy** *(B12)* — cùng lý do với `endpoint.md`: bản upstream có trước luật
10
+ > ATOMIC (2026-07-09), gộp nhiều kết cục vào một `**Expected:**` và dùng hệ mã
11
+ > `API-<FEATURE>-AUTH-001`. Ba nhóm ca giữ nguyên; cấu trúc theo `shared/tc-metadata-format.md`.
12
+
13
+ # Khuôn TC — chuỗi xác thực (login → token → gọi endpoint được bảo vệ)
14
+
15
+ Dùng khi TC phải đăng nhập lấy token trước khi gọi endpoint chính. Thứ tự gọi và điểm verify
16
+ của từng chuỗi: xem `auth-sequence.md`.
17
+
18
+ Phủ **3 nhóm ca**: chuỗi thành công · token hết hạn · login sai mật khẩu.
19
+
20
+ > **Một chuỗi auth KHÔNG phải một TC.** Chuỗi có nhiều bước, mỗi bước có kết cục riêng —
21
+ > nên nó là **nhiều TC độc lập**, mỗi TC giữ FULL steps để tự tới được trạng thái cần assert.
22
+ > Đây đúng là chỗ luật ATOMIC dễ bị bỏ nhất, vì cảm giác "một chuỗi = một ca".
23
+
24
+ ---
25
+
26
+ ## Nhóm ca 1 — Chuỗi thành công
27
+
28
+ ### TC_<FEATURE>_101 — login trả về access_token
29
+
30
+ - **Title:** Login role `<role>` với thông tin đúng trả về access_token
31
+ - **Feature:** <TICKET-ID> — <Tên feature>
32
+ - **Priority:** P0
33
+ - **Status:** Draft
34
+ - **Author:** AI
35
+ - **Tags:** api, happy-path, auth, <feature-tag>
36
+ - **Trace:** BR-xx
37
+ - **@trace.verifies:** {UC-ID}-SC{N}
38
+
39
+ #### Preconditions
40
+ - Tài khoản role `<role>` đã tồn tại (username `<username>`, password `<password>`).
41
+
42
+ #### Test Data
43
+ - Method: POST
44
+ - Path: `/auth/login`
45
+ - Headers: Content-Type: application/json
46
+ - Body:
47
+ ```json
48
+ { "username": "<username>", "password": "<password>" }
49
+ ```
50
+
51
+ #### Test Steps
52
+ 1. **[Action]** Gửi `POST /auth/login` với body trên.
53
+ 2. **[Verify]** Đọc `body.access_token`.
54
+
55
+ #### Expected Result
56
+ - HTTP 200 AND `body.access_token` exists
57
+
58
+ > ✅ Compound hợp lệ — cả hai vế nói về **cùng một** kết cục: "login thành công và có token".
59
+
60
+ ### TC_<FEATURE>_102 — token lấy được gọi được endpoint bảo vệ
61
+
62
+ - **Title:** Token của role `<role>` gọi <METHOD> <protected_path> trả về <success_code>
63
+ - **Priority:** P0
64
+ - **Tags:** api, happy-path, auth, <feature-tag>
65
+ - **Trace:** BR-xx
66
+
67
+ #### Preconditions
68
+ - Tài khoản role `<role>` đã tồn tại.
69
+
70
+ #### Test Steps
71
+ 1. **[Action]** Gửi `POST /auth/login` lấy `access_token`.
72
+ 2. **[Action]** Gửi `<METHOD> <protected_path>` với `Authorization: Bearer {access_token}`.
73
+ 3. **[Verify]** Đọc status code của request thứ hai.
74
+
75
+ #### Expected Result
76
+ - HTTP `<success_code>`
77
+
78
+ > ⚠️ Bước 1 **lặp lại đủ**, không viết "dùng token từ TC_101". Mỗi TC tách phải tự chạy độc
79
+ > lập — nếu nó phụ thuộc TC khác đã chạy thì thứ tự chạy trở thành điều kiện ẩn, và chạy riêng
80
+ > một TC sẽ fail vì lý do không liên quan tới thứ nó kiểm.
81
+
82
+ ---
83
+
84
+ ## Nhóm ca 2 — Token hết hạn
85
+
86
+ ### TC_<FEATURE>_103 — token hết hạn trả về 401
87
+
88
+ - **Priority:** P0
89
+ - **Tags:** api, negative, auth, security
90
+ - **Trace:** BR-xx
91
+
92
+ #### Preconditions
93
+ - Có sẵn một token đã quá thời hạn (`<expired_token>` — seed hoặc lấy từ fixture).
94
+
95
+ #### Test Data
96
+ - Headers: Authorization: Bearer `<expired_token>`
97
+
98
+ #### Test Steps
99
+ 1. **[Action]** Gửi `<METHOD> <protected_path>` với token đã hết hạn.
100
+ 2. **[Verify]** Đọc status code.
101
+
102
+ #### Expected Result
103
+ - HTTP 401
104
+
105
+ *Chuỗi refresh token (nếu API có) là nhóm ca riêng — xem `auth-sequence.md` §Chuỗi 2. Mỗi bước
106
+ của chuỗi refresh cũng là một TC.*
107
+
108
+ ---
109
+
110
+ ## Nhóm ca 3 — Login sai mật khẩu
111
+
112
+ ### TC_<FEATURE>_104 — sai password trả về 401
113
+
114
+ - **Priority:** P0
115
+ - **Tags:** api, negative, auth, security
116
+ - **Trace:** BR-xx
117
+
118
+ #### Test Data
119
+ - Body: `{ "username": "<username>", "password": "<wrong_password>" }`
120
+
121
+ #### Test Steps
122
+ 1. **[Action]** Gửi `POST /auth/login` với password sai.
123
+ 2. **[Verify]** Đọc status code.
124
+
125
+ #### Expected Result
126
+ - HTTP 401
127
+
128
+ ### TC_<FEATURE>_105 — sai password KHÔNG trả về token
129
+
130
+ - **Priority:** P0
131
+ - **Tags:** api, negative, auth, security
132
+ - **Trace:** BR-xx
133
+
134
+ #### Test Steps
135
+ 1. **[Action]** Gửi `POST /auth/login` với password sai.
136
+ 2. **[Verify]** Kiểm trường `access_token` trong body.
137
+
138
+ #### Expected Result
139
+ - `body.access_token` not exists
140
+
141
+ > **Vì sao tách khỏi TC_104 dù cùng một request.** *"Trả 401"* và *"không phát token"* là hai
142
+ > kết cục **fail độc lập được**: một API hỏng có thể trả 401 mà vẫn kèm token trong body. Gộp
143
+ > lại thì TC pass khi chỉ một nửa đúng — và nửa còn lại là lỗ bảo mật.
144
+ >
145
+ > Đây chính là luật *"assertion loại-trừ (KHÔNG có / KHÔNG xảy ra) là oracle THẬT"* ở
146
+ > `shared/tc-metadata-format.md`. Khi tách bằng script, kiểm lại không đánh rơi các bullet
147
+ > phủ định — chúng dễ bị bỏ nhất.
148
+
149
+ ---
150
+
151
+ ## Không cần Teardown
152
+
153
+ Cả 5 TC trên **không tạo bản ghi thật** — chúng chỉ login và đọc. Không thêm dòng
154
+ `#### Teardown` cho có. Thêm khi và chỉ khi TC tạo tài khoản mới hoặc đổi trạng thái thật; lúc
155
+ đó ghi `[Teardown: deactivate user via admin API]` *(`shared/precision-rules.md` §8)*.