@educa-corp/sdd-framework 0.9.7 → 0.9.8

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 (104) hide show
  1. package/bin/qc-base-map.json +13 -11
  2. package/bin/self-check.js +49 -4
  3. package/bin/trace-schema.json +3226 -3187
  4. package/core/FRAMEWORK_VERSION +1 -1
  5. package/core/commands/qc-analyze.md +2 -2
  6. package/core/commands/qc-automation-assess.md +3 -3
  7. package/core/commands/qc-design-script.md +60 -30
  8. package/core/commands/qc-design-test.md +79 -7
  9. package/core/commands/qc-plan.md +1 -1
  10. package/core/commands/qc-report.md +85 -76
  11. package/core/commands/qc-review-script.md +25 -16
  12. package/core/commands/qc-review-testcase.md +8 -7
  13. package/core/commands/qc-run-manualtest.md +1 -1
  14. package/core/commands/qc-run-script.md +15 -8
  15. package/core/modules/qc-playwright-ts/module.yaml +13 -0
  16. package/core/modules/qc-playwright-ts/stack-profile.yaml +99 -0
  17. package/core/modules/qc-wdio-appium/module.yaml +20 -0
  18. package/core/modules/qc-wdio-appium/stack-profile.yaml +107 -0
  19. package/core/skills/qc/qa-analyst/data-flow.md +1 -1
  20. package/core/skills/qc/qa-automation-assess/matrix.md +6 -3
  21. package/core/skills/qc/{qa-runner → qa-designer}/exploratory/session.md +8 -2
  22. package/core/skills/qc/qa-designer/functional/api.md +1 -1
  23. package/core/skills/qc/qa-designer/functional/job.md +128 -0
  24. package/core/skills/qc/qa-designer/integration/api.md +1 -1
  25. package/core/skills/qc/qa-designer/integration/db.md +1 -1
  26. package/core/skills/qc/qa-designer/integration/{kafka.md → queue.md} +20 -4
  27. package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +17 -0
  28. package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +17 -0
  29. package/core/skills/qc/qa-reviewer/script/_shared/review-rules.md +121 -0
  30. package/core/skills/qc/qa-reviewer/script/api/auth.md +49 -0
  31. package/core/skills/qc/qa-reviewer/script/api/endpoint.md +89 -0
  32. package/core/skills/qc/qa-reviewer/script/api/security.md +46 -0
  33. package/core/skills/qc/qa-reviewer/script/exploratory.md +2 -2
  34. package/core/skills/qc/qa-reviewer/script/mobile/e2e.md +41 -0
  35. package/core/skills/qc/qa-reviewer/script/mobile/functional.md +90 -0
  36. package/core/skills/qc/qa-reviewer/script/mobile/integration.md +41 -0
  37. package/core/skills/qc/qa-reviewer/script/mobile/non-functional.md +43 -0
  38. package/core/skills/qc/qa-reviewer/script/web/e2e.md +46 -0
  39. package/core/skills/qc/qa-reviewer/script/web/functional.md +111 -0
  40. package/core/skills/qc/qa-reviewer/script/web/integration.md +46 -0
  41. package/core/skills/qc/qa-reviewer/script/web/non-functional.md +49 -0
  42. package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +1 -1
  43. package/core/skills/qc/qa-reviewer/shared/review-file-template.md +26 -7
  44. package/core/skills/qc/qa-reviewer/test-case/e2e.md +1 -1
  45. package/core/skills/qc/qa-reviewer/test-case/exploratory.md +1 -1
  46. package/core/skills/qc/qa-reviewer/test-case/functional.md +1 -1
  47. package/core/skills/qc/qa-reviewer/test-case/integration.md +1 -1
  48. package/core/skills/qc/qa-reviewer/test-case/non-functional.md +1 -1
  49. package/core/skills/qc/qa-script-designer/_shared/api-conventions.md +94 -0
  50. package/core/skills/qc/qa-script-designer/_shared/file-naming-and-folders.md +109 -0
  51. package/core/skills/qc/qa-script-designer/_shared/mobile-conventions.md +196 -0
  52. package/core/skills/qc/qa-script-designer/_shared/web-conventions.md +257 -0
  53. package/core/skills/qc/qa-script-designer/api/auth.md +43 -0
  54. package/core/skills/qc/qa-script-designer/api/endpoint.md +61 -0
  55. package/core/skills/qc/qa-script-designer/api/security.md +41 -0
  56. package/core/skills/qc/qa-script-designer/mobile/e2e.md +35 -0
  57. package/core/skills/qc/qa-script-designer/mobile/functional/feature.md +32 -0
  58. package/core/skills/qc/qa-script-designer/mobile/functional/screen.md +42 -0
  59. package/core/skills/qc/qa-script-designer/mobile/integration.md +39 -0
  60. package/core/skills/qc/qa-script-designer/mobile/non-functional.md +39 -0
  61. package/core/skills/qc/qa-script-designer/web/e2e.md +36 -0
  62. package/core/skills/qc/qa-script-designer/web/functional/api.md +39 -0
  63. package/core/skills/qc/qa-script-designer/web/functional/gui-feature.md +34 -0
  64. package/core/skills/qc/qa-script-designer/web/functional/gui-screen.md +42 -0
  65. package/core/skills/qc/qa-script-designer/web/integration.md +43 -0
  66. package/core/skills/qc/qa-script-designer/web/non-functional.md +42 -0
  67. package/core/skills/qc/qa-script-runner/mobile/run.md +38 -0
  68. package/core/skills/qc/qa-script-runner/report.md +41 -0
  69. package/core/skills/qc/qa-script-runner/web/run.md +48 -0
  70. package/core/steps/qc-scope.md +43 -0
  71. package/core/steps/report-footer.md +2 -2
  72. package/docs/02-concepts/pipeline-steps/07-dev-selftest.md +1 -1
  73. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +10 -10
  74. package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +1 -1
  75. package/docs/02-concepts/traceability.md +1 -1
  76. package/docs/03-guides/developer.md +1 -1
  77. package/docs/03-guides/tester-qa.md +40 -12
  78. package/docs/04-reference/commands.md +1 -1
  79. package/docs/04-reference/modules.md +2 -1
  80. package/docs/explain/17-qc-design-test.md +2 -2
  81. package/docs/explain/19-qc-run-test.md +4 -4
  82. package/docs/explain/20-qc-report.md +1 -1
  83. package/docs/explain/23-fix-bug.md +2 -2
  84. package/docs/plans/qc-surgery/01-checklist.md +18 -6
  85. package/docs/plans/qc-surgery/PLAN_v2.md +295 -0
  86. package/docs/plans/qc-surgery/exec-S-ap-stack-typescript.md +420 -0
  87. package/docs/plans/qc-surgery/exec-S0-guard-cam-stack-cu.md +400 -0
  88. package/docs/plans/qc-surgery/exec-S1-hai-module-thay-qc-playwright.md +267 -0
  89. package/docs/plans/qc-surgery/exec-S2-qa-runner-thanh-script-designer-runner.md +340 -0
  90. package/docs/plans/qc-surgery/exec-S3-viet-lai-tieu-chi-review-script.md +322 -0
  91. package/docs/plans/qc-surgery/exec-S5-an-theo-don-dau-vet-stack-cu.md +292 -0
  92. package/package.json +1 -1
  93. package/core/modules/qc-playwright/stack-profile.yaml +0 -66
  94. package/core/skills/qc/qa-reviewer/script/e2e.md +0 -95
  95. package/core/skills/qc/qa-reviewer/script/functional.md +0 -109
  96. package/core/skills/qc/qa-reviewer/script/integration.md +0 -99
  97. package/core/skills/qc/qa-reviewer/script/non-functional.md +0 -134
  98. package/core/skills/qc/qa-runner/e2e.md +0 -49
  99. package/core/skills/qc/qa-runner/functional/api.md +0 -35
  100. package/core/skills/qc/qa-runner/functional/gui-feature.md +0 -57
  101. package/core/skills/qc/qa-runner/functional/gui-screen.md +0 -61
  102. package/core/skills/qc/qa-runner/integration.md +0 -47
  103. package/core/skills/qc/qa-runner/non-functional.md +0 -49
  104. package/core/skills/qc/qa-runner/report/report.md +0 -37
