@educa-corp/sdd-framework 0.9.5 → 0.9.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/bin/build.js +11 -1
  2. package/bin/lint-trace.js +397 -28
  3. package/bin/self-check.js +183 -12
  4. package/bin/trace-schema.json +2656 -1981
  5. package/core/FRAMEWORK_VERSION +1 -1
  6. package/core/commands/dev-gen-test.md +62 -0
  7. package/core/commands/generate-code.md +1 -1
  8. package/core/commands/generate-tech-docs.md +3 -3
  9. package/core/commands/map-testids.md +88 -11
  10. package/core/commands/qc-analyze.md +509 -425
  11. package/core/commands/qc-design-test.md +475 -247
  12. package/core/commands/qc-plan.md +134 -93
  13. package/core/commands/qc-review.md +216 -131
  14. package/core/commands/qc-run-test.md +346 -231
  15. package/core/commands/validate-traces.md +17 -2
  16. package/core/rules/workflow.md +40 -0
  17. package/core/skills/qc/qa-analyst/DOC_GAP.template.md +9 -1
  18. package/core/skills/qc/qa-designer/shared/duplicate-check-procedure.md +33 -5
  19. package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +24 -0
  20. package/core/skills/qc/qa-planner/test-plan.md +7 -0
  21. package/core/skills/qc/qa-runner/functional/gui-feature.md +1 -1
  22. package/core/skills/qc/qa-runner/functional/gui-screen.md +1 -1
  23. package/core/steps/qc-scope.md +67 -11
  24. package/core/steps/qc-stamp.md +142 -0
  25. package/core/steps/report-footer.md +13 -5
  26. package/core/templates/tech-design.template.md +3 -3
  27. package/docs/01-getting-started/quickstart.md +4 -3
  28. package/docs/02-concepts/architecture.md +14 -0
  29. package/docs/02-concepts/glossary.md +8 -0
  30. package/docs/02-concepts/overview.md +3 -2
  31. package/docs/02-concepts/pipeline-steps/04-bdd.md +1 -1
  32. package/docs/02-concepts/pipeline-steps/05-tech-docs.md +21 -5
  33. package/docs/02-concepts/pipeline-steps/06-code.md +12 -2
  34. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +60 -12
  35. package/docs/02-concepts/pipeline-steps/README.md +4 -3
  36. package/docs/02-concepts/traceability.md +2 -2
  37. package/docs/03-guides/architect.md +2 -2
  38. package/docs/03-guides/developer.md +5 -2
  39. package/docs/03-guides/tester-qa.md +17 -5
  40. package/docs/04-reference/commands.md +6 -3
  41. package/docs/04-reference/trace-schema.md +1 -1
  42. package/docs/explain/07-generate-tech-docs.md +5 -3
  43. package/docs/explain/08-review-tech-docs.md +15 -3
  44. package/docs/explain/09-generate-code.md +30 -4
  45. package/docs/explain/10-review-code.md +1 -1
  46. package/docs/explain/11-map-testids.md +72 -70
  47. package/docs/explain/12-dev-gen-test.md +1 -1
  48. package/docs/explain/15-qc-analyze.md +14 -2
  49. package/docs/explain/16-qc-plan.md +5 -1
  50. package/docs/explain/17-qc-design-test.md +26 -3
  51. package/docs/explain/18-qc-review.md +6 -2
  52. package/docs/explain/19-qc-run-test.md +29 -6
  53. package/docs/explain/20-qc-report.md +5 -2
  54. package/docs/explain/README.md +4 -1
  55. package/package.json +1 -1
@@ -14,14 +14,15 @@ Code là **hệ quả của spec**. Command biến scenario thành code sao cho:
14
14
 
15
15
  ## Vị trí & tiền đề
16
16
 
