@educa-corp/sdd-framework 0.9.3 → 0.9.5
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/lint-trace.js +230 -2
- package/bin/qc-base-map.json +119 -49
- package/bin/self-check.js +54 -0
- package/bin/trace-schema.json +58 -4
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/generate-bdd.md +1 -0
- package/core/commands/generate-code.md +39 -2
- package/core/commands/generate-tech-docs.md +21 -2
- package/core/commands/map-testids.md +88 -8
- package/core/commands/qc-analyze.md +429 -472
- package/core/commands/qc-design-test.md +251 -207
- package/core/commands/qc-plan.md +97 -197
- package/core/commands/qc-report.md +76 -60
- package/core/commands/qc-review.md +135 -185
- package/core/commands/qc-run-test.md +235 -274
- package/core/commands/review-tech-docs.md +20 -0
- 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 +1 -1
- package/core/rules/data-protection.md +52 -0
- package/core/rules/workflow.md +1 -1
- package/core/skills/qc/_shared/self-review-principles.md +112 -0
- package/core/skills/qc/qa-analyst/DOC_GAP.template.md +1 -1
- package/core/skills/qc/qa-analyst/spec-breakdown.md +2 -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-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 +3 -3
- 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/report-footer.md +3 -3
- package/core/templates/feature.template +1 -0
- package/core/templates/tech-design.template.md +1 -0
- package/docs/02-concepts/pipeline-steps/09-validate-traces.md +1 -1
- package/docs/04-reference/commands.md +1 -1
- package/docs/04-reference/trace-schema.md +39 -1
- package/docs/explain/00-setup-ai-first.md +1 -1
- package/docs/explain/11-map-testids.md +70 -69
- package/docs/plans/qc-implementation-log.md +145 -3
- 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
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# `gap-verify` đủ bộ — bổ vào bản gốc, KHÔNG fork
|
|
2
|
+
|
|
3
|
+
| | |
|
|
4
|
+
|---|---|
|
|
5
|
+
| **Đợt** | 1 — Nền tảng |
|
|
6
|
+
| **Bước** | **6/6** của Đợt 1 — đóng Đợt 1 |
|
|
7
|
+
| **File sửa** | `steps/gap-verify.md` |
|
|
8
|
+
| **File test** | Không có test tự động — xem B4 |
|
|
9
|
+
| **Ngày xong** | 2026-09-11 · commit `8cfe9f3` |
|
|
10
|
+
| **Phụ thuộc** | không có |
|
|
11
|
+
| **Ai dùng nó** | `/qc-analyze` (gọi trực tiếp) **và** `steps/review-fanout.md` ← `/refine-prd` · `/review-context` — **3 lệnh** |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
16
|
+
|
|
17
|
+
## A1 · Vấn đề
|
|
18
|
+
|
|
19
|
+
Có một quy trình soát lại dùng chung: mỗi khi hệ thống phát hiện *"tài liệu thiếu chỗ này"*, quy trình đó bắt phát hiện ấy **tự chứng minh** trước khi được ghi nhận — mở lại tài liệu gốc đọc, đối chiếu, loại những cái bịa.
|
|
20
|
+
|
|
21
|
+
Quy trình đó thiếu **hai phần** so với bản gốc nó được chép ra: một bước chuẩn bị rõ ràng ở đầu, và một bảng soát nhanh ở cuối.
|
|
22
|
+
|
|
23
|
+
Trưởng phòng QC phát hiện đúng chỗ thiếu. Nhưng cách đề xuất bổ sung là **tạo một bản thứ hai** — và bản gốc đang được **ba** công đoạn khác nhau dùng chung.
|
|
24
|
+
|
|
25
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
26
|
+
|
|
27
|
+
> **Như sửa quy trình trong sổ tay công ty.** Thấy thiếu một bước thì **thêm vào quyển sổ
|
|
28
|
+
> chung**, chứ không photo ra một bản riêng cho phòng mình rồi sửa trên đó. Hai quyển sổ
|
|
29
|
+
> **sẽ** khác nhau — không phải nếu, mà là khi — và lúc đó không ai biết quyển nào đúng.
|
|
30
|
+
|
|
31
|
+
Bổ vào bản gốc thì hai phòng còn lại **cũng được nâng cấp**, miễn phí.
|
|
32
|
+
|
|
33
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
34
|
+
|
|
35
|
+
Quy trình soát lại giờ có một bước chuẩn bị rõ ràng ở đầu (mở đúng tài liệu nguồn ra trước, đếm xem có bao nhiêu việc phải soát) và một bảng tích ở cuối để dán ra dùng khi review.
|
|
36
|
+
|
|
37
|
+
Và hai công đoạn khác — `/refine-prd`, `/review-context` — **tự động có hai phần đó**, không phải làm gì thêm.
|
|
38
|
+
|
|
39
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
40
|
+
|
|
41
|
+
- **Fork** — tạo một bản sao riêng của một tài liệu rồi sửa trên bản sao đó.
|
|
42
|
+
- **Finding / gap** — một phát hiện kiểu *"tài liệu chưa nói rõ chỗ này"*.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
> ### ✅ Phép thử người đọc
|
|
47
|
+
>
|
|
48
|
+
> **Đã thử với:** tự soát · **ngày:** 2026-09-11 · **phải giải thích thêm chỗ nào:** chưa thử với người thật.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# PHẦN B — Chi tiết kỹ thuật
|
|
53
|
+
|
|
54
|
+
## B1 · Cách hiển nhiên là gì, và vì sao nó sai
|
|
55
|
+
|
|
56
|
+
### Cách sai thứ nhất — fork, đúng như đề xuất
|
|
57
|
+
|
|
58
|
+
Đề xuất tạo `skills/qc/qa-analyst/gap-verify.md` là bản mở rộng, rồi cho `/qc-analyze` trỏ sang bản mới. Nghe rất hợp lý: bản mở rộng có phần chuyên biệt cho QC (`DOC_GAP.md` · `active_platform` · `GAP-UC{N}-{nnn}`), nên để nó riêng cho sạch.
|
|
59
|
+
|
|
60
|
+
Sai vì bản gốc được dùng ở **nhiều nơi hơn đề xuất tưởng**:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
steps/gap-verify.md
|
|
64
|
+
← commands/qc-analyze.tmpl (gọi trực tiếp)
|
|
65
|
+
← steps/review-fanout.md Phase 2.5
|
|
66
|
+
← qc-analyze · refine-prd · review-context
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Fork sẽ nâng cấp **một** lệnh và **bỏ rơi hai** lệnh kia trên bản cũ. Sáu tháng sau, ai sửa một phép thử ở bản A không biết bản B tồn tại, và `/qc-analyze` với `/refine-prd` áp **hai luật khác nhau cho cùng một việc** — thẩm định phát hiện — **im lặng**, vì `self-check` không so nội dung hai file với nhau.
|
|
70
|
+
|
|
71
|
+
Đây chính là hình dạng lỗi mà cả kỷ luật `upstream_sha` + `bin/qc-base-map.json` của framework được dựng lên để chống. Fork ở đây là tự tạo lại vấn đề đó từ bên trong.
|
|
72
|
+
|
|
73
|
+
### Cách sai thứ hai — copy nguyên phần chuyên biệt QC vào bản gốc
|
|
74
|
+
|
|
75
|
+
Đã quyết không fork thì chép nội dung bản mở rộng vào bản gốc.
|
|
76
|
+
|
|
77
|
+
Sai vì phần chuyên biệt viết cho QC: `DOC_GAP.md`, `active_platform`, định dạng `GAP-UC{N}-{nnn}`. Chép nguyên vào file dùng chung là bắt `/refine-prd` — vốn làm việc với findings YAML, không có `DOC_GAP.md` nào — đọc một quy trình nói về thứ nó không có.
|
|
78
|
+
|
|
79
|
+
Bản gốc **đã có sẵn** mục §Tham số (`FINDINGS` · `EVIDENCE_ROOT` · `VERDICT_FIELD`) đúng để làm việc này. Viết theo tham số, không theo tên cụ thể.
|
|
80
|
+
|
|
81
|
+
### Cách sai thứ ba — bảng soát nhanh chép lại nội dung phía trên
|
|
82
|
+
|
|
83
|
+
Checklist thì phải đủ chi tiết để dùng một mình chứ.
|
|
84
|
+
|
|
85
|
+
Sai vì checklist là **bản rút gọn của nội dung phía trên** — tức một dạng **bản sao**, đúng thứ bước này vừa từ chối ở cách sai thứ nhất. Ai sửa một phép thử ở trên mà quên checklist thì hai chỗ lệch.
|
|
86
|
+
|
|
87
|
+
## B2 · Cách làm đúng
|
|
88
|
+
|
|
89
|
+
**Bổ hai phần vào bản gốc**, viết theo tham số đã có:
|
|
90
|
+
|
|
91
|
+
**① GIAI ĐOẠN 0 — Chuẩn bị**, ba việc theo thứ tự. Đây là **bước hành động**, không phải mô tả — §Tham số nói *có những gì*, mục này nói *phải làm gì với chúng trước khi thẩm định finding đầu tiên*:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
1. Đọc FINDINGS, lấy danh sách cái đang mở
|
|
95
|
+
2. Dựng DOCUMENT_LIST — MỞ FILE NGUỒN THẬT từ EVIDENCE_ROOT
|
|
96
|
+
3. Chốt VERDICT_FIELD, và ĐẾM tổng số finding phải thẩm định
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Bước 2 là chỗ hay bị nhảy cóc nhất**, và có ghi chú riêng: agent đọc lướt danh sách finding rồi thẩm định **dựa trên trí nhớ** đọc spec từ lúc sinh finding, thay vì **mở lại** file thật. Hai việc khác nhau — verify phải là **một lượt đọc độc lập**, không phải một lượt nhớ lại. Đây là chỗ **thi hành** điều cấm ở §Nguyên tắc tối thượng (cấm dùng field evidence của finding làm bằng chứng cho chính nó).
|
|
100
|
+
|
|
101
|
+
**Bước 3 có một việc đề xuất không có:** **đếm** tổng số finding và ghi con số đó lại. Nó là **mẫu số** để biết đã đi hết chưa — thay vì bỏ dở giữa đường mà không ai thấy. Checklist ở cuối có một dòng đối chiếu lại con số này.
|
|
102
|
+
|
|
103
|
+
**② CHECKLIST NHANH**, với hai thứ chặn đúng rủi ro của chính nó:
|
|
104
|
+
|
|
105
|
+
- Mỗi dòng **trỏ về mục gốc** trong ngoặc: `(T1–T6)` · `(AP1–AP6)` · `(Q1–Q3)` · `(GIAI ĐOẠN 0/2/3)`.
|
|
106
|
+
- Một câu ở đầu bảng:
|
|
107
|
+
|
|
108
|
+
> ⚠️ **Bảng này là MỤC LỤC, KHÔNG phải định nghĩa.** Định nghĩa đủ nằm ở các mục phía trên.
|
|
109
|
+
> **Lệch nhau thì mục trên thắng.** Sửa một phép thử ở trên thì sửa dòng tương ứng ở đây —
|
|
110
|
+
> đừng sửa ở đây rồi tưởng đã xong.
|
|
111
|
+
|
|
112
|
+
**Và KHÔNG tạo `skills/qc/qa-analyst/gap-verify.md`.** Khi ốp Đợt 2 (copy skill từ thư mục đề xuất), phải **bỏ file đó khỏi danh sách copy** — dễ sót vì nó nằm lẫn trong `skills/qc/qa-analyst/`.
|
|
113
|
+
|
|
114
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
115
|
+
|
|
116
|
+
**Kiểu hỏng: im lặng** 🔴
|
|
117
|
+
|
|
118
|
+
| | |
|
|
119
|
+
|---|---|
|
|
120
|
+
| Hỏng ở đâu | Hai bản của một cổng thẩm định 200+ dòng, một cho `/qc-analyze`, một cho `/refine-prd` |
|
|
121
|
+
| Ai phát hiện | **Không ai** — `self-check` canh *file được tham chiếu có tồn tại không*, không canh *hai file có cùng nội dung không* |
|
|
122
|
+
| Sau bao lâu | Lần đầu ai đó sửa một phép thử — có thể vài tháng |
|
|
123
|
+
| Vì sao đắt | Khi đó câu hỏi *"framework thẩm định phát hiện theo luật nào?"* **không còn một câu trả lời**. Và hai lệnh sẽ cho hai kết quả khác nhau trên cùng một tập dữ liệu, mà không ai giải thích được vì sao |
|
|
124
|
+
|
|
125
|
+
## B4 · Verify bằng gì
|
|
126
|
+
|
|
127
|
+
| # | Phép thử | Kết quả |
|
|
128
|
+
|---|---|---|
|
|
129
|
+
| 1 | 🔴 Chỉ có **một** bản nguồn: `find . -name "gap-verify.md"` | ✅ `steps/` — hai bản còn lại là `.agent/` và `core/` do build sinh |
|
|
130
|
+
| 2 | Hai phần mới có mặt: `grep -n "^## GIAI ĐOẠN 0\|^## CHECKLIST NHANH" steps/gap-verify.md` | ✅ |
|
|
131
|
+
| 3 | Cả hai nơi tiêu thụ vẫn trỏ đúng | ✅ `qc-analyze.tmpl` + `review-fanout.md` |
|
|
132
|
+
| 4 | `build` · `self-check` · `test` · `lint-trace` | ✅ 212/212 |
|
|
133
|
+
| 5 | Ngân sách không đổi (`steps/` đọc lúc chạy) | ✅ 1208 KB |
|
|
134
|
+
|
|
135
|
+
**Phép thử tay chưa chạy** — phải kiểm **cả hai** nơi tiêu thụ, vì đây là bước đụng file dùng chung:
|
|
136
|
+
|
|
137
|
+
| # | Phép thử | Kết quả mong đợi |
|
|
138
|
+
|---|---|---|
|
|
139
|
+
| 6 | `/qc-analyze {TICKET-ID}` trên UC có gap | Chạy qua GIAI ĐOẠN 0 trước; phần T1–T6 không hồi quy |
|
|
140
|
+
| **7** | 🔴 `/refine-prd {TICKET-ID}` | Cũng chạy được, **và nhận được GIAI ĐOẠN 0 mới** |
|
|
141
|
+
|
|
142
|
+
Phép thử 7 là phép thử quyết định — **nếu fork thì `/refine-prd` sẽ không thay đổi gì**. Nó chứng minh phần lời của quyết định không-fork.
|
|
143
|
+
|
|
144
|
+
## B5 · Bài học
|
|
145
|
+
|
|
146
|
+
- **2026-09-11** — Kế hoạch nói *"2 lệnh tiêu thụ"*; thực tế **3** (`/review-context` cũng dùng qua `review-fanout`). Con số đó làm lập luận chống fork **mạnh hơn**, không yếu đi.
|
|
147
|
+
→ **Bài học chuyển giao được:** trước khi quyết định fork hay bổ sung một file dùng chung, **grep hết consumer, gồm cả consumer gián tiếp qua một file trung gian**. Số lượng consumer thường lớn hơn trí nhớ, và nó là dữ kiện chính của quyết định.
|
|
148
|
+
|
|
149
|
+
- **2026-09-11** — Checklist suýt tái tạo đúng vấn đề mà bước này từ chối: nó **là** một bản sao rút gọn. Chặn bằng hai thứ rẻ (trỏ về mục gốc + câu "mục trên thắng") thay vì bỏ hẳn checklist.
|
|
150
|
+
→ **Bài học chuyển giao được:** khi một thứ **buộc phải** trùng lặp nội dung (mục lục · bảng tóm tắt · checklist), làm rõ **bản nào thắng khi lệch** ngay trong chính nó. Đó là chi phí rẻ nhất để giữ một bản sao hợp pháp không biến thành một nguồn sự thật thứ hai.
|
|
151
|
+
|
|
152
|
+
## B6 · Copy được / không copy được
|
|
153
|
+
|
|
154
|
+
| | |
|
|
155
|
+
|---|---|
|
|
156
|
+
| ✅ **Copy được sang dự án khác** | Nguyên tắc **bổ vào bản gốc thay vì fork**, và cách kiểm trước khi quyết: grep hết consumer kể cả gián tiếp. Cách **tổng quát hoá bằng tham số** để một file dùng chung phục vụ nhiều consumer có dữ liệu khác nhau. Và cách xử lý bản-sao-hợp-pháp: trỏ về mục gốc + khai rõ bản nào thắng |
|
|
157
|
+
| ⚠️ **Chỉ đúng ở đây** | Nội dung T1–T6, 5 anti-pattern, 3 câu hỏi lọc — gắn với việc thẩm định gap tài liệu của pipeline này |
|
|
158
|
+
|
|
159
|
+
## B7 · Link
|
|
160
|
+
|
|
161
|
+
- Kế hoạch trước khi làm: [`../exec-d1-b6-gap-verify-mo-rong.md`](../exec-d1-b6-gap-verify-mo-rong.md)
|
|
162
|
+
- Bước liên quan: [`1-04`](1-04-self-review-dung-chung.md) (`gap-verify` sâu per-finding · self-review rộng per-run — bổ sung nhau, không thay nhau)
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Nhật ký từng bước — đợt đại phẫu phần QC
|
|
3
|
+
updated: 2026-09-11
|
|
4
|
+
format: theo D:\base\sdd-re-engine\docs\build-journal\_TEMPLATE-BUOC.md
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Nhật ký từng bước
|
|
8
|
+
|
|
9
|
+
> **Đây là ghi chép về việc ĐÃ LÀM.** Kế hoạch nằm ở [`../02-lo-trinh.md`](../02-lo-trinh.md),
|
|
10
|
+
> và bản thiết kế trước khi làm của từng bước nằm ở `../exec-d{N}-b{M}-*.md`. Bảy trong mười hai
|
|
11
|
+
> bước **lệch kế hoạch** — file ở đây ghi cái đã xảy ra, file `exec-*` ghi cái đã định làm.
|
|
12
|
+
> Đọc cả hai thì thấy vì sao đổi.
|
|
13
|
+
|
|
14
|
+
Mỗi file có **hai phần cho hai người đọc khác nhau**:
|
|
15
|
+
|
|
16
|
+
- **Phần A** — cho người không làm kỹ thuật (PO · QC · quản lý · dev mới). Đọc xong phần A là
|
|
17
|
+
**dừng được**.
|
|
18
|
+
- **Phần B** — cho người sẽ sửa code. Phần B **không** phải bản chi tiết hoá của phần A; đó là
|
|
19
|
+
một bài viết khác.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Đợt 0 — Hợp đồng test-id
|
|
24
|
+
|
|
25
|
+
Bệnh nền: hợp đồng test-id giữa FE và QC **có** nhưng hỏng 4 chỗ, nên QC không dựa được và phải
|
|
26
|
+
tự dò selector từ DOM. Script vì vậy giòn — dev đổi tên một class là test vỡ, và **không ai báo**.
|
|
27
|
+
|
|
28
|
+
| # | Bước | Kiểu hỏng đã đóng |
|
|
29
|
+
|---|---|---|
|
|
30
|
+
| 1 | [Cho `@trace.testid_attr` một chỗ để ở](0-01-testid-attr-co-cho-o.md) | Im lặng 🔴 |
|
|
31
|
+
| 2 | [Một nguồn duy nhất cho tên thuộc tính](0-02-mot-nguon-cho-testid-attr.md) | Im lặng 🔴 |
|
|
32
|
+
| 3 | [Skill thôi dạy dò DOM trước](0-03-skill-thoi-day-do-dom.md) | Im lặng 🔴 |
|
|
33
|
+
| 4 | [Máy canh bảng hợp đồng (T15/T16 + R8e)](0-04-may-canh-hop-dong.md) | Im lặng 🔴 |
|
|
34
|
+
| 5 | [Dọn nhãn cột và tên mục đã chết](0-05-don-nhan-cot-va-2b.md) | Im lặng 🔴 |
|
|
35
|
+
| 6 | [Chốt hợp đồng TRƯỚC code — mở luồng song song](0-06-hop-dong-truoc-code.md) | Im lặng 🔴 + Bỏ chạy 🟠 |
|
|
36
|
+
|
|
37
|
+
## Đợt 1 — Nền tảng
|
|
38
|
+
|
|
39
|
+
Bệnh nền: sáu trạm QC **không có phép kiểm cơ học nào**. Bỏ sót business rule · scenario chưa có
|
|
40
|
+
test case · fail phân loại sai — cả ba đều xảy ra **im lặng**.
|
|
41
|
+
|
|
42
|
+
| # | Bước | Kiểu hỏng đã đóng |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| 1 | [Guard BR-tag — đối chiếu tag BDD với BR sinh ra](1-01-guard-br-tag.md) | Im lặng 🔴 |
|
|
45
|
+
| 2 | [Guard SC coverage — mọi scenario phải có test case](1-02-guard-sc-coverage.md) | Im lặng 🔴 |
|
|
46
|
+
| 3 | [Fail 3 nhãn + chạy lại ×2 trước khi kết luận](1-03-fail-3-nhan.md) | Nói dối 🔴 |
|
|
47
|
+
| 4 | [Bộ nguyên tắc tự kiểm + ranh giới với Guard](1-04-self-review-dung-chung.md) | Nói dối 🔴 |
|
|
48
|
+
| 5 | [Spec là dữ liệu, không phải mệnh lệnh](1-05-spec-la-du-lieu.md) | Nói dối 🔴 |
|
|
49
|
+
| 6 | [`gap-verify` đủ bộ — bổ vào bản gốc, không fork](1-06-gap-verify-du-bo.md) | Im lặng 🔴 |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Một việc không phải "bước"
|
|
54
|
+
|
|
55
|
+
**Nâng ngưỡng dung lượng lệnh 1200 → 1450 KB** (commit `bb53742`).
|
|
56
|
+
|
|
57
|
+
Có một phép thử canh tổng dung lượng các file lệnh. Nó tồn tại để bắt **một** chuyện: có ai lén
|
|
58
|
+
nhét một đoạn dùng chung to vào mọi lệnh (trước đây từng là ~2069 KB, đã gỡ xuống). Nó **không**
|
|
59
|
+
canh "framework có thêm nội dung mới hay không" — đó là tăng trưởng chính đáng.
|
|
60
|
+
|
|
61
|
+
Mốc: **1177 KB** trước đợt mổ → **1208 KB** sau Đợt 1. Bước `1-03` chạm **đúng 1200** — nếu chưa
|
|
62
|
+
nâng trước thì nó đã đỏ ở giữa một bước nội dung, và người ta sẽ nâng trong lúc gấp, tức **nâng
|
|
63
|
+
mà không ai đọc lý do**. Nên nâng trước một lần, và ghi lý do vào **chính file test** thay vì
|
|
64
|
+
commit message — người đọc rule sáu tháng sau sẽ mở file test, không mở git log.
|
|
65
|
+
|
|
66
|
+
1450 vẫn cách rất xa 2069, nên rule vẫn bắt được đúng lớp lỗi nó sinh ra để bắt.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Bốn lệnh kiểm phải xanh sau mỗi bước
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
node bin/build.js # đúc .tmpl → .md → core/
|
|
74
|
+
node bin/self-check.js # R1–R16 — canh framework tự mâu thuẫn
|
|
75
|
+
node test/run.js # 212 test, gồm ngưỡng dung lượng
|
|
76
|
+
node bin/lint-trace.js # T1–T18 trên dữ liệu thật
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Còn nợ gì — đọc trước khi tin 12 bước này đã xong
|
|
80
|
+
|
|
81
|
+
| | Việc | Vì sao chưa làm |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| 🔴 | **Chưa ai chạy thử trên PRD thật** | Bốn lệnh trên chỉ canh **contract của framework**, không chạy `/qc-analyze` hay `/map-testids` lên một spec thật. Phép thử thật cần một dự án dùng thuộc tính **không** mặc định (`data-test`/`data-qa`) và một màn hình brownfield |
|
|
84
|
+
| 🟠 | Ba câu chèn (prompt injection) chưa kiểm tay | Phải làm trên **sandbox** — không commit một PRD có câu chèn vào git |
|
|
85
|
+
| 🟠 | **Phép thử người đọc chưa làm với người thật** | Mọi file ở đây ghi `tự soát`. Cần một người không làm kỹ thuật đọc Phần A rồi kể lại — xem mục "Phép thử người đọc" trong từng file |
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
buoc: Đợt 0 — Bước 1
|
|
3
|
+
title: Thêm @trace.testid_attr vào header template tech-design
|
|
4
|
+
phu_thuoc: không
|
|
5
|
+
trang_thai: đã làm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Đợt 0 · Bước 1 — Cho `@trace.testid_attr` một chỗ để ở
|
|
9
|
+
|
|
10
|
+
← [`01-checklist.md`](01-checklist.md) · [`02-lo-trinh.md`](02-lo-trinh.md)
|
|
11
|
+
|
|
12
|
+
> ✅ **Đã làm rồi.** File này là bản **ĐỊNH làm**. Cái **ĐÃ xảy ra** — gồm chỗ lệch
|
|
13
|
+
> so với kế hoạch này và lý do — ghi ở [`buoc/0-01-testid-attr-co-cho-o.md`](buoc/0-01-testid-attr-co-cho-o.md).
|
|
14
|
+
|
|
15
|
+
## 1. Vì sao
|
|
16
|
+
|
|
17
|
+
Kể bằng một chuyện có thể xảy ra tuần sau:
|
|
18
|
+
|
|
19
|
+
Dự án FE dùng thuộc tính `data-test` (không phải `data-testid` — nhiều team chọn vậy). QC sinh
|
|
20
|
+
script Playwright, dùng `page.getByTestId('login-submit-btn')`. Playwright mặc định tìm
|
|
21
|
+
`data-testid`, nên nó **không tìm thấy gì cả**. Test đỏ với thông báo:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
Error: locator.click: Timeout 30000ms exceeded.
|
|
25
|
+
Call log: waiting for getByTestId('login-submit-btn')
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Thông báo này **trông y hệt một bug sản phẩm** — "nút submit không hiển thị". QC mở bug, gán
|
|
29
|
+
cho dev. Dev mở app, bấm thử, nút vẫn ở đó. Mất một ngày của hai người, và cuối cùng phát hiện
|
|
30
|
+
chỉ là sai tên thuộc tính.
|
|
31
|
+
|
|
32
|
+
Framework **đã lường trước** chuyện này: có một field tên `@trace.testid_attr` để ghi *tên
|
|
33
|
+
thuộc tính* mà dự án dùng, và `/qc-run-test` đọc nó để cấu hình Playwright trước khi chạy. Cơ
|
|
34
|
+
chế đúng.
|
|
35
|
+
|
|
36
|
+
Nhưng field đó **không có chỗ nào trong template tech-design** — nên trong thực tế không ai
|
|
37
|
+
điền, và mọi lần chạy đều rơi vào nhánh fallback.
|
|
38
|
+
|
|
39
|
+
## 2. Tình trạng hiện tại
|
|
40
|
+
|
|
41
|
+
**Field được khai đầy đủ trong schema** — `bin/trace-schema.json:471-485`:
|
|
42
|
+
|
|
43
|
+
```json
|
|
44
|
+
"name": "@trace.testid_attr",
|
|
45
|
+
"artifact": "tech-design.md",
|
|
46
|
+
"scope": "file",
|
|
47
|
+
"written_by": ["map-testids"],
|
|
48
|
+
"read_by": ["generate-code", "qc-run-test", "qc-design-test"]
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Nhưng template không có nó** — `templates/tech-design.template.md:35-48` liệt kê 12 field, và
|
|
52
|
+
không có `testid_attr`:
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
---
|
|
56
|
+
@trace.id: {TICKET-ID}
|
|
57
|
+
@trace.domain: {domain}
|
|
58
|
+
@trace.prd: {TICKET-ID}
|
|
59
|
+
@trace.ucs: {TICKET-ID}-UC1, {TICKET-ID}-UC2{, …}
|
|
60
|
+
@trace.service: {service — từ header BDD @trace.service}
|
|
61
|
+
@trace.module: {module liên quan — vd dotnet, angular}
|
|
62
|
+
@trace.platforms: {system | web | app | webview | …}
|
|
63
|
+
@trace.bdd_versions: {MAP theo từng platform …}
|
|
64
|
+
@trace.api_source: {existing | —}
|
|
65
|
+
@trace.revision: 1
|
|
66
|
+
@trace.status: draft
|
|
67
|
+
@trace.generated_at: {YYYY-MM-DD}
|
|
68
|
+
---
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Kiểm lại bằng grep: `grep -rn "testid_attr" templates/ steps/ rules/` → **không kết quả**.
|
|
72
|
+
|
|
73
|
+
**`/map-testids` chỉ ghi field này ở một nhánh** — `commands/map-testids.tmpl:58-64` (Step 5):
|
|
74
|
+
|
|
75
|
+
- `:61` — *"Nếu tech-doc **tồn tại** → cập nhật **bảng** §4.5.6 của platform này"* → chỉ nói
|
|
76
|
+
bảng, **không nói header**.
|
|
77
|
+
- `:62` — *"Nếu **chưa** tồn tại (pure brownfield) → ghi một file tối thiểu: header `@trace`
|
|
78
|
+
(gồm `@trace.testid_attr`) + §4.5.6"* → chỉ nhánh này ghi header.
|
|
79
|
+
|
|
80
|
+
Đường đi thường của dự án là: `/generate-tech-docs` tạo tech-doc trước, rồi `/map-testids` chạy
|
|
81
|
+
sau. Tức là **luôn** đi vào nhánh `:61` — nhánh không ghi header.
|
|
82
|
+
|
|
83
|
+
Kết quả: `/qc-run-test` rơi vào nhánh *"Không tìm thấy field"* (`commands/qc-run-test.tmpl:53`),
|
|
84
|
+
in cảnh báo mềm rồi đoán theo platform.
|
|
85
|
+
|
|
86
|
+
## 3. Sẽ đổi thành gì
|
|
87
|
+
|
|
88
|
+
**a) Thêm field vào template**, đặt ngay sau `@trace.platforms` (vì nó là thuộc tính của
|
|
89
|
+
platform client):
|
|
90
|
+
|
|
91
|
+
```
|
|
92
|
+
@trace.platforms: {system | web | app | webview | … — tuỳ thư mục BDD nào tồn tại}
|
|
93
|
+
@trace.testid_attr: {tên thuộc tính chứa test-id của stack client — web data-testid|data-test|data-qa · RN testID · Flutter Key/Semantics(identifier:) · iOS accessibilityIdentifier. Bỏ trống nếu doc chỉ phủ platform `system`.}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**b) Sửa `/map-testids` Step 5** để **cả hai nhánh** đều ghi/làm mới header, không chỉ nhánh
|
|
97
|
+
brownfield. Câu ở `:61` đổi thành đại ý: *cập nhật bảng §4.5.6 của platform này, **và** đảm bảo
|
|
98
|
+
header `@trace` có `@trace.testid_attr` với giá trị vừa phân giải ở Step 0 (`:29`) — thiếu thì
|
|
99
|
+
thêm, lệch thì cảnh báo chứ không im lặng ghi đè.*
|
|
100
|
+
|
|
101
|
+
Vì sao "lệch thì cảnh báo": nếu tech-doc đang ghi `data-testid` mà `/map-testids` phân giải ra
|
|
102
|
+
`data-test`, thì một trong hai sai — có thể FE vừa đổi convention, có thể module khai sai. Ghi
|
|
103
|
+
đè im lặng là làm mất tín hiệu.
|
|
104
|
+
|
|
105
|
+
## 4. Sửa file nào
|
|
106
|
+
|
|
107
|
+
| File | Sửa gì |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `templates/tech-design.template.md` | Thêm 1 dòng `@trace.testid_attr` vào khối `@trace` (sau `:42`) |
|
|
110
|
+
| `commands/map-testids.tmpl` | Step 5 (`:58-64`): nhánh "tech-doc tồn tại" cũng ghi/làm mới header |
|
|
111
|
+
|
|
112
|
+
Không đụng `bin/trace-schema.json` — field đã khai đúng, không cần thêm gì.
|
|
113
|
+
|
|
114
|
+
## 5. Kiểm thế nào để biết đã xong
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
# 1. Field đã có trong template
|
|
118
|
+
grep -n "testid_attr" templates/tech-design.template.md # phải có 1 dòng
|
|
119
|
+
|
|
120
|
+
# 2. Build + máy tự kiểm còn xanh
|
|
121
|
+
node bin/build.js && node bin/self-check.js && node test/run.js
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Kiểm bằng tay trên một PRD web thật:
|
|
125
|
+
|
|
126
|
+
1. Chạy `/generate-tech-docs {TICKET-ID}` → mở tech-doc, header phải có dòng
|
|
127
|
+
`@trace.testid_attr:` (dù còn là placeholder).
|
|
128
|
+
2. Chạy `/map-testids {UC-ID}` → header phải có **giá trị thật** (vd `data-testid`), không còn
|
|
129
|
+
placeholder.
|
|
130
|
+
3. Chạy `/qc-run-test {UC-ID}` → report **không** được in cảnh báo
|
|
131
|
+
`⚠️ Tech-doc thiếu @trace.testid_attr`.
|
|
132
|
+
|
|
133
|
+
Bước 3 là phép thử thật: nếu cảnh báo vẫn hiện, field vẫn chưa tới được nơi cần dùng.
|
|
134
|
+
|
|
135
|
+
## 6. Nếu bỏ qua thì hỏng gì
|
|
136
|
+
|
|
137
|
+
Field vẫn tồn tại trên giấy, vẫn được 3 lệnh khai là đọc, và vẫn không ai điền. `/qc-run-test`
|
|
138
|
+
mãi rơi vào nhánh fallback `:53` → đoán tên thuộc tính theo platform. Đúng với dự án dùng
|
|
139
|
+
`data-testid` mặc định; **trượt 100% locator** với dự án dùng `data-test`/`data-qa`.
|
|
140
|
+
|
|
141
|
+
Và nó hỏng theo kiểu tệ nhất — `commands/qc-run-test.tmpl:55` đã nói thẳng:
|
|
142
|
+
|
|
143
|
+
> *"hỏng **im lặng theo kiểu tệ nhất**: test fail với 'element not found' — trông y hệt một bug
|
|
144
|
+
> sản phẩm, nên QC sẽ đi mở bug thay vì sửa selector."*
|
|
145
|
+
|
|
146
|
+
Ngoài ra Bước 4 (máy canh §4.5.6) có một check phụ thuộc field này, nên bỏ b1 thì b4 mất một
|
|
147
|
+
phần ba giá trị.
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
buoc: Đợt 0 — Bước 2
|
|
3
|
+
title: Chốt một nguồn duy nhất cho @trace.testid_attr
|
|
4
|
+
phu_thuoc: d0-b1
|
|
5
|
+
trang_thai: đã làm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Đợt 0 · Bước 2 — Hai nửa hợp đồng đang đọc hai file khác nhau
|
|
9
|
+
|
|
10
|
+
← [`01-checklist.md`](01-checklist.md) · [`02-lo-trinh.md`](02-lo-trinh.md)
|
|
11
|
+
|
|
12
|
+
> ✅ **Đã làm rồi.** File này là bản **ĐỊNH làm**. Cái **ĐÃ xảy ra** — gồm chỗ lệch
|
|
13
|
+
> so với kế hoạch này và lý do — ghi ở [`buoc/0-02-mot-nguon-cho-testid-attr.md`](buoc/0-02-mot-nguon-cho-testid-attr.md).
|
|
14
|
+
|
|
15
|
+
> **Đây là lỗi nặng nhất của Đợt 0.** Bước 1 cho field một chỗ để ở; bước này đảm bảo hai bên
|
|
16
|
+
> cùng đọc đúng chỗ đó.
|
|
17
|
+
>
|
|
18
|
+
> ⚠️ **Đừng nhầm hai thứ khi làm chung với [`exec-d0-b6`](exec-d0-b6-contract-truoc-code.md):**
|
|
19
|
+
> bước này nói về **TÊN THUỘC TÍNH** (`@trace.testid_attr`, 1 giá trị ở header) — fallback theo
|
|
20
|
+
> `active_module` **vẫn giữ**, kèm cảnh báo mềm. Còn b6 bỏ hẳn nhánh `/generate-code` tự sinh
|
|
21
|
+
> **GIÁ TRỊ id** khi §4.5.6 rỗng. Một cái là tên thuộc tính, một cái là giá trị id — bỏ nhầm là
|
|
22
|
+
> mất fallback đang cần.
|
|
23
|
+
|
|
24
|
+
## 1. Vì sao
|
|
25
|
+
|
|
26
|
+
`@trace.testid_attr` là **hợp đồng giữa hai bên**:
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
FE (/generate-code) ──đọc field──→ gắn thuộc tính này lên element
|
|
30
|
+
QC (/qc-run-test) ──đọc field──→ cấu hình Playwright tìm thuộc tính này
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Hợp đồng chỉ có nghĩa khi **cả hai đọc cùng một bản**. Hiện tại FE đọc bản ở file A, QC đọc bản
|
|
34
|
+
ở file B. Nếu hai bản khác nhau — hoặc một bản không tồn tại — thì FE gắn một kiểu, QC tìm một
|
|
35
|
+
kiểu, và không có gì báo.
|
|
36
|
+
|
|
37
|
+
Chuyện cụ thể: dự án khai `@trace.testid_attr: data-test` trong tech-doc. QC đọc được, cấu hình
|
|
38
|
+
đúng, tìm `data-test`. Còn `/generate-code` không đọc file đó — nó chỉ đọc header `.feature`,
|
|
39
|
+
không thấy gì, nên rơi về mặc định theo module là `data-testid`. FE gắn `data-testid`, QC tìm
|
|
40
|
+
`data-test`. **Không trùng một element nào.**
|
|
41
|
+
|
|
42
|
+
## 2. Tình trạng hiện tại
|
|
43
|
+
|
|
44
|
+
**Nửa QC — đọc từ `tech-design.md`** (`commands/qc-run-test.tmpl:45-46`):
|
|
45
|
+
|
|
46
|
+
> *"**TÊN THUỘC TÍNH test-id: đọc `@trace.testid_attr`, KHÔNG tự suy từ platform.** Đọc
|
|
47
|
+
> `@trace.testid_attr` từ **header tech-doc gộp** (do `/map-testids` ghi — nó đã phân giải một
|
|
48
|
+
> lần cho cả feature). Đây là **nửa QC của contract FE↔QC**: `/generate-code` đọc chính field
|
|
49
|
+
> này để emit thuộc tính lên element."*
|
|
50
|
+
|
|
51
|
+
**Schema cũng nói `tech-design.md`** (`bin/trace-schema.json:473`):
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
"artifact": "tech-design.md",
|
|
55
|
+
"scope": "file",
|
|
56
|
+
"written_by": ["map-testids"]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Nửa FE — đọc từ `.feature`** (`commands/generate-code.tmpl:505`):
|
|
60
|
+
|
|
61
|
+
> *"2. **Emit qua attribute platform** — chọn attribute theo **`active_module`** (bảng dưới,
|
|
62
|
+
> mặc định). Chỉ override khi header `.feature` khai tường minh `@trace.testid_attr={attr}`
|
|
63
|
+
> (hiếm; dùng cho stack lai)."*
|
|
64
|
+
|
|
65
|
+
Ba điểm lệch trong một câu:
|
|
66
|
+
|
|
67
|
+
| | Nửa QC + schema | Nửa FE |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| Field nằm ở đâu | `tech-design.md` | header `.feature` |
|
|
70
|
+
| Vai trò của field | **nguồn chính** — "KHÔNG tự suy từ platform" | **override hiếm** cho stack lai |
|
|
71
|
+
| Nguồn mặc định | không có; thiếu field thì cảnh báo rồi mới fallback | `active_module` |
|
|
72
|
+
|
|
73
|
+
Và điều trớ trêu: `qc-run-test.tmpl:55` có sẵn một đoạn giải thích **vì sao không được suy từ
|
|
74
|
+
platform**:
|
|
75
|
+
|
|
76
|
+
> *"suy từ platform là **phát biểu lại một sự thật đã được ghi ở nơi khác** — đúng lớp lỗi mà
|
|
77
|
+
> `bin/trace-schema.json` sinh ra để chống."*
|
|
78
|
+
|
|
79
|
+
Đó chính xác là điều `/generate-code` đang làm ở nửa bên kia.
|
|
80
|
+
|
|
81
|
+
## 3. Sẽ đổi thành gì
|
|
82
|
+
|
|
83
|
+
**Chốt một nguồn: header `tech-design.md`.** Ba lý do:
|
|
84
|
+
|
|
85
|
+
1. Khớp `bin/trace-schema.json:473` (`artifact: tech-design.md`) — không phải sửa schema.
|
|
86
|
+
2. Khớp `/map-testids`, lệnh **duy nhất** được khai là `written_by` của field.
|
|
87
|
+
3. Đúng cấp phạm vi: thuộc tính test-id là **một** giá trị cho cả PRD/platform, không phải mỗi
|
|
88
|
+
UC một giá trị. `.feature` là cấp UC → đặt field ở đó là mời gọi 5 UC khai 5 giá trị khác
|
|
89
|
+
nhau cho cùng một FE.
|
|
90
|
+
|
|
91
|
+
**Sửa `commands/generate-code.tmpl:505`** thành thứ tự ưu tiên rõ ràng:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
1. Header tech-doc gộp {TICKET-ID}-tech-design.md → @trace.testid_attr ← NGUỒN CHÍNH
|
|
95
|
+
2. Không có field → cảnh báo mềm + fallback theo active_module
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Khuôn cảnh báo dùng lại đúng cái đã có ở `qc-run-test.tmpl:53`, để hai bên nói cùng một giọng:
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
⚠️ Tech-doc thiếu @trace.testid_attr — fallback theo module ({attr mặc định}).
|
|
102
|
+
Nếu FE dùng thuộc tính khác thì MỌI locator của QC sẽ trượt.
|
|
103
|
+
Chạy /map-testids {UC-ID} để ghi field này.
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Bỏ hẳn đường đọc từ header `.feature`?** Không — nhưng hạ nó xuống dưới cùng và ghi rõ nó là
|
|
107
|
+
lối cũ (vd một PRD trải hai platform với hai attribute khác nhau, `.feature` của từng platform
|
|
108
|
+
có thể khai riêng). Điều kiện: nếu **cả hai** nơi đều có mà **lệch nhau** → cảnh báo, ưu tiên
|
|
109
|
+
tech-doc, **không im lặng chọn một bên**.
|
|
110
|
+
|
|
111
|
+
## 4. Sửa file nào
|
|
112
|
+
|
|
113
|
+
| File | Sửa gì |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `commands/generate-code.tmpl` | Mục §Test Selectors ý 2 (`:505`) — đổi thứ tự nguồn; thêm khối cảnh báo mềm |
|
|
116
|
+
| `commands/qc-run-test.tmpl` | Không đổi logic. Chỉ sửa câu ở `:46` nếu cần cho khớp câu chữ mới của `generate-code` |
|
|
117
|
+
| `docs/explain/09-generate-code.md` | Cập nhật ma trận phase (`:67` — dòng `Test Selectors (emit test-id)`) nếu nó mô tả nguồn cũ |
|
|
118
|
+
|
|
119
|
+
Không đụng `bin/trace-schema.json`.
|
|
120
|
+
|
|
121
|
+
## 5. Kiểm thế nào để biết đã xong
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
# 1. generate-code không còn coi .feature là nguồn chính
|
|
125
|
+
grep -n "testid_attr" commands/generate-code.tmpl
|
|
126
|
+
# → phải thấy "tech-design" đứng trước ".feature"
|
|
127
|
+
|
|
128
|
+
# 2. Build + máy tự kiểm
|
|
129
|
+
node bin/build.js && node bin/self-check.js && node test/run.js
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Kiểm bằng tay — **phép thử quyết định** của bước này. Trên một dự án dùng thuộc tính **không**
|
|
133
|
+
mặc định:
|
|
134
|
+
|
|
135
|
+
1. Đặt `@trace.testid_attr: data-test` vào header tech-doc.
|
|
136
|
+
2. Chạy `/generate-code {UC-ID} --phase=ui` → mở code FE vừa sinh, element phải mang
|
|
137
|
+
`data-test="..."`, **không** phải `data-testid="..."`.
|
|
138
|
+
3. Chạy `/qc-run-test {UC-ID}` → script phải cấu hình `testIdAttribute: 'data-test'` và locator
|
|
139
|
+
phải khớp.
|
|
140
|
+
|
|
141
|
+
Nếu bước 2 vẫn ra `data-testid`, tức `/generate-code` chưa đọc đúng nguồn.
|
|
142
|
+
|
|
143
|
+
## 6. Nếu bỏ qua thì hỏng gì
|
|
144
|
+
|
|
145
|
+
Hợp đồng FE↔QC vẫn là hai bản độc lập. Dự án nào dùng `data-testid` mặc định thì **may mà
|
|
146
|
+
trùng**, nên lỗi này ẩn — nó chỉ nổ ở dự án dùng `data-test`/`data-qa`, và nổ dưới dạng "test
|
|
147
|
+
đỏ hàng loạt không rõ lý do".
|
|
148
|
+
|
|
149
|
+
Nặng hơn: Bước 1 (thêm field vào template) sẽ **tạo cảm giác an toàn sai**. Field có mặt, có
|
|
150
|
+
giá trị, ai đọc tech-doc cũng thấy — nhưng `/generate-code` vẫn không dùng nó. Tức là làm b1 mà
|
|
151
|
+
bỏ b2 thì tệ hơn không làm gì: trước đây không ai tưởng field có tác dụng, sau b1 thì mọi người
|
|
152
|
+
tưởng có.
|