@educa-corp/sdd-framework 0.9.6 → 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 (147) hide show
  1. package/bin/lint-trace.js +4 -4
  2. package/bin/qc-base-map.json +13 -11
  3. package/bin/self-check.js +497 -16
  4. package/bin/trace-schema.json +3226 -2656
  5. package/core/FRAMEWORK_VERSION +1 -1
  6. package/core/commands/amend-prd.md +7 -1
  7. package/core/commands/debug.md +8 -2
  8. package/core/commands/define-product.md +38 -1
  9. package/core/commands/dev-gen-test.md +9 -3
  10. package/core/commands/dev-run-test.md +8 -2
  11. package/core/commands/dev-smoke-test.md +7 -1
  12. package/core/commands/extend-prd.md +7 -1
  13. package/core/commands/fix-bug.md +11 -5
  14. package/core/commands/generate-architecture.md +9 -1
  15. package/core/commands/generate-bdd.md +45 -5
  16. package/core/commands/generate-code.md +43 -4
  17. package/core/commands/generate-design-spec.md +7 -1
  18. package/core/commands/generate-prd.md +9 -1
  19. package/core/commands/generate-spec-manifest.md +7 -1
  20. package/core/commands/generate-tech-docs.md +41 -1
  21. package/core/commands/learn.md +7 -1
  22. package/core/commands/map-testids.md +11 -5
  23. package/core/commands/propose-scenario.md +7 -1
  24. package/core/commands/qc-analyze.md +12 -6
  25. package/core/commands/qc-automation-assess.md +356 -0
  26. package/core/commands/qc-design-script.md +430 -0
  27. package/core/commands/qc-design-test.md +98 -20
  28. package/core/commands/qc-plan.md +9 -3
  29. package/core/commands/qc-report.md +92 -77
  30. package/core/commands/qc-review-script.md +342 -0
  31. package/core/commands/{qc-review.md → qc-review-testcase.md} +86 -54
  32. package/core/commands/qc-run-manualtest.md +401 -0
  33. package/core/commands/qc-run-script.md +421 -0
  34. package/core/commands/refine-prd.md +7 -1
  35. package/core/commands/report-bug.md +9 -3
  36. package/core/commands/review-code.md +9 -3
  37. package/core/commands/review-context.md +11 -3
  38. package/core/commands/review-tech-docs.md +11 -3
  39. package/core/commands/setup-ai-first.md +7 -1
  40. package/core/commands/validate-traces.md +10 -4
  41. package/core/modules/qc-playwright-ts/module.yaml +13 -0
  42. package/core/modules/qc-playwright-ts/stack-profile.yaml +99 -0
  43. package/core/modules/qc-wdio-appium/module.yaml +20 -0
  44. package/core/modules/qc-wdio-appium/stack-profile.yaml +107 -0
  45. package/core/rules/workflow.md +2 -2
  46. package/core/skills/qc/_shared/self-review-principles.md +2 -2
  47. package/core/skills/qc/qa-analyst/DOC_GAP.template.md +1 -1
  48. package/core/skills/qc/qa-analyst/data-flow.md +1 -1
  49. package/core/skills/qc/qa-analyst/spec-issue-reporter.md +1 -1
  50. package/core/skills/qc/qa-automation-assess/matrix.md +123 -0
  51. package/core/skills/qc/qa-designer/e2e/journey.md +1 -1
  52. package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +1 -1
  53. package/core/skills/qc/{qa-runner → qa-designer}/exploratory/session.md +8 -2
  54. package/core/skills/qc/qa-designer/functional/api.md +2 -2
  55. package/core/skills/qc/qa-designer/functional/gui-feature.md +1 -1
  56. package/core/skills/qc/qa-designer/functional/gui-screen.md +1 -1
  57. package/core/skills/qc/qa-designer/functional/job.md +128 -0
  58. package/core/skills/qc/qa-designer/integration/api.md +2 -2
  59. package/core/skills/qc/qa-designer/integration/db.md +2 -2
  60. package/core/skills/qc/qa-designer/integration/gui.md +1 -1
  61. package/core/skills/qc/qa-designer/integration/{kafka.md → queue.md} +21 -5
  62. package/core/skills/qc/qa-designer/non-functional.md +1 -1
  63. package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +17 -0
  64. package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +28 -6
  65. package/core/skills/qc/qa-reviewer/script/_shared/review-rules.md +121 -0
  66. package/core/skills/qc/qa-reviewer/script/api/auth.md +49 -0
  67. package/core/skills/qc/qa-reviewer/script/api/endpoint.md +89 -0
  68. package/core/skills/qc/qa-reviewer/script/api/security.md +46 -0
  69. package/core/skills/qc/qa-reviewer/script/exploratory.md +3 -3
  70. package/core/skills/qc/qa-reviewer/script/mobile/e2e.md +41 -0
  71. package/core/skills/qc/qa-reviewer/script/mobile/functional.md +90 -0
  72. package/core/skills/qc/qa-reviewer/script/mobile/integration.md +41 -0
  73. package/core/skills/qc/qa-reviewer/script/mobile/non-functional.md +43 -0
  74. package/core/skills/qc/qa-reviewer/script/web/e2e.md +46 -0
  75. package/core/skills/qc/qa-reviewer/script/web/functional.md +111 -0
  76. package/core/skills/qc/qa-reviewer/script/web/integration.md +46 -0
  77. package/core/skills/qc/qa-reviewer/script/web/non-functional.md +49 -0
  78. package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +1 -1
  79. package/core/skills/qc/qa-reviewer/shared/review-file-template.md +29 -10
  80. package/core/skills/qc/qa-reviewer/test-case/e2e.md +2 -2
  81. package/core/skills/qc/qa-reviewer/test-case/exploratory.md +1 -1
  82. package/core/skills/qc/qa-reviewer/test-case/functional.md +2 -2
  83. package/core/skills/qc/qa-reviewer/test-case/integration.md +2 -2
  84. package/core/skills/qc/qa-reviewer/test-case/non-functional.md +2 -2
  85. package/core/skills/qc/qa-script-designer/_shared/api-conventions.md +94 -0
  86. package/core/skills/qc/qa-script-designer/_shared/file-naming-and-folders.md +109 -0
  87. package/core/skills/qc/qa-script-designer/_shared/mobile-conventions.md +196 -0
  88. package/core/skills/qc/qa-script-designer/_shared/web-conventions.md +257 -0
  89. package/core/skills/qc/qa-script-designer/api/auth.md +43 -0
  90. package/core/skills/qc/qa-script-designer/api/endpoint.md +61 -0
  91. package/core/skills/qc/qa-script-designer/api/security.md +41 -0
  92. package/core/skills/qc/qa-script-designer/mobile/e2e.md +35 -0
  93. package/core/skills/qc/qa-script-designer/mobile/functional/feature.md +32 -0
  94. package/core/skills/qc/qa-script-designer/mobile/functional/screen.md +42 -0
  95. package/core/skills/qc/qa-script-designer/mobile/integration.md +39 -0
  96. package/core/skills/qc/qa-script-designer/mobile/non-functional.md +39 -0
  97. package/core/skills/qc/qa-script-designer/web/e2e.md +36 -0
  98. package/core/skills/qc/qa-script-designer/web/functional/api.md +39 -0
  99. package/core/skills/qc/qa-script-designer/web/functional/gui-feature.md +34 -0
  100. package/core/skills/qc/qa-script-designer/web/functional/gui-screen.md +42 -0
  101. package/core/skills/qc/qa-script-designer/web/integration.md +43 -0
  102. package/core/skills/qc/qa-script-designer/web/non-functional.md +42 -0
  103. package/core/skills/qc/qa-script-runner/mobile/run.md +38 -0
  104. package/core/skills/qc/qa-script-runner/report.md +41 -0
  105. package/core/skills/qc/qa-script-runner/web/run.md +48 -0
  106. package/core/steps/context-loader.md +1 -1
  107. package/core/steps/gate.md +7 -1
  108. package/core/steps/qc-scope.md +45 -2
  109. package/core/steps/qc-stamp.md +4 -4
  110. package/core/steps/report-footer.md +10 -9
  111. package/docs/02-concepts/pipeline-steps/07-dev-selftest.md +1 -1
  112. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +13 -12
  113. package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +3 -3
  114. package/docs/02-concepts/traceability.md +1 -1
  115. package/docs/03-guides/developer.md +1 -1
  116. package/docs/03-guides/tester-qa.md +40 -11
  117. package/docs/04-reference/commands.md +4 -2
  118. package/docs/04-reference/modules.md +2 -1
  119. package/docs/04-reference/trace-schema.md +4 -4
  120. package/docs/explain/17-qc-design-test.md +5 -5
  121. package/docs/explain/18-qc-review.md +42 -20
  122. package/docs/explain/19-qc-run-test.md +13 -10
  123. package/docs/explain/20-qc-report.md +3 -3
  124. package/docs/explain/23-fix-bug.md +2 -2
  125. package/docs/explain/README.md +2 -2
  126. package/docs/plans/qc-surgery/01-checklist.md +86 -21
  127. package/docs/plans/qc-surgery/PLAN_v2.md +295 -0
  128. package/docs/plans/qc-surgery/exec-S-ap-stack-typescript.md +420 -0
  129. package/docs/plans/qc-surgery/exec-S0-guard-cam-stack-cu.md +400 -0
  130. package/docs/plans/qc-surgery/exec-S1-hai-module-thay-qc-playwright.md +267 -0
  131. package/docs/plans/qc-surgery/exec-S2-qa-runner-thanh-script-designer-runner.md +340 -0
  132. package/docs/plans/qc-surgery/exec-S3-viet-lai-tieu-chi-review-script.md +322 -0
  133. package/docs/plans/qc-surgery/exec-S5-an-theo-don-dau-vet-stack-cu.md +292 -0
  134. package/package.json +1 -1
  135. package/core/commands/qc-run-test.md +0 -561
  136. package/core/modules/qc-playwright/stack-profile.yaml +0 -66
  137. package/core/skills/qc/qa-reviewer/script/e2e.md +0 -95
  138. package/core/skills/qc/qa-reviewer/script/functional.md +0 -109
  139. package/core/skills/qc/qa-reviewer/script/integration.md +0 -99
  140. package/core/skills/qc/qa-reviewer/script/non-functional.md +0 -134
  141. package/core/skills/qc/qa-runner/e2e.md +0 -49
  142. package/core/skills/qc/qa-runner/functional/api.md +0 -35
  143. package/core/skills/qc/qa-runner/functional/gui-feature.md +0 -57
  144. package/core/skills/qc/qa-runner/functional/gui-screen.md +0 -61
  145. package/core/skills/qc/qa-runner/integration.md +0 -47
  146. package/core/skills/qc/qa-runner/non-functional.md +0 -49
  147. package/core/skills/qc/qa-runner/report/report.md +0 -37