@@ -0,0 +1,420 @@
1
+ ---
2
+ buoc: Bước S (chen giữa Đợt 2 và Đợt 3)
3
+ title: Áp stack đã chốt — TypeScript + Playwright / WDIO+Appium
4
+ phu_thuoc: Đợt 2 xong (d2-b1, d2-b2, d2-b3)
5
+ trang_thai: ✅ XONG 2026-09-17 — 6/6 việc · bốn lệnh kiểm XANH · R5 đỏ 27→0
6
+ ---
7
+
8
+ # Bước S — Áp stack đã chốt lên ba trạm còn sinh mã sai
9
+
10
+ ← [`PLAN_v2.md`](PLAN_v2.md) · [`01-checklist.md`](01-checklist.md) · [`00-nhat-ky.md`](00-nhat-ky.md)
11
+
12
+ | | |
13
+ |---|---|
14
+ | **Lớp** | chen giữa Đợt 2 và Đợt 3 — **CHẶN Đợt 3** |
15
+ | **Bước** | **S/1** (một bước, một commit cho lớp lệnh) |
16
+ | **File code** | `modules/qc-*` · `skills/qc/qa-script-{designer,runner}/` · `skills/qc/qa-reviewer/script/` · 3 `commands/qc-*-script.tmpl` |
17
+ | **File test** | `test/run.js` — thêm **một guard cơ học** (xem B4 · G3) |
18
+ | **Ngày xong** | — |
19
+ | **Phụ thuộc** | Đợt 2 xong ✅ · 4 quyết định ở §1 **chốt 2026-09-17** ✅ |
20
+ | **Ai dùng nó** | `/qc-design-script` · `/qc-run-script` · `/qc-review-script` · gián tiếp `/qc-report` (Đợt 3) |
21
+
22
+ ---
23
+
24
+ ## 0 · Đo lại trước — và PLAN_v2 dính đúng Bẫy 1 của chính nó
25
+
26
+ > Luật ở [`PLAN_v2.md §5 Bẫy 1`](PLAN_v2.md): *"chạy lại mọi lệnh đếm trong kế hoạch trước khi
27
+ > làm theo nó."* Chạy lại thì **3/6 con số trong §3 của PLAN_v2 sai** — lần thứ **tư** liên tiếp.
28
+
29
+ | Nơi khai | PLAN_v2 §3 khai | Đo lại 2026-09-16 | Lệch |
30
+ |---|---|---|---|
31
+ | `skills/qc/qa-runner/` | 6 file · 44 KB | **8 file · 26,931 B (26.3 KB)** | +2 file · **−40 % dung lượng** |
32
+ | đích `qa-script-designer/` *(proposal)* | 12 file · 88 KB | **14 file · 57,864 B (56.5 KB)** | +2 file · **−36 %** |
33
+ | đích `qa-script-runner/` *(proposal)* | 4 file · 20 KB | 4 file · **13,445 B (13.1 KB)** | đúng số file · **−35 %** |
34
+ | `modules/qc-playwright/` | 1 file · 66 dòng · thiếu `module.yaml` | ✅ **đúng y hệt** | — |
35
+ | ngân sách | 1368 / 1450 KB | ✅ **1368, còn 82 KB** | — |
36
+ | 4 lệnh kiểm | xanh · 244/244 | ✅ **244/244 PASS** | — |
37
+
38
+ **Vì sao lệch quan trọng, không phải chuyện làm tròn:** con số 44 KB → 88 KB đọc như *"thay một
39
+ bộ nhỏ bằng một bộ to gấp đôi"*. Số thật là **26 KB → 57 KB** — vẫn gấp đôi, nhưng **tổng khối
40
+ lượng nhỏ hơn ước lượng ~30 KB**, và điều đó đổi câu trả lời cho H1: gánh cả web lẫn mobile trong
41
+ một bước **rẻ hơn** so với bản kế hoạch mô tả.
42
+
43
+ **Ba phát hiện mới, không có trong PLAN_v2:**
44
+
45
+ | # | Phát hiện | Hệ quả |
46
+ |:-:|---|---|
47
+ | N1 | `active_platform` **đã có giá trị `system`** — `.agent/steps/qc-scope.md:9`, `bdd/system/` là hạng nhất trong `generate-bdd` | lane API **không phải trục thứ ba mới** — nó là **ô trống của một trục đã có**. Đổi hẳn câu trả lời §1.1 |
48
+ | N2 | Ngân sách 1450 KB **chỉ đếm `core/commands/*.md`** (`test/run.js:697`) — skill **không** tính | thêm 57 KB skill **không** chạm ngân sách. Rủi ro ngân sách chỉ nằm ở **3 file lệnh** (25+27+21 = 73 KB) |
49
+ | N3 | `docs/explain/18-qc-review.md` và `19-qc-run-test.md` **giải thích 2 lệnh đã bị tách bỏ từ Đợt 2** | **nợ độc lập mới** — chưa có trong PLAN_v2 §3 |
50
+
51
+ ---
52
+
53
+ ## 1 · BỐN CÂU — ĐÃ CHỐT 2026-09-17
54
+
55
+ > Giữ nguyên cả phần bằng chứng và các phương án **không** được chọn. Sáu tháng nữa, câu hỏi
56
+ > *"sao hồi đó không làm cách kia"* chỉ trả lời được nếu cách kia còn nằm đây.
57
+
58
+ | Mã | Câu | Chốt | Ngày |
59
+ |---|---|---|---|
60
+ | **S-API** | Lane API là nền thứ ba hay layer của web? | **Nền thứ ba ở tầng SKILL, KHÔNG phải module thứ ba** | 09-17 |
61
+ | **H1** | web + mobile cùng lúc hay web trước? | **B — cùng lúc (lệnh+module cả 3 lane), khác độ sâu (mobile port khung)** | 09-17 |
62
+ | **H2** | gỡ hẳn `qc-playwright` hay giữ deprecated? | **Gỡ hẳn** | 09-17 |
63
+ | **H3** | port `smoke.md` luôn? | **Không — để d4-b2** | 09-17 |
64
+
65
+ ### 1.1 · Lane API — nền thứ ba, hay một layer của web? ✅ **nền thứ ba ở tầng skill**
66
+
67
+ **Bằng chứng nghiêng về "nền thứ ba":**
68
+
69
+ | # | Bằng chứng | Nguồn |
70
+ |:-:|---|---|
71
+ | a | Thư mục gốc **riêng**: `api-automation/` — *"tách biệt với `automation/` (web) — tránh xung đột config và dependency"* | `API-Testing-Standards.md` §2.1 |
72
+ | b | `playwright.config.ts` **riêng**, `package.json` có thể riêng | §2.1 |
73
+ | c | **API Object ≠ Page Object**: `BaseAPI`, 1 method = 1 endpoint, **không** `expect()` trong object | §4 · `AGT-010` §2.2 |
74
+ | d | Dãy ID **độc lập**: `TC-API-001` ≠ `TC-001` *cùng một project* | §7.3 |
75
+ | e | `AGT-010` là agent riêng, **Approved** (`AGT-005` mới Draft) | `AGT-010` header |
76
+ | f | **`active_platform` đã có `system`** — `bdd/system/*.feature`, sổ trace `{UC}-system.tsv` | `qc-scope.md:9` **(N1)** |
77
+
78
+ **Bằng chứng nghiêng về "layer của web":**
79
+
80
+ | # | Bằng chứng | Nguồn |
81
+ |:-:|---|---|
82
+ | g | **Cùng stack**: Playwright + TypeScript + Playwright Test + `expect` — AD-API-001 nói rõ chọn Playwright API mode *"để giảm số tool, tái dùng infrastructure với Web testing"* | `API-Testing-Standards.md` §1 |
83
+ | h | Bộ đề xuất **đã xếp API dưới web**: `qa-script-designer/web/functional/api.md` · `qa-reviewer/script/web/functional/api.md` | `qcframework_proposal/` |
84
+ | i | Framework đã có `qa-designer/functional/api.md` + `qa-designer/api/*` (6 file) | `skills/qc/qa-designer/` |
85
+
86
+ > 🔑 **Chỗ hai bên tưởng mâu thuẫn nhưng không.** (i) là tầng **thiết kế test case** — *"test cái
87
+ > gì"*. Lane đang bàn là tầng **sinh mã** — *"code thế nào"*. Hai trục khác nhau, `qa-designer/`
88
+ > **không bị đụng** dù chốt kiểu gì.
89
+
90
+ **✅ CHỐT (09-17): nền thứ ba ở tầng SKILL, nhưng KHÔNG phải module thứ ba.**
91
+
92
+ ```
93
+ active_platform module skill lane
94
+ ─────────────────────────────────────────────────────────────────────────
95
+ web · webview → qc-playwright-ts → qa-script-designer/web/*
96
+ app · app-ios · … → qc-wdio-appium → qa-script-designer/mobile/*
97
+ system → qc-playwright-ts → qa-script-designer/api/*
98
+ (layout thứ hai:
99
+ api-automation/)
100
+ ```
101
+
102
+ Lý do: (c)(d)(e) làm **nội dung** khác hẳn → phải là file riêng; nhưng (g) làm **stack** y hệt
103
+ → dựng module thứ ba là chép hai lần cùng một dãy phiên bản `Playwright/TS v5.x`, và **hai bản
104
+ của một sự thật là nơi drift sống** *(đúng lý do `qc-scope.md` ra đời)*. Còn (f) nói lane API
105
+ không phải trục mới — nó lấp ô `system` mà **hôm nay đang hỏng im lặng**: chạy
106
+ `/qc-design-script` trên một PRD `system` thì lệnh vẫn sinh Page Object + Python UI script.
107
+
108
+ **Phương án KHÔNG chọn — "layer của web"** → bỏ `qa-script-designer/api/`, dồn vào
109
+ `web/functional/api.md` + `_shared/api-conventions.md`; `active_platform=system` map về lane
110
+ `web`. Rẻ hơn ~3 file, nhưng `api-automation/` (a) mất chỗ khai và ô `system` vẫn nhoè.
111
+
112
+ **Phương án KHÔNG chọn — "module thứ ba đầy đủ"** (`modules/qc-playwright-api/`) → trung thành
113
+ nhất với §2.1, nhưng dãy phiên bản `Playwright/TS v5.x` sống ở **hai** file.
114
+
115
+ ---
116
+
117
+ ### 1.2 · H1 — web + mobile cùng lúc, hay web trước? ✅ **B**
118
+
119
+ **Ràng buộc cứng, không phải sở thích** — [`PLAN_v2 §5 Bẫy 5`](PLAN_v2.md): tách được ≠ cái còn
120
+ lại dùng được. Gỡ `qc-playwright` mà chỉ port lane web thì `/qc-design-script` trên PRD `app`
121
+ **vẫn trỏ vào một module không còn tồn tại** — hỏng nặng hơn hôm nay.
122
+
123
+ | Phương án | Phạm vi | Cái giá |
124
+ |---|---|---|
125
+ | **A · cùng lúc, cùng độ sâu** | 3 lane đầy đủ | to nhất; mobile chưa có dự án thật để thử |
126
+ | **B · cùng lúc, khác độ sâu** ⭐ | **lớp lệnh + module: cả 3 lane**; **nội dung skill: web+api đầy đủ, mobile port khung `_shared/mobile-conventions.md` (12.7 KB) + 5 file lane** | mobile chưa được chạy thử thật — ghi rõ là hẹp *(§8 luật 4)* |
127
+ | **C · web trước + chặn cứng** | chỉ web(+api); `/qc-design-script` **từ chối** khi `active_platform=app`, kèm câu báo rõ | commit nhỏ nhất, **không sai im lặng**; nhưng đội mobile bị khoá cửa và phải mở lại ở một bước sau |
128
+
129
+ **✅ CHỐT (09-17): B.** Vì §0 đo được mobile chỉ là **12.7 KB `_shared` + 5 file lane ≈ 22 KB** —
130
+ rẻ hơn con số 88 KB mà bản kế hoạch cũ dựng lên. **C** là phương án lùi hợp lệ nếu cần commit
131
+ nhỏ; **A** bị loại vì viết sâu cho một nền chưa có dự án thật là viết hai lần.
132
+
133
+ > 🔴 **Nghĩa vụ đi kèm B — phải ghi, không được quên.** Lane mobile port **khung**, chưa chạy
134
+ > thử trên dự án thật. Theo [`PLAN_v2 §8 luật 4`](PLAN_v2.md) *("khai một phạm vi hẹp và nói rõ
135
+ > là hẹp")*, `modules/qc-wdio-appium/module.yaml` phải mang một dòng khai đúng điều đó, và
136
+ > `00-nhat-ky.md` phải có một dòng nợ: *"lane mobile chờ dự án thật để xác nhận"*.
137
+
138
+ ---
139
+
140
+ ### 1.3 · H2 — gỡ hẳn `qc-playwright` hay giữ deprecated 1 version? ✅ **gỡ hẳn**
141
+
142
+ **Bằng chứng:**
143
+ - Tiền lệ **đã chốt 09-16**: `E3` — *"gỡ `/qc-review` ngay, không giữ cầu"*.
144
+ - `bin/qc-base-map.json`: chỉ **3/82 entry** dính `qa-runner`, cả ba `targets: []`, `state: undecided`
145
+ → gỡ **không** làm đỏ `R16`.
146
+ - Giữ deprecated nghĩa là `modules/qc-playwright/stack-profile.yaml` (66 dòng Python) **vẫn ở
147
+ trong `core/`** — và nó là **nguồn duy nhất** mà 3 lệnh trích dẫn. Hai stack-profile QC cùng
148
+ sống = guard ở B4 không bao giờ xanh được.
149
+
150
+ **✅ CHỐT (09-17): gỡ hẳn.** Lịch sử đã nằm ở `qc-base-map.json` (snapshot `596149f`) và git.
151
+
152
+ **Phương án KHÔNG chọn — "giữ 1 version"** → guard S0 phải thêm ngoại lệ đường dẫn, tức **cửa
153
+ để Python quay lại**; và phải chốt thêm *"1 version"* là mốc nào, ai gỡ.
154
+
155
+ ---
156
+
157
+ ### 1.4 · H3 — port `qa-script-runner/smoke.md` luôn? ✅ **không, để d4-b2**
158
+
159
+ **Bằng chứng:** `smoke.md` = 5,895 B, thuộc `/qc-smoke-test` — lệnh của **d4-b2**, chưa tồn tại.
160
+ Port bây giờ để lại một file **không lệnh nào nạp**. `bin/self-check.js:274` (R4) đã cảnh báo
161
+ đúng lớp lỗi này cho key context; skill mồ côi thì **chưa có rule nào bắt** → nó sẽ nằm im.
162
+ Mặt khác d4-b2 **đã hết chặn** (chị QC chốt tên 09-16) và trạm 6 đã gắn `@smoke` cho `P0`.
163
+
164
+ **✅ CHỐT (09-17): KHÔNG port ở Bước S — để d4-b2**, và ghi một dòng nợ trỏ tới nó. Bước S đã là
165
+ bước to nhất của cả đợt; thêm một file cho một lệnh chưa có là mở rộng phạm vi không có người
166
+ dùng ở đầu kia.
167
+
168
+ **Phương án KHÔNG chọn — "port luôn"** → +1 file · +5.9 KB, và d4-b2 rút ngắn còn viết lệnh.
169
+
170
+ ---
171
+
172
+ # PHẦN A — Chuyện gì đang xảy ra
173
+ *(cho người không làm kỹ thuật · đọc xong phần này là đủ hiểu)*
174
+
175
+ ## A1 · Vấn đề
176
+
177
+ Đội QC đã chốt cách viết bài kiểm tra từ ngày 10/9 và xác nhận qua email. Từ hôm đó tới nay, bộ
178
+ khung vẫn phát ra bài viết theo cách cũ — cách đã bị bỏ. Ai nhận cũng phải viết lại từ đầu, nên
179
+ phần đáng lẽ được làm hộ trở thành phần phải dọn. Tệ hơn: không có gì báo sai. Mọi đèn kiểm đều
180
+ xanh, nên nhìn từ ngoài vào thì trạm này vẫn đang chạy tốt.
181
+
182
+ ## A2 · Cách giải quyết, nói bằng một hình ảnh
183
+
184
+ Như một xưởng in vẫn in bản đồ theo tên đường cũ sau khi thành phố đổi tên. Máy vẫn chạy, giấy
185
+ vẫn đẹp, không ai báo hỏng — chỉ người cầm bản đồ đi mới lạc, và lạc xong thì tưởng mình đọc sai.
186
+ Bước này thay **bản gốc trong máy**, một lần, cho cả ba loại bản đồ: web, điện thoại, dịch vụ.
187
+
188
+ ## A3 · Xong rồi thì thấy gì khác
189
+
190
+ Chạy lệnh sinh script sẽ ra file đuôi `.spec.ts` thay vì `.py`, nằm trong `automation/` (web)
191
+ hoặc `api-automation/` (dịch vụ), và lệnh **hỏi nền trước khi sinh** thay vì mặc định một nền.
192
+ Thêm một đèn kiểm mới: nếu chữ "Python" quay lại chỗ nào trong bộ QC, đèn đó đỏ.
193
+
194
+ ## A4 · Thuật ngữ dùng ở trên
195
+
196
+ - **stack** — bộ công cụ + ngôn ngữ mà test được viết bằng. Ở đây: TypeScript + Playwright (web, dịch vụ), WebdriverIO + Appium (điện thoại).
197
+ - **script** — bài kiểm tra ở dạng máy chạy được, khác với bài kiểm tra viết cho người đọc rồi làm tay.
198
+ - **nền / platform** — web · điện thoại · dịch vụ (`system`). Một lần chạy QC khoá đúng **một** nền.
199
+ - **module** — hồ sơ khai stack của một loại dự án, để lệnh đọc mà biết sinh code theo kiểu nào.
200
+ - **skill** — file hướng dẫn mà lệnh nạp lúc chạy; nội dung chi tiết nằm ở đây, không nằm trong lệnh.
201
+ - **Page Object / API Object** — cách gom thao tác lên một màn hình / một endpoint vào một chỗ, để sửa một lần thay vì sửa khắp nơi.
202
+ - **đèn kiểm / guard** — một phép kiểm tự động chạy sau mỗi lần sửa; đỏ thì không được commit.
203
+
204
+ ---
205
+
206
+ > ### ✅ Phép thử bắt buộc trước khi coi Phần A là xong
207
+ > **Đã thử với:** *(chưa — tự đọc to sau khi anh chốt §1, vì phạm vi đổi thì A3 đổi)* ·
208
+ > **ngày:** — · **phải giải thích thêm chỗ nào:** —
209
+
210
+ ---
211
+
212
+ # PHẦN B — Chi tiết kỹ thuật
213
+
214
+ ## B1 · Cách hiển nhiên là gì, và vì sao nó sai
215
+
216
+ ### Cách hiển nhiên 1 — tìm-thay chuỗi
217
+
218
+ Đo được: **363 dòng khớp** `pytest|python|\.py` trong `skills/ commands/ core/ templates/ rules/
219
+ bin/ docs/`, trải trên **47 file nguồn**. Cách hiển nhiên là thay `Python`→`TypeScript`,
220
+ `pytest`→`Playwright Test`, `.py`→`.spec.ts` rồi chạy 4 lệnh kiểm.
221
+
222
+ **Vì sao sai — cụ thể:** cái phải đổi **không phải từ vựng, là hình dạng**.
223
+
224
+ | Thứ đổi theo stack | `pytest-playwright` (nay) | `@playwright/test` + TS (chốt) |
225
+ |---|---|---|
226
+ | Nơi test sống | `tests/<project>/test_<feature>.py` | `automation/tests/<feature>/<feature>-happy-path.spec.ts` |
227
+ | Đối tượng | `pages/<feature>_page.py` · `BasePage` | `pages/<feature>.page.ts` — và **API lane dùng `api/<res>.api.ts` · `BaseAPI`, cấm `expect()` bên trong** |
228
+ | Cách gom setup | `conftest.py` + fixture pytest | `fixtures/*.fixture.ts` |
229
+ | Chạy | `python3 -m pytest` | `npx playwright test` |
230
+ | Mobile | *(không có)* | WDIO v9 + Appium v2 + UiAutomator2 · **gesture helper** · **environment validation checklist** |
231
+
232
+ Tìm-thay cho ra văn bản **đúng từ vựng TypeScript, đúng cấu trúc Python**. Và nó **qua cả 4 lệnh
233
+ kiểm** — `build.js` chỉ đúc template, `self-check.js` R1–R22 không biết stack, `run.js` không có
234
+ rule nào về stack QC, `lint-trace.js` soi sổ trace. Tức nó **vào được `main` với 244/244 xanh**,
235
+ rồi vỡ ở lần chạy thật đầu tiên của chị QC, hai tuần sau, dưới dạng *"framework sinh code không
236
+ chạy được"* — một câu không chỉ về đâu để sửa.
237
+
238
+ ### Cách hiển nhiên 2 — bê nguyên thư mục skill của bộ đề xuất
239
+
240
+ `qcframework_proposal/skills/qc/qa-script-designer/` có sẵn 14 file đúng hình dạng cần. Cách
241
+ hiển nhiên là copy cả cụm.
242
+
243
+ **Vì sao sai:** bộ đề xuất **chỉ nói tên stack**; `qc-base-new/` là *Official Project Knowledge*,
244
+ status **Approved**, và **ghim phiên bản cụ thể** (Appium v2.x · WebdriverIO v9.x · TypeScript
245
+ v5.x · UiAutomator2) + §Anti-patterns + §Flaky Policy. Copy đề xuất = chốt một stack **không có
246
+ số hiệu phiên bản** — đúng lớp lỗi mà `AD-API-001`/`AGT-010` sinh ra để chặn.
247
+
248
+ **Luật chia nguồn** *(đã ghi ở [`PLAN_v2 §4.2`](PLAN_v2.md), nhắc lại vì đây là chỗ dễ trượt)*:
249
+ **bố cục thư mục ← đề xuất · nội dung stack ← `qc-base-new/`.**
250
+
251
+ ## B2 · Cách làm đúng
252
+
253
+ **Thứ tự bắt buộc — guard trước, vá sau** *(quyết định `G3`, 09-16)*.
254
+
255
+ **S0 · Dựng đèn trước khi vá.** Thêm vào `test/run.js` một test: không dòng nào trong
256
+ `skills/qc/**` · `commands/qc-*.tmpl` · `modules/qc-*/` khớp `pytest|python3?\b|\.py\b`.
257
+ Chạy ngay → **đỏ với ~47 file**. Đó là bằng chứng đèn bắt được lỗi **đang sống**, không phải
258
+ đèn viết sau khi đã sạch nên vĩnh viễn xanh.
259
+
260
+ **S1 · Lớp module.** `modules/qc-playwright/` (1 file, 66 dòng, **thiếu `module.yaml`** — trong
261
+ khi `react/` và `flutter/` đều có 2 file) → hai module, mỗi cái **đủ cặp**:
262
+
263
+ | Module | `module.yaml` | `stack-profile.yaml` |
264
+ |---|---|---|
265
+ | `qc-playwright-ts` | `language: TypeScript` · `test_framework: "Playwright Test"` | `layout.web` = `automation/` · **`layout.api` = `api-automation/`** *(theo §1.1)* · locator priority · anti-patterns |
266
+ | `qc-wdio-appium` | `language: TypeScript` · `test_framework: "WebdriverIO + Appium"` | `layout.mobile` · gesture helper · environment validation checklist |
267
+
268
+ **S2 · Lớp skill.** `qa-runner/` **(8 file · 26.3 KB — bộ SINH MÃ Python, không phải "hướng dẫn
269
+ chạy test"; xem [`PLAN_v2 §5 Bẫy 3`](PLAN_v2.md))** → gỡ, thay bằng:
270
+
271
+ ```
272
+ qa-script-designer/
273
+ _shared/ web-conventions.md (16.5 KB) · mobile-conventions.md (12.7 KB) · file-naming-and-folders.md (7.3 KB)
274
+ web/ functional/{gui-screen,gui-feature,api}.md · integration.md · e2e.md · non-functional.md
275
+ mobile/ functional/{screen,feature}.md · integration.md · e2e.md · non-functional.md
276
+ api/ endpoint.md · auth.md · security.md ← lane thứ ba (S-API ✅)
277
+ qa-script-runner/
278
+ web/run.md · mobile/run.md · report.md ← smoke.md để d4-b2 (H3 ✅)
279
+ ```
280
+
281
+ Nguồn nội dung 3 file `api/`: `API-Testing-Standards.md` §4 *(API Object)* · §5 *(Test Spec, AAA)*
282
+ · §6 *(Assertion)* · §8 *(Test Design)* · §9 *(Security)*, cộng `AGT-010` §3 *(R01–R03)*.
283
+
284
+ **S3 · `qa-reviewer/script/`** 5 file phẳng (22 KB) → `script/{web,mobile,api}/*` — thực thi
285
+ `E1`, quyết định đã dời sang đây từ 09-16.
286
+
287
+ **S4 · Lớp lệnh — 3 file, và đây là chỗ duy nhất chạm ngân sách.** `qc-design-script`
288
+ `Role & stack` hiện **nhúng thẳng luật Python vào lệnh** (dòng 69–89 của `.tmpl`). Thay bằng
289
+ **bảng phân giải nền + một con trỏ sang skill**:
290
+
291
+ ```
292
+ active_platform → module → lane skill (bảng 3 dòng, ~10 dòng văn bản)
293
+ ```
294
+
295
+ **Con số và lý do:** 3 lệnh hiện **25 + 27 + 21 = 73 KB**; ngân sách còn **82 KB** và
296
+ `test/run.js:697` **chỉ đếm `core/commands/*.md`** *(N2)* — nên 57 KB skill **không tính vào
297
+ đây**. Kéo luật stack ra khỏi lệnh giúp 3 file **co lại**, và đó là cách duy nhất thêm lane thứ
298
+ ba mà không ăn vào 82 KB còn lại.
299
+
300
+ **S5 · Ăn theo.** `skills/qc/qa-automation-assess/matrix.md` (dòng 102–104 trỏ `qc-playwright`
301
+ Python) · `commands/qc-report.tmpl` (5 dòng) · `bin/trace-schema.json` (4 dòng: 1477 · 2419 ·
302
+ 2422 · 2496) · `bin/qc-base-map.json` (3 entry `undecided` → `decided`) · 6 file `docs/`.
303
+
304
+ ## B3 · Nếu làm sai thì hỏng theo kiểu nào
305
+
306
+ **Kiểu hỏng: Im lặng 🔴** *(và nó **đang** hỏng theo kiểu này ngay lúc này)*.
307
+
308
+ Hỏng ở đâu: lệnh sinh ra file `.py` mà không dự án nào chạy được — hoặc tệ hơn, `.spec.ts` đúng
309
+ từ vựng nhưng sai hình dạng thư mục nên `npx playwright test` không thấy file nào và báo
310
+ **"0 tests"**, tức **xanh**. Ai phát hiện: chị QC, ở lần chạy thật đầu tiên. Sau bao lâu: đo được
311
+ là **6 ngày và vẫn đang đếm** — stack chốt 09-10, hôm nay 09-16, 4 lệnh kiểm chưa đỏ một lần.
312
+
313
+ ## B4 · Verify bằng gì
314
+
315
+ | # | Phép thử | Kết quả mong đợi |
316
+ |:-:|---|---|
317
+ | 1 | **S0 chạy TRƯỚC khi sửa** | **ĐỎ**, liệt kê ~47 file. Xanh ngay từ đầu = đèn viết sai, làm lại |
318
+ | 2 | `grep -rniE 'pytest\|python' skills/ commands/ modules/` | **0** dòng (trừ `upstream/` và `docs/plans/`) |
319
+ | 3 | `ls modules/qc-playwright-ts modules/qc-wdio-appium` | mỗi thư mục **đúng 2 file** — bằng `react/` và `flutter/` |
320
+ | 4 | `ls modules/qc-playwright` | **không tồn tại** *(H2 ✅ gỡ hẳn)* |
321
+ | 5 | 4 lệnh kiểm | `build` ✅ · `self-check` R1–R22 ✅ · `run.js` **245/245** (thêm S0) ✅ · `lint-trace` T1–T20 ✅ |
322
+ | 6 | Ngân sách | `core/commands` **< 1450 KB**, và 3 lệnh script **≤ 73 KB** (không được phình) |
323
+ | 7 | **Phá có chủ đích:** đặt `active_platform=system` rồi chạy `/qc-design-script` | trỏ đúng lane API + `api-automation/` — **không** ra Page Object. *(Hôm nay: ra Page Object Python.)* |
324
+ | 8 | **Phá có chủ đích:** `active_platform=app` | trỏ `qc-wdio-appium` + lane mobile *(H1 ✅ B — lane có mặt, nội dung ở mức khung)* |
325
+ | 9 | `node bin/self-check.js` sau khi sửa `qc-base-map.json` | **R16 không đỏ** — 3 entry `qa-runner` có `targets: []` nên gỡ file cục bộ không phá map |
326
+
327
+ ## B5 · Bài học
328
+
329
+ - **2026-09-16** — PLAN_v2 §3 khai `qa-runner` **6 file · 44 KB**; đo lại: **8 file · 26.3 KB**. Đây là lần **thứ tư** một bản kế hoạch của đợt này mang số đã lỗi thời → nguyên nhân thật: số được chép từ bản kế hoạch trước chứ không đo lại. Sửa: §0 của mọi bước từ nay **bắt đầu bằng bảng đo lại**, kể cả khi bản trước vừa viết hôm qua.
330
+ - **2026-09-16** — Tưởng ngân sách 1450 KB chặn việc thêm skill. Đọc `test/run.js:697`: nó **chỉ đếm `core/commands/*.md`**. Nguyên nhân thật: PLAN_v2 ghi *"ngân sách core/commands"* nhưng để nó cạnh các số toàn repo nên đọc thành *"cả bộ"*. Sửa: ghi rõ **phép đếm**, không chỉ tên thư mục.
331
+
332
+ ## B6 · Copy được / không copy được
333
+
334
+ | | |
335
+ |---|---|
336
+ | ✅ **Copy được sang dự án khác** | Thứ tự **guard-trước-vá-sau** (`G3`); tách **bố cục ← đề xuất / nội dung ← tài liệu Approved**; luật *"một lệnh phải đổi cho mọi nền nó với tới, trong một commit"* (Bẫy 5); đo lại số trước khi làm theo kế hoạch (Bẫy 1) |
337
+ | ⚠️ **Chỉ đúng ở đây** | Tên `web`/`app`/`system` và cách `qc-scope.md` khoá một nền; ngưỡng 1450 KB và phép đếm của nó; ba lane gắn với `TestingOS` của chị QC; tài liệu nguồn tiếng Việt |
338
+
339
+ ## B7 · Link
340
+
341
+ - [`PLAN_v2.md`](PLAN_v2.md) — §3 *(thứ tự)* · §4 *(nguồn)* · §5 *(bẫy)* · §6 *(4 câu này)* · §7 `E1`·`E3`·`G3`
342
+ - [`exec-d2-b1-tach-qc-review.md`](exec-d2-b1-tach-qc-review.md) — §3.3, phần `E1` dời sang đây
343
+ - [`exec-d2-b2-tach-qc-run-test-atomic.md`](exec-d2-b2-tach-qc-run-test-atomic.md) — §4.4, chỗ khai sai bản chất `qa-runner` (Bẫy 3)
344
+ - [`exec-d3-b1-qc-report-gate-decision.md`](exec-d3-b1-qc-report-gate-decision.md) — bước **bị chặn** bởi bước này
345
+ - `upstream/qc-base-new/` — `Automation-Standards.md` · `Mobile-Automation-Standards.md` · `API-Testing-Standards.md` · `AGT-005` · `AGT-006` · `AGT-010`
346
+
347
+ ---
348
+
349
+ ## 8 · Phạm vi đã khoá — thứ tự triển khai
350
+
351
+ > 🔄 **Thứ tự đổi 2026-09-17 (`S-ORDER`): S0 → S1 → S5 → S3 → S2 → S4.**
352
+ > Đo được: **16/18 file đỏ không có phụ thuộc nào** — chỉ **S4** bị ép *(cần S2+S3)*. Thứ tự
353
+ > đánh số 2·3·4·5 là thói quen, không phải ràng buộc. Ba lý do đổi:
354
+ > **①** S5 chỉ ~10 dòng và **đã được S1 quyết định hết**, lại **chứa một bug thật**
355
+ > *(`qc-design-test.tmpl:151` còn trỏ `/qc-review` — lệnh đã gỡ ở `E3`)*.
356
+ > **②** S3 trước S2 là `G3` áp ở tầng nội dung: **viết tiêu chí review trước, viết bộ sinh sau**
357
+ > — ngược lại thì tiêu chí bị nắn theo thứ bộ sinh vừa đẻ ra, mất đối chứng độc lập.
358
+ > **③** Đứt session giữa chừng thì mất ít hơn.
359
+ > Phạm vi **không đổi** — vẫn 6 việc, vẫn cùng nội dung.
360
+
361
+ Sau khi chốt 4 câu, Bước S là **6 việc**.
362
+
363
+ | # | Việc | Kích thước đo được | Commit |
364
+ |:-:|---|---|:-:|
365
+ | **S0** ✅ | Guard cơ học — **6 entry `forbidden_patterns` có `scope`, chạy qua R5 sẵn có**. **ĐÃ LÀM 09-17**: đỏ **19 file** *(G3)*. Bản trình bày: [`exec-S0`](exec-S0-guard-cam-stack-cu.md) | `test/run.js` **không đổi** — vẫn 244 | 1 |
366
+ | **S1** ✅ | `modules/qc-playwright/` **gỡ** → `qc-playwright-ts` *(layout `web` + `api`)* + `qc-wdio-appium`, mỗi cái đủ `module.yaml` + `stack-profile.yaml` | 1 file → 4 file | 2 |
367
+ | **S2** ✅ | `qa-runner/` (8 file · 26.3 KB) **gỡ** → `qa-script-designer/` *(web+api đầy đủ, mobile khung)* + `qa-script-runner/` *(3 file)* | 26 KB → ~57 KB skill · **không chạm ngân sách** *(N2)* | 2 |
368
+ | **S3** ✅ | `qa-reviewer/script/` 5 file phẳng → **XOÁ, viết mới** `script/{web,mobile,api}/*` — thực thi `E1` | 22 KB, **viết lại** *(xem §8.1)* | 2 |
369
+ | **S4** ✅ | 2 lệnh: gỡ bảng `testid_attr` trùng bản + 6 dòng cuối → **bảng phân giải nền + con trỏ skill** | 73 KB → **phải ≤ 73 KB** | 2 |
370
+ | **S5** ✅ | Ăn theo: `matrix.md` · `qc-report.tmpl` · `trace-schema.json` *(4 dòng)* · `qc-base-map.json` *(3 entry)* · 6 `docs/` | ~15 chỗ | 2 |
371
+
372
+ ### 8.1 · Vì sao S3 là **viết lại**, không phải sửa — và vì sao chỉ S3
373
+
374
+ Câu hỏi *"sao không bỏ hết code Python của QC rồi xây lại từ đầu"* (09-17) được đo và trả lời
375
+ bằng số:
376
+
377
+ | Đo | Kết quả |
378
+ |---|---|
379
+ | Tỉ lệ nhiễm của `skills/qc/` | **112 / 5320 dòng = 2,1 %** |
380
+ | Phần **thật sự là Python** | `modules/qc-playwright/` + `qa-runner/` = **9 file** → **đã xoá hẳn** ở S1/S2 |
381
+ | 9 file nhiễm nhẹ nhất *(qa-designer · qa-planner · qa-reviewer/test-case · shared)* | cộng lại **18 dòng**, trong các file dài 120–272 dòng mà 98 % nội dung **không phụ thuộc ngôn ngữ** |
382
+ | `qa-reviewer/script/non-functional.md` · `functional.md` | **11,2 %** và **9,2 %** ← ngưỡng khác hẳn |
383
+
384
+ **S3 đổi vì mật độ, không vì nguyên tắc.** Tái bố cục một file sai stack 10 % *dưới cái tên
385
+ "sửa"* dẫn thẳng vào bẫy B1: người viết giữ khung cũ và chỉ đổi chữ. Nên: **xoá 5 file, viết mới**
386
+ từ `Automation-Standards` §10 · `Mobile-Automation-Standards` · `API-Testing-Standards` §10 ·
387
+ `AGT-006` *(có sẵn scoring rules)*. Giữ lại đúng một thứ: **cấu trúc mục của
388
+ `review-check-groups.md`**, để hai bên còn khớp.
389
+
390
+ **Vì sao KHÔNG xây lại cả `skills/qc/`** — nguồn không phủ đủ, và đã đo:
391
+
392
+ - `qc-base-new/` **7 file** tự khai trong header là **L4 Execution** + **L5 Governance**. Không
393
+ có file nào cho phân tích yêu cầu · doc-gap · test plan · thiết kế TC · review TC — **4 trạm
394
+ đầu**, và cả 4 đang **✅ đủ** theo §2.
395
+ - `qcframework_proposal/` cắt **2026-09-10**, mà nó **đã chứa** công của đợt này (`testid_attr`
396
+ 7 file · `self-review-principles` 8 file · `script-bug`/`product-gap` · §4.5.6) → **bản phái
397
+ sinh của chính framework**, không phải nguồn độc lập. Xây lại từ nó là mất 6 ngày sửa: 24 GAP
398
+ lane QC *(`dfe5bd5`)* · R17/R18/T19 · T20/R8f *(`02642bd`)* · G84 · G86+G87 · 0.9.6 · 0.9.7.
399
+
400
+ Đổi **18 dòng sửa tay** lấy **42 file sạch + 6 ngày sửa lỗi** là lỗ. Đổi **5 file mật độ ~10 %**
401
+ lấy một bản viết theo chuẩn Approved là lãi.
402
+
403
+ ---
404
+
405
+ **Vì sao hai commit chứ không một.** S0 **phải đỏ** — đó là bằng chứng của `G3`. Một commit đỏ
406
+ là vi phạm [`§8 luật 1`](PLAN_v2.md). Nên S0 vào commit 1 **cùng với** một dòng `skip` có ghi lý
407
+ do và hạn *(gỡ ở commit 2)*; hoặc S0 và S1–S5 gộp làm một commit và bằng chứng đỏ được ghi lại
408
+ trong `buoc/` thay vì trong lịch sử git. **Cách 2 gọn hơn và tôi đề xuất cách 2** — bằng chứng
409
+ đỏ chụp vào nhật ký bước, cây git vẫn xanh suốt.
410
+
411
+ **Bốn quyết định này vào [`PLAN_v2 §7`](PLAN_v2.md) với mã `S-API` · `H1` · `H2` · `H3`, và ra
412
+ khỏi §6.**
413
+
414
+ ---
415
+
416
+ ## 9 · Nợ độc lập phát hiện thêm ở bước này *(không làm trong Bước S)*
417
+
418
+ | Việc | Đo được |
419
+ |---|---|
420
+ | `docs/explain/18-qc-review.md` · `19-qc-run-test.md` giải thích **2 lệnh đã bị tách bỏ ở Đợt 2** | `commands/` không còn `qc-review.tmpl` · `qc-run-test.tmpl`; `docs/explain/` chưa có file cho 5 lệnh mới **(N3)** |