@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.
- package/bin/qc-base-map.json +13 -11
- package/bin/self-check.js +49 -4
- package/bin/trace-schema.json +3226 -3187
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/qc-analyze.md +2 -2
- package/core/commands/qc-automation-assess.md +3 -3
- package/core/commands/qc-design-script.md +60 -30
- package/core/commands/qc-design-test.md +79 -7
- package/core/commands/qc-plan.md +1 -1
- package/core/commands/qc-report.md +85 -76
- package/core/commands/qc-review-script.md +25 -16
- package/core/commands/qc-review-testcase.md +8 -7
- package/core/commands/qc-run-manualtest.md +1 -1
- package/core/commands/qc-run-script.md +15 -8
- package/core/modules/qc-playwright-ts/module.yaml +13 -0
- package/core/modules/qc-playwright-ts/stack-profile.yaml +99 -0
- package/core/modules/qc-wdio-appium/module.yaml +20 -0
- package/core/modules/qc-wdio-appium/stack-profile.yaml +107 -0
- package/core/skills/qc/qa-analyst/data-flow.md +1 -1
- package/core/skills/qc/qa-automation-assess/matrix.md +6 -3
- package/core/skills/qc/{qa-runner → qa-designer}/exploratory/session.md +8 -2
- package/core/skills/qc/qa-designer/functional/api.md +1 -1
- package/core/skills/qc/qa-designer/functional/job.md +128 -0
- package/core/skills/qc/qa-designer/integration/api.md +1 -1
- package/core/skills/qc/qa-designer/integration/db.md +1 -1
- package/core/skills/qc/qa-designer/integration/{kafka.md → queue.md} +20 -4
- package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +17 -0
- package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +17 -0
- package/core/skills/qc/qa-reviewer/script/_shared/review-rules.md +121 -0
- package/core/skills/qc/qa-reviewer/script/api/auth.md +49 -0
- package/core/skills/qc/qa-reviewer/script/api/endpoint.md +89 -0
- package/core/skills/qc/qa-reviewer/script/api/security.md +46 -0
- package/core/skills/qc/qa-reviewer/script/exploratory.md +2 -2
- package/core/skills/qc/qa-reviewer/script/mobile/e2e.md +41 -0
- package/core/skills/qc/qa-reviewer/script/mobile/functional.md +90 -0
- package/core/skills/qc/qa-reviewer/script/mobile/integration.md +41 -0
- package/core/skills/qc/qa-reviewer/script/mobile/non-functional.md +43 -0
- package/core/skills/qc/qa-reviewer/script/web/e2e.md +46 -0
- package/core/skills/qc/qa-reviewer/script/web/functional.md +111 -0
- package/core/skills/qc/qa-reviewer/script/web/integration.md +46 -0
- package/core/skills/qc/qa-reviewer/script/web/non-functional.md +49 -0
- package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +1 -1
- package/core/skills/qc/qa-reviewer/shared/review-file-template.md +26 -7
- package/core/skills/qc/qa-reviewer/test-case/e2e.md +1 -1
- package/core/skills/qc/qa-reviewer/test-case/exploratory.md +1 -1
- package/core/skills/qc/qa-reviewer/test-case/functional.md +1 -1
- package/core/skills/qc/qa-reviewer/test-case/integration.md +1 -1
- package/core/skills/qc/qa-reviewer/test-case/non-functional.md +1 -1
- package/core/skills/qc/qa-script-designer/_shared/api-conventions.md +94 -0
- package/core/skills/qc/qa-script-designer/_shared/file-naming-and-folders.md +109 -0
- package/core/skills/qc/qa-script-designer/_shared/mobile-conventions.md +196 -0
- package/core/skills/qc/qa-script-designer/_shared/web-conventions.md +257 -0
- package/core/skills/qc/qa-script-designer/api/auth.md +43 -0
- package/core/skills/qc/qa-script-designer/api/endpoint.md +61 -0
- package/core/skills/qc/qa-script-designer/api/security.md +41 -0
- package/core/skills/qc/qa-script-designer/mobile/e2e.md +35 -0
- package/core/skills/qc/qa-script-designer/mobile/functional/feature.md +32 -0
- package/core/skills/qc/qa-script-designer/mobile/functional/screen.md +42 -0
- package/core/skills/qc/qa-script-designer/mobile/integration.md +39 -0
- package/core/skills/qc/qa-script-designer/mobile/non-functional.md +39 -0
- package/core/skills/qc/qa-script-designer/web/e2e.md +36 -0
- package/core/skills/qc/qa-script-designer/web/functional/api.md +39 -0
- package/core/skills/qc/qa-script-designer/web/functional/gui-feature.md +34 -0
- package/core/skills/qc/qa-script-designer/web/functional/gui-screen.md +42 -0
- package/core/skills/qc/qa-script-designer/web/integration.md +43 -0
- package/core/skills/qc/qa-script-designer/web/non-functional.md +42 -0
- package/core/skills/qc/qa-script-runner/mobile/run.md +38 -0
- package/core/skills/qc/qa-script-runner/report.md +41 -0
- package/core/skills/qc/qa-script-runner/web/run.md +48 -0
- package/core/steps/qc-scope.md +43 -0
- package/core/steps/report-footer.md +2 -2
- package/docs/02-concepts/pipeline-steps/07-dev-selftest.md +1 -1
- package/docs/02-concepts/pipeline-steps/08-qc-automation.md +10 -10
- package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +1 -1
- package/docs/02-concepts/traceability.md +1 -1
- package/docs/03-guides/developer.md +1 -1
- package/docs/03-guides/tester-qa.md +40 -12
- package/docs/04-reference/commands.md +1 -1
- package/docs/04-reference/modules.md +2 -1
- package/docs/explain/17-qc-design-test.md +2 -2
- package/docs/explain/19-qc-run-test.md +4 -4
- package/docs/explain/20-qc-report.md +1 -1
- package/docs/explain/23-fix-bug.md +2 -2
- package/docs/plans/qc-surgery/01-checklist.md +18 -6
- package/docs/plans/qc-surgery/PLAN_v2.md +295 -0
- package/docs/plans/qc-surgery/exec-S-ap-stack-typescript.md +420 -0
- package/docs/plans/qc-surgery/exec-S0-guard-cam-stack-cu.md +400 -0
- package/docs/plans/qc-surgery/exec-S1-hai-module-thay-qc-playwright.md +267 -0
- package/docs/plans/qc-surgery/exec-S2-qa-runner-thanh-script-designer-runner.md +340 -0
- package/docs/plans/qc-surgery/exec-S3-viet-lai-tieu-chi-review-script.md +322 -0
- package/docs/plans/qc-surgery/exec-S5-an-theo-don-dau-vet-stack-cu.md +292 -0
- package/package.json +1 -1
- package/core/modules/qc-playwright/stack-profile.yaml +0 -66
- package/core/skills/qc/qa-reviewer/script/e2e.md +0 -95
- package/core/skills/qc/qa-reviewer/script/functional.md +0 -109
- package/core/skills/qc/qa-reviewer/script/integration.md +0 -99
- package/core/skills/qc/qa-reviewer/script/non-functional.md +0 -134
- package/core/skills/qc/qa-runner/e2e.md +0 -49
- package/core/skills/qc/qa-runner/functional/api.md +0 -35
- package/core/skills/qc/qa-runner/functional/gui-feature.md +0 -57
- package/core/skills/qc/qa-runner/functional/gui-screen.md +0 -61
- package/core/skills/qc/qa-runner/integration.md +0 -47
- package/core/skills/qc/qa-runner/non-functional.md +0 -49
- package/core/skills/qc/qa-runner/report/report.md +0 -37
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
---
|
|
2
|
+
buoc: Bước S · việc S3
|
|
3
|
+
title: Xoá 5 file review script phẳng, viết mới theo lane — và chốt bố cục thư mục test
|
|
4
|
+
phu_thuoc: S0 (đèn) · S1 (module + §2b) · S5 (dọn ăn theo)
|
|
5
|
+
trang_thai: ĐÃ TRIỂN KHAI 2026-09-17 · R5 đỏ 14→9 · R4 sạch · verdict 4 mức cho lane QC
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# S3 — Viết lại tiêu chí review script, và chốt nơi script sẽ nằm
|
|
9
|
+
|
|
10
|
+
← [`exec-S-ap-stack-typescript.md`](exec-S-ap-stack-typescript.md) · [`exec-S2-…`](exec-S2-qa-runner-thanh-script-designer-runner.md) · [`PLAN_v2.md`](PLAN_v2.md)
|
|
11
|
+
|
|
12
|
+
| | |
|
|
13
|
+
|---|---|
|
|
14
|
+
| **Lớp** | Bước S · việc **4/6** theo `S-ORDER` *(S0→S1→S5→**S3**→S2→S4)* |
|
|
15
|
+
| **File code** | `skills/qc/qa-reviewer/script/` — **xoá 5, viết mới** `script/{web,mobile,api}/*` |
|
|
16
|
+
| **File test** | không thêm |
|
|
17
|
+
| **Phụ thuộc** | S0 ✅ · S1 ✅ · S5 ✅ |
|
|
18
|
+
| **Ai dùng nó** | `/qc-review-script` |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 0 · Đo lại — 2026-09-17
|
|
23
|
+
|
|
24
|
+
| Đo | Kết quả |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `qa-reviewer/script/` hiện tại | **5 file · 22,033 B · 490 dòng**, phẳng theo tầng |
|
|
27
|
+
| Mật độ nhiễm | `non-functional` **11,2 %** · `functional` **9,2 %** · `e2e` 4,2 % · `integration` 4,0 % · `exploratory` 1,9 % |
|
|
28
|
+
| Proposal `qa-reviewer/script/` | **10 file** — `web/*` 5 *(có `web/functional/api.md`)* · `mobile/*` 4 · `exploratory.md` |
|
|
29
|
+
| `AGT-006` *(Draft)* | **R01–R07** · §3.2 Severity · §3.3 **Verdict Rules** · §4 Checklist 7 nhóm |
|
|
30
|
+
| Verdict framework đang dùng | **2 giá trị** — `APPROVED` (17 chỗ) · `NEEDS_FIX` (16 chỗ), **không có trong `trace-schema.json`** ⇒ là văn xuôi, không phải contract field |
|
|
31
|
+
| Verdict `AGT-006` | **4 giá trị** — `APPROVED` · `APPROVED_WITH_SUGGESTIONS` · `REVISION_REQUIRED` · `REJECTED` |
|
|
32
|
+
|
|
33
|
+
### 0.1 · 🔴 `{paths.qc_automation_dir}` — key KHÔNG TỒN TẠI, và proposal dùng nó ở **29 file**
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
framework khai (templates/project-context.yaml): qc_dir · qc_skills_dir
|
|
37
|
+
proposal dùng: qc_automation_dir ← không có ở đâu cả
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**File đích của S2 + S3 mang key này:**
|
|
41
|
+
|
|
42
|
+
| Cụm | Dính |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `qa-script-designer/` | **12/14** |
|
|
45
|
+
| `qa-script-runner/` | **4/4** |
|
|
46
|
+
| `qa-reviewer/script/` | **8/10** |
|
|
47
|
+
| | **24/28** |
|
|
48
|
+
|
|
49
|
+
`R4` của `self-check` bắt **đúng** lớp lỗi này — *"`{paths.X}` dùng mà X không có trong
|
|
50
|
+
`project-context.yaml` → ERROR"* — và từ S0, `R4` **đã quét `skills/`**. Nghĩa là: port
|
|
51
|
+
nguyên 24 file đó làm `R4` đỏ ngay, không cần ai phát hiện bằng mắt.
|
|
52
|
+
|
|
53
|
+
### 0.2 · 🔴 Hai bố cục thư mục test đang chọi nhau
|
|
54
|
+
|
|
55
|
+
```
|
|
56
|
+
Approved (Automation-Standards §2.3 — đã vào stack-profile ở S1)
|
|
57
|
+
automation/pages/<feature>.page.ts
|
|
58
|
+
automation/tests/<feature>/<feature>-happy-path.spec.ts
|
|
59
|
+
|
|
60
|
+
proposal (24/28 file đích)
|
|
61
|
+
{qc_automation_dir}/web/pages/{domain}/{prd-slug}/<feature>.page.ts
|
|
62
|
+
{qc_automation_dir}/web/tests/{domain}/{prd-slug}/<feature>.spec.ts
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Khác **ba** chỗ, không chỉ tên biến: có/không segment `{domain}/{prd-slug}` · có/không segment
|
|
66
|
+
`web/` *(module đã nói nền rồi)* · tên file `<feature>.spec.ts` vs `<feature>-<scenario>.spec.ts`.
|
|
67
|
+
|
|
68
|
+
> 🔑 **Chỗ này KHÔNG phải "đề xuất sai chuẩn".** `Automation-Standards §2.3` viết
|
|
69
|
+
> *"`tests/` mirror `test-suites/`"* — nhưng framework **không có** `test-suites/`; nó có
|
|
70
|
+
> `{qc_dir}/{TICKET-ID}/{platform}/test-cases/*.Test.md`. Áp **nguyên tắc** của chuẩn
|
|
71
|
+
> *(tests/ soi gương theo nơi test-case sống)* vào layout của framework thì ra **đúng hình dạng
|
|
72
|
+
> mà proposal viết**. Hai bên không mâu thuẫn về nguyên tắc — chúng mâu thuẫn vì chuẩn giả định
|
|
73
|
+
> một bố cục artifact mà framework không dùng.
|
|
74
|
+
|
|
75
|
+
### 0.3 · 🟢 `S-ORDER` được xác nhận đúng ngay ở bước đầu áp dụng
|
|
76
|
+
|
|
77
|
+
Quyết định *"viết tiêu chí review **trước** bộ sinh"* vừa trả cổ tức: **S3 lộ ra một quyết định
|
|
78
|
+
mà S2 phụ thuộc** *(§0.1 + §0.2 — nơi script sẽ nằm)*. Làm S2 trước thì bộ sinh đã **nướng sẵn
|
|
79
|
+
một bố cục**, và S3 chỉ còn cách chép theo — mất đúng cái đối chứng độc lập mà `G3` nói tới.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 1 · BA CÂU CHỜ CHỐT
|
|
84
|
+
|
|
85
|
+
### 1.1 · Bố cục thư mục test — chốt hình dạng nào?
|
|
86
|
+
|
|
87
|
+
| Phương án | Hình dạng | Cái giá |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| **A · Approved thuần** | `automation/tests/<feature>/<feature>-happy-path.spec.ts` | Tên feature phải **độc nhất toàn repo QC**. Hai PRD cùng có feature `login` là va nhau, **im lặng ghi đè** |
|
|
90
|
+
| **B · proposal thuần** | `{qc_automation_dir}/web/tests/{domain}/{prd-slug}/<feature>.spec.ts` | Cần **khai key mới** *(§1.2)*; segment `web/` **lặp lại điều module đã nói**; mất quy ước `<feature>-<scenario>` của chuẩn |
|
|
91
|
+
| **C · gốc theo chuẩn, phân tách theo PRD** ⭐ | `automation/tests/{TICKET-ID}/<feature>-<scenario>.spec.ts`<br/>`automation/pages/<feature>.page.ts` *(phẳng)* | Lệch chữ so với chuẩn, nhưng **giữ nguyên tắc** của chuẩn |
|
|
92
|
+
|
|
93
|
+
**Khuyến nghị: C.** Ba lý do đo được:
|
|
94
|
+
|
|
95
|
+
1. **Gốc `automation/` · `api-automation/` · `mobile-automation/` là của chuẩn Approved** và đã
|
|
96
|
+
nằm trong `stack-profile.yaml` từ S1 — giữ.
|
|
97
|
+
2. **Segment PRD là bất biến của framework**, không phải sở thích: `qc_artifact_dir` =
|
|
98
|
+
`{qc_dir}/{TICKET-ID}/{platform}/`, sổ trace = `{trace_dir}/{domain}/{prd-slug}/…`. `tests/`
|
|
99
|
+
không mang PRD thì nó là thư mục duy nhất trong cả dây chuyền **không biết mình thuộc PRD nào**.
|
|
100
|
+
3. **`pages/` để phẳng** — Page Object là **tài sản dùng lại**: một màn `login` phục vụ nhiều PRD.
|
|
101
|
+
Nhét nó vào `{TICKET-ID}/` là ép nhân bản, đúng thứ Page Object Pattern sinh ra để tránh.
|
|
102
|
+
|
|
103
|
+
> Điểm bất đối xứng ở (3) là **có chủ ý và phải ghi rõ**: `tests/` chia theo PRD, `pages/` thì
|
|
104
|
+
> không. Lý do: một spec **thuộc về** một PRD; một Page Object **được dùng bởi** nhiều PRD.
|
|
105
|
+
|
|
106
|
+
**Nếu chốt A** → phải thêm luật *"tên feature độc nhất toàn repo"* và một chỗ canh nó, nếu không
|
|
107
|
+
lỗi va tên là **ghi đè im lặng**.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
### 1.2 · Khai `{paths.qc_automation_dir}` hay không dùng key?
|
|
112
|
+
|
|
113
|
+
| Phương án | |
|
|
114
|
+
|---|---|
|
|
115
|
+
| **A · KHÔNG khai key** ⭐ | Gốc lấy từ `§layout` của module *(đã có từ S1)*; skill viết đường dẫn **tương đối với gốc đó**. Chuẩn Approved cũng **không tham số hoá** `automation/` |
|
|
116
|
+
| B · khai `qc_automation_dir` vào `project-context.yaml` | Dự án đổi chỗ được. Cái giá: một key mới phải qua `R4` · template · docs · `sync`, và **hai nguồn cho một sự thật** — `§layout` của module đã nói gốc ở đâu |
|
|
117
|
+
|
|
118
|
+
**Khuyến nghị: A.** Đây là cùng lập luận đã dùng ở S1 §1.1 khi bỏ `tech_stack.qc_module`: hỏi
|
|
119
|
+
lại một câu đã có đáp án thì hai đáp án sẽ lệch nhau, và lệch **im lặng**.
|
|
120
|
+
|
|
121
|
+
**Việc phải làm khi port:** thay `{paths.qc_automation_dir}/web/` → gốc `§layout.web` ở **24
|
|
122
|
+
file**. `R4` là lưới an toàn — không port sót file nào mà không bị bắt.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
### 1.3 · Verdict — giữ 2 giá trị hay lấy 4 của `AGT-006`?
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
framework (17 + 16 chỗ) APPROVED · NEEDS_FIX
|
|
130
|
+
AGT-006 §3.3 APPROVED · APPROVED_WITH_SUGGESTIONS · REVISION_REQUIRED · REJECTED
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
| Phương án | Cái giá |
|
|
134
|
+
|---|---|
|
|
135
|
+
| **A · giữ 2 verdict, lấy SEVERITY của AGT-006** ⭐ | `BLOCKER`·`MAJOR`·`MINOR`·`SUGGESTION` vào phần **finding**; verdict quy về 2: `BLOCKER∨MAJOR → NEEDS_FIX`, còn lại `→ APPROVED` kèm ghi chú |
|
|
136
|
+
| B · lấy 4 verdict cho **riêng** lane script | `/qc-review-script` và `/qc-review-testcase` **nói hai thứ tiếng** — cặp lệnh mà `E2` vừa tách ra để rõ vai, nay lệch từ vựng |
|
|
137
|
+
| C · lấy 4 verdict cho **cả hai** lane | Đụng `qa-reviewer/test-case/*` — mà `S-SCOPE` đã chốt **không đổi** tầng đó |
|
|
138
|
+
|
|
139
|
+
**✅ CHỐT (09-17): C — 4 verdict cho CẢ HAI lệnh soát của lane QC.** Khác khuyến nghị ban đầu của tôi (A), và anh đúng: xem §1.3b.
|
|
140
|
+
|
|
141
|
+
> ### 1.3b · Vì sao khuyến nghị A của tôi sai
|
|
142
|
+
>
|
|
143
|
+
> Tôi báo giá 4 verdict là *"sinh ra ba quyết định mới cho ba cửa chặn"* — `/qc-automation-assess`,
|
|
144
|
+
> `/qc-design-script`, `/qc-run-script` phải nói rõ chúng nhận mức nào. **Không đúng.** Đọc kỹ
|
|
145
|
+
> nguồn thì:
|
|
146
|
+
>
|
|
147
|
+
> - **Verdict là HÀM của severity**, không phải một phán quyết riêng. `AGT-006` §3.3 suy nó từ
|
|
148
|
+
> số đếm `BLOCKER`/`MAJOR`. Nên **lấy severity là đã có 4 verdict** — chúng chỉ là tên của
|
|
149
|
+
> bốn ô đếm. Phương án A của tôi *(giữ 2 verdict + lấy severity)* thực ra **đã chứa sẵn 4 mức**,
|
|
150
|
+
> chỉ là không đặt tên cho hai mức giữa.
|
|
151
|
+
> - **Ngưỡng qua cửa đã khai sẵn**: `AGT-005:132` và `AGT-010:130` — *"Code Review verdict
|
|
152
|
+
> ≥ APPROVED_WITH_SUGGESTIONS"*. Ba cửa chặn không phải quyết gì mới.
|
|
153
|
+
>
|
|
154
|
+
> Nguyên nhân tôi sai: ước lượng chi phí từ **số chỗ phải sửa chữ** mà chưa đọc **luật suy ra
|
|
155
|
+
> verdict**. Câu hỏi *"tài liệu để bao nhiêu verdict"* mới lộ ra.
|
|
156
|
+
|
|
157
|
+
**Phần vẫn đúng của khuyến nghị A:** severity của `AGT-006` là thứ **cả hai nguồn đều thiếu** *(đo: 0/10 file
|
|
158
|
+
proposal, 0/13 file framework)* và nó làm finding kiểm chứng được — giữ. Nhưng verdict là **từ
|
|
159
|
+
vựng chung của cặp review**; đổi một nửa là tạo drift ngay trong thứ vừa tách.
|
|
160
|
+
|
|
161
|
+
> Ghi kèm: verdict **chưa phải contract field** — `trace-schema.json` không có nó. Nếu sau này
|
|
162
|
+
> `/qc-report` (Đợt 3) đọc verdict để tính gate thì lúc ấy mới khai vào schema, **và lúc ấy mới
|
|
163
|
+
> là lúc bàn lại có cần 4 mức không**.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
168
|
+
|
|
169
|
+
## A1 · Vấn đề
|
|
170
|
+
|
|
171
|
+
Bộ tiêu chí dùng để soát bài kiểm tra tự động đang viết cho một cách làm đã bỏ — có chỗ tới hơn
|
|
172
|
+
một phần mười số dòng là của công cụ cũ. Nó cũng chỉ có **một** bản dùng chung cho cả web lẫn
|
|
173
|
+
điện thoại, trong khi hai nền soát những thứ khác hẳn nhau. Và có một câu chưa ai trả lời dứt
|
|
174
|
+
khoát: **bài kiểm tra sẽ được đặt ở thư mục nào** — hai tài liệu nguồn nói hai kiểu, chênh nhau
|
|
175
|
+
ở ba chỗ.
|
|
176
|
+
|
|
177
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
178
|
+
|
|
179
|
+
Như viết lại bảng chấm điểm trước khi mở lớp dạy nấu. Bảng cũ chấm theo công thức đã bỏ, nên
|
|
180
|
+
sửa từng dòng thì vẫn là bảng cũ. Viết bảng mới trước, rồi mới dạy — người dạy biết mình sẽ bị
|
|
181
|
+
chấm theo gì, và bảng chấm không bị nắn theo món mà người dạy lỡ nấu ra.
|
|
182
|
+
|
|
183
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
184
|
+
|
|
185
|
+
Năm file phẳng thành ba nhánh `web` · `mobile` · `api`, mỗi nhánh soát đúng thứ nền đó có. Mỗi
|
|
186
|
+
lỗi tìm được mang một mức nặng-nhẹ rõ ràng thay vì chỉ "đạt / phải sửa". Và **có một câu trả
|
|
187
|
+
lời duy nhất** cho câu hỏi bài kiểm tra nằm ở đâu. Số file đèn còn bắt tụt từ **14 xuống 9**.
|
|
188
|
+
|
|
189
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
190
|
+
|
|
191
|
+
- **review script** — soát bài kiểm tra tự động đã sinh ra, trước khi cho chạy thật.
|
|
192
|
+
- **lane / nhánh** — `web` · `mobile` · `api`; mỗi lần soát chỉ nạp một nhánh.
|
|
193
|
+
- **verdict** — kết luận của một lượt soát. Nay có hai giá trị: `APPROVED`, `NEEDS_FIX`.
|
|
194
|
+
- **severity** — mức nặng của một lỗi tìm được: `BLOCKER` · `MAJOR` · `MINOR` · `SUGGESTION`.
|
|
195
|
+
- **Page Object** — nơi gom thao tác của một màn hình vào một chỗ, để nhiều bài kiểm tra dùng lại.
|
|
196
|
+
- **`{TICKET-ID}`** — mã PRD; framework dùng nó để tách artifact của các PRD khác nhau.
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
> ### ✅ Phép thử đọc to
|
|
201
|
+
> **Đã thử với:** tự đọc to · **ngày:** 2026-09-17 · **phải giải thích thêm:** *"hai tài liệu
|
|
202
|
+
> nguồn nói hai kiểu"* — người nghe hỏi *"vậy cái nào đúng?"*. Câu trả lời là **cả hai đều đúng
|
|
203
|
+
> trong bối cảnh của nó**, và đó là §0.2. Đã thêm *"chênh nhau ở ba chỗ"* để thấy đây là chuyện
|
|
204
|
+
> đo được, không phải chuyện quan điểm.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
# PHẦN B — Chi tiết kỹ thuật
|
|
209
|
+
|
|
210
|
+
## B1 · Cách hiển nhiên là gì, và vì sao nó sai
|
|
211
|
+
|
|
212
|
+
**Cách hiển nhiên: port 10 file `script/{web,mobile}/*` của proposal, xong.** Chúng đã
|
|
213
|
+
TypeScript, đã tách lane, đã có `web/functional/api.md`.
|
|
214
|
+
|
|
215
|
+
**Sai ở ba chỗ, cả ba đo được:**
|
|
216
|
+
|
|
217
|
+
**1 · Kéo theo một key không tồn tại vào 8/10 file** *(§0.1)*. `R4` sẽ đỏ — may là có lưới. Nhưng
|
|
218
|
+
người sửa vội sẽ **khai đại `qc_automation_dir` vào `project-context.yaml`** cho đèn xanh, và thế
|
|
219
|
+
là framework có thêm một key **trùng nghĩa với `§layout`** của module: hai nguồn cho một sự thật,
|
|
220
|
+
đúng thứ S1 vừa từ chối khi bỏ `tech_stack.qc_module`.
|
|
221
|
+
|
|
222
|
+
**2 · Chốt bố cục thư mục bằng cách… không để ý mình đang chốt.** Port xong là đã chọn phương án
|
|
223
|
+
B của §1.1 — một quyết định ảnh hưởng tới mọi script sinh ra về sau, đi vào repo qua đường
|
|
224
|
+
*"copy cho nhanh"*.
|
|
225
|
+
|
|
226
|
+
**3 · Bỏ mất thứ cả hai nguồn đều thiếu.** Đo: `Severity`/`BLOCKER` xuất hiện **0/10** file
|
|
227
|
+
proposal và **0/13** file `qa-reviewer` hiện tại. `AGT-006` §3.2–§3.3 có cả severity lẫn verdict
|
|
228
|
+
rules, cộng `R05 — Positive Observations Required` *(mỗi review phải ghi ≥2–3 điểm tốt)* và
|
|
229
|
+
`R07 — Traceability First` *(thiếu header artifact ID ⇒ BLOCKER ngay, không review tiếp)*. Port
|
|
230
|
+
proposal thuần là bỏ cả ba.
|
|
231
|
+
|
|
232
|
+
> **Cách hiển nhiên thứ hai — "sửa 5 file cũ cho nhanh".** `S3′` đã bác: hai file nhiễm **11,2 %**
|
|
233
|
+
> và **9,2 %**; tái bố cục dưới cái tên *"sửa"* dẫn thẳng vào bẫy B1 của
|
|
234
|
+
> [`exec-S`](exec-S-ap-stack-typescript.md) — **đúng từ vựng TypeScript, đúng cấu trúc Python**.
|
|
235
|
+
|
|
236
|
+
## B2 · Cách làm đúng
|
|
237
|
+
|
|
238
|
+
**Ba nguồn, ba vai — không trộn:**
|
|
239
|
+
|
|
240
|
+
```
|
|
241
|
+
AGT-006 → LUẬT SOÁT: R01–R07 · severity · verdict rules · checklist 7 nhóm
|
|
242
|
+
qc-base-new §10 → TIÊU CHÍ CODE theo nền: Automation §9–§11 · Mobile §10–§11 · API §10–§11
|
|
243
|
+
proposal → KHUÔN FILE: Khi nào trigger / Khi KHÔNG trigger / Phase 1 Clarify /
|
|
244
|
+
Phase 2 Review / Checklist chi tiết / Output
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
① xoá qa-reviewer/script/{e2e,exploratory,functional,integration,non-functional}.md (5 file)
|
|
249
|
+
② viết script/web/{functional,integration,e2e,non-functional}.md ← Automation-Standards
|
|
250
|
+
③ viết script/mobile/{functional,integration,e2e,non-functional}.md ← Mobile-Standards
|
|
251
|
+
④ viết script/api/{endpoint,auth,security}.md ← API-Standards + AGT-010
|
|
252
|
+
⑤ viết script/_shared/review-rules.md ← AGT-006 R01–R07 + severity + verdict (§1.3 A)
|
|
253
|
+
⑥ dời exploratory.md → giữ ở gốc script/ (không thuộc nền nào)
|
|
254
|
+
⑦ sửa qc-review-script.tmpl §Skills — trỏ lane theo §2b
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
**Đường dẫn trong mọi tiêu chí lấy từ §1.1 C:**
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
automation/tests/{TICKET-ID}/<feature>-<scenario>.spec.ts spec — chia theo PRD
|
|
261
|
+
automation/pages/<feature>.page.ts page — PHẲNG, dùng lại
|
|
262
|
+
api-automation/tests/{TICKET-ID}/… · api/<resource>.api.ts
|
|
263
|
+
mobile-automation/tests/{TICKET-ID}/… · screens/<feature>.screen.ts
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
**Con số có lý do — `_shared/review-rules.md` tách riêng:** bốn nhánh × 4 file = **16 file** sẽ
|
|
267
|
+
cùng cần R01–R07 + bảng severity + verdict rules. Viết vào từng file là **16 bản của một luật**;
|
|
268
|
+
đó đúng là thứ `review-check-groups.md` *(13,8 KB, dùng chung cho test-case review)* đã tồn tại
|
|
269
|
+
để tránh.
|
|
270
|
+
|
|
271
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
272
|
+
|
|
273
|
+
**Nói dối 🔴.** Đây là trạm **cấp phép**: nó nói *"script này dùng được"*. Một tiêu chí viết cho
|
|
274
|
+
stack cũ vẫn chạy trót lọt và vẫn in ra `APPROVED` — nhưng nó vừa duyệt một thứ nó không kiểm.
|
|
275
|
+
Cụ thể: tiêu chí `non-functional` hiện kiểm `@pytest.mark.parametrize` trên browser; script
|
|
276
|
+
TypeScript dùng `projects` trong `playwright.config.ts`, nên mục đó **không khớp gì cả** và
|
|
277
|
+
reviewer sẽ… bỏ qua, rồi `APPROVED`. Ai phát hiện: không ai — đó là định nghĩa của Nói dối.
|
|
278
|
+
|
|
279
|
+
Và `R07` của `AGT-006` tồn tại đúng vì lý do này: *thiếu header artifact ID ⇒ BLOCKER ngay,
|
|
280
|
+
không review tiếp* — chặn cửa **trước** khi một lượt review vô nghĩa kịp in ra verdict.
|
|
281
|
+
|
|
282
|
+
## B4 · Verify bằng gì
|
|
283
|
+
|
|
284
|
+
| # | Phép thử | Kết quả mong đợi |
|
|
285
|
+
|:-:|---|---|
|
|
286
|
+
| 1 | `ls skills/qc/qa-reviewer/script/*.md` | chỉ còn **`exploratory.md`** ở gốc |
|
|
287
|
+
| 2 | `find skills/qc/qa-reviewer/script -type f \| wc -l` | **16** = 4 web + 4 mobile + 3 api + 1 `_shared` + 1 exploratory + 3 *(api gộp `_shared`?)* → **chốt con số lúc làm, ghi ra** |
|
|
288
|
+
| 3 | `self-check` | R5 đỏ **9** — bớt **đúng 5**, không hơn *(S3 không đụng `qa-runner` hay `commands/`)* |
|
|
289
|
+
| 4 | **`R4`** | **không đỏ** — 0 chỗ còn `{paths.qc_automation_dir}` |
|
|
290
|
+
| 5 | `grep -rn 'qc_automation_dir' skills/` | **0** |
|
|
291
|
+
| 6 | `grep -rlE 'BLOCKER\|MAJOR\|MINOR\|SUGGESTION' skills/qc/qa-reviewer/script/_shared/` | **1** — severity có đúng **một** bản |
|
|
292
|
+
| 7 | `grep -rc 'NEEDS_FIX\|APPROVED' skills/qc/qa-reviewer/script/` vs `test-case/` | **cùng từ vựng 2 verdict** — không có `REVISION_REQUIRED`/`REJECTED` |
|
|
293
|
+
| 8 | `grep -rn 'R0[1-7]' skills/qc/qa-reviewer/script/_shared/review-rules.md` | **7 rule đủ** |
|
|
294
|
+
| 9 | **Phá:** thêm `pytest` vào `script/api/endpoint.md` | **ĐỎ** — lane mới nằm trong tầm đèn |
|
|
295
|
+
| 10 | 4 lệnh kiểm | `build` ✅ · `244/244` ✅ · `lint-trace` ✅ · `self-check` đỏ 9 |
|
|
296
|
+
|
|
297
|
+
> **#4 là phép thử đáng giá nhất của S3.** Nó không kiểm thứ S3 làm — nó kiểm **thứ S3 suýt kéo
|
|
298
|
+
> vào**: 24 file đích mang một key không tồn tại. Và nó là lưới **cho cả S2**, vì S2 port 16 file
|
|
299
|
+
> nữa cùng loại.
|
|
300
|
+
|
|
301
|
+
## B5 · Bài học
|
|
302
|
+
|
|
303
|
+
- **2026-09-17** — Khai với anh rằng 4 verdict *"sinh ra ba quyết định mới cho ba cửa chặn"*. **Sai.** Đọc kỹ `AGT-006` §3.3 thì verdict là **hàm của severity** — đếm BLOCKER/MAJOR là ra, không có chỗ nào để người quyết; và ngưỡng qua cửa **đã khai sẵn** ở `AGT-005:132` + `AGT-010:130` *(`≥ APPROVED_WITH_SUGGESTIONS`)*. Nguyên nhân thật: ước lượng chi phí từ **số chỗ phải sửa chữ** mà không đọc **luật suy ra verdict**. Anh hỏi *"tài liệu để bao nhiêu verdict"* mới lộ. **Bài học: khi báo giá một thay đổi, đọc luật của thứ mình định thay trước khi đếm số dòng.**
|
|
304
|
+
- **2026-09-17** — Framework đã có **hai** bộ nhãn cho một việc: `FAIL`/`WARN` *(để trừ điểm)* và nay thêm `BLOCKER`/`MAJOR`/`MINOR`/`SUGGESTION` *(để suy verdict)*. Suýt để chúng sống song song không liên hệ. Sửa: ghi **phép ánh xạ tường minh** vào `review-file-template.md` — `FAIL ≡ BLOCKER ∪ MAJOR` · `WARN ≡ MINOR ∪ SUGGESTION`. Hai bộ nhãn không nối nhau là hai bộ nhãn sẽ lệch nhau.
|
|
305
|
+
- **2026-09-17** — Chèn một hàng vào bảng Markdown bằng phép thay chuỗi làm **vỡ bảng**: dòng mới rơi xuống dưới một dòng trống nên nó thành đoạn văn, không còn là hàng. `build` vẫn xanh, `self-check` vẫn xanh — không máy nào canh cấu trúc bảng. Chỉ thấy khi đọc lại file. **Phép thay chuỗi trên Markdown có cấu trúc thì phải đọc lại vùng vừa sửa, đừng tin exit code.**
|
|
306
|
+
- **2026-09-17** — `qc-review-script.tmpl` còn hai dòng luật của stack cũ mà đèn **không bắt được** vì chúng không chứa chữ bị cấm: *"Không Allure"* *(nay sai với mobile)* và *"Selector theo thứ tự ưu tiên (data-testid → role)"* *(thứ tự của web, không đúng Appium, và api không có locator)*. Đèn canh **tên công nghệ**, không canh **luật đã lỗi thời**. Đây là giới hạn đã biết của S0, nay có ca thật.
|
|
307
|
+
|
|
308
|
+
## B6 · Copy được / không copy được
|
|
309
|
+
|
|
310
|
+
| | |
|
|
311
|
+
|---|---|
|
|
312
|
+
| ✅ **Copy được** | Viết **tiêu chí chấm trước, bộ sinh sau** — nó lộ ra quyết định còn thiếu trước khi bộ sinh kịp nướng sẵn một câu trả lời (§0.3); khi hai tài liệu nguồn chọi nhau, hỏi **"nguyên tắc của chúng có chọi nhau không"** trước khi chọn bên (§0.2); một luật dùng chung cho n file thì viết **một bản**, đừng chép n lần |
|
|
313
|
+
| ⚠️ **Chỉ đúng ở đây** | Bất đối xứng `tests/` chia theo PRD còn `pages/` phẳng; từ vựng 2 verdict `APPROVED`/`NEEDS_FIX`; tên key `qc_automation_dir` của bộ đề xuất |
|
|
314
|
+
|
|
315
|
+
## B7 · Link
|
|
316
|
+
|
|
317
|
+
- [`exec-S-ap-stack-typescript.md`](exec-S-ap-stack-typescript.md) §8 `S-ORDER` · §8.1 `S3′` *(xoá, viết mới — không vá)*
|
|
318
|
+
- [`exec-S2-…`](exec-S2-qa-runner-thanh-script-designer-runner.md) §0.2 — bộ đo *"công của Đợt 0–2 có được giữ không"*, dùng lại cho S3
|
|
319
|
+
- `upstream/qc-base-new/AGT-006-code_reviewer.md` §3.1 R01–R07 · §3.2 Severity · §3.3 Verdict · §4 Checklist
|
|
320
|
+
- `upstream/qc-base-new/Automation-Standards.md` §9–§11 · `Mobile-…` §10–§11 · `API-…` §10–§11
|
|
321
|
+
- `modules/qc-*/stack-profile.yaml` §`layout` §`naming` — nguồn đường dẫn, **không tra lại chuẩn**
|
|
322
|
+
- `bin/self-check.js:256` — `R4`, lưới an toàn cho `{paths.X}`
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
---
|
|
2
|
+
buoc: Bước S · việc S5
|
|
3
|
+
title: Dọn dấu vết stack cũ ở các chỗ ăn theo — lệnh, schema, provenance, docs
|
|
4
|
+
phu_thuoc: S1 xong (tên file/report đã chốt ở stack-profile)
|
|
5
|
+
trang_thai: ĐÃ TRIỂN KHAI 2026-09-17 · 10/10 phép thử đạt · R5 đỏ 18→14
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# S5 — Bốn chỗ ăn theo, và một thứ đèn không nhìn thấy
|
|
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 theo thứ tự mới** `S-ORDER` *(S0→S1→**S5**→S3→S2→S4)* |
|
|
15
|
+
| **File code** | 4 lệnh · `bin/trace-schema.json` · `bin/qc-base-map.json` · 5 `docs/` |
|
|
16
|
+
| **File test** | không thêm |
|
|
17
|
+
| **Phụ thuộc** | S1 ✅ — mọi thứ S5 cần **đã được `stack-profile.yaml` quyết** *(tên file, reporter)* |
|
|
18
|
+
| **Ai dùng nó** | `/qc-analyze` · `/qc-design-test` · `/qc-report` · `/qc-automation-assess` |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## 0 · Đo lại — 2026-09-17
|
|
23
|
+
|
|
24
|
+
### 0.1 · Trong tầm đèn — 4 file · 11 dòng
|
|
25
|
+
|
|
26
|
+
| File | Dòng | Nội dung |
|
|
27
|
+
|---|:-:|---|
|
|
28
|
+
| `commands/qc-report.tmpl` | 5 | `pytest-html (--html=…)` · `python3 -m playwright show-trace` ×2 · *"report pytest-html + trace"* |
|
|
29
|
+
| `commands/qc-design-test.tmpl` | 3 | *"Python đến sau ở /qc-design-script"* · *"Output feed vào qc-design-script (Python) và qc-review"* · *"Bạn không viết Python"* |
|
|
30
|
+
| `skills/qc/qa-automation-assess/matrix.md` | 2 | ⚠️ **ghi chú TODO do chính S1 để lại** — xem §0.2 |
|
|
31
|
+
| `commands/qc-analyze.tmpl` | 1 | *"viết test case chi tiết hay Python (đó là qc-design-test / qc-design-script)"* |
|
|
32
|
+
|
|
33
|
+
**Mọi dòng đều đã có đáp án sẵn từ S1** — không phải quyết gì thêm:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
pytest-html → Playwright HTML Report (web·api) · Allure v2.x (mobile)
|
|
37
|
+
python3 -m playwright show-trace → npx playwright show-trace
|
|
38
|
+
"Python" → "script" / "TypeScript"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### 0.2 · 🔴 Hai trong mười một dòng đỏ là do **chính tôi** tạo ra ở S1
|
|
42
|
+
|
|
43
|
+
`matrix.md:106-107` — S1 đã thay xong nội dung stack cũ, nhưng để lại một ghi chú TODO **trích
|
|
44
|
+
nguyên tên cũ**:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
> *(Nội dung mô tả stack cũ — Python + pytest-playwright, `test_<feature>.py`,
|
|
48
|
+
> `<feature>_page.py` — thuộc việc S5 của Bước S, chưa sửa ở bước này.)*
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Nội dung thật **đã sạch**; chỉ ghi chú là đỏ. Nên S5 với file này = **xoá hai dòng ghi chú**.
|
|
52
|
+
|
|
53
|
+
> **Bài học ngay tại chỗ:** một ghi chú TODO **trích dẫn** pattern bị cấm làm đèn đếm chính phần
|
|
54
|
+
> bookkeeping của mình thành nợ. Ghi chú đúng cách là nói *"nội dung stack cũ, S5 sửa"* —
|
|
55
|
+
> **không liệt kê lại tên**. Đã làm sai một lần ở S1, ghi vào B5 để lần sau không lặp.
|
|
56
|
+
|
|
57
|
+
### 0.3 · Ngoài tầm đèn nhưng thuộc S5
|
|
58
|
+
|
|
59
|
+
| Nơi | Lượng | Nội dung |
|
|
60
|
+
|---|:-:|---|
|
|
61
|
+
| `bin/trace-schema.json` | **4 dòng** | `:1477` *"ghi đè script Python đã có"* · `:2420` `tests/…/test_<feature>.py` · `:2423` `pages/<feature>_page.py` · `:2497` *"pytest-html self-contained"* |
|
|
62
|
+
| `bin/qc-base-map.json` | **5 entry** `undecided` | 3 × `qa-runner` *(agent + script-writer + test-executor)* · **2 × `qa-gate/styled-report-*.py`** |
|
|
63
|
+
| `docs/` | **5 file · 13 dòng** | `08-qc-automation` 5 · `19-qc-run-test` 4 · `tester-qa` 2 · `04-reference/modules` 1 · `17-qc-design-test` 1 |
|
|
64
|
+
|
|
65
|
+
> `bin/` nằm ngoài `scope` của đèn **có chủ ý**: 6 entry `forbidden_patterns` của S0 mang chính
|
|
66
|
+
> chữ `pytest`/`Python` trong `reason` của chúng. Nếu một ngày ai nới scope sang `bin/`, đèn sẽ
|
|
67
|
+
> **báo động chính định nghĩa của nó**. Ghi ra đây để đừng nới.
|
|
68
|
+
|
|
69
|
+
### 0.4 · 🔴 Thứ đèn KHÔNG nhìn thấy — dây chuyền QC khai sai ở **6 lệnh**
|
|
70
|
+
|
|
71
|
+
Không phải Python, nên `R5` im. Đây là **drift từ Đợt 2** mà chưa ai đánh số lại:
|
|
72
|
+
|
|
73
|
+
| Lệnh | Tự khai | Đúng phải là |
|
|
74
|
+
|---|---|---|
|
|
75
|
+
| `qc-analyze` · `qc-plan` · `qc-design-test` · `qc-review-testcase` | Stage 1 · 2 · 3 · 4 | ✅ đúng |
|
|
76
|
+
| `qc-automation-assess` | **— (không khai)** | **5** |
|
|
77
|
+
| `qc-design-script` | Stage **5** | **6** |
|
|
78
|
+
| `qc-review-script` | **— (không khai)** | **7** |
|
|
79
|
+
| `qc-run-script` | Stage **7** | **8** |
|
|
80
|
+
| `qc-run-manualtest` | Stage **7b** | **8b** |
|
|
81
|
+
| `qc-report` | Stage **6 (cuối)** | **9** |
|
|
82
|
+
|
|
83
|
+
**Chỗ tự mâu thuẫn:** `qc-report` khai *"Stage 6 **(cuối)**"* trong khi hai lệnh khác khai
|
|
84
|
+
**Stage 7 / 7b** — trạm tự xưng cuối cùng đứng **trước** hai trạm sau nó.
|
|
85
|
+
|
|
86
|
+
> ⚠️ **Đính chính phép đo.** Bản trình bày đầu khai *"`qc-run-script` và `qc-run-manualtest`
|
|
87
|
+
> **cùng** khai Stage 7"*. Sai: regex `Stage [0-9]+` khớp phần `7` của chuỗi `Stage 7b`, nên hai
|
|
88
|
+
> giá trị khác nhau bị đếm thành một. Thật ra cặp `7`/`7b` **nhất quán với nhau** — chúng chỉ
|
|
89
|
+
> lệch so với sơ đồ 10 trạm *(đúng phải là `8`/`8b`)*. **Bài học:** một regex `[0-9]+` không có
|
|
90
|
+
> ranh giới token thì `7b` đọc thành `7` — đúng lớp lỗi mà hàm `mentions()` của `self-check.js`
|
|
91
|
+
> đã dựng `(?![A-Za-z0-9_])` để chống, và tôi vừa mắc lại bằng tay.
|
|
92
|
+
|
|
93
|
+
**Và chuỗi dây chuyền in trong 5 lệnh liệt kê 8 trạm**, thiếu đúng hai lệnh Đợt 2 vừa thêm:
|
|
94
|
+
`qc-automation-assess` *(d2-b3)* và `qc-run-manualtest` *(d2-b2)*.
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
đang in: analyze → plan → design-test → review-testcase → design-script
|
|
98
|
+
→ review-script → run-script → report (8 trạm)
|
|
99
|
+
thật sự: … → review-testcase → automation-assess → design-script
|
|
100
|
+
→ review-script → run-script → run-manualtest → report (10 lệnh)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Số đúng **đã có sẵn** ở [`PLAN_v2 §2`](PLAN_v2.md) — chưa bao giờ được chép ngược vào lệnh.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 1 · HAI CÂU CHỜ CHỐT
|
|
108
|
+
|
|
109
|
+
### 1.1 · Đánh số lại dây chuyền — làm trong S5 hay tách ra?
|
|
110
|
+
|
|
111
|
+
| Phương án | |
|
|
112
|
+
|---|---|
|
|
113
|
+
| **A · gộp vào S5** ⭐ | S5 **đã mở** 3 trong 6 file đó *(`qc-analyze` · `qc-design-test` · `qc-report`)*. Sửa thêm 3 file nữa là một lượt. Mở lại cùng file ở hai bước khác nhau là hai lần đọc, hai lần review, hai cơ hội sai lệch |
|
|
114
|
+
| B · tách thành nợ độc lập | Giữ S5 đúng nghĩa *"ăn theo việc đổi stack"*. Nhưng nó là **drift từ Đợt 2**, không phải từ stack — để lại thì nằm đó thêm một đợt nữa |
|
|
115
|
+
|
|
116
|
+
**Khuyến nghị: A**, kèm một điều kiện — **ghi rõ trong commit** rằng đây là drift Đợt 2, không
|
|
117
|
+
phải hệ quả của đổi stack. Lý do gộp không phải *"tiện tay"* mà là: một lệnh khai sai số trạm
|
|
118
|
+
làm người đọc **hiểu sai thứ tự chạy**, và ba trong sáu file đó đang mở sẵn.
|
|
119
|
+
|
|
120
|
+
> **Vì sao không dựng rule canh việc này:** số trạm là **một dữ kiện, không phải một contract
|
|
121
|
+
> field** — chưa có nơi nào máy đọc nó. Dựng rule cho một thứ chưa ai tiêu thụ là đúng lớp lỗi
|
|
122
|
+
> `R2` cảnh báo *(có producer, không consumer)*. Nếu sau này `/qc-metrics` (d4-b3) cần số trạm
|
|
123
|
+
> thì lúc đó mới khai vào schema.
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### 1.2 · Hai entry `styled-report-*.py` trong `qc-base-map` — chốt trạng thái nào?
|
|
128
|
+
|
|
129
|
+
Hai file Python **sinh báo cáo** của kho QC cũ, `state: undecided`, `targets: []`, **chưa bao
|
|
130
|
+
giờ port**, ghi chú: *"script chạy được, không phải prompt — framework QC hiện 100% markdown-first"*.
|
|
131
|
+
|
|
132
|
+
Đây chính là chỗ câu hỏi *"Python làm tốt hơn ở tầng sinh doc"* đáp xuống thành một quyết định
|
|
133
|
+
cụ thể.
|
|
134
|
+
|
|
135
|
+
| Phương án | |
|
|
136
|
+
|---|---|
|
|
137
|
+
| **A · `skipped`, lý do từ chuẩn mới** ⭐ | `Automation-Standards §1` + `OQ-01` *(đóng 2026-06-02)* chốt reporter là **Playwright HTML Report**; `Mobile §1` chốt **Allure v2.x**. Báo cáo do **công cụ sinh**, không do script tự viết — nên hai file này không còn đích để port |
|
|
138
|
+
| B · giữ `undecided` | `R16` không đỏ, nhưng entry `undecided` tồn tại để **chờ quyết**; giữ mãi là biến nó thành rác |
|
|
139
|
+
| C · port thành công cụ Node | việc mới, ngoài Bước S, và mâu thuẫn với chuẩn vừa chốt |
|
|
140
|
+
|
|
141
|
+
**Khuyến nghị: A.** Cùng lượt chốt nốt **3 entry `qa-runner`** → `replaced`, `targets` trỏ
|
|
142
|
+
`skills/qc/qa-script-designer/` và `qa-script-runner/` *(sau S2)*. Tức **để nguyên 3 entry đó ở
|
|
143
|
+
S5, xử ở S2** — S5 chỉ đóng 2 entry `styled-report`.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
148
|
+
|
|
149
|
+
## A1 · Vấn đề
|
|
150
|
+
|
|
151
|
+
Đội đã đổi bộ công cụ, nhưng vài chỗ trong bộ khung vẫn kể lại chuyện cũ: hướng dẫn bảo mở báo
|
|
152
|
+
cáo bằng một câu lệnh không còn chạy, và bảng khai *"lệnh này ghi ra file gì"* vẫn ghi đuôi file
|
|
153
|
+
của bộ cũ. Những dòng này không làm hỏng gì ngay — chúng chỉ làm người đọc tin vào một thứ không
|
|
154
|
+
còn đúng, rồi mất buổi sáng để phát hiện. Và có một chỗ lệch nữa nặng hơn: **sáu lệnh tự khai
|
|
155
|
+
mình là trạm số mấy trong dây chuyền, và bốn trong số đó khai sai** — có hai lệnh cùng nhận một
|
|
156
|
+
số, còn lệnh tự xưng *"trạm cuối"* thì đứng trước hai trạm khác.
|
|
157
|
+
|
|
158
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
159
|
+
|
|
160
|
+
Như sửa bảng chỉ dẫn treo dọc một dây chuyền sau khi chèn thêm hai công đoạn. Máy móc chạy đúng
|
|
161
|
+
rồi — chỉ có bảng là còn đánh số theo sơ đồ cũ, và người mới vào đọc bảng chứ không đọc máy.
|
|
162
|
+
|
|
163
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
164
|
+
|
|
165
|
+
Số file đèn còn bắt tụt từ **18 xuống 14**. Mở bất kỳ lệnh QC nào cũng thấy nó khai đúng mình
|
|
166
|
+
là trạm mấy trong mười, và không có hai lệnh nào trùng số. Hướng dẫn mở báo cáo ghi đúng câu
|
|
167
|
+
lệnh chạy được.
|
|
168
|
+
|
|
169
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
170
|
+
|
|
171
|
+
- **trạm / stage** — một bước trong dây chuyền QC; hiện có 10 lệnh, đánh số 1 → 9 *(có một trạm `8b`)*.
|
|
172
|
+
- **`trace-schema.json`** — bản khai máy đọc: lệnh nào ghi ra file gì, ở đâu. Sai ở đây thì máy canh sai.
|
|
173
|
+
- **provenance / `qc-base-map.json`** — sổ ghi mỗi file của kho QC gốc đã đi về đâu trong bộ khung.
|
|
174
|
+
- **`undecided`** — trạng thái *"chưa quyết lấy hay bỏ"*; để mãi thì nó thành rác chứ không thành quyết định.
|
|
175
|
+
- **reporter** — công cụ tự sinh báo cáo sau khi chạy test. Nay là Playwright HTML Report *(web · dịch vụ)* và Allure *(điện thoại)*.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
> ### ✅ Phép thử đọc to
|
|
180
|
+
> **Đã thử với:** tự đọc to · **ngày:** 2026-09-17 · **phải giải thích thêm:** *"trạm cuối đứng
|
|
181
|
+
> trước hai trạm khác"* nghe như nói đùa — đã thêm *"có hai lệnh cùng nhận một số"* ngay trước
|
|
182
|
+
> để người nghe hiểu đây là lỗi đánh số, không phải nghịch lý.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
# PHẦN B — Chi tiết kỹ thuật
|
|
187
|
+
|
|
188
|
+
## B1 · Cách hiển nhiên là gì, và vì sao nó sai
|
|
189
|
+
|
|
190
|
+
**Cách hiển nhiên: `sed` thay `pytest-html` → `Playwright HTML Report`, `python3 -m playwright`
|
|
191
|
+
→ `npx playwright`, `Python` → `TypeScript`. Mười một dòng, ba lệnh `sed`.**
|
|
192
|
+
|
|
193
|
+
**Sai ở ba chỗ:**
|
|
194
|
+
|
|
195
|
+
**1 · `Python` → `TypeScript` sai nghĩa ở 3/4 chỗ.** Đọc kỹ từng dòng:
|
|
196
|
+
|
|
197
|
+
| Dòng | Thay máy móc | Đúng phải là |
|
|
198
|
+
|---|---|---|
|
|
199
|
+
| `qc-analyze:113` *"viết test case chi tiết hay Python"* | *"…hay TypeScript"* | *"…hay **script**"* — câu này nói về **loại việc**, không về ngôn ngữ |
|
|
200
|
+
| `qc-design-test:152` *"Bạn **không** viết Python"* | *"không viết TypeScript"* | *"không viết **script**"* — ranh giới vai trò, không phải ranh giới ngôn ngữ |
|
|
201
|
+
| `qc-design-test:9` *"Python đến sau ở /qc-design-script"* | *"TypeScript đến sau"* | *"**script** đến sau"* |
|
|
202
|
+
|
|
203
|
+
Ba dòng này dùng tên ngôn ngữ để **trỏ một giai đoạn**. Thay tên ngôn ngữ giữ nguyên lỗi: sáu
|
|
204
|
+
tháng nữa đổi stack lần nữa thì lại phải sửa đúng ba dòng đó.
|
|
205
|
+
|
|
206
|
+
**2 · `pytest-html` → một tên không đủ.** Reporter giờ **khác nhau theo nền**: Playwright HTML
|
|
207
|
+
Report *(web · api)* và **Allure v2.x** *(mobile)* — và mobile **bắt buộc**, ngược luật
|
|
208
|
+
`No Allure` cũ. Một phép thay chuỗi cho ra một tên duy nhất, tức khai sai cho nền mobile.
|
|
209
|
+
|
|
210
|
+
**3 · `sed` không thấy `qc-design-test:152` còn trỏ `/qc-review`** — lệnh **đã gỡ ở Đợt 2 `E3`**.
|
|
211
|
+
Nó ngồi cùng dòng với chữ `Python`, nên chỉ ai **đọc** dòng đó mới thấy. Đèn bắt được file, không
|
|
212
|
+
bắt được bug thứ hai đi kèm.
|
|
213
|
+
|
|
214
|
+
> **Và cả ba đều không đụng tới §0.4** — dây chuyền khai sai ở 6 lệnh không chứa chữ nào bị cấm.
|
|
215
|
+
> `sed` xong, đèn xanh cho 4 file, **lỗi nặng hơn vẫn nguyên**.
|
|
216
|
+
|
|
217
|
+
## B2 · Cách làm đúng
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
① 4 file trong tầm đèn — sửa theo NGHĨA, không theo chuỗi
|
|
221
|
+
qc-analyze:113 Python → script
|
|
222
|
+
qc-design-test:9,151,152 Python → script · GỠ "và qc-review" (E3)
|
|
223
|
+
qc-report:14,27,29,33,67 pytest-html → reporter THEO NỀN (§2b)
|
|
224
|
+
python3 -m playwright → npx playwright
|
|
225
|
+
matrix.md:106-107 XOÁ ghi chú TODO của S1 (§0.2)
|
|
226
|
+
|
|
227
|
+
② bin/trace-schema.json — 4 dòng
|
|
228
|
+
:2420 test_<feature>.py → <feature>-<scenario>.spec.ts (naming của S1)
|
|
229
|
+
:2423 <feature>_page.py → <feature>.page.ts
|
|
230
|
+
:1477 "script Python" → "script"
|
|
231
|
+
:2497 "pytest-html" → "Playwright HTML Report / Allure theo nền"
|
|
232
|
+
|
|
233
|
+
③ bin/qc-base-map.json — 2 entry styled-report-*.py → skipped, lý do từ §1.2
|
|
234
|
+
(3 entry qa-runner để S2 xử — chúng cần targets trỏ thư mục S2 mới tạo)
|
|
235
|
+
|
|
236
|
+
④ 5 file docs/ — 13 dòng, cùng luật ①
|
|
237
|
+
|
|
238
|
+
⑤ §0.4 — đánh số lại 6 lệnh + chuỗi dây chuyền 10 trạm (nếu chốt §1.1 A)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
**Nguồn duy nhất cho ①②④:** `modules/qc-*/stack-profile.yaml` §`naming` và §`reporting` — đã
|
|
242
|
+
viết ở S1. **Không tra lại chuẩn** ở bước này; nếu hai bên lệch thì `stack-profile` đúng, vì nó
|
|
243
|
+
là thứ lệnh thật sự đọc.
|
|
244
|
+
|
|
245
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
246
|
+
|
|
247
|
+
**Ồn 🟠 cho ①②④ — Im lặng 🔴 cho ⑤.**
|
|
248
|
+
|
|
249
|
+
Mười một dòng kia sai thì có người đọc, gõ theo, thấy lệnh báo `command not found`, mất mười
|
|
250
|
+
phút rồi tự hiểu. Khó chịu, không nguy hiểm.
|
|
251
|
+
|
|
252
|
+
`⑤` khác hẳn: số trạm sai **không báo gì cả**. Một người mới đọc `/qc-report` thấy *"Stage 6
|
|
253
|
+
(cuối)"* sẽ tin dây chuyền có 6 trạm và **bỏ qua `/qc-automation-assess`** — đúng trạm quyết
|
|
254
|
+
định TC nào máy chạy, TC nào làm tay. Hệ quả là TC `Automatable: N` không ai chạy, và
|
|
255
|
+
`/qc-report` chấm PRD FAIL vĩnh viễn. Đó là lỗ mà `G1`/`d2-b3` sinh ra để bịt, quay lại qua
|
|
256
|
+
đường **tài liệu**.
|
|
257
|
+
|
|
258
|
+
## B4 · Verify bằng gì
|
|
259
|
+
|
|
260
|
+
| # | Phép thử | Kết quả mong đợi |
|
|
261
|
+
|:-:|---|---|
|
|
262
|
+
| 1 | `self-check` | R5 đỏ **14 file** — bớt **đúng 4**, không hơn *(S5 không đụng `qa-runner` hay `qa-reviewer/script`)* |
|
|
263
|
+
| 2 | `grep -rn 'pytest\|python' commands/qc-*.tmpl` | **0** |
|
|
264
|
+
| 3 | `grep -rn 'qc-review\b' commands/*.tmpl \| grep -v 'qc-review-'` | chỉ còn **1** — dòng kể lịch sử ở `qc-run-script.tmpl:39` |
|
|
265
|
+
| 4 | `grep -rn 'Python\|TypeScript' commands/qc-analyze.tmpl commands/qc-design-test.tmpl` | **0** — đã thay bằng *"script"*, không đổi tên ngôn ngữ này lấy tên ngôn ngữ khác |
|
|
266
|
+
| 5 | `grep -c 'Allure' commands/qc-report.tmpl` | **≥1** — reporter khai theo nền, không một tên |
|
|
267
|
+
| 6 | **Đếm Stage:** mỗi lệnh khai đúng 1 số, **không trùng**, đủ 1–9 + 8b | 10/10 lệnh, `qc-report` = **9**, không còn *"(cuối)"* ở số 6 |
|
|
268
|
+
| 7 | Chuỗi dây chuyền in trong lệnh | **10 trạm**, có `automation-assess` và `run-manualtest` |
|
|
269
|
+
| 8 | `grep -rn 'test_<feature>.py\|_page.py' bin/trace-schema.json` | **0** |
|
|
270
|
+
| 9 | `node bin/self-check.js` → `R16` | không đỏ — `qc-base-map` vẫn hợp lệ sau khi đổi 2 entry |
|
|
271
|
+
| 10 | 4 lệnh kiểm | `build` ✅ · `244/244` ✅ · `lint-trace` ✅ · `self-check` đỏ 14 |
|
|
272
|
+
|
|
273
|
+
## B5 · Bài học
|
|
274
|
+
|
|
275
|
+
- **2026-09-17** *(ghi trước khi làm, vì lỗi đã xảy ra ở S1)* — Ghi chú TODO ở `matrix.md:106-107` **trích nguyên** `Python + pytest-playwright`, `test_<feature>.py`, `<feature>_page.py`. Nội dung thật đã sạch, nhưng đèn đếm **chính phần bookkeeping** thành nợ: 2/11 dòng đỏ của S5 là do S1 tự tạo. Nguyên nhân thật: viết ghi chú theo phản xạ *"nêu rõ cái gì chưa sửa"* mà quên rằng "nêu rõ" ở đây nghĩa là **viết lại đúng chuỗi bị cấm**. Sửa: ghi chú TODO trong vùng có guard **mô tả**, không **trích dẫn**.
|
|
276
|
+
- **2026-09-17** — Đổi con trỏ skill của `/qc-report` sang `qa-script-runner/` — thư mục **S2 mới tạo, chưa tồn tại**. `R16` đỏ ngay: *"trỏ vào `{paths.qc_skills_dir}/qa-script-runner/` — KHÔNG TỒN TẠI"*. Nguyên nhân thật: **lấn sang việc của S2**; S5 là "đổi chữ theo stack", còn đấu dây skill là của bước tạo ra thư mục. Sửa: trả lại `qa-runner/report/` kèm ghi chú S2 đổi. **Rule của chính framework bắt được chỗ tôi vượt phạm vi** — đó là điều đáng giữ hơn cả việc sửa xong.
|
|
277
|
+
- **2026-09-17** — Khai *"`qc-run-script` và `qc-run-manualtest` cùng Stage 7"*. Sai: regex `Stage [0-9]+` nuốt `7b` thành `7`. Cặp `7`/`7b` vốn nhất quán; chúng chỉ lệch so với sơ đồ 10 trạm. Nguyên nhân thật: dùng regex **không có ranh giới token** để đếm — đúng lớp lỗi mà `self-check.js` `mentions()` đã chống bằng `(?![A-Za-z0-9_])`. Sửa: mọi phép đếm bằng regex trên **mã định danh** phải có ranh giới token, kể cả khi chỉ chạy một lần để lấy số.
|
|
278
|
+
- **2026-09-17** — Phép thay chuỗi cho `qc-review-testcase.tmpl` **trượt im lặng**: chuỗi dây chuyền của nó **xuống dòng giữa chừng** và liệt kê 6 trạm chứ không 8, nên không khớp mẫu. Chỉ phát hiện vì phép thử #7 đếm `5` file mà ra `4`. **Phép thử đếm cứu được một lần trượt mà `sed` báo thành công.**
|
|
279
|
+
|
|
280
|
+
## B6 · Copy được / không copy được
|
|
281
|
+
|
|
282
|
+
| | |
|
|
283
|
+
|---|---|
|
|
284
|
+
| ✅ **Copy được** | Sửa theo **nghĩa** chứ không theo chuỗi — một cái tên công nghệ dùng để **trỏ một giai đoạn** thì phải thay bằng tên giai đoạn, nếu không lần đổi stack sau lại sửa đúng chỗ đó; ghi chú TODO trong vùng có guard thì **mô tả, đừng trích dẫn**; nhân lúc đã mở file thì dọn nốt drift cùng chỗ, **nhưng nói rõ trong commit đó là drift khác nguồn** |
|
|
285
|
+
| ⚠️ **Chỉ đúng ở đây** | Sơ đồ 10 trạm và cách đánh số `8b`; việc `bin/` cố ý nằm ngoài scope đèn; hai file `styled-report-*.py` của kho QC cũ |
|
|
286
|
+
|
|
287
|
+
## B7 · Link
|
|
288
|
+
|
|
289
|
+
- [`exec-S-ap-stack-typescript.md`](exec-S-ap-stack-typescript.md) §8 *(`S-ORDER`)* · [`exec-S1-…`](exec-S1-hai-module-thay-qc-playwright.md) *(nguồn `naming` + `reporting`)*
|
|
290
|
+
- [`PLAN_v2.md`](PLAN_v2.md) §2 *(sơ đồ 10 trạm — số đúng đã có sẵn ở đây)* · §7 `E3` *(gỡ `/qc-review`)*
|
|
291
|
+
- `modules/qc-playwright-ts/stack-profile.yaml` §`naming` §`reporting` · `modules/qc-wdio-appium/…` §`reporting`
|
|
292
|
+
- `upstream/qc-base-new/Automation-Standards.md` §1 + OQ-01 · `Mobile-Automation-Standards.md` §1
|