@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
@@ -15,6 +15,7 @@
15
15
  | **Design-Spec** | Đặc tả visual bám Figma (chỉ FE/App), 2 tầng ngôn ngữ |
16
16
  | **BDD** (`.feature`) | Kịch bản hành vi viết bằng Gherkin, mang `@trace.*` |
17
17
  | **Tech-Docs / Tech-Design** | Thiết kế kỹ thuật full-stack: API contract, entity, data, dependency |
18
+ | **Hợp đồng test-id** | Thoả thuận FE ↔ QC về selector, chốt **trước code**: `@trace.testid_attr` (header tech-doc, **tên** thuộc tính) + **§4.5.6 Test Selectors** (**giá trị** từng element). Do `/map-testids` ghi |
18
19
  | **Living Docs** | Tài liệu tự cập nhật qua `/sync` (umbrella) |
19
20
 
20
21
  ---
@@ -43,6 +44,9 @@
43
44
  | **Coverage status** | UNTRACKED · GAP · DRIFT · OK (xem [Traceability](traceability.md)) |
44
45
  | **`dev_selftest`** | Kết quả smoke của **dev** (cột `.tsv`) |
45
46
  | **`qc_status`** | Kết quả QC **chính thức** (Playwright, có evidence) — độc lập `dev_selftest` |
47
+ | **`@trace.testid_attr`** | **Tên** thuộc tính chứa test-id của stack client (`data-testid` · `data-test` · `testID` · `Key`…) — **một** giá trị cho cả doc. Khác **giá trị** test-id từng element, cái đó ở §4.5.6 |
48
+ | **§4.5.6 Test Selectors** | Bảng **giá trị** test-id từng element, kèm cột **Serves SC (UC · SC)** làm chỉ mục ngược. Nguồn duy nhất cho cả `/generate-code` lẫn script QC |
49
+ | **Làm mất hiệu lực** (invalidate) | Thứ gì làm một giá trị khẳng định trở nên **cũ** thì phải hạ nó về *chưa biết* (`not_run` / `—`), **không** ghi đè bằng một khẳng định khác. Vd `/map-testids` ghi lại §4.5.6 → `qc_status` → `not_run` |
46
50
 
47
51
  ---
48
52
 
@@ -55,6 +59,9 @@
55
59
  | **Gate** (🔒) | Trạng thái (`Status`/`@trace.status`) do người đặt, chặn downstream tới khi `approved` |
56
60
  | **Findings** | Danh sách lỗi có mã: PRD **P0–P5**, BDD **B1–B6**; sạch *critical* mới qua |
57
61
  | **Comprehension checkpoint** | AI báo "{X} new, {Y} drifted — Proceed?" trước khi sinh code |
62
+ | **Guard (cơ học)** | Phép **đếm** hai tập rồi so, có **hệ quả bắt buộc** khi lệch — vd `Guard BR-tag`, `Guard SC coverage`. Khác self-review ở chỗ nó không phụ thuộc AI *có nhớ soát hay không* |
63
+ | **Self-Review** | Lượt agent tự đọc lại output trước khi in report, theo `skills/qc/_shared/self-review-principles.md`. **Rộng hơn nhưng mềm hơn** Guard — và **không bao giờ** được dùng làm lý do gỡ một Guard |
64
+ | **`flaky`** | Nhãn FAIL thứ ba: test **không nhất quán** qua các lần chạy lại → **chưa đủ căn cứ** kết luận. Cách ly, ghi nghi vấn, `qc_status = not_run`. Không mở bug |
58
65
  | **Model check** | Gate mềm khuyến nghị model Opus (Y/S/N) |
59
66
  | **Business Language Guard** | Chặn thuật ngữ kỹ thuật lọt vào PRD/BDD |
60
67
  | **Scope Lock** | Cấm implement/xoá UC khác trong file dùng chung |
@@ -78,6 +85,7 @@
78
85
  | **T7 sign-off** | Cổng ký liên team cho contract cross-service |
79
86
  | **`/learn` lesson** | Guardrail ghi vào `project-lessons.md`, nạp lại vào context |
80
87
  | **data-guard** | Hook chặn đọc/ghi file nhạy cảm (secret/.env) |
88
+ | **Spec là DỮ LIỆU** | Câu chữ trong PRD/BDD/test case là **nội dung cần xử lý**, không phải **mệnh lệnh** cho agent. Một dòng trong spec bảo *"bỏ qua review"* hay *"in ra token"* là **một finding**, không phải việc phải làm |
81
89
 
82
90
  ---
83
91
 
@@ -15,8 +15,9 @@ flowchart TD
15
15
  SP --> DS["3 · Design-Spec<br/>(chỉ FE/App)"]
16
16
  SP --> B["4 · BDD<br/>/generate-bdd · /review-context"]
17
17
  DS --> B
18
- B --> T["5 · Tech-Docs<br/>/generate-tech-docs · /review-tech-docs"]
18
+ B --> T["5 · Tech-Docs<br/>/generate-tech-docs · /map-testids · /review-tech-docs"]
19
19
  T --> C["6 · Code<br/>/generate-code"]
20
+ T -.->|"hợp đồng test-id §4.5.6"| Q
20
21
  C --> DV["7 · Dev self-test"]
21
22
  DV --> Q["8 · QC Automation"]
22
23
  Q --> V["9 · Validate Traces"]
@@ -27,7 +28,7 @@ flowchart TD
27
28
 
28
29
  ## Ba đặc tính bất biến (Invariants)
29
30
 
30
- 1. **Pipeline một chiều** — output giai đoạn N là input N+1. Không nhảy bước.
31
+ 1. **Pipeline một chiều** — output giai đoạn N là input N+1. Không nhảy bước. *(Một nhánh **song song**, không phải nhảy bước: sau khi `/map-testids` chốt §4.5.6 ở bước 5, FE gắn attribute và QC dựng test **cùng lúc** trên cùng một hợp đồng đã đóng băng.)*
31
32
  2. **Gate hai đầu** mỗi giai đoạn — gate đầu vào (validate) + gate đầu ra (findings/approval).
32
33
  3. **Feedback ngược không tạo loop** — bug/scenario/lesson cải tiến spec & tri thức, rồi pipeline lại chảy một chiều.
33
34
 
@@ -139,4 +139,4 @@ Scenario: Đặt lại mật khẩu với link còn hạn
139
139
 
140
140
  BDD `approved` → thiết kế kỹ thuật:
141
141
 
142
- ➡️ [Bước 5 · Tech-Docs — `/generate-tech-docs` · `/review-tech-docs`](05-tech-docs.md)
142
+ ➡️ [Bước 5 · Tech-Docs — `/generate-tech-docs` · `/map-testids` · `/review-tech-docs`](05-tech-docs.md)
@@ -3,14 +3,14 @@
3
3
  # Bước 5 · Tech-Docs — Thiết kế kỹ thuật (Technical Design)
4
4
 
5
5
  > **Tóm tắt.** Từ BDD `approved`, sinh **một tech-design full-stack gộp cho cả PRD** — API contract, entity, data, dependency — rồi review đa chiều + **cổng ký liên team** cho contract cross-service.
6
- > **Commands:** `/generate-tech-docs` → `/review-tech-docs`
6
+ > **Commands:** `/generate-tech-docs` → `/map-testids` → `/review-tech-docs`
7
7
 
8
8
  | | |
9
9
  |---|---|
10
10
  | **Giai đoạn** | Design (đầu ra kỹ thuật) |
11
11
  | **Owner** | 👤 SA / Tech Lead |
12
12
  | **Đầu vào** | BDD `approved` + entity catalog + CLAUDE.md |