17
- - **Vị trí:** Phase Implementation, sau BDD + tech-docs.
17
+ - **Vị trí:** Phase Implementation, sau BDD + tech-docs + [`/map-testids`](11-map-testids.md).
18
+ - **Chạy song song với QC.** Hợp đồng test-id (§4.5.6) đã đóng băng trước bước này, nên FE gắn attribute còn QC dựng test **cùng lúc** trên cùng một bảng — không bên nào chờ bên nào, không bên nào tự đặt id.
18
19
  - **Gate vào:** cảnh báo mềm DS1 (BDD chưa approved), DS2 (design-spec), DS3 (tech-doc contract BE), **DS4** (client contract §4.5.4) + **DS5** (FE reuse gate) — DS4/DS5 chỉ khi wire API thật (`integration`/`fe_full`). Xem [Hành vi theo mode](#hành-vi-theo-mode-phase--platform).
19
20
 
20
21
  ---
21
22
 
22
23
  ## Input / Output
23
24
 
24
- **Input:** `.feature` (hoặc UC-ID) + tech-design §4 + CLAUDE.md §2/§3/§5 + `.trace/…/{UC-ID}-{platform}.tsv` + `_seams.tsv`.
25
+ **Input:** `.feature` (hoặc UC-ID) + tech-design §4 + **hợp đồng test-id** (`@trace.testid_attr` ở header + §4.5.6 Test Selectors) + CLAUDE.md §2/§3/§5 + `.trace/…/{UC-ID}-{platform}.tsv` + `_seams.tsv`.
25
26
 
26
27
  **Output:** file code (tag `@trace` boundary) + trace row `.tsv` + cập nhật `_seams.tsv`.
27
28
 
@@ -64,7 +65,7 @@ Code là **hệ quả của spec**. Command biến scenario thành code sao cho:
64
65
  | **DS4** — client contract gate (§4.5.4) | ⏭️ cố ý | ✅ | ✅ | — |
65
66
  | **DS5** — FE component/service reuse gate | ⏭️ | ✅ | ✅ | — |
66
67
  | **Sinh UI** (Figma MCP, component, state) | ✅ | ⏭️ giữ UI cũ | ✅ | — |
67
- | **Test Selectors** (emit test-id) | ✅ | ⏭️ | ✅ | — |
68
+ | **Test Selectors** (**đọc** §4.5.6 rồi emit, xem dưới) | ✅ | ⏭️ | ✅ | — |
68
69
  | **Mock API Layer** | ✅ | ⏭️ | ⏭️ | — |
69
70
  | **Integration Phase** (real API adapter) | ⏭️ | ✅ | ✅ | — |
70
71
  | **Wire-up API** | dùng **mock** | **lật** mock→real | wire **thẳng** real | — (BE là API) |
@@ -83,6 +84,29 @@ Code là **hệ quả của spec**. Command biến scenario thành code sao cho:
83
84
 
84
85
  ---
85
86
 
87
+ ### Test Selectors — ĐỌC hợp đồng, không tự đặt *(đổi ở đợt sửa hợp đồng test-id)*
88
+
89
+ Trước đây lệnh này **tự sinh** test-id khi §4.5.6 rỗng. Đó là lỗi **im lặng**: QC đang viết script theo một bảng khác (hoặc không có bảng nào), và không ai báo cho ai.
90
+
91
+ **Giá trị** test-id lấy từ §4.5.6:
92
+
93
+ | Tình huống | Hành vi |
94
+ |---|---|
95
+ | §4.5.6 **có dòng** | Dùng đúng giá trị trong bảng |
96
+ | §4.5.6 **rỗng** | ⚠️ **Cảnh báo mạnh rồi hỏi Y/N.** Đường đúng là dừng, chạy `/map-testids`. Tiếp tục là **quyết định của dev**, không phải mặc định của máy |
97
+
98
+ **Tên thuộc tính** lấy từ `@trace.testid_attr` ở header tech-doc:
99
+
100
+ | Tình huống | Hành vi |
101
+ |---|---|
102
+ | Header có field | Dùng đúng tên đó (`data-testid` · `data-test` · `testID` · `Key`…) |
103
+ | Header **không có** | Cảnh báo mềm **nêu rõ rủi ro** → fallback theo `active_module`. Không im lặng hardcode |
104
+ | `.feature` cũng khai attr và **lệch** header | In **cả hai** giá trị, để người quyết. Không tự chọn bên nào |
105
+
106
+ > **Vì sao không suy từ platform cho nhanh:** suy từ platform là **phát biểu lại một sự thật đã ghi ở nơi khác**. Nó hỏng đúng ở ca field này sinh ra để phục vụ — web dùng `data-test`/`data-qa` thay vì mặc định, hoặc một PRD trải ba platform với ba thuộc tính khác nhau. Và nó hỏng **im lặng theo kiểu tệ nhất**: test fail `element not found`, trông y hệt một bug sản phẩm, nên QC đi mở bug thay vì sửa selector.
107
+
108
+ ---
109
+
86
110
  ## Checkpoint & Gate
87
111
 
88
112
  - 🛑 **Comprehension checkpoint** (Code Generation Plan) — điểm dừng chính; Dev xác nhận drift + scope đúng.
@@ -113,4 +137,6 @@ Code là **hệ quả của spec**. Command biến scenario thành code sao cho:
113
137
 
114
138
  ## Kết nối
115
139
 
116
- **Trước:** [`/review-tech-docs`](08-review-tech-docs.md) · **Sau:** lần đầu → [`/review-code`](10-review-code.md); gen lại → [`/dev-gen-test`](12-dev-gen-test.md).
140
+ **Trước:** [`/map-testids`](11-map-testids.md) → [`/review-tech-docs`](08-review-tech-docs.md) · **Song song:** [`/qc-design-test`](17-qc-design-test.md) · **Sau:** lần đầu → [`/review-code`](10-review-code.md); gen lại → [`/dev-gen-test`](12-dev-gen-test.md).
141
+
142
+ > Test-id sinh ra **không khớp code thật** → sửa bằng `/map-testids --from-code`, đừng sửa tay bảng. Lệnh đó tự hạ `qc_status` → `not_run` và `qc_run_at` → `—`, để QC biết test-script bám selector cũ đã hết hiệu lực.
@@ -1,4 +1,4 @@
1
- [← /generate-code](09-generate-code.md) · [Explain Home](README.md) · [Next: /map-testids →](11-map-testids.md)
1
+ [← /generate-code](09-generate-code.md) · [Explain Home](README.md) · [Next: /dev-gen-test →](12-dev-gen-test.md)
2
2
 
3
3
  # 10 · `/review-code` — Review code (read-only, 4 dimension)
4
4
 
@@ -1,70 +1,72 @@
1
- [← /review-code](10-review-code.md) · [Explain Home](README.md) · [Next: /dev-gen-test →](12-dev-gen-test.md)
2
-
3
- # 11 · `/map-testids` — Dán test-id ổn định cho UI (FE)
4
-
5
- > **Một câu.** Gắn **test-id ổn định** vào các element có hành động trên UI FE và ghi bản đồ selector vào tech-doc §4.5.6 — làm cầu nối để QC Playwright bám selector không vỡ.
6
-
7
- ---
8
-
9
- ## Vấn đề giải quyết
10
-
11
- Test tự động (Playwright) vỡ khi selector đổi (class/text thay đổi). `/map-testids` chuẩn hoá **test-id ổn định** cho mọi element tương tác, đảm bảo component tái dùng forward được test-id, và ghi map để QC dùng — tách concern "làm UI test được" khỏi "viết test".
12
-
13
- ---
14
-
15
- ## Vị trí & tiền đề
16
-
17
- - **Vị trí:** Phase **Tech Design**, sau `/generate-tech-docs` và **TRƯỚC** `/review-tech-docs` — tức trước cả `/generate-code`. Chốt hợp đồng test-id ở đây để FE và QC đọc cùng một bản đã đóng băng rồi **chạy song song**.
18
- - **Hai chế độ:** mặc định (feature mới — nguồn là design-spec + BDD, **không đụng code**) · `--from-code` (brownfield — đọc/patch code đã có, chạy một lần mỗi UC cũ).
19
- - **Chặn cứng:** chỉ FE/App (platform guard)BE không UI.
20
-
21
- ---
22
-
23
- ## Input / Output
24
-
25
- **Input:** design-spec (Component Inventory, màn hình) + step `When` của `.feature` FE + tech-doc §4.5.1 (cây component) + catalog component tái dùng. *Chế độ `--from-code` đọc thêm UI code FE.*
26
-
27
- **Output:** map §4.5.6 Test Selectors + `@trace.testid_attr` header tech-doc. *Chế độ `--from-code` còn patch test-id vào code FE.*
28
-
29
- ---
30
-
31
- ## Các bước xử lý (chi tiết)
32
-
33
- | Step | Việc |
34
- |------|------|
35
- | **0 · Platform guard** | Chỉ FE/App; else STOP |
36
- | **1 · Thu thập element có action** | Quét UI tìm element người dùng tương tác (nút, ô nhập, link…) |
37
- | **2 · Phân giải test-id ổn định** | Đặt test-id ổn định (không phụ thuộc text/class dễ đổi) cho mỗi element |
38
- | **3 · Đảm bảo component tái dùng forward test-id** | Component dùng lại (catalog) phải cho phép truyền test-id xuống sửa component nếu chưa |
39
- | **4 · Patch usage site** (chỉ EXTEND) | Gắn test-id vào nơi dùng, chỉ thêm (không viết đè) |
40
- | **5 · Ghi/làm mới map §4.5.6** | Ghi bảng Test Selectors vào tech-doc để QC bám |
41
- | **6 · Handoff** | Bàn giao cho QC (`/qc-*`) |
42
-
43
- ---
44
-
45
- ## Checkpoint & Gate
46
-
47
- - Không gate chặn; EXTEND-only khi patch (an toàn).
48
-
49
- ---
50
-
51
- ## Cơ chế đặc biệt
52
-
53
- - **Test-id ổn định** — chống test vỡ do đổi visual; nguyên tắc "selector là contract QC↔FE".
54
- - **Forward test-id qua component tái dùng** — sửa gốc component để test-id lan xuống, không hardcode từng chỗ.
55
- - **Map §4.5.6 tech-doc** — QC đọc từ một nguồn, không tự dò.
56
-
57
- ---
58
-
59
- ## 👓 Góc nhìn tối ưu
60
-
61
- - **Lệnh chốt hợp đồng FE↔QC** — vị trí **đã được chốt**: phase Tech Design, trước `/generate-code`. Trước đây vị trí mờ (tài liệu này từng nói 'giữa Tech Design & Code') nên lệnh hay bị bỏ qua; giờ `/generate-tech-docs` trỏ thẳng sang đây, và `/review-tech-docs` T6 không cho APPROVED nếu §4.5.6 rỗng.
62
- - **Phụ thuộc catalog component tái dùng** — nếu component không forward được prop test-id, Step 3 phát sinh sửa lan rộng.
63
- - **EXTEND-only** an toàn nhưng nếu test-id sai thì không tự sửa.
64
- - **Đã vào golden path** — `/generate-tech-docs` **`/map-testids`** `/review-tech-docs` (T6 chặn nếu §4.5.6 rỗng). `lint-trace` T15/T16 canh bảng, T17/T18 canh bảng-vs-code.
65
-
66
- ---
67
-
68
- ## Kết nối
69
-
70
- **Trước:** [`/generate-tech-docs`](08-generate-tech-docs.md) · **Sau:** [`/review-tech-docs`](07-review-tech-docs.md), rồi rẽ hai nhánh song song — [`/generate-code`](09-generate-code.md) (FE gắn attribute) ∥ [`/qc-design-test`](17-qc-design-test.md) (QC dựng test theo cùng hợp đồng).
1
+ [← /generate-tech-docs](07-generate-tech-docs.md) · [Explain Home](README.md) · [Next: /review-tech-docs →](08-review-tech-docs.md)
2
+
3
+ > **Số `11` số file lịch sử, không phải thứ tự chạy.** Lệnh này chạy ở **Phase Design**, giữa `/generate-tech-docs` và `/review-tech-docs` tức **trước** `/generate-code`. Giữ nguyên tên file để không gãy link cũ.
4
+
5
+ # 11 · `/map-testids` — Dán test-id ổn định cho UI (FE)
6
+
7
+ > **Một câu.** Gắn **test-id ổn định** vào các element có hành động trên UI FE và ghi bản đồ selector vào tech-doc §4.5.6 — làm cầu nối để QC Playwright bám selector không vỡ.
8
+
9
+ ---
10
+
11
+ ## Vấn đề giải quyết
12
+
13
+ Test tự động (Playwright) vỡ khi selector đổi (class/text thay đổi). `/map-testids` chuẩn hoá **test-id ổn định** cho mọi element tương tác, đảm bảo component tái dùng forward được test-id, và ghi map để QC dùng — tách concern "làm UI test được" khỏi "viết test".
14
+
15
+ ---
16
+
17
+ ## Vị trí & tiền đề
18
+
19
+ - **Vị trí:** Phase **Tech Design**, sau `/generate-tech-docs` và **TRƯỚC** `/review-tech-docs` tức trước cả `/generate-code`. Chốt hợp đồng test-id ở đây để FE và QC đọc cùng một bản đã đóng băng rồi **chạy song song**.
20
+ - **Hai chế độ:** mặc định (feature mới — nguồn là design-spec + BDD, **không đụng code**) · `--from-code` (brownfield — đọc/patch code đã có, chạy một lần mỗi UC cũ).
21
+ - **Chặn cứng:** chỉ FE/App (platform guard) — BE không có UI.
22
+
23
+ ---
24
+
25
+ ## Input / Output
26
+
27
+ **Input:** design-spec (Component Inventory, màn hình) + step `When` của `.feature` FE + tech-doc §4.5.1 (cây component) + catalog component tái dùng. *Chế độ `--from-code` đọc thêm UI code FE.*
28
+
29
+ **Output:** map §4.5.6 Test Selectors + `@trace.testid_attr` ở header tech-doc. *Chế độ `--from-code` còn patch test-id vào code FE.*
30
+
31
+ ---
32
+
33
+ ## Các bước xử lý (chi tiết)
34
+
35
+ | Step | Việc |
36
+ |------|------|
37
+ | **0 · Platform guard** | Chỉ FE/App; else STOP |
38
+ | **1 · Thu thập element action** | Quét UI tìm element người dùng tương tác (nút, ô nhập, link…) |
39
+ | **2 · Phân giải test-id ổn định** | Đặt test-id ổn định (không phụ thuộc text/class dễ đổi) cho mỗi element |
40
+ | **3 · Đảm bảo component tái dùng forward test-id** | Component dùng lại (catalog) phải cho phép truyền test-id xuống sửa component nếu chưa |
41
+ | **4 · Patch usage site** (chỉ EXTEND) | Gắn test-id vào nơi dùng, chỉ thêm (không viết đè) |
42
+ | **5 · Ghi/làm mới map §4.5.6** | Ghi bảng Test Selectors vào tech-doc để QC bám |
43
+ | **6 · Handoff** | Bàn giao cho QC (`/qc-*`) |
44
+
45
+ ---
46
+
47
+ ## Checkpoint & Gate
48
+
49
+ - Không gate chặn; EXTEND-only khi patch (an toàn).
50
+
51
+ ---
52
+
53
+ ## chế đặc biệt
54
+
55
+ - **Test-id ổn định** — chống test vỡ do đổi visual; nguyên tắc "selector là contract QC↔FE".
56
+ - **Forward test-id qua component tái dùng** — sửa gốc component để test-id lan xuống, không hardcode từng chỗ.
57
+ - **Map ở §4.5.6 tech-doc** — QC đọc từ một nguồn, không tự dò.
58
+
59
+ ---
60
+
61
+ ## 👓 Góc nhìn tối ưu
62
+
63
+ - **Lệnh chốt hợp đồng FE↔QC** vị trí **đã được chốt**: phase Tech Design, trước `/generate-code`. Trước đây vị trí mờ (tài liệu này từng nói 'giữa Tech Design & Code') nên lệnh hay bị bỏ qua; giờ `/generate-tech-docs` trỏ thẳng sang đây, và `/review-tech-docs` T6 không cho APPROVED nếu §4.5.6 rỗng.
64
+ - **Phụ thuộc catalog component tái dùng** — nếu component không forward được prop test-id, Step 3 phát sinh sửa lan rộng.
65
+ - **EXTEND-only** an toàn nhưng nếu test-id cũ sai thì không tự sửa.
66
+ - **Đã vào golden path** — `/generate-tech-docs` → **`/map-testids`** → `/review-tech-docs` (T6 chặn nếu §4.5.6 rỗng). `lint-trace` T15/T16 canh bảng, T17/T18 canh bảng-vs-code.
67
+
68
+ ---
69
+
70
+ ## Kết nối
71
+
72
+ **Trước:** [`/generate-tech-docs`](07-generate-tech-docs.md) · **Sau:** [`/review-tech-docs`](08-review-tech-docs.md), rồi rẽ hai nhánh song song — [`/generate-code`](09-generate-code.md) (FE gắn attribute) ∥ [`/qc-design-test`](17-qc-design-test.md) (QC dựng test theo cùng hợp đồng).
@@ -1,4 +1,4 @@
1
- [← /map-testids](11-map-testids.md) · [Explain Home](README.md) · [Next: /dev-run-test →](13-dev-run-test.md)
1
+ [← /review-code](10-review-code.md) · [Explain Home](README.md) · [Next: /dev-run-test →](13-dev-run-test.md)
2
2
 
3
3
  # 12 · `/dev-gen-test` — Sinh bộ tự-kiểm của Dev
4
4
 
@@ -26,7 +26,7 @@ QC chính thức cần hiểu yêu cầu **testable** trước khi viết test.
26
26
 
27
27
  **Input:** UC-ID + spec (PRD/BDD từ spec repo) + skill `qa-analyst`.
28
28
 
29
- **Output (per PRD × nền):** `{qc_dir}/{TICKET-ID}/{platform}/REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md` (11 cột, cột 2 là `UC`) + `{refinement_dir}/{TICKET-ID}-qa-findings.yaml`. Một lần chạy phủ **mọi UC có BDD `approved`** của PRD; UC còn nháp vào bảng *Phạm vi phân tích* với dấu `⏸ Chưa xét` (`--include-draft` để xét luôn).
29
+ **Output (per PRD × nền):** `{qc_dir}/{TICKET-ID}/{platform}/REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md` (11 cột, cột 2 là `UC`) + `{refinement_dir}/{TICKET-ID}-qa-findings.yaml`. Một lần chạy phủ **mọi UC có BDD `approved`** của PRD; UC còn nháp vào bảng *Phạm vi phân tích* với dấu `⏸ Chưa xét` (`--force` để xét luôn).
30
30
 
31
31
  ---
32
32
 
@@ -37,7 +37,19 @@ QC chính thức cần hiểu yêu cầu **testable** trước khi viết test.
37
37
  3. **Role qa-analyst** — nạp skill `{qc_skills_dir}/qa-analyst/`.
38
38
  4. **Trace mapping (bắt buộc)** — map yêu cầu ↔ scenario ↔ SC.
39
39
  5. **DOC_GAP (bắt buộc)** — ghi chỗ tài liệu thiếu/mơ hồ chặn test (blocker 🔴 xử trước ở `/qc-plan`).
40
- 6. **Output** REQUIREMENT_ANALYSIS.md + DOC_GAP.md.
40
+ 6. **Guard — BR-tag** *(mới)* phép **đếm cơ học** chống bỏ sót business rule:
41
+
42
+ ```
43
+ A = mọi business rule mà .feature ĐÃ GẮN TAG (@trace.business_rules)
44
+ B = mọi rule bản phân tích VỪA SINH RA (REQUIREMENT_ANALYSIS.md)
45
+ A ∖ B → rule BDD đã nhắc mà phân tích BỎ SÓT
46
+ ```
47
+
48
+ `A ∖ B` rỗng → in `Guard BR-tag: khớp {n}/{n}`. Không rỗng → **tự bổ sung từ PRD** rồi in danh sách đã bù. **In dòng này kể cả khi sạch** — guard im lặng khi sạch là guard không ai biết nó tồn tại.
49
+ 7. **Self-Review** — nạp `skills/qc/_shared/self-review-principles.md`, soát một lượt trước khi in report.
50
+ 8. **Output** REQUIREMENT_ANALYSIS.md + DOC_GAP.md.
51
+ > **Self-Review ≠ Guard.** Guard là phép **đếm cơ học**, có hệ quả bắt buộc khi lệch. Self-review là lượt đọc lại **rộng hơn nhưng mềm hơn**, soát ba nhóm lỗi mà phép đếm không bắt được (bịa dữ kiện · lẫn suy đoán với sự thật · bỏ dở giữa chừng). Một bộ nguyên tắc tự soát **không bao giờ** được dùng làm lý do gỡ một Guard — chính file `self-review-principles.md` ghi rõ ranh giới đó ngay ở đầu.
52
+
41
53
 
42
54
  ---
43
55
 
@@ -32,7 +32,10 @@ Không phải mọi scenario rủi ro ngang nhau. Trạm này xếp ưu tiên te
32
32
  1. **Role qa-planner** — nạp `{qc_skills_dir}/qa-planner/`.
33
33
  2. Đọc analysis + gap → **đánh giá rủi ro** từng vùng.
34
34
  3. Nêu **câu hỏi cho dev** (điểm chưa rõ để test đúng).
35
- 4. **Output** TEST_PLAN.md.
35
+ 4. **Self-Review** *(mới)* — nạp `skills/qc/_shared/self-review-principles.md`, soát một lượt trước khi in report.
36
+ 5. **Output** TEST_PLAN.md.
37
+ > **Self-Review ≠ Guard.** Guard là phép **đếm cơ học**, có hệ quả bắt buộc khi lệch. Self-review là lượt đọc lại **rộng hơn nhưng mềm hơn**, soát ba nhóm lỗi mà phép đếm không bắt được (bịa dữ kiện · lẫn suy đoán với sự thật · bỏ dở giữa chừng). Một bộ nguyên tắc tự soát **không bao giờ** được dùng làm lý do gỡ một Guard — chính file `self-review-principles.md` ghi rõ ranh giới đó ngay ở đầu.
38
+
36
39
 
37
40
  ---
38
41
 
@@ -46,6 +49,7 @@ Không phải mọi scenario rủi ro ngang nhau. Trạm này xếp ưu tiên te
46
49
 
47
50
  - **Risk-based** — kế hoạch bám rủi ro, không phủ đều.
48
51
  - **Câu hỏi cho dev** — kênh QC ↔ Dev có hồ sơ trước khi viết test.
52
+ - **Self-Review dùng chung** — sáu trạm QC nạp **cùng một** file nguyên tắc, không mỗi trạm một bản. Sáu bản sao thì sáu tháng sau sẽ là sáu luật khác nhau, **im lặng**.
49
53
 
50
54
  ---
51
55
 
@@ -15,12 +15,13 @@ Tách "thiết kế test case" (con người đọc/review được) khỏi "cod
15
15
  ## Vị trí & tiền đề
16
16
 
17
17
  - **Vị trí:** Phase QC (trạm 3), sau `/qc-plan`.
18
+ - **Không cần code.** Trạm này chỉ cần spec + **hợp đồng test-id §4.5.6** (đã chốt ở `/map-testids`, trước `/generate-code`). Nên nó chạy **song song với FE**.
18
19
 
19
20
  ---
20
21
 
21
22
  ## Input / Output
22
23
 
23
- **Input:** TEST_PLAN.md + skill `qa-designer` (chọn layer, nạp MỘT file).
24
+ **Input:** TEST_PLAN.md + skill `qa-designer` (chọn layer, nạp MỘT file) + `.feature` (nguồn scenario cho Guard) + **§4.5.6 Test Selectors**.
24
25
 
25
26
  **Output:** `{qc_dir}/{TICKET-ID}/{platform}/test-cases/*.Test.md` (trace mapping bắt buộc). Gọi theo **từng UC**, ghi vào thư mục test-case dùng chung cấp PRD.
26
27
 
@@ -31,13 +32,31 @@ Tách "thiết kế test case" (con người đọc/review được) khỏi "cod
31
32
  1. **Role qa-designer** — chọn layer, nạp một file skill (không nạp cả bộ → tiết kiệm context).
32
33
  2. **Conventions** — theo quy ước test-case của QC team.
33
34
  3. **Trace mapping (bắt buộc)** — mỗi test case ↔ SC.
34
- 4. **Output** `.Test.md`.
35
+ 4. **Guard — SC coverage** *(mới)* — phép **đếm cơ học**, không phải lượt đọc lại:
36
+
37
+ ```
38
+ 1. Thu SC — mọi @trace.scenario trong .feature của uc_list (đúng nền).
39
+ LOẠI SC của UC mà qc-scope đã lọc ra (⏸ BDD chưa approved).
40
+ 2. Thu V — mọi @trace.verifies trong các file *.Test.md VỪA GHI.
41
+ 3. Đếm — mỗi SC ∈ tập bước 1 phải có ≥ 1 TC trỏ tới.
42
+ ```
43
+
44
+ | Kết quả | Hành vi |
45
+ |---|---|
46
+ | Mọi SC đều có ≥1 TC | in `Guard SC coverage: khớp {K}/{K}` |
47
+ | Có SC chưa phủ | **viết bù TC ngay**, rồi in `⚠️ Guard SC coverage: {K}/{total} — đã bù {m} SC: {…}` |
48
+
49
+ **In dòng này kể cả khi sạch** — guard im lặng khi sạch là guard không ai biết nó tồn tại, nên cũng không ai phát hiện khi nó hỏng.
50
+
51
+ 5. **Self-Review** — nạp `skills/qc/_shared/self-review-principles.md`, soát một lượt trước khi in report.
52
+ 6. **Output** `.Test.md`.
35
53
 
36
54
  ---
37
55
 
38
56
  ## Checkpoint & Gate
39
57
 
40
- - Không gate ở đây; gate là `/qc-review` kế tiếp.
58
+ - Không gate chặn người ở đây; gate là `/qc-review` kế tiếp.
59
+ - Nhưng Guard SC coverage **có hệ quả bắt buộc**: thiếu thì lệnh **viết bù** rồi mới báo, không phải chỉ cảnh báo.
41
60
 
42
61
  ---
43
62
 
@@ -46,6 +65,8 @@ Tách "thiết kế test case" (con người đọc/review được) khỏi "cod
46
65
  - **Markdown-first** — test case là tài liệu review được trước khi thành code.
47
66
  - **Nạp MỘT file skill theo layer** — tối ưu context (chỉ layer cần).
48
67
  - **Trace mapping** — giữ test case gắn SC để về sau ghi `qc_status` đúng.
68
+ - **Guard SC coverage KHÔNG có đường thoát.** SC bị gap chặn **vẫn** phải có TC — viết đủ, mang dấu `🚫 Block: [GAP-UC{N}-{nnn}] — lý do`. Gap là thứ được **ghi vào** test case, không phải cái cớ để không viết. Cho phép "bỏ trống có lý do" là dạy hai luật trái nhau trong cùng một lệnh, và cho agent một câu-lý-do-cho-qua.
69
+ - **Guard ≠ Self-Review.** Guard là phép **đếm**, có hệ quả bắt buộc. Self-review là lượt đọc lại rộng hơn nhưng mềm hơn. Không bao giờ dùng self-review làm lý do gỡ Guard.
49
70
 
50
71
  ---
51
72
 
@@ -53,6 +74,8 @@ Tách "thiết kế test case" (con người đọc/review được) khỏi "cod
53
74
 
54
75
  - **Tách design ↔ script** thêm một trạm nhưng cho phép review sớm — đánh đổi giữa số bước và chất lượng gate.
55
76
  - **Phụ thuộc convention QC team** — cần đồng bộ khi convention đổi.
77
+ - **Guard đếm sớm, ở chỗ còn sửa được.** `/qc-report` cũng tính design coverage bằng **đúng phép đếm này**, nhưng ở **cuối** — lúc đó chỉ còn kịp báo, không còn kịp viết bù. Cùng một phép đếm, giá trị khác hẳn theo thời điểm.
78
+ - Con số `{K}/{total}` vốn **đã in** ở report từ trước, nhưng **không có phép tính nào đứng sau nó**. Một con số trong báo cáo không chứng minh có phép đo.
56
79
 
57
80
  ---
58
81
 
@@ -29,8 +29,11 @@ Không chạy test kém. Trạm này chặn: (1) test case chưa đủ tốt tr
29
29
  ## Các bước xử lý (chi tiết)
30
30
 
31
31
  1. **Role qa-reviewer** — nạp `{qc_skills_dir}/qa-reviewer/`.
32
- 2. **Review focus** — kiểm test case (đủ phủ, đúng SC) hoặc script (đúng logic, selector, không fake-pass).
33
- 3. Verdict: APPROVED đi tiếp; NEEDS_FIX sửa rồi review lại.
32
+ 2. **Review focus** — kiểm test case (đủ phủ, đúng SC) hoặc script (đúng logic, **selector bám §4.5.6 chứ không dò DOM**, không fake-pass).
33
+ 3. **Self-Review** *(mới)* nạp `skills/qc/_shared/self-review-principles.md`, soát một lượt trước khi in report.
34
+ 4. Verdict: APPROVED → đi tiếp; NEEDS_FIX → sửa rồi review lại.
35
+ > **Self-Review ≠ Guard.** Guard là phép **đếm cơ học**, có hệ quả bắt buộc khi lệch. Self-review là lượt đọc lại **rộng hơn nhưng mềm hơn**, soát ba nhóm lỗi mà phép đếm không bắt được (bịa dữ kiện · lẫn suy đoán với sự thật · bỏ dở giữa chừng). Một bộ nguyên tắc tự soát **không bao giờ** được dùng làm lý do gỡ một Guard — chính file `self-review-principles.md` ghi rõ ranh giới đó ngay ở đầu.
36
+
34
37
 
35
38
  ---
36
39
 
@@ -44,6 +47,7 @@ Không chạy test kém. Trạm này chặn: (1) test case chưa đủ tốt tr
44
47
 
45
48
  - **Một lệnh, hai vai** — review case và review script dùng chung skill, khác focus.
46
49
  - **Gate thật của QC** — điểm HITL chính trong dây chuyền tự động.
50
+ - **Review script thì soát cả nguồn selector** — script lấy locator từ §4.5.6 Test Selectors, hay tự dò DOM? Dò DOM là script giòn: dev đổi một class là vỡ, và **không ai báo**.
47
51
 
48
52
  ---
49
53
 
@@ -36,20 +36,41 @@
36
36
  1. **Role & stack** — qc-playwright (`stack-profile.yaml`): Python, pytest-playwright fixture, Page Object; mỗi test độc lập; gom theo (role, account) để auth không xen kẽ.
37
37
  2. **Skills** — nạp một file skill `qa-runner` theo layer.
38
38
  3. **Sinh script** từ `.Test.md`; tag `@trace.verifies={UC-ID}-SC{N}`.
39
- 4. **Chạy** phân loại mỗi FAIL: **script-bug** (fix selector/logic) vs **product-gap** (giữ FAIL + evidence, **không bao giờ fake-pass**).
39
+ 4. **Chạy.** Test đỏ **một lần** chưa nói được đỏ cái **chạy lại riêng test đó, tối đa 2 lần**, rồi mới phân loại thành **ba** nhãn:
40
+
41
+ ```
42
+ đỏ → đỏ → đỏ ⇒ NHẤT QUÁN → sang bước điều tra: script-bug | product-gap
43
+ đỏ → xanh ⇒ KHÔNG NHẤT QUÁN → flaky
44
+ đỏ → đỏ → xanh ⇒ KHÔNG NHẤT QUÁN → flaky
45
+ ```
46
+
47
+ | Nhãn | Khi nào | Hệ quả |
48
+ |---|---|---|
49
+ | `script-bug` | Sai locator / logic test / timing / dữ liệu test | QC tự sửa. **Không** mở bug |
50
+ | `product-gap` | Hành vi thật ≠ spec — defect thật | Mở bug qua `/report-bug`. Giữ `fail` + evidence, **không bao giờ fake-pass** |
51
+ | `flaky` | Không nhất quán ở bước chạy lại — **chưa đủ căn cứ** | Cách ly + ghi **nghi vấn** nguyên nhân. **Không** mở bug từ một lần chạy hên xui |
52
+
53
+ **Điều tra bằng bằng chứng, không đoán** — đọc trace/video/log: timeline, DOM snapshot, network, console **tại thời điểm fail**. Không chắc giữa `script-bug` và `product-gap` → **mời Dev cùng xem trace**.
54
+
55
+ 🛑 **Người xác nhận TRƯỚC khi hành động** — lệnh in đề xuất kèm evidence cụ thể rồi **dừng chờ**. **Không ghi `qc_status`** cho scenario nào còn FAIL chưa được xác nhận nhãn.
40
56
  5. **Write Trace State — `qc_status`** (kết quả QC chính thức) + `qc_run_at`, `qc_owner`, `qc_blocked_by`, `last_updated`.
41
57
 
42
58
  ⚠️ **ĐỌC cột `status` TRƯỚC KHI GHI `pass`** *(GAPS-v4 G55, đối xứng `/dev-run-test`)*: row `DRIFT`/`ORPHANED` + test xanh → **`not_run`**, không bao giờ `pass`. `fail` và `skip` ghi bình thường — chỉ giá trị **khẳng định** cần giấy phép.
43
59
 
44
60
  Và ở lệnh này hậu quả đi **xa hơn** `/dev-run-test`: một `pass` sai còn **đóng một bug** (§Đóng bug đã verify). Nên khi hạ về `not_run`, lệnh **KHÔNG** clear `qc_owner`/`qc_blocked_by` và **KHÔNG** chạy bước đóng bug — đóng bug dựa trên một lần QC chạy trên spec đã đổi là đóng sai.
45
- 6. **Đóng bug đã verify** chạy **TRƯỚC** bước clear cột (xem dưới).
46
- 7. **Refresh Panel Mirror** Living Docs local.
61
+ `flaky` `qc_status = not_run`, `qc_owner = qc`. Không tạo trạng thái mới: `not_run` đúng nghĩa *"chưa có kết luận"*, và `qc_owner = qc` để nó không rơi vào khoảng không ai nhận.
62
+ 6. **Self-Review** *(mới)*nạp `skills/qc/_shared/self-review-principles.md`, soát một lượt trước khi in report.
63
+ 7. **Đóng bug đã verify** — chạy **TRƯỚC** bước clear cột (xem dưới).
64
+ 8. **Refresh Panel Mirror** — Living Docs local.
47
65
 
48
66
  ---
49
67
 
50
68
  ## Checkpoint & Gate
51
69
 
52
70
  - Tiền đề: case đã APPROVED ở `/qc-review`. Script sinh ra → review lại ở `/qc-review` (script) trước PR.
71
+ - 🛑 **Xác nhận nhãn FAIL** — cổng chặn được **thêm vào**, trong khi framework vốn đang giảm số cổng chặn (G41). Lý do: **cả hai hướng sai đều không đảo ngược rẻ.** Gắn nhầm `script-bug` cho lỗi sản phẩm thật là **giấu bug** cho tới khi khách gặp; mở bug từ một lần chạy hên xui là **đốt thời gian dev** và làm mòn niềm tin vào QC.
72
+
73
+ > **Đây KHÔNG phải `retries` trong config test runner.** `retries` tự thử lại rồi báo *"passed on retry"* — nó **che** sự không nhất quán. Ở đây chạy **tách biệt từng lần để quan sát**, vì chính sự không nhất quán mới là thông tin cần.
53
74
 
54
75
  ---
55
76
 
@@ -57,6 +78,7 @@
57
78
 
58
79
  - **`qc_status` là trục authoritative** — khác `dev_selftest`; có evidence.
59
80
  - **Không fake-pass** — product-gap giữ nguyên FAIL, đẩy về PO/Dev.
81
+ - **Chạy lại để QUAN SÁT, không để cho qua** — ba nhãn FAIL tách *chưa đủ căn cứ* (`flaky`) ra khỏi *đã đủ căn cứ* (`script-bug` / `product-gap`). Trước đây hai nhãn gộp cả ba tình huống, nên một test hên xui hoặc bị gán bừa `script-bug` (giấu bug), hoặc thành một bug ma gửi cho dev.
60
82
  - **Stack QC tách hẳn dev** — `@trace.verifies` nối script ↔ SC.
61
83
  - **`active_platform` khoá sổ trace** — `qc_status` ghi đúng `{UC-ID}-{platform}.tsv`.
62
84
  - **Chủ sở hữu bước `🟡 Fixed → 🟢 Closed`.** `/report-bug` mở bug (`🟢 Open`), `/fix-bug` đặt `🟡 Fixed`, và **chỉ QC re-verify mới đóng được** — dev không tự đóng bug của mình.
@@ -80,9 +102,10 @@ Bug report đã đổi phải **commit + push** vào spec repo — file local l
80
102
  ## 👓 Góc nhìn tối ưu
81
103
 
82
104
  - **Trạm nặng nhất của QC** — sinh + chạy + phân loại + ghi trace. Chạy thật phụ thuộc môi trường (browser, data, service lên).
83
- - **Phân loại script-bug vs product-gap phụ thuộc AI/reviewer** sai loại hoặc giấu lỗi sản phẩm hoặc báo nhầm. Đáng tiêu chí rõ.
84
- - **Selector phụ thuộc `/map-testids`** — nếu chưa map, script giòn.
85
- - **Chạy lại tốn tài nguyên** — cân nhắc scoped run như dev-run-test.
105
+ - **Phân loại vẫn phụ thuộc người đọc evidence** — nhưng giờ có **tiêu chí và thứ tự bắt buộc**: chạy lại ×2 → đọc trace/log → đề xuất nhãn → **người xác nhận**. Chỗ mềm còn lại là ranh giới `script-bug` `product-gap` khi hành vi nằm ở **biên của spec**; đường ra đã ghi **mời Dev cùng xem trace**, không đoán cho xong.
106
+ - **Selector bám hợp đồng `/map-testids`** — §4.5.6 đã được chốt **trước** `/generate-code`, nên trạm này có bảng selector sẵn. Ca còn lại: §4.5.6 rỗng (dự án cũ chưa từng chạy `/map-testids`) script quay về dò DOM và **giòn**.
107
+ - **Chạy lại tốn tài nguyên** — ×2 cho mỗi FAIL chi phí thật. Đánh đổi có chủ đích: rẻ hơn nhiều so với một bug ma gửi cho dev, hoặc một defect thật bị gán `script-bug` rồi chôn.
108
+ - **`flaky` không có cột riêng trong sổ trace** — nó nằm ở `qc_status = not_run` + `qc_owner = qc` + nghi vấn ghi trong report. Cố ý: thêm một giá trị enum mới sẽ kéo theo sửa schema, lint, mọi lệnh đọc cột đó — trong khi `not_run` đã đúng nghĩa *chưa có kết luận*. Cái giá là **không truy được lịch sử flaky bằng máy**, phải đọc report.
86
109
 
87
110
  ---
88
111
 
@@ -30,8 +30,11 @@ Kết quả chạy cần được trình bày có bằng chứng và **định t
30
30
 
31
31
  1. **Role qa-runner/report** — nạp skill report.
32
32
  2. **Procedure** — tổng hợp kết quả run (pass/fail per SC), đính evidence.
33
- 3. Tách **product-gap** → định tuyến về PO/Dev (có thể thành `/report-bug`).
34
- 4. **Output** report.
33
+ 3. Tách **product-gap** → định tuyến về PO/Dev (có thể thành `/report-bug`). Scenario mang nhãn **`flaky`** **không** thành bug — nó là *chưa có kết luận*, `qc_status` để `not_run`.
34
+ 4. **Self-Review** *(mới)* — nạp `skills/qc/_shared/self-review-principles.md`, soát một lượt trước khi in report.
35
+ 5. **Output** report.
36
+ > **Self-Review ≠ Guard.** Guard là phép **đếm cơ học**, có hệ quả bắt buộc khi lệch. Self-review là lượt đọc lại **rộng hơn nhưng mềm hơn**, soát ba nhóm lỗi mà phép đếm không bắt được (bịa dữ kiện · lẫn suy đoán với sự thật · bỏ dở giữa chừng). Một bộ nguyên tắc tự soát **không bao giờ** được dùng làm lý do gỡ một Guard — chính file `self-review-principles.md` ghi rõ ranh giới đó ngay ở đầu.
37
+
35
38
 
36
39
  ---
37
40
 
@@ -98,12 +98,15 @@ Kết thúc bằng: **Status badge** (✅/❌/⚠️) · **Output Artifacts** (f
98
98
  - [05 · `/generate-design-spec`](05-generate-design-spec.md)
99
99
  - [06 · `/generate-bdd`](06-generate-bdd.md)
100
100
  - [07 · `/generate-tech-docs`](07-generate-tech-docs.md)
101
+ - [**11** · `/map-testids`](11-map-testids.md) ← **chốt hợp đồng test-id, TRƯỚC code**
101
102
  - [08 · `/review-tech-docs`](08-review-tech-docs.md)
102
103
 
104
+ > Danh sách này xếp theo **thứ tự chạy**, không theo số file. `/map-testids` giữ số `11` vì lý do lịch sử — giữ tên file để không gãy link cũ.
105
+ > §4.5.6 chốt ở đây là lý do **Phase Implementation** và **Phase QC Automation** chạy được **song song**.
106
+
103
107
  ### Phase Implementation
104
108
  - [09 · `/generate-code`](09-generate-code.md)
105
109
  - [10 · `/review-code`](10-review-code.md)
106
- - [11 · `/map-testids`](11-map-testids.md)
107
110
 
108
111
  ### Phase Dev Self-Test
109
112
  - [12 · `/dev-gen-test`](12-dev-gen-test.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@educa-corp/sdd-framework",
3
- "version": "0.9.5",
3
+ "version": "0.9.6",
4
4
  "description": "Spec Driven Development workflow framework for Claude Code",
5
5
  "bin": {
6
6
  "sdd-framework": "./bin/index.js"