@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,226 @@
|
|
|
1
|
+
# Chốt hợp đồng TRƯỚC code — mở luồng FE ∥ QC song song
|
|
2
|
+
|
|
3
|
+
| | |
|
|
4
|
+
|---|---|
|
|
5
|
+
| **Đợt** | 0 — Hợp đồng test-id |
|
|
6
|
+
| **Bước** | **6/6** của Đợt 0 — bước lớn nhất, **2 commit** |
|
|
7
|
+
| **File sửa** | **A** `commands/{generate-tech-docs,map-testids,generate-code,review-tech-docs}.tmpl` · `steps/report-footer.md` · `docs/explain/11-map-testids.md`<br>**B** `bin/lint-trace.js` · `bin/trace-schema.json` · `commands/map-testids.tmpl` |
|
|
8
|
+
| **File test** | `test/run.js` — 6 test mới (phần B) |
|
|
9
|
+
| **Ngày xong** | 2026-09-11 · commit `a3a5f30` (A) + `8073507` (B) |
|
|
10
|
+
| **Phụ thuộc** | [`0-01`](0-01-testid-attr-co-cho-o.md) · [`0-02`](0-02-mot-nguon-cho-testid-attr.md) · [`0-04`](0-04-may-canh-hop-dong.md) |
|
|
11
|
+
| **Ai dùng nó** | Cả **nhánh dev** (`generate-tech-docs` · `generate-code` · `review-tech-docs`) lẫn nhánh QC — bước duy nhất của Đợt 0 đụng cả hai |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
16
|
+
|
|
17
|
+
## A1 · Vấn đề
|
|
18
|
+
|
|
19
|
+
Đội làm giao diện và đội kiểm thử **phải xếp hàng**: đội kiểm thử ngồi chờ tới khi màn hình làm xong mới bắt đầu viết được kịch bản kiểm tra, vì trước đó họ không biết phải gọi tên từng nút bằng gì.
|
|
20
|
+
|
|
21
|
+
Nhưng tên các nút thì **hoàn toàn có thể đặt từ bản thiết kế**, trước khi ai gõ dòng code đầu tiên. Việc xếp hàng không phải vì thiếu thông tin — mà vì **thứ tự làm việc đang sai**.
|
|
22
|
+
|
|
23
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
24
|
+
|
|
25
|
+
> **Như đánh số phòng trên bản vẽ trước khi xây.** Có bản vẽ rồi thì thợ xây đi gắn biển số,
|
|
26
|
+
> và **cùng lúc đó** bên nội thất đã đặt được đồ theo số phòng — không ai phải chờ ai.
|
|
27
|
+
> Cả hai đọc **cùng một bản vẽ đã chốt**.
|
|
28
|
+
|
|
29
|
+
Chứ không phải: xây xong rồi mới đi đo từng phòng để đánh số, rồi bên nội thất mới bắt đầu.
|
|
30
|
+
|
|
31
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
32
|
+
|
|
33
|
+
Sau khi duyệt bản thiết kế kỹ thuật, **hai nhánh chạy song song**: bên làm giao diện và bên viết kịch bản kiểm thử, không bên nào chờ bên nào.
|
|
34
|
+
|
|
35
|
+
Và nếu bản thiết kế có phần giao diện mà **chưa điền bảng tên nút**, người duyệt sẽ **không cho qua** — thay vì cho qua rồi mỗi bên tự nghĩ một kiểu.
|
|
36
|
+
|
|
37
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
38
|
+
|
|
39
|
+
- **Bản thiết kế kỹ thuật (tech-doc)** — bản vẽ thi công, chứa cả bảng tên nút.
|
|
40
|
+
- **Song song** — hai việc chạy cùng lúc, không phải một việc chờ việc kia xong.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
> ### ✅ Phép thử người đọc
|
|
45
|
+
>
|
|
46
|
+
> **Đã 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.
|
|
47
|
+
> *(A1 cố ý không nhắc "test-id", "contract", "pipeline" — cả ba đẩy xuống phần B.)*
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# PHẦN B — Chi tiết kỹ thuật
|
|
52
|
+
|
|
53
|
+
## B1 · Cách hiển nhiên là gì, và vì sao nó sai
|
|
54
|
+
|
|
55
|
+
### Cách sai thứ nhất — siết "một người ghi" mà GIỮ NGUYÊN thứ tự
|
|
56
|
+
|
|
57
|
+
Đây là cách tôi **đã đề xuất và đã phải rút lại**, nên nó không phải giả định.
|
|
58
|
+
|
|
59
|
+
Vấn đề nhìn thấy được là *hai lệnh cùng ghi bảng §4.5.6, luật chống giẫm chân chỉ là một câu văn xuôi*. Cách chữa hiển nhiên: `generate-tech-docs` chỉ tạo **khung rỗng**, `/map-testids` là người duy nhất ghi row.
|
|
60
|
+
|
|
61
|
+
Sai vì `/map-testids` đang chạy **sau** `/generate-code`. Chuỗi sẽ thành:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
/generate-tech-docs → §4.5.6 RỖNG
|
|
65
|
+
/generate-code → đọc §4.5.6 thấy rỗng → TỰ SINH id theo quy ước
|
|
66
|
+
/map-testids → đi nhặt lại id mà generate-code vừa bịa
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Hợp đồng thành thứ **do code quyết định** — đúng cái vừa bị bác ở câu hỏi *"có thể trích xuất locator từ source code không?"*. Phân vai đúng, thứ tự sai, và kết quả tệ hơn lúc đầu.
|
|
70
|
+
|
|
71
|
+
**Hai nửa phải đi cùng nhau.** Đây là nội dung chính của B2.
|
|
72
|
+
|
|
73
|
+
### Cách sai thứ hai — chặn cứng `/generate-code` khi bảng rỗng
|
|
74
|
+
|
|
75
|
+
Đã chốt hợp đồng đi trước thì code không có hợp đồng phải dừng. Dứt khoát, dễ hiểu.
|
|
76
|
+
|
|
77
|
+
Sai vì nó **chặn người đang làm việc** — gồm cả những dự án chưa liên quan gì tới đợt mổ này. Người dùng chọn hướng khác: **cảnh báo mạnh rồi để dev quyết**. Và lựa chọn đó chỉ an toàn **vì** phần B có `T18` bắt những id tạm sinh ra ở đó (xem B2).
|
|
78
|
+
|
|
79
|
+
### Cách sai thứ ba — làm 2 cờ audit trong `/validate-traces` như kế hoạch
|
|
80
|
+
|
|
81
|
+
Kế hoạch đề hai **cờ audit**. Nghe hợp lý vì framework đã có 17 cờ như vậy.
|
|
82
|
+
|
|
83
|
+
Sai ở ba điểm: cờ audit do **LLM tự khai** (không xác định, không test được); thêm cờ phải đụng `validate-traces` với counter ở **hai** tầng (rule `R7` canh) **và** `templates/ci/trace-gate.yml` (rule `R10` canh) — cả một vùng rủi ro; và cờ audit **có thể vô tình lọt vào `gate.blocking`** rồi chặn PR cả team.
|
|
84
|
+
|
|
85
|
+
Trong khi `bin/lint-trace.js` **đã có sẵn** hạ tầng quét code (`CODE_DIRS`/`CODE_EXT`/`walk`) mà rule `T14` đang dùng.
|
|
86
|
+
|
|
87
|
+
## B2 · Cách làm đúng
|
|
88
|
+
|
|
89
|
+
### Phần A — thứ tự và phân vai
|
|
90
|
+
|
|
91
|
+
**Framework đã khai đúng vị trí từ trước.** `steps/report-footer.md:63-64`:
|
|
92
|
+
|
|
93
|
+
```
|
|
94
|
+
| Tech Design | /generate-tech-docs · /map-testids · /review-tech-docs |
|
|
95
|
+
| Code | /generate-code · /review-code |
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`/map-testids` nằm ở phase **Tech Design**, trước phase **Code**. `GAPS-v4.md:724` cũng vậy. Chỉ `docs/explain/11-map-testids.md` nói ngược — và chính nó tự thừa nhận *"vị trí pipeline hơi mờ"*. Nên bước này **không phát minh kiến trúc mới**, nó chốt một mâu thuẫn có sẵn theo hướng đúng.
|
|
99
|
+
|
|
100
|
+
Và nguyên liệu đã sẵn: `map-testids.tmpl:33` lấy element từ *"step `When` trong `.feature` + các màn Design Spec"* — cả hai đều có **trước** code.
|
|
101
|
+
|
|
102
|
+
**Thứ tự mới:**
|
|
103
|
+
|
|
104
|
+
```
|
|
105
|
+
/generate-design-spec ─┐
|
|
106
|
+
├→ /generate-tech-docs (§4.5 + §4.5.6 KHUNG RỖNG)
|
|
107
|
+
/generate-bdd ─────────┘ ↓
|
|
108
|
+
/map-testids (CHỐT hợp đồng — KHÔNG đụng code)
|
|
109
|
+
↓
|
|
110
|
+
/review-tech-docs (duyệt CẢ hợp đồng)
|
|
111
|
+
↓ APPROVED
|
|
112
|
+
┌──────────────────┴──────────────────┐
|
|
113
|
+
/generate-code /qc-design-test
|
|
114
|
+
↓ ↓
|
|
115
|
+
/review-code /qc-review-script
|
|
116
|
+
└──────────────────┬──────────────────┘
|
|
117
|
+
/qc-run-script ← chỗ ĐẦU TIÊN cần code chạy thật
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
Hai nhánh không đụng nhau vì đọc **cùng một hợp đồng đã đóng băng**, không đọc output của nhau.
|
|
121
|
+
|
|
122
|
+
**Phân vai — một người ghi, bốn người đọc, một người canh:**
|
|
123
|
+
|
|
124
|
+
| Lệnh | Với §4.5.6 |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `/generate-tech-docs` | Khung rỗng + header placeholder. **Không ghi row** |
|
|
127
|
+
| `/map-testids` | **Người duy nhất ghi row.** Hai chế độ: mặc định (design-spec + BDD, không đụng code) · `--from-code` (brownfield, chạy một lần mỗi UC cũ) |
|
|
128
|
+
| `/generate-code` · `/qc-design-test` | Chỉ **đọc** |
|
|
129
|
+
| `bin/lint-trace.js` | **Canh** |
|
|
130
|
+
|
|
131
|
+
Chế độ `--from-code` **không phải viết mới** — là nhánh `existing`/`reused` đã có (`:34-40`), chỉ tách thành cờ tường minh thay vì để lệnh tự đoán theo sự tồn tại của code.
|
|
132
|
+
|
|
133
|
+
**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.** `/review-tech-docs` **T6** không cho `APPROVED` nếu có §4.5 client mà §4.5.6 rỗng. Ba lý do tốt hơn một cơ chế nhắc: không cần actor mới, không thời điểm mới (`report-footer.md:100` vốn đã chặn) · đúng bản chất — tech-doc có phần UI mà không khai test selector thì **chưa viết xong**, như có §4 API mà không khai endpoint · chuỗi Next tự dẫn.
|
|
134
|
+
|
|
135
|
+
**`/generate-code` khi bảng rỗng — cảnh báo mạnh rồi để người quyết.** In rõ ba hệ quả (QC không bám được · `/map-testids` sau này có thể phải **sửa code** · `T18` sẽ báo), gợi ý cách đúng, rồi hỏi `Y/N`. Chọn `Y` thì report **bắt buộc** ghi `⚠️ {n} test-id TẠM (chưa vào §4.5.6)`. `--yes` coi như `Y` nhưng dòng cảnh báo vẫn bắt buộc.
|
|
136
|
+
|
|
137
|
+
### Phần B — máy canh và làm mất hiệu lực
|
|
138
|
+
|
|
139
|
+
**Hai rule trong `lint-trace --code`, không phải cờ audit:**
|
|
140
|
+
|
|
141
|
+
```
|
|
142
|
+
T17 id đã khai ở §4.5.6 mà KHÔNG có trong code ⚠️ warn
|
|
143
|
+
T18 id trong code mà KHÔNG có trong §4.5.6 nào ⚠️ warn
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
**Cả hai WARN, không ERROR** — id đoán từ thiết kế **không sống sót 100%**: lúc implement, dev có thể gộp hai element thành một component hoặc tách một thành hai. Đây là **nợ cần thấy**, không phải cái sai chặn người.
|
|
147
|
+
|
|
148
|
+
`T18` là **lưới bắt phía sau** cho quyết định ở phần A: chính vì có nó mà `/generate-code` được phép hỏi rồi đi tiếp thay vì chặn cứng.
|
|
149
|
+
|
|
150
|
+
**Bốn điều khoản chống báo oan**, mỗi cái có một test canh: chưa có hợp đồng nào → không kiểm · thiếu `--code` → in `BỎ QUA` tường minh · **code chưa có id nào của thuộc tính đó** (UI chưa viết / `--code` trỏ sai gốc) → im lặng, không phán "FE chưa gắn" cho cả bảng · `@trace.testid_attr` thiếu hoặc còn placeholder → không so, vì **không biết tìm thuộc tính nào thì mọi so sánh là đoán**.
|
|
151
|
+
|
|
152
|
+
**`T18` so với HỢP NHẤT mọi bảng**, không so từng doc: monorepo có nhiều PRD, id của PRD khác nằm trong code là bình thường — so per-doc sẽ báo oan gần hết.
|
|
153
|
+
|
|
154
|
+
**`/map-testids` thành invalidator của `qc_status`.** Đổi một id nghĩa là mọi script QC bám id cũ **hết đúng**. `rules/workflow.md:57-64` đã có luật:
|
|
155
|
+
|
|
156
|
+
> *"lệnh nào làm giá trị đó **HẾT ĐÚNG** thì **BẮT BUỘC** hạ nó về giá trị 'chưa biết'. Giữ một `pass` đã hết hiệu lực là **báo cáo sai**."*
|
|
157
|
+
|
|
158
|
+
Và đã có tiền lệ thi hành: `qc_status.written_by` gồm `generate-bdd` · `generate-code` với vai invalidator. Thêm `map-testids` vào đúng khuôn đó. Chuỗi chạy được nhờ cột **"Phục vụ SC"** làm chỉ mục ngược:
|
|
159
|
+
|
|
160
|
+
```
|
|
161
|
+
đổi id → đọc cột "Phục vụ SC" → hạ qc_status các SC đó về not_run, qc_run_at → —
|
|
162
|
+
KHÔNG đụng qc_owner/qc_blocked_by — rules/workflow.md:65-67 miễn trừ tường minh
|
|
163
|
+
(chúng là con trỏ tới bug; đổi test-id không làm con bug biến mất)
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
167
|
+
|
|
168
|
+
Bước này đóng **hai** kiểu hỏng khác nhau:
|
|
169
|
+
|
|
170
|
+
| Kiểu | Ở đâu |
|
|
171
|
+
|---|---|
|
|
172
|
+
| **Bỏ chạy** 🟠 | QC xếp hàng chờ code. Đúng nhưng chậm — và việc tách `/qc-design-script` khỏi `/qc-run-script` ở Đợt 2 sẽ **không thu được lợi ích song song** nếu thứ tự vẫn sai |
|
|
173
|
+
| **Im lặng** 🔴 | Hợp đồng do **code** quyết định: QC đọc §4.5.6 thấy rỗng nên đi dò DOM, còn FE đã gắn một bộ id không ai biết. Tới lúc "đối chiếu lúc integration" thì cả hai bên đã làm xong theo hai hướng khác nhau |
|
|
174
|
+
|
|
175
|
+
| | |
|
|
176
|
+
|---|---|
|
|
177
|
+
| Ai phát hiện | Kiểu Im lặng: không ai, cho tới khi `/qc-run-script` đỏ hàng loạt ở cuối |
|
|
178
|
+
| Sau bao lâu | Cuối chu kỳ — chỗ đắt nhất để phát hiện |
|
|
179
|
+
| Nguy hiểm nhất | Làm phần A **mà bỏ phần B**: contract chốt từ thiết kế, không ai đối chiếu với thực tế → id đoán sai nằm im trong tech-doc, và không gì báo |
|
|
180
|
+
|
|
181
|
+
## B4 · Verify bằng gì
|
|
182
|
+
|
|
183
|
+
**6 test tự động** cho phần B:
|
|
184
|
+
|
|
185
|
+
| # | Phép thử | Kết quả |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| 1 | Bảng khớp code | ✅ không T17/T18 |
|
|
188
|
+
| 2 | Bảng có id mà code không có | ✅ **T17**, exit 0 (warn, không chặn) |
|
|
189
|
+
| 3 | Code có id mà bảng không có | ✅ **T18** |
|
|
190
|
+
| 4 | 🔴 Code chưa có id nào (UI chưa viết) | ✅ **im lặng** — không phán "FE chưa gắn" cho cả bảng |
|
|
191
|
+
| 5 | 🔴 Thiếu `--code` | ✅ in *"T17/T18 BỎ QUA"*, không im lặng báo sạch |
|
|
192
|
+
| 6 | 🔴 Header còn placeholder | ✅ **không so code** — không biết attr thì mọi so sánh là đoán |
|
|
193
|
+
|
|
194
|
+
**Chín phép thử tay chưa chạy** — cần PRD thật. Ca **3** và **5** là phép thử quyết định:
|
|
195
|
+
|
|
196
|
+
| # | Phép thử | Kết quả mong đợi |
|
|
197
|
+
|---|---|---|
|
|
198
|
+
| 7 | `/generate-tech-docs` | §4.5.6 có **khung** nhưng **0 row** |
|
|
199
|
+
| 8 | `/review-tech-docs` ngay sau | **NEEDS_FIX** |
|
|
200
|
+
| **9** | `/map-testids {UC}` khi **chưa có code FE** | Điền row từ design-spec+BDD; **không đụng file code nào** |
|
|
201
|
+
| 10 | `/generate-code --phase=ui` | Element mang đúng id trong bảng, không bịa |
|
|
202
|
+
| **11** | `/qc-design-test` chạy **song song**, trước khi `/generate-code` xong | Chạy được, bám id từ §4.5.6 |
|
|
203
|
+
| 12 | `/generate-code` khi bảng rỗng | Cảnh báo + hỏi; chọn `Y` thì report ghi rõ id là **TẠM** |
|
|
204
|
+
| 13 | Sửa tay một id, chạy `/map-testids` | `qc_status` các SC ở cột "Phục vụ SC" → `not_run`; `qc_owner`/`qc_blocked_by` **giữ nguyên** |
|
|
205
|
+
| 14 | Repo cũ chưa từng chạy `/map-testids` | **0 cờ test-id** |
|
|
206
|
+
|
|
207
|
+
## B5 · Bài học
|
|
208
|
+
|
|
209
|
+
- **2026-09-11** — Bản đề xuất "siết một người ghi" của tôi **có lỗ**, và lỗ đó chỉ lộ ra khi người dùng nói *"từ UI đã có thể đặt ra test-id rồi, tôi hướng tới việc chốt locator trước bước sinh code để FE và QC chạy song song"*. Tôi đã phản đối hướng đó vì sợ `/generate-code` tự bịa id — mà lỗ ấy **chỉ tồn tại nếu `/map-testids` chạy sau code**. Câu hỏi của người dùng sửa luôn lỗ trong đề xuất của tôi.
|
|
210
|
+
→ **Bài học chuyển giao được:** khi một cách chữa "đúng về phân vai" mà vẫn thấy sai ở đâu đó, hãy kiểm **thứ tự thực thi** trước khi kết luận cách chữa đó hỏng. Phân vai và thứ tự là hai trục độc lập; sai một trục không có nghĩa trục kia sai.
|
|
211
|
+
|
|
212
|
+
- **2026-09-11** — Đề xuất "cờ nhắc chạy `/map-testids`" của tôi **đặt sai chỗ**: cờ audit đọc code, mà ở thời điểm cần nhắc thì **chưa có code**. Lỗi lộ ra từ câu hỏi *"nhắc cho ai, khi dev chưa code?"*. Cách đúng hoá ra không phải nhắc ai cả — mà biến nó thành **điều kiện hoàn chỉnh của tài liệu**, kiểm ở đúng lúc đã có người ngồi soát tài liệu đó.
|
|
213
|
+
→ **Bài học chuyển giao được:** trước khi thiết kế một cơ chế nhắc việc, hỏi *"ở thời điểm cần nhắc, dữ liệu để phát hiện đã tồn tại chưa?"*. Nếu chưa, cơ chế đó sai tầng — đi tìm một cổng đã có sẵn ở đúng thời điểm.
|
|
214
|
+
|
|
215
|
+
## B6 · Copy được / không copy được
|
|
216
|
+
|
|
217
|
+
| | |
|
|
218
|
+
|---|---|
|
|
219
|
+
| ✅ **Copy được sang dự án khác** | **Chốt hợp đồng ở tầng thiết kế để hai bên tiêu thụ song song** — áp được cho mọi cặp producer/consumer phải khớp nhau (API contract, schema sự kiện, tên cột). Nguyên tắc: hợp đồng phải đi **trước** cả hai bên, không được để một bên vừa dùng vừa định nghĩa. **Nhắc việc = điều kiện hoàn chỉnh của artifact**, kiểm ở cổng review đã có, thay vì dựng cơ chế thông báo mới. Và: cho phép "đi tiếp có cảnh báo" **chỉ khi** có một lưới bắt phía sau |
|
|
220
|
+
| ⚠️ **Chỉ đúng ở đây** | Thứ tự lệnh cụ thể, cấu trúc `§4.5.x`, và việc `qc_status` có cơ chế invalidator sẵn — đều gắn với framework này |
|
|
221
|
+
|
|
222
|
+
## B7 · Link
|
|
223
|
+
|
|
224
|
+
- Kế hoạch trước khi làm: [`../exec-d0-b6-contract-truoc-code.md`](../exec-d0-b6-contract-truoc-code.md)
|
|
225
|
+
- Mạch hỏi–đáp dẫn tới bước này: [`../00-nhat-ky.md` §3.2 và §3.3](../00-nhat-ky.md)
|
|
226
|
+
- Bước liên quan: [`0-04`](0-04-may-canh-hop-dong.md) (T15/T16 + R8e tự canh luôn T17/T18) · [`0-05`](0-05-don-nhan-cot-va-2b.md) (phần "golden path" chuyển từ đó sang đây)
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Guard BR-tag — đối chiếu tag trong BDD với luật nghiệp vụ sinh ra
|
|
2
|
+
|
|
3
|
+
| | |
|
|
4
|
+
|---|---|
|
|
5
|
+
| **Đợt** | 1 — Nền tảng |
|
|
6
|
+
| **Bước** | **1/6** của Đợt 1 |
|
|
7
|
+
| **File sửa** | `commands/qc-analyze.tmpl` |
|
|
8
|
+
| **File test** | Không có test tự động — xem B4 |
|
|
9
|
+
| **Ngày xong** | 2026-09-11 · commit `20d177b` |
|
|
10
|
+
| **Phụ thuộc** | không có |
|
|
11
|
+
| **Ai dùng nó** | `/qc-analyze` — trạm đầu tiên của pipeline QC |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
16
|
+
|
|
17
|
+
## A1 · Vấn đề
|
|
18
|
+
|
|
19
|
+
Trạm đầu tiên của kiểm thử đọc tài liệu yêu cầu rồi liệt kê ra **các luật nghiệp vụ** — *"sai mật khẩu 3 lần thì khoá tài khoản"*, kiểu vậy. Mọi việc phía sau dựa trên danh sách đó.
|
|
20
|
+
|
|
21
|
+
Bỏ sót một luật thì **không có gì báo**. Không có luật → không ai xếp rủi ro cho nó → không ai viết kịch bản kiểm tra → không có gì để chạy → và báo cáo cuối cùng ghi *"đã kiểm 100%"*.
|
|
22
|
+
|
|
23
|
+
Con số 100% đó tính trên **những thứ đã biết**, không phải trên **những thứ cần kiểm**. Nên nó sai theo hướng nguy hiểm nhất: **trông như đã xong**.
|
|
24
|
+
|
|
25
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
26
|
+
|
|
27
|
+
> **Như đối chiếu hai bản danh sách khách mời.** Cô dâu có một bản, chú rể có một bản. Không ai
|
|
28
|
+
> nhớ hết — nhưng **đặt hai bản cạnh nhau** thì tên nào có ở bản này mà thiếu ở bản kia **hiện ra ngay**.
|
|
29
|
+
|
|
30
|
+
Ở đây bản thứ hai đã có sẵn: khi viết kịch bản nghiệp vụ, hệ thống đã **ghi kèm** luật nào thuộc kịch bản nào. Chỉ là trước giờ không ai đem hai bản ra đối chiếu.
|
|
31
|
+
|
|
32
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
33
|
+
|
|
34
|
+
Cuối báo cáo của trạm này có thêm một dòng: `Guard BR-tag: khớp 12/12`. Nếu có luật bị bỏ sót, nó **tự tìm lại nội dung trong tài liệu gốc, bổ sung vào bản phân tích**, rồi mới báo — kèm danh sách đã bổ sung.
|
|
35
|
+
|
|
36
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
37
|
+
|
|
38
|
+
- **Luật nghiệp vụ (`BR` — business rule)** — một quy tắc sản phẩm phải tuân theo.
|
|
39
|
+
- **Kịch bản (`SC` — scenario)** — một tình huống cụ thể dùng để kiểm tra.
|
|
40
|
+
- **Tag** — nhãn ghi kèm kịch bản, nói kịch bản này thi hành những luật nào.
|
|
41
|
+
- **Guard** — một phép kiểm do máy làm, chạy như nhau mỗi lần.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
> ### ✅ Phép thử người đọc
|
|
46
|
+
>
|
|
47
|
+
> **Đã 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.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# PHẦN B — Chi tiết kỹ thuật
|
|
52
|
+
|
|
53
|
+
## B1 · Cách hiển nhiên là gì, và vì sao nó sai
|
|
54
|
+
|
|
55
|
+
### Cách sai thứ nhất — thêm một câu vào phần tự kiểm
|
|
56
|
+
|
|
57
|
+
*"Hãy kiểm tra lại xem đã map đủ BR chưa"*. Rẻ, một dòng, và nghe như đã giải quyết.
|
|
58
|
+
|
|
59
|
+
Sai vì tự kiểm là agent **tự đọc lại bài của mình** — nó bỏ sót **đúng chỗ nó đã bỏ sót lúc viết**. Cùng một agent, cùng một điểm mù, chỉ khác là đọc lần thứ hai. Muốn bắt được cái thiếu thì phải so với **một nguồn khác**, do **một lệnh khác** ghi ra.
|
|
60
|
+
|
|
61
|
+
Đây là lý do [`1-04`](1-04-self-review-dung-chung.md) phải viết hẳn một mục về ranh giới giữa hai thứ.
|
|
62
|
+
|
|
63
|
+
### Cách sai thứ hai — guard chỉ in cảnh báo
|
|
64
|
+
|
|
65
|
+
Phát hiện thiếu thì báo, để người đi xử lý. Đúng vai trò của một cái "guard".
|
|
66
|
+
|
|
67
|
+
Sai vì nó **thêm một dòng nữa để bỏ qua**. Người chạy lệnh đang ở giữa một việc khác; một dòng cảnh báo giữa report dài sẽ trôi qua. Và lần sau nó lại hiện, rồi lại trôi.
|
|
68
|
+
|
|
69
|
+
Guard này **tự sửa rồi mới báo**: quay lại tài liệu gốc, tìm nội dung thật của luật đó, bổ sung vào bản phân tích. Cảnh báo là để người **biết đã có chuyện gì xảy ra**, không phải để họ đi làm việc đó.
|
|
70
|
+
|
|
71
|
+
### Cách sai thứ ba — coi chiều ngược lại cũng là lỗi
|
|
72
|
+
|
|
73
|
+
Nếu `A ∖ B` (tag có, phân tích thiếu) là lỗi, thì `B ∖ A` (phân tích có, tag không nhắc) cũng phải là lỗi chứ?
|
|
74
|
+
|
|
75
|
+
Sai. QC sinh ra `BR09` mà không tag nào nhắc tới → rất có thể QC **phát hiện một luật mà BDD chưa phủ**. Đó là **phát hiện tốt**, không phải sai sót. Xử lý đúng: ghi thành một gap trong `DOC_GAP.md` (BDD thiếu scenario cho luật này), **đừng xoá**.
|
|
76
|
+
|
|
77
|
+
Một guard mà phạt cả hai chiều sẽ dạy agent chỉ liệt kê đúng những gì BDD đã nhắc — tức là **thôi suy nghĩ**.
|
|
78
|
+
|
|
79
|
+
## B2 · Cách làm đúng
|
|
80
|
+
|
|
81
|
+
Mục §Trace mapping có sẵn trong lệnh đi **một chiều**: từ `BR` mà QC tạo ra → `SC` sở hữu nó. Chiều đó đúng và cần. Chiều **ngược lại** mới bắt được cái bỏ sót, và nó chưa được kiểm ở đâu.
|
|
82
|
+
|
|
83
|
+
BDD đã tự nói ra một phần đáp án — mỗi scenario mang tag do `/generate-bdd` ghi khi sinh từ PRD:
|
|
84
|
+
|
|
85
|
+
```gherkin
|
|
86
|
+
# @trace.scenario: FT-101-UC1-SC3
|
|
87
|
+
# @trace.business_rules: FT-101-UC1-BR02, FT-101-UC1-BR07
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Bản phân tích chỉ có `BR01`–`BR05` thì `BR07` là **luật BDD biết mà QC bỏ sót** — và đó là một **phép so khớp chuỗi**, máy làm được.
|
|
91
|
+
|
|
92
|
+
**Bốn bước, thuần đếm và so:**
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
1. Thu A — mọi giá trị @trace.business_rules trong .feature của uc_list (đúng nền)
|
|
96
|
+
2. Thu B — mọi BR-xx trong REQUIREMENT_ANALYSIS.md VỪA GHI
|
|
97
|
+
3. So — A ∖ B
|
|
98
|
+
4. Xử lý — rỗng → in "Guard BR-tag: khớp {n}/{n}"
|
|
99
|
+
≠ rỗng → quay lại PRD lấy NỘI DUNG THẬT, bổ sung ngay, rồi in danh sách
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Bước 4 ghi rõ **không thêm một dòng trống mang tên `BR-xx` cho đủ số** — đó là cách một guard tự-sửa bị làm cho vô nghĩa.
|
|
103
|
+
|
|
104
|
+
**In dòng `Guard BR-tag:` kể cả khi sạch.** Guard im lặng khi sạch là guard **không ai biết nó tồn tại** — và không ai phát hiện được khi nó chết.
|
|
105
|
+
|
|
106
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
107
|
+
|
|
108
|
+
**Kiểu hỏng: im lặng** 🔴 — và đây là chỗ **đắt nhất trong cả pipeline**, vì lỗi nhân lên theo từng trạm.
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
thiếu BR → /qc-plan không xếp rủi ro → /qc-design-test không viết test case
|
|
112
|
+
→ /qc-run-test không có gì chạy → /qc-report báo "coverage 100%"
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
| | |
|
|
116
|
+
|---|---|
|
|
117
|
+
| Hỏng ở đâu | Trạm đầu tiên — nên mọi trạm sau đều thừa hưởng |
|
|
118
|
+
| Ai phát hiện | Không ai trong nội bộ. Người dùng cuối, khi tính năng đó hỏng thật |
|
|
119
|
+
| Sau bao lâu | Sau khi phát hành |
|
|
120
|
+
| Vì sao đắt | Báo cáo cuối **nói dối một cách trung thực**: nó tính đúng trên mẫu số *"số scenario đã biết"*. Không ai sai ở khâu tính; sai ở khâu mẫu số thiếu |
|
|
121
|
+
|
|
122
|
+
## B4 · Verify bằng gì
|
|
123
|
+
|
|
124
|
+
| # | Phép thử | Kết quả |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| 1 | `build` · `self-check` · `test` · `lint-trace` | ✅ 212/212 |
|
|
127
|
+
| 2 | `git diff --stat` — đúng 1 file nguồn | ✅ |
|
|
128
|
+
|
|
129
|
+
**Phép thử tay chưa chạy** — cần PRD thật. Ca **3** là phép thử quyết định:
|
|
130
|
+
|
|
131
|
+
| # | Phép thử | Kết quả mong đợi |
|
|
132
|
+
|---|---|---|
|
|
133
|
+
| 3 | UC có `.feature` với tag trỏ ≥3 luật · chạy `/qc-analyze` | Report in `Guard BR-tag: khớp N/N` |
|
|
134
|
+
| **4** | 🔴 Xoá tay một `BR-xx` khỏi `REQUIREMENT_ANALYSIS.md`, chạy lại | In `⚠️ bổ sung 1 rule…` **và** file có lại rule đó **kèm nội dung thật từ PRD** |
|
|
135
|
+
| 5 | Sinh một `BR` mà không tag nào nhắc | **Không** bị coi là lỗi; được ghi thành gap trong `DOC_GAP.md` |
|
|
136
|
+
|
|
137
|
+
Ca 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 dung**, không chỉ kiểm có dòng.
|
|
138
|
+
|
|
139
|
+
**Thành thật về giới hạn:** không có test tự động cho bước này. Bốn lệnh kiểm chỉ canh contract của framework — chúng không chạy `/qc-analyze` lên một PRD. Guard này chỉ được chứng minh khi có người chạy ca 4.
|
|
140
|
+
|
|
141
|
+
## B5 · Bài học
|
|
142
|
+
|
|
143
|
+
- **2026-09-11** — Không có gì lệch kế hoạch ở bước này; nó chạy đúng như `exec-d1-b1` mô tả. Ghi lại để đối chiếu: **6 trong 12 bước** của hai đợt lệch kế hoạch, và điểm chung của 6 bước đó là **kế hoạch viết trước khi đọc kỹ file đích**. Bước này kế hoạch viết sau khi đã grep xác nhận `/qc-design-test` không có phép kiểm tương đương — nên không lệch.
|
|
144
|
+
→ **Bài học chuyển giao được:** tỷ lệ kế hoạch-lệch-thực-tế là một chỉ số về việc **kế hoạch có được viết trên dữ liệu hay không**, chứ không phải về việc kế hoạch tốt hay dở.
|
|
145
|
+
|
|
146
|
+
## B6 · Copy được / không copy được
|
|
147
|
+
|
|
148
|
+
| | |
|
|
149
|
+
|---|---|
|
|
150
|
+
| ✅ **Copy được sang dự án khác** | Nguyên tắc **so với một nguồn khác, do một tác nhân khác ghi** — đó là điều phân biệt một phép kiểm thật với một lượt tự đọc lại. Nguyên tắc **guard tự sửa rồi mới báo**. Nguyên tắc **chiều ngược không phải lỗi** — một phép so hai tập thường chỉ có một chiều là sai sót, chiều kia là phát hiện |
|
|
151
|
+
| ⚠️ **Chỉ đúng ở đây** | Tên tag `@trace.business_rules`, định dạng `BR-xx`, và việc BDD được sinh ra từ PRD nên tag đáng tin — đều gắn với pipeline này |
|
|
152
|
+
|
|
153
|
+
## B7 · Link
|
|
154
|
+
|
|
155
|
+
- Kế hoạch trước khi làm: [`../exec-d1-b1-guard-br-tag.md`](../exec-d1-b1-guard-br-tag.md)
|
|
156
|
+
- Bước liên quan: [`1-02`](1-02-guard-sc-coverage.md) (guard anh em — chiều *đếm* thay vì *so khớp*) · [`1-04`](1-04-self-review-dung-chung.md) (ranh giới guard vs tự kiểm)
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Guard SC coverage — mọi kịch bản phải có ít nhất một test case
|
|
2
|
+
|
|
3
|
+
| | |
|
|
4
|
+
|---|---|
|
|
5
|
+
| **Đợt** | 1 — Nền tảng |
|
|
6
|
+
| **Bước** | **2/6** của Đợt 1 |
|
|
7
|
+
| **File sửa** | `commands/qc-design-test.tmpl` |
|
|
8
|
+
| **File test** | Không có test tự động — xem B4 |
|
|
9
|
+
| **Ngày xong** | 2026-09-11 · commit `249c233` |
|
|
10
|
+
| **Phụ thuộc** | không có |
|
|
11
|
+
| **Ai dùng nó** | `/qc-design-test`; con số nó tính ra là đầu vào của `/qc-report` ở Đợt 3 |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
16
|
+
|
|
17
|
+
## A1 · Vấn đề
|
|
18
|
+
|
|
19
|
+
Một tính năng có 6 tình huống cần kiểm. Đội kiểm thử viết 11 bài kiểm tra — nghe có vẻ dư dả. Nhưng 11 bài đó dồn vào 4 tình huống, còn **2 tình huống không có bài nào**.
|
|
20
|
+
|
|
21
|
+
Không ai biết. Và đến cuối, hai tình huống đó nằm im ở trạng thái *"chưa chạy"* — **trông y hệt** hai tình huống đã có bài kiểm nhưng chưa tới lượt chạy.
|
|
22
|
+
|
|
23
|
+
Hai trạng thái đó dẫn tới hai việc hoàn toàn khác nhau — **viết bài kiểm**, so với **chạy bài kiểm** — mà nhìn vào sổ thì giống nhau.
|
|
24
|
+
|
|
25
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
26
|
+
|
|
27
|
+
> **Như điểm danh.** Không đếm xem có bao nhiêu người trong phòng — mà **gọi tên từng người
|
|
28
|
+
> trong danh sách** và xem ai không có mặt. Hai cách cho ra hai con số rất khác nhau khi có
|
|
29
|
+
> người ngồi trùng chỗ.
|
|
30
|
+
|
|
31
|
+
Ở đây: không đếm xem viết được bao nhiêu bài kiểm, mà **đi từ danh sách tình huống** và hỏi từng cái *"có bài nào phủ chưa?"*.
|
|
32
|
+
|
|
33
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
34
|
+
|
|
35
|
+
Cuối báo cáo có thêm dòng `Guard SC coverage: khớp 6/6`. Nếu thiếu, lệnh **viết bù ngay** rồi mới báo — kèm danh sách tình huống đã bù.
|
|
36
|
+
|
|
37
|
+
Và con số `{K}/{total}` vốn đã in ở báo cáo từ trước giờ **có một phép tính đứng sau nó**, thay vì là ước lượng.
|
|
38
|
+
|
|
39
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
40
|
+
|
|
41
|
+
- **Tình huống (`SC` — scenario)** — một trường hợp cụ thể cần kiểm, viết sẵn trong tài liệu kịch bản.
|
|
42
|
+
- **Bài kiểm (`TC` — test case)** — mô tả các bước kiểm một tình huống.
|
|
43
|
+
- **Coverage (độ phủ)** — tỷ lệ tình huống đã được bài kiểm phủ tới.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
> ### ✅ Phép thử người đọc
|
|
48
|
+
>
|
|
49
|
+
> **Đã 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.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# PHẦN B — Chi tiết kỹ thuật
|
|
54
|
+
|
|
55
|
+
## B1 · Cách hiển nhiên là gì, và vì sao nó sai
|
|
56
|
+
|
|
57
|
+
### Cách sai thứ nhất — cho phép "bỏ trống có lý do"
|
|
58
|
+
|
|
59
|
+
Đây là cách **kế hoạch của tôi đề xuất**, và nó sai — phát hiện lúc đọc lệnh, trước khi viết.
|
|
60
|
+
|
|
61
|
+
Lập luận nghe rất hợp lý: có tình huống thật sự chưa viết được test case, vì một khoảng trống trong tài liệu (`gap`) chặn lại. Nên cho phép bỏ qua **nếu** trỏ tới một `GAP-xx` có thật.
|
|
62
|
+
|
|
63
|
+
Sai vì lệnh **đã có quy ước ngược lại** — `qc-design-test.tmpl:137`:
|
|
64
|
+
|
|
65
|
+
> *"Một TC bị block bởi gap **vẫn viết đủ** + `🚫 Block: [GAP-UC{N}-{nnn}](../DOC_GAP.md) — lý do`."*
|
|
66
|
+
|
|
67
|
+
Nghĩa là: **gap không phải cái cớ để không viết test case — nó là thứ được GHI VÀO test case.** Tình huống bị chặn vẫn có test case (mang dấu `🚫 Block`), vẫn được tính là đã phủ.
|
|
68
|
+
|
|
69
|
+
Thêm đường thoát như kế hoạch là **dạy hai luật trái nhau trong cùng một lệnh**, và tệ hơn: nó cho agent một **câu-lý-do-cho-qua**. Guard viết lỏng thì lần sau sẽ có một câu lý do chung chung, và phép đếm mất nghĩa.
|
|
70
|
+
|
|
71
|
+
Bản triển khai vì vậy **nghiêm hơn mà đơn giản hơn** — chỉ hai trạng thái, không cái nào cho phép bỏ trống.
|
|
72
|
+
|
|
73
|
+
### Cách sai thứ hai — đợi báo cáo cuối tính là đủ
|
|
74
|
+
|
|
75
|
+
`/qc-report` ở Đợt 3 sẽ tính **design coverage** bằng **đúng phép đếm này**. Vậy làm hai lần để làm gì?
|
|
76
|
+
|
|
77
|
+
Sai vì **thời điểm**. Guard đếm **lúc thiết kế** — còn kịp viết bù. Báo cáo đếm **ở cuối** — chỉ còn kịp báo. Phát hiện *"design coverage 68%"* ở phút chót là lúc không còn thời gian viết bù 32%.
|
|
78
|
+
|
|
79
|
+
## B2 · Cách làm đúng
|
|
80
|
+
|
|
81
|
+
**Phát hiện lúc đọc lệnh:** report **đã có sẵn** con số `Trace: {N} TC map tới {K}/{total} 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 việc gì. Con số tồn tại như một ước lượng. Guard là phép đếm đứng sau nó, cộng với hệ quả.
|
|
82
|
+
|
|
83
|
+
**Ba bước:**
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
1. Thu SC — mọi @trace.scenario trong .feature của uc_list (đúng nền).
|
|
87
|
+
LOẠI SC của UC mà qc-scope đã lọc ra (⏸ chưa xét — BDD chưa approved):
|
|
88
|
+
chúng ngoài phạm vi lần chạy này, không phải bỏ sót.
|
|
89
|
+
2. Thu V — mọi @trace.verifies trong các file *.Test.md VỪA GHI.
|
|
90
|
+
3. Đếm — mỗi SC ∈ tập bước 1 phải có ≥ 1 TC trỏ tới.
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Chỉ còn hai trạng thái, không cái nào cho phép bỏ trống:**
|
|
94
|
+
|
|
95
|
+
| | Xử lý |
|
|
96
|
+
|---|---|
|
|
97
|
+
| SC **trong** phạm vi | Phải có TC. Bị gap chặn → TC mang dấu `🚫 Block` |
|
|
98
|
+
| SC **ngoài** phạm vi | `qc-scope` đã lọc ở bước 1, không tính vào mẫu số |
|
|
99
|
+
|
|
100
|
+
Thấy một SC *"không thể viết TC"* mà không thuộc hai loại trên → **đó là một gap**: mở gap trong `DOC_GAP.md`, viết TC kèm `🚫 Block` trỏ tới nó.
|
|
101
|
+
|
|
102
|
+
**In dòng `Guard SC coverage:` kể cả khi sạch** — cùng lý do với [`1-01`](1-01-guard-br-tag.md).
|
|
103
|
+
|
|
104
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
105
|
+
|
|
106
|
+
**Kiểu hỏng: im lặng** 🔴
|
|
107
|
+
|
|
108
|
+
| | |
|
|
109
|
+
|---|---|
|
|
110
|
+
| Hỏng ở đâu | Một tình huống không có bài kiểm nào |
|
|
111
|
+
| Ai phát hiện | Không ai — trong sổ trace nó là `qc_status = not_run`, **trông y hệt** một tình huống đã có bài kiểm nhưng chưa chạy |
|
|
112
|
+
| Sau bao lâu | Không bao giờ, trừ khi có người ngồi đối chiếu tay |
|
|
113
|
+
| Vì sao đắt | Hai trạng thái *"chưa viết"* và *"đã viết, chưa chạy"* dẫn tới hai việc khác nhau hoàn toàn, mà **không có chỗ nào trong hệ thống phân biệt được**. Guard này là chỗ duy nhất |
|
|
114
|
+
|
|
115
|
+
Và nó truyền sang Đợt 3: `/qc-report` sẽ ra verdict `PASS/FAIL` dựa trên coverage — con số đó vô nghĩa nếu mẫu số chưa từng được kiểm.
|
|
116
|
+
|
|
117
|
+
## B4 · Verify bằng gì
|
|
118
|
+
|
|
119
|
+
| # | Phép thử | Kết quả |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| 1 | `build` · `self-check` · `test` · `lint-trace` | ✅ 212/212 |
|
|
122
|
+
| 2 | `git diff --stat` — đúng 1 file nguồn | ✅ |
|
|
123
|
+
|
|
124
|
+
**Phép thử tay chưa chạy** — ca **4** và **5** là phép thử quyết định:
|
|
125
|
+
|
|
126
|
+
| # | Phép thử | Kết quả mong đợi |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| 3 | UC có `.feature` ≥5 scenario, chạy `/qc-design-test` | Report in `Guard SC coverage: khớp K/K` |
|
|
129
|
+
| **4** | Xoá hết TC trỏ tới một SC, chạy lại | In `⚠️ (K-1)/K` **và viết bù TC** |
|
|
130
|
+
| **5** | 🔴 Thử để agent ghi lý do *"tương tự SC2"* | Guard **không** chấp nhận — phải viết TC (kèm `🚫 Block` nếu bị gap chặn) |
|
|
131
|
+
| 6 | UC có BDD chưa `approved` | SC của UC đó **không** tính vào mẫu số |
|
|
132
|
+
|
|
133
|
+
Ca 5 là chỗ dễ hỏng nhất. Nếu guard nhận một câu lý do chung chung thì nó đã tự vô hiệu hoá — và đó chính là điều bản kế hoạch suýt làm.
|
|
134
|
+
|
|
135
|
+
## B5 · Bài học
|
|
136
|
+
|
|
137
|
+
- **2026-09-11** — Kế hoạch thiết kế một đường thoát mâu thuẫn với quy ước `🚫 Block` **đã có trong chính lệnh đó** (`:137`). Lỗi lộ ra khi đọc lệnh để tìm chỗ chèn guard — tức **sau khi viết kế hoạch, trước khi viết code**.
|
|
138
|
+
→ **Bài học chuyển giao được:** trước khi thêm một **ngoại lệ** vào một luật mới, đọc xem luật cũ **đã xử lý ca đó chưa**. Ở đây ca "bị gap chặn" đã có cách xử lý rồi (ghi vào TC), và thêm ngoại lệ là tạo ra cách xử lý thứ hai cho cùng một tình huống — hai cách thì agent sẽ chọn cách dễ hơn.
|
|
139
|
+
|
|
140
|
+
- **2026-09-11** — Report đã có sẵn con số `{K}/{total}` mà **không có phép tính nào đứng sau**. Đây là một dạng nợ khó thấy: nhìn vào report thì tưởng đã có kiểm.
|
|
141
|
+
→ **Bài học chuyển giao được:** một con số trong báo cáo **không chứng minh** có phép đo. Khi rà một hệ thống, tìm cả **chỗ có số mà không có phép tính**, không chỉ tìm chỗ thiếu số.
|
|
142
|
+
|
|
143
|
+
## B6 · Copy được / không copy được
|
|
144
|
+
|
|
145
|
+
| | |
|
|
146
|
+
|---|---|
|
|
147
|
+
| ✅ **Copy được sang dự án khác** | Nguyên tắc **đếm từ danh sách gốc, không đếm sản phẩm** (điểm danh, không đếm đầu người). Nguyên tắc **đừng thêm ngoại lệ khi luật cũ đã xử lý ca đó**. Và: **kiểm sớm ở chỗ còn sửa được**, không đợi báo cáo cuối — cùng một phép đếm, giá trị khác hẳn theo thời điểm |
|
|
148
|
+
| ⚠️ **Chỉ đúng ở đây** | Tag `@trace.scenario`/`@trace.verifies`, quy ước `🚫 Block`, và việc `qc-scope` lọc UC theo trạng thái BDD |
|
|
149
|
+
|
|
150
|
+
## B7 · Link
|
|
151
|
+
|
|
152
|
+
- Kế hoạch trước khi làm: [`../exec-d1-b2-guard-sc-coverage.md`](../exec-d1-b2-guard-sc-coverage.md) — đã ghi lại đường thoát bị bỏ, kèm bản gốc
|
|
153
|
+
- Bước liên quan: [`1-01`](1-01-guard-br-tag.md) (guard anh em) · Đợt 3 `/qc-report` (dùng đúng phép đếm này để tính design coverage)
|