@educa-corp/sdd-framework 0.9.1 → 0.9.3
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/qc-base-map.json +595 -0
- package/bin/self-check.js +146 -0
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/propose-scenario.md +1 -1
- package/core/commands/qc-analyze.md +398 -37
- package/core/commands/qc-design-test.md +141 -2
- package/core/commands/qc-plan.md +159 -10
- package/core/commands/qc-review.md +134 -1
- package/core/commands/qc-run-test.md +135 -2
- package/core/commands/refine-prd.md +47 -20
- package/core/commands/report-bug.md +1 -1
- package/core/commands/review-context.md +27 -1
- package/core/modules/qc-playwright/stack-profile.yaml +3 -3
- package/core/skills/qc/qa-analyst/DOC_GAP.template.md +147 -0
- package/core/skills/qc/qa-analyst/acceptance-criteria.md +5 -3
- package/core/skills/qc/qa-analyst/business-rules.md +39 -5
- package/core/skills/qc/qa-analyst/data-flow.md +6 -4
- package/core/skills/qc/qa-analyst/exhaustive-gap-scanner.md +174 -0
- package/core/skills/qc/qa-analyst/spec-breakdown.md +10 -8
- package/core/skills/qc/qa-analyst/spec-issue-reporter.md +112 -0
- package/core/skills/qc/qa-designer/e2e/journey.md +3 -3
- package/core/skills/qc/qa-designer/exploratory/charter.md +1 -1
- package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +2 -2
- package/core/skills/qc/qa-designer/functional/api.md +3 -3
- package/core/skills/qc/qa-designer/functional/gui-feature.md +3 -3
- package/core/skills/qc/qa-designer/functional/gui-screen.md +3 -3
- package/core/skills/qc/qa-designer/integration/api.md +3 -3
- package/core/skills/qc/qa-designer/integration/db.md +3 -3
- package/core/skills/qc/qa-designer/integration/gui.md +3 -3
- package/core/skills/qc/qa-designer/integration/kafka.md +3 -3
- package/core/skills/qc/qa-designer/non-functional.md +3 -3
- package/core/skills/qc/qa-planner/risk-model.md +106 -0
- package/core/skills/qc/qa-planner/test-plan.md +35 -21
- package/core/skills/qc/qa-reviewer/script/e2e.md +1 -1
- package/core/skills/qc/qa-reviewer/script/exploratory.md +1 -1
- package/core/skills/qc/qa-reviewer/script/functional.md +1 -1
- package/core/skills/qc/qa-reviewer/script/integration.md +1 -1
- package/core/skills/qc/qa-reviewer/script/non-functional.md +1 -1
- package/core/skills/qc/qa-reviewer/test-case/e2e.md +1 -1
- package/core/skills/qc/qa-reviewer/test-case/exploratory.md +1 -1
- package/core/skills/qc/qa-reviewer/test-case/functional.md +1 -1
- package/core/skills/qc/qa-reviewer/test-case/integration.md +2 -2
- package/core/skills/qc/qa-reviewer/test-case/non-functional.md +1 -1
- package/core/skills/qc/qa-runner/e2e.md +1 -1
- package/core/skills/qc/qa-runner/exploratory/session.md +2 -2
- package/core/skills/qc/qa-runner/functional/api.md +1 -1
- package/core/skills/qc/qa-runner/functional/gui-feature.md +1 -1
- package/core/skills/qc/qa-runner/functional/gui-screen.md +1 -1
- package/core/skills/qc/qa-runner/integration.md +1 -1
- package/core/skills/qc/qa-runner/non-functional.md +1 -1
- package/core/skills/qc/qa-runner/report/report.md +1 -1
- package/core/steps/context-loader.md +1 -1
- package/core/steps/gap-verify.md +231 -0
- package/core/steps/qc-scope.md +119 -0
- package/core/steps/review-fanout.md +27 -1
- package/core/templates/project-context.yaml +5 -3
- package/docs/02-concepts/pipeline-steps/08-qc-automation.md +2 -2
- package/docs/04-reference/commands.md +1 -1
- package/docs/04-reference/configuration.md +146 -146
- package/docs/explain/03-refine-prd.md +8 -6
- package/docs/explain/15-qc-analyze.md +10 -7
- package/docs/explain/16-qc-plan.md +3 -3
- package/docs/explain/17-qc-design-test.md +1 -1
- package/docs/plans/qc-implementation-log.md +1587 -0
- package/docs/plans/qc-merge-plan.md +502 -0
- package/docs/plans/qc-sync-command.md +359 -0
- package/package.json +1 -1
- package/scripts/migrate-qc-docs.js +261 -0
- package/core/skills/qc/qa-analyst/DOC_GAPS.template.md +0 -63
|
@@ -162,7 +162,7 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
|
|
|
162
162
|
🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
|
|
163
163
|
|
|
164
164
|
|
|
165
|
-
*Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID.
|
|
165
|
+
*Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID. Đọc output của qc-analyze + qc-plan (`REQUIREMENT_ANALYSIS.md`, `DOC_GAP.md`, `TEST_PLAN.md`) từ `{qc_artifact_dir}` — **cả ba đều ở cấp PRD, lọc theo cột `UC`** để lấy phần của UC này — và file `.feature` của đúng platform đó (với `@trace.scenario` mỗi scenario). Với layer GUI, cũng đọc bảng **Test Selectors** §4.5.6 (block platform) của tech-doc gộp tại `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` (nếu có — bảng gộp mọi UC của platform, **lọc theo cột "Serves SC" khớp SC của UC này**) — các test-id ổn định mà QC sẽ định vị theo.*
|
|
166
166
|
|
|
167
167
|
## Context
|
|
168
168
|
**BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
|
|
@@ -172,6 +172,139 @@ Bỏ qua bước này thì `{paths.*}`, `{tech_stack.*}`, `{conventions.*}`, gua
|
|
|
172
172
|
`project-lessons`, và routing service (chế độ umbrella) đều **chưa được phân giải** — mọi
|
|
173
173
|
placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
174
174
|
|
|
175
|
+
## Phạm vi QC
|
|
176
|
+
|
|
177
|
+
# QC Scope — phân giải phạm vi cho mọi lệnh `qc-*`
|
|
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.
|
|
247
|
+
|
|
248
|
+
`{paths.qc_dir}` là folder top-level **nhìn thấy** trong repo QC (mặc định `docs/`, **không**
|
|
249
|
+
phải `.agent/` ẩn) để đội QC mở và xử lý output dễ dàng. Spec chính thức ở lại spec submodule
|
|
250
|
+
của PO — đừng ghi artifact QC vào đó.
|
|
251
|
+
|
|
252
|
+
> **Sổ trace KHÔNG theo layout này.** Nó vẫn là `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv`
|
|
253
|
+
> — 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
|
|
254
|
+
> **cột `UC`** của bảng gap, không qua đường dẫn file.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 4 — `uc_list`
|
|
259
|
+
|
|
260
|
+
Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/*.feature`. Mỗi file → một
|
|
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ý |
|
|
266
|
+
|---|---|---|
|
|
267
|
+
| **Trong phạm vi** | `@trace.status: approved` | phân tích / thiết kế / chạy bình thường |
|
|
268
|
+
| **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 |
|
|
269
|
+
|
|
270
|
+
In bảng phạm vi ra trước khi làm gì:
|
|
271
|
+
```
|
|
272
|
+
Phạm vi QC — {TICKET-ID} / {active_platform}
|
|
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
|
+
```
|
|
277
|
+
|
|
278
|
+
**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
|
|
279
|
+
trong artifact là dựa trên BDD nháp.
|
|
280
|
+
|
|
281
|
+
**Không UC nào `approved` và không có `--include-draft` → DỪNG:**
|
|
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
|
+
```
|
|
287
|
+
|
|
288
|
+
> **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
|
|
289
|
+
> **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à
|
|
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.
|
|
292
|
+
|
|
293
|
+
> **Vì sao `--yes` không thay được `--include-draft`.** `--yes` nghĩa *"tôi không ngồi đây để
|
|
294
|
+
> 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
|
|
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.
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
> **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
|
|
299
|
+
> 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
|
|
300
|
+
> artifact nằm chung ở `{qc_artifact_dir}` cấp PRD, không còn một thư mục mỗi UC.
|
|
301
|
+
>
|
|
302
|
+
> Nên `DOC_GAP.md` / `TEST_PLAN.md` đọc được ở đây phủ **cả PRD**: **lọc theo cột `UC`** để lấy
|
|
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.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
|
|
175
308
|
---
|
|
176
309
|
|
|
177
310
|
## Role
|
|
@@ -211,7 +344,13 @@ qc-run-test ghi `qc_status` theo từng scenario.
|
|
|
211
344
|
|
|
212
345
|
## Output
|
|
213
346
|
|
|
214
|
-
Ghi các file `.Test.md` dưới `{
|
|
347
|
+
Ghi các file `.Test.md` dưới `{qc_artifact_dir}test-cases/`
|
|
348
|
+
(= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/test-cases/`) — **một thư mục test-case dùng
|
|
349
|
+
chung cho cả PRD**. Tên file mang `<FEATURE>` nên các UC không đâm nhau; cột **Trace SC** là
|
|
350
|
+
chỗ phân biệt TC thuộc UC nào.
|
|
351
|
+
|
|
352
|
+
Dòng `🚫 Block: [GAP-UC{N}-{nnn}](DOC_GAP.md)` trỏ **liên kết tương đối một cấp lên**
|
|
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.
|
|
215
354
|
|
|
216
355
|
## Report
|
|
217
356
|
|
package/core/commands/qc-plan.md
CHANGED
|
@@ -162,7 +162,7 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
|
|
|
162
162
|
🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
|
|
163
163
|
|
|
164
164
|
|
|
165
|
-
*Lưu ý: Với lệnh này, target ở Bước 1 là một
|
|
165
|
+
*Lưu ý: Với lệnh này, target ở Bước 1 là một **TICKET-ID** (mã PRD) — hoặc UC-ID / file feature, cả hai quy về TICKET-ID ở §Phạm vi QC. Đọc output của qc-analyze (`REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`) từ `{qc_artifact_dir}` và file `.feature` của đúng nền đó.*
|
|
166
166
|
|
|
167
167
|
## Context
|
|
168
168
|
**BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
|
|
@@ -174,6 +174,140 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
|
174
174
|
|
|
175
175
|
---
|
|
176
176
|
|
|
177
|
+
## Phạm vi QC
|
|
178
|
+
|
|
179
|
+
# QC Scope — phân giải phạm vi cho mọi lệnh `qc-*`
|
|
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.
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
> **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
|
+
> `qa-planner/test-plan.md` vốn viết *"Test Plan cho một **feature**"* và template của nó là
|
|
302
|
+
> `# Test Plan – <Feature>` với metadata `Feature / Project / Module`: đây là quay về đúng
|
|
303
|
+
> tầng mà skill gốc được viết cho.
|
|
304
|
+
>
|
|
305
|
+
> `uc_list` ở đây **phải khớp** bảng *Phạm vi phân tích* trong `DOC_GAP.md`. Lệch nhau nghĩa là
|
|
306
|
+
> BDD đã đổi trạng thái sau lần chạy trạm 1 → nêu ra và khuyên chạy lại `/qc-analyze`, đừng
|
|
307
|
+
> âm thầm lập plan cho một tập UC khác với tập đã phân tích.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
177
311
|
## Role
|
|
178
312
|
|
|
179
313
|
Bạn là **QC Planner** — stage 2. Từ output của qc-analyze, tạo TEST PLAN:
|
|
@@ -183,15 +317,27 @@ thiết kế test case cụ thể (đó là qc-design-test).
|
|
|
183
317
|
|
|
184
318
|
## Skills (`{paths.qc_skills_dir}/qa-planner/`)
|
|
185
319
|
|
|
186
|
-
- `test-plan.md` —
|
|
187
|
-
|
|
188
|
-
|
|
320
|
+
- `test-plan.md` — khung plan: scope theo từng test layer (functional / integration /
|
|
321
|
+
e2e / non-functional), what-if, entry/exit criteria, và danh sách questions-for-dev
|
|
322
|
+
suy ra từ `DOC_GAP.md`.
|
|
323
|
+
- `risk-model.md` — **cách tính** mức rủi ro: 7 nguồn rủi ro · khả năng × thiệt hại → P0–P3 ·
|
|
324
|
+
và dùng mức đó chia **độ sâu** test. `test-plan.md` có khung bảng `§6`; file này là cách điền.
|
|
325
|
+
Nạp cùng lúc, không phải thay thế.
|
|
189
326
|
|
|
190
327
|
## Output
|
|
191
328
|
|
|
192
|
-
Ghi test plan vào `{
|
|
193
|
-
|
|
194
|
-
|
|
329
|
+
Ghi **đúng MỘT** test plan cho cả PRD vào `{qc_artifact_dir}TEST_PLAN.md`
|
|
330
|
+
(= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/TEST_PLAN.md`). Giới hạn plan trong các
|
|
331
|
+
scenario của **các UC trong phạm vi** (`{UC-ID}-SC{N}` từ `.feature` của nền này) để
|
|
332
|
+
qc-design-test thiết kế case theo từng scenario.
|
|
333
|
+
|
|
334
|
+
Bắt buộc:
|
|
335
|
+
- **Bảng `§3 Test items` có cột `UC`** — một plan giờ phủ nhiều UC, không có cột đó thì không
|
|
336
|
+
ai biết vùng test nào thuộc UC nào.
|
|
337
|
+
- **`§2 Phạm vi`** liệt kê rõ UC `⏸ Chưa xét` ở phần *Out of scope*, kèm lý do (BDD chưa
|
|
338
|
+
approved). *Không có dòng này thì một UC bị bỏ trông giống một UC không có gì để test.*
|
|
339
|
+
- **`§5 Entry criteria` chặn theo từng UC**, không chặn cả PRD: gap 🔴 Blocker ở UC3 không
|
|
340
|
+
dừng việc thiết kế test cho UC1. Ghi `Ready` / `Blocked` cho **mỗi** UC.
|
|
195
341
|
|
|
196
342
|
## Report
|
|
197
343
|
|
|
@@ -199,8 +345,11 @@ case theo từng scenario.
|
|
|
199
345
|
Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
|
|
200
346
|
|
|
201
347
|
```
|
|
202
|
-
/qc-plan Hoàn tất — {
|
|
348
|
+
/qc-plan Hoàn tất — {TICKET-ID} ({active_platform})
|
|
349
|
+
Phạm vi: {n} UC trong plan{nếu có: " · ⏸ {m} chưa xét"}
|
|
203
350
|
Plan: {risks} rủi ro · {questions} câu hỏi mở cho dev · layers: {list}
|
|
204
|
-
|
|
205
|
-
|
|
351
|
+
File: {paths.qc_dir}/{TICKET-ID}/{active_platform}/TEST_PLAN.md
|
|
352
|
+
Sẵn sàng: {danh sách UC Ready} | Chặn: {danh sách UC Blocked + GAP-ID chặn nó}
|
|
353
|
+
Next: /qc-design-test {UC-ID} ← thiết kế test case, chạy cho từng UC đã Ready
|
|
354
|
+
(gửi questions-for-dev cho PO/Dev cho các UC còn Blocked)
|
|
206
355
|
```
|
|
@@ -162,7 +162,7 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
|
|
|
162
162
|
🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
|
|
163
163
|
|
|
164
164
|
|
|
165
|
-
*Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID
|
|
165
|
+
*Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID (`active_platform` + `qc_artifact_dir` do §Phạm vi QC phân giải). Phát hiện review mode từ `$ARGUMENTS`/context: review test-case `.Test.md` (sau design) hoặc review Python script (sau run). Đọc artifact từ `{qc_artifact_dir}` và source test/page-object đã sinh.*
|
|
166
166
|
|
|
167
167
|
## Context
|
|
168
168
|
**BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
|
|
@@ -172,6 +172,139 @@ Bỏ qua bước này thì `{paths.*}`, `{tech_stack.*}`, `{conventions.*}`, gua
|
|
|
172
172
|
`project-lessons`, và routing service (chế độ umbrella) đều **chưa được phân giải** — mọi
|
|
173
173
|
placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
174
174
|
|
|
175
|
+
## Phạm vi QC
|
|
176
|
+
|
|
177
|
+
# QC Scope — phân giải phạm vi cho mọi lệnh `qc-*`
|
|
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.
|
|
247
|
+
|
|
248
|
+
`{paths.qc_dir}` là folder top-level **nhìn thấy** trong repo QC (mặc định `docs/`, **không**
|
|
249
|
+
phải `.agent/` ẩn) để đội QC mở và xử lý output dễ dàng. Spec chính thức ở lại spec submodule
|
|
250
|
+
của PO — đừng ghi artifact QC vào đó.
|
|
251
|
+
|
|
252
|
+
> **Sổ trace KHÔNG theo layout này.** Nó vẫn là `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv`
|
|
253
|
+
> — 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
|
|
254
|
+
> **cột `UC`** của bảng gap, không qua đường dẫn file.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## 4 — `uc_list`
|
|
259
|
+
|
|
260
|
+
Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/*.feature`. Mỗi file → một
|
|
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ý |
|
|
266
|
+
|---|---|---|
|
|
267
|
+
| **Trong phạm vi** | `@trace.status: approved` | phân tích / thiết kế / chạy bình thường |
|
|
268
|
+
| **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 |
|
|
269
|
+
|
|
270
|
+
In bảng phạm vi ra trước khi làm gì:
|
|
271
|
+
```
|
|
272
|
+
Phạm vi QC — {TICKET-ID} / {active_platform}
|
|
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
|
+
```
|
|
277
|
+
|
|
278
|
+
**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
|
|
279
|
+
trong artifact là dựa trên BDD nháp.
|
|
280
|
+
|
|
281
|
+
**Không UC nào `approved` và không có `--include-draft` → DỪNG:**
|
|
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
|
+
```
|
|
287
|
+
|
|
288
|
+
> **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
|
|
289
|
+
> **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à
|
|
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.
|
|
292
|
+
|
|
293
|
+
> **Vì sao `--yes` không thay được `--include-draft`.** `--yes` nghĩa *"tôi không ngồi đây để
|
|
294
|
+
> 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
|
|
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.
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
> **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
|
|
299
|
+
> 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
|
|
300
|
+
> artifact nằm chung ở `{qc_artifact_dir}` cấp PRD, không còn một thư mục mỗi UC.
|
|
301
|
+
>
|
|
302
|
+
> Nên `DOC_GAP.md` / `TEST_PLAN.md` đọc được ở đây phủ **cả PRD**: **lọc theo cột `UC`** để lấy
|
|
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.
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
|
|
175
308
|
---
|
|
176
309
|
|
|
177
310
|
## Role
|