@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
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
---
|
|
2
|
+
buoc: Đợt 0 — Bước 6
|
|
3
|
+
title: Chốt contract test-id TRƯỚC code — mở luồng FE ∥ QC song song
|
|
4
|
+
phu_thuoc: d0-b1, d0-b2, d0-b4
|
|
5
|
+
trang_thai: đã làm
|
|
6
|
+
canh_bao: đụng cả nhánh dev, không chỉ nhánh QC
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Đợt 0 · Bước 6 — Hợp đồng đi trước, hai bên tiêu thụ song song
|
|
10
|
+
|
|
11
|
+
← [`01-checklist.md`](01-checklist.md) · [`02-lo-trinh.md`](02-lo-trinh.md) ·
|
|
12
|
+
Mạch hỏi–đáp dẫn tới bước này: [`00-nhat-ky.md` §3](00-nhat-ky.md)
|
|
13
|
+
|
|
14
|
+
> ✅ **Đã làm rồi.** File này là bản **ĐỊNH làm**. Cái **ĐÃ xảy ra** — gồm chỗ lệch
|
|
15
|
+
> so với kế hoạch này và lý do — ghi ở [`buoc/0-06-hop-dong-truoc-code.md`](buoc/0-06-hop-dong-truoc-code.md).
|
|
16
|
+
|
|
17
|
+
> Bước lớn nhất của Đợt 0, và là bước duy nhất **đụng cả nhánh dev** (`/generate-tech-docs`,
|
|
18
|
+
> `/generate-code`, `/review-tech-docs`, `/validate-traces`), không chỉ nhánh QC.
|
|
19
|
+
|
|
20
|
+
## 1. Vì sao
|
|
21
|
+
|
|
22
|
+
### 1.1 FE và QC đang phải xếp hàng, mà lẽ ra không cần
|
|
23
|
+
|
|
24
|
+
Hôm nay QC phải **chờ code xong** mới dựng được locator, vì hợp đồng test-id chỉ trở nên đáng
|
|
25
|
+
tin sau khi `/map-testids` đối chiếu với code thật. Nhưng nguyên liệu để **đặt tên** test-id thì
|
|
26
|
+
có từ trước code — `commands/map-testids.tmpl:33` nói rõ nguồn của nó:
|
|
27
|
+
|
|
28
|
+
> *"Từ các step `When` trong `.feature` FE của UC **+ các màn Design Spec**, liệt kê mọi element
|
|
29
|
+
> **có action** mà scenario chạm tới (button, input, link, select, toggle, form-submit)."*
|
|
30
|
+
|
|
31
|
+
Cả hai nguồn đều nằm ở phase Tech Design, trước code. Nên việc xếp hàng là **do vị trí lệnh**,
|
|
32
|
+
không do thiếu thông tin.
|
|
33
|
+
|
|
34
|
+
### 1.2 Framework đã khai đúng vị trí — chỉ một tài liệu nói ngược
|
|
35
|
+
|
|
36
|
+
| Nơi | Đặt `/map-testids` ở đâu |
|
|
37
|
+
|---|---|
|
|
38
|
+
| `steps/report-footer.md:63-64` | Phase **Tech Design** (`/generate-tech-docs · /map-testids · /review-tech-docs`), **trước** phase Code |
|
|
39
|
+
| `GAPS-v4.md:724` | trước `/generate-code --phase=ui` |
|
|
40
|
+
| `docs/explain/11-map-testids.md:1,17,69` | **sau** `/generate-code` + `/review-code` ← lệch |
|
|
41
|
+
|
|
42
|
+
Và chính file lệch đó tự thừa nhận (`:60`):
|
|
43
|
+
|
|
44
|
+
> *"vị trí pipeline hơi mờ (giữa Tech Design & Code). Chạy sớm quá thì UI chưa xong, muộn quá
|
|
45
|
+
> thì QC phải chờ."*
|
|
46
|
+
|
|
47
|
+
Bước này **không phát minh kiến trúc mới** — nó chốt mâu thuẫn có sẵn theo hướng đúng, và làm
|
|
48
|
+
cho hướng đó thực sự hoạt động.
|
|
49
|
+
|
|
50
|
+
### 1.3 Ba việc đang bị gộp trong một lệnh
|
|
51
|
+
|
|
52
|
+
| | Việc | Cần code chưa? |
|
|
53
|
+
|---|---|---|
|
|
54
|
+
| 1 | Chốt contract từ design-spec + BDD | **Không** |
|
|
55
|
+
| 2 | FE gắn attribute theo contract (`/generate-code`) | — |
|
|
56
|
+
| 3 | Đối chiếu contract với code thật, patch chỗ thiếu | **Có** |
|
|
57
|
+
|
|
58
|
+
`/map-testids` hiện làm **1 + 3** trộn lẫn, và chính việc 3 kéo cả lệnh xuống sau code. Tách 1
|
|
59
|
+
khỏi 3 là đủ.
|
|
60
|
+
|
|
61
|
+
## 2. Tình trạng hiện tại
|
|
62
|
+
|
|
63
|
+
**Hai lệnh cùng ghi §4.5.6:**
|
|
64
|
+
|
|
65
|
+
- `commands/generate-tech-docs.tmpl:79` — *"thêm sub-block `§4.5.1.x {Screen} — {UC}` + row vào
|
|
66
|
+
§4.5.6 dùng chung của nhóm platform đó"*
|
|
67
|
+
- `commands/map-testids.tmpl:58-64` — Step 5 ghi/làm mới bảng
|
|
68
|
+
|
|
69
|
+
Luật chống giẫm chân là **một câu văn xuôi** (`map-testids.tmpl:62`): *"`/generate-tech-docs` …
|
|
70
|
+
không được ghi đè các id §4.5.6 mà lệnh này đã ghi"* — luật đúng, không ai canh.
|
|
71
|
+
|
|
72
|
+
**`/generate-code` có nhánh tự sinh id** — `commands/generate-code.tmpl:504`:
|
|
73
|
+
|
|
74
|
+
> *"Nếu chưa có (vd `--phase=ui` trước khi §4.5 được vẽ), **sinh id theo quy ước**
|
|
75
|
+
> `{uc-lower}-{screen}-{element}-{type}` … chúng sẽ được đối chiếu với §4.5.6 của tech-design
|
|
76
|
+
> lúc integration."*
|
|
77
|
+
|
|
78
|
+
Nhánh này là chỗ contract **bị code quyết định ngược trở lại**.
|
|
79
|
+
|
|
80
|
+
**`/review-tech-docs` không kiểm §4.5.6** — đã grep: `grep -n "Selector|testid|4.5.6"
|
|
81
|
+
commands/review-tech-docs.tmpl` → 0 kết quả. Nên một tech-doc có §4.5 mà bảng rỗng vẫn được
|
|
82
|
+
`APPROVED`.
|
|
83
|
+
|
|
84
|
+
## 3. Sẽ đổi thành gì
|
|
85
|
+
|
|
86
|
+
### 3.1 Phân vai — một người ghi, bốn người đọc, một người canh
|
|
87
|
+
|
|
88
|
+
| Lệnh | Với §4.5.6 |
|
|
89
|
+
|---|---|
|
|
90
|
+
| `/generate-tech-docs` | Tạo **khung rỗng** + header `@trace.testid_attr`. **Không ghi row nào** |
|
|
91
|
+
| `/map-testids` | **Người duy nhất ghi row.** Nguồn: design-spec + BDD |
|
|
92
|
+
| `/generate-code` | Chỉ **đọc**. **Bỏ hẳn** nhánh tự sinh id (`:504`) — §4.5.6 rỗng giờ là lỗi quy trình |
|
|
93
|
+
| `/qc-design-test`, `/qc-design-script` | Chỉ **đọc** |
|
|
94
|
+
| `/validate-traces` | **Đối chiếu** contract vs code, bật cờ. Không ghi |
|
|
95
|
+
|
|
96
|
+
Phân vai này chỉ sạch được **nhờ** thứ tự mới ở §3.2 — nếu `/map-testids` vẫn chạy sau
|
|
97
|
+
`/generate-code` thì khung rỗng sẽ khiến `/generate-code` bịa id.
|
|
98
|
+
|
|
99
|
+
### 3.2 Thứ tự mới + chuỗi Next rẽ nhánh
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
/generate-design-spec ─┐
|
|
103
|
+
├→ /generate-tech-docs (§4.5 + §4.5.6 khung rỗng)
|
|
104
|
+
/generate-bdd ─────────┘ ↓ Next
|
|
105
|
+
/map-testids (CHỐT contract — không đụng code)
|
|
106
|
+
↓ Next
|
|
107
|
+
/review-tech-docs (duyệt CẢ contract)
|
|
108
|
+
↓ chỉ khi APPROVED
|
|
109
|
+
┌──────────────────┴──────────────────┐
|
|
110
|
+
/generate-code /qc-design-test
|
|
111
|
+
(FE gắn attr từ contract) /qc-design-script
|
|
112
|
+
↓ ↓
|
|
113
|
+
/review-code /qc-review-script
|
|
114
|
+
└──────────────────┬──────────────────┘
|
|
115
|
+
↓
|
|
116
|
+
/qc-run-script ← chỗ ĐẦU TIÊN cần code chạy thật
|
|
117
|
+
↓
|
|
118
|
+
/validate-traces ← đối chiếu contract vs code (việc 3)
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Hai nhánh không đụng nhau vì chúng đọc **cùng một contract đã đóng băng**, không đọc output của
|
|
122
|
+
nhau.
|
|
123
|
+
|
|
124
|
+
Đây cũng là chỗ việc tách `/qc-run-test` ở Đợt 2 trả lãi: `/qc-design-script` (viết script) tách
|
|
125
|
+
khỏi `/qc-run-script` (chạy script) → viết script không cần code chạy. **Trước Đợt 2 thì nhánh
|
|
126
|
+
QC chỉ song song được tới `/qc-design-test`**; sau Đợt 2 mới song song trọn vẹn.
|
|
127
|
+
|
|
128
|
+
### 3.3 Hai chế độ của `/map-testids`
|
|
129
|
+
|
|
130
|
+
Tên lệnh **giữ nguyên** — "lập bản đồ test-id" vẫn đúng nghĩa cho cả hai vai.
|
|
131
|
+
|
|
132
|
+
```
|
|
133
|
+
/map-testids {UC-ID} ← mặc định: chốt contract từ design-spec + BDD.
|
|
134
|
+
KHÔNG đọc code, KHÔNG patch code.
|
|
135
|
+
Actor: người viết tech-doc.
|
|
136
|
+
|
|
137
|
+
/map-testids {UC-ID} --from-code ← thu hoạch contract từ code đã có (brownfield).
|
|
138
|
+
Đọc + patch code như hiện nay.
|
|
139
|
+
Actor: Dev. Chạy MỘT LẦN cho mỗi UC cũ.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Chế độ `--from-code` **không phải viết mới** — nó chính là nhánh `existing`/`reused` đang có
|
|
143
|
+
(`map-testids.tmpl:40`, `:46-52`), chỉ tách ra thành cờ tường minh thay vì để lệnh tự đoán theo
|
|
144
|
+
sự tồn tại của code.
|
|
145
|
+
|
|
146
|
+
Ở chế độ mặc định, mọi element đều là `new` → Step 3 (patch catalog) và Step 4 (patch usage
|
|
147
|
+
site) **bị bỏ qua**, chỉ chạy Step 1, 2, 5.
|
|
148
|
+
|
|
149
|
+
### 3.4 Nhắc việc = điều kiện hoàn chỉnh của tài liệu, không phải thông báo
|
|
150
|
+
|
|
151
|
+
Không dựng cơ chế nhắc. Thay vào đó `/review-tech-docs` **không cho `APPROVED`** khi:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
có block §4.5 cho platform client VÀ §4.5.6 của block đó rỗng
|
|
155
|
+
→ NEEDS_FIX, báo: "Tech-doc có phần UI nhưng chưa khai Test Selectors.
|
|
156
|
+
Chạy /map-testids {UC-ID} rồi review lại."
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Ba lý do cách này đúng hơn một cơ chế nhắc:
|
|
160
|
+
|
|
161
|
+
1. **Không cần actor mới, không thời điểm mới** — `/review-tech-docs` vốn đã chặn:
|
|
162
|
+
`steps/report-footer.md:100` (*"`/generate-code` nếu APPROVED; sửa doc nếu NEEDS_FIX"*).
|
|
163
|
+
2. **Đúng về bản chất** — tech-doc có phần UI mà không khai test selector thì **chưa viết xong**,
|
|
164
|
+
giống như có §4 API mà không khai endpoint.
|
|
165
|
+
3. **Chuỗi Next tự dẫn** — xong `/generate-tech-docs` thì report báo chạy `/map-testids` tiếp.
|
|
166
|
+
Không ai phải nhớ.
|
|
167
|
+
|
|
168
|
+
### 3.5 Hai cờ audit — đối chiếu contract vs code, KHÔNG chặn PR
|
|
169
|
+
|
|
170
|
+
| Cờ | Bật khi | Nghĩa |
|
|
171
|
+
|---|---|---|
|
|
172
|
+
| `TESTID_UNIMPLEMENTED` | §4.5.6 có row · code FE của UC đó đã tồn tại · **không tìm thấy id đó trong code** | FE chưa gắn hoặc gắn sai → QC sẽ trượt locator |
|
|
173
|
+
| `TESTID_UNDECLARED` | Code có element mang test-id **không có** trong §4.5.6 | Ai đó gắn ngoài contract → contract không còn đủ |
|
|
174
|
+
|
|
175
|
+
**Cả hai vào `audit_flags`, KHÔNG vào `gate.blocking`.** Bằng chứng cho thấy an toàn:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
audit_flags = 17 cờ ← báo cáo
|
|
179
|
+
gate.blocking = 4 cờ ← chặn PR: orphaned · trace_orphan · seam_unwired · stub_unresolved
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
13/17 cờ hiện tại **không chặn gì**, kể cả `TECHDOC_DRIFT`, `BDD_DRIFT`. Và `bin/self-check.js`
|
|
183
|
+
rule **R9** báo ERROR nếu *"cờ 🟠 lọt vào `gate.blocking`"* — nên nếu sau này ai lỡ tay đưa cờ
|
|
184
|
+
test-id vào nhóm chặn thì build đỏ, chứ không âm thầm chặn PR cả team.
|
|
185
|
+
|
|
186
|
+
**Phạm vi kiểm neo vào sự tồn tại của contract:**
|
|
187
|
+
|
|
188
|
+
| Tình trạng §4.5.6 | Cờ bật? | Vì sao |
|
|
189
|
+
|---|:---:|---|
|
|
190
|
+
| Không có bảng / bảng rỗng | **Không** | Chưa khai contract nào → không có lời khẳng định nào để sai |
|
|
191
|
+
| Có row, code khớp | Không | Đúng |
|
|
192
|
+
| Có row, code thiếu id | Có (🟠) | Đã khai rồi mà code không làm |
|
|
193
|
+
|
|
194
|
+
Nên dự án đã gen code **trước** đợt điều chỉnh này sẽ **im lặng hoàn toàn** — chúng chưa từng
|
|
195
|
+
chạy `/map-testids` nên §4.5.6 rỗng. Cờ nói *"chỗ nào đã hứa thì phải giữ"*, không nói *"mọi chỗ
|
|
196
|
+
đều phải có hợp đồng"*.
|
|
197
|
+
|
|
198
|
+
### 3.6 `/map-testids` thành invalidator của `qc_status`
|
|
199
|
+
|
|
200
|
+
Khi contract đổi một id, mọi script QC bám id cũ **hết hiệu lực**. Framework đã có luật cho đúng
|
|
201
|
+
chuyện này — `rules/workflow.md:57-64`:
|
|
202
|
+
|
|
203
|
+
> *"lệnh nào làm giá trị đó **HẾT ĐÚNG** (spec đổi, code đổi) thì **BẮT BUỘC** hạ nó về giá trị
|
|
204
|
+
> 'chưa biết' (`not_run` / `—`). Giữ một `pass` đã hết hiệu lực là **báo cáo sai**, không phải
|
|
205
|
+
> tôn trọng quyền sở hữu."*
|
|
206
|
+
|
|
207
|
+
Và đã có tiền lệ thi hành — `bin/trace-schema.json` cột `qc_status`:
|
|
208
|
+
|
|
209
|
+
```json
|
|
210
|
+
"$comment": "Chủ (ghi pass/fail/skip): qc-run-test — DUY NHẤT.
|
|
211
|
+
Invalidator (chỉ hạ về not_run khi spec/code vừa đổi): generate-bdd · generate-code."
|
|
212
|
+
"written_by": ["qc-run-test", "generate-bdd", "generate-code"]
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
Thêm `map-testids` vào danh sách đó, vai **invalidator**. Chuỗi chạy được nhờ cột "Phục vụ SC"
|
|
216
|
+
làm chỉ mục ngược:
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
đổi id ft101-login-submit-btn → ft101-auth-submit-btn
|
|
220
|
+
├─ đọc cột "Phục vụ SC" của row đó → UC1·SC1, UC1·SC3, UC2·SC2
|
|
221
|
+
├─ hạ qc_status 3 SC đó → not_run (qc_run_at → —)
|
|
222
|
+
│ KHÔNG hạ qc_owner/qc_blocked_by — rules/workflow.md:65-67 miễn trừ tường minh
|
|
223
|
+
│ (spec đổi không làm con bug biến mất)
|
|
224
|
+
└─ in cảnh báo: "3 scenario có script bám id cũ — chạy /qc-design-script {UC-ID} lại"
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
QC thấy ở đâu: sổ trace và dashboard hiện 3 SC đó `not_run` thay vì `pass` — **đã là** cách QC
|
|
228
|
+
biết "phải chạy lại" với mọi thay đổi spec khác, không phải học cơ chế mới.
|
|
229
|
+
|
|
230
|
+
Lý lẽ tại sao phải hạ chứ không giữ, `rules/workflow.md:83-84` nói thẳng:
|
|
231
|
+
|
|
232
|
+
> *"`pass` **không** mang nghĩa 'test đã chạy xanh' — nó mang nghĩa 'scenario này đã được nghiệm
|
|
233
|
+
> thu theo spec **hiện tại**'."*
|
|
234
|
+
|
|
235
|
+
### 3.7 Khi contract lệch code — ai đúng
|
|
236
|
+
|
|
237
|
+
`--from-code` phát hiện lệch, agent **đề xuất**, **người xác nhận** (cùng hình dạng với cổng
|
|
238
|
+
phân loại Fail):
|
|
239
|
+
|
|
240
|
+
| Ca | Ai đúng | Xử lý |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| Contract đúng, code gắn sai/thiếu | **Contract** (mặc định) | Patch code — Step 4 đã làm sẵn, chế độ EXTEND |
|
|
243
|
+
| Element thật đã đổi (gộp 2 thành 1, tách 1 thành 2, bỏ hẳn) | **Code** | Cập nhật row §4.5.6 → kéo theo §3.6 |
|
|
244
|
+
|
|
245
|
+
Mặc định contract thắng vì sửa một attribute là thao tác cơ học, an toàn, đảo ngược được; còn
|
|
246
|
+
sửa contract là đổi một tài liệu đã review.
|
|
247
|
+
|
|
248
|
+
### 3.8 Bốn đánh đổi phải nói ra
|
|
249
|
+
|
|
250
|
+
1. **Id đoán từ thiết kế không sống sót 100%** — dev có thể gộp/tách element lúc implement. →
|
|
251
|
+
cờ ở §3.5 là **bắt buộc**, không tuỳ chọn. Contract không có máy đối chiếu sẽ mục trong một
|
|
252
|
+
sprint.
|
|
253
|
+
2. **QC gen được script nhưng script chưa chạy được** — đúng thiết kế. Phải ghi vào tài liệu để
|
|
254
|
+
không ai kỳ vọng test xanh trước khi FE xong.
|
|
255
|
+
3. **Dự án không có design-spec** (chưa có Figma node link) → contract chỉ rút từ step `When` của
|
|
256
|
+
BDD, phủ ít hơn. Cần luật fallback: `/map-testids` phải **ghi rõ** contract là "đầy đủ" hay
|
|
257
|
+
"một phần (thiếu design-spec)", để `/qc-design-script` biết mà cảnh báo.
|
|
258
|
+
4. **Brownfield** → phải chạy `--from-code` một lần trước khi đi tiếp theo chiều xuôi.
|
|
259
|
+
|
|
260
|
+
### 3.9 Tên id nhúng UC — chấp nhận có ý thức
|
|
261
|
+
|
|
262
|
+
Quy ước `{uc-lower}-{screen}-{element}-{type}` cho ra `ft101-login-submit-btn`, trong khi cột
|
|
263
|
+
"Phục vụ SC" cho phép id đó phục vụ `UC1·SC1, UC2·SC2`. Tên nói "của UC1" mà phục vụ nhiều UC.
|
|
264
|
+
|
|
265
|
+
**Không đổi quy ước** — đổi là đổi mọi id đã gắn trong code, đắt hơn giá trị thu được. Thay vào
|
|
266
|
+
đó ghi một câu vào `templates/tech-design.template.md` §4.5.6: *tiền tố UC là UC **đầu tiên**
|
|
267
|
+
giới thiệu element này; nó không có nghĩa element chỉ thuộc UC đó — xem cột "Phục vụ SC" để
|
|
268
|
+
biết phạm vi thật.*
|
|
269
|
+
|
|
270
|
+
## 4. Sửa file nào
|
|
271
|
+
|
|
272
|
+
| File | Sửa gì |
|
|
273
|
+
|---|---|
|
|
274
|
+
| `commands/generate-tech-docs.tmpl` | `:79`, `:225` — thôi ghi row §4.5.6, chỉ tạo khung bảng + header |
|
|
275
|
+
| `commands/map-testids.tmpl` | Đảo trọng tâm sang nguồn design-spec + BDD; thêm cờ `--from-code`; Step 3/4 chỉ chạy ở chế độ đó; thêm phần invalidate `qc_status` (§3.6) |
|
|
276
|
+
| `commands/generate-code.tmpl` | `:504` — **bỏ** nhánh tự sinh id; §4.5.6 rỗng → DỪNG, báo chạy `/map-testids` |
|
|
277
|
+
| `commands/review-tech-docs.tmpl` | Thêm kiểm: có §4.5 client mà §4.5.6 rỗng → `NEEDS_FIX` |
|
|
278
|
+
| `commands/validate-traces.tmpl` | 2 cờ mới + counter ở **cả** Step 7 **lẫn** khối `summary` — rule **R7** canh đủ hai tầng |
|
|
279
|
+
| `bin/trace-schema.json` | `audit_flags` +2 · **không** đụng `gate.blocking` · thêm `map-testids` vào `written_by` của `qc_status` + `qc_run_at`, sửa `$comment` theo khuôn `generate-bdd`/`generate-code` |
|
|
280
|
+
| `templates/tech-design.template.md` | Ghi chú §3.9 vào §4.5.6 |
|
|
281
|
+
| `templates/ci/trace-gate.yml` | Rule **R10** canh file này theo kịp danh sách cờ |
|
|
282
|
+
| `steps/report-footer.md` | `:63` phase · `:99-101` chuỗi Next rẽ hai nhánh song song (§3.2) |
|
|
283
|
+
| `docs/explain/11-map-testids.md` | `:1`, `:17`, `:24`, `:60`, `:63`, `:69` — sửa vị trí pipeline, gỡ câu "vị trí hơi mờ" và "không bắt buộc", đổi mô tả Input |
|
|
284
|
+
| `docs/04-reference/commands.md` | `:77` — actor không còn chỉ là Dev; ghi hai chế độ |
|
|
285
|
+
|
|
286
|
+
⚠️ **Quan hệ với `exec-d0-b5`:** việc "đưa `/map-testids` vào golden path" ở b5 được **thay thế
|
|
287
|
+
hoàn toàn** bởi §3.2 + §3.4 của bước này. Làm b6 thì bỏ phần đó khỏi b5, tránh sửa
|
|
288
|
+
`steps/report-footer.md` hai lần theo hai hướng.
|
|
289
|
+
|
|
290
|
+
⚠️ **Quan hệ với `exec-d0-b2`:** b2 giữ `active_module` làm fallback kèm cảnh báo mềm. Bước này
|
|
291
|
+
**bỏ hẳn** nhánh tự sinh **id**, nhưng fallback cho **tên thuộc tính** (`active_module`) thì
|
|
292
|
+
giữ — hai thứ khác nhau, đừng bỏ nhầm.
|
|
293
|
+
|
|
294
|
+
## 5. Kiểm thế nào để biết đã xong
|
|
295
|
+
|
|
296
|
+
```bash
|
|
297
|
+
node bin/build.js && node bin/self-check.js && node test/run.js && node bin/lint-trace.js
|
|
298
|
+
|
|
299
|
+
# generate-tech-docs không còn ghi row
|
|
300
|
+
grep -n "row vào §4.5.6\|row vào 4.5.6" commands/generate-tech-docs.tmpl # → 0
|
|
301
|
+
|
|
302
|
+
# generate-code không còn tự sinh id
|
|
303
|
+
grep -n "sinh id theo quy ước" commands/generate-code.tmpl # → 0
|
|
304
|
+
|
|
305
|
+
# cờ mới KHÔNG nằm trong gate.blocking
|
|
306
|
+
node -e "const s=require('./bin/trace-schema.json');
|
|
307
|
+
console.log(JSON.stringify(s.gate.blocking.map(g=>g.counter)))"
|
|
308
|
+
# → không có testid_* nào
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Kiểm bằng tay — **ca 3 và 5 là phép thử quyết định**:
|
|
312
|
+
|
|
313
|
+
| Ca | Dựng thế nào | Kỳ vọng |
|
|
314
|
+
|---|---|---|
|
|
315
|
+
| 1 | `/generate-tech-docs` trên PRD có platform web | §4.5.6 có **khung bảng** nhưng **0 row**; header có `@trace.testid_attr` |
|
|
316
|
+
| 2 | `/review-tech-docs` ngay sau đó | **NEEDS_FIX** — báo chưa khai Test Selectors |
|
|
317
|
+
| 3 | `/map-testids {UC-ID}` (mặc định), **chưa có code FE** | Điền row từ design-spec + BDD; **không** đụng file code nào; `/review-tech-docs` giờ `APPROVED` |
|
|
318
|
+
| 4 | `/generate-code {UC-ID} --phase=ui` | Element mang đúng id trong §4.5.6, không bịa id mới |
|
|
319
|
+
| **5** | Song song: `/qc-design-test` + `/qc-design-script` **trước khi** `/generate-code` chạy | Chạy được, script bám id từ §4.5.6 *(cần Đợt 2 mới có `/qc-design-script`; trước đó kiểm tới `/qc-design-test`)* |
|
|
320
|
+
| 6 | `/generate-code` khi §4.5.6 rỗng | **DỪNG**, báo chạy `/map-testids` trước |
|
|
321
|
+
| 7 | Sửa tay một id trong §4.5.6, chạy `/map-testids` | `qc_status` của mọi SC ở cột "Phục vụ SC" hạ về `not_run`; `qc_owner`/`qc_blocked_by` **giữ nguyên** |
|
|
322
|
+
| 8 | Xoá id đó khỏi code FE, chạy `/validate-traces` | Cờ `TESTID_UNIMPLEMENTED` bật, **PR không bị chặn** |
|
|
323
|
+
| 9 | Repo cũ chưa từng chạy `/map-testids` → `/validate-traces` | **0 cờ test-id** (§4.5.6 rỗng → không kiểm) |
|
|
324
|
+
| 10 | `/map-testids {UC-ID} --from-code` trên màn brownfield | Thu hoạch id từ code vào §4.5.6; lệch thì **hỏi người**, không tự chọn bên |
|
|
325
|
+
|
|
326
|
+
Ca 9 là ca bảo vệ dự án đang chạy — nếu nó ra cờ thì cơ chế đang kiểm quá phạm vi.
|
|
327
|
+
|
|
328
|
+
## 6. Nếu bỏ qua thì hỏng gì
|
|
329
|
+
|
|
330
|
+
**Bỏ cả bước:** QC vẫn phải chờ code xong mới dựng locator. Đợt 2 tách `/qc-design-script` khỏi
|
|
331
|
+
`/qc-run-script` sẽ **không thu được lợi ích song song** — tách ra rồi vẫn xếp hàng.
|
|
332
|
+
|
|
333
|
+
**Làm §3.1 (phân vai) mà bỏ §3.2 (đổi thứ tự):** đây là cái **tệ nhất**, và là lỗi mà bản nháp
|
|
334
|
+
đầu của bước này mắc phải. Chuỗi sẽ thành:
|
|
335
|
+
|
|
336
|
+
```
|
|
337
|
+
/generate-tech-docs → §4.5.6 rỗng → /generate-code TỰ BỊA id → /map-testids đi nhặt lại
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
Hợp đồng thành thứ **do code quyết định** — đúng cái đã bị bác ở câu hỏi "trích xuất locator từ
|
|
341
|
+
source code". Hai nửa này phải đi cùng nhau.
|
|
342
|
+
|
|
343
|
+
**Làm §3.2 mà bỏ §3.5 (cờ đối chiếu):** contract chốt từ thiết kế, không ai đối chiếu với thực
|
|
344
|
+
tế → id đoán sai sẽ nằm im trong tech-doc, FE gắn một kiểu, QC tìm một kiểu, và không gì báo
|
|
345
|
+
cho tới khi `/qc-run-script` đỏ hàng loạt ở cuối. Đúng kiểu hỏng im lặng mà cả Đợt 0 tồn tại để
|
|
346
|
+
chống.
|
|
347
|
+
|
|
348
|
+
**Bỏ §3.6 (invalidate `qc_status`):** id đổi mà `qc_status` vẫn `pass` → dashboard báo scenario
|
|
349
|
+
đã nghiệm thu, trong khi script của nó đang định vị một element không còn tồn tại. Đó là **báo
|
|
350
|
+
cáo sai**, đúng nghĩa mà `rules/workflow.md:61` gọi tên.
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
buoc: Đợt 1 — Bước 1
|
|
3
|
+
title: Guard cơ học BR-tag cho /qc-analyze
|
|
4
|
+
phu_thuoc: không
|
|
5
|
+
trang_thai: đã làm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Đợt 1 · Bước 1 — Đối chiếu business rule đã tag trong BDD với BR mà QC sinh ra
|
|
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/1-01-guard-br-tag.md`](buoc/1-01-guard-br-tag.md).
|
|
14
|
+
|
|
15
|
+
## 1. Vì sao
|
|
16
|
+
|
|
17
|
+
`/qc-analyze` đọc PRD + BDD rồi sinh ra danh sách business rule (`BR-01`, `BR-02`…) làm nền cho
|
|
18
|
+
mọi test case về sau. Nếu nó **bỏ sót một rule**, hậu quả đi suốt pipeline: không có BR → không
|
|
19
|
+
có test case → không có test → lỗi thoát ra production. Và không ai biết là đã bỏ sót, vì không
|
|
20
|
+
có gì để so.
|
|
21
|
+
|
|
22
|
+
Nhưng có một chuyện may mắn: **BDD đã tự nói ra một phần đáp án**. Mỗi scenario trong `.feature`
|
|
23
|
+
mang tag `@trace.business_rules` — do `/generate-bdd` ghi khi sinh scenario từ PRD. Ví dụ:
|
|
24
|
+
|
|
25
|
+
```gherkin
|
|
26
|
+
# @trace.scenario: FT-101-UC1-SC3
|
|
27
|
+
# @trace.business_rules: BR-02, BR-07
|
|
28
|
+
Scenario: Đăng nhập sai mật khẩu 3 lần liên tiếp
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Nghĩa là: BDD đang khẳng định scenario này thi hành `BR-02` và `BR-07`. Nếu bản phân tích của QC
|
|
32
|
+
chỉ có `BR-01` đến `BR-05`, thì `BR-07` là **rule mà BDD biết nhưng QC bỏ sót** — và đó là một
|
|
33
|
+
phép **so khớp chuỗi**, máy làm được, không cần ai đọc lại.
|
|
34
|
+
|
|
35
|
+
Đây là điểm khác biệt quan trọng so với self-review: self-review là agent **tự đọc lại bài của
|
|
36
|
+
mình**, nên nó bỏ sót đúng chỗ nó đã bỏ sót lúc viết. Guard đọc **một nguồn khác** (tag trong
|
|
37
|
+
BDD) rồi đối chiếu — không phụ thuộc agent có để ý hay không.
|
|
38
|
+
|
|
39
|
+
## 2. Tình trạng hiện tại
|
|
40
|
+
|
|
41
|
+
`commands/qc-analyze.tmpl:121-126` **có nói** về việc map, nhưng chỉ là chỉ dẫn, không có phép
|
|
42
|
+
kiểm:
|
|
43
|
+
|
|
44
|
+
> **## Trace mapping (bắt buộc)**
|
|
45
|
+
>
|
|
46
|
+
> *"File `.feature` chính thức đã định nghĩa scenario là `@trace.scenario={UC-ID}-SC{N}` với
|
|
47
|
+
> `@trace.business_rules`. Map mọi `BR-xx` / `AC-xx` bạn tạo ra tới `{UC-ID}-SC{N}` sở hữu nó
|
|
48
|
+
> và ghi lại mapping…"*
|
|
49
|
+
|
|
50
|
+
Chú ý hướng của câu này: **"map mọi BR bạn tạo ra tới SC"** — tức là đi từ *BR của QC* → *SC*.
|
|
51
|
+
Chiều ngược lại — *tag trong BDD* → *BR của QC* — **không được kiểm**. Mà chính chiều ngược đó
|
|
52
|
+
mới bắt được cái bỏ sót.
|
|
53
|
+
|
|
54
|
+
Không có gì trong `bin/` kiểm việc này: rule liên quan gần nhất là `T14` của `lint-trace.js`
|
|
55
|
+
(đối chiếu code với sổ trace), không phải BR.
|
|
56
|
+
|
|
57
|
+
## 3. Sẽ đổi thành gì
|
|
58
|
+
|
|
59
|
+
Thêm một mục **`## Guard — BR-tag`** vào `commands/qc-analyze.tmpl`, chạy **sau** khi đã ghi
|
|
60
|
+
`REQUIREMENT_ANALYSIS.md` và **trước** khối CHECKPOINT.
|
|
61
|
+
|
|
62
|
+
Quy trình 4 bước, thuần đếm và so khớp:
|
|
63
|
+
|
|
64
|
+
```
|
|
65
|
+
1. Thu: đọc mọi file .feature của uc_list (đúng active_platform), thu toàn bộ giá trị
|
|
66
|
+
trong tag @trace.business_rules → tập A.
|
|
67
|
+
2. Thu: đọc mọi BR-xx trong REQUIREMENT_ANALYSIS.md vừa ghi → tập B.
|
|
68
|
+
3. So: A \ B = rule mà BDD nhắc mà QC không có.
|
|
69
|
+
4. Xử lý:
|
|
70
|
+
- A \ B rỗng → in "Guard BR-tag: khớp {n}/{n}"
|
|
71
|
+
- A \ B ≠ rỗng → BỔ SUNG NGAY vào REQUIREMENT_ANALYSIS.md (đọc lại PRD để lấy nội dung
|
|
72
|
+
rule đó), rồi in:
|
|
73
|
+
"⚠️ Guard BR-tag: bổ sung {k} rule BDD đã nhắc mà phân tích bỏ sót: {list}"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
**Hai điểm thiết kế cần giữ đúng:**
|
|
77
|
+
|
|
78
|
+
**a) Guard tự sửa, không chỉ tự báo.** Nếu chỉ in cảnh báo rồi để người xử lý thì nó thành một
|
|
79
|
+
dòng nữa để bỏ qua. Agent phải quay lại PRD, tìm rule đó, bổ sung vào bản phân tích — rồi mới
|
|
80
|
+
báo. Người đọc cảnh báo để **biết đã có chuyện gì xảy ra**, không phải để đi làm việc đó.
|
|
81
|
+
|
|
82
|
+
**b) Chiều ngược lại (B \ A) KHÔNG phải lỗi.** QC sinh `BR-09` mà không tag nào trong BDD nhắc
|
|
83
|
+
tới → có thể QC phát hiện một rule mà BDD chưa phủ. Đó là **phát hiện tốt**, không phải sai.
|
|
84
|
+
Ghi nó thành một gap trong `DOC_GAP.md` (BDD thiếu scenario cho rule này), đừng xoá.
|
|
85
|
+
|
|
86
|
+
**In tường minh trong report.** Dòng `Guard BR-tag:` phải xuất hiện trong report cuối, kể cả khi
|
|
87
|
+
sạch. Guard im lặng khi sạch là guard không ai biết có tồn tại — và không ai phát hiện được khi
|
|
88
|
+
nó chết.
|
|
89
|
+
|
|
90
|
+
## 4. Sửa file nào
|
|
91
|
+
|
|
92
|
+
| File | Sửa gì |
|
|
93
|
+
|---|---|
|
|
94
|
+
| `commands/qc-analyze.tmpl` | Thêm mục `## Guard — BR-tag` sau §Trace mapping (`:126`); thêm dòng `Guard BR-tag:` vào khối Report cuối |
|
|
95
|
+
|
|
96
|
+
Nguồn nội dung tham khảo: `D:\base\qcframework_proposal\command\qc-analyze.md` (mục Guard
|
|
97
|
+
tương ứng).
|
|
98
|
+
|
|
99
|
+
Không đụng `bin/` — guard này chạy trong lệnh, không phải rule của lint. *(Có thể cân nhắc đưa
|
|
100
|
+
nó thành rule máy về sau, nhưng nó cần đọc `REQUIREMENT_ANALYSIS.md` — artifact QC — nên chưa
|
|
101
|
+
thuộc phạm vi `lint-trace.js` hiện tại.)*
|
|
102
|
+
|
|
103
|
+
## 5. Kiểm thế nào để biết đã xong
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
node bin/build.js && node bin/self-check.js && node test/run.js
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Kiểm bằng **ca lỗi cố ý** — đây là phép thử thật:
|
|
110
|
+
|
|
111
|
+
1. Chọn một UC có `.feature` với tag `@trace.business_rules` trỏ tới ≥3 rule.
|
|
112
|
+
2. Chạy `/qc-analyze {TICKET-ID}` bình thường → report phải in `Guard BR-tag: khớp N/N`.
|
|
113
|
+
3. **Tạo ca lỗi:** sửa tay `REQUIREMENT_ANALYSIS.md`, xoá một `BR-xx` mà BDD có tag.
|
|
114
|
+
4. Chạy lại `/qc-analyze {TICKET-ID}` → phải in `⚠️ Guard BR-tag: bổ sung 1 rule…` và
|
|
115
|
+
`REQUIREMENT_ANALYSIS.md` phải có lại rule đó **kèm nội dung thật từ PRD**, không phải một
|
|
116
|
+
dòng trống mang tên `BR-xx`.
|
|
117
|
+
|
|
118
|
+
Bước 4 là chỗ dễ làm sai nhất: guard có thể "bổ sung" một dòng rỗng cho đủ số. Phải kiểm nội
|
|
119
|
+
dung.
|
|
120
|
+
|
|
121
|
+
## 6. Nếu bỏ qua thì hỏng gì
|
|
122
|
+
|
|
123
|
+
Bỏ sót business rule ở trạm đầu tiên là loại lỗi **đắt nhất** trong cả pipeline, vì nó nhân lên
|
|
124
|
+
theo từng trạm: không có BR → `/qc-plan` không xếp rủi ro cho nó → `/qc-design-test` không viết
|
|
125
|
+
test case → `/qc-run-script` không có gì để chạy → `/qc-report` báo coverage 100%.
|
|
126
|
+
|
|
127
|
+
Và **coverage 100% ở đây là con số sai theo hướng nguy hiểm nhất**: nó tính trên mẫu số là "số
|
|
128
|
+
scenario đã biết", không phải "số rule cần phủ". Đợt 3 sẽ dựng verdict PASS/FAIL trên chính con
|
|
129
|
+
số đó — nên guard này là điều kiện để con số của Đợt 3 có nghĩa.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
buoc: Đợt 1 — Bước 2
|
|
3
|
+
title: Guard cơ học SC coverage cho /qc-design-test
|
|
4
|
+
phu_thuoc: không
|
|
5
|
+
trang_thai: đã làm
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Đợt 1 · Bước 2 — Mọi scenario phải có ít nhất một test case trỏ tới
|
|
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/1-02-guard-sc-coverage.md`](buoc/1-02-guard-sc-coverage.md).
|
|
14
|
+
|
|
15
|
+
## 1. Vì sao
|
|
16
|
+
|
|
17
|
+
`/qc-design-test` nhận danh sách scenario từ `.feature` rồi viết test case. Câu hỏi đơn giản mà
|
|
18
|
+
hiện không ai trả lời được: **scenario nào chưa có test case nào phủ?**
|
|
19
|
+
|
|
20
|
+
Ví dụ cụ thể. `.feature` của UC1 có 6 scenario:
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
FT-101-UC1-SC1 Đăng nhập thành công
|
|
24
|
+
FT-101-UC1-SC2 Sai mật khẩu
|
|
25
|
+
FT-101-UC1-SC3 Sai mật khẩu 3 lần → khoá tài khoản
|
|
26
|
+
FT-101-UC1-SC4 Đăng nhập bằng OTP
|
|
27
|
+
FT-101-UC1-SC5 Tài khoản chưa kích hoạt
|
|
28
|
+
FT-101-UC1-SC6 Đăng nhập khi hết session
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
QC viết `TC_LOGIN.Test.md` với 11 test case. Trong đó SC5 và SC6 **không có test case nào**
|
|
32
|
+
trace tới — có thể vì bị bỏ sót lúc viết, có thể vì đang chờ PO trả lời một gap. Không ai phát
|
|
33
|
+
hiện. Đến `/qc-report` thì SC5, SC6 nằm im ở `not_run`, và không ai biết là **chưa từng được
|
|
34
|
+
thiết kế**, chứ không phải "đã thiết kế nhưng chưa chạy".
|
|
35
|
+
|
|
36
|
+
Hai trạng thái đó khác nhau hoàn toàn về hành động cần làm — nhưng nhìn vào sổ trace thì giống
|
|
37
|
+
nhau. Guard này là chỗ phân biệt.
|
|
38
|
+
|
|
39
|
+
Đây cũng là **phép đếm thuần**: liệt kê `{UC-ID}-SC{N}` từ `.feature`, đếm số test case có
|
|
40
|
+
`@trace.verifies` trỏ tới từng cái. Không suy luận, không phụ thuộc agent để ý.
|
|
41
|
+
|
|
42
|
+
## 2. Tình trạng hiện tại
|
|
43
|
+
|
|
44
|
+
`commands/qc-design-test.tmpl` **không có phép kiểm nào**. Đã grep xác nhận:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
grep -n "Kiểm tra phủ\|coverage\|Trace SC\|thiếu TC\|SC{N}" commands/qc-design-test.tmpl
|
|
48
|
+
# → chỉ 1 kết quả:
|
|
49
|
+
# :146 - **@trace.verifies:** {UC-ID}-SC{N}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Tức là lệnh **định nghĩa định dạng** của tag `@trace.verifies` (test case phải mang tag gì),
|
|
53
|
+
nhưng **không đếm** xem mọi scenario đã được phủ chưa. Định dạng đúng ≠ phủ đủ.
|
|
54
|
+
|
|
55
|
+
Không có gì trong `bin/` kiểm việc này ở cấp test case: `lint-trace.js` canh sổ `.tsv` và code,
|
|
56
|
+
không mở `.Test.md`.
|
|
57
|
+
|
|
58
|
+
## 3. Sẽ đổi thành gì
|
|
59
|
+
|
|
60
|
+
Thêm mục **`## Guard — SC coverage`** vào `commands/qc-design-test.tmpl`, chạy **sau** khi ghi
|
|
61
|
+
xong file `.Test.md` và **trước** khối CHECKPOINT.
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
1. Thu: liệt kê mọi @trace.scenario trong .feature của UC (đúng active_platform) → tập SC.
|
|
65
|
+
2. Thu: liệt kê mọi giá trị @trace.verifies trong .Test.md vừa ghi → tập V.
|
|
66
|
+
3. Đếm: với mỗi sc ∈ SC, đếm số TC có sc ∈ @trace.verifies.
|
|
67
|
+
4. Xử lý:
|
|
68
|
+
- Mọi SC đều có ≥1 TC → in "Guard SC coverage: khớp {k}/{k}"
|
|
69
|
+
- Có SC chưa phủ → với TỪNG SC thiếu, phải ghi MỘT TRONG HAI:
|
|
70
|
+
(a) viết bổ sung test case ngay, HOẶC
|
|
71
|
+
(b) ghi lý do có căn cứ + tham chiếu gap thật, dạng:
|
|
72
|
+
"⚠️ {UC-ID}-SC{N} — chưa có TC, block bởi GAP-UC1-003 (đang Open)"
|
|
73
|
+
Rồi in: "⚠️ Guard SC coverage: {k-m}/{k} — {m} SC chưa phủ (xem danh sách)"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
> ⚠️ **SỬA LÚC TRIỂN KHAI (2026-09-11) — phần bảng "lý do hợp lệ" dưới đây đã BỊ BỎ.**
|
|
77
|
+
>
|
|
78
|
+
> Bản gốc thiết kế một đường thoát: *SC chưa có TC thì được bỏ qua nếu trỏ tới một `GAP-xx` có
|
|
79
|
+
> thật*. Đọc kỹ lệnh thì đường đó **mâu thuẫn với quy ước đã có** —
|
|
80
|
+
> `commands/qc-design-test.tmpl:137`:
|
|
81
|
+
>
|
|
82
|
+
> > *"Một TC bị block bởi gap **vẫn viết đủ** + `🚫 Block: [GAP-UC{N}-{nnn}](../DOC_GAP.md) — lý do`."*
|
|
83
|
+
>
|
|
84
|
+
> Tức **gap không phải cái cớ để không viết TC — nó là thứ được GHI VÀO TC.** SC bị gap chặn vẫn
|
|
85
|
+
> có TC (mang dấu `🚫 Block`), vẫn được đếm là đã phủ. Thêm đường thoát như bản gốc là dạy hai
|
|
86
|
+
> luật trái nhau trong cùng một lệnh, và cho agent một câu-lý-do-cho-qua.
|
|
87
|
+
>
|
|
88
|
+
> **Bản đã triển khai nghiêm hơn và đơn giản hơn:** chỉ còn hai trạng thái, không cái nào cho
|
|
89
|
+
> phép bỏ trống — SC **trong** phạm vi thì phải có TC (bị chặn thì TC mang `🚫 Block`); SC
|
|
90
|
+
> **ngoài** phạm vi thì `qc-scope` đã lọc ở bước thu, không tính vào mẫu số. Thấy một SC
|
|
91
|
+
> "không thể viết TC" mà không thuộc hai loại đó → **đó là một gap**: mở gap rồi viết TC kèm
|
|
92
|
+
> `🚫 Block` trỏ tới nó.
|
|
93
|
+
>
|
|
94
|
+
> **Phát hiện kèm theo:** report của lệnh đã có sẵn con số `Trace: {N} TC map tới {K}/{total}
|
|
95
|
+
> scenario` từ trước — nhưng **không có phép tính nào sinh ra nó** và `K < total` không dẫn tới
|
|
96
|
+
> việc gì. Guard biến con số đó từ ước lượng thành kết quả của một phép đếm, và thêm hệ quả.
|
|
97
|
+
|
|
98
|
+
*(Bản gốc, giữ để đối chiếu:)* **Điểm thiết kế quan trọng: không cho phép "bỏ qua vô cớ".** Có
|
|
99
|
+
hai lý do hợp lệ để một scenario chưa có test case:
|
|
100
|
+
|
|
101
|
+
| Lý do | Có được chấp nhận? |
|
|
102
|
+
|---|---|
|
|
103
|
+
| Bị chặn bởi một gap `DOC_GAP` đang `Open` — chưa biết hành vi đúng là gì thì không viết được test | ✅ Được, **nhưng phải trỏ tới `GAP-xx` có thật** trong `DOC_GAP.md` |
|
|
104
|
+
| Scenario nằm ngoài phạm vi lần này (UC chưa `approved`, đã lọc bởi `qc-scope`) | ✅ Được, ghi rõ |
|
|
105
|
+
| "Tương tự SC khác nên không cần" | ❌ Không. Nếu thật sự trùng thì `.feature` sai — mở gap, đừng bỏ im lặng |
|
|
106
|
+
| Không lý do | ❌ Không |
|
|
107
|
+
|
|
108
|
+
Yêu cầu "phải trỏ tới `GAP-xx` có thật" là để guard không bị vô hiệu hoá bằng một câu lý do
|
|
109
|
+
chung chung. `DOC_GAP.md` nằm cùng `qc_artifact_dir` nên kiểm được.
|
|
110
|
+
|
|
111
|
+
**In tường minh kể cả khi sạch** — cùng lý do như `exec-d1-b1`: guard im lặng là guard không ai
|
|
112
|
+
biết khi nó chết.
|
|
113
|
+
|
|
114
|
+
## 4. Sửa file nào
|
|
115
|
+
|
|
116
|
+
| File | Sửa gì |
|
|
117
|
+
|---|---|
|
|
118
|
+
| `commands/qc-design-test.tmpl` | Thêm mục `## Guard — SC coverage` trước khối CHECKPOINT; thêm dòng `Guard SC coverage:` vào Report cuối |
|
|
119
|
+
|
|
120
|
+
Nguồn nội dung tham khảo: `D:\base\qcframework_proposal\command\qc-design-test.md` (mục Guard
|
|
121
|
+
tương ứng).
|
|
122
|
+
|
|
123
|
+
## 5. Kiểm thế nào để biết đã xong
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
node bin/build.js && node bin/self-check.js && node test/run.js
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Kiểm bằng ca lỗi cố ý:
|
|
130
|
+
|
|
131
|
+
1. Chọn UC có `.feature` ≥5 scenario. Chạy `/qc-design-test {UC-ID}` → report in
|
|
132
|
+
`Guard SC coverage: khớp K/K`.
|
|
133
|
+
2. **Ca thiếu:** sửa tay `.Test.md`, xoá hết test case trỏ tới một SC → chạy lại → phải in
|
|
134
|
+
`⚠️ Guard SC coverage: (K-1)/K` **và** phải hoặc viết bù test case, hoặc đòi một `GAP-xx`.
|
|
135
|
+
3. **Ca lý do khống:** thử để agent ghi lý do "tương tự SC2" → guard phải **không** chấp nhận,
|
|
136
|
+
phải đòi gap thật hoặc viết test case.
|
|
137
|
+
|
|
138
|
+
Ca 3 là chỗ dễ hỏng: guard viết lỏng thì agent sẽ học cách viết một câu lý do cho qua.
|
|
139
|
+
|
|
140
|
+
## 6. Nếu bỏ qua thì hỏng gì
|
|
141
|
+
|
|
142
|
+
Hai hậu quả, cái sau nặng hơn:
|
|
143
|
+
|
|
144
|
+
**1. Lỗ hổng phủ im lặng.** Scenario không có test case trông giống scenario có test case nhưng
|
|
145
|
+
chưa chạy. Người đọc dashboard không phân biệt được, nên không ai đi lấp.
|
|
146
|
+
|
|
147
|
+
**2. Con số của Đợt 3 mất nghĩa.** `/qc-report` mới tính:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
Design coverage = số SC có ≥1 TC-approved trỏ tới / tổng số SC
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
Đó **chính là** phép đếm mà guard này làm — chỉ khác là guard làm ở **lúc thiết kế** (còn kịp
|
|
154
|
+
sửa), còn `/qc-report` làm ở **cuối** (chỉ còn kịp báo). Không có guard thì Đợt 3 sẽ báo
|
|
155
|
+
"design coverage 68%" ở phút cuối, khi không còn thời gian viết bù 32%. Và cờ `INVERSION` của
|
|
156
|
+
Đợt 3 (design coverage < execution coverage) cũng dựa vào cùng phép đếm này.
|
|
157
|
+
|
|
158
|
+
Nói cách khác: guard này là phiên bản "chặn sớm" của một chỉ số mà Đợt 3 sẽ dùng để ra verdict.
|
|
159
|
+
Làm nó trước là để verdict kia có nghĩa.
|