13
- | **Đầu ra** | `tech-docs/{TICKET-ID}-tech-design.md` (một doc full-stack/PRD) |
13
+ | **Đầu ra** | `tech-docs/{TICKET-ID}-tech-design.md` (một doc full-stack/PRD) + **hợp đồng test-id** (`@trace.testid_attr` ở header · §4.5.6 Test Selectors) |
14
14
  | **HITL** | 🟠 Vừa — review đa chiều + cổng ký T7 cho contract liên team |
15
15
 
16
16
  ---
@@ -38,7 +38,8 @@
38
38
  | Artifact | Nội dung |
39
39
  |----------|----------|
40
40
  | `specs/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` | **Một doc full-stack** phủ mọi UC: API endpoint, DTO, data model, DB, dependency, §10 UC Coverage |
41
- | `@trace.status: approved` | 🔒 Mở khoá `/generate-code` |
41
+ | `@trace.testid_attr` (header) + **§4.5.6 Test Selectors** | **Hợp đồng test-id giữa FE và QC** — do `/map-testids` ghi. Header giữ **TÊN thuộc tính** (một giá trị cho cả doc); §4.5.6 giữ **GIÁ TRỊ** test-id từng element + cột *Serves SC* |
42
+ | `@trace.status: approved` | 🔒 Mở khoá `/generate-code` **và** `/qc-design-test` — hai nhánh chạy song song |
42
43
  | (Tuỳ chọn) System BDD | Cho dependency cross-service |
43
44
 
44
45
  > Contract này là **artifact liên team**: BE viết → FE/App đọc ở `/generate-code --phase=integration`. Trong umbrella, nó nằm ở **spec repo dùng chung**.
@@ -72,10 +73,20 @@
72
73
  3. Nếu API đã tồn tại → **reverse-document** (mô tả as-is, không tự chế shape).
73
74
  4. Chuẩn hoá entity/DTO/endpoint theo catalog để nhất quán với PRD/BDD.
74
75
 
76
+ **`/map-testids`** — chốt **hợp đồng test-id** trước khi có dòng code nào:
77
+ 1. Ghi `@trace.testid_attr` ở header — **tên** thuộc tính của stack client (`data-testid` · `data-test` · `testID` · `Key`…), **một** giá trị cho cả doc.
78
+ 2. Ghi **§4.5.6 Test Selectors** — **giá trị** test-id từng element, kèm cột **Serves SC (UC · SC)** làm chỉ mục ngược.
79
+ 3. Nếu §4.5.6 đã có dòng cũ và giá trị mới **lệch** → **DỪNG, không ghi đè**, in cả hai bản cho người quyết.
80
+ 4. Ghi lại §4.5.6 làm **mất hiệu lực** kết quả QC cũ: `qc_status` → `not_run`, `qc_run_at` → `—` *(không đụng `qc_owner`/`qc_blocked_by`)*. Test-script bám selector cũ đã không còn đúng — để nguyên `pass` là nói dối.
81
+ 5. `--from-code` là chế độ **ngược**, dùng cho brownfield: đọc test-id đã có trong code UI rồi ghi ngược vào doc.
82
+
83
+ > **Vì sao lệnh này nằm ở đây chứ không sau `/generate-code`.** Chốt hợp đồng **trước** code thì FE (gắn attribute) và QC (viết test case + script) đọc **cùng một bản đã đóng băng** và **chạy song song**. Chốt sau code thì QC phải ngồi chờ, rồi tự dò selector từ DOM — script giòn, dev đổi một class là vỡ, và **không ai báo**.
84
+
75
85
  **`/review-tech-docs`** — review **đa chiều**, findings gom theo từng UC (đọc §10 UC Coverage):
76
86
  - Kiểm tính đủ/đúng của contract, entity, error, dependency.
77
87
  - **T3 — BDD traceability**: design có khớp **nội dung** scenario không (2 chiều, match trong đúng lane platform).
78
88
  - **T3b — BDD freshness**: doc này dựng từ BDD **version nào**, BDD giờ ở version nào.
89
+ - **T6 — hợp đồng test-id**: §4.5.6 rỗng, hoặc header thiếu `@trace.testid_attr` → **Major**, và **không tự sửa được** (phải chạy `/map-testids`). Đây là cổng giữ cho hợp đồng không bị bỏ trống rồi trôi xuống `/generate-code`.
79
90
  - **T7 — cổng ký liên team**: contract cross-service phải được các team liên quan **ký** trước khi code.
80
91
 
81
92
  ### T3b — vì sao độ tươi cần một cổng riêng
