@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,167 @@
|
|
|
1
|
+
# Skill thôi dạy dò DOM trước khi đọc hợp đồng
|
|
2
|
+
|
|
3
|
+
| | |
|
|
4
|
+
|---|---|
|
|
5
|
+
| **Đợt** | 0 — Hợp đồng test-id |
|
|
6
|
+
| **Bước** | **3/6** của Đợt 0 |
|
|
7
|
+
| **File sửa** | `skills/qc/qa-runner/functional/gui-screen.md` · `.../gui-feature.md` |
|
|
8
|
+
| **File test** | Không có test tự động — xem B4 |
|
|
9
|
+
| **Ngày xong** | 2026-09-11 · commit `292291f` |
|
|
10
|
+
| **Phụ thuộc** | không có — chạy song song `0-01`/`0-02` được |
|
|
11
|
+
| **Ai dùng nó** | `/qc-run-test` nạp hai skill này khi sinh script cho màn hình GUI |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
16
|
+
|
|
17
|
+
## A1 · Vấn đề
|
|
18
|
+
|
|
19
|
+
Công ty phát cho nhân viên mới hai tập tài liệu. Tập mỏng ghi: *"tìm hàng theo mã vạch trên kệ"*. Tập dày — tập họ thật sự mở ra lúc đi làm — ghi: *"cứ xuống kho nhìn hình dáng thùng mà đoán"*.
|
|
20
|
+
|
|
21
|
+
Nhân viên làm theo tập dày, vì đó là tập cầm trên tay lúc làm việc.
|
|
22
|
+
|
|
23
|
+
Rồi kho đổi cách xếp thùng, mọi người tìm nhầm hết. Và cả công ty kết luận: *"mã vạch ở đây không dùng được"* — trong khi mã vạch vẫn dán đầy đủ, chỉ là **không ai được bảo hãy dùng nó**.
|
|
24
|
+
|
|
25
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
26
|
+
|
|
27
|
+
> **Đảo thứ tự trong sách hướng dẫn, không xé trang.** Việc "nhìn hình dáng mà đoán" vẫn cần
|
|
28
|
+
> — kho cũ chưa dán mã vạch thì phải đoán. Nhưng nó chuyển từ **việc đầu tiên** xuống **việc
|
|
29
|
+
> cuối cùng**, và mỗi lần phải làm vậy thì **ghi lại một dòng**: kệ nào chưa có mã.
|
|
30
|
+
|
|
31
|
+
Nhờ dòng ghi đó, số lần phải đoán **giảm dần** theo thời gian — thay vì giữ nguyên mãi.
|
|
32
|
+
|
|
33
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
34
|
+
|
|
35
|
+
Kịch bản kiểm thử sinh ra sẽ bám vào **nhãn đã thoả thuận** thay vì tên lớp hiển thị. Và mỗi khi gặp một nút chưa có nhãn, nó **ghi lại một mục** đề nghị đội lập trình bổ sung — thay vì lặng lẽ dùng tên lớp rồi để đó.
|
|
36
|
+
|
|
37
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
38
|
+
|
|
39
|
+
- **DOM** — cấu trúc bên trong của một trang web; "dò DOM" là mở trang ra xem thật để tìm cách gọi tên từng nút.
|
|
40
|
+
- **Skill** — tập tài liệu chuyên môn mà trợ lý AI mở ra đọc khi bắt tay vào một việc cụ thể. Khác với **lệnh**, là bản chỉ dẫn tổng quát nó đọc trước.
|
|
41
|
+
- **Hợp đồng test-id** — bảng ghi sẵn nhãn của từng nút, để hai bên không phải đoá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
|
+
> *(A1 cố ý không nhắc "DOM" hay "selector" — hai từ đó đẩy xuống A4.)*
|
|
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 — chỉ sửa lệnh, không sửa skill
|
|
57
|
+
|
|
58
|
+
Đây là cách gần như ai cũng làm, vì lệnh **nghe có vẻ quan trọng hơn** skill. Và lệnh đã nói đúng rồi — `commands/qc-run-test.tmpl:43`:
|
|
59
|
+
|
|
60
|
+
> *"**Locator từ test-id contract (không scan runtime).** … **Ưu tiên map; fallback** về role/label/text/CSS chỉ cho element có action mà **không** có test-id trong §4.5.6."*
|
|
61
|
+
|
|
62
|
+
Sai vì **thứ tự đọc**. Agent đọc lệnh trước, rồi **nạp skill để làm việc**. Skill là thứ nó đọc **sau** và **gần lúc viết code nhất** — nên skill thắng. Câu trong skill, `gui-screen.md:26`:
|
|
63
|
+
|
|
64
|
+
> *"**Probe DOM thật trước khi viết selector** (SPA không `data-testid`): dump class/`aria-label`/role bằng script Playwright tạm → ghi selector đúng (BEM `feature__el`…)"*
|
|
65
|
+
|
|
66
|
+
Và nó nằm ở **Phase 1 — Clarify**, tức **việc đầu tiên**, không phải fallback.
|
|
67
|
+
|
|
68
|
+
Đây là nguyên nhân gốc khiến đội QC tin rằng phải tự dò selector — và là lý do trưởng phòng QC hỏi *"có thể dùng prompt trích xuất locator từ source code không?"*. Họ không sai; **framework đang bảo họ làm vậy**.
|
|
69
|
+
|
|
70
|
+
### Cách sai thứ hai — xoá hẳn câu "probe DOM" đi
|
|
71
|
+
|
|
72
|
+
Đã biết nó sai thì xoá. Nghe dứt khoát.
|
|
73
|
+
|
|
74
|
+
Sai vì hai lẽ. **Một:** dự án brownfield chưa có contract **vẫn cần dò** — xoá là lấy đi một năng lực đang dùng, mà không ai khai. **Hai:** kiến thức trong câu đó là kinh nghiệm thật và đắt: *BEM `feature__el`* · *element interactive có thể `role="tab/menuitem"` + class `--active`* · *carousel dot thường `role="tab"`, không phải `aria-selected`*. Xoá câu là xoá cả những thứ đó.
|
|
75
|
+
|
|
76
|
+
### Cách sai thứ ba — chép nguyên luật từ lệnh sang skill
|
|
77
|
+
|
|
78
|
+
"Skill tự chứa" là nguyên tắc có thật của bộ skill này, nên chép sang nghe hợp lý.
|
|
79
|
+
|
|
80
|
+
Sai vì tạo **ba bản** của một luật (lệnh + 2 skill). Chúng sẽ lệch — không phải nếu, mà là khi — và lệch **im lặng**, vì không rule nào so nội dung các file với nhau.
|
|
81
|
+
|
|
82
|
+
## B2 · Cách làm đúng
|
|
83
|
+
|
|
84
|
+
Đảo thành thang **3 bậc**, giữ nguyên kiến thức cũ nhưng đẩy xuống bậc cuối:
|
|
85
|
+
|
|
86
|
+
```
|
|
87
|
+
1. Test-id contract — bảng §4.5.6, lọc theo cột "Phục vụ SC" khớp SC của UC này.
|
|
88
|
+
TÊN thuộc tính đọc từ @trace.testid_attr (đừng suy từ nền).
|
|
89
|
+
2. Role + accessible name — cho element có action mà §4.5.6 chưa phủ.
|
|
90
|
+
3. Dò DOM — CHỈ khi 1 và 2 đều trượt. Dump class/aria-label/role, rồi nhìn kết quả:
|
|
91
|
+
3a. Element ĐÃ mang test-id trong code
|
|
92
|
+
→ DỪNG, đừng tự dùng id nhặt được. Chạy /map-testids để đưa vào §4.5.6
|
|
93
|
+
rồi quay lại bậc 1.
|
|
94
|
+
3b. Element KHÔNG có test-id nào
|
|
95
|
+
→ mới dùng class/role (BEM · role=tab/menuitem · carousel dot…)
|
|
96
|
+
VÀ ghi một GAP: element nào, màn nào, thiếu test-id.
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Tách 3a/3b là phần quan trọng nhất, và nó đến từ một câu hỏi lúc review.** Bản nháp đầu viết bậc 3 thành một khối, nên nó **nuốt luôn** ca *"code đã có test-id mà bảng chưa có"*:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
§4.5.6 rỗng → bậc 1 trượt → bậc 2 trượt → bậc 3 dump DOM
|
|
103
|
+
→ thấy data-testid="login-submit-btn" → dùng luôn
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Trông thì ổn — script vẫn bám test-id ổn định. Nhưng mất ba thứ: id **không bao giờ vào §4.5.6** (lần sau lại phải khám phá lại) · **không ai review** id đó (dev chưa hề hứa giữ nó) · và ca A **bị nguỵ trang thành ca B** (nhìn script không phân biệt được "id từ hợp đồng" với "id nhặt từ DOM").
|
|
107
|
+
|
|
108
|
+
Ba ca dễ bị gộp nhầm, và chúng đi ba đường:
|
|
109
|
+
|
|
110
|
+
| | Code có test-id? | §4.5.6 có? | Đường đúng |
|
|
111
|
+
|---|:---:|:---:|---|
|
|
112
|
+
| **A** | ✅ | ❌ | `/map-testids` — nhánh `existing`, reverse-document rồi quay lại bậc 1 |
|
|
113
|
+
| **B** | ❌ | ❌ | **bậc 3b** — dùng class/role + ghi gap |
|
|
114
|
+
| **C** | ✅ | ✅ nhưng lệch | Ca đối chiếu, người quyết bên nào đúng ([`0-06`](0-06-hop-dong-truoc-code.md)) |
|
|
115
|
+
|
|
116
|
+
**Câu chịu lực, không phải trang trí:** *"mỗi lần xuống bậc 3b là một GAP phải ghi"*. Không có nó thì thang 3 bậc chỉ là "vẫn được dò, chỉ là xếp thứ ba" — và thói quen không đổi. Có nó thì số lần dò **giảm dần** theo thời gian, vì mỗi lần dò đẻ ra một việc cho dev.
|
|
117
|
+
|
|
118
|
+
Và mỗi skill **trỏ** tới luật ở lệnh, kèm câu **"không chép lại ở đây"** viết thẳng vào file — để người sau không "bổ sung cho đầy đủ".
|
|
119
|
+
|
|
120
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
121
|
+
|
|
122
|
+
**Kiểu hỏng: im lặng** 🔴
|
|
123
|
+
|
|
124
|
+
| | |
|
|
125
|
+
|---|---|
|
|
126
|
+
| Hỏng ở đâu | Mọi selector trong script QC — chúng bám tên class thay vì nhãn đã thoả thuận |
|
|
127
|
+
| Ai phát hiện | Không ai, **cho tới lần dev đổi tên một class** |
|
|
128
|
+
| Sau bao lâu | Vài tuần tới vài tháng — đúng lúc không ai nhớ hai việc đó liên quan gì tới nhau |
|
|
129
|
+
| Vì sao đắt | Class **không phải thứ dev cam kết giữ**. Test vỡ vì một thay đổi hoàn toàn hợp lệ, và QC đi sửa script thay vì đi đòi một hợp đồng |
|
|
130
|
+
|
|
131
|
+
Thêm một tầng: nếu chỉ sửa lệnh mà quên skill, thì `0-01` + `0-02` + `0-04` **đều mất giá trị** — hợp đồng đầy đủ, máy canh chạy, và QC vẫn dò DOM.
|
|
132
|
+
|
|
133
|
+
## B4 · Verify bằng gì
|
|
134
|
+
|
|
135
|
+
| # | Phép thử | Kết quả |
|
|
136
|
+
|---|---|---|
|
|
137
|
+
| 1 | Kiến thức cũ còn nguyên, chỉ đổi vị trí: `grep -n "dò DOM" skills/qc/qa-runner/functional/gui-*.md` | ✅ có ở bậc 3, không ở đầu |
|
|
138
|
+
| 2 | Skill giờ trỏ tới hợp đồng: `grep -c "4.5.6\|testid_attr" …/gui-screen.md` | ✅ 3 |
|
|
139
|
+
| 3 | 🔴 Không ai chép lại luật: `grep -rc "không scan runtime" skills/qc/` | ✅ **0** |
|
|
140
|
+
| 4 | `build` · `self-check` · `test` · `lint-trace` | ✅ 212/212 |
|
|
141
|
+
|
|
142
|
+
**Phép thử tay chưa chạy** — trên một UC **đã có** §4.5.6 đầy đủ:
|
|
143
|
+
|
|
144
|
+
| # | Phép thử | Kết quả mong đợi |
|
|
145
|
+
|---|---|---|
|
|
146
|
+
| 5 | `/qc-run-test {UC-ID}`, đọc script sinh ra | Locator là `get_by_test_id(...)` với giá trị **trùng bảng §4.5.6** |
|
|
147
|
+
| 6 | Quan sát quá trình | **Không** thấy agent chạy script dump DOM trong khi §4.5.6 có sẵn id |
|
|
148
|
+
| 7 | Dựng ca A (code có id, bảng chưa có) | Agent **DỪNG** và đề nghị `/map-testids`, không tự dùng id nhặt được |
|
|
149
|
+
|
|
150
|
+
Phép thử 3 là phép thử canh chính nguyên tắc: nếu một ngày nó ra khác 0, có nghĩa ai đó vừa chép luật vào skill và ba bản đã bắt đầu tồn tại.
|
|
151
|
+
|
|
152
|
+
## B5 · Bài học
|
|
153
|
+
|
|
154
|
+
- **2026-09-11** — Bản nháp đầu của bậc 3 viết thành một khối, và nó **nuốt ca A mà không ai biết**. Lỗi lộ ra không phải do đọc lại code, mà do một câu hỏi lúc review: *"cái này phục vụ cho việc có code, lấy test-id nhưng trong bảng chưa có, đúng không?"* — câu hỏi đó buộc phải phân biệt ba ca A/B/C, và lúc phân biệt thì thấy bậc 3 đang gộp hai ca vào một.
|
|
155
|
+
→ **Bài học chuyển giao được:** khi viết một thang ưu tiên có bậc "làm tạm", hãy hỏi **bậc tạm đó đang gộp mấy tình huống khác nhau**. Bậc cuối của một thang ưu tiên là chỗ mọi ca chưa nghĩ tới rơi vào — và chúng thường cần xử lý khác nhau.
|
|
156
|
+
|
|
157
|
+
## B6 · Copy được / không copy được
|
|
158
|
+
|
|
159
|
+
| | |
|
|
160
|
+
|---|---|
|
|
161
|
+
| ✅ **Copy được sang dự án khác** | Nguyên tắc **skill thắng lệnh**: tài liệu mà agent đọc gần lúc làm nhất sẽ quyết định hành vi, nên sửa tầng trên mà quên tầng dưới là không sửa gì. Cách "không cấm cách tạm, nhưng bắt nó để lại dấu vết" để một thói quen tự giảm dần. Và việc tách bậc cuối thành các nhánh theo tình huống |
|
|
162
|
+
| ⚠️ **Chỉ đúng ở đây** | Nội dung kỹ thuật ở bậc 3b (BEM · `role="tab"` · carousel dot không dùng `aria-selected`) gắn với stack web cụ thể. Hai skill này còn viết cho Python/pytest — Đợt 2 sẽ đổi sang TypeScript, nên câu chỉ dẫn đã viết trung tính sẵn (bỏ chữ "Playwright") |
|
|
163
|
+
|
|
164
|
+
## B7 · Link
|
|
165
|
+
|
|
166
|
+
- Kế hoạch trước khi làm: [`../exec-d0-b3-sua-skill-probe-dom.md`](../exec-d0-b3-sua-skill-probe-dom.md) — §3b có bảng ba ca A/B/C đầy đủ
|
|
167
|
+
- Bước liên quan: [`0-01`](0-01-testid-attr-co-cho-o.md) · [`0-02`](0-02-mot-nguon-cho-testid-attr.md) (sửa hợp đồng) · [`1-03`](1-03-fail-3-nhan.md) (lặp lại đúng lớp lỗi này ở chỗ khác: 8 chỗ skill dạy nhị phân)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Máy canh bảng hợp đồng — T15/T16, và R8e canh chính hai rule đó
|
|
2
|
+
|
|
3
|
+
| | |
|
|
4
|
+
|---|---|
|
|
5
|
+
| **Đợt** | 0 — Hợp đồng test-id |
|
|
6
|
+
| **Bước** | **4/6** của Đợt 0 |
|
|
7
|
+
| **File sửa** | `bin/lint-trace.js` · `bin/trace-schema.json` · `bin/self-check.js` · `docs/04-reference/trace-schema.md` |
|
|
8
|
+
| **File test** | `test/run.js` — 7 test mới |
|
|
9
|
+
| **Ngày xong** | 2026-09-11 · commit `5b4403c` |
|
|
10
|
+
| **Phụ thuộc** | [`0-01`](0-01-testid-attr-co-cho-o.md) · [`0-02`](0-02-mot-nguon-cho-testid-attr.md) |
|
|
11
|
+
| **Ai dùng nó** | `bin/lint-trace.js` chạy ở CI và gọi tay; `bin/self-check.js` chạy mỗi lần build |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
16
|
+
|
|
17
|
+
## A1 · Vấn đề
|
|
18
|
+
|
|
19
|
+
Ba bước trước sửa xong quyển sổ địa chỉ và dạy mọi người dùng nó. Nhưng **không ai kiểm quyển sổ đó**.
|
|
20
|
+
|
|
21
|
+
Sáu tháng sau: vài căn nhà bị đập đi, sổ vẫn ghi địa chỉ cũ. Vài căn xây mới, không ai thêm vào sổ. Một quyển sổ mới chép tay thì quên hẳn trang đầu.
|
|
22
|
+
|
|
23
|
+
Từng chuyện một đều nhỏ, **không ai báo**, và đến lúc đội giao hàng thấy sổ sai quá nhiều thì họ quay về cách cũ — tự đi tìm.
|
|
24
|
+
|
|
25
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
26
|
+
|
|
27
|
+
> **Như máy soát vé ở cửa.** Không cần ai nhớ phải kiểm — cứ đi qua cửa là máy tự đối chiếu.
|
|
28
|
+
> Vé không có trong hệ thống thì kêu ngay, chứ không đợi tới lúc vào chỗ ngồi mới phát hiện.
|
|
29
|
+
|
|
30
|
+
Và có một máy thứ hai **canh chính cái máy soát vé đó** — để nếu một ngày ai tháo nó ra, hệ thống báo lỗi thay vì im lặng cho tất cả đi qua.
|
|
31
|
+
|
|
32
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
33
|
+
|
|
34
|
+
Chạy lệnh kiểm tra dữ liệu sẽ thấy thêm dòng báo nếu bảng địa chỉ trỏ vào một kịch bản không tồn tại, hoặc tài liệu có phần giao diện mà quên ghi loại nhãn. Và nếu dự án **chưa dùng** cơ chế này thì **hoàn toàn im lặng** — không ai bị làm phiền.
|
|
35
|
+
|
|
36
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
37
|
+
|
|
38
|
+
- **Lệnh kiểm tra dữ liệu (`lint-trace`)** — chương trình đọc các file dữ liệu thật của dự án rồi báo chỗ sai. Khác với **`self-check`**, chỉ kiểm bản thân framework có tự mâu thuẫn không.
|
|
39
|
+
- **Rule** — một phép kiểm cụ thể, có mã riêng (`T15`, `T16`…) để nói tới nó cho gọn.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
> ### ✅ Phép thử người đọc
|
|
44
|
+
>
|
|
45
|
+
> **Đã 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.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
# PHẦN B — Chi tiết kỹ thuật
|
|
50
|
+
|
|
51
|
+
## B1 · Cách hiển nhiên là gì, và vì sao nó sai
|
|
52
|
+
|
|
53
|
+
### Cách sai thứ nhất — viết đủ ba rule như kế hoạch
|
|
54
|
+
|
|
55
|
+
Kế hoạch (`exec-d0-b4`) đề ba rule, trong đó có: *"element có action xuất hiện trong step `When` của `.feature` mà không có row §4.5.6 → WARN"*. Nghe rất hợp lý: đó chính là ca **"bảng thiếu row"**, ca quan trọng nhất.
|
|
56
|
+
|
|
57
|
+
Sai vì nhận diện *"element có action"* từ **văn xuôi Gherkin tiếng Việt** là **suy luận**, không phải phép đếm. Câu `Khi người dùng bấm "Đăng nhập"` và câu `Khi hệ thống gửi email xác nhận` — cái đầu có element, cái sau không, và phân biệt hai cái đó bằng regex là bất khả.
|
|
58
|
+
|
|
59
|
+
Kết cục sẽ là một trong hai: bỏ sót gần hết (rule vô dụng), hoặc báo loạn. Và **báo loạn thì đắt hơn vô dụng**: người ta thêm `--warn-only` vào CI, rồi mất luôn hai rule kia.
|
|
60
|
+
|
|
61
|
+
Ca đó đã có hai lớp phủ tốt hơn: [`0-03`](0-03-skill-thoi-day-do-dom.md) bắt ghi gap mỗi lần phải dò DOM, và [`0-06`](0-06-hop-dong-truoc-code.md) thêm `T18` so bảng với **code thật**. Phần còn lại — element không có nhãn ở đâu cả — **không kiểm được bằng máy** ở tầng này (phải parse JSX/Vue/Flutter), và câu trả lời đúng cho nó là một cổng review, không phải một rule lint.
|
|
62
|
+
|
|
63
|
+
### Cách sai thứ hai — thêm rule vào `lint-trace.js` rồi thôi
|
|
64
|
+
|
|
65
|
+
Rule chạy, test xanh, xong việc.
|
|
66
|
+
|
|
67
|
+
Sai vì **không ai canh rule đó có còn tồn tại không**. Sáu tháng sau ai đó refactor `lint-trace.js`, xoá nhầm một nhánh — không gì báo. Đây đúng hình dạng lỗi mà cả đợt mổ này đang dọn: *luật đúng, viết rõ, và không ai canh.*
|
|
68
|
+
|
|
69
|
+
### Cách sai thứ ba — quên dòng mẫu trong template
|
|
70
|
+
|
|
71
|
+
Template §4.5.6 có một dòng ví dụ, và **mọi tech-doc mới sinh ra đều mang nó**:
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
| `{uc}-{screen}-{element}-{type}` | {Nút submit} | {Component} | {submit} | {UC1 · SC1, UC1 · SC3} |
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Không bỏ qua dòng này thì **mọi tech-doc vừa tạo đã đỏ ngay** vì "SC1 của UC1 không tồn tại". Một rule báo oan ở lần chạy đầu tiên sẽ bị tắt trong tuần đầu.
|
|
78
|
+
|
|
79
|
+
## B2 · Cách làm đúng
|
|
80
|
+
|
|
81
|
+
**Hai rule, cả hai là phép so khớp xác định:**
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
T15 mọi SC ở cột "Phục vụ SC" phải có thật trong .feature của nền đó 🔴 error
|
|
85
|
+
T16 có §4.5 client mà header thiếu HẲN @trace.testid_attr 🔴 error
|
|
86
|
+
…có nhưng còn placeholder {…} (chưa chạy /map-testids) ⚠️ warn
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
**T16 hai mức, không phải một** — chi tiết này quyết định rule sống hay chết. Sau bước `0-01`, tech-doc mới sinh ra **có** field ở dạng placeholder. Đó là *"chưa chạy `/map-testids`"*, một trạng thái **hợp lệ trong quy trình**, không phải *"thiếu"*. Báo ERROR ở đó là làm mọi tech-doc mới đỏ ngay khi vừa tạo.
|
|
90
|
+
|
|
91
|
+
**Điều khoản quan trọng nhất — phạm vi neo vào sự tồn tại của hợp đồng:**
|
|
92
|
+
|
|
93
|
+
| Tình trạng §4.5.6 | Có kiểm? | Vì sao |
|
|
94
|
+
|---|:---:|---|
|
|
95
|
+
| Không có bảng / bảng rỗng | **Không** | Chưa khai hợp đồng nào → không có lời khẳng định nào để sai |
|
|
96
|
+
| Có row, khớp | Không báo | Đúng |
|
|
97
|
+
| Có row, sai | **Có** | Đã hứa rồi mà không giữ |
|
|
98
|
+
|
|
99
|
+
Hai rule nói *"chỗ nào đã hứa thì phải giữ"*, **không** nói *"mọi chỗ đều phải có hợp đồng"*. Thiếu điều khoản này thì mọi dự án đang chạy đỏ ngay ngày nâng version.
|
|
100
|
+
|
|
101
|
+
**Ba quyết định nhỏ, mỗi cái có lý do:**
|
|
102
|
+
|
|
103
|
+
1. **Pass riêng, chạy TRƯỚC khối thoát sớm.** `bin/lint-trace.js:881` thoát ngay khi không tìm thấy thư mục `.trace/`. Hai rule mới **không cần sổ trace** — chúng so tech-doc với `.feature`. Neo vào sổ là làm chúng câm ở repo chỉ có spec. Và câu thông báo đổi từ *"chưa có gì để kiểm"* thành *"bỏ qua T1–T14"*, vì sau thay đổi này câu cũ thành sai.
|
|
104
|
+
|
|
105
|
+
2. **Không thêm cờ `--tech-docs`.** Kế hoạch đề xuất thêm. Không cần: `templates/project-context.yaml:100` khai `tech_docs_dir: "specs"` — trùng `specs_dir`. Cờ `--specs` sẵn có là đủ.
|
|
106
|
+
|
|
107
|
+
3. **Biến thể định dạng → im lặng, không đoán.** Cột "Phục vụ SC" parse được hai dạng (`UC1 · SC1` rút gọn và `FT-101-UC1-SC1` đầy đủ). Không parse được token nào → **bỏ qua**. Báo oan là cách nhanh nhất để một rule mới mất niềm tin.
|
|
108
|
+
|
|
109
|
+
**Và nửa "máy canh chính nó":**
|
|
110
|
+
|
|
111
|
+
Framework có sẵn khuôn — `bin/trace-schema.json:1754` khai `"lint_rule": "T12"`, và `bin/self-check.js:1089` kiểm rằng `lint-trace.js` **thực sự phát ra** rule đó, bằng regex `\b(?:err|warn)\('(T\d+)'`. Làm theo đúng khuôn: thêm khối `testid_contract` khai hai rule kèm `why`, và thêm **R8e** vào `self-check.js` canh chúng.
|
|
112
|
+
|
|
113
|
+
Không có R8e thì khối schema chỉ là chữ.
|
|
114
|
+
|
|
115
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
116
|
+
|
|
117
|
+
Bước này có **hai** kiểu hỏng ngược nhau, và phải chặn cả hai:
|
|
118
|
+
|
|
119
|
+
| Kiểu | Xảy ra khi | Hậu quả |
|
|
120
|
+
|---|---|---|
|
|
121
|
+
| **Ồn** 🟠 | Rule báo oan (dòng mẫu · tech-doc mới · biến thể định dạng) | CI đỏ vì chuyện bình thường → người ta thêm `--warn-only` → **mất cả hai rule** |
|
|
122
|
+
| **Im lặng** 🔴 | Rule viết sai, không bao giờ khớp gì | Tưởng được canh mà không — tệ hơn không có rule, vì nó cho cảm giác an toàn |
|
|
123
|
+
|
|
124
|
+
| | |
|
|
125
|
+
|---|---|
|
|
126
|
+
| Ai phát hiện | Kiểu **Ồn**: ngay, và phản ứng là tắt nó đi. Kiểu **Im lặng**: không ai |
|
|
127
|
+
| Vì sao viết test cả hai chiều | Ca "phải đỏ" chặn kiểu Im lặng. Ca "phải im" chặn kiểu Ồn. Chỉ test một chiều là chỉ chặn một nửa |
|
|
128
|
+
|
|
129
|
+
## B4 · Verify bằng gì
|
|
130
|
+
|
|
131
|
+
**7 test tự động** trong `test/run.js`, và **ba ca quan trọng nhất là ca phải IM LẶNG**:
|
|
132
|
+
|
|
133
|
+
| # | Phép thử | Kết quả |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| 1 | Hợp đồng khớp `.feature` | ✅ exit 0 |
|
|
136
|
+
| 2 | Row trỏ `UC1 · SC99` (SC không tồn tại) | ✅ **T15**, exit 1 |
|
|
137
|
+
| 3 | 🔴 Dòng mẫu còn placeholder | ✅ **im lặng** — doc mới tinh không được đỏ |
|
|
138
|
+
| 4 | Header thiếu hẳn `@trace.testid_attr` | ✅ **T16** error |
|
|
139
|
+
| 5 | Header còn placeholder | ✅ **T16 warn**, exit 0 — không chặn |
|
|
140
|
+
| 6 | 🔴 Tech-doc backend-only (không có §4.5) | ✅ **im lặng hoàn toàn** |
|
|
141
|
+
| 7 | 🔴 Repo chưa có tech-doc | ✅ in *"bỏ qua T15/T16"*, **không** im lặng báo sạch |
|
|
142
|
+
|
|
143
|
+
**Và đã chứng minh cơ chế canh bắt được bản sai** — phá có chủ đích:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
sed -i "s/err('T15'/err('TXX'/" bin/lint-trace.js
|
|
147
|
+
node bin/self-check.js
|
|
148
|
+
# ❌ [R8] schema khai `testid_contract` rule `T15` mà bin/lint-trace.js KHÔNG phát ra
|
|
149
|
+
# exit: 1
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
R8e đỏ đúng. Nếu nó vẫn xanh thì ta chỉ vừa viết thêm chữ vào schema.
|
|
153
|
+
|
|
154
|
+
## B5 · Bài học
|
|
155
|
+
|
|
156
|
+
- **2026-09-11** — Kế hoạch viết **trước** khi đọc kỹ `bin/lint-trace.js`, nên sai ba chỗ: đề 3 rule (một cái không kiểm được bằng máy), đề thêm cờ không cần, và bỏ sót chuyện lệnh thoát sớm khi không có sổ trace. Cả ba chỉ lộ ra khi mở file ra đọc.
|
|
157
|
+
→ **Bài học chuyển giao được:** với bước đầu tiên viết code thật (khác với sửa văn bản chỉ dẫn), **đọc file đích trước khi viết kế hoạch**, không phải sau. Ba chỗ sai ở đây đều là chỗ chỉ thấy được khi nhìn code.
|
|
158
|
+
|
|
159
|
+
- **2026-09-11** — Bản nháp T15 gom cả dòng phân cách markdown `|---|---|` thành một id tên `---`, vì regex cho phép id bắt đầu bằng dấu gạch. **Lỗi này do test "ca sạch" bắt**, không phải do đọc lại code.
|
|
160
|
+
→ **Bài học chuyển giao được:** test chiều *"phải đỏ"* chỉ chứng minh rule **có khả năng** bắt. Test chiều *"phải im lặng"* mới chứng minh nó **không bắt nhầm** — và với một rule mới, bắt nhầm là kiểu hỏng dễ xảy ra hơn.
|
|
161
|
+
|
|
162
|
+
## B6 · Copy được / không copy được
|
|
163
|
+
|
|
164
|
+
| | |
|
|
165
|
+
|---|---|
|
|
166
|
+
| ✅ **Copy được sang dự án khác** | Nguyên tắc **phạm vi neo vào sự tồn tại của hợp đồng** — một rule mới chỉ kiểm nơi đã có lời khẳng định, để dự án cũ không đỏ hàng loạt. Cách khai rule trong schema rồi để một rule khác canh nó có thực sự tồn tại. Thiết kế test **hai chiều**, và ca "phá có chủ đích" để chứng minh cơ chế canh hoạt động. Nguyên tắc "không parse được → im lặng, không đoán" |
|
|
167
|
+
| ⚠️ **Chỉ đúng ở đây** | Cấu trúc bảng §4.5.6, cách đọc cột theo vị trí, và chi tiết `tech_docs_dir` trùng `specs_dir` — đều gắn với bố cục tài liệu của framework này |
|
|
168
|
+
|
|
169
|
+
## B7 · Link
|
|
170
|
+
|
|
171
|
+
- Kế hoạch trước khi làm: [`../exec-d0-b4-may-canh-4-5-6.md`](../exec-d0-b4-may-canh-4-5-6.md) — §3 ghi lại cả ba chỗ đã sửa so với bản gốc
|
|
172
|
+
- Tài liệu rule: `docs/04-reference/trace-schema.md` §Hợp đồng test-id
|
|
173
|
+
- Bước liên quan: [`0-06`](0-06-hop-dong-truoc-code.md) (thêm `T17`/`T18` so bảng với **code**) · [`1-04`](1-04-self-review-dung-chung.md) (ranh giới: nơi nào có guard thì đừng thay bằng tự kiểm)
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Dọn nhãn cột và tên mục đã chết
|
|
2
|
+
|
|
3
|
+
| | |
|
|
4
|
+
|---|---|
|
|
5
|
+
| **Đợt** | 0 — Hợp đồng test-id |
|
|
6
|
+
| **Bước** | **5/6** của Đợt 0 |
|
|
7
|
+
| **File sửa** | `commands/{map-testids,qc-design-test,qc-run-test}.tmpl` · `modules/qc-playwright/stack-profile.yaml` · `skills/spec/SKILL.tmpl` · `specs/domain-knowledge/figma-components/{react,flutter}.md` |
|
|
8
|
+
| **File test** | Không có test tự động — xem B4 |
|
|
9
|
+
| **Ngày xong** | 2026-09-11 · commit `931ffe2` |
|
|
10
|
+
| **Phụ thuộc** | [`0-01`](0-01-testid-attr-co-cho-o.md) → [`0-04`](0-04-may-canh-hop-dong.md) |
|
|
11
|
+
| **Ai dùng nó** | Mọi lệnh đọc/ghi bảng §4.5.6 |
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# PHẦN A — Chuyện gì đang xảy ra
|
|
16
|
+
|
|
17
|
+
## A1 · Vấn đề
|
|
18
|
+
|
|
19
|
+
Trong công ty, cùng một cái cột trong cùng một bảng biểu đang được gọi bằng **hai cái tên**: bản hướng dẫn A gọi là *"Phục vụ"*, bản hướng dẫn B gọi là *"Serves"*. Người đọc bản B mở bảng ra, tìm cột *"Serves"*, không thấy, và kết luận **bảng này không có thông tin đó**.
|
|
20
|
+
|
|
21
|
+
Tệ hơn: bốn chỗ trong tài liệu vẫn chỉ người ta tới **mục §2b** — một mục đã bị đánh số lại từ lâu và **không còn tồn tại**. Ai đi theo chỉ dẫn đó sẽ tìm mãi không ra, rồi tự xoay xở cách khác.
|
|
22
|
+
|
|
23
|
+
## A2 · Cách giải quyết, nói bằng một hình ảnh
|
|
24
|
+
|
|
25
|
+
> **Như đổi biển chỉ đường sau khi đổi tên phố.** Đổi tên phố mà quên gỡ biển cũ thì người
|
|
26
|
+
> lạ vẫn đi theo biển cũ, tới nơi không thấy gì, rồi hỏi đường — và kết luận là bản đồ sai.
|
|
27
|
+
|
|
28
|
+
Dọn hết biển cũ, và thống nhất **một** cách gọi tên cho mỗi thứ.
|
|
29
|
+
|
|
30
|
+
## A3 · Xong rồi thì thấy gì khác
|
|
31
|
+
|
|
32
|
+
Không thấy gì mới — đây là dọn dẹp. Cái quan sát được: tìm chữ `§2b` hay `Serves SC` trong toàn bộ mã nguồn giờ ra **không kết quả nào**.
|
|
33
|
+
|
|
34
|
+
## A4 · Thuật ngữ dùng ở trên
|
|
35
|
+
|
|
36
|
+
- **§4.5.6 / §2b** — số hiệu mục trong tài liệu thiết kế kỹ thuật. Mục này từng mang số `2b`, sau đó được đánh lại thành `4.5.6`.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
> ### ✅ Phép thử người đọc
|
|
41
|
+
>
|
|
42
|
+
> **Đã 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.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
# PHẦN B — Chi tiết kỹ thuật
|
|
47
|
+
|
|
48
|
+
## B1 · Cách hiển nhiên là gì, và vì sao nó sai
|
|
49
|
+
|
|
50
|
+
### Cách sai thứ nhất — `sed` một phát trên toàn repo
|
|
51
|
+
|
|
52
|
+
Lỗi đổi tên thì đổi tên. Một dòng lệnh, xong.
|
|
53
|
+
|
|
54
|
+
Sai vì repo có **ba tầng file cho cùng một nội dung**: nguồn (`commands/*.tmpl`), bản đúc (`commands/*.md`, `core/`), và bản mirror dogfood (`.agent/`). `sed` trên tất cả thì sửa được cả ba — nhưng **lần `npm run build` sau sẽ đúc lại từ nguồn và ghi đè**. Nếu lỡ tay sửa bản đúc mà quên nguồn, thay đổi biến mất mà không ai biết vì sao.
|
|
55
|
+
|
|
56
|
+
Phải sửa **file nguồn**, rồi build. Đây là lý do phép thử #1 ở B4 kiểm cả bản đúc — để chắc thay đổi đã đi qua đúng đường.
|
|
57
|
+
|
|
58
|
+
### Cách sai thứ hai — bỏ qua `stack-profile.yaml` vì Đợt 2 sẽ thay nó
|
|
59
|
+
|
|
60
|
+
File `modules/qc-playwright/stack-profile.yaml` sẽ bị **thay hẳn** ở Đợt 2 (Python → TypeScript). Sửa bây giờ là công cốc — kế hoạch cũng gợi ý cân nhắc bỏ qua.
|
|
61
|
+
|
|
62
|
+
Sai vì ba lẽ: một dòng, chi phí gần bằng không; nó **sai ngay hôm nay** và Đợt 2 còn xa; và nếu Đợt 2 trượt lịch thì câu sai nằm lại vô thời hạn. Bỏ qua chỉ tiết kiệm được một lần `sed`.
|
|
63
|
+
|
|
64
|
+
### Cách sai thứ ba — nghĩ cột 3 chỉ là "khác cách gọi"
|
|
65
|
+
|
|
66
|
+
Hai chỗ mô tả cột thứ ba của bảng:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
template: | … | Component (§4.5.1.x) | … |
|
|
70
|
+
map-testids.tmpl:68: | … | Component (reused/existing/new) | … |
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Nhìn qua tưởng cùng một cột gọi hai kiểu. **Không phải** — chúng đòi hai thứ **khác nội dung**: một bên hỏi *component nào trong cây*, bên kia hỏi *id này từ đâu ra*.
|
|
74
|
+
|
|
75
|
+
## B2 · Cách làm đúng
|
|
76
|
+
|
|
77
|
+
**a) Nhãn cột 5 — chốt theo template**, vì `generate-tech-docs` include template để **sinh ra** bảng, nên template là bản gốc. Đổi 5 chỗ nguồn từ `"Serves SC"` sang `"Phục vụ SC"` — khớp **nguyên văn** thứ agent thật sự nhìn thấy trong bảng.
|
|
78
|
+
|
|
79
|
+
**b) Cột 3 — bỏ phân loại `reused/existing/new` khỏi bảng.** Lý do không phải thẩm mỹ: phân loại đó **thay đổi theo thời gian** — một element `new` thành `existing` **ngay khi dev viết code**. Nhét dữ liệu biến thiên vào bảng hợp đồng là làm bảng sai dần mà không ai đi cập nhật lại. Phân loại vẫn giữ nguyên vai trò **nội bộ lúc chạy** ở Step 1–4 của `/map-testids` (nó rẽ nhánh theo đó).
|
|
80
|
+
|
|
81
|
+
Cột 3 nhận `§4.5.1.x`, **hoặc tên component** khi §4.5.1 chưa vẽ — cần nhánh sau vì ca brownfield: `/map-testids` có thể tạo tech-doc tối thiểu chỉ có §4.5.6.
|
|
82
|
+
|
|
83
|
+
**c) Ghi thẳng vào lệnh một điều trước đây không ai biết:**
|
|
84
|
+
|
|
85
|
+
> *"Thứ tự cột là load-bearing: `lint-trace` **T15** đọc cột 'Phục vụ SC' theo **vị trí** (ô nội dung cuối cùng). Viết sai thứ tự là T15 đọc nhầm ô."*
|
|
86
|
+
|
|
87
|
+
Sau bước `0-04` đây là hệ quả **thật**, không phải lý thuyết — và nó là lý do bước dọn dẹp này có sức nặng hơn lúc lập kế hoạch.
|
|
88
|
+
|
|
89
|
+
**d) "§2b" → "§4.5.6"** ở 4 chỗ nguồn. Kế hoạch chỉ liệt kê 3; `flutter.md` bị bỏ sót hẳn, và `SKILL.md` thực ra phải sửa ở `SKILL.tmpl` vì `.md` là bản đúc.
|
|
90
|
+
|
|
91
|
+
## B3 · Nếu làm sai thì hỏng theo kiểu nào
|
|
92
|
+
|
|
93
|
+
**Kiểu hỏng: im lặng** 🔴
|
|
94
|
+
|
|
95
|
+
| | |
|
|
96
|
+
|---|---|
|
|
97
|
+
| Hỏng ở đâu | Agent đọc "§2b" đi tìm một mục không tồn tại trong tech-doc |
|
|
98
|
+
| Ai phát hiện | Không ai — agent **không báo lỗi**, nó kết luận *"tech-doc không có bảng test selector"* rồi đi dò DOM |
|
|
99
|
+
| Sau bao lâu | Không bao giờ, nếu không ai đọc lại script sinh ra |
|
|
100
|
+
| Vì sao đắt | Đây **đúng vòng lỗi mà [`0-03`](0-03-skill-thoi-day-do-dom.md) vừa chặn**, chỉ đi qua một cửa khác. Sửa skill xong mà để biển chỉ đường sai thì kết quả y hệt |
|
|
101
|
+
|
|
102
|
+
Riêng nhãn cột lệch có thêm một tầng sau bước `0-04`: người viết bảng theo văn bản chỉ dẫn, mà hai văn bản đang mô tả hai bảng khác nhau → viết sai **thứ tự cột** → `T15` đọc nhầm ô.
|
|
103
|
+
|
|
104
|
+
## B4 · Verify bằng gì
|
|
105
|
+
|
|
106
|
+
| # | Phép thử | Kết quả |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| 1 | `grep -rn "§2b"` toàn repo, **kể cả bản đúc** (trừ `GAPS` lịch sử) | ✅ 0 |
|
|
109
|
+
| 2 | `grep -rn "Serves SC" commands/ modules/ skills/ specs/` | ✅ 0 |
|
|
110
|
+
| 3 | `grep -n "Mỗi row:" commands/map-testids.tmpl` | ✅ 5 cột khớp template, không còn `(reused/existing/new)` |
|
|
111
|
+
| 4 | `build` · `self-check` · `test` · `lint-trace` | ✅ 212/212 |
|
|
112
|
+
|
|
113
|
+
Phép thử 1 cố ý kiểm **cả bản đúc** — nếu bản đúc còn "§2b" trong khi nguồn đã sạch thì nghĩa là chưa build, hoặc đã sửa nhầm tầng.
|
|
114
|
+
|
|
115
|
+
## B5 · Bài học
|
|
116
|
+
|
|
117
|
+
- **2026-09-11** — Phát hiện ngoài dự kiến: `modules/qc-playwright/stack-profile.yaml:22` có **4 dấu `"`** trong một YAML scalar được bọc `"..."` — tức **sai cú pháp YAML**. Chưa vỡ vì **không có parser nào đọc file này** (`grep yaml bin/*.js` → 0; chỉ agent đọc). Dòng 49 cùng file thì escape đúng (`\"`), và các module khác sạch — nên đây là ca lẻ. Đã sửa luôn khi đụng vào (dùng nháy đơn bên trong).
|
|
118
|
+
→ **Bài học chuyển giao được:** file cấu hình **không ai parse bằng máy** là chỗ lỗi cú pháp sống lâu nhất. Chúng chỉ nổ vào ngày có người thêm một parser — và khi đó lỗi trông như lỗi của parser mới.
|
|
119
|
+
|
|
120
|
+
- **2026-09-11** — Kế hoạch liệt kê 3 chỗ có "§2b", thực tế **4** (thiếu `flutter.md`), và một chỗ ghi nhầm tầng (`SKILL.md` thay vì `SKILL.tmpl`).
|
|
121
|
+
→ **Bài học chuyển giao được:** danh sách "các chỗ cần sửa" viết trong kế hoạch luôn phải **chạy lại grep lúc triển khai**, không dùng lại danh sách cũ. Kế hoạch viết ở một thời điểm; repo đổi sau đó.
|
|
122
|
+
|
|
123
|
+
## B6 · Copy được / không copy được
|
|
124
|
+
|
|
125
|
+
| | |
|
|
126
|
+
|---|---|
|
|
127
|
+
| ✅ **Copy được sang dự án khác** | Nguyên tắc: khi cùng một thứ được gọi bằng hai tên, chọn **bản gốc là nơi sinh ra nó**, không phải nơi dùng nó. Nguyên tắc: **đừng nhét dữ liệu biến thiên vào bảng hợp đồng** — hợp đồng là thứ không ai đi cập nhật lại. Và: sửa file **nguồn** rồi build, đừng sửa bản đúc |
|
|
128
|
+
| ⚠️ **Chỉ đúng ở đây** | Tên cột, số hiệu mục, và ba tầng file (nguồn/đúc/mirror) đều là đặc thù của framework này |
|
|
129
|
+
|
|
130
|
+
## B7 · Link
|
|
131
|
+
|
|
132
|
+
- Kế hoạch trước khi làm: [`../exec-d0-b5-don-nhan-lech.md`](../exec-d0-b5-don-nhan-lech.md) — phần "đưa `/map-testids` vào golden path" đã **chuyển sang** [`0-06`](0-06-hop-dong-truoc-code.md)
|
|
133
|
+
- Bước liên quan: [`0-03`](0-03-skill-thoi-day-do-dom.md) (cùng vòng lỗi, cửa khác) · [`0-04`](0-04-may-canh-hop-dong.md) (làm thứ tự cột thành load-bearing)
|