@educa-corp/sdd-framework 0.9.2 → 0.9.4
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/build.js +11 -0
- package/bin/qc-base-map.json +119 -49
- package/bin/self-check.js +30 -0
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/qc-analyze.md +85 -75
- package/core/commands/qc-design-test.md +144 -25
- package/core/commands/qc-plan.md +40 -7
- package/core/commands/qc-review.md +74 -7
- package/core/commands/qc-run-test.md +21 -1
- package/core/commands/setup-ai-first.md +5 -5
- package/core/commands/update-framework.md +1 -1
- package/core/commands/validate-traces.md +1 -1
- package/core/modules/qc-playwright/stack-profile.yaml +3 -3
- package/core/rules/workflow.md +1 -1
- package/core/skills/qc/qa-analyst/DOC_GAP.template.md +47 -17
- package/core/skills/qc/qa-analyst/acceptance-criteria.md +1 -1
- package/core/skills/qc/qa-analyst/business-rules.md +2 -2
- package/core/skills/qc/qa-analyst/data-flow.md +2 -2
- package/core/skills/qc/qa-analyst/spec-breakdown.md +4 -4
- package/core/skills/qc/qa-analyst/spec-issue-reporter.md +14 -2
- package/core/skills/qc/qa-designer/api/auth-chain.md +155 -0
- package/core/skills/qc/qa-designer/api/auth-sequence.md +75 -0
- package/core/skills/qc/qa-designer/api/common-headers.md +61 -0
- package/core/skills/qc/qa-designer/api/crud-sequence.md +122 -0
- package/core/skills/qc/qa-designer/api/endpoint.md +231 -0
- package/core/skills/qc/qa-designer/api/http-status-codes.md +102 -0
- package/core/skills/qc/qa-designer/e2e/journey.md +13 -8
- package/core/skills/qc/qa-designer/exploratory/charter.md +2 -0
- package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +7 -4
- package/core/skills/qc/qa-designer/functional/api.md +87 -18
- package/core/skills/qc/qa-designer/functional/gui-feature.md +12 -9
- package/core/skills/qc/qa-designer/functional/gui-screen.md +12 -10
- package/core/skills/qc/qa-designer/integration/api.md +12 -5
- package/core/skills/qc/qa-designer/integration/db.md +12 -6
- package/core/skills/qc/qa-designer/integration/gui.md +12 -5
- package/core/skills/qc/qa-designer/integration/kafka.md +12 -5
- package/core/skills/qc/qa-designer/non-functional.md +12 -5
- package/core/skills/qc/qa-designer/shared/action-keywords-glossary.md +91 -0
- package/core/skills/qc/qa-designer/shared/duplicate-check-procedure.md +105 -0
- package/core/skills/qc/qa-designer/shared/implicit-scenarios.md +22 -0
- package/core/skills/qc/qa-designer/shared/precision-rules.md +198 -0
- package/core/skills/qc/qa-designer/shared/read-doc-gap-inputs.md +25 -0
- package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +93 -0
- package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +243 -0
- package/core/skills/qc/qa-planner/risk-model.md +1 -1
- package/core/skills/qc/qa-planner/test-plan.md +24 -13
- package/core/skills/qc/qa-reviewer/script/e2e.md +9 -1
- package/core/skills/qc/qa-reviewer/script/exploratory.md +9 -1
- package/core/skills/qc/qa-reviewer/script/functional.md +9 -1
- package/core/skills/qc/qa-reviewer/script/integration.md +9 -1
- package/core/skills/qc/qa-reviewer/script/non-functional.md +9 -1
- package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +26 -0
- package/core/skills/qc/qa-reviewer/shared/review-check-groups.md +207 -0
- package/core/skills/qc/qa-reviewer/shared/review-file-template.md +228 -0
- package/core/skills/qc/qa-reviewer/test-case/e2e.md +71 -13
- package/core/skills/qc/qa-reviewer/test-case/exploratory.md +53 -4
- package/core/skills/qc/qa-reviewer/test-case/functional.md +63 -15
- package/core/skills/qc/qa-reviewer/test-case/integration.md +64 -12
- package/core/skills/qc/qa-reviewer/test-case/non-functional.md +72 -13
- package/core/skills/qc/qa-runner/e2e.md +1 -1
- package/core/skills/qc/qa-runner/exploratory/session.md +1 -1
- package/core/steps/context-loader.md +1 -1
- package/core/steps/qc-scope.md +119 -0
- package/core/templates/project-context.yaml +3 -1
- package/docs/02-concepts/pipeline-steps/08-qc-automation.md +1 -1
- package/docs/02-concepts/pipeline-steps/09-validate-traces.md +1 -1
- package/docs/04-reference/configuration.md +146 -146
- package/docs/04-reference/trace-schema.md +1 -1
- package/docs/explain/00-setup-ai-first.md +1 -1
- package/docs/explain/15-qc-analyze.md +1 -1
- package/docs/explain/16-qc-plan.md +1 -1
- package/docs/explain/17-qc-design-test.md +1 -1
- package/docs/plans/qc-implementation-log.md +288 -5
- package/docs/plans/qc-sync-command.md +2 -1
- package/package.json +1 -1
- package/scripts/migrate-qc-docs.js +261 -0
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
version: 1.0
|
|
3
|
-
updated: 2026-
|
|
3
|
+
updated: 2026-09-04
|
|
4
4
|
ported_from: ui-automation-testing
|
|
5
|
+
upstream_path: skills/qa-tc-reviewer/ui-test-reviewer.md
|
|
6
|
+
upstream_sha: 54f81eb6a132d3fb331f18ce0f1a7eb3a9cf19df
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
# Review Test Case — Functional
|
|
@@ -10,7 +12,7 @@ Review bộ functional TC và đánh giá chất lượng.
|
|
|
10
12
|
|
|
11
13
|
## Khi nào trigger
|
|
12
14
|
- "review TC cho [Feature]" / "check coverage"
|
|
13
|
-
- Sau khi qa-designer xong, trước khi PO approve hoặc trước khi
|
|
15
|
+
- Sau khi qa-designer xong, trước khi PO approve hoặc trước khi /qc-run-test
|
|
14
16
|
|
|
15
17
|
## Khi KHÔNG trigger
|
|
16
18
|
- Review Python script → dùng qa-reviewer
|
|
@@ -18,6 +20,32 @@ Review bộ functional TC và đánh giá chất lượng.
|
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
|
23
|
+
## Trọng tâm review — tầng Functional / GUI
|
|
24
|
+
|
|
25
|
+
- **Assertion hiển thị đo được**: cấm *"hiển thị đúng"* — phải nêu element / nhãn / chuỗi / màu
|
|
26
|
+
cụ thể. Thiếu → `[VAGUE_EXPECTED]`.
|
|
27
|
+
- **EP + BVA từng field có ràng buộc**: mỗi phân vùng một TC; biên đủ 3 hoặc 4 giá trị tuỳ dạng
|
|
28
|
+
ràng buộc (`../../qa-designer/shared/precision-rules.md` §7). Thiếu → `[GAP]`.
|
|
29
|
+
- **Bảng quyết định cho logic nhiều điều kiện**: có ≥2 điều kiện → **tự dựng bảng** để đếm N rule
|
|
30
|
+
tối thiểu (sau khi gộp don't-care); thiếu TC so với N → `[DT_COVERAGE_GAP]`.
|
|
31
|
+
- **State Transition**: element có vòng đời phải phủ transition hợp lệ **và** transition bị cấm.
|
|
32
|
+
Thiếu nhánh cấm → `[GAP]`.
|
|
33
|
+
- **Phân nhóm GUI vs Functional** *(Nhóm 3 riêng của tầng này)*: GUI = chỉ hiển thị tĩnh, không
|
|
34
|
+
đổi trạng thái · Functional = có hành động / validate / luồng. Sai nhóm → `[WRONG_GROUP]`;
|
|
35
|
+
một TC gánh cả hai → `[SPLIT_NEEDED]`.
|
|
36
|
+
- **`Type` khớp nội dung**: Happy Path (luồng chính lý tưởng) · Positive (input hợp lệ → xử lý
|
|
37
|
+
đúng) · Negative (input sai → chặn & báo lỗi) · Edge (biên/cực trị). Lệch → `[WRONG_TYPE]`.
|
|
38
|
+
|
|
39
|
+
## 7 nhóm kiểm tra
|
|
40
|
+
|
|
41
|
+
> **Nạp `{paths.qc_skills_dir}/qa-reviewer/shared/review-check-groups.md`** — bộ xương 7 nhóm
|
|
42
|
+
> chạy tuần tự · registry nhãn lỗi dùng chung · 7 mẫu-hay-thiếu của Nhóm 6 · nền tảng ISTQB ·
|
|
43
|
+
> anti-pattern. Nhóm 5 (Nội dung TC) trỏ tiếp sang `shared/review-file-template.md`.
|
|
44
|
+
>
|
|
45
|
+
> Dưới đây chỉ là phần **riêng của tầng này**.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
21
49
|
## Phase 1 — Clarify
|
|
22
50
|
|
|
23
51
|
1. Đọc tất cả .Test.md trong folder được chỉ định
|
|
@@ -55,22 +83,42 @@ D. ANTI-PATTERN:
|
|
|
55
83
|
|
|
56
84
|
---
|
|
57
85
|
|
|
58
|
-
##
|
|
86
|
+
## Phase 3 — Coverage tổng thể
|
|
87
|
+
|
|
88
|
+
Sau khi duyệt hết từng TC, kiểm **bộ TC** có đủ bốn nhóm không:
|
|
59
89
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
90
|
+
| Nhóm | Kỳ vọng tối thiểu |
|
|
91
|
+
|---|---|
|
|
92
|
+
| Display | Mỗi section / vùng giao diện có ≥1 TC |
|
|
93
|
+
| Validation | Mỗi field có ràng buộc: required + BVA (3 hoặc 4 TC) + định dạng nếu có |
|
|
94
|
+
| State | Mỗi element có ≥2 trạng thái: ≥1 TC trạng thái đầu + ≥1 TC chuyển trạng thái |
|
|
95
|
+
| Interaction | Mỗi hành động (click / hover / bàn phím) ≥1 TC |
|
|
96
|
+
|
|
97
|
+
Thiếu nhóm nào → bảng **Coverage gaps** của file review, kèm `[GAP]` + mã TC đề xuất bổ sung.
|
|
68
98
|
|
|
69
99
|
---
|
|
70
|
-
|
|
100
|
+
|
|
101
|
+
## Kiểm tra cấu trúc file TC
|
|
102
|
+
|
|
103
|
+
> **Nạp `{paths.qc_skills_dir}/qa-reviewer/shared/review-file-template.md`** §*Kiểm tra cấu trúc
|
|
104
|
+
> TC* — luật ATOMIC 1-bullet · không ký tự `|` · 9 cụm từ mơ hồ · teardown đúng chỗ · marker ≠
|
|
105
|
+
> oracle · 6 dấu vết hỏng sau khi chạy script tách. **Không lặp lại ở đây.**
|
|
106
|
+
>
|
|
107
|
+
> Luật gốc mà trạm 3 phải theo nằm ở `qa-designer/shared/tc-metadata-format.md` +
|
|
108
|
+
> `precision-rules.md`. **Cổng và luật phải đọc từ cùng một chỗ** — giữ hai bản là tự tạo lại
|
|
109
|
+
> chỗ chúng lệch nhau, và cổng lệch thì nó đánh trượt file làm đúng.
|
|
110
|
+
|
|
71
111
|
## Output
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
112
|
+
|
|
113
|
+
**Ghi vào `{qc_artifact_dir}test-cases/REVIEW_<FEATURE>.md`** — đứng cạnh file TC vừa soát.
|
|
114
|
+
Nạp `../shared/review-file-template.md` để lấy khuôn + quy tắc ghi (tích luỹ theo vòng, chỉ sửa
|
|
115
|
+
hàng của tầng mình, KHÔNG ghi đè tầng khác).
|
|
116
|
+
|
|
117
|
+
Mỗi tiêu chí: ✅ PASS | ⚠️ WARN | ❌ FAIL + evidence cụ thể
|
|
118
|
+
|
|
119
|
+
**Điểm `XX/100`** — trừ 5đ mỗi `FAIL`, 2đ mỗi `WARN`. ≥80 đạt · 60–79 cần cải thiện · <60 không đạt.
|
|
120
|
+
**Verdict:** `điểm ≥ 80` VÀ không còn `FAIL` chặn → **`APPROVED`**; ngược lại **`NEEDS_FIX`**.
|
|
121
|
+
|
|
75
122
|
Đề xuất TC cần thêm/sửa/xoá, sắp theo priority. Liệt kê TC thiếu Trace BR (⚠️) cần bổ sung.
|
|
76
123
|
|
|
124
|
+
**KHÔNG tự sửa file TC** — chỉ nhận xét, chấm điểm, nêu cụ thể sửa gì. Người sửa là `/qc-design-test`.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
version: 1.0
|
|
3
|
-
updated: 2026-
|
|
3
|
+
updated: 2026-09-04
|
|
4
4
|
ported_from: ui-automation-testing
|
|
5
|
+
upstream_path: skills/qa-tc-reviewer/integration-test-reviewer.md
|
|
6
|
+
upstream_sha: 639b54afb68df83b22168581c5c57b0f91d9cf6a
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
# Review Test Case — Integration
|
|
@@ -10,7 +12,7 @@ Review bộ TC tích hợp (GUI↔Backend, API, DB) và đánh giá chất lư
|
|
|
10
12
|
|
|
11
13
|
## Khi nào trigger
|
|
12
14
|
- "review TC integration cho [Feature]" / "check TC tích hợp"
|
|
13
|
-
- Sau khi qa-designer/integration/* xong, trước khi
|
|
15
|
+
- Sau khi qa-designer/integration/* xong, trước khi /qc-run-test
|
|
14
16
|
|
|
15
17
|
## Khi KHÔNG trigger
|
|
16
18
|
- Review TC functional 1 màn/endpoint → `test-case/functional`
|
|
@@ -18,6 +20,30 @@ Review bộ TC tích hợp (GUI↔Backend, API, DB) và đánh giá chất lư
|
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
|
23
|
+
## Trọng tâm review — tầng Integration
|
|
24
|
+
|
|
25
|
+
- **Verify CẢ HAI phía**: mỗi handshake phải assert trạng thái của **Module A và Module B** +
|
|
26
|
+
tầng dữ liệu (DB/cache nhất quán). Chỉ verify một phía → `[GAP]`.
|
|
27
|
+
- **Phủ đủ chế độ lỗi**: happy → một bên lỗi (suy giảm mềm) → timeout / mất mạng → dữ liệu cũ /
|
|
28
|
+
tranh chấp → tương thích ngược. Thiếu nhánh lỗi → `[GAP]`.
|
|
29
|
+
- **Di trú DB phải có TC rollback**, không chỉ chiều tiến. Thiếu → `[GAP]` **mức cao**.
|
|
30
|
+
- **Chuỗi API**: phủ lỗi một-phần (bước 2 trượt khi bước 1 đã commit) + tính idempotent.
|
|
31
|
+
- **Mock hợp lý**: **không mock cả hai bên** — mock hết thì TC không còn kiểm gì. Kỹ thuật
|
|
32
|
+
State Transition / bảng quyết định phải khớp loại integration.
|
|
33
|
+
- **Phân nhóm & `Type`** *(Nhóm 3 riêng của tầng này)*: TC integration thuộc nhóm **Nghiệp vụ**,
|
|
34
|
+
**không có** nhóm GUI. `Type` khớp nội dung. Sai nhóm → `[WRONG_GROUP]`; `Type` lệch →
|
|
35
|
+
`[WRONG_TYPE]`; một TC gánh nhiều cặp module → `[SPLIT_NEEDED]`.
|
|
36
|
+
|
|
37
|
+
## 7 nhóm kiểm tra
|
|
38
|
+
|
|
39
|
+
> **Nạp `{paths.qc_skills_dir}/qa-reviewer/shared/review-check-groups.md`** — bộ xương 7 nhóm
|
|
40
|
+
> chạy tuần tự · registry nhãn lỗi dùng chung · 7 mẫu-hay-thiếu của Nhóm 6 · nền tảng ISTQB ·
|
|
41
|
+
> anti-pattern. Nhóm 5 (Nội dung TC) trỏ tiếp sang `shared/review-file-template.md`.
|
|
42
|
+
>
|
|
43
|
+
> Dưới đây chỉ là phần **riêng của tầng này**.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
21
47
|
## Phase 1 — Clarify
|
|
22
48
|
|
|
23
49
|
1. Đọc tất cả TC integration trong folder chỉ định; xác định loại: GUI↔Backend / API / DB
|
|
@@ -46,24 +72,50 @@ C. QUALITY:
|
|
|
46
72
|
- TC concurrency: mô tả rõ 2+ request đồng thời + kết quả kỳ vọng?
|
|
47
73
|
|
|
48
74
|
D. FORMAT & TRACE:
|
|
49
|
-
- Metadata đủ; Trace `[BR-xx]` hoặc `⚠️ Chưa có Business Rule`; `🚫 Block: [GAP-
|
|
50
|
-
- Cuối file có Trace matrix + bảng TC
|
|
75
|
+
- Metadata đủ; Trace `[BR-xx]` hoặc `⚠️ Chưa có Business Rule`; `🚫 Block: [GAP-UC{N}-{nnn}]` nếu bị chặn?
|
|
76
|
+
- Cuối file có **Trace matrix** (BR ↔ TC ↔ SC) + **danh sách** TC bị block — cả hai **dạng danh sách, KHÔNG bảng** (file TC không được có ký tự `|` — luật B12)?
|
|
51
77
|
- Steps phân biệt `[Action]`/`[Verify]`; Expected 1 bullet; KHÔNG `✅/❌` inline.
|
|
52
78
|
|
|
53
79
|
---
|
|
54
80
|
|
|
55
|
-
##
|
|
81
|
+
## Phase 3 — Coverage tổng thể
|
|
82
|
+
|
|
83
|
+
*(Bản gốc không có mục này cho tầng integration — bảng dưới suy ra từ chính các trục phủ ở
|
|
84
|
+
§Trọng tâm review trên.)*
|
|
56
85
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
86
|
+
| Trục | Kỳ vọng tối thiểu |
|
|
87
|
+
|---|---|
|
|
88
|
+
| Happy | Mỗi cặp Module A ↔ Module B có ≥1 TC verify **cả hai phía** |
|
|
89
|
+
| Failure | Mỗi cặp có ≥1 TC một-bên-lỗi (suy giảm mềm) |
|
|
90
|
+
| Timeout | Mỗi lời gọi qua mạng có ≥1 TC timeout / mất mạng |
|
|
91
|
+
| Rollback | Mỗi thao tác ghi có ≥1 TC rollback — **không chỉ chiều tiến** |
|
|
92
|
+
|
|
93
|
+
Điền vào bảng **Coverage handshake (Integration)** ở cuối file review.
|
|
61
94
|
|
|
62
95
|
---
|
|
63
96
|
|
|
97
|
+
## Kiểm tra cấu trúc file TC
|
|
98
|
+
|
|
99
|
+
> **Nạp `{paths.qc_skills_dir}/qa-reviewer/shared/review-file-template.md`** §*Kiểm tra cấu trúc
|
|
100
|
+
> TC* — luật ATOMIC 1-bullet · không ký tự `|` · 9 cụm từ mơ hồ · teardown đúng chỗ · marker ≠
|
|
101
|
+
> oracle · 6 dấu vết hỏng sau khi chạy script tách. **Không lặp lại ở đây.**
|
|
102
|
+
>
|
|
103
|
+
> Luật gốc mà trạm 3 phải theo nằm ở `qa-designer/shared/tc-metadata-format.md` +
|
|
104
|
+
> `precision-rules.md`. **Cổng và luật phải đọc từ cùng một chỗ** — giữ hai bản là tự tạo lại
|
|
105
|
+
> chỗ chúng lệch nhau, và cổng lệch thì nó đánh trượt file làm đúng.
|
|
106
|
+
|
|
64
107
|
## Output
|
|
65
108
|
|
|
66
|
-
|
|
67
|
-
|
|
109
|
+
**Ghi vào `{qc_artifact_dir}test-cases/REVIEW_<FEATURE>.md`** — đứng cạnh file TC vừa soát.
|
|
110
|
+
Nạp `../shared/review-file-template.md` để lấy khuôn + quy tắc ghi (tích luỹ theo vòng, chỉ sửa
|
|
111
|
+
hàng của tầng mình, KHÔNG ghi đè tầng khác).
|
|
112
|
+
|
|
113
|
+
Mỗi tiêu chí: ✅ PASS | ⚠️ WARN | ❌ FAIL + evidence cụ thể (TC ID / điểm tích hợp)
|
|
114
|
+
|
|
115
|
+
**Điểm `XX/100`** — trừ 5đ mỗi `FAIL`, 2đ mỗi `WARN`. ≥80 đạt · 60–79 cần cải thiện · <60 không đạt.
|
|
116
|
+
**Verdict:** `điểm ≥ 80` VÀ không còn `FAIL` chặn → **`APPROVED`**; ngược lại **`NEEDS_FIX`**.
|
|
117
|
+
|
|
68
118
|
Điểm tích hợp thiếu TC; TC Expected mờ nhạt; TC DB thiếu cleanup.
|
|
69
|
-
|
|
119
|
+
Điền thêm bảng **Coverage handshake (Integration)** ở cuối file review.
|
|
120
|
+
|
|
121
|
+
**KHÔNG tự sửa file TC** — chỉ nhận xét, chấm điểm, nêu cụ thể sửa gì. Người sửa là `/qc-design-test`.
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
---
|
|
2
2
|
version: 1.0
|
|
3
|
-
updated: 2026-
|
|
3
|
+
updated: 2026-09-04
|
|
4
4
|
ported_from: ui-automation-testing
|
|
5
|
+
upstream_path: skills/qa-tc-reviewer/nfr-test-reviewer.md
|
|
6
|
+
upstream_sha: 5747c7a6bdfaaea1ff3e958688b38b3aeaf9b15a
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
# Review Test Case — Non-Functional
|
|
@@ -10,7 +12,7 @@ Review bộ TC phi chức năng (performance, security, accessibility, compatibi
|
|
|
10
12
|
|
|
11
13
|
## Khi nào trigger
|
|
12
14
|
- "review TC non-functional cho [Feature]" / "check TC hiệu năng/bảo mật/accessibility"
|
|
13
|
-
- Sau khi qa-designer/non-functional xong, trước khi
|
|
15
|
+
- Sau khi qa-designer/non-functional xong, trước khi /qc-run-test
|
|
14
16
|
|
|
15
17
|
## Khi KHÔNG trigger
|
|
16
18
|
- Review TC chức năng → `test-case/functional`
|
|
@@ -18,6 +20,35 @@ Review bộ TC phi chức năng (performance, security, accessibility, compatibi
|
|
|
18
20
|
|
|
19
21
|
---
|
|
20
22
|
|
|
23
|
+
## Trọng tâm review — tầng NFR
|
|
24
|
+
|
|
25
|
+
- **Tiêu chí đo lường BẮT BUỘC**: mỗi TC có **ngưỡng + đơn vị + điều kiện môi trường**
|
|
26
|
+
(`P95 < X ms` · `error rate < Y%` · `contrast ≥ 4.5:1` · `Lighthouse a11y ≥ 90`).
|
|
27
|
+
Thiếu ngưỡng → `[VAGUE_EXPECTED]`. Đơn vị chuẩn theo domain:
|
|
28
|
+
`../../qa-designer/shared/precision-rules.md` §3.
|
|
29
|
+
- **Đúng loại + đúng công cụ**: `Type` ∈ {Load · Stress · Spike · Security · A11y · i18n}; có
|
|
30
|
+
công cụ phù hợp (k6 / ZAP / axe-core…) + marker đúng
|
|
31
|
+
(`@pytest.mark.performance|security|a11y|i18n`).
|
|
32
|
+
- **Phủ theo loại**: Performance có baseline + hồ sơ đồng thời · Security phủ OWASP áp dụng được
|
|
33
|
+
(A01/A03/A07…) · A11y phủ **bàn phím + trình đọc màn hình**, không chỉ axe-core tự động ·
|
|
34
|
+
i18n có tiếng Việt dấu đặc biệt (ộ/ề/ắ) + emoji + tràn chữ.
|
|
35
|
+
- **Môi trường**: NFR không chạy CI thường → TC phải ghi rõ môi trường riêng (load-test /
|
|
36
|
+
staging) và lịch chạy (nightly / trước phát hành). Thiếu → `[MISSING_DATA]`.
|
|
37
|
+
- **Phân nhóm & `Type`** *(Nhóm 3 riêng của tầng này)*: TC NFR thuộc nhóm **NFR**, **không có**
|
|
38
|
+
nhóm GUI. `Type` phải là một trong {Load · Stress · Spike · Security · A11y · i18n}. Sai nhóm →
|
|
39
|
+
`[WRONG_GROUP]`; `Type` không thuộc tập trên → `[WRONG_TYPE]`; một TC đo nhiều loại NFR →
|
|
40
|
+
`[SPLIT_NEEDED]`.
|
|
41
|
+
|
|
42
|
+
## 7 nhóm kiểm tra
|
|
43
|
+
|
|
44
|
+
> **Nạp `{paths.qc_skills_dir}/qa-reviewer/shared/review-check-groups.md`** — bộ xương 7 nhóm
|
|
45
|
+
> chạy tuần tự · registry nhãn lỗi dùng chung · 7 mẫu-hay-thiếu của Nhóm 6 · nền tảng ISTQB ·
|
|
46
|
+
> anti-pattern. Nhóm 5 (Nội dung TC) trỏ tiếp sang `shared/review-file-template.md`.
|
|
47
|
+
>
|
|
48
|
+
> Dưới đây chỉ là phần **riêng của tầng này**.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
21
52
|
## Phase 1 — Clarify
|
|
22
53
|
|
|
23
54
|
1. Đọc tất cả TC non-functional trong folder chỉ định; xác định loại: performance / security / accessibility / compatibility
|
|
@@ -49,25 +80,53 @@ C. ENVIRONMENT & DATA:
|
|
|
49
80
|
- Cleanup / teardown sau mỗi TC (đặc biệt security test)?
|
|
50
81
|
|
|
51
82
|
D. FORMAT & TRACE:
|
|
52
|
-
- Metadata đủ; Trace `[BR-xx]` / SLA source rõ; `🚫 Block: [GAP-
|
|
83
|
+
- Metadata đủ; Trace `[BR-xx]` / SLA source rõ; `🚫 Block: [GAP-UC{N}-{nnn}]` nếu bị chặn?
|
|
53
84
|
- Steps phân biệt `[Action]`/`[Verify]`; Expected 1 bullet; KHÔNG `✅/❌` inline?
|
|
54
|
-
- Cuối file có Trace matrix + bảng TC
|
|
85
|
+
- Cuối file có **Trace matrix** (BR ↔ TC ↔ SC) + **danh sách** TC bị block — cả hai **dạng danh sách, KHÔNG bảng** (file TC không được có ký tự `|` — luật B12)?
|
|
55
86
|
|
|
56
87
|
---
|
|
57
88
|
|
|
58
|
-
##
|
|
89
|
+
## Phase 3 — Coverage tổng thể
|
|
59
90
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
91
|
+
*(Bản gốc không có mục này cho tầng NFR — bảng dưới suy ra từ chính các trục phủ ở §Trọng tâm
|
|
92
|
+
review trên.)*
|
|
93
|
+
|
|
94
|
+
| Loại NFR | Kỳ vọng tối thiểu |
|
|
95
|
+
|---|---|
|
|
96
|
+
| Performance | Có baseline + ≥1 hồ sơ tải đồng thời, cả hai có ngưỡng + đơn vị |
|
|
97
|
+
| Security | Phủ các mục OWASP **áp dụng được** cho tính năng này (A01/A03/A07…) |
|
|
98
|
+
| Accessibility | Bàn phím **và** trình đọc màn hình — không chỉ axe-core tự động |
|
|
99
|
+
| i18n | Tiếng Việt dấu đặc biệt + emoji + tràn chữ |
|
|
100
|
+
| Compatibility | Các nền / trình duyệt mà PRD nêu |
|
|
101
|
+
|
|
102
|
+
Loại nào PRD **không** yêu cầu → ghi `N/A` kèm lý do, **đừng bỏ trống**: ô trống không phân biệt
|
|
103
|
+
được *"không áp dụng"* với *"quên kiểm"*.
|
|
104
|
+
Điền vào bảng **Chi tiết NFR** ở cuối file review.
|
|
65
105
|
|
|
66
106
|
---
|
|
67
107
|
|
|
108
|
+
## Kiểm tra cấu trúc file TC
|
|
109
|
+
|
|
110
|
+
> **Nạp `{paths.qc_skills_dir}/qa-reviewer/shared/review-file-template.md`** §*Kiểm tra cấu trúc
|
|
111
|
+
> TC* — luật ATOMIC 1-bullet · không ký tự `|` · 9 cụm từ mơ hồ · teardown đúng chỗ · marker ≠
|
|
112
|
+
> oracle · 6 dấu vết hỏng sau khi chạy script tách. **Không lặp lại ở đây.**
|
|
113
|
+
>
|
|
114
|
+
> Luật gốc mà trạm 3 phải theo nằm ở `qa-designer/shared/tc-metadata-format.md` +
|
|
115
|
+
> `precision-rules.md`. **Cổng và luật phải đọc từ cùng một chỗ** — giữ hai bản là tự tạo lại
|
|
116
|
+
> chỗ chúng lệch nhau, và cổng lệch thì nó đánh trượt file làm đúng.
|
|
117
|
+
|
|
68
118
|
## Output
|
|
69
119
|
|
|
70
|
-
|
|
71
|
-
|
|
120
|
+
**Ghi vào `{qc_artifact_dir}test-cases/REVIEW_<FEATURE>.md`** — đứng cạnh file TC vừa soát.
|
|
121
|
+
Nạp `../shared/review-file-template.md` để lấy khuôn + quy tắc ghi (tích luỹ theo vòng, chỉ sửa
|
|
122
|
+
hàng của tầng mình, KHÔNG ghi đè tầng khác).
|
|
123
|
+
|
|
124
|
+
Mỗi tiêu chí: ✅ PASS | ⚠️ WARN | ❌ FAIL + evidence cụ thể (TC ID)
|
|
125
|
+
|
|
126
|
+
**Điểm `XX/100`** — trừ 5đ mỗi `FAIL`, 2đ mỗi `WARN`. ≥80 đạt · 60–79 cần cải thiện · <60 không đạt.
|
|
127
|
+
**Verdict:** `điểm ≥ 80` VÀ không còn `FAIL` chặn → **`APPROVED`**; ngược lại **`NEEDS_FIX`**.
|
|
128
|
+
|
|
72
129
|
Danh sách TC Expected mờ nhạt (thiếu ngưỡng); loại non-functional thiếu coverage.
|
|
73
|
-
|
|
130
|
+
Ghi rõ TC nào cần môi trường đặc biệt. Điền thêm bảng **Chi tiết NFR** ở cuối file review.
|
|
131
|
+
|
|
132
|
+
**KHÔNG tự sửa file TC** — chỉ nhận xét, chấm điểm, nêu cụ thể sửa gì. Người sửa là `/qc-design-test`.
|
|
@@ -26,7 +26,7 @@ Skill **tự chứa**: convert journey `.Test.md` → Python pytest + Playwright
|
|
|
26
26
|
|
|
27
27
|
## Phase 2 — Generate
|
|
28
28
|
Mỗi journey → 1 test; bọc từng bước `with step("…")`; cuối journey verify đủ V1…Vn (tạo/mã/định tuyến/đồng bộ/danh sách).
|
|
29
|
-
Journey còn phụ thuộc gap → tạo test `@pytest.mark.skip(reason="GAP-
|
|
29
|
+
Journey còn phụ thuộc gap → tạo test `@pytest.mark.skip(reason="GAP-UC{N}-{nnn}")` hoặc xfail.
|
|
30
30
|
|
|
31
31
|
## Phase 3 — Verify
|
|
32
32
|
`py_compile` + `pytest --collect-only -q` · chạy (môi trường staging + CRM) · cập nhật Status TC.
|
|
@@ -33,4 +33,4 @@ Input: session note (#BUG + #IDEA).
|
|
|
33
33
|
- **Weekly summary** (nếu yêu cầu): overview, top findings, coverage gap, recommendations.
|
|
34
34
|
|
|
35
35
|
## Output
|
|
36
|
-
Mode 1: file session note. Mode 2: bug reports + file TC trong `{
|
|
36
|
+
Mode 1: file session note. Mode 2: bug reports + file TC trong `{qc_artifact_dir}test-cases/` + summary.
|
|
@@ -35,7 +35,7 @@ Thực hiện các bước theo đúng thứ tự. Lưu mọi thứ vào bộ nh
|
|
|
35
35
|
**Paths (nếu có):**
|
|
36
36
|
- `paths.specs_dir` → gốc của spec artifact — PRD, BDD, tech-docs, design-spec. Cấu trúc: `{specs_dir}/{domain}/{prd-slug}/{ {TICKET-ID}-{prd-slug}.md | bdd/ | tech-docs/ | design-spec/}` (file PRD đặt tên `{TICKET-ID}-{prd-slug}.md`, là file `.md` duy nhất ở gốc feature folder)
|
|
37
37
|
- `paths.refinement_dir` → thư mục output cho findings/review
|
|
38
|
-
- `paths.qc_dir` → gốc artifact QC automation (
|
|
38
|
+
- `paths.qc_dir` → gốc artifact QC automation (ở top-level, gom theo **PRD**: `{qc_dir}/{TICKET-ID}/{platform}/` — một `DOC_GAP.md` / `TEST_PLAN.md` / `REQUIREMENT_ANALYSIS.md` cho cả PRD, các UC là mục bên trong. Xem `steps/qc-scope.md`)
|
|
39
39
|
- `paths.qc_skills_dir` → nơi các lệnh qc-* nạp QC skill (mặc định bundled `.agent/skills/qc`; override sang repo/submodule riêng của team QC để bản nâng cấp framework không ghi đè)
|
|
40
40
|
- `paths.product_definitions_dir` → gốc product definition
|
|
41
41
|
- `paths.domain_knowledge_dir` → gốc domain knowledge
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# QC Scope — phân giải phạm vi cho mọi lệnh `qc-*`
|
|
2
|
+
|
|
3
|
+
**Chạy TRƯỚC phần logic riêng của lệnh, và SAU `steps/gate.md`.** Bước này chốt bốn thứ mà
|
|
4
|
+
cả 6 trạm QC đều cần, để chúng không tự suy mỗi trạm một kiểu:
|
|
5
|
+
|
|
6
|
+
| Biến | Là gì |
|
|
7
|
+
|---|---|
|
|
8
|
+
| `TICKET-ID` | mã PRD — **thư mục artifact QC mang tên này** |
|
|
9
|
+
| `active_platform` | `web` \| `app` \| `system` \| … — một QC pass khoá đúng MỘT nền |
|
|
10
|
+
| `qc_artifact_dir` | `{paths.qc_dir}/{TICKET-ID}/{active_platform}/` |
|
|
11
|
+
| `uc_list` | các UC của (PRD × nền) này, kèm trạng thái BDD từng UC |
|
|
12
|
+
|
|
13
|
+
> **Vì sao gom về một chỗ.** Luật phân giải nền từng được copy-paste ở 5 lệnh và câu chữ đã
|
|
14
|
+
> lệch nhau. Năm bản của một luật là nơi drift sống: sửa bốn, quên một, và trạm bị quên ghi
|
|
15
|
+
> artifact vào sai thư mục **trong im lặng**.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 1 — `TICKET-ID`
|
|
20
|
+
|
|
21
|
+
Artifact QC gom theo **PRD**, không theo UC. Nên mọi trạm phải quy được về `TICKET-ID`:
|
|
22
|
+
|
|
23
|
+
| `$ARGUMENTS` là | Cách lấy |
|
|
24
|
+
|---|---|
|
|
25
|
+
| **UC-ID** (`{TICKET-ID}-UC{N}`) | phần **trước** `-UC` — đúng luật `steps/gate.md` Bước 1 dùng để tìm tech-doc gộp |
|
|
26
|
+
| **TICKET-ID** | dùng trực tiếp |
|
|
27
|
+
| một **path file** (`.feature` / PRD / design-spec) | phân giải `{domain}` + `{prd-slug}` theo luật `context-loader` Bước 1, rồi lấy `TICKET-ID` từ tên file PRD `{TICKET-ID}-{prd-slug}.md` — file `.md` duy nhất ở gốc feature folder |
|
|
28
|
+
|
|
29
|
+
Đối chiếu: `TICKET-ID` suy ra phải khớp tên file PRD thật. Lệch → **DỪNG**, in cả hai giá
|
|
30
|
+
trị. (Suy sai `TICKET-ID` là ghi cả một PRD vào sai thư mục — không có bước nào phía sau bắt được.)
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 2 — `active_platform`
|
|
35
|
+
|
|
36
|
+
> **PHẢI phân giải TRƯỚC mọi phép đọc `.feature`.** `{UC-ID}-SC{N}` chỉ độc nhất trong
|
|
37
|
+
> (UC × nền), nên một UC đa nền có **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`,
|
|
38
|
+
> `bdd/system/` — và mỗi file mang `@trace.status` **riêng**: bản web có thể `approved`
|
|
39
|
+
> trong khi bản app còn `draft`. Đọc "file `.feature` của UC" khi chưa biết nền là đọc một
|
|
40
|
+
> file **bất kỳ trong ba**: báo `approved` trong khi bản đang dùng còn nháp, hoặc chặn oan
|
|
41
|
+
> một bản đã duyệt.
|
|
42
|
+
|
|
43
|
+
Theo thứ tự, dừng ở cái đầu tiên khớp:
|
|
44
|
+
|
|
45
|
+
1. `$ARGUMENTS` nêu nền (`web`/`app`/`system`/…) → dùng.
|
|
46
|
+
2. Target là một file `.feature` → đọc `# @trace.platform` của nó.
|
|
47
|
+
3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/` — **đúng một** thư mục nền → dùng nó.
|
|
48
|
+
4. Glob `{paths.qc_dir}/{TICKET-ID}/*/` — **đúng một** thư mục nền đã có artifact → dùng nó.
|
|
49
|
+
*(chỉ dùng cho trạm 2–6; trạm `/qc-analyze` là trạm tạo ra thư mục đó nên không có gì để soi.)*
|
|
50
|
+
5. Nhiều nền mà không suy được → hỏi *"QC pass này cho nền nào? (web/app/system)"*.
|
|
51
|
+
**Có `--yes`:** không hỏi — DỪNG với lỗi rõ ràng, vì đoán bừa nền là ghi artifact vào sai
|
|
52
|
+
thư mục và ghi `qc_status` vào sai sổ trace:
|
|
53
|
+
```
|
|
54
|
+
❌ {TICKET-ID} có {n} nền ({list}) — không suy được nền nào cho QC pass này.
|
|
55
|
+
Chạy headless thì phải nêu tường minh: /{lệnh} {TICKET-ID} web --yes
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Lưu `active_platform`. Từ đây, **mọi** phép đọc `.feature` chỉ đọc thư mục
|
|
59
|
+
`bdd/{active_platform}/` — không trộn SC chéo nền.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 3 — `qc_artifact_dir`
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
qc_artifact_dir = {paths.qc_dir}/{TICKET-ID}/{active_platform}/
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Chứa: `REQUIREMENT_ANALYSIS.md` · `DOC_GAP.md` · `TEST_PLAN.md` · `test-cases/*.Test.md`
|
|
70
|
+
— **mỗi loại đúng MỘT file cho cả PRD**, các UC là mục/hàng bên trong.
|
|
71
|
+
|
|
72
|
+
`{paths.qc_dir}` là folder top-level **nhìn thấy** trong repo QC (mặc định `docs/`, **không**
|
|
73
|
+
phải `.agent/` ẩn) để đội QC mở và xử lý output dễ dàng. Spec chính thức ở lại spec submodule
|
|
74
|
+
của PO — đừng ghi artifact QC vào đó.
|
|
75
|
+
|
|
76
|
+
> **Sổ trace KHÔNG theo layout này.** Nó vẫn là `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv`
|
|
77
|
+
> — một sổ cho mỗi (UC × nền), vì mỗi hàng là một scenario. Liên kết giữa hai bên đi qua
|
|
78
|
+
> **cột `UC`** của bảng gap, không qua đường dẫn file.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 4 — `uc_list`
|
|
83
|
+
|
|
84
|
+
Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/*.feature`. Mỗi file → một
|
|
85
|
+
UC: đọc `# @trace.id` (mã UC) và `# @trace.status` từ header.
|
|
86
|
+
|
|
87
|
+
Chia hai nhóm:
|
|
88
|
+
|
|
89
|
+
| Nhóm | Điều kiện | Xử lý |
|
|
90
|
+
|---|---|---|
|
|
91
|
+
| **Trong phạm vi** | `@trace.status: approved` | phân tích / thiết kế / chạy bình thường |
|
|
92
|
+
| **Chưa xét** | khác `approved` | **KHÔNG** phân tích; vẫn ghi một hàng vào bảng phạm vi kèm trạng thái thật |
|
|
93
|
+
|
|
94
|
+
In bảng phạm vi ra trước khi làm gì:
|
|
95
|
+
```
|
|
96
|
+
Phạm vi QC — {TICKET-ID} / {active_platform}
|
|
97
|
+
✅ {UC-ID} {tên UC} approved
|
|
98
|
+
⏸ {UC-ID} {tên UC} draft → chưa xét
|
|
99
|
+
→ {n} UC trong phạm vi · {m} chưa xét
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Cờ `--include-draft`:** phân tích cả UC chưa duyệt, nhưng **vẫn in bảng trên** và đánh dấu
|
|
103
|
+
trong artifact là dựa trên BDD nháp.
|
|
104
|
+
|
|
105
|
+
**Không UC nào `approved` và không có `--include-draft` → DỪNG:**
|
|
106
|
+
```
|
|
107
|
+
❌ {TICKET-ID} ({active_platform}): 0/{n} UC có BDD approved — không có gì để chạy.
|
|
108
|
+
Cách đúng: người duyệt đặt `# @trace.status: approved` rồi chạy lại.
|
|
109
|
+
Muốn chạy sớm trên BDD nháp (prototype): thêm --include-draft
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> **Vì sao có `--include-draft` chứ không chặn cứng.** QC sớm trên BDD nháp là một cách dùng
|
|
113
|
+
> **cố ý được cho phép** từ trước (guard cũ là cảnh báo mềm, không phải chặn). Bỏ hẳn nó là
|
|
114
|
+
> lấy đi một năng lực đang có mà không ai khai. Còn để mặc định `approved`-only thì cái
|
|
115
|
+
> thường gặp là cái an toàn, và cái sớm phải nói ra.
|
|
116
|
+
|
|
117
|
+
> **Vì sao `--yes` không thay được `--include-draft`.** `--yes` nghĩa *"tôi không ngồi đây để
|
|
118
|
+
> trả lời"*; `--include-draft` nghĩa *"tôi biết BDD còn nháp và vẫn muốn chạy"*. Gộp hai cái
|
|
119
|
+
> là để một lần chạy headless âm thầm phân tích spec chưa chốt rồi bàn giao như thể đã chốt.
|
|
@@ -64,7 +64,9 @@ paths:
|
|
|
64
64
|
|
|
65
65
|
# QC's OWN analysis/design working docs (qc-analyze/plan/design-test outputs:
|
|
66
66
|
# REQUIREMENT_ANALYSIS.md, DOC_GAP.md, TEST_PLAN.md, test-cases/*.Test.md).
|
|
67
|
-
#
|
|
67
|
+
# Grouped by PRD: {qc_dir}/{TICKET-ID}/{platform}/ — ONE DOC_GAP.md / TEST_PLAN.md /
|
|
68
|
+
# REQUIREMENT_ANALYSIS.md per (PRD x platform), with the UCs as rows/sections inside
|
|
69
|
+
# (see steps/qc-scope.md). Default "docs" (the QC team's own
|
|
68
70
|
# convention), VISIBLE — not hidden under .agent/. NOTE: specs (PRD / .feature /
|
|
69
71
|
# design-spec) are NOT here — they come from the PO spec submodule (spec_source).
|
|
70
72
|
qc_dir: "docs"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
|
|
39
39
|
| Artifact | Nội dung |
|
|
40
40
|
|----------|----------|
|
|
41
|
-
| `docs/{
|
|
41
|
+
| `docs/{TICKET-ID}/{platform}/…` | `REQUIREMENT_ANALYSIS.md`, `DOC_GAP.md`, `TEST_PLAN.md`, `test-cases/*.Test.md` — **mỗi loại đúng một file cho cả (PRD × nền)**, các UC là hàng/mục bên trong (cột `UC`) |
|
|
42
42
|
| Script Python pytest-playwright | Sinh từ `.Test.md` đã review |
|
|
43
43
|
| Cột `qc_status` trong `.trace/…/{UC-ID}-{platform}.tsv` | Trạng thái QC **chính thức** |
|
|
44
44
|
| Evidence + report | `/qc-report` — kèm product-gap đẩy về PO/Dev |
|
|
@@ -40,7 +40,7 @@ Traceability chỉ có giá trị khi **kiểm được**. Bước này cho mộ
|
|
|
40
40
|
| Artifact | Nội dung |
|
|
41
41
|
|----------|----------|
|
|
42
42
|
| Ma trận coverage spec ↔ code ↔ test | Trạng thái từng SC + `code_coverage` tổng |
|
|
43
|
-
| `{trace_dir}/trace-report.json` | Bản máy đọc cho **panel VS Code** ("
|
|
43
|
+
| `{trace_dir}/trace-report.json` | Bản máy đọc cho **panel VS Code** ("SDD Board") — bị **ghi đè** mỗi lần chạy |
|
|
44
44
|
| `{trace_dir}/trace-history.jsonl` | **Nhật ký append-only** — mỗi lần chạy ghi thêm 1 dòng *delta*. Đây là **dữ liệu**, không phải mirror: **phải commit**, mất là mất vĩnh viễn |
|
|
45
45
|
| Cờ audit | 6 cờ drift + 4 cờ 🔴 chặn PR + `PRD_UNTRACKED_EDIT` 🔴 (không chặn) + 2 cờ ⓘ (bảng dưới) |
|
|
46
46
|
| Hàng đợi | Đếm PRD change request còn `Open` kèm **số ngày chờ** (Step 7b) |
|