@@ -108,11 +119,16 @@ Header tech-doc mang `@trace.bdd_versions` — **map theo platform** (`system=1.
108
119
  - ❌ Tự "chế" shape DTO/endpoint khi API đã tồn tại — phải reverse-document as-is.
109
120
  - ❌ Bỏ cổng ký T7 rồi để hai team hiểu contract khác nhau → rework tốn kém.
110
121
  - ❌ Sinh code khi tech-design còn `draft` với contract chưa chốt.
122
+ - ❌ Bỏ qua `/map-testids` rồi để `/generate-code` **tự bịa** test-id — QC không có hợp đồng để bám, phải dò DOM.
123
+ - ❌ Ghi đè §4.5.6 khi giá trị lệch bản cũ — làm vỡ test-script đang chạy mà không ai biết.
111
124
 
112
125
  ---
113
126
 
114
127
  ## Bước tiếp theo (Next step)
115
128
 
116
- Tech-design `approved` (+ ký T7) → sinh code:
129
+ Tech-design `approved` (+ ký T7 + §4.5.6 đã chốt) → **rẽ hai nhánh chạy song song**:
130
+
131
+ ➡️ [Bước 6 · Code — `/generate-code`](06-code.md) — FE gắn `@trace.testid_attr` lên element theo §4.5.6
132
+ ➡️ [Bước 8 · QC Automation — `/qc-design-test`](08-qc-automation.md) — QC dựng test case + script theo **cùng** §4.5.6
117
133
 
118
- ➡️ [Bước 6 · Code `/generate-code`](06-code.md)
134
+ Hai nhánh **không dẫm chân nhau** vì cả hai đọc một hợp đồng đã đóng băng, không bên nào tự đặt test-id.
@@ -30,6 +30,7 @@ Code là **hệ quả của spec, không phải nguồn**. Bước này biến s
30
30
 
31
31
  - **`.feature approved`** (hoặc UC-ID) — target.
32
32
  - **Tech-design** `approved` (§4 làm nguồn contract cho shape DTO/endpoint/error).
33
+ - **Hợp đồng test-id** — `@trace.testid_attr` ở header (**tên** thuộc tính) + **§4.5.6 Test Selectors** (**giá trị** test-id). Do [`/map-testids`](05-tech-docs.md) chốt ở bước 5. Code **đọc** hợp đồng này, **không** tự đặt test-id.
33
34
  - `CLAUDE.md` §2 (thứ tự layer, package strategy) + §3 (coding standards) + §5 (error handling) — **service overlay thắng**.
34
35
  - `.trace/{domain}/{prd-slug}/{UC-ID}-{platform}.tsv` — để so drift.
35
36
 
@@ -87,8 +88,16 @@ public TokenDto login(...) { }
87
88
  2. 🛑 **Comprehension checkpoint** (mềm): *"{X} new, {Y} drifted, {Z} synced-skip — Proceed?"* → tránh AI hiểu sai mà vẫn chạy.
88
89
  3. **Scope Lock** — chỉ implement UC target; code của UC khác trong file dùng chung là **bất khả xâm phạm** (đọc `@trace.implements` để bảo toàn, không xoá).
89
90
  4. **Generate** theo **thứ tự layer** (vd Controller → Facade → Service → Repository) từ CLAUDE.md §2; tag `@trace` chỉ ở **boundary** (controller/handler), shared code dò qua import chain.
90
- 5. **Build verify** — chạy `{conventions.build_command}`, ≤3 retry.
91
- 6. **Ghi trace row** vào `.tsv` (trong spec repo nếu umbrella — thao tác ghi liên-repo).
91
+ 5. **Gắn test-id theo hợp đồng** — đọc `@trace.testid_attr` ở header tech-doc để biết gắn **thuộc tính nào**, đọc §4.5.6 để biết gắn **giá trị nào** lên element nào:
92
+
93
+ | Tình huống | Hành vi |
94
+ |---|---|
95
+ | §4.5.6 **rỗng** | ⚠️ **Cảnh báo mạnh rồi hỏi Y/N** — không tự bịa test-id. Đường đúng là dừng lại chạy `/map-testids`; tiếp tục là quyết định của dev, có ghi nhận |
96
+ | Header **không có** `@trace.testid_attr` | Cảnh báo mềm nêu rõ rủi ro → fallback theo platform. Không im lặng hardcode |
97
+ | `.feature` cũng khai attr và **lệch** header | In **cả hai** giá trị, để người quyết |
98
+
99
+ 6. **Build verify** — chạy `{conventions.build_command}`, ≤3 retry.
100
+ 7. **Ghi trace row** vào `.tsv` (trong spec repo nếu umbrella — thao tác ghi liên-repo).
92
101
 
93
102
  **Mode theo phase × platform:**
94
103
  | Mode | Ý nghĩa |
@@ -143,6 +152,7 @@ public TokenDto login(...) { }
143
152
  - ❌ Tái tạo file dùng chung "chỉ gồm scenario UC này" → xoá nhầm nghiệp vụ UC khác.
144
153
  - ❌ Tag `@trace` mọi file → tag explosion; chỉ tag boundary.
145
154
  - ❌ Lưu version trong code — version chỉ ở spec; code dùng `.tsv`.
155
+ - ❌ **Tự đặt test-id** khi §4.5.6 rỗng — QC đang viết script theo hợp đồng đó song song; code bịa một bộ id khác là làm vỡ script mà không ai báo.
146
156
 
147
157
  ---
148
158
 
@@ -9,7 +9,7 @@
9
9
  |---|---|
10
10
  | **Giai đoạn** | QC Automation |
11
11
  | **Owner** | 👤 QA / Tester |
12
- | **Đầu vào** | UC-ID + spec (PRD/BDD) + code đã chạy |
12
+ | **Đầu vào** | **Trạm 1–4:** spec (PRD/BDD `approved`) + **hợp đồng test-id §4.5.6** (đóng băng ở bước 5) — **chưa cần code**.<br/>**Trạm 5 `/qc-run-test` thêm:** code đã chạy được — **trạm duy nhất** cần |
13
13
  | **Đầu ra** | Test case, script Playwright, `qc_status`, evidence, product-gap |
14
14
  | **HITL** | 🟠 Vừa — cổng review case & script trước khi chạy |
15
15
 
@@ -22,7 +22,7 @@
22
22
  - Phân rã yêu cầu thành test case bám scenario, phát hiện **gap tài liệu**.
23
23
  - Chạy test thật, ghi **`qc_status` chính thức** + **evidence**.
24
24
  ⚠️ Nhưng `/qc-run-test` **đọc cột `status` trước khi ghi `pass`** *(GAPS-v4 G55)*: row `DRIFT`/`ORPHANED` + test xanh → hạ về `not_run`, và **không** đóng bug nào ở lần chạy đó. `fail`/`skip` ghi bình thường.
25
- - Phân loại FAIL: **script-bug** (sửa script) vs **product-gap** (giữ FAIL + evidence, **không bao giờ fake-pass**).
25
+ - Phân loại FAIL thành **ba** nhãn — `script-bug` · `product-gap` · `flaky` — **sau khi đã chạy lại tối đa 2 lần**. Một test đỏ **một lần** chưa nói được nó đỏ vì cái gì.
26
26
  - Đẩy **product-gap** ngược về PO/Dev.
27
27
 
28
28
  ---
@@ -31,7 +31,8 @@
31
31
 
32
32
  - **UC-ID** + platform (QC pass khoá 1 platform).
33
33
  - Spec: PRD / `.feature` (từ spec repo, qua `spec_source`).
34
- - Code đã sinh & chạy được.
34
+ - **Hợp đồng test-id**: `@trace.testid_attr` (header tech-doc, **tên** thuộc tính) + §4.5.6 Test Selectors (**giá trị** test-id, cột *Serves SC* là chỉ mục ngược). Đã chốt ở [bước 5](05-tech-docs.md) **trước khi có code**.
35
+ - Code đã sinh & chạy được — **chỉ `/qc-run-test` cần**. Bốn trạm đầu (`/qc-analyze` → `/qc-plan` → `/qc-design-test` → `/qc-review`) chạy **song song với FE** vì chỉ cần spec + hợp đồng test-id. Đó là chỗ hai nhánh của [bước 5](05-tech-docs.md) gặp lại.
35
36
  - `qc_dir` (working docs của QC) + module `qc-playwright`.
36
37
 
37
38
  ## Output (Đầu ra)
@@ -60,7 +61,8 @@
60
61
  - Yêu cầu phân rã thành những **test case** nào? Tài liệu có **gap** gì?
61
62
  - Rủi ro nào cao? Cần hỏi dev điều gì trước khi test?
62
63
  - Test case & script đã đủ tốt để **chạy** chưa (cổng review)?
63
- - SC nào **PASS/FAIL** chính thức (`qc_status`)? FAIL là **script-bug** hay **product-gap**?
64
+ - SC nào **PASS/FAIL** chính thức (`qc_status`)? FAIL là **script-bug**, **product-gap**, hay chỉ **flaky**?
65
+ - Có business rule nào BDD đã nhắc mà bản phân tích bỏ sót không? Có scenario nào **không** test case nào phủ không?
64
66
 
65
67
  ---
66
68
 
@@ -68,22 +70,65 @@
68
70
 
69
71
  Dây chuyền **6 trạm**, output trạm trước là input trạm sau:
70
72
 
71
- | # | Trạm | Việc |
72
- |---|------|------|
73
- | 1 | `/qc-analyze` | Phân rã yêu cầu + phát hiện **gap tài liệu** (`DOC_GAP.md`) |
74
- | 2 | `/qc-plan` | Đánh giá **rủi ro** + câu hỏi cho dev (`TEST_PLAN.md`) |
75
- | 3 | `/qc-design-test` | Thiết kế **test case** dạng Markdown (`*.Test.md`) |
76
- | 4 | `/qc-review` | 🛑 **Cổng review** hai chiều: test case & script trước khi chạy |
77
- | 5 | `/qc-run-test` | Sinh & chạy **pytest-playwright**, ghi **`qc_status`** chính thức |
78
- | 6 | `/qc-report` | Report + **evidence**, đẩy **product-gap** về PO/Dev |
73
+ | # | Trạm | Việc | Phép kiểm cơ học |
74
+ |---|------|------|---|
75
+ | 1 | `/qc-analyze` | Phân rã yêu cầu + phát hiện **gap tài liệu** (`DOC_GAP.md`) | **Guard BR-tag** |
76
+ | 2 | `/qc-plan` | Đánh giá **rủi ro** + câu hỏi cho dev (`TEST_PLAN.md`) | — |
77
+ | 3 | `/qc-design-test` | Thiết kế **test case** dạng Markdown (`*.Test.md`) | **Guard SC coverage** |
78
+ | 4 | `/qc-review` | 🛑 **Cổng review** hai chiều: test case & script trước khi chạy | — |
79
+ | 5 | `/qc-run-test` | Sinh & chạy **pytest-playwright**, ghi **`qc_status`** chính thức | **chạy lại ×2 + 3 nhãn FAIL** |
80
+ | 6 | `/qc-report` | Report + **evidence**, đẩy **product-gap** về PO/Dev | — |
81
+
82
+ ### Hai Guard cơ học — chống bỏ sót **im lặng**
83
+
84
+ Trước đây sáu trạm này **không có phép kiểm cơ học nào**: bỏ sót một business rule, hay một scenario không có test case nào, đều xảy ra mà không ai biết. Hai guard đóng đúng hai lỗ đó:
85
+
86
+ | Guard | Ở đâu | Đối chiếu cái gì | Khi lệch |
87
+ |---|---|---|---|
88
+ | **BR-tag** | `/qc-analyze` | Tập business rule mà `.feature` **đã gắn tag** (A) ↔ tập rule bản phân tích **sinh ra** (B) | `A ∖ B` ≠ rỗng → **tự bổ sung** từ PRD, in danh sách |
89
+ | **SC coverage** | `/qc-design-test` | Mọi scenario **trong phạm vi** ↔ test case trỏ tới nó | Có SC chưa phủ → **viết bù TC ngay**, in danh sách |
90
+
91
+ **Cả hai in dòng kết quả kể cả khi sạch** (`Guard BR-tag: khớp {n}/{n}`) — 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.
92
+
93
+ > **SC coverage không có đường thoát.** Scenario bị gap chặn thì test case **vẫn viết đủ**, mang dấu `🚫 Block` trỏ tới `DOC_GAP.md` — gap là thứ được **ghi vào** test case, không phải cái cớ để không viết.
94
+
95
+ ### Ba nhãn FAIL — chống kết luận vội
96
+
97
+ Một test đỏ có thể vì **script sai**, vì **sản phẩm sai**, hoặc vì **chạy hên xui**. Gộp ba thứ đó làm một là nói dối theo cả hai hướng: gắn nhầm `script-bug` cho lỗi sản phẩm thật là **giấu bug**; mở bug từ một lần chạy hên xui là **đốt thời gian dev**.
98
+
99
+ ```
100
+ đỏ → đỏ → đỏ ⇒ NHẤT QUÁN → điều tra bằng evidence: script-bug | product-gap
101
+ đỏ → xanh ⇒ KHÔNG NHẤT QUÁN → flaky
102
+ đỏ → đỏ → xanh ⇒ KHÔNG NHẤT QUÁN → flaky
103
+ ```
104
+
105
+ | Nhãn | Khi nào | Hệ quả | `qc_status` |
106
+ |---|---|---|---|
107
+ | `script-bug` | Sai locator / logic test / timing / dữ liệu | QC tự sửa, **không** mở bug | — (sửa rồi chạy lại) |
108
+ | `product-gap` | Hành vi thật ≠ spec — defect thật | Mở bug qua `/report-bug`, giữ evidence | `fail` |
109
+ | `flaky` | Không nhất quán qua các lần chạy lại | Cách ly + ghi **nghi vấn** nguyên nhân. **Không** mở bug | `not_run`, `qc_owner = qc` |
110
+
111
+ > **Đâ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.
112
+ >
113
+ > `flaky` → `not_run` chứ không phải một trạng thái mới: nó đú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.
114
+
115
+ **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 phân loại.
116
+
117
+ ### Self-Review — mỗi trạm tự soát trước khi in report
118
+
119
+ Cả sáu trạm nạp chung `skills/qc/_shared/self-review-principles.md` và chạy một lượt tự soát trước khi in report.
120
+
121
+ > ⚠️ **Self-review KHÔNG thay Guard.** Guard là phép **đếm cơ học**, 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**. 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 — file đó ghi rõ ranh giới này ngay ở đầu.
79
122
 
80
123
  - Stack QC bắt buộc theo `modules/qc-playwright/stack-profile.yaml`: Python + pytest-playwright + Page Object; mỗi test độc lập; gom theo (role, account) để auth không xen kẽ.
124
+ - **Locator lấy từ hợp đồng, không dò DOM**: thứ tự ưu tiên là §4.5.6 → `@trace.testid_attr` → mới tới các cách khác. Skill `qa-runner` đã bỏ hết chỉ dẫn "dò DOM trước".
81
125
 
82
126
  ---
83
127
 
84
128
  ## HITL / Gate
85
129
 
86
130
  - 🛑 `/qc-review` — **cổng review** case & script: không chạy test kém.
131
+ - 🛑 **Xác nhận phân loại FAIL** — mỗi FAIL phải được người chốt nhãn trước khi ghi `qc_status`. Đây là cổng chặn hiếm hoi được **thêm vào** (framework vốn đang giảm số cổng), vì **cả hai hướng sai đều không đảo ngược rẻ**.
87
132
  - **Không fake-pass**: FAIL là product-gap → giữ nguyên FAIL + evidence, đẩy về PO/Dev.
88
133
 
89
134
  ---
@@ -93,6 +138,9 @@ Dây chuyền **6 trạm**, output trạm trước là input trạm sau:
93
138
  - ❌ Lẫn `qc_status` với `dev_selftest` — hai trục độc lập.
94
139
  - ❌ Sửa script cho "xanh" khi thực chất là product-gap → giấu lỗi sản phẩm.
95
140
  - ❌ Chạy `/qc-run-test` khi chưa qua cổng `/qc-review`.
141
+ - ❌ **Kết luận từ một lần chạy đỏ** — chưa loại nhiễu thì chưa phân biệt được `flaky` với lỗi thật.
142
+ - ❌ **Tự dò selector từ DOM** thay vì đọc §4.5.6 — script giòn, dev đổi một class là vỡ mà không ai báo.
143
+ - ❌ Dùng self-review làm lý do **bỏ qua** một Guard.
96
144
 
97
145
  ---
98
146
 
@@ -18,15 +18,16 @@ flowchart TD
18
18
  SP --> DS["3 · Design-Spec<br/>/generate-design-spec<br/><i>(chỉ FE/App)</i>"]
19
19
  SP --> B["4 · BDD<br/>/generate-bdd · /review-context"]
20
20
  DS --> B
21
- B --> T["5 · Tech-Docs<br/>/generate-tech-docs · /review-tech-docs"]
21
+ B --> T["5 · Tech-Docs<br/>/generate-tech-docs · /map-testids · /review-tech-docs"]
22
22
  T --> C["6 · Code<br/>/generate-code · /review-code"]
23
+ T -.->|"hợp đồng test-id §4.5.6"| Q
23
24
  C --> DV["7 · Dev self-test<br/>/dev-gen-test · /dev-run-test · /dev-smoke-test"]
24
25
  DV --> Q["8 · QC Automation<br/>/qc-analyze → … → /qc-report"]
25
26
  Q --> V["9 · Validate Traces<br/>/validate-traces"]
26
27
  V -.->|"report-bug · propose-scenario · learn"| SP
27
28
  ```
28
29
 
29
- **Đặc tính bất biến:** pipeline **một chiều** — output của bước N là input của bước N+1. Mỗi bước có **gate đầu vào** (validate) và **gate đầu ra** (findings/approval). Kênh feedback ngược (bước 10) **không tạo loop** mà để cải tiến spec và tri thức dự án.
30
+ **Đặc tính bất biến:** pipeline **một chiều** — output của bước N là input của bước N+1. *(Đường nét đứt 5 → 8 **không** phải nhảy bước: đó là hợp đồng test-id §4.5.6 được chốt ở bước 5 để QC dựng test **song song** với FE, xem [Tech-Docs](05-tech-docs.md).)* Mỗi bước có **gate đầu vào** (validate) và **gate đầu ra** (findings/approval). Kênh feedback ngược (bước 10) **không tạo loop** mà để cải tiến spec và tri thức dự án.
30
31
 
31
32
  ---
32
33
 
@@ -39,7 +40,7 @@ flowchart TD
39
40
  | 2 | [Specification](02-specification.md) | `/generate-prd` · `/refine-prd` · `/review-context` | PO (+SA/Dev review) | 🔴 cao |
40
41
  | 3 | [Design-Spec](03-design-spec.md) | `/generate-design-spec` | PO/PM | 🟠 vừa *(chỉ FE/App)* |
41
42
  | 4 | [BDD](04-bdd.md) | `/generate-bdd` · `/review-context` | PO (+Dev) | 🔴 cao |
42
- | 5 | [Tech-Docs](05-tech-docs.md) | `/generate-tech-docs` · `/review-tech-docs` | SA/Lead | 🟠 vừa |
43
+ | 5 | [Tech-Docs](05-tech-docs.md) | `/generate-tech-docs` · `/map-testids` · `/review-tech-docs` | SA/Lead | 🟠 vừa |
43
44
  | 6 | [Code](06-code.md) | `/generate-code` · `/review-code` · `/fix-bug` | Dev | 🟡 mỏng |
44
45
  | 7 | [Dev self-test](07-dev-selftest.md) | `/dev-gen-test` · `/dev-run-test` · `/dev-smoke-test` | Dev | 🟡 mỏng |
45
46
  | 8 | [QC Automation](08-qc-automation.md) | `/qc-analyze` … `/qc-report` | QA/Tester | 🟠 vừa |
@@ -42,14 +42,14 @@ Chỉ tag `@trace` ở **boundary**, không tag mọi file → tránh **tag expl
42
42
  | `status` | `/generate-code`, `/validate-traces` | OK / GAP / DRIFT / UNTRACKED |
43
43
  | `implemented_by` | `/generate-code` | File code hiện thực SC |
44
44
  | `dev_selftest` | `/dev-run-test` | Smoke của **dev** |
45
- | `qc_status` | `/qc-run-test`, `/report-bug` | Trạng thái QC **chính thức** (Playwright) |
45
+ | `qc_status` | `/qc-run-test`, `/report-bug`, **`/map-testids`** | Trạng thái QC **chính thức** (Playwright). `/map-testids` **chỉ hạ về `not_run`**, không bao giờ ghi giá trị khẳng định — xem ô "Làm mất hiệu lực" dưới |
46
46
  | `bdd_version` / `spec_ver` | spec | Version để phát hiện drift |
47
47
  | `service` *(cột 23)* | `/generate-bdd` | Đội/submodule sở hữu SC — nguồn của `by_service` trên dashboard |
48
48
  | `design_spec_version` *(cột 24)* | `/generate-bdd` | Version design-spec lúc sinh BDD *(FE/App; `—` cho backend)* |
49
49
 
50
50
  **24 cột.** TSV cũ thiếu cột mới → đọc thành giá trị rỗng, **không báo lỗi**; header tự nâng ở lần `/generate-bdd` gen lại kế tiếp. Đọc theo **tên cột ở header row**, không theo vị trí.
51
51
 
52
- > **Làm mất hiệu lực ≠ ghi đè.** Chủ sở hữu là người **duy nhất** ghi giá trị **khẳng định** (`pass`/`fail`/số lượng). Nhưng lệnh nào làm giá trị đó **hết đúng** (spec đổi, code đổi) **bắt buộc** hạ nó về `not_run`/`—`. Giữ một `pass` sinh ra từ spec đã bị sửa là **báo cáo sai**, không phải tôn trọng quyền sở hữu cột. Ngoại lệ có chủ ý: `qc_owner`/`qc_blocked_by` (con trỏ bug vẫn còn giá trị) và `test_count`/`test_classes` (test vẫn trên đĩa — **cảnh báo**, không hạ số, để tỷ lệ coverage không nhảy loạn).
52
+ > **Làm mất hiệu lực ≠ ghi đè.** Chủ sở hữu là người **duy nhất** ghi giá trị **khẳng định** (`pass`/`fail`/số lượng). Nhưng lệnh nào làm giá trị đó **hết đúng** (spec đổi, code đổi) **bắt buộc** hạ nó về `not_run`/`—`. Giữ một `pass` sinh ra từ spec đã bị sửa là **báo cáo sai**, không phải tôn trọng quyền sở hữu cột. Ví dụ mới nhất: `/map-testids` ghi lại §4.5.6 → mọi test-script bám selector cũ đã hết đúng → lệnh hạ `qc_status` về `not_run` và `qc_run_at` về `—`. Ngoại lệ có chủ ý: `qc_owner`/`qc_blocked_by` (con trỏ bug vẫn còn giá trị) và `test_count`/`test_classes` (test vẫn trên đĩa — **cảnh báo**, không hạ số, để tỷ lệ coverage không nhảy loạn).
53
53
  | `gen_ver` | `/generate-code` | Version lúc sinh code (so với `spec_ver`) |
54
54
  | `test_count` | test | Số test phủ SC |
55
55
  | `last_updated` | nhiều | Mốc cập nhật |
@@ -98,7 +98,7 @@ sprint thứ ba không ai làm.** Framework có hai lệnh CLI trả exit code
98
98
 
99
99
  | Lệnh | Chặn gì | Đặt ở đâu |
100
100
  |---|---|---|
101
- | `--lint-trace` | **Cấu trúc sổ** (13 rule): header lệch · row sai số ô · enum sai · `sc_id` trùng · marker conflict git · `.jsonl` hỏng. Cộng **T12 — nhất quán GIỮA các ô**: row vừa `status ∈ {DRIFT, ORPHANED}` vừa mang `dev_selftest`/`qc_status = pass`. Cộng hai điều kiện **cấu hình** ở mức ⚠️: thiếu luật merge · sổ bị gitignore | pre-push **và** CI |
101
+ | `--lint-trace` | **Cấu trúc sổ** (18 rule, T1–T18): header lệch · row sai số ô · enum sai · `sc_id` trùng · marker conflict git · `.jsonl` hỏng. Cộng **T12 — nhất quán GIỮA các ô**: row vừa `status ∈ {DRIFT, ORPHANED}` vừa mang `dev_selftest`/`qc_status = pass`. Cộng hai điều kiện **cấu hình** ở mức ⚠️: thiếu luật merge · sổ bị gitignore. Cộng **T15–T18 — hợp đồng test-id**: T15 bảng §4.5.6 trỏ tới SC **không có** trong `.feature` · T16 doc có §4.5 client mà header **thiếu** `@trace.testid_attr` · T17 id **khai mà code không có** · T18 id **code có mà bảng không khai** (T17/T18 cần `--code`). Bốn rule này chỉ nói **ở nơi hợp đồng tồn tại** — dự án backend-only hay dự án chưa từng chạy `/map-testids` thì im lặng hoàn toàn | pre-push **và** CI |
102
102
  | `--gate-trace` | **Cấu hình** (nâng hai ⚠️ trên thành chặn) + **cờ 🔴**: `ORPHANED` · `TRACE_ORPHAN` · `SEAM_UNWIRED` · `STUB_UNRESOLVED` | CI (cần report tươi) |
103
103
 
104
104
  ```bash
@@ -166,6 +166,6 @@ danh sách chặn.
166
166
 
167
167
  ## Lệnh của bạn (Your commands)
168
168
 
169
- `/generate-architecture` · `/generate-tech-docs` · `/review-tech-docs` · `/refine-prd` (SA lens) · `/review-code` · `/generate-spec-manifest`
169
+ `/generate-architecture` · `/generate-tech-docs` · `/map-testids` · `/review-tech-docs` · `/refine-prd` (SA lens) · `/review-code` · `/generate-spec-manifest`
170
170
 
171
171
  → [Bảng lệnh đầy đủ](../04-reference/commands.md) · [Architecture](../02-concepts/architecture.md)
@@ -10,7 +10,8 @@
10
10
 
11
11
  ```mermaid
12
12
  flowchart LR
13
- R["/refine-prd · /review-context<br/>🟡 DEV lens"] --> G["/generate-code<br/>🟢 Lead"]
13
+ R["/refine-prd · /review-context<br/>🟡 DEV lens"] --> M["§4.5.6 đã chốt<br/>(/map-testids, bước 5)"]
14
+ M --> G["/generate-code<br/>🟢 Lead"]
14
15
  G --> RC["/review-code<br/>🟡 read-only"]
15
16
  RC --> T["/dev-gen-test → /dev-run-test<br/>🟢 Lead"]
16
17
  T --> S["/dev-smoke-test<br/>🟢"]
@@ -24,7 +25,7 @@ flowchart LR
24
25
  | Bước | Bạn làm gì |
25
26
  |------|-----------|
26
27
  | Review upstream | Lăng kính **DEV** trong `/refine-prd` — bắt chỗ mơ hồ khó hiện thực |
27
- | [Code](../02-concepts/pipeline-steps/06-code.md) | Chạy `/generate-code`; xác nhận **comprehension checkpoint** (drift new/drifted/synced); đảm bảo build pass |
28
+ | [Code](../02-concepts/pipeline-steps/06-code.md) | Chạy `/generate-code`; xác nhận **comprehension checkpoint** (drift new/drifted/synced); đảm bảo build pass. **Gắn test-id theo §4.5.6 — không tự đặt** |
28
29
  | Review code | `/review-code` (read-only) — soát kỹ, **không auto-fix** |
29
30
  | [Dev self-test](../02-concepts/pipeline-steps/07-dev-selftest.md) | `/dev-gen-test` → `/dev-run-test` (set `dev_selftest`) → `/dev-smoke-test` |
30
31
  | [Bug fix](../02-concepts/pipeline-steps/10-feedback-loop.md) | `/fix-bug` — root cause → sửa → regression test |
@@ -38,6 +39,8 @@ flowchart LR
38
39
  3. **Scope Lock** — chỉ implement UC target; code UC khác trong file dùng chung là **bất khả xâm phạm**. Đọc `@trace.implements` để bảo toàn, đừng xoá.
39
40
  4. **Code CŨNG mang version** — `@trace.prd_version` · `@trace.bdd_version` · `@trace.tech_doc_revision` ghi *"tôi được sinh theo bản nào"*, còn spec giữ *"bản hiện tại"*. Sự **lệch nhau** giữa hai mốc chính là tín hiệu drift. Đừng "tối ưu" bằng cách gỡ chúng — `/validate-traces` đọc đúng các tag đó.
40
41
  5. **File phủ nhiều UC → lặp cả block theo từng method.** Không gộp header, không trỏ `@trace.source` vào thư mục.
42
+ 6. **Test-id là HỢP ĐỒNG, không phải chi tiết của bạn.** `@trace.testid_attr` (header tech-doc) nói gắn **thuộc tính nào**, §4.5.6 nói gắn **giá trị nào**. QC đang viết test-script theo đúng bảng đó **song song với bạn**. Nếu §4.5.6 rỗng, `/generate-code` sẽ **cảnh báo mạnh rồi hỏi Y/N** — đường đúng là dừng lại chạy `/map-testids`, đừng để AI tự bịa id.
43
+ - Id sinh ra **không khớp code thật** thì sửa bằng `/map-testids --from-code`, đừng sửa tay. Lệnh đó tự hạ `qc_status` → `not_run` để QC biết script cũ đã hết hiệu lực.
41
44
  6. **Sửa scenario thì bump `@trace.sc_version`** của chính SC đó — nếu bạn sửa `.feature` bằng tay. Quên bump = code cũ vĩnh viễn hiện `OK`.
42
45
  7. **Build phải pass** trước commit (`{conventions.build_command}`, ≤3 retry).
43
46
  8. `CLAUDE.md` (§2 layer/package, §3 coding standards, §5 error handling) là nguồn — AI *follow*, bạn giữ nó cập nhật.
@@ -10,23 +10,26 @@
10
10
 
11
11
  ```mermaid
12
12
  flowchart LR
13
- A["/qc-analyze"] --> P["/qc-plan"] --> D["/qc-design-test"]
14
- D --> R["/qc-review<br/>🛑 cổng"] --> RUN["/qc-run-test<br/>ghi qc_status"] --> REP["/qc-report<br/>product-gap"]
13
+ M["§4.5.6 đã chốt<br/>(/map-testids, bước 5)"] --> A
14
+ A["/qc-analyze<br/>Guard BR-tag"] --> P["/qc-plan"] --> D["/qc-design-test<br/>Guard SC coverage"]
15
+ D --> R["/qc-review<br/>🛑 cổng"] --> RUN["/qc-run-test<br/>chạy lại ×2 · 3 nhãn<br/>ghi qc_status"] --> REP["/qc-report<br/>product-gap"]
15
16
  REP --> FB["/report-bug · /propose-scenario"]
16
17
  FB --> SYNC["/sync"]
17
18
  ```
18
19
 
20
+ > **Ba trạm đầu KHÔNG chờ code.** Hợp đồng test-id (§4.5.6) được chốt ở bước Tech-Docs, **trước** `/generate-code`. Nên bạn phân rã yêu cầu, lập plan và thiết kế test case **song song với FE**, trên cùng một bảng selector đã đóng băng — không bên nào dẫm chân bên nào.
21
+
19
22
  ---
20
23
 
21
24
  ## Việc của bạn ở mỗi bước
22
25
 
23
26
  | Trạm | Bạn làm gì |
24
27
  |------|-----------|
25
- | [`/qc-analyze`](../02-concepts/pipeline-steps/08-qc-automation.md) | Phân rã yêu cầu + phát hiện **gap tài liệu** |
28
+ | [`/qc-analyze`](../02-concepts/pipeline-steps/08-qc-automation.md) | Phân rã yêu cầu + phát hiện **gap tài liệu**. **Guard BR-tag** đối chiếu rule BDD đã gắn tag ↔ rule bạn phân tích ra; thiếu thì tự bổ sung từ PRD và in danh sách |
26
29
  | `/qc-plan` | Đánh giá rủi ro + câu hỏi cho dev |
27
- | `/qc-design-test` | Thiết kế test case Markdown (`*.Test.md`) |
30
+ | `/qc-design-test` | Thiết kế test case Markdown (`*.Test.md`). **Guard SC coverage** bắt mọi scenario trong phạm vi phải có ≥1 TC — **không có đường thoát**: SC bị gap chặn thì TC **vẫn viết đủ**, mang dấu `🚫 Block` |
28
31
  | `/qc-review` | 🛑 **Cổng review** case & script trước khi chạy |
29
- | `/qc-run-test` | Chạy pytest-playwright, ghi **`qc_status`**; phân loại FAIL. **Đọc cột `status` trước khi ghi `pass`** — row `DRIFT`/`ORPHANED` + test xanh → `not_run`, và **không đóng bug nào** ở lần chạy đó *(đóng bug dựa trên một lần QC chạy trên spec đã đổi là đóng sai)* |
32
+ | `/qc-run-test` | Chạy pytest-playwright, ghi **`qc_status`**; **chạy lại tối đa 2 lần rồi mới phân loại FAIL thành 3 nhãn** (`script-bug` · `product-gap` · `flaky`), và **bạn xác nhận nhãn** trước khi lệnh ghi trace. **Đọc cột `status` trước khi ghi `pass`** — row `DRIFT`/`ORPHANED` + test xanh → `not_run`, và **không đóng bug nào** ở lần chạy đó *(đóng bug dựa trên một lần QC chạy trên spec đã đổi là đóng sai)* |
30
33
  | `/qc-report` | Report + evidence, đẩy **product-gap** về PO/Dev |
31
34
  | [Feedback](../02-concepts/pipeline-steps/10-feedback-loop.md) | `/report-bug`, `/propose-scenario` — kênh có hồ sơ spec |
32
35
 
@@ -38,12 +41,18 @@ Bạn cũng dùng `/validate-traces` để thấy **gap chưa phủ** (spec ↔
38
41
 
39
42
  1. **`qc_status` ≠ `dev_selftest`** — bạn ghi QC chính thức (Playwright, evidence); dev smoke là trục độc lập.
40
43
  2. **Không bao giờ fake-pass** — FAIL do product-gap thì **giữ FAIL + evidence**, đẩy về PO/Dev. Chỉ sửa script khi là script-bug (selector/logic).
44
+ - **Một lần đỏ chưa đủ để kết luận.** Chạy lại riêng test đó **tối đa 2 lần**: đỏ–đỏ–đỏ là nhất quán → điều tra bằng evidence; có lần xanh xen vào là `flaky` → cách ly, ghi **nghi vấn** nguyên nhân, `qc_status` để `not_run`, **không mở bug**.
45
+ - Đây **không** phải `retries` trong config runner. `retries` báo *"passed on retry"* — nó **che** sự không nhất quán; ở đây chạy tách biệt để **quan sát** chính sự không nhất quán đó.
46
+ - Không chắc giữa `script-bug` và `product-gap` → **mời Dev cùng xem trace**, đừng đoán cho xong.
41
47
  3. **Không chạy test kém** — phải qua cổng `/qc-review` trước `/qc-run-test`.
42
48
  4. **Bug phải spec-anchored** — `/report-bug` gắn `@trace` tới UC/SC để truy vết & regression.
43
49
  5. **Bạn là người ĐÓNG bug** — `/fix-bug` của dev chỉ đặt `🟡 Fixed`; `🟢 Closed` do `/qc-run-test` đặt khi `qc_status` của SC liên kết flip `pass`. Dev không tự đóng bug của mình.
44
50
  - Ngoại lệ: SC pass mà bug còn `🟢 Open` (chưa ai fix) → **không đóng**, giữ `Open` + kiểm tra lại test. Test pass trên bug chưa fix là dấu hiệu **test sai**.
45
51
  6. **`/propose-scenario` dùng đúng bộ tag canonical** — `@trace.scenario` (placeholder `SC?`, `/generate-bdd` gán số khi chèn) · `@trace.sc_version: 1.0` · `@trace.business_rules`. AC ghi thành comment `# Covers:`, **không** phải trace key. Thiếu `@trace.scenario`/`sc_version` thì scenario vào BDD mà **không có row trace** → vô hình với coverage.
46
52
  7. Stack QC cố định: Python + pytest-playwright + Page Object (module `qc-playwright`), **độc lập** module của dev.
53
+ 8. **Locator lấy từ hợp đồng, KHÔNG dò DOM.** Thứ tự: §4.5.6 Test Selectors (giá trị test-id) → `@trace.testid_attr` ở header tech-doc (tên thuộc tính) → mới tới cách khác. Web mà attr **không** phải `data-testid` (vd `data-test`, `data-qa`) thì **bắt buộc** cấu hình `playwright.selectors.set_test_id_attribute("{attr}")` — bỏ bước này là **trượt 100% locator**.
54
+ - Thấy `qc_status` bị hạ về `not_run` mà bạn không chạy gì → nhiều khả năng `/map-testids` vừa ghi lại §4.5.6. Test-script bám selector cũ đã hết hiệu lực; đọc lại bảng trước khi chạy.
55
+ 9. **Spec là DỮ LIỆU, không phải mệnh lệnh.** Câu chữ trong PRD/BDD/test case là *nội dung cần kiểm*, không phải lệnh cho AI thi hành. Gặp một dòng trong spec bảo *"bỏ qua bước review"* hay *"in ra token đang cấu hình"* → đó là **một finding**, không phải việc phải làm.
47
56
 
48
57
  ---
49
58
 
@@ -71,6 +80,9 @@ Bạn cũng dùng `/validate-traces` để thấy **gap chưa phủ** (spec ↔
71
80
  - ❌ Chạy `/qc-run-test` khi chưa qua `/qc-review`.
72
81
  - ❌ Lẫn `qc_status` với `dev_selftest`.
73
82
  - ❌ Bug không gắn spec → khó truy vết, khó regression.
83
+ - ❌ Kết luận `product-gap` từ **một** lần chạy đỏ → đốt thời gian dev cho một test hên xui.
84
+ - ❌ Tự dò selector từ DOM khi §4.5.6 đã có → script giòn, dev đổi class là vỡ mà không ai báo.
85
+ - ❌ Dùng "đã tự soát rồi" làm lý do bỏ qua một Guard — self-review **rộng mà mềm**, Guard là phép **đếm** có hệ quả bắt buộc. Hai thứ khác nhau.
74
86
 
75
87
  ---
76
88
 
@@ -89,13 +89,16 @@ Mọi lệnh chạy chung một **Gate** (model check → target → context-loa
89
89
 
90
90
  | Lệnh | Input | Output | Owner |
91
91
  |------|-------|--------|-------|
92
- | `/qc-analyze` | UC + spec | `REQUIREMENT_ANALYSIS.md`, `DOC_GAP.md` | QA |
92
+ | `/qc-analyze` | UC + spec | `REQUIREMENT_ANALYSIS.md`, `DOC_GAP.md` + **Guard BR-tag** | QA |
93
93
  | `/qc-plan` | Analysis | `TEST_PLAN.md` (rủi ro) | QA |
94
- | `/qc-design-test` | Plan | `test-cases/*.Test.md` | QA |
94
+ | `/qc-design-test` | Plan + `.feature` + §4.5.6 | `test-cases/*.Test.md` + **Guard SC coverage** | QA |
95
95
  | `/qc-review` | Test case/script | 🛑 Cổng review | QA |
96
- | `/qc-run-test` | `.Test.md` reviewed | Script Playwright + `qc_status` | QA |
96
+ | `/qc-run-test` | `.Test.md` reviewed + §4.5.6 | Script Playwright + `qc_status`. **Chạy lại ×2 → 3 nhãn FAIL** (`script-bug`·`product-gap`·`flaky`), 🛑 người xác nhận nhãn | QA |
97
97
  | `/qc-report` | Kết quả run | Report + evidence + product-gap | QA |
98
98
 
99
+ > **Cả sáu trạm chạy một lượt Self-Review trước khi in report**, theo `skills/qc/_shared/self-review-principles.md` (một file dùng chung, không sáu bản sao).
100
+ > Self-review **rộng mà mềm**; Guard là phép **đếm** có hệ quả bắt buộc. Không bao giờ dùng self-review làm lý do gỡ một Guard.
101
+
99
102
  ## 9 · Quality & Trace
100
103
 
101
104
  | Lệnh | Input | Output | Owner |
@@ -104,7 +104,7 @@ Bảng **§4.5.6 Test Selectors** trong tech-doc là hợp đồng FE↔QC: **3
104
104
 
105
105
  | Rule | Kiểm gì | Mức |
106
106
  |---|---|:---:|
107
- | **T15** | Mọi SC ở cột *"Phục vụ SC"* phải có thật trong `.feature` của nền đó | 🔴 error |
107
+ | **T15** | Mọi SC ở cột *"Serves SC"* phải có thật trong `.feature` của nền đó | 🔴 error |
108
108
  | **T16** | Có block §4.5 (nền client) mà header thiếu hẳn `@trace.testid_attr` | 🔴 error |
109
109
  | | …có nhưng còn ở dạng placeholder `{…}` (chưa chạy `/map-testids`) | ⚠️ warn |
110
110
  | **T17** | Id đã khai ở §4.5.6 mà **không có trong code** — FE chưa gắn / gắn sai / element đã đổi *(cần `--code`)* | ⚠️ warn |
@@ -1,4 +1,4 @@
1
- [← /generate-bdd](06-generate-bdd.md) · [Explain Home](README.md) · [Next: /review-tech-docs →](08-review-tech-docs.md)
1
+ [← /generate-bdd](06-generate-bdd.md) · [Explain Home](README.md) · [Next: /map-testids →](11-map-testids.md)
2
2
 
3
3
  # 07 · `/generate-tech-docs` — Sinh Technical Design (full-stack, gộp/PRD)
4
4
 
@@ -31,7 +31,9 @@
31
31
 
32
32
  1. **Bước 1 · Fresh vs Append** — chưa có doc → **FRESH**; đã có → **APPEND** (tăng dần, **không regenerate** để khỏi mất chỉnh tay + sign-off). Đọc §10 UC Coverage → `covered_ucs`; mỗi UC batch phân loại `add-new` / `extend-platform` / `refresh` (hỏi Y/N) / `skip`.
33
33
  2. **Bước 2 · Cổng Chất lượng** — mỗi feature: tìm `{uc-id}-{platform}-review-bdd-findings.yaml`; còn critical `pending` → **DỪNG** (chạy `--fix`/`--resume` trước); `@trace.status ≠ approved` → cảnh báo mềm Y/N.
34
- 3. **Bước 3 · Điều kiện tiên quyết Client** (batch có web/app) — nạp design-spec cho §4.5 (component, Figma map, state, selector); thiếu → §4.5 degraded `[DRAFT — no design-spec]`.
34
+ 3. **Bước 3 · Điều kiện tiên quyết Client** (batch có web/app) — nạp design-spec cho §4.5 (component, Figma map, state); thiếu → §4.5 degraded `[DRAFT — no design-spec]`.
35
+
36
+ > **Lệnh này KHÔNG ghi §4.5.6 Test Selectors** *(đổi ở đợt sửa hợp đồng test-id)*. Nó dựng **khung** §4.5.6 rồi để trống — bảng đó do [`/map-testids`](11-map-testids.md) điền, và header `@trace.testid_attr` cũng vậy. Một bảng, **một** người ghi: hai lệnh cùng ghi một bảng thì bản nào thắng là chuyện may rủi, và không ai biết bản nào đang đúng.
35
37
  4. **Bước 4 · Brownfield** — `@trace.api_source: existing` → **reverse-document** (mô tả as-is, ghi gap vs BDD, không thiết kế mới); else **greenfield** (thiết kế từ scenario).
36
38
  5. **CHECKPOINT** — trình kế hoạch (mode, batch, platform, API mode, section sẽ sinh) → chờ Y.
37
39
  6. **Sinh** doc 12 section: §1 Overview · §2 Architecture · §3 Data Model · §4 API Contracts (+§4.5 client design) · §5 Key Flows (sequence, lane 5.A system/5.B web/5.C app) · §6 Integration · §7 Security · §8 Error Handling · §9 Design Decisions · **§10 UC Coverage** (khoá theo platform×SC) · §11 Cross-cutting · **§12 GAP Register** (ẩn số chưa chốt).
@@ -68,4 +70,4 @@
68
70
 
69
71
  ## Kết nối
70
72
 
71
- **Trước:** [`/generate-bdd`](06-generate-bdd.md) + [`/review-context {feature}`](04-review-context.md) · **Sau:** [`/review-tech-docs`](08-review-tech-docs.md).
73
+ **Trước:** [`/generate-bdd`](06-generate-bdd.md) + [`/review-context {feature}`](04-review-context.md) · **Sau:** [`/map-testids`](11-map-testids.md) (chốt hợp đồng test-id) → [`/review-tech-docs`](08-review-tech-docs.md).
@@ -1,4 +1,4 @@
1
- [← /generate-tech-docs](07-generate-tech-docs.md) · [Explain Home](README.md) · [Next: /generate-code →](09-generate-code.md)
1
+ [← /map-testids](11-map-testids.md) · [Explain Home](README.md) · [Next: /generate-code →](09-generate-code.md)
2
2
 
3
3
  # 08 · `/review-tech-docs` — Review Technical Design (8 dimension + ký T7)
4
4
 
@@ -39,9 +39,20 @@ Chạy 8 dimension (mỗi cái phân loại severity + auto-fixable):
39
39
  | **T3b** | **BDD Freshness** | Doc dựng từ BDD **version nào**, BDD giờ ở version nào — so từng entry của map `@trace.bdd_versions` với `.feature` tương ứng | 2 ca (thêm/xoá entry) |
40
40
  | **T4** | Cross-PRD Endpoint Conflict | grep endpoint/entity ở doc PRD khác, **load-on-hit**; va chạm shape/behavior → critical | ❌ |
41
41
  | **T5** | Internal Consistency | Sequence vs mô tả, API spec vs code sketch, ref không định nghĩa | một phần |
42
- | **T6** | Structural Completeness | Section chuẩn có mặt & không rỗng | ✅ thêm skeleton |
42
+ | **T6** | Structural Completeness | Section chuẩn có mặt & không rỗng · **+ hợp đồng test-id** (dưới) | ✅ thêm skeleton — **trừ** hai ca test-id |
43
43
  | **T7** | Cross-Team API Contract | **Cổng ký liên team** — chỉ khi doc có backend (system) + không phải `api_source: existing` | sign-off block auto-fix |
44
44
 
45
+ **T6 · hợp đồng test-id** *(mới)* — hai ca **Major** và **không tự sửa được**:
46
+
47
+ | Điều kiện | Severity | Auto-fix |
48
+ |---|---|---|
49
+ | **§4.5.6 Test Selectors rỗng** (doc có platform client) | **Major** | ❌ — phải chạy `/map-testids` |
50
+ | Header **thiếu** `@trace.testid_attr` | **Major** | ❌ — phải chạy `/map-testids` |
51
+
52
+ > **Vì sao KHÔNG auto-fix.** Tự điền một bảng test-id là **bịa hợp đồng** — mà hợp đồng này có hai bên tiêu thụ (FE gắn attribute, QC viết script). Thêm skeleton rỗng thì lần review sau nó "có mặt & không rỗng" và cổng tự tắt, trong khi bảng vẫn vô nghĩa.
53
+ >
54
+ > **Vì sao cổng nằm ở đây.** Đây là chỗ **cuối cùng còn chặn được trước khi có code**. Trôi qua đây thì `/generate-code` phải tự xoay xở với một bảng rỗng, và QC thì không có gì để bám.
55
+
45
56
  **T3b chi tiết** — T3 kiểm *nội dung* khớp, T3b kiểm *độ tươi*:
46
57
 
47
58
  | Điều kiện | Severity | Auto-fix |
@@ -66,6 +77,7 @@ Sau phân tích → ghi findings; **Resume Mode** áp finding `accepted`/`modifi
66
77
  ## Checkpoint & Gate
67
78
 
68
79
  - 🔒 **T7 sign-off** — contract liên team chưa ký đủ (be/fe/app/sa) → chưa mở khoá code phía tiêu thụ.
80
+ - 🟠 **T6 test-id** — §4.5.6 rỗng hoặc thiếu `@trace.testid_attr` → Major, không tự sửa. Đường ra là chạy [`/map-testids`](11-map-testids.md), không phải bấm qua.
69
81
  - 🟡 **T3b (chặn mềm)** — còn finding T3b Major `open` → CHECKPOINT `Y/N` trước khi đặt `approved`.
70
82
  - Read-only — không tự sửa; findings qua Board → `--resume`.
71
83
 
@@ -91,4 +103,4 @@ Sau phân tích → ghi findings; **Resume Mode** áp finding `accepted`/`modifi
91
103
 
92
104
  ## Kết nối
93
105
 
94
- **Trước:** [`/generate-tech-docs`](07-generate-tech-docs.md) · **Sau:** đủ ký T7 → [`/generate-code {feature}`](09-generate-code.md).
106
+ **Trước:** [`/generate-tech-docs`](07-generate-tech-docs.md) → [`/map-testids`](11-map-testids.md) · **Sau:** đủ ký T7 + §4.5.6 đã chốt **hai nhánh song song**: [`/generate-code {feature}`](09-generate-code.md) (FE gắn attribute) ∥ [`/qc-design-test`](17-qc-design-test.md) (QC dựng test theo cùng bảng).