@educa-corp/sdd-framework 0.9.3 → 0.9.4
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/qc-base-map.json +119 -49
- package/bin/self-check.js +30 -0
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/qc-analyze.md +6 -119
- package/core/commands/qc-design-test.md +123 -143
- package/core/commands/qc-plan.md +6 -119
- package/core/commands/qc-review.md +59 -125
- package/core/commands/qc-run-test.md +6 -119
- 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/rules/workflow.md +1 -1
- 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 +1 -1
- package/docs/02-concepts/pipeline-steps/09-validate-traces.md +1 -1
- package/docs/04-reference/trace-schema.md +1 -1
- package/docs/explain/00-setup-ai-first.md +1 -1
- package/docs/plans/qc-implementation-log.md +145 -3
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
version: 1.0
|
|
3
|
-
updated: 2026-
|
|
3
|
+
updated: 2026-09-04
|
|
4
4
|
ported_from: ai-automation-qc-base
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -9,6 +9,9 @@ ported_from: ai-automation-qc-base
|
|
|
9
9
|
> Stage 3 của QC automation pipeline native (qc-analyze → qc-plan → qc-design-test → qc-review → qc-run-test → qc-report). Port từ qa-designer của team QC. Sinh test case Markdown (`.Test.md`) — Python đến sau ở /qc-run-test.
|
|
10
10
|
|
|
11
11
|
## Gate
|
|
12
|
+
|
|
13
|
+
*Checkpoint: **chặn thường** — lệnh ghi file test-case. `--yes` bỏ qua được (gate Bước 3a).*
|
|
14
|
+
|
|
12
15
|
# Gate — Quy trình vào chuẩn cho mọi lệnh
|
|
13
16
|
|
|
14
17
|
Mọi lệnh PHẢI chạy gate này trước khi thực thi phần logic riêng của nó.
|
|
@@ -174,136 +177,73 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
|
174
177
|
|
|
175
178
|
## Phạm vi QC
|
|
176
179
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
**Chạy TRƯỚC phần logic riêng của lệnh, và SAU `steps/gate.md`.** Bước này chốt bốn thứ mà
|
|
180
|
-
cả 6 trạm QC đều cần, để chúng không tự suy mỗi trạm một kiểu:
|
|
181
|
-
|
|
182
|
-
| Biến | Là gì |
|
|
183
|
-
|---|---|
|
|
184
|
-
| `TICKET-ID` | mã PRD — **thư mục artifact QC mang tên này** |
|
|
185
|
-
| `active_platform` | `web` \| `app` \| `system` \| … — một QC pass khoá đúng MỘT nền |
|
|
186
|
-
| `qc_artifact_dir` | `{paths.qc_dir}/{TICKET-ID}/{active_platform}/` |
|
|
187
|
-
| `uc_list` | các UC của (PRD × nền) này, kèm trạng thái BDD từng UC |
|
|
188
|
-
|
|
189
|
-
> **Vì sao gom về một chỗ.** Luật phân giải nền từng được copy-paste ở 5 lệnh và câu chữ đã
|
|
190
|
-
> lệch nhau. Năm bản của một luật là nơi drift sống: sửa bốn, quên một, và trạm bị quên ghi
|
|
191
|
-
> artifact vào sai thư mục **trong im lặng**.
|
|
192
|
-
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
## 1 — `TICKET-ID`
|
|
196
|
-
|
|
197
|
-
Artifact QC gom theo **PRD**, không theo UC. Nên mọi trạm phải quy được về `TICKET-ID`:
|
|
198
|
-
|
|
199
|
-
| `$ARGUMENTS` là | Cách lấy |
|
|
200
|
-
|---|---|
|
|
201
|
-
| **UC-ID** (`{TICKET-ID}-UC{N}`) | phần **trước** `-UC` — đúng luật `steps/gate.md` Bước 1 dùng để tìm tech-doc gộp |
|
|
202
|
-
| **TICKET-ID** | dùng trực tiếp |
|
|
203
|
-
| một **path file** (`.feature` / PRD / design-spec) | phân giải `{domain}` + `{prd-slug}` theo luật `context-loader` Bước 1, rồi lấy `TICKET-ID` từ tên file PRD `{TICKET-ID}-{prd-slug}.md` — file `.md` duy nhất ở gốc feature folder |
|
|
204
|
-
|
|
205
|
-
Đối chiếu: `TICKET-ID` suy ra phải khớp tên file PRD thật. Lệch → **DỪNG**, in cả hai giá
|
|
206
|
-
trị. (Suy sai `TICKET-ID` là ghi cả một PRD vào sai thư mục — không có bước nào phía sau bắt được.)
|
|
207
|
-
|
|
208
|
-
---
|
|
209
|
-
|
|
210
|
-
## 2 — `active_platform`
|
|
211
|
-
|
|
212
|
-
> **PHẢI phân giải TRƯỚC mọi phép đọc `.feature`.** `{UC-ID}-SC{N}` chỉ độc nhất trong
|
|
213
|
-
> (UC × nền), nên một UC đa nền có **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`,
|
|
214
|
-
> `bdd/system/` — và mỗi file mang `@trace.status` **riêng**: bản web có thể `approved`
|
|
215
|
-
> trong khi bản app còn `draft`. Đọc "file `.feature` của UC" khi chưa biết nền là đọc một
|
|
216
|
-
> file **bất kỳ trong ba**: báo `approved` trong khi bản đang dùng còn nháp, hoặc chặn oan
|
|
217
|
-
> một bản đã duyệt.
|
|
218
|
-
|
|
219
|
-
Theo thứ tự, dừng ở cái đầu tiên khớp:
|
|
220
|
-
|
|
221
|
-
1. `$ARGUMENTS` nêu nền (`web`/`app`/`system`/…) → dùng.
|
|
222
|
-
2. Target là một file `.feature` → đọc `# @trace.platform` của nó.
|
|
223
|
-
3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/` — **đúng một** thư mục nền → dùng nó.
|
|
224
|
-
4. Glob `{paths.qc_dir}/{TICKET-ID}/*/` — **đúng một** thư mục nền đã có artifact → dùng nó.
|
|
225
|
-
*(chỉ dùng cho trạm 2–6; trạm `/qc-analyze` là trạm tạo ra thư mục đó nên không có gì để soi.)*
|
|
226
|
-
5. Nhiều nền mà không suy được → hỏi *"QC pass này cho nền nào? (web/app/system)"*.
|
|
227
|
-
**Có `--yes`:** không hỏi — DỪNG với lỗi rõ ràng, vì đoán bừa nền là ghi artifact vào sai
|
|
228
|
-
thư mục và ghi `qc_status` vào sai sổ trace:
|
|
229
|
-
```
|
|
230
|
-
❌ {TICKET-ID} có {n} nền ({list}) — không suy được nền nào cho QC pass này.
|
|
231
|
-
Chạy headless thì phải nêu tường minh: /{lệnh} {TICKET-ID} web --yes
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
Lưu `active_platform`. Từ đây, **mọi** phép đọc `.feature` chỉ đọc thư mục
|
|
235
|
-
`bdd/{active_platform}/` — không trộn SC chéo nền.
|
|
236
|
-
|
|
237
|
-
---
|
|
238
|
-
|
|
239
|
-
## 3 — `qc_artifact_dir`
|
|
240
|
-
|
|
241
|
-
```
|
|
242
|
-
qc_artifact_dir = {paths.qc_dir}/{TICKET-ID}/{active_platform}/
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
Chứa: `REQUIREMENT_ANALYSIS.md` · `DOC_GAP.md` · `TEST_PLAN.md` · `test-cases/*.Test.md`
|
|
246
|
-
— **mỗi loại đúng MỘT file cho cả PRD**, các UC là mục/hàng bên trong.
|
|
180
|
+
**BẮT BUỘC — đọc `.agent/steps/qc-scope.md` và thực thi TOÀN BỘ quy trình trong đó**,
|
|
181
|
+
rồi mới tiếp tục phần bên dưới.
|
|
247
182
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
183
|
+
Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
|
|
184
|
+
`qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--include-draft`).
|
|
185
|
+
Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —
|
|
186
|
+
cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
|
|
251
187
|
|
|
252
|
-
> **
|
|
253
|
-
>
|
|
254
|
-
>
|
|
188
|
+
> **Trạm này vẫn gọi theo TỪNG UC** *(B11)* — thiết kế và chạy test **thật sự** làm tăng dần
|
|
189
|
+
> theo UC, nên giữ khả năng làm UC1 khi UC3 chưa xong là đúng. Chỉ **chỗ đọc/ghi** đổi: mọi
|
|
190
|
+
> artifact nằm chung ở `{qc_artifact_dir}` cấp PRD, không còn một thư mục mỗi UC.
|
|
191
|
+
>
|
|
192
|
+
> Nên `DOC_GAP.md` / `TEST_PLAN.md` đọc được ở đây phủ **cả PRD**: **lọc theo cột `UC`** để lấy
|
|
193
|
+
> phần của UC đang làm. Đừng coi toàn bộ bảng gap là của UC này — sẽ chặn oan.
|
|
255
194
|
|
|
256
195
|
---
|
|
257
196
|
|
|
258
|
-
##
|
|
197
|
+
## Cờ — chọn tầng nào, tách mịn đến đâu
|
|
259
198
|
|
|
260
|
-
|
|
261
|
-
UC: đọc `# @trace.id` (mã UC) và `# @trace.status` từ header.
|
|
262
|
-
|
|
263
|
-
Chia hai nhóm:
|
|
264
|
-
|
|
265
|
-
| Nhóm | Điều kiện | Xử lý |
|
|
199
|
+
| Cờ | Ghi file | Nhóm |
|
|
266
200
|
|---|---|---|
|
|
267
|
-
|
|
|
268
|
-
|
|
|
201
|
+
| *(không cờ)* | `TC_<FEATURE>.Test.md` | 1 GUI · 2 Validation · 3 Functional · 4 Integration (qua UI) · 5 NFR · 6 E2E |
|
|
202
|
+
| `--api` | `TC_<FEATURE>_API.Test.md` | 1 Endpoint · 2 Integration API/DB/Kafka |
|
|
203
|
+
| `--all` | **cả hai file** | như trên, hai file riêng biệt |
|
|
204
|
+
| `--atomic-max` | — | bật chế độ tách tối đa (xem dưới) |
|
|
269
205
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
✅ {UC-ID} {tên UC} approved
|
|
274
|
-
⏸ {UC-ID} {tên UC} draft → chưa xét
|
|
275
|
-
→ {n} UC trong phạm vi · {m} chưa xét
|
|
276
|
-
```
|
|
206
|
+
**Câu hỏi phân file:** *"TC này verify được mà **không cần UI** không?"* → **có** = file API ·
|
|
207
|
+
**không** = file giao diện. Đây đúng là Bước 2 của
|
|
208
|
+
`{paths.qc_skills_dir}/qa-designer/shared/skill-decision-tree.md`.
|
|
277
209
|
|
|
278
|
-
**
|
|
279
|
-
|
|
210
|
+
> **Vì sao hai file riêng chứ không một file rồi ghi từng nhóm** *(B12)*. `--api` chạy sau khi
|
|
211
|
+
> file giao diện đã tồn tại là chuyện thường. Nếu chung một file, lần chạy `--api` phải **giữ
|
|
212
|
+
> nguyên** năm nhóm kia — mà viết-lại-cả-file là đúng thứ agent hay làm. Hai file thì hai chế
|
|
213
|
+
> độ **không bao giờ chạm nhau**, và không cần tin vào việc agent nhớ giữ phần cũ.
|
|
280
214
|
|
|
281
|
-
|
|
282
|
-
```
|
|
283
|
-
❌ {TICKET-ID} ({active_platform}): 0/{n} UC có BDD approved — không có gì để chạy.
|
|
284
|
-
Cách đúng: người duyệt đặt `# @trace.status: approved` rồi chạy lại.
|
|
285
|
-
Muốn chạy sớm trên BDD nháp (prototype): thêm --include-draft
|
|
286
|
-
```
|
|
215
|
+
### `--atomic-max` — tách tới mức nhỏ nhất
|
|
287
216
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
> lấy đi một năng lực đang có mà không ai khai. Còn để mặc định `approved`-only thì cái
|
|
291
|
-
> thường gặp là cái an toàn, và cái sớm phải nói ra.
|
|
217
|
+
Mặc định đã là ATOMIC (mỗi TC đúng một kết cục), nhưng **cho phép** bullet compound khi cả hai
|
|
218
|
+
vế nói về **cùng một** kết cục — ví dụ *"form hiển thị đủ 3 trường"* vẫn là 1 TC.
|
|
292
219
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
> là để một lần chạy headless âm thầm phân tích spec chưa chốt rồi bàn giao như thể đã chốt.
|
|
220
|
+
`--atomic-max` **nổ danh sách completeness thành từng thành phần**: thẻ hiển thị đủ 5 thông tin
|
|
221
|
+
→ **5 TC**. Ở đội QC, một tính năng đi từ 82 lên 130 TC.
|
|
296
222
|
|
|
223
|
+
**⛔ Thủ tục bắt buộc TRƯỚC khi ghi** *(theo `shared/precision-rules.md` §2.1)*:
|
|
297
224
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
> phần của UC đang làm. Đừng coi toàn bộ bảng gap là của UC này — sẽ chặn oan.
|
|
225
|
+
1. **Chốt ranh giới completeness** — hai mức có quy mô rất khác nhau:
|
|
226
|
+
| Giá trị | Nghĩa |
|
|
227
|
+
|---|---|
|
|
228
|
+
| `--atomic-max=keep-completeness` | giữ danh sách completeness là 1 TC (~95 TC ở ví dụ thật) |
|
|
229
|
+
| `--atomic-max=explode` | nổ từng thành phần (~130 TC) |
|
|
304
230
|
|
|
305
|
-
|
|
231
|
+
Không nêu giá trị → **HỎI** trước khi ghi, vì đây là quyết định quy mô của người chủ, không
|
|
232
|
+
phải của agent. **Có `--yes`:** không hỏi — **DỪNG** với lỗi rõ ràng:
|
|
233
|
+
```
|
|
234
|
+
❌ --atomic-max cần nêu ranh giới ở chế độ headless (quy mô chênh ~35%).
|
|
235
|
+
Chạy: /qc-design-test {UC-ID} --atomic-max=explode --yes
|
|
236
|
+
hoặc --atomic-max=keep-completeness --yes
|
|
237
|
+
```
|
|
238
|
+
*(Một cổng chỉ chặn được khi có người ngồi đó thì ở chế độ headless nó không phải cổng, nó
|
|
239
|
+
là treo — cùng bài học với `steps/qc-scope.md` §2.)*
|
|
306
240
|
|
|
241
|
+
2. **Dựng bản đồ tách trước khi ghi** — mỗi TC cha tách ra mấy mảnh. Đừng để agent con tự suy
|
|
242
|
+
trong lúc ghi; đó là chỗ sinh ra TC cắt cụt.
|
|
243
|
+
3. **GIỮ 1 TC** cho bốn loại này kể cả ở chế độ tối đa: *predicate đa-điều-kiện định-nghĩa-một-
|
|
244
|
+
khái-niệm* · *ngưỡng* (`≥44pt`, `WCAG AA ≥4.5:1`) · *exclusivity* chọn-một · *qualifier*.
|
|
245
|
+
4. **Verify bằng DIFF, không tin lời đếm của agent** — ID liên tục · mỗi Expected 1 bullet ·
|
|
246
|
+
0 ký tự `|` · Trace matrix không còn ID chết · P0/P1 khớp tag.
|
|
307
247
|
|
|
308
248
|
---
|
|
309
249
|
|
|
@@ -313,44 +253,78 @@ Bạn là **QC Designer** — stage 3. Sinh/bảo trì các file test-case Markd
|
|
|
313
253
|
(`.Test.md`) từ requirement đã phân tích + plan. Output feed vào qc-run-test (Python) và
|
|
314
254
|
qc-review. Bạn **không** viết Python.
|
|
315
255
|
|
|
316
|
-
## Skills
|
|
256
|
+
## Skills (`{paths.qc_skills_dir}/qa-designer/`)
|
|
257
|
+
|
|
258
|
+
**Luật dùng chung — nạp theo tầng, không nạp hết mỗi lần:**
|
|
317
259
|
|
|
318
|
-
|
|
|
260
|
+
| Khi nào | Nạp |
|
|
319
261
|
|---|---|
|
|
320
|
-
|
|
|
321
|
-
|
|
|
322
|
-
|
|
|
323
|
-
|
|
|
324
|
-
|
|
|
325
|
-
|
|
|
326
|
-
|
|
|
262
|
+
| **Luôn** | `shared/tc-metadata-format.md` — khuôn TC · luật ATOMIC · phân nhóm · hai file `.Test.md` · Trace · `🚫 Block` · `Test-ID attribute` |
|
|
263
|
+
| **Luôn** | `shared/precision-rules.md` — cấm 9 cụm mơ hồ · đơn vị theo domain · toán tử · BVA 3-hay-4 giá trị · phủ đủ ô decision table · 3 mức teardown |
|
|
264
|
+
| **Luôn** | `shared/action-keywords-glossary.md` — một hành động một từ (Click/Tap/Enter/Select…) |
|
|
265
|
+
| **Luôn** | `shared/implicit-scenarios.md` — 7 tình huống bắt buộc nghĩ tới, áp cho mọi nhóm |
|
|
266
|
+
| Đầu phiên, một lần | `shared/read-doc-gap-inputs.md` — đọc **đủ** mọi file trong bảng *Tài liệu đầu vào đã đọc* của `DOC_GAP.md`, không chỉ đọc bản tóm tắt gap |
|
|
267
|
+
| Không chắc chọn tầng nào | `shared/skill-decision-tree.md` — cây quyết định UI / Integration-GUI / E2E / Integration |
|
|
268
|
+
| Thêm TC vào file đã có | `shared/duplicate-check-procedure.md` — grep trước khi viết; và **trùng chéo UC** giờ mới thực sự xảy ra vì mọi UC dùng chung một thư mục |
|
|
269
|
+
| `--api` / `--all` | `api/endpoint.md` · `api/auth-chain.md` · `api/http-status-codes.md` · `api/common-headers.md` · `api/crud-sequence.md` · `api/auth-sequence.md` |
|
|
270
|
+
|
|
271
|
+
**Rồi chọn tầng, nạp MỘT file:**
|
|
272
|
+
|
|
273
|
+
| Layer | File | Ghi vào |
|
|
274
|
+
|---|---|---|
|
|
275
|
+
| 1 màn hình | `functional/gui-screen.md` | file giao diện |
|
|
276
|
+
| Feature đa-màn | `functional/gui-feature.md` | file giao diện |
|
|
277
|
+
| API endpoint | `functional/api.md` | **file API** |
|
|
278
|
+
| Integration qua UI | `integration/gui.md` | file giao diện |
|
|
279
|
+
| Integration API/DB/Kafka | `integration/{api,db,kafka}.md` | **file API** |
|
|
280
|
+
| End-to-end journey | `e2e/journey.md` | file giao diện |
|
|
281
|
+
| Non-functional | `non-functional.md` | theo câu hỏi phân file |
|
|
282
|
+
| Exploratory | `exploratory/{charter,explore-to-functional}.md` | file giao diện |
|
|
327
283
|
|
|
328
284
|
## Conventions
|
|
329
285
|
|
|
286
|
+
*Khuôn TC, luật ATOMIC, phân nhóm, không-dùng-bảng, Trace, `🚫 Block`, `Test-ID attribute` —
|
|
287
|
+
đều ở `shared/tc-metadata-format.md`. **Đừng lặp lại ở đây.** Dưới đây chỉ là thứ riêng của trạm:*
|
|
288
|
+
|
|
330
289
|
- Markdown-first; đừng design khi requirement chưa rõ — đẩy ngược về qc-plan/qc-analyze.
|
|
331
|
-
- TC ID `TC_<FEATURE>_<NNN>`; tách happy/negative; mỗi TC một mối quan tâm assertion; giá trị expected cụ thể.
|
|
332
|
-
- Priority `P0/P1/P2`; Tags (`smoke regression happy-path negative ui …`); Status `Draft → In Progress → Pass/Fail/Skip`.
|
|
333
|
-
- Một TC bị block bởi gap vẫn được viết + đánh dấu `🚫 Block: GAP-xx`.
|
|
334
290
|
- **Tham chiếu test-id, không phải gợi ý hình ảnh.** Với mỗi step GUI tác động lên một element, trích test-id ổn định từ bảng §4.5.6 của tech-doc gộp (vd "click `ft001-login-submit-btn`") để qc-run-test dựng locator từ contract. Nếu một element có action không có test-id trong §4.5.6, ghi chú lại (qc-run-test sẽ fallback về locator role/text chậm hơn).
|
|
335
|
-
-
|
|
336
|
-
-
|
|
291
|
+
- **Đọc `@trace.testid_attr` từ header tech-doc gộp** (do `/map-testids` ghi) và ghi lại một
|
|
292
|
+
dòng `Test-ID attribute: {attr}` ở metadata đầu file `.Test.md`. §4.5.6 chỉ cho **giá trị**
|
|
293
|
+
test-id; `@trace.testid_attr` là **tên thuộc tính** chứa chúng — `/qc-run-test` cần nó để cấu
|
|
294
|
+
hình locator, `/qc-review` cần nó để biết selector trong script có đúng contract không.
|
|
295
|
+
Không đọc được `@trace.testid_attr` → ghi
|
|
296
|
+
`Test-ID attribute: — (thiếu @trace.testid_attr, chạy /map-testids)`; **đừng bỏ trống, đừng
|
|
297
|
+
tự đoán theo platform** — đoán sai thì mọi locator trượt 100%, vì một lý do không liên quan
|
|
298
|
+
gì tới thứ đang được test. *(Dạng dòng metadata: `shared/tc-metadata-format.md`.)*
|
|
299
|
+
- Một TC bị block bởi gap **vẫn viết đủ** + `🚫 Block: [GAP-UC{N}-{nnn}](../DOC_GAP.md) — lý do`.
|
|
300
|
+
Chặn theo **UC ở cột `UC` của hàng gap đó**, không chặn cả PRD.
|
|
337
301
|
|
|
338
302
|
## Trace mapping (bắt buộc)
|
|
339
303
|
|
|
340
|
-
Ngoài trace `BR-xx`, mỗi TC ghi scenario framework mà nó verify
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
304
|
+
Ngoài trace `BR-xx`, mỗi TC ghi scenario framework mà nó verify — **dạng trường danh sách,
|
|
305
|
+
không phải cột bảng** (file TC không có ký tự `|`):
|
|
306
|
+
|
|
307
|
+
```
|
|
308
|
+
- **@trace.verifies:** {UC-ID}-SC{N}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Lấy từ `@trace.scenario` của `.feature`. Một SC map được nhiều TC. Đây là **join key** cho phép
|
|
312
|
+
qc-run-test ghi `qc_status` theo từng scenario vào sổ trace — **thiếu nó thì test vẫn chạy,
|
|
313
|
+
vẫn pass/fail, nhưng kết quả không vào được sổ và không ai biết nó phủ kịch bản nào.**
|
|
314
|
+
|
|
315
|
+
Cuối file: **Trace matrix** (BR ↔ TC ↔ SC) + **danh sách TC bị block** — cả hai dạng danh sách.
|
|
344
316
|
|
|
345
317
|
## Output
|
|
346
318
|
|
|
347
|
-
Ghi
|
|
348
|
-
(= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/test-cases/`) — **một thư mục
|
|
349
|
-
|
|
350
|
-
|
|
319
|
+
Ghi dưới `{qc_artifact_dir}test-cases/`
|
|
320
|
+
(= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/test-cases/`) — **một thư mục dùng chung cho cả
|
|
321
|
+
PRD**. Tên file mang `<FEATURE>` nên các UC không đâm nhau; `@trace.verifies` là chỗ phân biệt
|
|
322
|
+
TC thuộc UC nào.
|
|
323
|
+
|
|
324
|
+
**Đuôi bắt buộc là `.Test.md`.** `/qc-run-test` và `/qc-review` tìm `*.Test.md`; ghi ra
|
|
325
|
+
`TC_<FEATURE>.md` là ghi ra thứ **không trạm nào tìm thấy**, và **không có gì báo lỗi**.
|
|
351
326
|
|
|
352
|
-
|
|
353
|
-
(`DOC_GAP.md` nằm ở thư mục cha của `test-cases/`) — đúng như trước, layout đổi không ảnh hưởng.
|
|
327
|
+
`🚫 Block` trỏ `../DOC_GAP.md` — lên một cấp, vì file gap ở thư mục cha của `test-cases/`.
|
|
354
328
|
|
|
355
329
|
## Report
|
|
356
330
|
|
|
@@ -358,8 +332,14 @@ Dòng `🚫 Block: [GAP-UC{N}-{nnn}](DOC_GAP.md)` trỏ **liên kết tương đ
|
|
|
358
332
|
Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
|
|
359
333
|
|
|
360
334
|
```
|
|
361
|
-
/qc-design-test Hoàn tất — {UC-ID}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
335
|
+
/qc-design-test Hoàn tất — {UC-ID} ({active_platform})
|
|
336
|
+
Chế độ: {giao diện | API | cả hai}{nếu --atomic-max: " · tách tối đa ({explode|keep-completeness})"}
|
|
337
|
+
Files: {qc_artifact_dir}test-cases/TC_<FEATURE>.Test.md — {n} TC
|
|
338
|
+
{nếu có: "…_API.Test.md — {m} TC"}
|
|
339
|
+
Nhóm : GUI {a} · Validation {b} · Functional {c} · Integration {d} · NFR {e} · E2E {f}
|
|
340
|
+
{nếu file API: "Endpoint {g} · Integration API/DB/Kafka {h}"}
|
|
341
|
+
Trùng: {k} TC bỏ vì trùng ({j} trùng chéo UC — trỏ trace về UC nguồn thay vì viết lại)
|
|
342
|
+
Block: {blocked} TC bị chặn bởi gap — theo UC: {UC1: n · UC2: n}
|
|
343
|
+
Trace: {N} TC map tới {K}/{total} scenario của {UC-ID}
|
|
344
|
+
Next : /qc-review {UC-ID} ← review test case trước khi sinh script
|
|
365
345
|
```
|
package/core/commands/qc-plan.md
CHANGED
|
@@ -176,126 +176,13 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
|
176
176
|
|
|
177
177
|
## Phạm vi QC
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
**Chạy TRƯỚC phần logic riêng của lệnh, và SAU `steps/gate.md`.** Bước này chốt bốn thứ mà
|
|
182
|
-
cả 6 trạm QC đều cần, để chúng không tự suy mỗi trạm một kiểu:
|
|
183
|
-
|
|
184
|
-
| Biến | Là gì |
|
|
185
|
-
|---|---|
|
|
186
|
-
| `TICKET-ID` | mã PRD — **thư mục artifact QC mang tên này** |
|
|
187
|
-
| `active_platform` | `web` \| `app` \| `system` \| … — một QC pass khoá đúng MỘT nền |
|
|
188
|
-
| `qc_artifact_dir` | `{paths.qc_dir}/{TICKET-ID}/{active_platform}/` |
|
|
189
|
-
| `uc_list` | các UC của (PRD × nền) này, kèm trạng thái BDD từng UC |
|
|
190
|
-
|
|
191
|
-
> **Vì sao gom về một chỗ.** Luật phân giải nền từng được copy-paste ở 5 lệnh và câu chữ đã
|
|
192
|
-
> lệch nhau. Năm bản của một luật là nơi drift sống: sửa bốn, quên một, và trạm bị quên ghi
|
|
193
|
-
> artifact vào sai thư mục **trong im lặng**.
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## 1 — `TICKET-ID`
|
|
198
|
-
|
|
199
|
-
Artifact QC gom theo **PRD**, không theo UC. Nên mọi trạm phải quy được về `TICKET-ID`:
|
|
200
|
-
|
|
201
|
-
| `$ARGUMENTS` là | Cách lấy |
|
|
202
|
-
|---|---|
|
|
203
|
-
| **UC-ID** (`{TICKET-ID}-UC{N}`) | phần **trước** `-UC` — đúng luật `steps/gate.md` Bước 1 dùng để tìm tech-doc gộp |
|
|
204
|
-
| **TICKET-ID** | dùng trực tiếp |
|
|
205
|
-
| một **path file** (`.feature` / PRD / design-spec) | phân giải `{domain}` + `{prd-slug}` theo luật `context-loader` Bước 1, rồi lấy `TICKET-ID` từ tên file PRD `{TICKET-ID}-{prd-slug}.md` — file `.md` duy nhất ở gốc feature folder |
|
|
206
|
-
|
|
207
|
-
Đối chiếu: `TICKET-ID` suy ra phải khớp tên file PRD thật. Lệch → **DỪNG**, in cả hai giá
|
|
208
|
-
trị. (Suy sai `TICKET-ID` là ghi cả một PRD vào sai thư mục — không có bước nào phía sau bắt được.)
|
|
209
|
-
|
|
210
|
-
---
|
|
211
|
-
|
|
212
|
-
## 2 — `active_platform`
|
|
213
|
-
|
|
214
|
-
> **PHẢI phân giải TRƯỚC mọi phép đọc `.feature`.** `{UC-ID}-SC{N}` chỉ độc nhất trong
|
|
215
|
-
> (UC × nền), nên một UC đa nền có **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`,
|
|
216
|
-
> `bdd/system/` — và mỗi file mang `@trace.status` **riêng**: bản web có thể `approved`
|
|
217
|
-
> trong khi bản app còn `draft`. Đọc "file `.feature` của UC" khi chưa biết nền là đọc một
|
|
218
|
-
> file **bất kỳ trong ba**: báo `approved` trong khi bản đang dùng còn nháp, hoặc chặn oan
|
|
219
|
-
> một bản đã duyệt.
|
|
220
|
-
|
|
221
|
-
Theo thứ tự, dừng ở cái đầu tiên khớp:
|
|
222
|
-
|
|
223
|
-
1. `$ARGUMENTS` nêu nền (`web`/`app`/`system`/…) → dùng.
|
|
224
|
-
2. Target là một file `.feature` → đọc `# @trace.platform` của nó.
|
|
225
|
-
3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/` — **đúng một** thư mục nền → dùng nó.
|
|
226
|
-
4. Glob `{paths.qc_dir}/{TICKET-ID}/*/` — **đúng một** thư mục nền đã có artifact → dùng nó.
|
|
227
|
-
*(chỉ dùng cho trạm 2–6; trạm `/qc-analyze` là trạm tạo ra thư mục đó nên không có gì để soi.)*
|
|
228
|
-
5. Nhiều nền mà không suy được → hỏi *"QC pass này cho nền nào? (web/app/system)"*.
|
|
229
|
-
**Có `--yes`:** không hỏi — DỪNG với lỗi rõ ràng, vì đoán bừa nền là ghi artifact vào sai
|
|
230
|
-
thư mục và ghi `qc_status` vào sai sổ trace:
|
|
231
|
-
```
|
|
232
|
-
❌ {TICKET-ID} có {n} nền ({list}) — không suy được nền nào cho QC pass này.
|
|
233
|
-
Chạy headless thì phải nêu tường minh: /{lệnh} {TICKET-ID} web --yes
|
|
234
|
-
```
|
|
235
|
-
|
|
236
|
-
Lưu `active_platform`. Từ đây, **mọi** phép đọc `.feature` chỉ đọc thư mục
|
|
237
|
-
`bdd/{active_platform}/` — không trộn SC chéo nền.
|
|
238
|
-
|
|
239
|
-
---
|
|
240
|
-
|
|
241
|
-
## 3 — `qc_artifact_dir`
|
|
242
|
-
|
|
243
|
-
```
|
|
244
|
-
qc_artifact_dir = {paths.qc_dir}/{TICKET-ID}/{active_platform}/
|
|
245
|
-
```
|
|
246
|
-
|
|
247
|
-
Chứa: `REQUIREMENT_ANALYSIS.md` · `DOC_GAP.md` · `TEST_PLAN.md` · `test-cases/*.Test.md`
|
|
248
|
-
— **mỗi loại đúng MỘT file cho cả PRD**, các UC là mục/hàng bên trong.
|
|
249
|
-
|
|
250
|
-
`{paths.qc_dir}` là folder top-level **nhìn thấy** trong repo QC (mặc định `docs/`, **không**
|
|
251
|
-
phải `.agent/` ẩn) để đội QC mở và xử lý output dễ dàng. Spec chính thức ở lại spec submodule
|
|
252
|
-
của PO — đừng ghi artifact QC vào đó.
|
|
253
|
-
|
|
254
|
-
> **Sổ trace KHÔNG theo layout này.** Nó vẫn là `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv`
|
|
255
|
-
> — một sổ cho mỗi (UC × nền), vì mỗi hàng là một scenario. Liên kết giữa hai bên đi qua
|
|
256
|
-
> **cột `UC`** của bảng gap, không qua đường dẫn file.
|
|
257
|
-
|
|
258
|
-
---
|
|
259
|
-
|
|
260
|
-
## 4 — `uc_list`
|
|
261
|
-
|
|
262
|
-
Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/*.feature`. Mỗi file → một
|
|
263
|
-
UC: đọc `# @trace.id` (mã UC) và `# @trace.status` từ header.
|
|
264
|
-
|
|
265
|
-
Chia hai nhóm:
|
|
266
|
-
|
|
267
|
-
| Nhóm | Điều kiện | Xử lý |
|
|
268
|
-
|---|---|---|
|
|
269
|
-
| **Trong phạm vi** | `@trace.status: approved` | phân tích / thiết kế / chạy bình thường |
|
|
270
|
-
| **Chưa xét** | khác `approved` | **KHÔNG** phân tích; vẫn ghi một hàng vào bảng phạm vi kèm trạng thái thật |
|
|
271
|
-
|
|
272
|
-
In bảng phạm vi ra trước khi làm gì:
|
|
273
|
-
```
|
|
274
|
-
Phạm vi QC — {TICKET-ID} / {active_platform}
|
|
275
|
-
✅ {UC-ID} {tên UC} approved
|
|
276
|
-
⏸ {UC-ID} {tên UC} draft → chưa xét
|
|
277
|
-
→ {n} UC trong phạm vi · {m} chưa xét
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
**Cờ `--include-draft`:** phân tích cả UC chưa duyệt, nhưng **vẫn in bảng trên** và đánh dấu
|
|
281
|
-
trong artifact là dựa trên BDD nháp.
|
|
282
|
-
|
|
283
|
-
**Không UC nào `approved` và không có `--include-draft` → DỪNG:**
|
|
284
|
-
```
|
|
285
|
-
❌ {TICKET-ID} ({active_platform}): 0/{n} UC có BDD approved — không có gì để chạy.
|
|
286
|
-
Cách đúng: người duyệt đặt `# @trace.status: approved` rồi chạy lại.
|
|
287
|
-
Muốn chạy sớm trên BDD nháp (prototype): thêm --include-draft
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
> **Vì sao có `--include-draft` chứ không chặn cứng.** QC sớm trên BDD nháp là một cách dùng
|
|
291
|
-
> **cố ý được cho phép** từ trước (guard cũ là cảnh báo mềm, không phải chặn). Bỏ hẳn nó là
|
|
292
|
-
> lấy đi một năng lực đang có mà không ai khai. Còn để mặc định `approved`-only thì cái
|
|
293
|
-
> thường gặp là cái an toàn, và cái sớm phải nói ra.
|
|
294
|
-
|
|
295
|
-
> **Vì sao `--yes` không thay được `--include-draft`.** `--yes` nghĩa *"tôi không ngồi đây để
|
|
296
|
-
> trả lời"*; `--include-draft` nghĩa *"tôi biết BDD còn nháp và vẫn muốn chạy"*. Gộp hai cái
|
|
297
|
-
> là để một lần chạy headless âm thầm phân tích spec chưa chốt rồi bàn giao như thể đã chốt.
|
|
179
|
+
**BẮT BUỘC — đọc `.agent/steps/qc-scope.md` và thực thi TOÀN BỘ quy trình trong đó**,
|
|
180
|
+
rồi mới tiếp tục phần bên dưới.
|
|
298
181
|
|
|
182
|
+
Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
|
|
183
|
+
`qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--include-draft`).
|
|
184
|
+
Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —
|
|
185
|
+
cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
|
|
299
186
|
|
|
300
187
|
> **Trạm này chạy CẢ PRD, đúng như trạm 1** *(B11)* — một `TEST_PLAN.md` cho mỗi (PRD × nền).
|
|
301
188
|
> `qa-planner/test-plan.md` vốn viết *"Test Plan cho một **feature**"* và template của nó là
|