@educa-corp/sdd-framework 0.9.4 → 0.9.6
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 -1
- package/bin/lint-trace.js +599 -2
- package/bin/self-check.js +195 -0
- package/bin/trace-schema.json +2656 -1927
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/dev-gen-test.md +62 -0
- package/core/commands/generate-bdd.md +1 -0
- package/core/commands/generate-code.md +39 -2
- package/core/commands/generate-tech-docs.md +24 -5
- package/core/commands/map-testids.md +164 -7
- package/core/commands/qc-analyze.md +163 -9
- package/core/commands/qc-design-test.md +294 -2
- package/core/commands/qc-plan.md +57 -3
- package/core/commands/qc-report.md +76 -60
- package/core/commands/qc-review.md +102 -1
- package/core/commands/qc-run-test.md +194 -5
- package/core/commands/review-tech-docs.md +20 -0
- package/core/commands/validate-traces.md +17 -2
- package/core/modules/qc-playwright/stack-profile.yaml +1 -1
- package/core/rules/data-protection.md +52 -0
- package/core/rules/workflow.md +40 -0
- package/core/skills/qc/_shared/self-review-principles.md +112 -0
- package/core/skills/qc/qa-analyst/DOC_GAP.template.md +9 -1
- package/core/skills/qc/qa-designer/shared/duplicate-check-procedure.md +33 -5
- package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +24 -0
- package/core/skills/qc/qa-planner/test-plan.md +7 -0
- package/core/skills/qc/qa-runner/e2e.md +2 -2
- package/core/skills/qc/qa-runner/functional/gui-feature.md +9 -3
- package/core/skills/qc/qa-runner/functional/gui-screen.md +9 -3
- package/core/skills/qc/qa-runner/integration.md +1 -1
- package/core/skills/qc/qa-runner/non-functional.md +1 -1
- package/core/skills/spec/SKILL.md +1 -1
- package/core/steps/context-loader.md +7 -2
- package/core/steps/gap-verify.md +67 -0
- package/core/steps/qc-scope.md +67 -11
- package/core/steps/qc-stamp.md +142 -0
- package/core/steps/report-footer.md +15 -7
- package/core/templates/feature.template +1 -0
- package/core/templates/tech-design.template.md +4 -3
- package/docs/01-getting-started/quickstart.md +4 -3
- package/docs/02-concepts/architecture.md +14 -0
- package/docs/02-concepts/glossary.md +8 -0
- package/docs/02-concepts/overview.md +3 -2
- package/docs/02-concepts/pipeline-steps/04-bdd.md +1 -1
- package/docs/02-concepts/pipeline-steps/05-tech-docs.md +21 -5
- package/docs/02-concepts/pipeline-steps/06-code.md +12 -2
- package/docs/02-concepts/pipeline-steps/08-qc-automation.md +60 -12
- package/docs/02-concepts/pipeline-steps/README.md +4 -3
- package/docs/02-concepts/traceability.md +2 -2
- package/docs/03-guides/architect.md +2 -2
- package/docs/03-guides/developer.md +5 -2
- package/docs/03-guides/tester-qa.md +17 -5
- package/docs/04-reference/commands.md +7 -4
- package/docs/04-reference/trace-schema.md +38 -0
- package/docs/explain/07-generate-tech-docs.md +5 -3
- package/docs/explain/08-review-tech-docs.md +15 -3
- package/docs/explain/09-generate-code.md +30 -4
- package/docs/explain/10-review-code.md +1 -1
- package/docs/explain/11-map-testids.md +10 -7
- package/docs/explain/12-dev-gen-test.md +1 -1
- package/docs/explain/15-qc-analyze.md +14 -2
- package/docs/explain/16-qc-plan.md +5 -1
- package/docs/explain/17-qc-design-test.md +26 -3
- package/docs/explain/18-qc-review.md +6 -2
- package/docs/explain/19-qc-run-test.md +29 -6
- package/docs/explain/20-qc-report.md +5 -2
- package/docs/explain/README.md +4 -1
- package/docs/plans/qc-surgery/00-nhat-ky.md +497 -0
- package/docs/plans/qc-surgery/01-checklist.md +92 -0
- package/docs/plans/qc-surgery/02-lo-trinh.md +266 -0
- package/docs/plans/qc-surgery/buoc/0-01-testid-attr-co-cho-o.md +157 -0
- package/docs/plans/qc-surgery/buoc/0-02-mot-nguon-cho-testid-attr.md +135 -0
- package/docs/plans/qc-surgery/buoc/0-03-skill-thoi-day-do-dom.md +167 -0
- package/docs/plans/qc-surgery/buoc/0-04-may-canh-hop-dong.md +173 -0
- package/docs/plans/qc-surgery/buoc/0-05-don-nhan-cot-va-2b.md +133 -0
- package/docs/plans/qc-surgery/buoc/0-06-hop-dong-truoc-code.md +226 -0
- package/docs/plans/qc-surgery/buoc/1-01-guard-br-tag.md +156 -0
- package/docs/plans/qc-surgery/buoc/1-02-guard-sc-coverage.md +153 -0
- package/docs/plans/qc-surgery/buoc/1-03-fail-3-nhan.md +176 -0
- package/docs/plans/qc-surgery/buoc/1-04-self-review-dung-chung.md +175 -0
- package/docs/plans/qc-surgery/buoc/1-05-spec-la-du-lieu.md +164 -0
- package/docs/plans/qc-surgery/buoc/1-06-gap-verify-du-bo.md +162 -0
- package/docs/plans/qc-surgery/buoc/README.md +85 -0
- package/docs/plans/qc-surgery/exec-d0-b1-testid-attr-header.md +147 -0
- package/docs/plans/qc-surgery/exec-d0-b2-thong-nhat-nguon-testid-attr.md +152 -0
- package/docs/plans/qc-surgery/exec-d0-b3-sua-skill-probe-dom.md +173 -0
- package/docs/plans/qc-surgery/exec-d0-b4-may-canh-4-5-6.md +168 -0
- package/docs/plans/qc-surgery/exec-d0-b5-don-nhan-lech.md +196 -0
- package/docs/plans/qc-surgery/exec-d0-b6-contract-truoc-code.md +350 -0
- package/docs/plans/qc-surgery/exec-d1-b1-guard-br-tag.md +129 -0
- package/docs/plans/qc-surgery/exec-d1-b2-guard-sc-coverage.md +159 -0
- package/docs/plans/qc-surgery/exec-d1-b3-fail-3-bucket.md +158 -0
- package/docs/plans/qc-surgery/exec-d1-b4-self-review-principles.md +145 -0
- package/docs/plans/qc-surgery/exec-d1-b5-noi-quy-spec-la-du-lieu.md +156 -0
- package/docs/plans/qc-surgery/exec-d1-b6-gap-verify-mo-rong.md +179 -0
- package/docs/plans/qc-surgery/exec-d2-b1-tach-qc-review.md +166 -0
- package/docs/plans/qc-surgery/exec-d2-b2-tach-qc-run-test-atomic.md +267 -0
- package/docs/plans/qc-surgery/exec-d2-b3-qc-automation-assess.md +198 -0
- package/docs/plans/qc-surgery/exec-d3-b1-qc-report-gate-decision.md +209 -0
- package/docs/plans/qc-surgery/exec-d4-b1-qc-design-testdata.md +146 -0
- package/docs/plans/qc-surgery/exec-d4-b2-qc-smoke-test.md +179 -0
- package/docs/plans/qc-surgery/exec-d4-b3-qc-metrics-va-lint.md +198 -0
- package/docs/plans/qc-surgery/exec-d4-b4-lint-spec-injection.md +199 -0
- package/package.json +1 -1
package/core/commands/qc-plan.md
CHANGED
|
@@ -180,10 +180,22 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
|
180
180
|
rồi mới tiếp tục phần bên dưới.
|
|
181
181
|
|
|
182
182
|
Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
|
|
183
|
-
`qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--
|
|
183
|
+
`qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--force`).
|
|
184
184
|
Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —
|
|
185
185
|
cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
|
|
186
186
|
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Stamp phiên bản nguồn
|
|
190
|
+
|
|
191
|
+
**BẮT BUỘC — đọc `.agent/steps/qc-stamp.md` và thực thi phần áp cho lệnh này**,
|
|
192
|
+
rồi mới tiếp tục phần bên dưới.
|
|
193
|
+
|
|
194
|
+
Nó có **hai vế**: §1 **ghi** khối `Nguồn & phiên bản` vào artifact lệnh này sinh ra ·
|
|
195
|
+
§2 **so** stamp của artifact lệnh này ĐỌC với version hiện tại của spec.
|
|
196
|
+
Bỏ vế ghi thì trạm sau không có gì để so; bỏ vế so thì stamp thành một con số không ai
|
|
197
|
+
đọc — và một bộ TC lỗi thời sẽ chạy xanh rồi ghi `pass` **hợp lệ theo mọi phép kiểm**.
|
|
198
|
+
|
|
187
199
|
> **Trạm này chạy CẢ PRD, đúng như trạm 1** *(B11)* — một `TEST_PLAN.md` cho mỗi (PRD × nền).
|
|
188
200
|
> `qa-planner/test-plan.md` vốn viết *"Test Plan cho một **feature**"* và template của nó là
|
|
189
201
|
> `# Test Plan – <Feature>` với metadata `Feature / Project / Module`: đây là quay về đúng
|
|
@@ -226,6 +238,45 @@ Bắt buộc:
|
|
|
226
238
|
- **`§5 Entry criteria` chặn theo từng UC**, không chặn cả PRD: gap 🔴 Blocker ở UC3 không
|
|
227
239
|
dừng việc thiết kế test cho UC1. Ghi `Ready` / `Blocked` cho **mỗi** UC.
|
|
228
240
|
|
|
241
|
+
### Chạy lại — ghi đè ở đây AN TOÀN, và đây là lý do
|
|
242
|
+
|
|
243
|
+
*(Ba trạm QC khác — `/qc-analyze` · `/qc-design-test` · `/qc-run-test` — đều ở mức **chặn CỨNG** kèm
|
|
244
|
+
§Chạy lại. Trạm này **không**, và đó là kết luận có chủ ý, không phải chỗ sót.)*
|
|
245
|
+
|
|
246
|
+
`TEST_PLAN.md` **không có ô nào người nhập tay**: `questions-for-dev` là danh sách **gửi đi** — câu
|
|
247
|
+
trả lời quay về qua `DOC_GAP.md` hoặc kênh chat, **không ai điền ngược vào file này**; stamp phiên
|
|
248
|
+
bản và cột `Ready`/`Blocked` đều do lệnh **tự tính** từ `DOC_GAP.md`. Nên chạy lại = lập lại, không
|
|
249
|
+
mất gì.
|
|
250
|
+
|
|
251
|
+
*Khai máy đọc: `bin/trace-schema.json` → `artifact_writers.enrolled.qc-plan.has_human_content = false`
|
|
252
|
+
— và **R18 ép khai `why`** chính vì một ngoại lệ có chủ ý trông y hệt một chỗ sót.*
|
|
253
|
+
|
|
254
|
+
> **⚠️ Khai lại nếu điều này hết đúng.** Có ai bắt đầu điền tay vào `TEST_PLAN.md` — một cột quyết
|
|
255
|
+
> định, một ghi chú duyệt, một câu trả lời dán vào — thì lệnh này **phải** lên `checkpoint_levels.hard`
|
|
256
|
+
> và có §Chạy lại như `/qc-analyze`. Đánh giá "an toàn" ở trên đúng **hôm nay**, không đúng vĩnh viễn.
|
|
257
|
+
|
|
258
|
+
> **`Blocked` là mức ƯU TIÊN, KHÔNG phải lệnh cấm** *(G66)*. Nó nghĩa *"UC này chưa sẵn sàng
|
|
259
|
+
> để **nghiệm thu**"*, không phải *"chưa được **thiết kế**"*. `/qc-design-test` vẫn chạy được và
|
|
260
|
+
> **nên** chạy — TC chạm gap mang dấu `🚫 Block` trỏ về hàng gap, và cơ chế đó chỉ có nghĩa khi
|
|
261
|
+
> trạm 3 thực sự chạy trên UC `Blocked`.
|
|
262
|
+
>
|
|
263
|
+
> Cái bị chặn là **chạy test**, không phải **viết test**. Chờ PO trả lời mất ngày tới tuần; cấm
|
|
264
|
+
> thiết kế trong lúc chờ là ném đi đúng phần song song mà `/map-testids` mở ra. Và đây là cùng
|
|
265
|
+
> lập luận đã dùng ngay ở gạch trên — chặn theo UC chứ không theo PRD — chỉ áp thêm một bậc:
|
|
266
|
+
> một blocker trong UC1 cũng không dừng việc thiết kế **phần còn lại** của UC1.
|
|
267
|
+
|
|
268
|
+
## Self-Review *(trước khi in Report)*
|
|
269
|
+
|
|
270
|
+
Theo 3 nhóm ở `{paths.qc_skills_dir}/_shared/self-review-principles.md` — **không chép lại ở đây**.
|
|
271
|
+
|
|
272
|
+
- **Bịa:** mỗi dòng rủi ro neo được vào một `BR`/`AC`/`GAP` **có thật** trong
|
|
273
|
+
`REQUIREMENT_ANALYSIS.md`/`DOC_GAP.md` — không phải rủi ro chung chung tự nghĩ ra kiểu *"hiệu
|
|
274
|
+
năng có thể chậm"*? Mỗi `questions-for-dev` suy ra từ một gap cụ thể?
|
|
275
|
+
- **Nhảy bước:** đã đọc **cả hai** file đầu vào (`REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`) và lọc
|
|
276
|
+
theo cột `UC` — không chỉ đọc file thứ nhất?
|
|
277
|
+
- **Số liệu:** `{risks}`/`{questions}` in ở report = đúng số dòng thật trong `TEST_PLAN.md` vừa
|
|
278
|
+
ghi, không phải đếm nhẩm?
|
|
279
|
+
|
|
229
280
|
## Report
|
|
230
281
|
|
|
231
282
|
**Đọc `.agent/steps/report-footer.md`** và áp đúng khuôn footer trong đó (Status Badge ·
|
|
@@ -237,6 +288,9 @@ Phạm vi: {n} UC trong plan{nếu có: " · ⏸ {m} chưa xét"}
|
|
|
237
288
|
Plan: {risks} rủi ro · {questions} câu hỏi mở cho dev · layers: {list}
|
|
238
289
|
File: {paths.qc_dir}/{TICKET-ID}/{active_platform}/TEST_PLAN.md
|
|
239
290
|
Sẵn sàng: {danh sách UC Ready} | Chặn: {danh sách UC Blocked + GAP-ID chặn nó}
|
|
240
|
-
|
|
241
|
-
|
|
291
|
+
Self-review: {✅ sạch | ⚠️ {n} điểm cần chú ý — liệt kê}
|
|
292
|
+
Next: /qc-design-test {UC-ID} ← thiết kế test case, từng UC một
|
|
293
|
+
Ưu tiên UC `Ready`. UC `Blocked` VẪN thiết kế được — TC chạm gap mang dấu
|
|
294
|
+
🚫 Block và chưa chạy tới khi gap Answered.
|
|
295
|
+
(song song: gửi questions-for-dev cho PO/Dev để gỡ blocker)
|
|
242
296
|
```
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
---
|
|
2
|
-
version: 1.0
|
|
3
|
-
updated: 2026-06-11
|
|
4
|
-
ported_from: ai-automation-qc-base
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# /qc-report — QC Test Report & Evidence
|
|
8
|
-
|
|
9
|
-
> Stage 6 (cuối) của QC automation pipeline native (qc-analyze → qc-plan → qc-design-test → qc-review → qc-run-test → qc-report). Port từ bước report của qa-runner team QC.
|
|
10
|
-
|
|
11
|
-
## Gate
|
|
1
|
+
---
|
|
2
|
+
version: 1.0
|
|
3
|
+
updated: 2026-06-11
|
|
4
|
+
ported_from: ai-automation-qc-base
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /qc-report — QC Test Report & Evidence
|
|
8
|
+
|
|
9
|
+
> Stage 6 (cuối) của QC automation pipeline native (qc-analyze → qc-plan → qc-design-test → qc-review → qc-run-test → qc-report). Port từ bước report của qa-runner team QC.
|
|
10
|
+
|
|
11
|
+
## Gate
|
|
12
12
|
# Gate — Quy trình vào chuẩn cho mọi lệnh
|
|
13
13
|
|
|
14
14
|
Mọi lệnh PHẢI chạy gate này trước khi thực thi phần logic riêng của nó.
|
|
@@ -160,58 +160,74 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
|
|
|
160
160
|
- "N" → dừng, hỏi người dùng muốn thay đổi gì.
|
|
161
161
|
- Có `--yes` và mức *chặn thường* → coi như "Y", **nhưng vẫn IN khối CHECKPOINT** nếu có cờ
|
|
162
162
|
🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
*Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID. Sinh report từ lần chạy `/qc-run-test` gần nhất. Dùng module **qc-playwright** (pytest-html + Playwright Trace — không Allure, không dashboard viết tay).*
|
|
166
|
-
|
|
167
|
-
## Context
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
*Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID. Sinh report từ lần chạy `/qc-run-test` gần nhất. Dùng module **qc-playwright** (pytest-html + Playwright Trace — không Allure, không dashboard viết tay).*
|
|
166
|
+
|
|
167
|
+
## Context
|
|
168
168
|
**BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
|
|
169
169
|
rồi mới tiếp tục phần bên dưới.
|
|
170
170
|
|
|
171
171
|
Bỏ qua bước này thì `{paths.*}`, `{tech_stack.*}`, `{conventions.*}`, guardrail từ
|
|
172
172
|
`project-lessons`, và routing service (chế độ umbrella) đều **chưa được phân giải** — mọi
|
|
173
|
-
placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
174
|
-
|
|
175
|
-
---
|
|
176
|
-
|
|
177
|
-
## Role
|
|
178
|
-
|
|
179
|
-
Bạn là stage **QC Report** — biến lần chạy gần nhất thành report + evidence chia sẻ được.
|
|
180
|
-
|
|
181
|
-
## Skill (`{paths.qc_skills_dir}/qa-runner/report/`)
|
|
182
|
-
|
|
183
|
-
- `report.md` — tự đủ: pytest-html (`--html=reports/<feature>/report.html
|
|
184
|
-
--self-contained-html`) + Playwright Trace (`test-results/<nodeid>/trace.zip`, xem qua
|
|
185
|
-
`python3 -m playwright show-trace <file>`), kèm screenshot/evidence đính trên FAIL/SKIP.
|
|
186
|
-
|
|
187
|
-
## Procedure
|
|
188
|
-
|
|
189
|
-
1. Định vị artifact của lần chạy gần nhất cho `{UC-ID}` (report pytest-html + trace).
|
|
190
|
-
2. Sinh/làm mới `reports/<feature>/report.html` (self-contained) và đảm bảo mỗi
|
|
191
|
-
FAIL/SKIP có trace + screenshot đính kèm.
|
|
192
|
-
3. Tóm tắt TOTAL / PASS / FAIL / SKIP; với mỗi FAIL gồm lệnh `show-trace` và
|
|
193
|
-
phân loại là script-bug hay product-gap.
|
|
194
|
-
4. **Bàn giao product-gap về spec (có nhắc).** Với mỗi FAIL phân loại **product-gap**
|
|
195
|
-
(defect thật, impl ≠ spec — không phải script-bug), in một
|
|
196
|
-
`/report-bug {UC-ID} {one-line expected-vs-actual}` sẵn-chạy để QC file nó vào spec repo dùng chung.
|
|
197
|
-
BUG_FLOW của `/report-bug` rồi định tuyến root cause (Code / BDD / PRD / Design / Env). Không bao giờ
|
|
198
|
-
fake-pass một product-gap — nó giữ FAIL trong `qc_status` cho tới khi fix + chạy lại. **script-bug
|
|
199
|
-
KHÔNG được file** (QC fix script và chạy lại). Liệt kê các lệnh; đừng tự tạo report.
|
|
200
|
-
|
|
201
|
-
## Report
|
|
202
|
-
|
|
173
|
+
placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Role
|
|
178
|
+
|
|
179
|
+
Bạn là stage **QC Report** — biến lần chạy gần nhất thành report + evidence chia sẻ được.
|
|
180
|
+
|
|
181
|
+
## Skill (`{paths.qc_skills_dir}/qa-runner/report/`)
|
|
182
|
+
|
|
183
|
+
- `report.md` — tự đủ: pytest-html (`--html=reports/<feature>/report.html
|
|
184
|
+
--self-contained-html`) + Playwright Trace (`test-results/<nodeid>/trace.zip`, xem qua
|
|
185
|
+
`python3 -m playwright show-trace <file>`), kèm screenshot/evidence đính trên FAIL/SKIP.
|
|
186
|
+
|
|
187
|
+
## Procedure
|
|
188
|
+
|
|
189
|
+
1. Định vị artifact của lần chạy gần nhất cho `{UC-ID}` (report pytest-html + trace).
|
|
190
|
+
2. Sinh/làm mới `reports/<feature>/report.html` (self-contained) và đảm bảo mỗi
|
|
191
|
+
FAIL/SKIP có trace + screenshot đính kèm.
|
|
192
|
+
3. Tóm tắt TOTAL / PASS / FAIL / SKIP; với mỗi FAIL gồm lệnh `show-trace` và
|
|
193
|
+
phân loại là script-bug hay product-gap.
|
|
194
|
+
4. **Bàn giao product-gap về spec (có nhắc).** Với mỗi FAIL phân loại **product-gap**
|
|
195
|
+
(defect thật, impl ≠ spec — không phải script-bug), in một
|
|
196
|
+
`/report-bug {UC-ID} {one-line expected-vs-actual}` sẵn-chạy để QC file nó vào spec repo dùng chung.
|
|
197
|
+
BUG_FLOW của `/report-bug` rồi định tuyến root cause (Code / BDD / PRD / Design / Env). Không bao giờ
|
|
198
|
+
fake-pass một product-gap — nó giữ FAIL trong `qc_status` cho tới khi fix + chạy lại. **script-bug
|
|
199
|
+
KHÔNG được file** (QC fix script và chạy lại). Liệt kê các lệnh; đừng tự tạo report.
|
|
200
|
+
|
|
201
|
+
## Self-Review *(trước khi in Report)*
|
|
202
|
+
|
|
203
|
+
Theo 3 nhóm ở `{paths.qc_skills_dir}/_shared/self-review-principles.md` — **không chép lại ở đây**.
|
|
204
|
+
|
|
205
|
+
- **Bịa:** mọi con số trong báo cáo trích được về **một dòng cụ thể** của sổ trace / output
|
|
206
|
+
runner / bug report — không nội suy khi thiếu mẫu?
|
|
207
|
+
- **Nhảy bước:** đã tổng hợp trên **toàn bộ** SC trong phạm vi, không chỉ những SC có kết quả
|
|
208
|
+
đẹp? Đã liệt kê cả SC `not_run` và `flaky`, không im lặng bỏ khỏi bảng?
|
|
209
|
+
- **Số liệu:** mọi `%` là phép chia thật **và nói rõ mẫu số**? Chỗ thiếu dữ liệu ghi **"chưa đủ
|
|
210
|
+
dữ liệu"** thay vì điền một số cho đủ bảng?
|
|
211
|
+
|
|
212
|
+
> **Trạm này là nơi số liệu đi ra khỏi đội QC.** Một con số sai ở các trạm trước còn người trong
|
|
213
|
+
> đội nhìn thấy; sai ở đây là đi vào báo cáo cho Lead/PM. Nhóm 3 vì vậy là nhóm nặng nhất ở đây:
|
|
214
|
+
> *"một bảng đầy số sai tệ hơn một bảng có ô trống ghi rõ lý do"*.
|
|
215
|
+
|
|
216
|
+
## Report
|
|
217
|
+
|
|
203
218
|
**Đọc `.agent/steps/report-footer.md`** và áp đúng khuôn footer trong đó (Status Badge ·
|
|
204
|
-
Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
|
|
205
|
-
|
|
206
|
-
```
|
|
207
|
-
/qc-report Hoàn tất — {UC-ID}
|
|
208
|
-
Report: reports/<feature>/report.html (TOTAL {N} · PASS {p} · FAIL {f} · SKIP {s})
|
|
209
|
-
Trace : test-results/<nodeid>/trace.zip (python3 -m playwright show-trace <file>)
|
|
210
|
-
|
|
211
|
-
Product-gap cần file ({g}): ← chạy các lệnh này để PO/Dev thấy trên /sync (script-bug bị loại)
|
|
212
|
-
/report-bug {UC-ID} {gap 1 — expected vs actual}
|
|
213
|
-
/report-bug {UC-ID} {gap 2 …}
|
|
214
|
-
(không có → skip)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
219
|
+
Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
/qc-report Hoàn tất — {UC-ID}
|
|
223
|
+
Report: reports/<feature>/report.html (TOTAL {N} · PASS {p} · FAIL {f} · SKIP {s})
|
|
224
|
+
Trace : test-results/<nodeid>/trace.zip (python3 -m playwright show-trace <file>)
|
|
225
|
+
|
|
226
|
+
Product-gap cần file ({g}): ← chạy các lệnh này để PO/Dev thấy trên /sync (script-bug bị loại)
|
|
227
|
+
/report-bug {UC-ID} {gap 1 — expected vs actual}
|
|
228
|
+
/report-bug {UC-ID} {gap 2 …}
|
|
229
|
+
(không có → skip)
|
|
230
|
+
|
|
231
|
+
Self-review: {✅ sạch | ⚠️ {n} điểm cần chú ý — liệt kê}
|
|
232
|
+
Next: /validate-traces {UC-ID} ← làm mới Living Docs (qc_status), rồi tạo PR
|
|
233
|
+
```
|
|
@@ -178,10 +178,22 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
|
178
178
|
rồi mới tiếp tục phần bên dưới.
|
|
179
179
|
|
|
180
180
|
Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
|
|
181
|
-
`qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--
|
|
181
|
+
`qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--force`).
|
|
182
182
|
Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —
|
|
183
183
|
cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
|
|
184
184
|
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## Stamp phiên bản nguồn
|
|
188
|
+
|
|
189
|
+
**BẮT BUỘC — đọc `.agent/steps/qc-stamp.md` và thực thi phần áp cho lệnh này**,
|
|
190
|
+
rồi mới tiếp tục phần bên dưới.
|
|
191
|
+
|
|
192
|
+
Nó có **hai vế**: §1 **ghi** khối `Nguồn & phiên bản` vào artifact lệnh này sinh ra ·
|
|
193
|
+
§2 **so** stamp của artifact lệnh này ĐỌC với version hiện tại của spec.
|
|
194
|
+
Bỏ vế ghi thì trạm sau không có gì để so; bỏ vế so thì stamp thành một con số không ai
|
|
195
|
+
đọc — và một bộ TC lỗi thời sẽ chạy xanh rồi ghi `pass` **hợp lệ theo mọi phép kiểm**.
|
|
196
|
+
|
|
185
197
|
> **Trạm này vẫn gọi theo TỪNG UC** *(B11)* — thiết kế và chạy test **thật sự** làm tăng dần
|
|
186
198
|
> theo UC, nên giữ khả năng làm UC1 khi UC3 chưa xong là đúng. Chỉ **chỗ đọc/ghi** đổi: mọi
|
|
187
199
|
> artifact nằm chung ở `{qc_artifact_dir}` cấp PRD, không còn một thư mục mỗi UC.
|
|
@@ -222,9 +234,63 @@ tồn tại cho UC và mới hơn → review script. Nếu mơ hồ, hỏi.
|
|
|
222
234
|
- Soát test-case: `test-case/{functional,e2e,integration,non-functional,exploratory}.md`
|
|
223
235
|
- Soát script: `script/{functional,e2e,integration,non-functional,exploratory}.md`
|
|
224
236
|
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Guard — hợp đồng test-id, chân thứ tư *(phép so GIÁ TRỊ, một chiều)*
|
|
240
|
+
|
|
241
|
+
`bin/trace-schema.json` canh **ba** chân của hợp đồng test-id: §4.5.6 ↔ `.feature` (**T15**) ·
|
|
242
|
+
↔ header `@trace.testid_attr` (**T16**) · ↔ code (**T17/T18**). Chân thứ tư — §4.5.6 ↔ `*.Test.md`
|
|
243
|
+
— **không máy nào canh**, và nó là chân duy nhất mà giá trị id bị **chép cứng vào một artifact bền**:
|
|
244
|
+
|
|
245
|
+
```
|
|
246
|
+
generate-code → bake vào code → T17/T18 canh
|
|
247
|
+
qc-run-test → dựng locator LÚC CHẠY → đọc bảng tươi, tự cứu
|
|
248
|
+
qc-design-test → chép vào .Test.md → KHÔNG AI CANH ← chỗ này
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Phạm vi — đọc trước khi làm gì.** Không có block §4.5 client trong tech-doc (dự án backend-only,
|
|
252
|
+
hoặc chưa từng chạy `/map-testids`) → **im lặng hoàn toàn**, không kiểm gì. Cùng điều khoản phạm vi
|
|
253
|
+
với `testid_contract`: *"hai rule nói 'chỗ nào đã hứa thì phải giữ', KHÔNG nói 'mọi chỗ đều phải có
|
|
254
|
+
hợp đồng'"*.
|
|
255
|
+
|
|
256
|
+
**Phép so — MỘT chiều:**
|
|
257
|
+
|
|
258
|
+
1. Gom mọi test-id được nhắc trong các `*.Test.md` của UC này.
|
|
259
|
+
2. Gom id ở §4.5.6 (block `active_platform`), **lọc theo cột "Serves SC"** khớp SC của UC này.
|
|
260
|
+
3. `TC ∖ bảng` = id TC nhắc mà hợp đồng không có → ⚠️.
|
|
261
|
+
|
|
262
|
+
| Kết quả | Làm gì |
|
|
263
|
+
|---|---|
|
|
264
|
+
| Rỗng | **im lặng, đi tiếp** |
|
|
265
|
+
| Có id lạ | `⚠️ Hợp đồng test-id: {k} id trong .Test.md không có ở §4.5.6: {danh sách}`<br/>kèm: `Chạy /map-testids {UC-ID} (đưa vào hợp đồng) hoặc /qc-design-test {UC-ID} (sửa TC theo hợp đồng).` |
|
|
266
|
+
|
|
267
|
+
**Chiều ngược KHÔNG kiểm.** §4.5.6 có id mà không TC nào nhắc là **bình thường** — không phải element
|
|
268
|
+
nào cũng cần một bước TC. Kiểm chiều đó là **ồn**, đúng bất đối xứng T17/T18 đã chọn.
|
|
269
|
+
|
|
270
|
+
**Mức là `warn`, không chặn.** Id sai làm test **ĐỎ**, không làm test **xanh giả** — nên nó **không**
|
|
271
|
+
thuộc lớp *"báo cáo sai"* và không đi cùng đường với `steps/qc-stamp.md` §2 (chặn `pass`). Chặn ở đây
|
|
272
|
+
là chặn nhầm loại.
|
|
273
|
+
|
|
274
|
+
> **Vì sao vẫn đáng cảnh báo dù test sẽ tự đỏ.** Vì nó đỏ **đội lốt thứ khác**. `testid_contract` ghi
|
|
275
|
+
> đúng chữ: *"test đỏ 'element not found' **trông y hệt bug sản phẩm**"* — nên `/qc-report` phân loại
|
|
276
|
+
> *product-gap* rồi in một `/report-bug` sẵn-chạy, và một lỗi **hợp đồng nội bộ** đi ra khỏi đội QC
|
|
277
|
+
> thành phiếu lỗi gửi PO. Dòng ⚠️ này là chỗ duy nhất chặn được chuyến đi đó.
|
|
278
|
+
|
|
225
279
|
## Review focus
|
|
226
280
|
|
|
227
281
|
- **Test-case:** mọi `{UC-ID}-SC{N}` đã phủ? happy + negative + boundary? expected cụ thể? trace (`BR-xx` + `@trace.verifies` SC) có mặt? không có TC orphan?
|
|
282
|
+
- **`🚫 Block` còn hiệu lực không?** Mỗi TC mang `🚫 Block: [GAP-UC{N}-{nnn}]` → mở `../DOC_GAP.md`,
|
|
283
|
+
đọc `Trạng thái` của hàng gap đó. **Gap đã `Answered` mà TC vẫn mang dấu ⇒ FINDING.**
|
|
284
|
+
|
|
285
|
+
> **Vì sao là finding chứ không phải chuyện nhỏ** *(G66)*. Một TC mang `🚫 Block` là một TC có
|
|
286
|
+
> `Expected Result` dựa trên **giả định chưa ai xác nhận**. Khi gap được trả lời, quy trình
|
|
287
|
+
> `tc-metadata-format.md` §*Quy trình khi gap được giải quyết* đòi **cập nhật lại Expected Result**
|
|
288
|
+
> — giả định có thể đã sai. Bỏ qua thì TC chạy với oracle sai, fail, rồi `/qc-report` phân loại
|
|
289
|
+
> *product-gap* và in một `/report-bug`: một lỗ hổng **tài liệu** đi ra ngoài đội QC thành **phiếu
|
|
290
|
+
> lỗi sản phẩm** gửi PO.
|
|
291
|
+
>
|
|
292
|
+
> `/qc-design-test` §Guard 🚫 Block **tự gỡ** các dấu này khi chạy lại. Trạm này là **lưới bắt phía
|
|
293
|
+
> sau** cho ca guard đó không chạy (TC sửa tay, hoặc gap được trả lời sau lần chạy trạm 3 cuối).
|
|
228
294
|
- **Script:** khớp `.Test.md` 1-1? Page Object 3 lớp + BasePage gọn? `expect()` không phải bare assert? không hard-code URL/cred/timeout, không `time.sleep`, không Allure? selector theo priority (data-testid→role→…)? 100% TC đã script (không còn Draft)?
|
|
229
295
|
|
|
230
296
|
Sinh findings (mức độ + vị trí + cách sửa), chấm điểm, ra verdict.
|
|
@@ -247,6 +313,25 @@ Ghi vào **`{qc_artifact_dir}test-cases/REVIEW_<FEATURE>.md`** — đứng **c
|
|
|
247
313
|
⚠️ **Tên file review KHÔNG có đuôi `.Test.md`.** Đuôi đó là của file test case; gắn vào file
|
|
248
314
|
review sẽ làm `/qc-run-test` nhặt nó lên như một file test case rồi cố sinh script từ một bảng điểm.
|
|
249
315
|
|
|
316
|
+
### Chạy lại — ghi đè ở đây AN TOÀN, và đây là lý do
|
|
317
|
+
|
|
318
|
+
*(Ba trạm QC khác — `/qc-analyze` · `/qc-design-test` · `/qc-run-test` — đều ở mức **chặn CỨNG** kèm
|
|
319
|
+
§Chạy lại. Trạm này **không**, và đó là kết luận có chủ ý, không phải chỗ sót.)*
|
|
320
|
+
|
|
321
|
+
`REVIEW_<FEATURE>.md` là **biên bản của một lần soát**. Sinh lại = **soát lại**, đúng bản chất của
|
|
322
|
+
nó — không có trạng thái nào tích luỹ qua các lần chạy, không ô nào người điền tay vào sau.
|
|
323
|
+
|
|
324
|
+
*Khai máy đọc: `bin/trace-schema.json` → `artifact_writers.enrolled.qc-review.has_human_content = false`
|
|
325
|
+
— và **R18 ép khai `why`** chính vì một ngoại lệ có chủ ý trông y hệt một chỗ sót.*
|
|
326
|
+
|
|
327
|
+
> **⚠️ Khai lại nếu điều này hết đúng.** Có ai bắt đầu ghi vào biên bản thứ **không sinh lại được** —
|
|
328
|
+
> chữ ký duyệt, một quyết định *"chấp nhận rủi ro"*, ghi chú thảo luận với dev — thì lệnh này **phải**
|
|
329
|
+
> lên `checkpoint_levels.hard` và có §Chạy lại như `/qc-analyze`.
|
|
330
|
+
>
|
|
331
|
+
> *Lưu ý ngược lại: file TC mà trạm này **soát** thì **có** phần người làm tay, và `/qc-design-test`
|
|
332
|
+
> đã ở mức `hard` vì đúng lý do đó (G78). Trạm này chỉ an toàn ở phần nó **ghi**, không phải phần nó
|
|
333
|
+
> **đọc**.*
|
|
334
|
+
|
|
250
335
|
Khuôn đầy đủ + quy tắc ghi: `shared/review-file-template.md`. Ba điều bắt buộc:
|
|
251
336
|
|
|
252
337
|
- **Điểm `XX/100`** — trừ 5đ mỗi `FAIL`, 2đ mỗi `WARN`. Verdict **suy ra được**:
|
|
@@ -261,6 +346,21 @@ Khuôn đầy đủ + quy tắc ghi: `shared/review-file-template.md`. Ba điề
|
|
|
261
346
|
> hai kết quả, và **vòng 2 không so được với vòng 1** — tức không ai biết sửa xong có tốt lên
|
|
262
347
|
> không. Một cổng không để lại dấu vết đo được thì không phải cổng, nó là một lượt đọc.
|
|
263
348
|
|
|
349
|
+
## Self-Review *(trước khi in Report)*
|
|
350
|
+
|
|
351
|
+
Theo 3 nhóm ở `{paths.qc_skills_dir}/_shared/self-review-principles.md` — **không chép lại ở đây**.
|
|
352
|
+
|
|
353
|
+
- **Bịa:** mỗi finding trỏ được về **dòng cụ thể** trong artifact đang soát — không phải nhận xét
|
|
354
|
+
chung chung? Và **cấm dùng chính field `quote` của finding làm bằng chứng cho nó** — mở lại file
|
|
355
|
+
đọc lại đoạn đó.
|
|
356
|
+
- **Nhảy bước:** đã đi hết bộ tiêu chí của **đúng vai** (test-case hay script), không trộn hai
|
|
357
|
+
bộ? Verdict `APPROVED` phát ra **sau** khi soát đủ, không phải vì "trông ổn"?
|
|
358
|
+
- **Số liệu:** số finding theo mức (critical/major/minor) = đếm thật trên file findings vừa ghi?
|
|
359
|
+
|
|
360
|
+
> **Verdict của lệnh này là cổng cho trạm sau.** Một `APPROVED` phát ra sớm không chỉ sai ở đây —
|
|
361
|
+
> nó mở đường cho `/qc-run-test` sinh script từ một bộ TC chưa đạt. Đây là chỗ self-review đắt
|
|
362
|
+
> nhất nếu bỏ qua.
|
|
363
|
+
|
|
264
364
|
## Report
|
|
265
365
|
|
|
266
366
|
**Đọc `.agent/steps/report-footer.md`** và áp đúng khuôn footer trong đó (Status Badge ·
|
|
@@ -271,6 +371,7 @@ Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
|
|
|
271
371
|
Điểm : {XX}/100 ({fail} FAIL × −5đ · {warn} WARN × −2đ){nếu có vòng trước: " ← vòng #{N-1}: {YY}/100"}
|
|
272
372
|
Verdict: {APPROVED | NEEDS_FIX} — {n} findings ({crit} chặn)
|
|
273
373
|
File : {qc_artifact_dir}test-cases/REVIEW_<FEATURE>.md (thêm 1 hàng vào bảng Tổng quan)
|
|
374
|
+
Self-review: {✅ sạch | ⚠️ {n} điểm cần chú ý — liệt kê}
|
|
274
375
|
Next (test-case APPROVED): /qc-run-test {UC-ID}
|
|
275
376
|
Next (script APPROVED) : /qc-report {UC-ID} rồi tạo PR
|
|
276
377
|
(NEEDS_FIX → fix artifact bị gắn cờ, rồi chạy lại /qc-review {UC-ID})
|
|
@@ -9,6 +9,11 @@ ported_from: ai-automation-qc-base
|
|
|
9
9
|
> Stage 5 của QC automation pipeline native (qc-analyze → qc-plan → qc-design-test → qc-review → qc-run-test → qc-report). Port từ qa-runner của team QC. Sinh & chạy Python pytest-playwright từ `.Test.md` đã review, rồi ghi `qc_status` **chính thức** vào trace TSV.
|
|
10
10
|
|
|
11
11
|
## Gate
|
|
12
|
+
|
|
13
|
+
*Checkpoint: **chặn CỨNG** — ghi đè script Python đã có → XOÁ bản sửa `script-bug` mà chính lệnh này bảo QC tự sửa bằng tay. `--yes` KHÔNG bỏ qua được (gate Bước 3a).*
|
|
14
|
+
|
|
15
|
+
*Mức cứng chỉ áp khi **sinh lại** file script đã tồn tại. Chạy lại test trên script không đổi — việc thường nhất của trạm này — **không** chạm CHECKPOINT cứng. Xem §Chạy lại.*
|
|
16
|
+
|
|
12
17
|
# Gate — Quy trình vào chuẩn cho mọi lệnh
|
|
13
18
|
|
|
14
19
|
Mọi lệnh PHẢI chạy gate này trước khi thực thi phần logic riêng của nó.
|
|
@@ -178,10 +183,22 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
|
178
183
|
rồi mới tiếp tục phần bên dưới.
|
|
179
184
|
|
|
180
185
|
Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
|
|
181
|
-
`qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--
|
|
186
|
+
`qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--force`).
|
|
182
187
|
Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —
|
|
183
188
|
cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
|
|
184
189
|
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Stamp phiên bản nguồn
|
|
193
|
+
|
|
194
|
+
**BẮT BUỘC — đọc `.agent/steps/qc-stamp.md` và thực thi phần áp cho lệnh này**,
|
|
195
|
+
rồi mới tiếp tục phần bên dưới.
|
|
196
|
+
|
|
197
|
+
Nó có **hai vế**: §1 **ghi** khối `Nguồn & phiên bản` vào artifact lệnh này sinh ra ·
|
|
198
|
+
§2 **so** stamp của artifact lệnh này ĐỌC với version hiện tại của spec.
|
|
199
|
+
Bỏ vế ghi thì trạm sau không có gì để so; bỏ vế so thì stamp thành một con số không ai
|
|
200
|
+
đọc — và một bộ TC lỗi thời sẽ chạy xanh rồi ghi `pass` **hợp lệ theo mọi phép kiểm**.
|
|
201
|
+
|
|
185
202
|
> **Trạm này vẫn gọi theo TỪNG UC** *(B11)* — thiết kế và chạy test **thật sự** làm tăng dần
|
|
186
203
|
> theo UC, nên giữ khả năng làm UC1 khi UC3 chưa xong là đúng. Chỉ **chỗ đọc/ghi** đổi: mọi
|
|
187
204
|
> artifact nằm chung ở `{qc_artifact_dir}` cấp PRD, không còn một thư mục mỗi UC.
|
|
@@ -218,13 +235,111 @@ Quy tắc stack (BẮT BUỘC — từ `modules/qc-playwright/stack-profile.yaml
|
|
|
218
235
|
- pytest-playwright fixture; mỗi test độc lập; gom theo (role, account) để auth không bao giờ xen kẽ.
|
|
219
236
|
- Không hard-code URL/cred/timeout (dùng `Env.*` / `CONFIG[...]`); không `time.sleep()`; không Allure.
|
|
220
237
|
- Phủ **100%** TC trong file — mỗi TC kết thúc Pass/Fail/Skip (không còn Draft).
|
|
221
|
-
- Phân loại mỗi FAIL: script-bug (fix selector/logic)
|
|
238
|
+
- Phân loại mỗi FAIL — **ba** nhãn, sau khi đã chạy lại (xem §Chạy lại trước khi kết luận): `script-bug` (fix selector/logic) · `product-gap` (giữ FAIL + evidence, **không bao giờ fake-pass**) · `flaky` (không nhất quán qua các lần chạy lại — chưa đủ căn cứ để kết luận hai cái kia).
|
|
222
239
|
|
|
223
240
|
## Skills — chọn layer, nạp MỘT file (`{paths.qc_skills_dir}/qa-runner/`)
|
|
224
241
|
|
|
225
242
|
`functional/{gui-screen,gui-feature,api}.md`, `integration.md`, `e2e.md`,
|
|
226
243
|
`non-functional.md`, `exploratory/session.md`.
|
|
227
244
|
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Guard — hợp đồng test-id, chân thứ tư *(phép so GIÁ TRỊ, một chiều)*
|
|
248
|
+
|
|
249
|
+
`bin/trace-schema.json` canh **ba** chân của hợp đồng test-id: §4.5.6 ↔ `.feature` (**T15**) ·
|
|
250
|
+
↔ header `@trace.testid_attr` (**T16**) · ↔ code (**T17/T18**). Chân thứ tư — §4.5.6 ↔ `*.Test.md`
|
|
251
|
+
— **không máy nào canh**, và nó là chân duy nhất mà giá trị id bị **chép cứng vào một artifact bền**:
|
|
252
|
+
|
|
253
|
+
```
|
|
254
|
+
generate-code → bake vào code → T17/T18 canh
|
|
255
|
+
qc-run-test → dựng locator LÚC CHẠY → đọc bảng tươi, tự cứu
|
|
256
|
+
qc-design-test → chép vào .Test.md → KHÔNG AI CANH ← chỗ này
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Phạm vi — đọc trước khi làm gì.** Không có block §4.5 client trong tech-doc (dự án backend-only,
|
|
260
|
+
hoặc chưa từng chạy `/map-testids`) → **im lặng hoàn toàn**, không kiểm gì. Cùng điều khoản phạm vi
|
|
261
|
+
với `testid_contract`: *"hai rule nói 'chỗ nào đã hứa thì phải giữ', KHÔNG nói 'mọi chỗ đều phải có
|
|
262
|
+
hợp đồng'"*.
|
|
263
|
+
|
|
264
|
+
**Phép so — MỘT chiều:**
|
|
265
|
+
|
|
266
|
+
1. Gom mọi test-id được nhắc trong các `*.Test.md` của UC này.
|
|
267
|
+
2. Gom id ở §4.5.6 (block `active_platform`), **lọc theo cột "Serves SC"** khớp SC của UC này.
|
|
268
|
+
3. `TC ∖ bảng` = id TC nhắc mà hợp đồng không có → ⚠️.
|
|
269
|
+
|
|
270
|
+
| Kết quả | Làm gì |
|
|
271
|
+
|---|---|
|
|
272
|
+
| Rỗng | **im lặng, đi tiếp** |
|
|
273
|
+
| Có id lạ | `⚠️ Hợp đồng test-id: {k} id trong .Test.md không có ở §4.5.6: {danh sách}`<br/>kèm: `Chạy /map-testids {UC-ID} (đưa vào hợp đồng) hoặc /qc-design-test {UC-ID} (sửa TC theo hợp đồng).` |
|
|
274
|
+
|
|
275
|
+
**Chiều ngược KHÔNG kiểm.** §4.5.6 có id mà không TC nào nhắc là **bình thường** — không phải element
|
|
276
|
+
nào cũng cần một bước TC. Kiểm chiều đó là **ồn**, đúng bất đối xứng T17/T18 đã chọn.
|
|
277
|
+
|
|
278
|
+
**Mức là `warn`, không chặn.** Id sai làm test **ĐỎ**, không làm test **xanh giả** — nên nó **không**
|
|
279
|
+
thuộc lớp *"báo cáo sai"* và không đi cùng đường với `steps/qc-stamp.md` §2 (chặn `pass`). Chặn ở đây
|
|
280
|
+
là chặn nhầm loại.
|
|
281
|
+
|
|
282
|
+
> **Vì sao vẫn đáng cảnh báo dù test sẽ tự đỏ.** Vì nó đỏ **đội lốt thứ khác**. `testid_contract` ghi
|
|
283
|
+
> đúng chữ: *"test đỏ 'element not found' **trông y hệt bug sản phẩm**"* — nên `/qc-report` phân loại
|
|
284
|
+
> *product-gap* rồi in một `/report-bug` sẵn-chạy, và một lỗi **hợp đồng nội bộ** đi ra khỏi đội QC
|
|
285
|
+
> thành phiếu lỗi gửi PO. Dòng ⚠️ này là chỗ duy nhất chặn được chuyến đi đó.
|
|
286
|
+
|
|
287
|
+
## Output — lệnh này ghi những gì, ở đâu
|
|
288
|
+
|
|
289
|
+
| Đường dẫn | Nội dung | Người sửa tay? |
|
|
290
|
+
|---|---|:---:|
|
|
291
|
+
| `tests/<project>/test_<feature>.py` | pytest script, 1-1 với `test-cases/` | ✅ **có** — `script-bug` |
|
|
292
|
+
| `pages/<feature>_page.py` | Page Object (3 lớp + BasePage) | ✅ **có** — `script-bug` |
|
|
293
|
+
| `{paths.trace_dir}/…/{UC-ID}-{platform}.tsv` | `qc_status` · `qc_run_at` | ❌ lệnh ghi |
|
|
294
|
+
|
|
295
|
+
*(Đường dẫn theo `modules/qc-playwright/stack-profile.yaml` §layout.)*
|
|
296
|
+
|
|
297
|
+
> **Vì sao mục này tồn tại** *(G77)*. Trước đó lệnh **không có §Output** — nó không khai mình ghi
|
|
298
|
+
> cái gì, ở đâu; đường dẫn chỉ nằm trong `stack-profile.yaml` của module. Và một lệnh **không khai
|
|
299
|
+
> mình ghi gì** thì không ai nghĩ tới việc **bảo vệ thứ nó ghi**. Cột *"người sửa tay?"* là câu hỏi
|
|
300
|
+
> mà `bin/trace-schema.json` → `artifact_writers` giờ ép mọi lệnh phải trả lời.
|
|
301
|
+
|
|
302
|
+
## Chạy lại — script đã tồn tại *(CHẠY, đừng sinh lại)*
|
|
303
|
+
|
|
304
|
+
**Vòng đời của trạm này là CHẠY nhiều, SINH ít.** §Phân loại FAIL ngay dưới đây khai tường minh
|
|
305
|
+
rằng QC **sửa tay** chính các file trên: *"`script-bug` … **QC tự sửa.** Không mở bug"*. Nên chuỗi
|
|
306
|
+
bình thường là **sinh → chạy → đỏ → QC sửa tay → chạy lại**, và bước cuối **không được** là bước xoá.
|
|
307
|
+
|
|
308
|
+
| Tình huống | Xử lý |
|
|
309
|
+
|---|---|
|
|
310
|
+
| Script đã có, TC không đổi | **CHẠY nó. KHÔNG sinh lại.** Không hỏi gì — đây là ca thường nhất |
|
|
311
|
+
| TC mới xuất hiện trong `.Test.md` | Sinh **chỉ phần thiếu**; giữ nguyên file đã có |
|
|
312
|
+
| Script có **thay đổi chưa commit** | **DỪNG** — xem dưới |
|
|
313
|
+
|
|
314
|
+
**Nhận diện "đã sửa tay" bằng `git status` / `git diff`** trên các đường dẫn ở §Output. Không cần ai
|
|
315
|
+
đánh dấu gì, và nó bắt được **mọi** lần sửa — kể cả lần người sửa quên đánh dấu.
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
❌ {n} file script có thay đổi chưa commit — có thể là bản sửa `script-bug` của bạn.
|
|
319
|
+
Sinh lại sẽ XOÁ chúng.
|
|
320
|
+
Files: {danh sách}
|
|
321
|
+
Cách đúng: commit bản sửa trước, rồi chạy lại.
|
|
322
|
+
Cố ý bỏ hết và sinh từ trắng: thêm --force
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**KHÔNG đoán.** Thấy thay đổi chưa commit thì lệnh **không tự phân biệt** *"QC vừa sửa"* với *"lần
|
|
326
|
+
sinh trước chưa commit"* — hai ca đó nhìn giống hệt nhau trên `git status`. **Dừng oan một lần rẻ
|
|
327
|
+
hơn xoá nhầm một lần.**
|
|
328
|
+
|
|
329
|
+
> **Vì sao dùng `git`, không dùng marker hay băm nội dung.** Marker (`# @qc.hand-fixed`) **dựa vào kỷ
|
|
330
|
+
> luật** — đúng thứ đã bác ở G64 (*"dựa vào `Next` là dựa vào kỷ luật, không phải vào máy"*). Băm nội
|
|
331
|
+
> dung **du nhập một lớp cơ chế mới**: `rules/workflow.md` ghi *"0 content hash trong toàn bộ
|
|
332
|
+
> codebase"*, và G63 đã chọn không mở mặt trận đó.
|
|
333
|
+
>
|
|
334
|
+
> **Vì sao ca "không đổi gì" phải HOÀN TOÀN im lặng.** Chạy lại test là việc làm hàng chục lần một
|
|
335
|
+
> ngày. Bản vá nào làm nó hỏi mỗi lần thì người dùng sẽ thêm `--force` vào alias — và lúc đó ta mất
|
|
336
|
+
> sạch lớp bảo vệ này.
|
|
337
|
+
|
|
338
|
+
Có `--force` → report **bắt buộc** khai:
|
|
339
|
+
```
|
|
340
|
+
⚠️ --force: sinh lại từ trắng — đã xoá {n} file script có thay đổi chưa commit: {danh sách}
|
|
341
|
+
```
|
|
342
|
+
|
|
228
343
|
## Trace tag (bắt buộc)
|
|
229
344
|
|
|
230
345
|
Mỗi pytest test được sinh ra mang scenario framework mà nó verify, lấy từ
|
|
@@ -235,6 +350,60 @@ Trace matrix của `.Test.md`:
|
|
|
235
350
|
def test_TC_<FEATURE>_001_...(...): ...
|
|
236
351
|
```
|
|
237
352
|
|
|
353
|
+
## Chạy lại trước khi kết luận — rồi người xác nhận *(bắt buộc cho MỌI FAIL)*
|
|
354
|
+
|
|
355
|
+
Một test đỏ **một lần** chưa nói được nó đỏ vì cái gì. Trước khi phân loại, phải loại nhiễu.
|
|
356
|
+
|
|
357
|
+
### Bước 1 — Chạy lại riêng test đó, tối đa 2 lần
|
|
358
|
+
|
|
359
|
+
```
|
|
360
|
+
đỏ → đỏ → đỏ ⇒ NHẤT QUÁN → sang Bước 2, phân loại script-bug | product-gap
|
|
361
|
+
đỏ → xanh ⇒ KHÔNG NHẤT QUÁN → flaky
|
|
362
|
+
đỏ → đỏ → xanh ⇒ KHÔNG NHẤT QUÁN → flaky
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
**Đây KHÔNG phải `retries` trong config của test runner.** `retries` tự thử lại rồi báo
|
|
366
|
+
*"passed on retry"* — nó **che** sự không nhất quán. Ở đây chạy **tách biệt từng lần** để
|
|
367
|
+
**quan sát**, vì chính sự không nhất quán mới là thông tin cần.
|
|
368
|
+
|
|
369
|
+
### Bước 2 — Điều tra bằng bằng chứng, KHÔNG đoán
|
|
370
|
+
|
|
371
|
+
Đọc trace/video/log: timeline, DOM snapshot, network, console **tại thời điểm fail**. Rồi chọn
|
|
372
|
+
một trong ba:
|
|
373
|
+
|
|
374
|
+
| Nhãn | Khi nào | Hệ quả |
|
|
375
|
+
|---|---|---|
|
|
376
|
+
| `script-bug` | Sai locator / logic test / timing / dữ liệu test | QC tự sửa. **Không** mở bug |
|
|
377
|
+
| `product-gap` | Hành vi thật ≠ spec (PRD/BDD/AC) — defect thật | Mở bug qua `/report-bug`. Giữ `fail` + evidence, **không bao giờ fake-pass** |
|
|
378
|
+
| `flaky` | Không nhất quán ở Bước 1 — **chưa đủ căn cứ** | Cách ly + **ghi nghi vấn nguyên nhân**. **Không** mở bug từ một lần chạy hên xui |
|
|
379
|
+
|
|
380
|
+
### Bước 3 — Người xác nhận TRƯỚC khi hành động
|
|
381
|
+
|
|
382
|
+
In đề xuất kèm bằng chứng cụ thể, rồi **dừng chờ**:
|
|
383
|
+
|
|
384
|
+
```
|
|
385
|
+
❓ {UC-ID}-SC{N} FAIL — đề xuất: {script-bug | product-gap | flaky}
|
|
386
|
+
Bằng chứng: {trích từ trace/log — vd "locator #submit-btn không tồn tại, DOM đã đổi id"
|
|
387
|
+
hoặc "lần 1 đỏ, lần 2 xanh — không nhất quán"}
|
|
388
|
+
Xác nhận phân loại này? (script-bug / product-gap / flaky)
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**KHÔNG ghi `qc_status` cho scenario nào còn FAIL chưa được xác nhận phân loại.**
|
|
392
|
+
|
|
393
|
+
**Nếu QC không chắc** giữa `script-bug` và `product-gap` (bằng chứng mơ hồ, hành vi ở biên của
|
|
394
|
+
spec) → **mời Dev cùng xem trace**, đừng tự đoán cho xong.
|
|
395
|
+
|
|
396
|
+
> **Vì sao chỗ này đáng chặn** — trong khi framework vốn đang cố giảm số cổng chặn (G41): vì
|
|
397
|
+
> **hai hướng sai đều không đảo ngược rẻ**. Gắn nhầm `script-bug` cho lỗi sản phẩm thật là
|
|
398
|
+
> **giấu bug** — dev không bao giờ thấy nó. Gắn nhầm `product-gap` cho lỗi test là mở bug sai,
|
|
399
|
+
> hoặc tệ hơn là đi "sửa" một sản phẩm không có lỗi. Đây không phải cổng hỏi cho có; nó là điểm
|
|
400
|
+
> rẽ mà agent **không có quyền tự quyết**.
|
|
401
|
+
>
|
|
402
|
+
> **Vì sao `flaky` không phải thùng rác.** Nhãn này **buộc** ghi nghi vấn nguyên nhân (thường:
|
|
403
|
+
> thiếu cách ly dữ liệu · thiếu `storageState` · race condition thật) và cách ly test, không
|
|
404
|
+
> phải chỉ đánh dấu rồi bỏ qua. Nó là *"chưa kết luận được"*, không phải *"thôi bỏ đi"* — và
|
|
405
|
+
> một test nằm mãi ở `flaky` là một việc còn nợ, nhìn thấy được.
|
|
406
|
+
|
|
238
407
|
## Write Trace State — qc_status (kết quả QC CHÍNH THỨC)
|
|
239
408
|
|
|
240
409
|
Sau khi chạy, cập nhật **sổ của platform đang test** `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv` (`{active_platform}` đã phân giải ở Bước 1 — chính là platform của QC pass này; nếu `domain`/`prd_slug` không phân giải được từ spec target, định vị TSV bằng cách glob `{paths.trace_dir}/**/{UC-ID}-{active_platform}.tsv` — nó được tạo trước đó bởi `/generate-bdd`) — cho mỗi scenario row (khớp
|
|
@@ -242,10 +411,11 @@ Sau khi chạy, cập nhật **sổ của platform đang test** `{paths.trace_di
|
|
|
242
411
|
|
|
243
412
|
| Cột | Giá trị |
|
|
244
413
|
|--------|-------|
|
|
245
|
-
| `qc_status` | Đọc cột `status` của row **TRƯỚC** — xem §Guard ngay dưới bảng. Row `OK`/`GAP`/`UNTRACKED`: `pass` nếu mọi QC test của SC này pass · `fail` nếu có cái fail · `skip` nếu tất cả skip/xfail · `not_run` nếu không QC test nào phủ n
|
|
414
|
+
| `qc_status` | Đọc cột `status` của row **TRƯỚC** — xem §Guard ngay dưới bảng. Row `OK`/`GAP`/`UNTRACKED`: `pass` nếu mọi QC test của SC này pass · `fail` nếu có cái fail **đã xác nhận là `script-bug` hoặc `product-gap`** · `skip` nếu tất cả skip/xfail · `not_run` nếu không QC test nào phủ nó, **hoặc SC chỉ có kết quả `flaky`** (xem hàng dưới). Row **`DRIFT`/`ORPHANED`**: **không bao giờ ghi `pass`** — hạ về `not_run` |
|
|
415
|
+
| `qc_status` khi `flaky` | **`not_run`, KHÔNG phải `fail`.** `fail` ngụ ý *đã có kết luận*; kết quả không nhất quán qua các lần chạy lại nghĩa là **chưa có** kết luận nào. Ghi `fail` ở đây là khẳng định một điều chưa biết — và nó còn đẩy việc sang dev (xem `qc_owner`) trong khi việc đang thuộc QC |
|
|
246
416
|
| `qc_run_at` | hôm nay `YYYY-MM-DD` |
|
|
247
417
|
| `last_updated` | hôm nay `YYYY-MM-DD` |
|
|
248
|
-
| `qc_owner` | **SC đang chờ ai** (view "pending" của PM/PO): `dev` nếu FAIL = product-gap (defect thật → dev fix) · `po` nếu `skip`/`not_run` vì một **`DOC_GAP` 🔴 Blocker đang open** chặn test (PO phải làm rõ PRD/BDD) · `—` nếu `pass`, hoặc FAIL = script-bug (QC tự fix — tạm thời) |
|
|
418
|
+
| `qc_owner` | **SC đang chờ ai** (view "pending" của PM/PO): `dev` nếu FAIL = product-gap (defect thật → dev fix) · `po` nếu `skip`/`not_run` vì một **`DOC_GAP` 🔴 Blocker đang open** chặn test (PO phải làm rõ PRD/BDD) · **`qc` nếu FAIL = flaky** (QC phải điều tra nguyên nhân, **không** phải việc của dev) · `—` nếu `pass`, hoặc FAIL = script-bug (QC tự fix — tạm thời) |
|
|
249
419
|
| `qc_blocked_by` | artifact liên kết: `GAP-{id}` khi bị chặn bởi spec gap (set ở đây) · `BUG-{id}` khi `/report-bug` đã được file cho product-gap (backfill bởi `/report-bug`) · `—` ngược lại |
|
|
250
420
|
|
|
251
421
|
Set `qc_owner`/`qc_blocked_by` cùng với `qc_status`. Khi `pass`, **clear** cả hai về `—` — nhưng **PHẢI chạy §Đóng bug đã verify bên dưới TRƯỚC**, vì `qc_blocked_by` chính là con trỏ tới bug và clear xong là mất đường về.
|
|
@@ -354,6 +524,21 @@ một **mirror tiện lợi cục bộ**. File `trace-report.json` đã merge (c
|
|
|
354
524
|
bên trong từng sub-agent.
|
|
355
525
|
|
|
356
526
|
|
|
527
|
+
## Self-Review *(trước khi in Report)*
|
|
528
|
+
|
|
529
|
+
Theo 3 nhóm ở `{paths.qc_skills_dir}/_shared/self-review-principles.md` — **không chép lại ở đây**.
|
|
530
|
+
|
|
531
|
+
- **Bịa:** mỗi đề xuất phân loại FAIL dựa trên bằng chứng **đọc thật** từ trace/video/log — trích
|
|
532
|
+
được dòng lỗi, tên locator, response code — không phải *"thường thì đây là script-bug"*?
|
|
533
|
+
- **Nhảy bước:** **MỌI** FAIL đã qua chạy lại ×2 trước khi đề xuất — không có cái nào bị bỏ bước
|
|
534
|
+
vì "chắc chắn là gì rồi"? Guard `DRIFT`/`ORPHANED` đã áp cho **từng** row trước khi ghi `pass`,
|
|
535
|
+
không chỉ vài row?
|
|
536
|
+
- **Số liệu:** `{pass}`/`{fail}`/`{skip}`/`{flaky}` = **đúng output của test runner**
|
|
537
|
+
(`--reporter=list` / summary), không phải đếm lại bằng mắt qua context?
|
|
538
|
+
|
|
539
|
+
> Nhóm 3 ở trạm này là chỗ dễ sai nhất: kết quả chạy nằm trong output của runner, nhưng agent đã
|
|
540
|
+
> đọc qua nó một lần rồi **nhớ lại** lúc viết report. Lấy lại từ nguồn, đừng nhớ.
|
|
541
|
+
|
|
357
542
|
## Report
|
|
358
543
|
|
|
359
544
|
**Đọc `.agent/steps/report-footer.md`** và áp đúng khuôn footer trong đó (Status Badge ·
|
|
@@ -361,11 +546,15 @@ Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
|
|
|
361
546
|
|
|
362
547
|
```
|
|
363
548
|
/qc-run-test Report — {UC-ID} ({qc-playwright})
|
|
364
|
-
QC: ✅ {pass} pass | ❌ {fail} fail | ⏭️ {skip} skip (TCs: {total})
|
|
549
|
+
QC: ✅ {pass} pass | ❌ {fail} fail | ⏭️ {skip} skip | 🔄 {flaky} flaky (TCs: {total})
|
|
550
|
+
{chỉ khi có FAIL — ngược lại bỏ}
|
|
551
|
+
Phân loại FAIL (người đã xác nhận): script-bug {a} · product-gap {b} · flaky {c} → cách ly
|
|
552
|
+
{với mỗi flaky: "🔄 {UC-ID}-SC{N} — nghi: {nguyên nhân}" }
|
|
365
553
|
Trace: {paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv updated (qc_status, qc_run_at)
|
|
366
554
|
{chỉ khi có bug đổi trạng thái — ngược lại bỏ}
|
|
367
555
|
🐞 Bugs: {BUG-ID} → 🟢 Closed (verified {UC-ID}-SC{N})
|
|
368
556
|
⚠️ {BUG-ID} giữ 🟢 Open — SC pass nhưng bug chưa có Resolution, kiểm tra lại test
|
|
557
|
+
Self-review: {✅ sạch | ⚠️ {n} điểm cần chú ý — liệt kê}
|
|
369
558
|
Next: /qc-report {UC-ID} ← sinh report + evidence
|
|
370
559
|
/qc-review {UC-ID} ← review script đã sinh trước khi merge
|
|
371
560
|
📊 Living Docs: chạy /validate-traces (hoặc /sync) để push qc_status lên dashboard spec-module.
|