@@ -0,0 +1,267 @@
1
+ ---
2
+ buoc: Bước S · việc S1
3
+ title: Hai module QC thay qc-playwright — qc-playwright-ts + qc-wdio-appium
4
+ phu_thuoc: S0 xong 2026-09-17 (đèn đỏ 19 file, sau quyết định S-SCOPE)
5
+ trang_thai: ĐÃ TRIỂN KHAI 2026-09-17 · 8/8 phép thử đạt · R5 đỏ 27→19→18
6
+ ---
7
+
8
+ # S1 — Hai hồ sơ stack thay một hồ sơ đã bỏ
9
+
10
+ ← [`exec-S-ap-stack-typescript.md`](exec-S-ap-stack-typescript.md) · [`exec-S0-guard-cam-stack-cu.md`](exec-S0-guard-cam-stack-cu.md) · [`PLAN_v2.md`](PLAN_v2.md)
11
+
12
+ | | |
13
+ |---|---|
14
+ | **Lớp** | Bước S · việc **2/6** |
15
+ | **File code** | `modules/qc-playwright/` *(xoá)* → `modules/qc-playwright-ts/` + `modules/qc-wdio-appium/` |
16
+ | **File test** | chưa có rule nào canh module — xem §1.1 |
17
+ | **Ngày xong** | — |
18
+ | **Phụ thuộc** | **S0 xong** ✅ *(đèn đỏ **19 file** sau `S-SCOPE`; 1 trong đó là `modules/qc-playwright/stack-profile.yaml` — bước này xoá nó)* |
19
+ | **Ai dùng nó** | `/qc-design-script` · `qa-automation-assess/matrix.md` · *(gián tiếp)* `/qc-run-script` |
20
+
21
+ ---
22
+
23
+ ## 0 · Đo lại — 2026-09-17
24
+
25
+ | Đo | Kết quả | Kế hoạch khai |
26
+ |---|---|---|
27
+ | `modules/qc-playwright/` | **1 file · 66 dòng**, thiếu `module.yaml` | ✅ đúng |
28
+ | Module thiếu `module.yaml` | **1/16** — và đúng là `qc-playwright` | ✅ đúng |
29
+ | Ai **đọc** `stack-profile.yaml` | `commands/qc-design-script.tmpl` · `skills/qc/qa-automation-assess/matrix.md` *(§layout)* · `commands/generate-bdd.tmpl:270` *(chỉ `platform_type`, cho lane DEV)* | — |
30
+ | Ai **đọc** `module.yaml` | **KHÔNG AI** — 0 lệnh, 0 script, 0 test. Chỉ 3 file `docs/` mô tả nó | ❗ kế hoạch ngầm định nó là hợp đồng |
31
+ | `tech_stack.qc_module` — cơ chế chọn module QC | **KHÔNG TỒN TẠI.** Chỉ xuất hiện đúng 1 lần: trong **comment của chính `qc-playwright/stack-profile.yaml:4`** | ❗ chưa ai biết |
32
+
33
+ ### 0.1 · 🔴 Hai chỗ kế hoạch chưa biết
34
+
35
+ **① `module.yaml` không phải hợp đồng, là quy ước.** Không máy nào đọc nó. Thêm nó vào hai
36
+ module mới là để **đứng cùng hàng với 15 module kia**, không phải vì có thứ gì sẽ vỡ nếu thiếu.
37
+ Nói đúng như vậy thì người sau không đi tìm cái consumer không tồn tại.
38
+
39
+ **② Cơ chế chọn module QC chưa được cài.** `stack-profile.yaml:4` viết *"Selected via
40
+ `tech_stack.qc_module` or per `/qc-*` run"* — nhưng `qc_module` **không xuất hiện ở bất kỳ lệnh,
41
+ template, rule hay script nào**. Hôm nay `/qc-design-script` trỏ thẳng vào `modules/qc-playwright`
42
+ bằng **đường dẫn viết cứng**. Nghĩa là S1 không chỉ *thay hồ sơ* — nó phải **quyết một cơ chế
43
+ chưa từng có**. Đó là §1.1.
44
+
45
+ ### 0.2 · Nợ độc lập phát hiện thêm
46
+
47
+ `commands/generate-bdd.tmpl:270` suy `active_platform` cho `context-engineering` · `phaser-game`
48
+ theo *"`platform_type` của stack-profile"*. Đo: **0/16 module khai `platform_type`**. Một luật
49
+ trỏ vào một field không tồn tại ở đâu cả — đúng lớp lỗi `R1`/`R3` sinh ra để bắt, nhưng nằm
50
+ ngoài tầm chúng. **Lane DEV, không thuộc Bước S** → ghi vào `PLAN_v2 §3 nợ độc lập`.
51
+
52
+ ---
53
+
54
+ ## 1 · BA CÂU CHỜ CHỐT
55
+
56
+ ### 1.1 · Chọn module lúc chạy bằng gì?
57
+
58
+ | Phương án | Cách làm | Cái giá |
59
+ |---|---|---|
60
+ | **A · phân giải từ `active_platform`** ⭐ | Bảng 3 dòng trong lệnh. `active_platform` đã được `qc-scope.md` phân giải ở **trạm 1** và **khoá cho cả pass QC** | không thêm field cấu hình nào; nhưng dự án không tự chọn được module khác |
61
+ | **B · cài thật `tech_stack.qc_module`** | Đúng như comment hứa: một key trong `project-context.yaml` | **Hai nguồn sự thật cho một câu hỏi.** Và một field **không đủ**: một PRD có cả `web` lẫn `app` thì `qc_module` phải là gì? |
62
+ | **C · khai `platform_type` trong stack-profile QC** | Dùng lại cơ chế `generate-bdd` đang định dùng | cơ chế đó **đang hỏng** — 0/16 module khai *(§0.2)*. Dựng lên trên một thứ chưa chạy |
63
+
64
+ **Khuyến nghị: A.** Lý do không phải gọn hơn — mà là: `qc-scope.md` ra đời vì *"luật phân giải
65
+ nền từng được copy-paste ở 5 lệnh và câu chữ đã lệch nhau"*. Thêm `qc_module` là **hỏi lại một
66
+ câu đã có đáp án**, và hai đáp án khác nhau cho cùng một pass QC là kiểu hỏng Im lặng: artifact
67
+ ghi vào `qc_artifact_dir` của nền này, script sinh theo layout của nền kia.
68
+
69
+ **Nếu chốt B** → phải trả lời thêm: `qc_module` là một giá trị hay một map theo platform · ai
70
+ ghi nó · lệnh nào đọc · nó thắng hay thua `active_platform` khi hai bên lệch.
71
+
72
+ ---
73
+
74
+ ### 1.2 · Một `stack-profile.yaml` hai layout, hay hai file?
75
+
76
+ `S-API` đã chốt **một module `qc-playwright-ts` phục vụ cả web và api**. Câu còn lại là hình dạng
77
+ file. Hiện `stack-profile.yaml` có **một** khối `architecture.folder_structure` phẳng.
78
+
79
+ ```
80
+ A ⭐ qc-playwright-ts/stack-profile.yaml B qc-playwright-ts/stack-profile.yaml
81
+ layout: (chỉ web)
82
+ web: automation/… qc-playwright-api/stack-profile.yaml
83
+ api: api-automation/… (chỉ api)
84
+ ```
85
+
86
+ **Khuyến nghị: A.** Cùng lý do đã chốt ở `S-API`: hai file nghĩa là dãy phiên bản
87
+ `Playwright / TypeScript v5.x` khai hai lần. Cái giá của A: `qc-design-script.tmpl` và
88
+ `matrix.md` đang trỏ `§layout` phẳng → phải đổi thành `§layout.{web|api|mobile}`. **Đo được: 2
89
+ chỗ** *(`qc-design-script.tmpl:126` · `matrix.md:104`)* — rẻ.
90
+
91
+ ---
92
+
93
+ ### 1.3 · `stack-profile.yaml` dày bao nhiêu?
94
+
95
+ `Automation-Standards.md` là **527 dòng**, `Mobile-` **656**, `API-` **714**. Hồ sơ hiện tại
96
+ **66 dòng**. Câu hỏi: đổ bao nhiêu vào hồ sơ, để lại bao nhiêu cho skill?
97
+
98
+ | Phương án | Hồ sơ | Cái giá |
99
+ |---|---|---|
100
+ | **A · mỏng** ⭐ | ~90 dòng: lệnh build/test/report · `layout.{web,api}` · quy ước đặt tên · trace tag. **Luật viết code** *(§4 Page Object · §6 Locator · §7 Assertion · §10 Anti-patterns · §11 Flaky)* ở `qa-script-designer/_shared/*-conventions.md` — **đã có sẵn 16.5 KB web + 12.7 KB mobile** trong bộ đề xuất | phải nói rõ ranh giới, kẻo hai bên chép nhau |
101
+ | **B · dày** | nhồi §1–§11 vào hồ sơ, ~400 dòng | **hồ sơ được đọc NGUYÊN vào ngữ cảnh mỗi lần chạy lệnh**; skill thì nạp **một file theo lane**. Dày nghĩa là mỗi lần làm web vẫn phải đọc cả luật mobile |
102
+
103
+ **Khuyến nghị: A**, với ranh giới phát biểu được thành một câu: **hồ sơ trả lời *"chạy ở đâu,
104
+ đặt tên gì"*; skill trả lời *"viết thế nào"*.** Thứ nào máy cần để dựng đường dẫn → hồ sơ. Thứ
105
+ nào người cần để viết code đúng → skill.
106
+
107
+ ---
108
+
109
+ # PHẦN A — Chuyện gì đang xảy ra
110
+
111
+ ## A1 · Vấn đề
112
+
113
+ Bộ khung có mười sáu tờ khai, mỗi tờ mô tả một loại dự án: dùng ngôn ngữ gì, đặt file ở đâu,
114
+ chạy bằng lệnh nào. Tờ của đội QC đang mô tả bộ công cụ mà đội đã bỏ từ tháng trước — và nó là
115
+ tờ **duy nhất trong mười sáu tờ bị thiếu mất trang bìa**. Lệnh sinh script đọc đúng tờ đó để
116
+ biết đặt file ở đâu, nên mọi đường dẫn nó dựng ra đều theo một cách tổ chức không còn ai dùng.
117
+
118
+ ## A2 · Cách giải quyết, nói bằng một hình ảnh
119
+
120
+ Như đổi biển chỉ đường ở một ngã ba sau khi khu dân cư tách làm hai. Không sửa chữ trên tấm biển
121
+ cũ — gỡ nó xuống, dựng hai tấm mới, và viết lại cái bảng ở đầu đường để người ta biết rẽ nhánh
122
+ nào. Tấm cũ giữ lại chỉ làm người ta rẽ nhầm rồi tự trách mình đọc không kỹ.
123
+
124
+ ## A3 · Xong rồi thì thấy gì khác
125
+
126
+ `modules/qc-playwright/` biến mất; thay vào đó là `modules/qc-playwright-ts/` và
127
+ `modules/qc-wdio-appium/`, **mỗi cái đủ hai file** như mười lăm module kia. Chạy
128
+ `/qc-design-script` sẽ thấy nó **nói ra nền nó đang dùng** trước khi sinh gì, thay vì im lặng
129
+ dùng một nền mặc định.
130
+
131
+ ## A4 · Thuật ngữ dùng ở trên
132
+
133
+ - **module / tờ khai** — hồ sơ mô tả một loại dự án để lệnh biết sinh code theo kiểu nào. Có 16 cái trong `modules/`.
134
+ - **`module.yaml`** — trang bìa: tên, ngôn ngữ, framework, bộ test. **Không máy nào đọc** — để người đọc và để đứng cùng hàng.
135
+ - **`stack-profile.yaml`** — ruột: lệnh chạy, bố cục thư mục, quy ước đặt tên. Đây là thứ lệnh thật sự đọc.
136
+ - **layout** — phần mô tả đặt file ở đâu. Web và API có hai bố cục khác nhau nên cần hai mục.
137
+ - **nền / `active_platform`** — `web` · `app` · `system`; một pass QC khoá đúng một nền.
138
+
139
+ ---
140
+
141
+ > ### ✅ Phép thử đọc to
142
+ > **Đã thử với:** tự đọc to · **ngày:** 2026-09-17 · **phải giải thích thêm:** cụm *"trang bìa"*
143
+ > ở A1 — người nghe hỏi *"thiếu bìa thì sao?"*. Câu trả lời đúng là **không sao cả về mặt máy**,
144
+ > và đó chính là §0.1 ①. Đã giữ hình ảnh nhưng A4 nói thẳng *"không máy nào đọc"*.
145
+
146
+ ---
147
+
148
+ # PHẦN B — Chi tiết kỹ thuật
149
+
150
+ ## B1 · Cách hiển nhiên là gì, và vì sao nó sai
151
+
152
+ **Cách hiển nhiên:** `git mv modules/qc-playwright modules/qc-playwright-ts`, sửa nội dung file
153
+ từ Python sang TypeScript, thêm `module.yaml`, rồi copy sang làm `qc-wdio-appium`.
154
+
155
+ **Sai vì hai lý do:**
156
+
157
+ **1 · Đổi tên giữ lại hình dạng của thứ bị bỏ.** Hồ sơ hiện tại được viết cho **một** nền, nên
158
+ nó có **một** `folder_structure`, **một** khối `build`, **một** bộ `naming`. Đổi tên rồi sửa chữ
159
+ cho ra một hồ sơ TypeScript **vẫn chỉ phục vụ một nền** — và `S-API` đã chốt `qc-playwright-ts`
160
+ phải phục vụ **hai** (web + api). Cái phải đổi là **số nhánh**, không phải từ vựng. Đây đúng là
161
+ bẫy B1 của [`exec-S`](exec-S-ap-stack-typescript.md), lặp lại ở cấp file cấu hình.
162
+
163
+ **2 · Copy hồ sơ web sang làm hồ sơ mobile bỏ mất thứ chỉ mobile mới có.**
164
+ `Mobile-Automation-Standards.md` dài hơn bản web **129 dòng**, và phần dôi ra không phải văn
165
+ vẻ: **§6 API Helper** · **§9 Gesture Helper** · **§12 Environment Validation Checklist**. Một
166
+ hồ sơ mobile copy từ web sẽ im lặng thiếu ba mục đó, và cái thiếu chỉ lộ ra khi có người chạy
167
+ thật trên máy thật — tức **sau khi đã tin là xong**.
168
+
169
+ ## B2 · Cách làm đúng
170
+
171
+ **① Xoá `modules/qc-playwright/`** *(quyết định `H2`)*. Đèn S0 đang đỏ 19 dòng ở file này — xoá
172
+ là cách duy nhất làm nó hết đỏ, và đó là bằng chứng việc xoá đã xảy ra thật.
173
+
174
+ **② `modules/qc-playwright-ts/`** — `module.yaml` *(quy ước, §0.1 ①)* + `stack-profile.yaml`:
175
+
176
+ ```
177
+ build: test/e2e/report/show-trace → npx playwright test …
178
+ layout:
179
+ web: automation/{playwright.config.ts, pages/, tests/, data/, fixtures/, helpers/}
180
+ api: api-automation/{playwright.config.ts, api/, tests/, data/, fixtures/, helpers/}
181
+ naming: <feature>.page.ts · <res>.api.ts · <feature>-happy-path.spec.ts
182
+ trace_tags: giữ nguyên — @trace.verifies / source / test_type
183
+ ```
184
+
185
+ **③ `modules/qc-wdio-appium/`** — cùng cặp, `layout.mobile`, **cộng ba mục chỉ mobile mới có**
186
+ *(§6 API Helper · §9 Gesture Helper · §12 Environment Validation Checklist)*, và
187
+ `Reporting: Allure Report v2.x` — **ngược với luật `No Allure` hiện hành**; xem [`exec-S0
188
+ §0.3 P1`](exec-S0-guard-cam-stack-cu.md).
189
+
190
+ **④ Bảng phân giải nền** *(theo §1.1 A)* — đặt ở **một chỗ**, `steps/qc-scope.md`, vì đó là nơi
191
+ `active_platform` đã sống. Ba lệnh script trỏ tới, không chép lại:
192
+
193
+ ```
194
+ web · webview → qc-playwright-ts · layout.web
195
+ app · app-ios · … → qc-wdio-appium · layout.mobile
196
+ system → qc-playwright-ts · layout.api
197
+ ```
198
+
199
+ **Con số có lý do:** **2 chỗ** phải đổi từ `§layout` phẳng sang `§layout.{…}` —
200
+ `qc-design-script.tmpl:126` và `matrix.md:104`. Đã đếm, không ước lượng.
201
+
202
+ ## B3 · Nếu làm sai thì hỏng theo kiểu nào
203
+
204
+ **Im lặng 🔴.** Một hồ sơ mobile thiếu §12 Environment Validation vẫn là YAML hợp lệ, vẫn được
205
+ lệnh đọc, vẫn dựng ra đường dẫn trông đúng. Lệnh sinh script mobile chạy trót lọt, file được
206
+ ghi, cột `Script file` được điền — rồi `npx wdio` báo *"no devices"* trên máy chị QC. Ai phát
207
+ hiện: chị QC. Sau bao lâu: lần đầu có người chạy mobile thật, tức có thể **nhiều tuần** sau khi
208
+ Bước S được tích xong.
209
+
210
+ ## B4 · Verify bằng gì
211
+
212
+ | # | Phép thử | Kết quả mong đợi |
213
+ |:-:|---|---|
214
+ | 1 | `ls modules/qc-playwright` | **không tồn tại** |
215
+ | 2 | `ls modules/qc-playwright-ts modules/qc-wdio-appium` | mỗi cái **đúng 2 file** — bằng 15 module kia |
216
+ | 3 | `node bin/self-check.js` | R5 đỏ **18 file** *(bớt đúng 1: `modules/qc-playwright/stack-profile.yaml`)* — **và không bớt hơn**, vì S1 chưa đụng skill |
217
+ | 4 | `grep -rn 'qc-playwright\b' commands/ skills/ bin/` | **0** — không chỗ nào còn trỏ tên cũ |
218
+ | 5 | `grep -c 'Allure' modules/qc-wdio-appium/stack-profile.yaml` | **≥1** — nếu 0 thì đã copy nhầm luật `No Allure` của web |
219
+ | 6 | `grep -E 'Gesture\|Environment Validation\|API Helper' modules/qc-wdio-appium/stack-profile.yaml` | **3 mục đủ** — chống lỗi B1 #2 |
220
+ | 7 | **Phá:** đặt `active_platform=system`, đọc bảng ở `qc-scope.md` | trỏ `qc-playwright-ts` · `layout.api` — **không** ra `layout.web` |
221
+ | 8 | `node bin/build.js` · `node test/run.js` · `node bin/lint-trace.js` | xanh · **244/244** |
222
+
223
+ > Phép thử **3** là phép quan trọng: nó bắt đèn S0 làm việc **như một thước đo tiến độ**, không
224
+ > chỉ như một cái chặn. Mỗi việc S1…S5 phải làm con số đỏ giảm đúng bằng phần nó nhận.
225
+
226
+ ## B5 · Bài học
227
+
228
+ - **2026-09-17** — Đèn S0 bắt **chính file tôi vừa viết**: `qc-playwright-ts/stack-profile.yaml` có comment *"trước đây là `#` (Python)"*. Đỏ 19 thay vì 18 đúng như nó phải thế. Cám dỗ là cấp `except` cho file mình vừa tạo — đó chính là kiểu mục ruỗng mà B3 của S0 cảnh báo. Sửa: đổi chữ thành *"stack cũ dùng `#`"*, câu vẫn đủ nghĩa mà không cần tên ngôn ngữ. **Guard đầu tiên bắt được lỗi đầu tiên, và nó bắt người viết ra nó.**
229
+ - **2026-09-17** — `node bin/build.js` dựng lại `core/` sạch, nhưng **`.agent/` vẫn giữ `modules/qc-playwright/`**. Bốn lệnh kiểm **không** phủ bản cài. Nguyên nhân thật: `--init` mới là bước đồng bộ `core/` → `.agent/` kèm prune (G44), và `build.js` không gọi nó. Sửa: sau một bước **xoá** file framework, phải chạy thêm `node bin/index.js --init` rồi kiểm `.agent/`.
230
+ - **2026-09-17** — Chạy `--init` thì prune **vẫn không xoá** module cũ: nó rơi vào nhánh *"người dùng đã sửa"* → backup + giữ lại. Nhưng file đó **byte-identical** với bản trong git, không ai đụng. Nguyên nhân thật ở §0.3 — lỗi lane installer, không phải lỗi bước này.
231
+
232
+ ### 0.3 · 🔴 Nợ độc lập nghiêm trọng phát hiện khi chạy S1 — lane installer
233
+
234
+ ```
235
+ .gitignore:23 .agent/.install-manifest.json ← KHÔNG theo git
236
+ index.js:624 "Commit .agent/ to git so your whole team has the framework"
237
+ ```
238
+
239
+ `.agent/` đi qua git, **manifest thì không**. Trên bất kỳ máy nào không phải máy đã chạy đúng
240
+ lần install sinh ra bộ file đó, manifest lệch với đĩa → điều kiện **(3)** của `G44`
241
+ *("hash khớp manifest ⇒ nguyên bản")* **sai với cả file chưa ai đụng**, và prune tụt xuống
242
+ nhánh *"người dùng đã sửa"*: backup rồi **giữ lại**.
243
+
244
+ **Hệ quả:** với mọi đội làm đúng lời installer dặn *(commit `.agent/`)*, `G44` **âm thầm thoái
245
+ hoá thành backup-and-keep**. Một lệnh bị bỏ vì nó **sai** vẫn nằm trong `.claude/commands/` của
246
+ cả đội, vẫn hiện trong menu `/`, vẫn chạy được — đúng thứ `G44` sinh ra để chặn.
247
+
248
+ **Bằng chứng đo được hôm nay:** `.agent/modules/qc-playwright/stack-profile.yaml` có
249
+ `sha1 = e195bcc8…`, **giống hệt bản đã commit** *(`git diff HEAD` trống)*, vẫn bị xếp là
250
+ *"đã sửa"* và được giữ lại sau `--init`.
251
+
252
+ **Không thuộc Bước S** → ghi vào `PLAN_v2 §3 nợ độc lập`. Đã dọn tay ở bước này để S1 sạch.
253
+
254
+ ## B6 · Copy được / không copy được
255
+
256
+ | | |
257
+ |---|---|
258
+ | ✅ **Copy được** | Dùng số đỏ của guard làm **thước đo tiến độ từng việc**, không chỉ làm cổng chặn; hỏi *"ai thật sự đọc file này"* trước khi coi nó là hợp đồng; khi tách một hồ sơ làm hai, đếm phần **dôi ra** của bản dài hơn thay vì copy bản ngắn |
259
+ | ⚠️ **Chỉ đúng ở đây** | Tên `qc-playwright-ts`/`qc-wdio-appium`; việc `active_platform` đã được khoá ở trạm 1; ba mục riêng của chuẩn mobile; ngưỡng 16 module |
260
+
261
+ ## B7 · Link
262
+
263
+ - [`exec-S-ap-stack-typescript.md`](exec-S-ap-stack-typescript.md) §8 *(S1 là việc 2/6)* · §7 `S-API` · `H1` · `H2`
264
+ - [`exec-S0-guard-cam-stack-cu.md`](exec-S0-guard-cam-stack-cu.md) §0.3 P1 *(Allure đảo chiều)*
265
+ - `.agent/steps/qc-scope.md:9` — nơi `active_platform` được phân giải
266
+ - `commands/qc-design-script.tmpl:126` · `skills/qc/qa-automation-assess/matrix.md:104` — 2 chỗ trỏ `§layout`
267
+ - `upstream/qc-base-new/Automation-Standards.md` §1–§2 · `Mobile-Automation-Standards.md` §6 · §9 · §12
@@ -0,0 +1,340 @@
1
+ ---
2
+ buoc: Bước S · việc S2
3
+ title: Gỡ qa-runner (sinh mã Python) → qa-script-designer + qa-script-runner
4
+ phu_thuoc: S0 xong (đèn đỏ 18) · S1 xong (hai module mới)
5
+ trang_thai: ĐÃ TRIỂN KHAI 2026-09-17 · R5 đỏ 9→2 · 3 WARN scope tắt hết
6
+ ---
7
+
8
+ # S2 — Bộ sinh mã đổi nền, và tách làm hai việc
9
+
10
+ ← [`exec-S-ap-stack-typescript.md`](exec-S-ap-stack-typescript.md) · [`exec-S1-…`](exec-S1-hai-module-thay-qc-playwright.md) · [`PLAN_v2.md`](PLAN_v2.md)
11
+
12
+ | | |
13
+ |---|---|
14
+ | **Lớp** | Bước S · việc **3/6** — việc **to nhất** của cả bước |
15
+ | **File code** | `skills/qc/qa-runner/` *(xoá 8 file)* → `qa-script-designer/` + `qa-script-runner/` |
16
+ | **File test** | không thêm — đèn S0 đã canh, và 2 WARN `scope khớp 0 file` đang chờ đúng bước này |
17
+ | **Phụ thuộc** | S0 ✅ *(đỏ 18)* · S1 ✅ *(hai module + bảng §2b)* |
18
+ | **Ai dùng nó** | `/qc-design-script` · `/qc-run-script` · `/qc-report` |
19
+
20
+ ---
21
+
22
+ ## 0 · Đo lại — 2026-09-17
23
+
24
+ | Đo | Kết quả |
25
+ |---|---|
26
+ | `skills/qc/qa-runner/` | **8 file · 26,931 B** — 7/8 dính stack cũ *(`exploratory/session.md` sạch)* |
27
+ | Ai **nạp** nó | `qc-design-script.tmpl:91` · `qc-run-script.tmpl:62` · `qc-report.tmpl:25` *(riêng `report/`)* |
28
+ | Đích `qa-script-designer/` *(proposal)* | **14 file · 57,864 B** |
29
+ | Đích `qa-script-runner/` *(proposal)* | 4 file · 13,445 B → **3 file** sau `H3` *(bỏ `smoke.md`, 5,895 B)* = **7,550 B** |
30
+ | Ngân sách | **không chạm** — `test/run.js:697` chỉ đếm `core/commands/*.md` *(N2)* |
31
+
32
+ ### 0.1 · Kho thứ TƯ — `/d/base/sdd-framework-qcreview/`
33
+
34
+ Bộ đề xuất tự khai nguồn trong comment đầu mỗi file: *"kế thừa nguyên vẹn từ
35
+ `sdd-framework_qcreview`"*. Kho đó **có thật**, tên đúng là `sdd-framework-qcreview`
36
+ *(gạch nối)*, commit duy nhất **2026-09-09** *"Initial commit: review sdd-framework & proposal"*.
37
+
38
+ **Đo thì proposal là bản sau và rộng hơn** — không cần dùng kho này:
39
+
40
+ | File | qcreview (09-09) | proposal (09-10) | khác |
41
+ |---|---|---|---|
42
+ | `_shared/web-conventions.md` | 16,269 B | 16,503 B | **9 dòng** |
43
+ | `_shared/mobile-conventions.md` | 12,331 B | 12,714 B | **3 dòng** |
44
+ | `web/functional/gui-screen.md` | 2,090 B | 2,322 B | **3 dòng** |
45
+
46
+ Proposal còn có thêm `_shared/injection-scanner.md` · `intake-validator.md` · `qa-designer/api/*`.
47
+ **Kết luận: dùng proposal, bỏ qua qcreview.** Ghi ra đây để lần sau không ai đi tìm lại.
48
+
49
+ ### 0.2 · 🟢 Công của Đợt 0–2 **được giữ** trong bộ đề xuất — đã kiểm từng mục
50
+
51
+ Đây là rủi ro lớn nhất của S2 *(xoá 8 file là xoá cả những thứ đợt này vừa sửa)*. Đo:
52
+
53
+ | Công của đợt | `qa-runner` | proposal `qa-script-designer` | |
54
+ |---|:-:|:-:|---|
55
+ | Hợp đồng test-id §4.5.6 | 2/8 | **5/14** | 🟢 proposal **phủ rộng hơn** |
56
+ | `@trace.testid_attr` | 2/8 | 2/14 | 🟢 giữ |
57
+ | `@trace.verifies` | **0/8** | **3/14** | 🟢 proposal **có, bản hiện tại không** |
58
+ | `Automatable` *(G1 · d2-b3)* | **0/8** | **12/14** | 🟢 proposal **có, bản hiện tại không** |
59
+ | `script-bug` / `product-gap` *(d1-b3)* | 5/8 | 1/14 · **4/4 ở `qa-script-runner`** | 🟢 **đúng chỗ hơn** — phân loại FAIL thuộc runner, không thuộc designer |
60
+
61
+ > **Con số đáng chú ý nhất: `Automatable` 0/8 → 12/14.** `qa-runner` hiện **không biết**
62
+ > `/qc-automation-assess` tồn tại — nó sinh script cho mọi TC. Đó là ngầm định mà `G1`/`d2-b3`
63
+ > sinh ra để bỏ, và **bản hiện tại chưa được vá ở tầng skill**. S2 không chỉ đổi stack; nó
64
+ > đóng nốt một lỗ của Đợt 2.
65
+
66
+ **Kiểm sâu hai chỗ dễ mất nhất — cả hai ĐẠT:**
67
+
68
+ - **Đợt 0 b3 *(test-id contract, thôi dò DOM)*** → `_shared/web-conventions.md §1.1` giữ nguyên
69
+ bảng 3 nhánh, và **dịch đúng sang TypeScript**: `playwright.config.ts → use: { testIdAttribute: 'data-test' }`.
70
+ Bản lệnh hiện tại đang ghi API Python `set_test_id_attribute()` — **proposal đúng hơn bản đang chạy**.
71
+ - **Flaky policy** → `web-conventions.md §7` dùng **quarantine**, khớp `Automation-Standards §11`,
72
+ **không** mang ngữ nghĩa `reruns` cũ. ⚠️ Khác một chữ: proposal tag `@quarantine`, chuẩn tag
73
+ `@flaky` — phải chốt một *(§1.3)*.
74
+
75
+ ### 0.3 · ⚠️ Comment nguồn gốc phải GỠ khi port
76
+
77
+ Mỗi file proposal mở đầu bằng một dòng HTML comment kiểu:
78
+
79
+ ```
80
+ <!-- Nguồn: kế thừa nguyên vẹn từ sdd-framework_qcreview …; giữ nguyên Playwright HTML Report,
81
+ không thay bằng pytest-html -->
82
+ ```
83
+
84
+ Hai lý do phải gỡ: nó trỏ một kho **không phải upstream of record** của framework này
85
+ *(`bin/qc-base-map.json` mới là nơi ghi provenance)*, và nó **nhắc tên stack cũ** → đèn S0 sẽ
86
+ đỏ ngay trên file vừa port. Đo: **3 dòng** ở `report.md`, **1** ở `web/run.md`.
87
+
88
+ ---
89
+
90
+ ## 0.4 · 🔄 ĐO LẠI SAU S3 *(2026-09-17)* — bản trình bày đầu đã lỗi thời ở ba chỗ
91
+
92
+ S3 chốt `S-PATH` và `S-VERDICT`, và điều đó đổi S2 nhiều hơn tưởng.
93
+
94
+ ### ① Port KHÔNG còn là copy — **16/18 file phải mổ đường dẫn**
95
+
96
+ | Phải đổi | Ở đâu |
97
+ |---|---|
98
+ | `{paths.qc_automation_dir}` → gốc `§layout` của module | designer **12/14** · runner **4/4** |
99
+ | `{domain}/{prd-slug}` → `{TICKET-ID}` | designer **12/14** · runner **4/4** |
100
+ | `<feature>.spec.ts` → `<feature>-<scenario>.spec.ts` | **10 chỗ** |
101
+ | `@quarantine` → `@flaky` | **4 file** |
102
+
103
+ Bản đầu viết *"port 14 file + viết 4"*. Đúng hơn: **port có mổ 16 file + viết 4**. Việc không
104
+ đổi, nhưng đừng gọi nó là copy — gọi sai tên là lý do người ta bỏ qua bước rà.
105
+
106
+ ### ② 🔴 S3 làm **S1 hết đúng ở 5 chỗ** — và không có gì bắt được
107
+
108
+ ```
109
+ S-PATH chốt tests/{TICKET-ID}/<feature>-<scenario>.spec.ts
110
+ S1 đang khai tests/<feature>/<feature>-<scenario>.spec.ts
111
+ ```
112
+
113
+ | File | Dòng |
114
+ |---|---|
115
+ | `modules/qc-playwright-ts/stack-profile.yaml` | **39** *(layout.web)* · **52** *(layout.api)* · **62** *(naming.spec)* |
116
+ | `modules/qc-wdio-appium/stack-profile.yaml` | **37** *(layout.mobile)* · **46** *(naming.spec)* |
117
+
118
+ S1 chép **đúng chữ** của `Automation-Standards §2.3`; S3 chốt **đúng nguyên tắc** của nó áp vào
119
+ layout framework. Cả hai đều có lý ở thời điểm của mình — và **không rule nào đối chiếu
120
+ `stack-profile` với tiêu chí review**, nên nó nằm im cho tới khi đọc lại bằng mắt.
121
+
122
+ > **Đây là giới hạn thứ hai của đèn S0, sau ca *"Không Allure"* ở S3 B5.** Đèn canh **tên công
123
+ > nghệ**; nó không canh **hai file của ta nói khác nhau**. Ghi thành nợ, **đừng dựng rule ngay** —
124
+ > chưa biết lớp lỗi này có tái diễn không.
125
+
126
+ ### ③ S3 đã phát cho bộ sinh một **bản đặc tả có thể đếm được**
127
+
128
+ `skills/qc/qa-reviewer/script/` nay chứa **22 luật `⇒ BLOCKER`** và **53 luật `⇒ MAJOR`**. Đó là
129
+ danh sách những thứ bộ sinh **phải** làm đúng, viết ra trước khi bộ sinh tồn tại — đúng điều
130
+ `S-ORDER` đặt cược. S2 không còn phải đoán *"script thế nào là đạt"*.
131
+
132
+ ### ④ Việc đấu dây còn treo, S2 phải đóng
133
+
134
+ | Treo | Ở đâu |
135
+ |---|---|
136
+ | Con trỏ skill của `/qc-report` | `qc-report.tmpl:30` — `qa-runner/report/` + ghi chú *"S2 đổi"* |
137
+ | 3 entry `qa-runner` `undecided` | `bin/qc-base-map.json` — chờ `targets` trỏ thư mục S2 tạo |
138
+ | 2 WARN `scope khớp 0 file` | `qa-script-designer/**` · `qa-script-runner/**` |
139
+ | Glob chết sau khi xoá | `skills/qc/qa-runner/**` — gỡ **trong cùng commit**, sau khi WARN nổ |
140
+
141
+ ---
142
+
143
+ ## 1 · BA CÂU CHỜ CHỐT
144
+
145
+ ### 1.1 · Lane `api/` — port hay viết mới?
146
+
147
+ `S-API` đã chốt lane API là nền thứ ba ở tầng skill. Nhưng **bộ đề xuất không có `api/`** — nó
148
+ chỉ có `web/functional/api.md` *(2,026 B)*, tức API như một layer của web.
149
+
150
+ | Phương án | Việc | Cái giá |
151
+ |---|---|---|
152
+ | **A · viết mới từ chuẩn Approved** ⭐ | 3 file `api/{endpoint,auth,security}.md` + `_shared/api-conventions.md`, nguồn `API-Testing-Standards` §4 *(API Object)* §5 *(AAA)* §6 *(Assertion)* §8 *(Test Design)* §9 *(Security)* + `AGT-010` §3 *(R01–R03)* | ~4 file phải viết tay, không có bản mẫu |
153
+ | **B · nhân bản `web/functional/api.md`** | copy rồi sửa | file đó viết cho **API-trong-dự-án-web**, dùng Page Object và `automation/`. Lane `system` cần **API Object** *(cấm `expect()` bên trong)* và `api-automation/`. Nhân bản là chép sai hình dạng — đúng bẫy B1 |
154
+
155
+ **Khuyến nghị: A.** `AGT-010` là agent **Approved** riêng, với `R01 API Object Mandatory` ·
156
+ `R02 No waitForTimeout` · `R03 No Hardcoded Data` — đủ để viết, không phải bịa.
157
+ **Giữ `web/functional/api.md`** cho ca API-phụ-trợ trong dự án web *(setup/teardown qua API)*,
158
+ trỏ sang `_shared/api-conventions.md` để luật chỉ có một bản.
159
+
160
+ ---
161
+
162
+ ### 1.2 · `qa-runner/exploratory/session.md` — file duy nhất KHÔNG dính stack
163
+
164
+ 7/8 file `qa-runner` dính Python; file này **sạch**, và bộ đề xuất **không có** file tương ứng
165
+ trong `qa-script-designer/` *(nó nằm ở `qa-designer/exploratory/session.md` — tầng tài liệu)*.
166
+
167
+ | Phương án | |
168
+ |---|---|
169
+ | **A · dời sang `qa-designer/exploratory/`** ⭐ | Exploratory là **phiên khám phá của người**, không sinh script. Nó thuộc tầng thiết kế, và `S-SCOPE` đã chốt tầng đó không đổi stack. Bộ đề xuất xếp đúng chỗ này |
170
+ | B · giữ trong `qa-script-designer/` | giữ một file không sinh script trong bộ sinh script |
171
+ | C · xoá | mất nội dung đang dùng được |
172
+
173
+ **Khuyến nghị: A.** Kiểm trước khi dời: `qa-designer/exploratory/` đã có `charter.md` và
174
+ `explore-to-functional.md`; phải xác nhận `session.md` **không trùng** hai file đó *(đo lúc làm,
175
+ không đoán)*.
176
+
177
+ ---
178
+
179
+ ### 1.3 · Tag cách ly test flaky — `@quarantine` hay `@flaky`?
180
+
181
+ | Nguồn | Tag | Ngữ nghĩa |
182
+ |---|---|---|
183
+ | `Automation-Standards §11` **(Approved)** | `@flaky` | exclude khỏi main suite, điều tra trong 1 sprint, ghi Automation Health Log |
184
+ | proposal `web-conventions §7` | `@quarantine` | flaky lặp ≥3 lần chạy gần nhất → tách khỏi suite chính |
185
+
186
+ Cùng một việc, hai tên. **Khuyến nghị: `@flaky` theo chuẩn Approved**, và giữ **ngưỡng ≥3 lần**
187
+ của proposal vì chuẩn không nêu ngưỡng — chuẩn nói *"phải investigate"*, proposal nói *"khi nào
188
+ thì tách"*. Hai thứ bổ sung nhau, không mâu thuẫn.
189
+
190
+ > Vì sao không để cả hai tag: tag là thứ `/qc-run-script` và CI **lọc bằng chuỗi**. Hai tên cho
191
+ > một trạng thái nghĩa là một nửa số test cách ly sẽ lọt qua bộ lọc, **im lặng**.
192
+
193
+ ---
194
+
195
+ # PHẦN A — Chuyện gì đang xảy ra
196
+
197
+ ## A1 · Vấn đề
198
+
199
+ Bộ hướng dẫn mà máy đọc để viết bài kiểm tra tự động vẫn đang dạy viết bằng thứ tiếng đã bỏ. Tệ
200
+ hơn là nó gộp hai việc rất khác nhau vào một chỗ: *viết bài* và *chạy bài rồi chấm*. Ai sửa phần
201
+ chấm điểm cũng phải mở đúng file đang dạy cách viết, nên hai việc kéo nhau, và không ai dám sửa
202
+ một mình. Còn một chỗ hổng nữa: bộ này **không biết** rằng đội đã quyết một số bài phải làm tay
203
+ — nó cứ viết máy cho tất cả.
204
+
205
+ ## A2 · Cách giải quyết, nói bằng một hình ảnh
206
+
207
+ Như tách sổ tay đầu bếp ra khỏi sổ chấm món. Trước đây hai thứ chép chung một quyển, nên muốn
208
+ đổi cách chấm cũng phải giở đúng trang công thức. Tách ra thì bếp cứ nấu, giám khảo cứ chấm, và
209
+ sửa bên nào cũng không làm rơi bên kia.
210
+
211
+ ## A3 · Xong rồi thì thấy gì khác
212
+
213
+ Thư mục `qa-runner` biến mất, thay bằng hai thư mục tên đúng việc chúng làm:
214
+ `qa-script-designer` *(viết)* và `qa-script-runner` *(chạy và chấm)*. Hai cảnh báo đang hiện
215
+ trong `self-check` sẽ tự tắt. Và số file đèn còn bắt tụt từ **18 xuống 11**.
216
+
217
+ ## A4 · Thuật ngữ dùng ở trên
218
+
219
+ - **skill** — file hướng dẫn mà lệnh nạp lúc chạy; máy đọc, không phải người chạy.
220
+ - **lane** — nhánh theo nền: `web` · `mobile` · `api`. Mỗi lần chạy chỉ nạp một nhánh.
221
+ - **`_shared`** — phần luật dùng chung của một nhánh, để các file trong nhánh khỏi chép lại nhau.
222
+ - **flaky** — bài kiểm tra lúc đỏ lúc xanh mà sản phẩm không đổi; thường do thời gian chờ hoặc mạng.
223
+ - **quarantine / cách ly** — tách bài flaky ra khỏi bộ chính để nó thôi làm nhiễu, rồi mới điều tra.
224
+ - **`Automatable`** — nhãn đội QC gán cho từng bài: máy chạy được (`Y`) hay phải làm tay (`N`).
225
+
226
+ ---
227
+
228
+ > ### ✅ Phép thử đọc to
229
+ > **Đã thử với:** tự đọc to · **ngày:** 2026-09-17 · **phải giải thích thêm:** cụm *"bộ này
230
+ > không biết đội đã quyết một số bài phải làm tay"* — người nghe hỏi *"không biết thì nó làm
231
+ > sao?"*. Câu trả lời là **nó viết máy cho tất cả**, đã thêm vào cuối A1.
232
+
233
+ ---
234
+
235
+ # PHẦN B — Chi tiết kỹ thuật
236
+
237
+ ## B1 · Cách hiển nhiên là gì, và vì sao nó sai
238
+
239
+ **Cách hiển nhiên: `cp -r` cả cụm từ bộ đề xuất sang, xong.** 14 + 4 file đã đúng hình dạng, đã
240
+ TypeScript, đã tách lane. Copy là xong trong một phút.
241
+
242
+ **Sai vì bốn lý do, cả bốn đo được:**
243
+
244
+ **1 · Kéo theo comment nguồn gốc trỏ một kho không phải upstream of record** *(§0.3)*. 4 dòng,
245
+ và chúng còn nhắc tên stack cũ nên đèn S0 đỏ ngay trên file vừa port.
246
+
247
+ **2 · Copy nguyên nghĩa là nhận luôn `@quarantine`** trong khi chuẩn Approved dùng `@flaky`
248
+ *(§1.3)*. Một tag lệch tên là một nửa số test cách ly lọt qua bộ lọc CI, **im lặng**.
249
+
250
+ **3 · Bộ đề xuất KHÔNG có lane `api/`** *(§1.1)* — copy xong vẫn thiếu đúng nhánh mà `S-API`
251
+ vừa chốt, và ô `system` vẫn hỏng như trước S1.
252
+
253
+ **4 · Copy bỏ sót việc dời `exploratory/session.md`** *(§1.2)* — file duy nhất trong `qa-runner`
254
+ không dính stack sẽ bị xoá cùng thư mục, trong khi nó vẫn dùng được và thuộc về tầng khác.
255
+
256
+ > **Cách hiển nhiên thứ hai — "viết lại tất cả từ `qc-base-new` cho chắc".** Cũng sai, và §0.2
257
+ > là lý do: bộ đề xuất **đã mang công của Đợt 0–2** *(§4.5.6 5/14 · `Automatable` 12/14 ·
258
+ > `@trace.verifies` 3/14)*, có chỗ còn **đúng hơn bản đang chạy**. Viết lại từ `qc-base-new`
259
+ > — bộ 7 file chỉ phủ tầng L4 — là bỏ đi phần đó rồi dựng lại bằng tay.
260
+
261
+ ## B2 · Cách làm đúng
262
+
263
+ **Nguồn chia đôi, như luật đã ghi ở [`PLAN_v2 §4.2`](PLAN_v2.md):**
264
+ **bố cục + nội dung layer ← proposal · luật stack và phiên bản ← `qc-base-new`.**
265
+
266
+ ```
267
+ ① dời qa-runner/exploratory/session.md → qa-designer/exploratory/ (§1.2, kiểm trùng trước)
268
+ ② xoá qa-runner/** (7 file còn lại)
269
+ ③ port qa-script-designer/ ← proposal 14 file
270
+ - gỡ comment nguồn gốc (§0.3)
271
+ - @quarantine → @flaky, giữ ngưỡng ≥3 (§1.3)
272
+ - đối chiếu _shared/web-conventions §1–§10 với Automation-Standards §4–§11
273
+ - đối chiếu _shared/mobile-conventions với Mobile-Automation-Standards
274
+ ④ viết qa-script-designer/api/{endpoint,auth,security}.md + _shared/api-conventions.md
275
+ nguồn: API-Testing-Standards §4 §5 §6 §8 §9 + AGT-010 §3 (R01–R03) (§1.1)
276
+ ⑤ port qa-script-runner/ ← proposal 3 file (web/run.md · mobile/run.md · report.md)
277
+ smoke.md KHÔNG port (H3)
278
+ ⑥ trỏ 3 lệnh: qa-runner/ → qa-script-{designer,runner}/ + lane theo §2b
279
+ ⑦ gỡ scope glob "skills/qc/qa-runner/**" khỏi trace-schema — CÙNG commit
280
+ ```
281
+
282
+ **⑦ không phải việc dọn dẹp — nó là một phép thử.** Sau ②, glob đó khớp 0 file và cảnh báo
283
+ `scope khớp 0 file` sẽ nổ. Cảnh báo đó **phải nổ** *(chứng minh việc xoá đã xảy ra thật)*, rồi
284
+ mới gỡ glob. Gỡ trước là mất bằng chứng.
285
+
286
+ **Vì sao `_shared` tách riêng cho từng nền:** `web-conventions` **16.5 KB** và
287
+ `mobile-conventions` **12.7 KB** — nhồi chung thì mỗi lần sinh script web vẫn phải đọc 12.7 KB
288
+ luật Appium. Cùng lý do đã chốt ở S1 §1.3 cho hồ sơ stack.
289
+
290
+ ## B3 · Nếu làm sai thì hỏng theo kiểu nào
291
+
292
+ **Im lặng 🔴, và đây là bước dễ hỏng im lặng nhất của cả Bước S.** Một skill port thiếu một mục
293
+ vẫn là Markdown hợp lệ, vẫn được lệnh nạp, vẫn sinh ra script chạy được. Ví dụ cụ thể: mất
294
+ `Automatable` khỏi `web/functional/gui-screen.md` → lệnh sinh script cho **mọi** TC, TC không
295
+ automate được thành `test.fixme` rồi **biến mất khỏi báo cáo**, và `/qc-report` chấm cả PRD là
296
+ FAIL vĩnh viễn. Ai phát hiện: không ai, cho tới lần review báo cáo đầu tiên có người hỏi *"sao
297
+ TC này không bao giờ chạy"*.
298
+
299
+ Đèn S0 **không bắt được lớp lỗi này** — nó canh chữ, không canh nội dung thiếu. Phép thử B4 #4
300
+ là thứ duy nhất canh.
301
+
302
+ ## B4 · Verify bằng gì
303
+
304
+ | # | Phép thử | Kết quả mong đợi |
305
+ |:-:|---|---|
306
+ | 1 | `ls skills/qc/qa-runner` | không tồn tại |
307
+ | 2 | `find skills/qc/qa-script-designer -type f \| wc -l` | **18** = 14 port + 3 api + 1 api-conventions |
308
+ | 3 | `find skills/qc/qa-script-runner -type f \| wc -l` | **3** *(không có `smoke.md`)* |
309
+ | 4 | **Đếm lại 5 mục §0.2** trên bộ MỚI | `Automatable` **≥12** · §4.5.6 **≥5** · `@trace.verifies` **≥3** · `testid_attr` **≥2** · `script-bug`/`product-gap` **≥4 ở runner**. Thiếu bất kỳ mục nào = port hụt |
310
+ | 5 | `grep -rn 'sdd-framework_qcreview\|qcreview' skills/qc/` | **0** — comment nguồn gốc đã gỡ |
311
+ | 6 | `grep -rn '@quarantine' skills/qc/` | **0**; `grep -rn '@flaky'` **≥1** |
312
+ | 7 | `self-check` | R5 đỏ **11 file** *(18 − 7 `qa-runner`)*; **WARN `scope khớp 0 file` biến mất cả 3** *(2 cái cũ tự tắt, `qa-runner/**` do ⑦ gỡ)* |
313
+ | 8 | **Phá:** thêm `pytest` vào `qa-script-designer/api/endpoint.md` | **ĐỎ** — lane mới thật sự nằm trong tầm đèn |
314
+ | 9 | `grep -rn 'qa-runner' commands/*.tmpl` | **0** |
315
+ | 10 | 4 lệnh kiểm | `build` ✅ · `244/244` ✅ · `lint-trace` ✅ · `self-check` đỏ 11 |
316
+
317
+ > **#7 là phép thử kép.** Số đỏ phải giảm **đúng 7** *(không hơn: S2 không đụng `qa-reviewer/script`
318
+ > hay `commands/`)*, **và** cả ba WARN scope phải tắt. Một trong hai sai là phạm vi đã lệch.
319
+
320
+ ## B5 · Bài học
321
+
322
+ - **2026-09-17** — Viết khối ghi chú mới *(trạng thái tích hợp đã thi hành)* và **tự đặt chữ bị cấm vào đó**: *"Module Python cũ"*, *"một stack-profile Python còn sống"*. Đèn đỏ ngay trên đoạn văn tôi vừa gõ. **Lần thứ hai** mắc đúng lỗi này — lần đầu ở S1 với comment `trace_tags`, và bài học *"mô tả, đừng trích dẫn"* đã ghi ở S5 B5. Ghi một lần không đủ: sửa thành **đọc lại vùng vừa viết bằng chính đèn trước khi chạy tiếp**, không chờ tới bước verify.
323
+ - **2026-09-17** — Bộ đề xuất mang một khối *"Ghi chú tích hợp sdd-framework"* — **chỉ dẫn migration gửi cho chúng ta**, nay đã thi hành xong và **dự đoán sai hai chỗ**: nó bảo khai tên module qua `tech_stack.qc_module` *(S1 đã bác)* và gợi ý giữ module cũ *"tạm thời để tương thích ngược"* *(H2 đã bác)*. Port mù là nhập một bản kế hoạch quá hạn vào làm tài liệu hướng dẫn. **Tài liệu nguồn có hai loại: mô tả cái gì ĐÚNG, và chỉ dẫn phải LÀM gì. Loại thứ hai hết hạn ngay khi làm xong — phải thay bằng mô tả hiện trạng, không phải dịch chữ.**
324
+ - **2026-09-17** — Cơ chế `scope khớp 0 file` *(thêm ở S0)* hoạt động **đúng cả hai chiều trong một lần chạy**: hai cảnh báo `qa-script-designer/**` · `qa-script-runner/**` **tự tắt** khi thư mục ra đời, và cảnh báo `qa-runner/**` **bật lên** khi thư mục bị xoá. Đó là bằng chứng việc xoá đã xảy ra thật — gỡ glob sau khi thấy nó, không gỡ trước.
325
+ - **2026-09-17** — `session.md` định dời nguyên vẹn, nhưng đối chiếu thì **Mode 2 của nó trùng nghĩa với `explore-to-functional.md` Phase 4** — và bản ở `session.md` là bản rút gọn. Dời mù là nhân đôi một format, và **bản ngắn sẽ lạc hậu trước** trong khi người đọc không biết mình đang đọc bản cũ. Đã cắt Mode 2 thành con trỏ. **"Dời file" không phải một thao tác cơ học — phải hỏi nội dung nó có trùng chỗ mới không.**
326
+
327
+ ## B6 · Copy được / không copy được
328
+
329
+ | | |
330
+ |---|---|
331
+ | ✅ **Copy được** | Trước khi xoá một thư mục, **đếm từng thứ đợt trước vừa thêm vào nó** và kiểm bản thay có giữ không — §0.2 là khuôn; tách nguồn *"bố cục ← đề xuất, luật ← tài liệu Approved"*; để cảnh báo `scope khớp 0 file` **nổ trước rồi mới gỡ glob**, dùng chính guard làm bằng chứng công việc đã xảy ra |
332
+ | ⚠️ **Chỉ đúng ở đây** | Tên `qa-script-designer`/`qa-script-runner`; 4 kho nguồn và thứ tự ưu tiên giữa chúng; tag `@flaky`; con số 18/3/11 |
333
+
334
+ ## B7 · Link
335
+
336
+ - [`exec-S-ap-stack-typescript.md`](exec-S-ap-stack-typescript.md) §8 *(S2 là việc 3/6)* · §7 `S-API` · `H1` · `H3` · `S-SCOPE`
337
+ - [`exec-S0-guard-cam-stack-cu.md`](exec-S0-guard-cam-stack-cu.md) §1.2b *(phạm vi đèn)* · B4 #10 *(glob chết)*
338
+ - `/d/base/qcframework_proposal/skills/qc/qa-script-{designer,runner}/` — nguồn bố cục
339
+ - `upstream/qc-base-new/` — `Automation-Standards` §4–§11 · `Mobile-Automation-Standards` · `API-Testing-Standards` §4 §5 §6 §8 §9 · `AGT-010` §3
340
+ - `/d/base/sdd-framework-qcreview/` — kho thứ tư, **đã loại**: proposal là bản sau và rộng hơn *(§0.1)*