@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,400 @@
|
|
|
1
|
+
---
|
|
2
|
+
buoc: Bước S · việc S0
|
|
3
|
+
title: Đèn cơ học cấm stack QC cũ quay lại
|
|
4
|
+
phu_thuoc: không (đây là việc đầu tiên của Bước S)
|
|
5
|
+
trang_thai: ĐÃ TRIỂN KHAI 2026-09-17 · phạm vi thu về TẦNG SCRIPT · self-check ĐỎ 19 file (đúng ý đồ G3)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
> ⚠️ **Đọc §1.2b trước.** Bản đầu của file này lấy phạm vi `skills/qc/**` (đỏ **27 file**).
|
|
9
|
+
> Quyết định **S-SCOPE** ngày 2026-09-17 thu lại còn **tầng script** (đỏ **19 file**) — 5 tầng
|
|
10
|
+
> tài liệu *(phân tích · doc-gap · test plan · thiết kế TC · review TC)* **không đổi stack**.
|
|
11
|
+
> Mọi con số "27" dưới đây được giữ nguyên như bản đo gốc và có ghi chú kèm.
|
|
12
|
+
|
|
13
|
+
# S0 — Dựng đèn trước, dọn sau
|
|
14
|
+
|
|
15
|
+
← [`exec-S-ap-stack-typescript.md`](exec-S-ap-stack-typescript.md) · [`PLAN_v2.md`](PLAN_v2.md) · [`01-checklist.md`](01-checklist.md)
|
|
16
|
+
|
|
17
|
+
| | |
|
|
18
|
+
|---|---|
|
|
19
|
+
| **Lớp** | Bước S · việc **1/6** — chạy **trước** S1–S5 |
|
|
20
|
+
| **File code** | `bin/trace-schema.json` *(`forbidden_patterns`)* · `bin/self-check.js` *(`allSourceFiles`, R5)* |
|
|
21
|
+
| **File test** | `test/run.js` — *(xem §1.1: có thể không đụng)* |
|
|
22
|
+
| **Ngày xong** | — |
|
|
23
|
+
| **Phụ thuộc** | không — S0 cố ý chạy khi repo **còn bẩn** |
|
|
24
|
+
| **Ai dùng nó** | `node bin/self-check.js` — 1 trong 4 lệnh kiểm, chạy sau mỗi bước |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 0 · Đo được — 2026-09-17
|
|
29
|
+
|
|
30
|
+
### 0.1 · Đèn sẽ đỏ như thế nào
|
|
31
|
+
|
|
32
|
+
Phạm vi quét **67 file** *(`skills/qc/**` · `commands/qc-*.tmpl` · `modules/qc-*/**`)* →
|
|
33
|
+
**27 file đỏ · 146 dòng**:
|
|
34
|
+
|
|
35
|
+
| Nhóm | File đỏ | Số phận |
|
|
36
|
+
|---|:-:|---|
|
|
37
|
+
| `skills/qc/` **còn lại** | **14** | phải sửa tay |
|
|
38
|
+
| `skills/qc/qa-runner/` | 7 *(trong 8)* | **xoá** ở S2 — tự hết đỏ |
|
|
39
|
+
| `commands/qc-*.tmpl` | 5 | phải sửa tay |
|
|
40
|
+
| `modules/qc-*/` | 1 | **thay** ở S1 |
|
|
41
|
+
|
|
42
|
+
Năm file nặng nhất: `modules/qc-playwright/stack-profile.yaml` **19 dòng** ·
|
|
43
|
+
`qa-reviewer/script/non-functional.md` **15** · `qa-runner/functional/gui-screen.md` **12** ·
|
|
44
|
+
`qa-reviewer/script/functional.md` **10** · `qa-runner/functional/gui-feature.md` **10**.
|
|
45
|
+
|
|
46
|
+
### 0.2 · 🔴 Đèn tìm ra 10 file mà danh sách của Bước S BỎ SÓT
|
|
47
|
+
|
|
48
|
+
[`exec-S §B2`](exec-S-ap-stack-typescript.md) liệt kê: `qa-runner` · `qa-reviewer/script/*` ·
|
|
49
|
+
`matrix.md` · 3 lệnh script · `qc-report.tmpl` · module · `trace-schema` · `qc-base-map` · docs.
|
|
50
|
+
Đèn đỏ ở **10 file nữa**, không có trong bất kỳ dòng nào của kế hoạch:
|
|
51
|
+
|
|
52
|
+
| File chưa ai kể tên | Dòng | Nó đang dạy gì theo stack cũ |
|
|
53
|
+
|---|:-:|---|
|
|
54
|
+
| `skills/qc/qa-designer/shared/tc-metadata-format.md` | 3 | `@pytest.mark.flaky(...)` trong Tags · `conftest.py` fixture · section `#### Python Test Mapping` |
|
|
55
|
+
| `skills/qc/qa-reviewer/shared/review-check-groups.md` | 4 | *"marker pytest khớp Tags"* · `@pytest.mark.flaky(reruns=2, reruns_delay=3)` |
|
|
56
|
+
| `skills/qc/qa-planner/test-plan.md` | 3 | Exit criteria ghi *"báo cáo pytest-html"* |
|
|
57
|
+
| `commands/qc-design-test.tmpl` | 3 | *"Output feed vào qc-design-script (Python)"* |
|
|
58
|
+
| `skills/qc/qa-designer/shared/precision-rules.md` | 2 | teardown thuộc `conftest.py` scope=function |
|
|
59
|
+
| `skills/qc/qa-designer/api/auth-sequence.md` | 1 | *"Token fixture (Python)"* |
|
|
60
|
+
| `skills/qc/qa-reviewer/shared/review-file-template.md` | 1 | `- Mobile: @pytest.mark.flaky(...)` |
|
|
61
|
+
| `skills/qc/qa-reviewer/test-case/functional.md` | 1 | *"Review Python script → dùng qa-reviewer"* |
|
|
62
|
+
| `skills/qc/qa-reviewer/test-case/non-functional.md` | 1 | `@pytest.mark.performance\|security\|a11y\|i18n` |
|
|
63
|
+
| `commands/qc-analyze.tmpl` | 1 | *"…hay Python (đó là qc-design-test / qc-design-script)"* |
|
|
64
|
+
|
|
65
|
+
**Đây là toàn bộ lý do S0 đi trước.** Tám trong mười file nằm ở tầng **thiết kế test case** —
|
|
66
|
+
`qa-designer`, `qa-reviewer/test-case` — chỗ mà không ai nghĩ *"đổi stack script"* sẽ chạm tới.
|
|
67
|
+
Đi theo danh sách tay của Bước S thì mười file này **ở lại**, và `.Test.md` sinh ra sau đó vẫn
|
|
68
|
+
mang `@pytest.mark.flaky` trong Tags — tức stack cũ sống tiếp trong **đầu vào** của stack mới.
|
|
69
|
+
|
|
70
|
+
### 0.3 · Hai phát hiện làm đổi nội dung Bước S *(không chỉ đổi đèn)*
|
|
71
|
+
|
|
72
|
+
| # | Phát hiện | Nguồn |
|
|
73
|
+
|:-:|---|---|
|
|
74
|
+
| **P1** | Framework hiện ghi **"No Allure"** như một luật cứng *(`stack-profile.yaml` · 12 file nhắc `Allure`)*. Chuẩn MỚI **ngược lại**: mobile **bắt buộc** Allure Report v2.x | `Mobile-Automation-Standards.md:48` · `API-Testing-Standards.md:49` · `Automation-Standards.md:515` *(OQ-01: web dùng Playwright HTML, Allure defer Phase 2)* |
|
|
75
|
+
| **P2** | `BasePage` **sống sót** sang stack mới — không phải dấu vết cần xoá | `Automation-Standards.md:221` `export class BasePage` · `:108` `base.page.ts` |
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 1 · BA CÂU CHỜ CHỐT
|
|
80
|
+
|
|
81
|
+
### 1.1 · Đèn sống ở đâu — R5 hay một test mới?
|
|
82
|
+
|
|
83
|
+
**Bằng chứng: cơ chế này ĐÃ TỒN TẠI.** `bin/self-check.js:279` — **R5 · *"pattern bị cấm quay
|
|
84
|
+
lại"***, đọc `forbidden_patterns` từ `bin/trace-schema.json`. Hiện có **11 entry**, mỗi entry
|
|
85
|
+
gồm `pattern` · `gap` · `reason` · `except`. Và nó còn làm một việc mà một test viết vội sẽ
|
|
86
|
+
không làm: **cảnh báo khi một `except` trở nên vô nghĩa** *(`self-check.js:293`)* — tức danh
|
|
87
|
+
sách miễn trừ không mục ruỗng theo thời gian.
|
|
88
|
+
|
|
89
|
+
**Nhưng R5 có hai giới hạn thật, đã đo:**
|
|
90
|
+
|
|
91
|
+
| Giới hạn | Đo được | Hệ quả |
|
|
92
|
+
|---|---|---|
|
|
93
|
+
| `allSourceFiles()` **không quét `skills/`** | chỉ có `commands/*.tmpl` · `steps/` · `templates/` · `rules/` · `modules/` | **21 trong 27** file đỏ nằm ngoài tầm R5 |
|
|
94
|
+
| `s.text.includes(fp.pattern)` — so chuỗi thô, **không có phạm vi** | `pytest` trên toàn scan set → dính `dev-gen-test` · `dev-run-test` · `dev-smoke-test` · `sync.tmpl` | 4 dương tính giả *(xem §1.3)* |
|
|
95
|
+
|
|
96
|
+
| Phương án | Việc phải làm | Cái giá |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| **A · R5 + mở scan set + thêm trường `scope`** ⭐ | `allSourceFiles()` thêm `skills/**/*.md`; `forbidden_patterns[].scope` = mảng glob, **thiếu `scope` = toàn scan set** *(11 entry cũ không đổi hành vi)*; thêm 6 entry | đụng vào một rule đang xanh — nhưng **đã đo: mở scan set sang 64 file `skills/` làm 11 pattern cũ hit thêm 0 lần** |
|
|
99
|
+
| **B · test riêng trong `test/run.js`** *(bản exec-S viết)* | 1 test mới, regex tự do, allowlist riêng | **cơ chế thứ hai cho cùng một lớp việc** — hai chỗ khai pattern bị cấm, và cái mới không có luật *"`except` phải kèm lý do"* lẫn cảnh báo except chết |
|
|
100
|
+
| **C · cả hai** | R5 cho pattern thô, test cho phép đếm = 0 | thừa; hai đèn cùng đỏ một lỗi làm người ta sửa một chỗ rồi tưởng xong |
|
|
101
|
+
|
|
102
|
+
**Khuyến nghị: A.** Lý do không phải *"đỡ code hơn"* — mà là: danh sách pattern bị cấm của dự
|
|
103
|
+
án này **phải có đúng một chỗ để đọc**. Có hai chỗ thì lần sau người ta thêm vào chỗ sai và đèn
|
|
104
|
+
không bao giờ sáng. *(Nếu chọn A thì `test/run.js` **không đổi**, vẫn 244 test — khác con số
|
|
105
|
+
"244 → 245" ghi ở [`exec-S §8`](exec-S-ap-stack-typescript.md), tôi sẽ sửa lại bảng đó.)*
|
|
106
|
+
|
|
107
|
+
---
|
|
108
|
+
|
|
109
|
+
### 1.2 · Phạm vi có gồm `docs/` không?
|
|
110
|
+
|
|
111
|
+
**Đo được:** ngoài 27 file trong phạm vi, `docs/` *(trừ `docs/plans/`)* còn **7 file · 14 dòng**
|
|
112
|
+
dính. Trong đó **2 file là lane DEV** — `docs/explain/13-dev-run-test.md` ·
|
|
113
|
+
`14-dev-smoke-test.md` — **hợp lệ vĩnh viễn** *(xem §1.3)*. Còn lại **5 file QC thật**:
|
|
114
|
+
`02-concepts/pipeline-steps/08-qc-automation.md` · `03-guides/tester-qa.md` ·
|
|
115
|
+
`04-reference/modules.md` · `explain/17-qc-design-test.md` · `explain/19-qc-run-test.md`.
|
|
116
|
+
|
|
117
|
+
| Phương án | Cái giá |
|
|
118
|
+
|---|---|
|
|
119
|
+
| **Không gồm `docs/`** ⭐ | S5 sửa 5 file bằng tay; sau này docs **drift lại được trong im lặng**. Nhưng `docs/` chưa bao giờ nằm trong scan set của self-check — kéo nó vào là mở một mặt trận mới giữa Bước S |
|
|
120
|
+
| Gồm `docs/**` trừ `docs/plans/` | chặt hơn; phải thêm `except` cho 2 file DEV, và mọi đoạn **kể lịch sử** *("stack cũ là Python")* sau này đều phải xin `except` |
|
|
121
|
+
|
|
122
|
+
**Khuyến nghị: không gồm `docs/` ở S0.** Ghi thành **một dòng nợ có chủ**, không phải một chỗ
|
|
123
|
+
quên. Lý do: `docs/` là nơi duy nhất **cần** nói *"trước đây là Python"* — đèn ở đó sẽ cãi nhau
|
|
124
|
+
với chính nhu cầu của tài liệu, và cãi nhau thì người ta cấp `except` bừa. `docs/plans/` thì
|
|
125
|
+
chắc chắn phải nằm ngoài: cả file anh đang đọc vi phạm pattern **hàng chục lần**.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
### 1.2b · 🔴 S-SCOPE — phạm vi thu về TẦNG SCRIPT *(chốt 2026-09-17, SAU khi S0 đã chạy)*
|
|
130
|
+
|
|
131
|
+
**Quyết định:** chỉ thay TypeScript cho Python **ở các trạm phía sau (tầng script)**, theo
|
|
132
|
+
`qc-base-new`. Năm tầng tài liệu **không đổi stack**: phân tích yêu cầu · quét doc-gap · lập test
|
|
133
|
+
plan · thiết kế test case · review test case.
|
|
134
|
+
|
|
135
|
+
**Đo trước khi chốt** *(đây là số làm quyết định này có căn cứ)*:
|
|
136
|
+
|
|
137
|
+
| Đo | Kết quả |
|
|
138
|
+
|---|---|
|
|
139
|
+
| 5 tầng tài liệu | **41 file · 0 file chạy được** — 100 % `.md`, là hướng dẫn cho agent |
|
|
140
|
+
| Lệnh QC gọi script của framework | **0** |
|
|
141
|
+
| `upstream/qc-base-new/` (7 file · 98 KB) nhắc `python\|pytest\|pandas\|jupyter\|.py` | **0 lần** |
|
|
142
|
+
| Python **chạy được** duy nhất từng có ở lane QC | 2 file kho CŨ: `qa-gate/styled-report-*.py`, state `undecided`, `targets: []`, **chưa bao giờ port** |
|
|
143
|
+
|
|
144
|
+
Nên *"Python vs TypeScript"* **không phải một trục có thật** ở 5 tầng đó: không có chương trình
|
|
145
|
+
nào ở đấy, bằng ngôn ngữ nào cũng vậy.
|
|
146
|
+
|
|
147
|
+
**Phạm vi đèn đổi theo:**
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
skills/qc/qa-runner/** skills/qc/qa-reviewer/script/**
|
|
151
|
+
skills/qc/qa-automation-assess/** commands/qc-*.tmpl
|
|
152
|
+
modules/qc-*/**
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**Kết quả đo lại: đỏ 27 → 19 file.** Tám file rời khỏi tầm — và 16 dòng của chúng chia **bốn
|
|
156
|
+
nhóm không cùng loại**:
|
|
157
|
+
|
|
158
|
+
| Nhóm | Dòng | Là gì | Xử lý |
|
|
159
|
+
|---|:-:|---|---|
|
|
160
|
+
| **A** khai stack trong Test Plan | 3 | `qa-planner/test-plan.md` — *"báo cáo pytest-html + Playwright Trace"* | **nợ** — Test Plan là tài liệu chị QC **ký**; ký một kế hoạch mô tả công cụ không còn dùng là sai ở mức hồ sơ |
|
|
161
|
+
| **B** marker/fixture pytest làm **quy ước viết TC** | 10 | `@pytest.mark.flaky` trong Tags · `conftest.py` trong Preconditions | **nợ, chủ là chị QC** — xem ⚠️ dưới |
|
|
162
|
+
| **C** luật **CẤM** khuôn cũ | 2 | *"Không thêm `#### Python Test Mapping`"* · *"còn `#### Python Test Mapping` → `[OLD_TEMPLATE]`"* | **GIỮ NGUYÊN — đây là guard, không phải Python** |
|
|
163
|
+
| **D** chỉ đường | 1 | *"Review Python script → dùng qa-reviewer"* | nợ, 1 dòng |
|
|
164
|
+
|
|
165
|
+
> 🔴 **Nhóm C là chỗ bản S0 đầu tiên sai.** Hai dòng đó **cấm** Python, và đèn đang bắt chúng.
|
|
166
|
+
> Ai dọn theo danh sách đỏ một cách máy móc sẽ **xoá mất một guard đang làm việc**. Thu phạm vi
|
|
167
|
+
> đã giải quyết: chúng nằm ngoài tầm, **không cần `except`** — và như vậy tốt hơn `except`, vì
|
|
168
|
+
> `except` là *"ngoại lệ trong vùng ta quản"*, còn đây là *"không thuộc vùng ta quản"*.
|
|
169
|
+
|
|
170
|
+
> ⚠️ **Nhóm B không phải chuyện dịch cú pháp — nó đảo ngược ý nghĩa.**
|
|
171
|
+
> `@pytest.mark.flaky(reruns=2, reruns_delay=3)` = **tự chạy lại 2 lần, test VẪN trong suite
|
|
172
|
+
> chính**. `@flaky` của chuẩn mới *(`Automation-Standards.md` §11)* = **QUARANTINE: exclude khỏi
|
|
173
|
+
> main suite**, điều tra trong 1 sprint, ghi Automation Health Log. Hôm nay
|
|
174
|
+
> `review-check-groups.md:93` **ép** mọi TC mobile mang marker cũ; dịch thẳng sang chuẩn mới
|
|
175
|
+
> thành *ép mọi TC mobile bị cách ly khỏi suite chính*.
|
|
176
|
+
> Thêm nữa: **retry ×2 đã chuyển sang runner từ Đợt 1 b3** — marker trong TC giờ là **nguồn thứ
|
|
177
|
+
> hai** quyết định cùng một việc. Đây là **một quyết định về luật QC**, thuộc chị QC.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
### 1.3 · Bộ pattern — cấm chữ gì, và cố ý KHÔNG cấm chữ gì?
|
|
182
|
+
|
|
183
|
+
> 🔴 **Điểm dễ sai nhất của cả S0.** Xem B1.
|
|
184
|
+
|
|
185
|
+
**Đề xuất 6 pattern**, tất cả `scope: ["skills/qc/**", "commands/qc-*.tmpl", "modules/qc-*/**"]`:
|
|
186
|
+
|
|
187
|
+
| Pattern | Bắt gì | File dính *(trong phạm vi)* |
|
|
188
|
+
|---|---|:-:|
|
|
189
|
+
| `pytest` | tên test runner cũ | 18 |
|
|
190
|
+
| `Python` · `python` | tên ngôn ngữ — **hai entry** vì R5 so chuỗi phân biệt hoa thường | 16 · 11 |
|
|
191
|
+
| `conftest.py` | cơ chế fixture của pytest | 8 |
|
|
192
|
+
| `_page.py` | quy ước đặt tên Page Object cũ | 4 |
|
|
193
|
+
| `.py` | mọi đuôi file Python còn sót | 17 |
|
|
194
|
+
|
|
195
|
+
**Cố ý KHÔNG cấm — có bằng chứng:**
|
|
196
|
+
|
|
197
|
+
| Chữ | Dính | Vì sao giữ |
|
|
198
|
+
|---|:-:|---|
|
|
199
|
+
| `Allure` | 12 file | Chuẩn mobile MỚI **bắt buộc** Allure v2.x *(P1)*. Cấm nó là mã hoá một luật **ngược tài liệu Approved** |
|
|
200
|
+
| `BasePage` | 6 file | Chuẩn web MỚI vẫn dùng — `export class BasePage` *(P2)* |
|
|
201
|
+
| `time.sleep` | 9 file | Là **anti-pattern nội dung**, không phải dấu vết stack. Luật *"không hardcode wait"* sống tiếp, chỉ đổi cách viết (`waitForTimeout`). Thuộc S2, không thuộc đèn |
|
|
202
|
+
| `test_<feature>` | 9 file | Quy ước đặt tên — sẽ đổi ở S2 cùng nội dung, cấm ở đây là đỏ hai lần một lỗi |
|
|
203
|
+
|
|
204
|
+
**Khuyến nghị: đúng 6 pattern trên.** Hẹp, và nói rõ là hẹp *([`PLAN_v2 §8 luật 4`](PLAN_v2.md))*.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
209
|
+
|
|
210
|
+
## A1 · Vấn đề
|
|
211
|
+
|
|
212
|
+
Đội QC bỏ một bộ công cụ hồi tháng trước, nhưng hơn trăm dòng viết theo bộ cũ vẫn nằm rải rác
|
|
213
|
+
trong bộ khung — và cho tới sáng nay **không ai biết chính xác là bao nhiêu dòng, ở những file
|
|
214
|
+
nào**. Dọn bằng mắt thì sẽ sót, và chỗ sót không kêu lên: nó lặng lẽ dạy sai cho lần chạy sau.
|
|
215
|
+
Còn một nửa nữa của vấn đề: dọn xong rồi, vài tháng sau một người chép lại một đoạn từ file cũ,
|
|
216
|
+
và hiện không có gì chặn.
|
|
217
|
+
|
|
218
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
219
|
+
|
|
220
|
+
Như dán biển *"không để đồ ở đây"* lên lối thoát hiểm, thay vì dặn miệng từng người rồi mong
|
|
221
|
+
người vào sau còn nhớ. Biển không dọn hộ đống đồ đang có — nó chỉ bảo đảm rằng dọn xong thì
|
|
222
|
+
không ai chất lại mà không bị nhắc.
|
|
223
|
+
|
|
224
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
225
|
+
|
|
226
|
+
Chạy `node bin/self-check.js` **ngay hôm nay** sẽ ra một dòng đỏ liệt kê đúng **27 file**. Dọn
|
|
227
|
+
xong ở các việc sau thì dòng đó biến mất. Và nếu về sau ai chép lại một đoạn cũ vào, nó hiện lại
|
|
228
|
+
ngay lần chạy kế — không cần ai nhớ luật.
|
|
229
|
+
|
|
230
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
231
|
+
|
|
232
|
+
- **bộ công cụ / stack** — ngôn ngữ + thư viện mà test được viết bằng. Bộ cũ: Python + pytest. Bộ mới: TypeScript + Playwright.
|
|
233
|
+
- **đèn / guard / rule** — một phép kiểm tự động; đỏ thì không được commit.
|
|
234
|
+
- **`self-check`** — một trong bốn lệnh kiểm, soi tính nhất quán của chính bộ khung. Đang có 22 luật, đánh số R1–R22.
|
|
235
|
+
- **R5** — luật *"pattern bị cấm quay lại"*, đã có sẵn; S0 thêm việc cho nó chứ không dựng luật mới.
|
|
236
|
+
- **pattern** — một đoạn chữ bị cấm xuất hiện, ví dụ `pytest`.
|
|
237
|
+
- **`except`** — danh sách file được phép chứa đoạn chữ đó, **mỗi file kèm một lý do viết ra**.
|
|
238
|
+
- **phạm vi / scope** — vùng file mà một luật soi tới. Ngoài vùng thì luật im.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
> ### ✅ Phép thử bắt buộc trước khi coi Phần A là xong
|
|
243
|
+
> **Đã thử với:** tự đọc to · **ngày:** 2026-09-17 ·
|
|
244
|
+
> **phải giải thích thêm chỗ nào:** câu *"đèn không dọn hộ"* ở A2 — người nghe lần đầu tưởng
|
|
245
|
+
> S0 đã sửa xong 27 file. Đã thêm một câu ở A3 tách rõ *"hôm nay đỏ"* và *"dọn ở các việc sau"*.
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
# PHẦN B — Chi tiết kỹ thuật
|
|
250
|
+
|
|
251
|
+
## B1 · Cách hiển nhiên là gì, và vì sao nó sai
|
|
252
|
+
|
|
253
|
+
### Cách hiển nhiên — cấm chữ `Python` và `pytest` trên toàn repo
|
|
254
|
+
|
|
255
|
+
9/10 người viết đúng một dòng: *"không file nào được chứa `python` hay `pytest`"*, chạy, thấy
|
|
256
|
+
đỏ, và thấy mình đang làm đúng.
|
|
257
|
+
|
|
258
|
+
**Vì sao sai — ba lý do, tất cả đo được:**
|
|
259
|
+
|
|
260
|
+
**1 · Python là ngôn ngữ sản phẩm hạng nhất của chính framework này.** `modules/context-engineering`
|
|
261
|
+
là một module Python thật; `commands/dev-gen-test.tmpl:190` ghi *"**Nếu language = Python** (mặc
|
|
262
|
+
định)"*; `commands/dev-run-test.tmpl:69` in `pytest tests/{domain}/{test_file}.py -v`;
|
|
263
|
+
`commands/sync.tmpl:317` nhận diện dự án qua `requirements.txt` / `pyproject.toml`. Cấm toàn cục
|
|
264
|
+
làm đỏ **4 lệnh lane DEV** — **100 % dương tính giả**. Thứ bị bỏ là **Python với tư cách stack
|
|
265
|
+
TEST của QC**, không phải Python. Đèn phải khoá theo **phạm vi**, không theo từ.
|
|
266
|
+
|
|
267
|
+
**2 · Cấm theo "mùi của stack cũ" sẽ cấm nhầm thứ sống sót.** Cách hiển nhiên phiên bản hăng hái
|
|
268
|
+
hơn là quét luôn mọi chữ trông giống bộ cũ. Đo thì: `Allure` dính **12 file** — và
|
|
269
|
+
`Mobile-Automation-Standards.md:48` khai **Reporting | Allure Report | v2.x** là bắt buộc;
|
|
270
|
+
`BasePage` dính **6 file** — và `Automation-Standards.md:221` có `export class BasePage`. Cấm hai
|
|
271
|
+
chữ này là **mã hoá vào máy một luật ngược với tài liệu Approved**, rồi sáu tháng sau có người
|
|
272
|
+
mất nửa ngày để hiểu vì sao đèn không cho họ làm đúng chuẩn.
|
|
273
|
+
|
|
274
|
+
> Riêng `Allure` còn là một **mâu thuẫn nội dung** mà Bước S phải xử, không phải chuyện đèn:
|
|
275
|
+
> framework đang ghi *"No Allure"* như luật cứng, chuẩn mobile mới thì bắt buộc. Xem §0.3 P1.
|
|
276
|
+
|
|
277
|
+
**3 · Dựng một test mới là bỏ qua một cơ chế đã có.** `R5` + `forbidden_patterns` đã làm đúng
|
|
278
|
+
việc này cho **11 pattern**, và có một tính chất mà test viết tay hay thiếu: nó **cảnh báo khi
|
|
279
|
+
một `except` không còn cần thiết**. Đặt danh sách cấm ở chỗ thứ hai nghĩa là lần sau người ta
|
|
280
|
+
thêm vào chỗ sai — và một đèn không bao giờ sáng thì tệ hơn không có đèn, vì nó trông như có.
|
|
281
|
+
|
|
282
|
+
## B2 · Cách làm đúng
|
|
283
|
+
|
|
284
|
+
**Ba sửa đổi nhỏ, không sửa đổi nào chạm nội dung QC** *(nội dung là việc của S1–S5)*.
|
|
285
|
+
|
|
286
|
+
**① Mở scan set.** `bin/self-check.js` · `allSourceFiles()` thêm một dòng:
|
|
287
|
+
`push(path.join(ROOT,'skills'), f => f.endsWith('.md'))`.
|
|
288
|
+
|
|
289
|
+
> **Đã đo trước khi đề xuất:** chạy thử 11 pattern hiện có trên **64 file** `skills/**/*.md` →
|
|
290
|
+
> **0 hit**. Mở scan set **không làm đỏ** bất cứ thứ gì đang xanh. Con số này là điều kiện để
|
|
291
|
+
> phương án A an toàn; không có nó thì đây chỉ là một lời hứa.
|
|
292
|
+
|
|
293
|
+
**② Thêm trường `scope` cho `forbidden_patterns`.** Mảng glob. **Thiếu `scope` = toàn scan set**
|
|
294
|
+
— nên **11 entry cũ giữ nguyên hành vi, không phải sửa một entry nào**. Lọc thêm đúng một mệnh
|
|
295
|
+
đề vào vòng lặp R5 ở `self-check.js:284`:
|
|
296
|
+
|
|
297
|
+
```
|
|
298
|
+
const inScope = s => !fp.scope || fp.scope.some(g => match(g, s.rel));
|
|
299
|
+
const hits = sources.filter(s => inScope(s) && s.text.includes(fp.pattern) && !except.has(s.rel));
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**③ Thêm 6 entry**, mỗi entry `scope: ["skills/qc/**","commands/qc-*.tmpl","modules/qc-*/**"]`,
|
|
303
|
+
`gap: "S0"`, và `reason` nói **hậu quả**, không nói luật:
|
|
304
|
+
|
|
305
|
+
> *"Stack test của QC chốt 2026-09-10 là TypeScript + Playwright (web/api) và WDIO+Appium
|
|
306
|
+
> (mobile), xác nhận qua email. Một dòng `pytest` còn sót trong phạm vi QC không làm đỏ test
|
|
307
|
+
> nào — nó chỉ làm lệnh sinh ra script mà không dự án nào chạy được, và lỗi đó hiện ra ở lần
|
|
308
|
+
> chạy thật của QC chứ không ở CI. Python vẫn hợp lệ ở lane DEV; vì thế entry này có `scope`."*
|
|
309
|
+
|
|
310
|
+
**Vì sao 6 pattern chứ không 2:** `pytest` + `Python` bỏ sót **8 file** chỉ mang dấu vết hình
|
|
311
|
+
dạng (`conftest.py`, `_page.py`, `.py` trần). Vì sao không 10: xem §1.3 — bốn chữ còn lại sống
|
|
312
|
+
sót sang stack mới hoặc thuộc tầng nội dung.
|
|
313
|
+
|
|
314
|
+
**Vì sao `Python` và `python` là hai entry:** `String.includes` phân biệt hoa thường, và R5 cố ý
|
|
315
|
+
dùng so chuỗi thô thay vì regex *(pattern là **chữ** người đọc được, không phải biểu thức người
|
|
316
|
+
phải giải mã)*. Hai entry là cái giá của tính chất đó — chấp nhận, và ghi lại đây để người sau
|
|
317
|
+
đừng "tối ưu" nó thành regex rồi làm hỏng 11 entry kia.
|
|
318
|
+
|
|
319
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
320
|
+
|
|
321
|
+
**Ồn 🟠 — và Ồn là kiểu hỏng riêng của mọi cái đèn.**
|
|
322
|
+
|
|
323
|
+
Hỏng ở đâu: phạm vi quá rộng *(cấm toàn repo)* → 4 lệnh DEV đỏ oan. Người ta không gỡ luật —
|
|
324
|
+
người ta cấp `except` cho 4 file, rồi lần sau cấp tiếp, và sau đủ nhiều lần thì `except` dài hơn
|
|
325
|
+
danh sách cấm. Lúc đó **R5 vẫn xanh và không còn nghĩa gì**. Ai phát hiện: không ai — đó là chỗ
|
|
326
|
+
tệ. Một luật bị rỗng ruột trông y hệt một luật đang làm việc.
|
|
327
|
+
|
|
328
|
+
Kiểu hỏng phụ — **Im lặng 🔴** nếu phạm vi quá hẹp: bỏ `docs/` ra ngoài *(khuyến nghị §1.2)*
|
|
329
|
+
nghĩa là Python **quay lại qua đường tài liệu** được. Đây là một **lỗ đã biết, cố ý để lại**,
|
|
330
|
+
và vì thế nó phải thành một dòng nợ có người chịu — xem §9.
|
|
331
|
+
|
|
332
|
+
## B4 · Verify bằng gì
|
|
333
|
+
|
|
334
|
+
| # | Phép thử | Kết quả mong đợi | **Chạy thật 09-17** |
|
|
335
|
+
|:-:|---|---|---|
|
|
336
|
+
| 1 | `node bin/self-check.js` **ngay sau ①②③, trước khi sửa nội dung** | **R5 ĐỎ** | ✅ **ĐỎ 27 file** *(phạm vi gốc)* → sau **S-SCOPE**: **ĐỎ 19 file** — 7 `qa-runner` *(sẽ xoá)* · 5 `qa-reviewer/script` · 1 `qa-automation-assess` · 5 `commands/qc-*` · 1 `modules/qc-*` |
|
|
337
|
+
| 2 | So output `self-check` trước/sau khi **chỉ** làm ① | khác biệt **0 dòng** | ✅ **xanh y nguyên** — mở scan set sang `skills/` không đỏ thêm gì |
|
|
338
|
+
| 3 | `node test/run.js` | **244/244**, không đổi | ✅ **244/244** |
|
|
339
|
+
| 4 | `node bin/build.js` · `node bin/lint-trace.js` | xanh | ✅ cả hai xanh |
|
|
340
|
+
| 5 | **Phá:** thêm `pytest` vào `commands/dev-gen-test.tmpl` | **KHÔNG đỏ** | ✅ **0 lần nhắc** trong output R5 — `scope` chặn thật |
|
|
341
|
+
| 6 | **Phá:** thêm `pytest` vào một file `skills/qc` **đang sạch** | **ĐỎ** | ✅ `qa-runner/exploratory/session.md` → **1 lần nhắc** |
|
|
342
|
+
| 6b | **Phá:** thêm `pytest` vào **tầng tài liệu** `qa-designer/e2e/journey.md` | **KHÔNG đỏ** *(sau S-SCOPE)* | ✅ **0 lần nhắc** — 5 tầng tài liệu thật sự ngoài tầm |
|
|
343
|
+
| 7a | **Phá:** `except` trỏ file **đã sạch** | **WARN R5** | ✅ `⚠️ [R5] \`_page.py\`: except \`…/risk-model.md\` không còn chứa pattern (hoặc ngoài scope)` |
|
|
344
|
+
| 7b | **Phá:** `except` trỏ file **đang đỏ** | file đó **biến mất** khỏi danh sách | ✅ `matrix.md` rời khỏi danh sách của `_page.py`, 3 file kia ở lại |
|
|
345
|
+
| 9 | `scope` phủ **thư mục S2 sắp tạo** *(`qa-script-designer/**` · `qa-script-runner/**`)* | 2 glob có mặt · WARN *"khớp 0 file"* cho tới khi S2 tạo chúng | ✅ **2 WARN đúng 2 thư mục đó** — và WARN sẽ tự tắt khi S2 tạo file |
|
|
346
|
+
| 10 | Sau S2, glob `skills/qc/qa-runner/**` trở thành glob chết | WARN *"khớp 0 file"* → **gỡ glob trong CHÍNH commit của S2** | ⏳ chờ S2 |
|
|
347
|
+
| 8 | Sau S1–S5 | R5 **xanh**, `except` **rỗng**, **0 WARN scope** | ⏳ chờ S1–S5 |
|
|
348
|
+
|
|
349
|
+
> **Phép thử 5 và 6 ra ngược nhau — đó là điều phải chứng minh.** Cùng đỏ nghĩa là `scope` bị bỏ
|
|
350
|
+
> qua; cùng xanh nghĩa là pattern không khớp gì. Một xanh một đỏ mới nói `scope` đang làm việc.
|
|
351
|
+
>
|
|
352
|
+
> **Đèn đỏ ở đâu — bản ghi 2026-09-17** *(đây là bằng chứng của `G3`, chụp lại vì cây git sẽ
|
|
353
|
+
> xanh suốt — xem [`exec-S §8`](exec-S-ap-stack-typescript.md))*:
|
|
354
|
+
>
|
|
355
|
+
> | Pattern | File đỏ |
|
|
356
|
+
> |---|:-:|
|
|
357
|
+
> | `pytest` | 21 |
|
|
358
|
+
> | `Python` | 19 |
|
|
359
|
+
> | `python` | 13 |
|
|
360
|
+
> | `conftest.py` | 8 |
|
|
361
|
+
> | `_page.py` | 4 |
|
|
362
|
+
> | `.py` | 16 |
|
|
363
|
+
> | **hợp** | **27** |
|
|
364
|
+
|
|
365
|
+
> Phép thử **5** và **6** là hai phép quan trọng nhất và chúng phải chạy **ngược nhau**. Một cái
|
|
366
|
+
> xanh một cái đỏ mới chứng minh `scope` đúng; cả hai cùng đỏ nghĩa là `scope` bị bỏ qua, cả hai
|
|
367
|
+
> cùng xanh nghĩa là pattern không khớp gì cả.
|
|
368
|
+
|
|
369
|
+
## B5 · Bài học
|
|
370
|
+
|
|
371
|
+
- **2026-09-17** — Định viết đèn thành một test mới trong `test/run.js` *(exec-S §B2 đã viết thế)*. Đọc `self-check.js:279` thì **R5 đã là đúng cơ chế đó**, kèm cả `except` có lý do và cảnh báo except chết. Nguyên nhân thật: kế hoạch được viết từ *"cần một đèn"* chứ không từ *"đã có đèn nào chưa"*. Sửa: trước khi thêm một cơ chế, **grep tên lớp việc trong `bin/` đã**.
|
|
372
|
+
- **2026-09-17** — Bản nháp đầu định cấm luôn `Allure` và `BasePage` vì trông giống stack cũ. Đối chiếu `qc-base-new/` thì cả hai **sống sót**, và Allure còn **bắt buộc** cho mobile. Nguyên nhân thật: suy *"cũ"* từ trí nhớ thay vì tra tài liệu Approved. Sửa: mọi pattern định cấm phải `grep` qua `upstream/qc-base-new/` trước.
|
|
373
|
+
- **2026-09-17** — Phạm vi đèn trỏ `skills/qc/qa-runner/**` — **thư mục S2 sắp XOÁ** — và không trỏ `qa-script-designer/**` · `qa-script-runner/**` — **hai thư mục S2 sắp TẠO**. Sau S2 đèn sẽ **xanh vì rỗng, không vì sạch**: 6 file skill TypeScript mới ra đời **không ai canh**. Nguyên nhân thật: phạm vi được viết theo **hiện trạng** trong khi rule này sinh ra để canh **trạng thái sau khi đổi**. Sửa: thêm 2 glob, **và** thêm cảnh báo `scope` khớp 0 file — vì lỗi này tôi bắt được bằng tay, lần sau sẽ không ai bắt.
|
|
374
|
+
- **2026-09-17** — Lúc chạy phép thử 7, dùng `JSON.stringify(schema, null, 2)` để chèn `except` tạm → **reformat toàn bộ 3.100 dòng** của `trace-schema.json`. Không hỏng gì vì có bản sao, nhưng `git diff --stat` suýt không đọc được. Nguyên nhân thật: sửa một file cấu hình lớn bằng parse–ghi-lại thay vì sửa tại chỗ. Sửa: phép thử phá trên file JSON lớn phải **sao lưu trước, `git diff --stat` sau** — đã làm và xác nhận đúng **38 dòng thêm, 0 dòng đổi**.
|
|
375
|
+
|
|
376
|
+
## B6 · Copy được / không copy được
|
|
377
|
+
|
|
378
|
+
| | |
|
|
379
|
+
|---|---|
|
|
380
|
+
| ✅ **Copy được sang dự án khác** | Dựng đèn **trước** khi dọn, và lấy danh sách đỏ làm danh sách việc *(đèn tìm ra 10 file mà người viết kế hoạch không nghĩ tới)*; `scope` mặc định = toàn bộ để entry cũ không đổi hành vi; mỗi miễn trừ kèm lý do + cảnh báo khi miễn trừ hết cần; hai phép thử phá **ngược nhau** để chứng minh phạm vi |
|
|
381
|
+
| ⚠️ **Chỉ đúng ở đây** | Tên `R5` và hình dạng `forbidden_patterns`; việc Python là ngôn ngữ sản phẩm hợp lệ *(dự án khác có thể cấm toàn cục là đúng)*; 6 pattern cụ thể; `docs/plans/` nằm ngoài vì kế hoạch phải trích dẫn pattern |
|
|
382
|
+
|
|
383
|
+
## B7 · Link
|
|
384
|
+
|
|
385
|
+
- [`exec-S-ap-stack-typescript.md`](exec-S-ap-stack-typescript.md) — §8 *(S0 là việc 1/6)*, §B2 *(danh sách file — §0.2 của file này bổ sung 10 file)*
|
|
386
|
+
- [`PLAN_v2.md`](PLAN_v2.md) — §7 `G3` *("dựng rule trước khi vá")* · §8 luật 1 và luật 4
|
|
387
|
+
- `bin/self-check.js:279` — R5 · `:90` `allSourceFiles()` · `:293` cảnh báo except chết
|
|
388
|
+
- `bin/trace-schema.json` — `forbidden_patterns` *(11 entry)*
|
|
389
|
+
- `upstream/qc-base-new/` — `Automation-Standards.md:221` `BasePage` · `Mobile-Automation-Standards.md:48` Allure
|
|
390
|
+
|
|
391
|
+
---
|
|
392
|
+
|
|
393
|
+
## 9 · Nợ S0 để lại — có chủ, không phải chỗ quên
|
|
394
|
+
|
|
395
|
+
| Nợ | Vì sao để lại | Ai / khi nào |
|
|
396
|
+
|---|---|---|
|
|
397
|
+
| `docs/` **ngoài tầm đèn** — 5 file QC còn Python sau S5 vẫn không được canh | `docs/` cần nói *"trước đây là Python"*; đèn ở đó sẽ đẻ ra `except` bừa *(§1.2)* | rà lại sau Đợt 4, khi biết docs ổn định tới đâu |
|
|
398
|
+
| Đèn canh **chữ**, không canh **hình dạng** — một skill viết đúng TypeScript nhưng bố cục thư mục kiểu pytest vẫn qua | Canh hình dạng cần parse, không phải so chuỗi | nếu tái phát thì mới dựng; đừng dựng trước |
|
|
399
|
+
| **8 file · 16 dòng ở 5 tầng tài liệu** vẫn mô tả stack Python — ngoài tầm đèn sau `S-SCOPE` | Quyết định 09-17: 5 tầng đó không đổi stack | **nhóm A + D** *(4 dòng)*: sửa bất cứ lúc nào, không chặn ai. **nhóm B** *(10 dòng)*: **chị QC** — xem ⚠️ ở §1.2b, nó đảo ngược ý nghĩa chứ không phải đổi cú pháp. **nhóm C** *(2 dòng)*: **không sửa**, đó là guard |
|
|
400
|
+
| **Hệ quả trực tiếp của nhóm B:** `.Test.md` do trạm 3 sinh ra vẫn dặn QC ghi `@pytest.mark.flaky`, còn trạm 6 sinh script TypeScript **không hiểu marker đó** | Là giá của `S-SCOPE`, đã biết khi chốt | người sinh script phải tự dịch mỗi lần. Ghi ra đây để nó là **một lựa chọn**, không phải một chỗ quên |
|