@educa-corp/sdd-framework 0.8.1 → 0.9.0

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.
Files changed (66) hide show
  1. package/bin/lint-trace.js +156 -1
  2. package/bin/self-check.js +0 -146
  3. package/bin/trace-schema.json +7 -3
  4. package/core/FRAMEWORK_VERSION +1 -1
  5. package/core/commands/generate-code.md +42 -0
  6. package/core/commands/propose-scenario.md +1 -1
  7. package/core/commands/qc-analyze.md +22 -260
  8. package/core/commands/qc-design-test.md +1 -1
  9. package/core/commands/qc-plan.md +4 -7
  10. package/core/commands/qc-run-test.md +1 -1
  11. package/core/commands/refine-prd.md +20 -47
  12. package/core/commands/report-bug.md +1 -1
  13. package/core/commands/review-context.md +1 -27
  14. package/core/commands/validate-traces.md +154 -3
  15. package/core/skills/qc/qa-analyst/DOC_GAPS.template.md +63 -0
  16. package/core/skills/qc/qa-analyst/acceptance-criteria.md +2 -4
  17. package/core/skills/qc/qa-analyst/business-rules.md +4 -38
  18. package/core/skills/qc/qa-analyst/data-flow.md +3 -5
  19. package/core/skills/qc/qa-analyst/spec-breakdown.md +7 -9
  20. package/core/skills/qc/qa-designer/e2e/journey.md +2 -2
  21. package/core/skills/qc/qa-designer/exploratory/charter.md +1 -1
  22. package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +1 -1
  23. package/core/skills/qc/qa-designer/functional/api.md +2 -2
  24. package/core/skills/qc/qa-designer/functional/gui-feature.md +2 -2
  25. package/core/skills/qc/qa-designer/functional/gui-screen.md +2 -2
  26. package/core/skills/qc/qa-designer/integration/api.md +2 -2
  27. package/core/skills/qc/qa-designer/integration/db.md +2 -2
  28. package/core/skills/qc/qa-designer/integration/gui.md +2 -2
  29. package/core/skills/qc/qa-designer/integration/kafka.md +2 -2
  30. package/core/skills/qc/qa-designer/non-functional.md +2 -2
  31. package/core/skills/qc/qa-planner/test-plan.md +10 -13
  32. package/core/skills/qc/qa-reviewer/script/e2e.md +1 -1
  33. package/core/skills/qc/qa-reviewer/script/exploratory.md +1 -1
  34. package/core/skills/qc/qa-reviewer/script/functional.md +1 -1
  35. package/core/skills/qc/qa-reviewer/script/integration.md +1 -1
  36. package/core/skills/qc/qa-reviewer/script/non-functional.md +1 -1
  37. package/core/skills/qc/qa-reviewer/test-case/e2e.md +1 -1
  38. package/core/skills/qc/qa-reviewer/test-case/exploratory.md +1 -1
  39. package/core/skills/qc/qa-reviewer/test-case/functional.md +1 -1
  40. package/core/skills/qc/qa-reviewer/test-case/integration.md +2 -2
  41. package/core/skills/qc/qa-reviewer/test-case/non-functional.md +1 -1
  42. package/core/skills/qc/qa-runner/e2e.md +1 -1
  43. package/core/skills/qc/qa-runner/exploratory/session.md +1 -1
  44. package/core/skills/qc/qa-runner/functional/api.md +1 -1
  45. package/core/skills/qc/qa-runner/functional/gui-feature.md +1 -1
  46. package/core/skills/qc/qa-runner/functional/gui-screen.md +1 -1
  47. package/core/skills/qc/qa-runner/integration.md +1 -1
  48. package/core/skills/qc/qa-runner/non-functional.md +1 -1
  49. package/core/skills/qc/qa-runner/report/report.md +1 -1
  50. package/core/steps/review-fanout.md +1 -27
  51. package/core/templates/project-context.yaml +2 -2
  52. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +2 -2
  53. package/docs/04-reference/commands.md +1 -1
  54. package/docs/explain/03-refine-prd.md +6 -8
  55. package/docs/explain/15-qc-analyze.md +7 -10
  56. package/docs/explain/16-qc-plan.md +2 -2
  57. package/package.json +3 -2
  58. package/bin/qc-base-map.json +0 -595
  59. package/core/skills/qc/qa-analyst/DOC_GAP.template.md +0 -117
  60. package/core/skills/qc/qa-analyst/exhaustive-gap-scanner.md +0 -174
  61. package/core/skills/qc/qa-analyst/spec-issue-reporter.md +0 -100
  62. package/core/skills/qc/qa-planner/risk-model.md +0 -106
  63. package/core/steps/gap-verify.md +0 -231
  64. package/docs/plans/qc-implementation-log.md +0 -1446
  65. package/docs/plans/qc-merge-plan.md +0 -502
  66. package/docs/plans/qc-sync-command.md +0 -358
@@ -9,9 +9,6 @@ ported_from: ai-automation-qc-base
9
9
  > Stage 1 của QC automation pipeline native (qc-analyze → qc-plan → qc-design-test → qc-review → qc-run-test → qc-report). Port từ qa-analyst của team QC. Markdown-first: không có script ở đây.
10
10
 
11
11
  ## Gate
12
-
13
- *Checkpoint: **chặn thường** — lệnh ghi 2 file artifact. `--yes` bỏ qua được (gate Bước 3a).*
14
-
15
12
  # Gate — Quy trình vào chuẩn cho mọi lệnh
16
13
 
17
14
  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ó.
@@ -165,7 +162,7 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
165
162
  🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
166
163
 
167
164
 
168
- *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID hoặc file feature/PRD. Đọc spec chính thức của UC đó — file `.feature` (mang `@trace.id={UC-ID}` và mỗi scenario `@trace.scenario={UC-ID}-SC{N}`), PRD, và design-spec — từ feature package `{paths.specs_dir}/{domain}/{prd-slug}/` (file `.feature` dưới `bdd/`, file PRD `{TICKET-ID}-{prd-slug}.md` ở gốc folder, và design-spec dưới `design-spec/`). Spec của framework CHÍNH LÀ source of truth; đừng suy lại các requirement đã có ở đó. **Ngoài ra đọc tech-doc gộp** `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` làm **nguồn thứ hai** — xem §Đối chiếu tài liệu kỹ thuật.*
165
+ *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID hoặc file feature/PRD. Đọc spec chính thức của UC đó — file `.feature` (mang `@trace.id={UC-ID}` và mỗi scenario `@trace.scenario={UC-ID}-SC{N}`), PRD, và design-spec — từ feature package `{paths.specs_dir}/{domain}/{prd-slug}/` (file `.feature` dưới `bdd/`, file PRD `{TICKET-ID}-{prd-slug}.md` ở gốc folder, và design-spec dưới `design-spec/`). Spec của framework CHÍNH LÀ source of truth; đừng suy lại các requirement đã có ở đó.*
169
166
 
170
167
  ## Context
171
168
  **BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
@@ -177,103 +174,30 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
177
174
 
178
175
  ---
179
176
 
180
- ## Platform Resolution *(thiết lập cho cả QC pass — mọi stage sau kế thừa)*
181
-
182
- > **PHẢI chạy TRƯỚC Guard bên dưới.** `{UC-ID}-SC{N}` chỉ độc nhất trong (UC × platform), nên
183
- > một UC đa nền có **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`, `bdd/system/` — và mỗi
184
- > file mang `@trace.status` **riêng**: bản web có thể `approved` trong khi bản app còn `draft`.
185
- > Guard đọc "file `.feature` của UC" khi chưa biết platform là đọc một file **bất kỳ trong ba**:
186
- > báo `approved` trong khi bản đang dùng còn nháp, hoặc chặn oan một bản đã duyệt.
187
- > Chốt platform trước thì Guard mới có đúng một file để đọc.
188
-
189
- `{UC-ID}-SC{N}` chỉ độc nhất trong (UC × platform) — `web SC3` và `app SC3` là hai scenario khác nhau, và sổ trace tách theo `{UC-ID}-{platform}.tsv`. Nên **một QC pass khoá đúng MỘT platform**, và mọi artifact QC nằm dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/`.
190
-
191
- Phân giải `active_platform` — theo thứ tự, dừng ở cái đầu tiên khớp:
192
- 1. `$ARGUMENTS` nêu platform (`web`/`app`/`system`) → dùng.
193
- 2. Target ở Bước 1 là một file `.feature` → đọc `# @trace.platform` của nó.
194
- 3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/` — **đúng một** thư mục platform tồn tại → dùng nó.
195
- 4. Nhiều platform mà không suy được → hỏi *"QC pass này cho platform nào? (web/app/system)"*.
196
- **Có `--yes`:** không hỏi — dừng với lỗi rõ ràng, vì đoán bừa platform là ghi artifact vào sai
197
- thư mục và ghi `qc_status` vào sai sổ trace:
198
- ```
199
- ❌ {UC-ID} có {n} platform ({list}) — không suy được platform nào cho QC pass này.
200
- Chạy headless thì phải nêu tường minh: /qc-analyze {UC-ID} web --yes
201
- ```
202
-
203
- Lưu `active_platform`. Đọc **đúng file `.feature` của platform đó** (`{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{UC-ID}*.feature`) làm nguồn SC — không trộn SC chéo platform.
204
-
205
- ---
206
-
207
177
  ## Guard — BDD đã duyệt chưa
208
178
 
209
- *Chạy SAU Platform Resolution xem do khối trên.*
210
-
211
- Đọc `# @trace.status:` từ header file `.feature` **của `{active_platform}` đã phân giải**:
179
+ Đọc `# @trace.status:` từ header file `.feature` của UC target:
212
180
  - `approved` → tiếp tục bình thường.
213
181
  - `draft` (hoặc khác `approved`) → **CHECKPOINT cảnh báo mềm** (không chặn cứng — cho phép QC sớm/prototype):
214
182
  ```
215
- ⚠️ BDD của {UC-ID} ({active_platform}) đang ở @trace.status: {status} (chưa duyệt). QC chạy trên BDD chưa chốt có thể phải làm lại.
183
+ ⚠️ BDD của {UC-ID} đang ở @trace.status: {status} (chưa duyệt). QC chạy trên BDD chưa chốt có thể phải làm lại.
216
184
  Khuyến nghị: review-context (BDD) sạch + người duyệt đặt `# @trace.status: approved` rồi mới chạy QC.
217
185
  Vẫn chạy QC bây giờ? (Y/N)
218
186
  ```
219
- Chỉ tiếp khi chọn Y — **trừ khi `$ARGUMENTS` có `--yes`**: coi như Y, **nhưng vẫn IN khối cảnh
220
- báo** (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy QC đã chạy trên BDD nháp).
221
-
222
- > **Vì sao `--yes` phải phủ cả guard mềm này.** `steps/gate.md` mở đường chạy headless
223
- > (`claude -p "… --yes"`), nhưng `--yes` chỉ khai là bỏ qua CHECKPOINT của gate. Guard mềm ở đây
224
- > là một `(Y/N)` thứ hai — nên lệnh vẫn treo vô hạn ở chế độ không có người trả lời, và đường
225
- > headless mà gate hứa bị bít cho đúng lệnh này. Một cổng chỉ chặn được khi có người ngồi đó
226
- > thì ở chế độ headless nó không phải cổng, nó là treo.
187
+ Chỉ tiếp khi chọn Y.
227
188
 
228
189
  ---
229
190
 
230
- ## Đối chiếu tài liệu kỹ thuật *(nguồn thứ hai bắt lệch nghiệp vụ ↔ kỹ thuật)*
231
-
232
- Định vị tech-doc gộp cấp PRD: `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`.
233
- Nó phủ **nhiều UC** — lọc theo `@trace.ucs` ở header, chỉ đọc phần chạm `{UC-ID}` đang xét.
234
-
235
- **Không tìm thấy → cảnh báo mềm, KHÔNG chặn** (dự án có thể chưa dựng tech-doc):
236
- ```
237
- ⚠️ Không có tech-doc cho {TICKET-ID} — phân tích chỉ dựa trên PRD + BDD + design-spec.
238
- Lệch giữa yêu cầu nghiệp vụ và hợp đồng kỹ thuật (enum, mã lỗi, ràng buộc field) sẽ KHÔNG được phát hiện ở trạm này.
239
- ```
240
-
241
- **Có → đối chiếu các mục sau với PRD/BDD, mỗi chỗ vênh là một gap `CONTRADICTORY`:**
242
-
243
- | Mục tech-doc | Đối chiếu gì với PRD/BDD |
244
- |---|---|
245
- | §3 Mô hình dữ liệu | thực thể/field/quan hệ PRD nhắc tới có khớp không |
246
- | **§4 Hợp đồng API** | **enum & tập giá trị hợp lệ** · ràng buộc field (độ dài, định dạng, bắt buộc) · **mã lỗi** — PRD nêu bao nhiêu nhánh lỗi, contract định nghĩa bao nhiêu |
247
- | §4.5 Ánh xạ component UI | màn/component PRD·design-spec mô tả có mặt đủ không |
248
- | §5 Luồng chính | thứ tự bước, nhánh rẽ có khớp scenario `.feature` không |
249
- | §6 Điểm tích hợp | side-effect PRD nêu (gửi sự kiện, gọi dịch vụ khác) có được định nghĩa không |
250
- | §8 Xử lý lỗi & biên | trường hợp biên PRD nêu có đường xử lý không, và ngược lại |
251
-
252
- > **Vì sao mục này tồn tại.** Có một lớp gap **chỉ lộ ra khi so hai loại tài liệu với nhau** —
253
- > đọc riêng bên nào cũng thấy hợp lý. Ca điển hình: PRD viết *"chọn lớp 1–6"*, contract định
254
- > nghĩa enum `1..9`. Không ai đọc cả hai thì không ai thấy, và nó ra tận lúc chạy thật.
255
- > **Đây là lý do trạm này đọc tech-doc — không phải để hiểu kỹ thuật, mà để bắt chỗ hai bên nói khác nhau.**
256
-
257
- ### §12 GAP Register — ĐỌC, KHÔNG GHI
258
-
259
- Tech-doc có sổ ẩn số thiết kế riêng (`§12`), với vòng đời và người chịu trách nhiệm riêng, và
260
- `/generate-code` đã canh nó. **Trạm này chỉ đọc, tuyệt đối không ghi vào.**
191
+ ## Platform Resolution *(thiết lập cho cả QC passmọi stage sau kế thừa)*
261
192
 
262
- Với mỗi mục `open` trong §12 chạm `{UC-ID}`:
263
- - **KHÔNG mở gap mới** trong `DOC_GAP.md` về cùng chuyện đó.
264
- - Ghi vào `REQUIREMENT_ANALYSIS.md` mục *"Đang chờ chốt (từ §12 tech-doc)"*: `{id}` · điều chưa biết · owner · severity.
265
- - Test case chạm nó về sau sẽ bị chặn — nhưng bị chặn bởi **một mục đã có người xử lý**, không phải bởi một câu hỏi mới gửi PO.
193
+ `{UC-ID}-SC{N}` chỉ độc nhất trong (UC × platform) — `web SC3` `app SC3` là hai scenario khác nhau, và sổ trace tách theo `{UC-ID}-{platform}.tsv`. Nên **một QC pass khoá đúng MỘT platform**, và mọi artifact QC nằm dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/`.
266
194
 
267
- > **Vì sao không ghi vào.** Một ẩn số đã nằm trong §12 nghĩa là **đã có người đang lo**: có
268
- > owner, mức chặn, cổng chặn sinh code. Mở lại thành gap QC là gửi PO một câu hỏi
269
- > về thứ không phải việc của PO, và tạo hai sổ cùng theo dõi một chuyện — rồi chúng lệch nhau.
270
- > Đây đúng **câu hỏi lọc Q1** của `steps/gap-verify.md` (*"chỗ này đã được trả lời tài liệu
271
- > khác chưa?"*), chỉ mở rộng phạm vi "tài liệu khác" thêm một nguồn.
195
+ Phân giải `active_platform`:
196
+ - Target Bước 1 một file `.feature` đọc `# @trace.platform` của nó.
197
+ - `$ARGUMENTS` nêu platform (`web`/`app`/`system`) dùng.
198
+ - Ngược lại hỏi: *"QC pass này cho platform nào? (web/app/system)"* chờ chọn.
272
199
 
273
- **Ngoại lệ mục `spec-defect` việc của PO.** §12 phân ba loại: `nội tại` (backend tự quyết) ·
274
- `cross-service` (đội khác) · `spec-defect` (PRD/BDD sai hoặc thiếu). Hai loại đầu → ghi "đang chờ".
275
- Loại thứ ba **đúng là gap tài liệu** → vẫn ghi vào `DOC_GAP.md`, trỏ ngược về `{id}` của §12 để
276
- không đếm hai lần.
200
+ Lưu `active_platform`. Đọc **đúng file `.feature` của platform đó** (`{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{UC-ID}*.feature`) làm nguồn SC không trộn SC chéo platform.
277
201
 
278
202
  ---
279
203
 
@@ -304,204 +228,42 @@ File `.feature` chính thức đã định nghĩa scenario là `@trace.scenario=
304
228
  và ghi lại mapping — qc-design-test và qc-run-test cần nó để gắn tag
305
229
  `@trace.verifies` cho test và ghi `qc_status` theo từng scenario.
306
230
 
307
- ## Quét gap — hai nguồn, gộp rồi mới thẩm định
308
-
309
- Gap đến từ **hai chỗ**, và chúng bổ sung nhau chứ không thay thế:
310
-
311
- | Nguồn | Trả lời câu | Gap là |
312
- |---|---|---|
313
- | **4 kỹ năng phân tích** ở trên | *"yêu cầu là gì?"* | sản phẩm phụ — đang bóc luật nghiệp vụ thì gặp chỗ mâu thuẫn |
314
- | **Quét theo lăng kính** *(dưới đây)* | *"còn thiếu gì?"* | mục tiêu chính |
315
-
316
- ### Quét theo lăng kính
317
-
318
- Chạy `steps/review-fanout.md` với:
231
+ ## DOC_GAPS (bắt buộc)
319
232
 
320
- | Tham số | Giá trị |
321
- |---|---|
322
- | `DIMENSIONS` | **4 lăng kính** — `D2 Xử lý lỗi` · `D3 Giao diện` · `D4 Dữ liệu & cấu hình` · `D5 Đối chiếu chéo` **(thu hẹp — xem dưới)** *(định nghĩa ở `{paths.qc_skills_dir}/qa-analyst/exhaustive-gap-scanner.md`)* |
323
- | `FINDINGS SCHEMA` | như §Output dưới đây |
324
- | `GRANULARITY` | **`auto`** — chia theo ngưỡng kích thước, KHÔNG ép mịn theo từng UC |
325
- | `VERIFY` | **`off`** — thẩm định chạy MỘT lần ở bước sau, trên tập đã gộp |
326
-
327
- **`D5` chạy ở dạng THU HẸP — chỉ 2 trong 4 cặp tài liệu:**
328
-
329
- | Cặp | |
330
- |---|---|
331
- | `PRD ↔ design-spec/` | ✅ **SO** — không ai đối chiếu nội dung. `/generate-bdd` chỉ kiểm `Built from PRD` (số phiên bản); cùng phiên bản mà nội dung lệch thì lọt |
332
- | `bdd/{platform}/ ↔ design-spec/` | ✅ **SO** — không ai |
333
- | `PRD ↔ bdd/` | ❌ bỏ — `/review-context` **B1** đã làm |
334
- | `PRD·bdd/ ↔ tech-docs/` | ❌ bỏ — §Đối chiếu tài liệu kỹ thuật **ở trên** đã làm |
335
-
336
- > **Cả hai cặp SO đều dính `design-spec/`** — artifact duy nhất trong feature package mà **không
337
- > tài liệu nào đối chiếu nội dung với nó**. Đừng lẫn với `tech-docs/`: `design-spec/` là *giao diện
338
- > Designer vẽ*, `tech-docs/` là *hợp đồng hệ thống* — và `tech-docs/` đã được phủ ở §trên.
339
- >
340
- > Trạm này **đã đọc `design-spec/`** từ trước (nó nằm trong danh sách nguồn ở Gate), nên `D5`
341
- > không nạp thêm file nào — chỉ bắt nó **so** thay vì chỉ **đọc**. Rẻ hơn một lăng kính thường.
342
-
343
- **`D1 Luật nghiệp vụ` là lăng kính duy nhất KHÔNG bật:** `qa-analyst/business-rules.md` đã hỏi
344
- 4/5 câu của nó, và hỏi cụ thể hơn — *"min/max · ký tự cho phép · trim · định dạng"* thay vì
345
- *"ngưỡng đã chốt chưa"*.
346
-
347
- > **Ghi lại vì sao `D5` từng bị tắt:** lý do ban đầu là *"trùng nhiều"* — **đúng một nửa**. Nó phủ
348
- > **bốn** cặp, chỉ **hai** cặp đã có người làm. Sai vì suy từ ấn tượng thay vì đếm danh sách; bảng
349
- > kiểm chứng 31 câu hỏi (`docs/plans/qc-implementation-log.md`) là thứ đáng lẽ phải làm **trước**
350
- > khi quyết. Đừng tắt lại `D5` mà không đọc bảng đó.
351
-
352
- > **`GRANULARITY = auto`, không phải `per-uc`.** `/refine-prd` ép mịn theo từng UC vì ở tầng PRD
353
- > một gap bỏ sót **im lặng đi tiếp** tới tận lúc chạy thật. Ở đây khác: gap bỏ sót còn **bốn lớp
354
- > chặn phía sau** — trạm 3 bật ngược khi không viết nổi giá trị mong đợi, trạm 4 soát độ phủ,
355
- > trạm 5 phân loại lỗi thật vs script sai. Ép mịn ở đây tốn gấp ~3 lần cho tính năng nhỏ mà đổi
356
- > lấy một lưới an toàn đã có sẵn ba lớp khác.
357
-
358
- ### Gộp trước, thẩm định sau
359
-
360
- Gộp gap từ **cả hai nguồn** vào một tập trước khi sang bước thẩm định.
361
-
362
- > **Không thẩm định từng nguồn riêng.** Phép kiểm `T6` của `gap-verify` là *"hai gap cùng gốc
363
- > thì gộp lại"* — nó chỉ chạy được khi **thấy toàn bộ** tập. Thẩm định hai lần trên hai tập rời
364
- > thì không bắt được trùng lặp chéo nguồn, và PO nhận hai câu hỏi giống nhau.
365
-
366
- ---
367
-
368
- ## DOC_GAP (bắt buộc)
369
-
370
- Luôn tạo một file gaps theo `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md`:
233
+ Luôn tạo một file gaps theo `{paths.qc_skills_dir}/qa-analyst/DOC_GAPS.template.md`:
371
234
  - Mỗi gap `GAP-xx`, phân loại MISSING / AMBIGUOUS / CONTRADICTORY / ASSUMPTION / OPEN QUESTION, với severity (🔴 Blocker → 🟢 Low) và function/BR/AC bị ảnh hưởng.
372
235
  - Không bao giờ bịa câu trả lời; đánh dấu giả định là `ASSUMPTION` để PO/dev confirm.
373
236
  - Bất kỳ `🔴 Blocker` nào còn `Open` ⇒ UC chưa sẵn sàng cho qc-design-test — bàn giao cho qc-plan.
374
237
  - **Đẩy các defect spec thực sự lên PO (không chỉ giữ local).** Một blocker là lỗi thật
375
238
  trong spec chính thức — `AMBIGUOUS` / `CONTRADICTORY` / `MISSING` trong PRD/BDD — phải tới
376
- PO qua feedback flow, không chỉ nằm trong `DOC_GAP.md`: tạo `/report-bug {UC-ID} {desc}`
239
+ PO qua feedback flow, không chỉ nằm trong `DOC_GAPS.md`: tạo `/report-bug {UC-ID} {desc}`
377
240
  (BUG_FLOW của nó phân loại PRD vs BDD), hoặc `/propose-scenario {UC-ID}` nếu gap là thiếu test
378
241
  coverage. Gap `ASSUMPTION` / `OPEN QUESTION` được confirm qua questions-for-dev của qc-plan — không file thành bug.
379
242
 
380
- ### Thẩm định trước khi bàn giao *(bắt buộc)*
381
-
382
- Sinh xong `DOC_GAP.md`, **đọc `.agent/steps/gap-verify.md` và chạy toàn bộ quy trình trong đó**
383
- với:
384
- - `FINDINGS` = mọi gap đang `Open` trong `DOC_GAP.md`
385
- - `EVIDENCE_ROOT` = `{paths.specs_dir}` — spec repo của PO, **không** phải `{paths.qc_dir}`
386
- - `VERDICT_FIELD` = cột `Trạng thái` + `Câu trả lời` của bảng gap
387
- - `RERATE` = `on`
388
-
389
- Gap rớt thẩm định được **đóng kèm lý do**, KHÔNG xoá — người đọc phải kiểm chứng được vì sao
390
- nó bị loại. Cập nhật `Tổng số gap` + bảng ưu tiên sau khi áp verdict, và in khối
391
- `[GAP VERIFY]` + cam kết cuối vào report.
392
-
393
- > **Vì sao bắt buộc, không phải tuỳ chọn.** Bước phân tích ở trên chỉ có lực **tìm thêm** —
394
- > bốn skill lần lượt quét spec và mỗi cái đều được khuyến khích ghi ra chỗ nghi ngờ. Không có
395
- > bước nào hỏi ngược *"cái vừa ghi có thật không?"*. Hệ quả đo được ở đội QC: phần lớn gap sinh
396
- > ra là gap ảo — spec đã trả lời ở tài liệu khác, hoặc trích dẫn sai, hoặc là chuyện QC tự quyết
397
- > được. Mà gap ảo không chỉ tốn thời gian PO: nó **làm PO mất tin vào cả danh sách**, và lúc đó
398
- > những gap thật cũng chết theo. `gap-verify` là bộ lọc duy nhất đứng giữa hai chuyện đó.
399
-
400
243
  ## Output
401
244
 
402
- Ghi **hai file** dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/` + **một** dưới `{paths.refinement_dir}/` — **đừng** tách phân tích
245
+ Ghi **đúng HAI file** dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/` — **đừng** tách phân tích
403
246
  thành một-file-mỗi-bước (không có file spec-breakdown / business-rules / data-flow / AC riêng):
404
247
 
405
248
  1. **`{paths.qc_dir}/{UC-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md`** — bản phân tích hợp nhất duy nhất.
406
249
  Section theo thứ tự: phân rã requirement → bảng business-rule (`BR-xx`) → data-flow →
407
250
  acceptance-criteria (`AC-xx`), mỗi `BR`/`AC` map tới `{UC-ID}-SC{N}` (của `.feature` platform này) sở hữu nó.
408
- Cuối file thêm mục **"Đang chờ chốt (từ §12 tech-doc)"**các ẩn số thiết kế `open` chạm UC này
409
- (`{id}` · điều chưa biết · owner · severity). Rỗng thì ghi "Không có"; **đừng bỏ mục**.
410
- 2. **`{paths.qc_dir}/{UC-ID}/{active_platform}/DOC_GAP.md`** — file gap, theo
411
- `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` + luật viết ở
412
- `{paths.qc_skills_dir}/qa-analyst/spec-issue-reporter.md`. Bắt buộc:
413
- - **Bảng 10 cột** đúng thứ tự, có cột **Giao cho đội** (Dev / PO / BA / Design / Kiến trúc / Dữ liệu).
414
- - Ô câu hỏi đủ **bốn phần** tách bằng `<br/>`: **Bối cảnh → Vấn đề → Tại sao quan trọng → Gợi ý**.
415
- *Ba phần đầu cho PO xếp ưu tiên; phần cuối cho PO trả lời nhanh mà không phải nghĩ lại từ đầu.*
416
- - Section **"Tài liệu đầu vào đã đọc để phân tích"** đặt ngay sau metadata — liệt kê **đủ** mọi
417
- file đã mở. Đây là căn cứ độ phủ: không có nó thì không ai phân biệt được *"đã đọc và không thấy"*
418
- với *"chưa đọc"*.
419
- - Mức nặng nhất dùng từ **`🔴 Blocker`** (không phải `Critical`) — `/qc-run-test` đọc đúng từ này
420
- để đặt *"scenario đang chờ PO"* vào sổ trace.
421
- 3. **`{paths.refinement_dir}/{UC-ID}-qa-findings.yaml`** — **cùng dữ liệu gap**, ở định dạng Review Board đọc được. Xem §Bản findings dưới đây.
251
+ 2. **`{paths.qc_dir}/{UC-ID}/{active_platform}/DOC_GAPS.md`**file gaps (theo `{paths.qc_skills_dir}/qa-analyst/DOC_GAPS.template.md`).
422
252
 
423
253
  `{paths.qc_dir}` là folder top-level NHÌN THẤY trong QC repo (mặc định `docs/`, **không** phải
424
254
  `.agent/review/` ẩn) để team QC mở và xử lý output dễ dàng. Spec chính thức ở lại
425
255
  spec submodule của PO — đừng ghi phân tích vào đó.
426
256
 
427
- ### Bản findings — một nguồn, hai mặt
428
-
429
- File `.yaml` và `DOC_GAP.md` là **cùng một tập gap**, không phải hai tập. Sinh `DOC_GAP.md`
430
- trước (nó là bản người đọc), rồi **render** sang `.yaml` — đừng phân tích lại lần hai.
431
-
432
- Dùng **đúng schema của `/refine-prd`** để Review Board đọc được cả hai loại file:
433
-
434
- ```yaml
435
- prd_source: "{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md"
436
- uc_id: "{UC-ID}"
437
- platform: "{active_platform}"
438
- generated_at: "{ISO datetime}"
439
- generated_by: "qc-analyze"
440
- status: "pending_review"
441
-
442
- findings:
443
- - id: "F001"
444
- lens: "QA" # LUÔN là QA — file này chỉ có một lăng kính
445
- severity: "critical" # critical | major | minor ← map từ 🔴/🟠/🟡⚪ của DOC_GAP
446
- section: "{section PRD/BDD chứa vấn đề}"
447
- uc_id: "{UC-ID}"
448
- quote: "{trích nguyên văn ≤120 ký tự từ spec tại đúng chỗ}"
449
- finding: "{gap là gì}"
450
- suggestion: "{cần PO/BA làm rõ điều gì}"
451
- resolution_edge_cases: [] # để [] — phân tích bậc-hai là việc của /refine-prd
452
- auto_fixable: false # LUÔN false — xem cảnh báo dưới
453
- status: "pending"
454
- applied_via: ""
455
- gap_ref: "GAP-xx" # trỏ ngược về hàng trong DOC_GAP.md
456
-
457
- summary:
458
- total_findings: {N}
459
- by_severity: { critical: {N}, major: {N}, minor: {N} }
460
- by_lens: { QA: {N} }
461
- recommendation: "APPROVED_WITH_MINOR_CHANGES | NEEDS_REVISION | BLOCKED"
462
- ```
463
-
464
- > **`auto_fixable` LUÔN `false`, và KHÔNG có `--resume` cho file này.**
465
- >
466
- > Review Board có nút *"chấp nhận rồi tự sửa PRD"*. Với gap của `/refine-prd` thì đúng — nó chạy
467
- > ở **thời điểm PRD**, sửa PRD lúc đó là sửa đúng chỗ đúng lúc.
468
- >
469
- > Gap của lệnh này phát hiện **sau khi code đã xong**. Tự sửa PRD ở thời điểm đó là **sửa sau lưng
470
- > cả dây chuyền**: BDD sinh từ PRD cũ, code sinh từ BDD đó, sổ kết quả kiểm thử neo vào scenario
471
- > của BDD đó. Đổi PRD mà không đi lại đường ấy thì mọi thứ phía sau nói dối.
472
- >
473
- > Đường đúng vẫn là kênh đã có: `/report-bug` cho defect spec thật, `/propose-scenario` cho thiếu
474
- > độ phủ. File `.yaml` này để **PO đọc và quyết trong một chỗ quen**, không phải để máy tự áp.
475
-
476
- **File riêng, không ghi chung với `/refine-prd`.** Tên có `{UC-ID}` nên không đụng
477
- `{prd-slug}-findings.yaml`. Ghi chung sẽ phá trường `applied_to_version` mà `/refine-prd` dùng để
478
- phân biệt *"PRD đổi do chính tôi áp fix"* với *"có người lạ sửa"* — và nó sẽ mãi mãi tưởng có
479
- người sửa sau lưng, mỗi lần chạy đều quét lại toàn bộ kèm cảnh báo giả.
480
-
481
- ---
482
-
483
257
  ## Report
484
258
 
485
259
  **Đọc `.agent/steps/report-footer.md`** và áp đúng khuôn footer trong đó (Status Badge ·
486
260
  Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
487
261
 
488
262
  ```
489
- /qc-analyze Hoàn tất — {UC-ID} ({active_platform})
490
- Files : {paths.qc_dir}/{UC-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md + DOC_GAP.md (10 cột)
491
- {paths.refinement_dir}/{UC-ID}-qa-findings.yaml ← mở bằng Review Board (chuột phải)
492
- Nguồn : PRD · BDD({active_platform}) · design-spec · tech-doc{nếu thiếu tech-doc: " (THIẾU — không đối chiếu được nghiệp vụ ↔ kỹ thuật)"}
493
- Quét : 4 kỹ năng phân tích + 4 lăng kính (xử lý lỗi · giao diện · dữ liệu & cấu hình
494
- · đối chiếu chéo: PRD↔design-spec, bdd↔design-spec)
495
- Verify: {raw} gap thô → {N} còn Open (❌ {invalid} bịa/đã-trả-lời · ⚠️ {reclass} không phải gap nghiệp vụ · 🔁 {merge} trùng)
496
- Gaps : {N} ({blockers} blocker) ← blocker là spec-defect? → /report-bug {UC-ID} | coverage gap → /propose-scenario {UC-ID}
497
- Chờ chốt: {G} ẩn số §12 tech-doc đang open chạm UC này (đã ghi vào REQUIREMENT_ANALYSIS, KHÔNG hỏi lại PO)
498
- SC map: {M} BR/AC map tới {K} scenario
499
- Next : /qc-plan {UC-ID} {active_platform} ← rủi ro / what-if / câu hỏi cho dev
500
- (giải quyết các gap 🔴 Blocker với PO/Dev trước)
263
+ /qc-analyze Hoàn tất — {UC-ID}
264
+ Files: {paths.qc_dir}/{UC-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md + DOC_GAPS.md (2 files)
265
+ Gaps: {N} ({blockers} blocker) ← blocker là spec-defect? → /report-bug {UC-ID} | coverage gap /propose-scenario {UC-ID}
266
+ SC mapping: {M} BR/AC map tới {K} scenario
267
+ Next: /qc-plan {UC-ID} ← risk / what-if / questions-for-dev
268
+ (giải quyết các gap 🔴 Blocker với PO/Dev trước)
501
269
  ```
502
-
503
- > **Dòng `Next` là bắt buộc in, không phải trang trí.** Dây gốc của đội QC ra **cả** file gap
504
- > **và** kế hoạch test trong một lần chạy (13/14 lần đo được ở repo của họ). Ở framework đó là
505
- > **hai lệnh**. Người quen dây cũ sẽ dừng lại ở đây và tưởng đã xong — dòng này là chỗ duy nhất
506
- > nói cho họ biết còn một bước nữa.
507
-
@@ -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. **Phân giải `active_platform`** (QC pass khoá 1 platform): nếu `$ARGUMENTS` nêu platform → dùng; else glob `{paths.qc_dir}/{UC-ID}/*/` — đúng 1 folder platform → dùng nó, nhiều folder → hỏi. Đọc output của qc-analyze + qc-plan (`REQUIREMENT_ANALYSIS.md`, `DOC_GAP.md`, `TEST_PLAN.md`) từ `{paths.qc_dir}/{UC-ID}/{active_platform}/` 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.*
165
+ *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID. **Phân giải `active_platform`** (QC pass khoá 1 platform): nếu `$ARGUMENTS` nêu platform → dùng; else glob `{paths.qc_dir}/{UC-ID}/*/` — đúng 1 folder platform → dùng nó, nhiều folder → hỏi. Đọc output của qc-analyze + qc-plan (`REQUIREMENT_ANALYSIS.md`, `DOC_GAPS.md`, `TEST_PLAN.md`) từ `{paths.qc_dir}/{UC-ID}/{active_platform}/` 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 đó**,
@@ -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. **Phân giải `active_platform`** (QC pass khoá 1 platform — artifact dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/`): nếu `$ARGUMENTS` nêu platform (web/app/system) → dùng; else glob `{paths.qc_dir}/{UC-ID}/*/` — đúng 1 folder platform → dùng nó, nhiều folder → hỏi platform nào. Đọc output của qc-analyze (`REQUIREMENT_ANALYSIS.md` + `DOC_GAP.md`) từ `{paths.qc_dir}/{UC-ID}/{active_platform}/` và file `.feature` của đúng platform đó.*
165
+ *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID. **Phân giải `active_platform`** (QC pass khoá 1 platform — artifact dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/`): nếu `$ARGUMENTS` nêu platform (web/app/system) → dùng; else glob `{paths.qc_dir}/{UC-ID}/*/` — đúng 1 folder platform → dùng nó, nhiều folder → hỏi platform nào. Đọc output của qc-analyze (`REQUIREMENT_ANALYSIS.md` + `DOC_GAPS.md`) từ `{paths.qc_dir}/{UC-ID}/{active_platform}/` và file `.feature` của đúng platform đó.*
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 đó**,
@@ -183,12 +183,9 @@ thiết kế test case cụ thể (đó là qc-design-test).
183
183
 
184
184
  ## Skills (`{paths.qc_skills_dir}/qa-planner/`)
185
185
 
186
- - `test-plan.md` — khung plan: scope theo từng test layer (functional / integration /
187
- e2e / non-functional), what-if, entry/exit criteria, và danh sách questions-for-dev
188
- suy ra từ `DOC_GAP.md`.
189
- - `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 ·
190
- 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.
191
- Nạp cùng lúc, không phải thay thế.
186
+ - `test-plan.md` — tự đủ: risk matrix, what-if, scope theo từng test layer
187
+ (functional / integration / e2e / non-functional), entry/exit criteria, và
188
+ danh sách questions-for-dev suy ra từ `DOC_GAPS.md`.
192
189
 
193
190
  ## Output
194
191
 
@@ -225,7 +225,7 @@ Sau khi chạy, cập nhật **sổ của platform đang test** `{paths.trace_di
225
225
  | `qc_status` | Đọc cột `status` của row **TRƯỚC** — xem §Guard ngay dưới bảng. Row `OK`/`GAP`/`UNTRACKED`: `pass` nếu mọi QC test của SC này pass · `fail` nếu có cái fail · `skip` nếu tất cả skip/xfail · `not_run` nếu không QC test nào phủ nó. Row **`DRIFT`/`ORPHANED`**: **không bao giờ ghi `pass`** — hạ về `not_run` |
226
226
  | `qc_run_at` | hôm nay `YYYY-MM-DD` |
227
227
  | `last_updated` | hôm nay `YYYY-MM-DD` |
228
- | `qc_owner` | **SC đang chờ ai** (view "pending" của PM/PO): `dev` nếu FAIL = product-gap (defect thật → dev fix) · `po` nếu `skip`/`not_run` vì một **`DOC_GAP` 🔴 Blocker đang open** chặn test (PO phải làm rõ PRD/BDD) · `—` nếu `pass`, hoặc FAIL = script-bug (QC tự fix — tạm thời) |
228
+ | `qc_owner` | **SC đang chờ ai** (view "pending" của PM/PO): `dev` nếu FAIL = product-gap (defect thật → dev fix) · `po` nếu `skip`/`not_run` vì một **`DOC_GAPS` 🔴 Blocker đang open** chặn test (PO phải làm rõ PRD/BDD) · `—` nếu `pass`, hoặc FAIL = script-bug (QC tự fix — tạm thời) |
229
229
  | `qc_blocked_by` | artifact liên kết: `GAP-{id}` khi bị chặn bởi spec gap (set ở đây) · `BUG-{id}` khi `/report-bug` đã được file cho product-gap (backfill bởi `/report-bug`) · `—` ngược lại |
230
230
 
231
231
  Set `qc_owner`/`qc_blocked_by` cùng với `qc_status`. Khi `pass`, **clear** cả hai về `—` — nhưng **PHẢI chạy §Đóng bug đã verify bên dưới TRƯỚC**, vì `qc_blocked_by` chính là con trỏ tới bug và clear xong là mất đường về.
@@ -1,4 +1,4 @@
1
- # /refine-prd — Phân tích PRD qua 4 lăng kính review
1
+ # /refine-prd — Phân tích PRD qua 3 lăng kính review
2
2
 
3
3
  > **Ranh giới — lệnh này chỉ áp được fix cho vấn đề mà CHÍNH NÓ tìm ra.** Resume Mode Phase 2 tự
4
4
  > cấm đụng bất kỳ section nào không được một finding chấp nhận trỏ tới, và findings sinh từ việc soi
@@ -259,11 +259,10 @@ vòng không sinh thêm gì mới, *trước khi* ghi file findings.
259
259
 
260
260
  Lệnh gọi cung cấp hai thứ bắt buộc + hai tuỳ chọn:
261
261
  - **DIMENSIONS** — danh sách các chiều review để fan out
262
- (`/refine-prd` → 4 lăng kính; `/review-context` → các P-check hoặc B-check; `/qc-analyze` → 3 lăng kính quét gap).
262
+ (`/refine-prd` → 3 lăng kính; `/review-context` → các P-check hoặc B-check).
263
263
  - **FINDINGS SCHEMA** — dạng YAML mà mỗi finding phải theo (định nghĩa trong lệnh).
264
264
  - **GRANULARITY** *(tuỳ chọn, mặc định `auto`)* — `auto`: chọn độ mịn fan-out theo bảng ngưỡng kích thước ở Phase 1 (hành vi cũ). `per-uc`: **LUÔN** fan-out theo từng UC, **bỏ qua ngưỡng** — dùng cho review cần độ đầy đủ cao (`/refine-prd` truyền cái này để lần đầu đã quét sâu). Lệnh không truyền → `auto` → hành vi không đổi.
265
265
  - **CHANGED_SCOPE** *(tuỳ chọn)* — danh sách UC/section đã thay đổi (review **delta**). Nếu được truyền, Phase 1 chỉ fan-out trên các phạm vi này + PRD-global; Phase 2 critic vẫn quét **toàn doc** làm lưới an toàn. Không truyền → quét toàn bộ như thường.
266
- - **VERIFY** *(tuỳ chọn, mặc định `off`)* — `on` chèn **Phase 2.5** (`steps/gap-verify.md`) giữa critic và dedup: mỗi finding phải mở lại tài liệu nguồn tự chứng minh trước khi được giữ. Không truyền → hành vi không đổi.
267
266
 
268
267
  > **Bỏ qua ở chế độ sub-agent:** Nếu Gate Bước 0 đã set `_agent_mode: true`, toàn bộ
269
268
  > quy trình này bị **bỏ qua** — orchestrator đã chạy sẵn một dimension/UC cho mỗi
@@ -382,31 +381,6 @@ Ghi lại `convergence_rounds` (số vòng critic đã chạy) cho report.
382
381
 
383
382
  ---
384
383
 
385
- ## Phase 2.5 — Thẩm định *(chỉ chạy khi `VERIFY = on`)*
386
-
387
- **Vì sao có bước này.** Phase 1 và Phase 2 chỉ có **một chiều lực**: fan-out mở rộng bề
388
- ngang, critic lặp cho tới khi không còn gì mới — cả hai đều hỏi *"còn thiếu gì nữa?"*.
389
- Không có gì hỏi ngược lại *"cái vừa tìm ra có thật không?"*. Nên quy trình này đẩy **recall**
390
- lên mà **không có gì kéo precision lại**, và càng lặp critic thì tỉ lệ finding bịa càng cao —
391
- đúng thứ nó tự sinh ra: khẳng định hành vi tài liệu không nêu, trích evidence sai, hoặc gắn
392
- nhãn vấn đề cho thứ thực ra là chuyện làm-kỹ-hơn.
393
-
394
- Chạy `steps/gap-verify.md` trên `ALL_FINDINGS` với:
395
- - `FINDINGS` = `ALL_FINDINGS` (sau Phase 2)
396
- - `EVIDENCE_ROOT` = `{paths.specs_dir}` — hoặc giá trị lệnh gọi chỉ định
397
- - `VERDICT_FIELD` = trường trạng thái của FINDINGS SCHEMA mà lệnh định nghĩa
398
- - `RERATE` = `on`
399
-
400
- Finding bị `❌ INVALID` / `⚠️ RECLASSIFY` / `🔁 MERGE` **không đi tiếp sang Phase 3** — nhưng
401
- **KHÔNG bị xoá**: chúng vào file findings với trạng thái đóng + lý do, để người đọc kiểm chứng
402
- được vì sao chúng bị loại. Ghi lại số liệu verdict cho report.
403
-
404
- > **Chạy TRƯỚC Phase 3, không phải sau.** Dedup và giải quyết xung đột là việc tốn suy luận;
405
- > làm nó trên một tập còn lẫn finding bịa là vừa phí, vừa nguy hiểm — một finding ảo có thể
406
- > "thắng" một finding thật ở bước giữ-cái-severity-cao-hơn.
407
-
408
- ---
409
-
410
384
  ## Phase 3 — Dedup, giải quyết xung đột, merge
411
385
 
412
386
  Các sub-agent chạy **mù với nhau** (độc lập = độ phủ đa dạng). Chúng không bao giờ
@@ -438,7 +412,7 @@ Convergence: {convergence_rounds} vòng critic — file findings đã đầy đ
438
412
 
439
413
  ---
440
414
 
441
- ## Phân tích — 4 lăng kính (fan out cả bốn, rồi hội tụ)
415
+ ## Phân tích — 3 lăng kính (fan out cả ba, rồi hội tụ)
442
416
 
443
417
  Chạy review qua **Quy trình Review** ở trên (`steps/review-fanout.md`).
444
418
 
@@ -457,23 +431,22 @@ Chạy review qua **Quy trình Review** ở trên (`steps/review-fanout.md`).
457
431
  - **`applied_to_version` có mặt VÀ `==` version PRD hiện tại** → PRD đổi đúng bằng phần lệnh này tự áp, không actor khác động vào → **DELTA**: `CHANGED_SCOPE` = { `uc_id`/`section` của các finding `status: applied` trong findings cũ } ∪ { UC có trong PRD hiện tại nhưng chưa từng xuất hiện ở findings cũ }. Truyền `CHANGED_SCOPE` này vào Quy trình Review.
458
432
  - **`applied_to_version` vắng mặt HOẶC `≠` version hiện tại** → PRD đã bị sửa bởi **actor khác** (lệnh `/review-context`, sửa tay…) sau lần resume này → KHÔNG tin được phạm vi hẹp → **FULL** (KHÔNG truyền `CHANGED_SCOPE`), kèm cảnh báo: `"PRD đổi ngoài tầm theo dõi của findings (applied_to_version={A} ≠ hiện tại={C}); quét lại toàn bộ để khỏi sót UC do người/lệnh khác sửa."`
459
433
 
460
- **DIMENSIONS** = 4 lăng kính dưới đây — fan out một sub-agent cho mỗi lăng kính, mỗi cái quét
434
+ **DIMENSIONS** = 3 lăng kính dưới đây — fan out một sub-agent cho mỗi lăng kính, mỗi cái quét
461
435
  toàn bộ PRD qua đúng lăng kính của nó:
462
436
 
463
- - **Lăng kính QA (tầng nghiệm thu)** *(bật lại 2026-08-25 — xem `docs/plans/qc-implementation-log.md` B8)*: AC có nêu **outcome quan sát/kiểm được** chưa? — **KHÔNG** hỏi "AC đủ chi tiết chưa" (câu đó kéo cơ chế vào AC). Chi tiết cơ chế (số lần retry, timeout, tên/chủ cờ, nhánh lỗi vụn) thuộc **BR/BL**: nếu gap là cơ chế → suggestion phải **route sang BR/BL + AC ref**, KHÔNG phình AC. AC có lặp lại nội dung BR (trùng tầng) không → nếu có, đề xuất làm mỏng AC.
437
+ <!-- ─────────────────────────────────────────────────────────────────────────────
438
+ LĂNG KÍNH BỊ TẮT (DISABLED — KHÔNG dùng, KHÔNG fan-out agent cho lăng kính này):
464
439
 
465
- > **Phạm vi lăng kính QA hẹpchủ ý, đừng nới.** hỏi về **HÌNH THỨC** của AC (*"phát biểu này kiểm chứng được không?"* · *"có lặp tầng không?"*), **KHÔNG** về **NỘI DUNG** (*"còn thiếu gì?"*).
466
- >
467
- > do thời điểm: đây **chỉ PRD** — design-spec, BDD, tech-doc đều chưa tồn tại. Nên không thể phân biệt *"PRD thiếu X"* với *"PRD cố ý để X cho design-spec"*, và phép kiểm *"đã trả lời ở tài liệu khác chưa?"* **không có tài liệu khác để tra**.
468
- >
469
- > Số liệu thật (14 lần chạy ở repo QC): review chỉ-đọc-PRD ra **14 gap** trung bình, review đủ 4 nguồn ra **9,4** — khoảng 5 gap chênh lệch là câu hỏi mà tài liệu sau **trả lời hộ**. Nới lăng kính này sang câu hỏi nội dung là cố tình sinh ra 5 gap đó rồi gửi PO.
470
- >
471
- > Câu hỏi nội dung thuộc `/qc-analyze` (3 lăng kính: xử lỗi · giao diện · dữ liệu & cấu hình), nơi đã có đủ 4 nguồn để tra.
472
- >
473
- > **CÁCH TẮT LẠI QA** *(nếu cần)*: gỡ bullet QA ở trên · đổi `4 lăng kính`/`cả bốn` → `3`/`cả ba` ở
474
- > dòng tiêu đề, heading `## Phân tích`, câu `DIMENSIONS = N lăng kính`, và `steps/review-fanout.md` ·
475
- > gỡ `QA` khỏi enum `lens:` + `by_lens` + ghi chú `phán đoán QA/DEV/SA/PO` · sửa
476
- > `docs/explain/03-refine-prd.md` cho khớp · rebuild `node bin/build.js`.
440
+ - **Lăng kính QA (tầng nghiệm thu)**: AC nêu **outcome quan sát/kiểm được** chưa? **KHÔNG** hỏi "AC đủ chi tiết chưa" (câu đó kéo chế vào AC). Chi tiết chế (số lần retry, timeout, tên/chủ cờ, nhánh lỗi vụn) thuộc **BR/BL**: nếu gap là cơ chế → suggestion phải **route sang BR/BL + AC ref**, KHÔNG phình AC. AC có lặp lại nội dung BR (trùng tầng) không → nếu có, đề xuất làm mỏng AC.
441
+
442
+ CÁCH THÊM LẠI QA (khi cần bật lại giai đoạn sau):
443
+ 1. Chuyển bullet QA ở trên ra khỏi block comment này, đặt lên đầu danh sách DIMENSIONS.
444
+ 2. Đổi "3 lăng kính"/"cả ba" "4 lăng kính"/"cả bốn" ở: dòng tiêu đề (# /refine-prd),
445
+ heading "## Phân tích", câu "DIMENSIONS = N lăng kính", và steps/review-fanout.md.
446
+ 3. Thêm "QA" lại vào enum của `lens:` trong FINDINGS SCHEMA + vào `by_lens`.
447
+ 4. Thêm "QA" lại vào ghi chú "phán đoán DEV/SA/PO" ở LƯU Ý của schema.
448
+ 5. Rebuild: node bin/build.js
449
+ ───────────────────────────────────────────────────────────────────────────── -->
477
450
 
478
451
  > **Nguyên tắc chung cho DEV & SA — đọc bằng mắt kỹ thuật, VIẾT bằng lời nghiệp vụ.**
479
452
  > Hai lăng kính này dùng con mắt kỹ thuật để **phát hiện chỗ nghiệp vụ mô tả thiếu/mơ hồ/mâu thuẫn đến mức sẽ chặn triển khai** — mục tiêu là **làm rõ vấn đề nghiệp vụ để sau này xử lý được về mặt kỹ thuật**. **KHÔNG** đưa góc nhìn kỹ thuật vào PRD, **KHÔNG** đề xuất giải pháp/cơ chế kỹ thuật. Mọi `finding` và `suggestion` phải **thuần nghiệp vụ** (tuân Business Language Guard): mô tả *cái nghiệp vụ còn thiếu/chưa rõ* và *hỏi cần làm rõ gì*, chứ không nói *làm thế nào về kỹ thuật*.
@@ -510,7 +483,7 @@ status: "pending_review"
510
483
 
511
484
  findings:
512
485
  - id: "F001"
513
- lens: "DEV" # QA | DEV | SA | PO
486
+ lens: "DEV" # DEV | SA | PO
514
487
  severity: "major" # critical | major | minor
515
488
  section: "§2. Acceptance Criteria" # nhãn heading/section dạng người đọc
516
489
  uc_id: "{TICKET-ID}-UC{N}" # UC mà finding thuộc về; "" nếu PRD-global (scope, metrics, problem statement)
@@ -526,8 +499,8 @@ findings:
526
499
  # true = AI tự tin cao vào suggestion này; Review Board có thể hiển thị nút "quick accept"
527
500
  # false = cần human đọc kỹ và ghi quyết định trước khi accept
528
501
  # Resume Mode luôn áp dụng theo status (accepted|modified), bất kể auto_fixable.
529
- # LƯU Ý: /refine-prd CỐ Ý không có `--fix` mode (khác /review-context) — finding 4 lăng kính
530
- # là phán đoán QA/DEV/SA/PO, phải qua người duyệt ở Board; auto_fixable ở đây CHỈ là gợi ý
502
+ # LƯU Ý: /refine-prd CỐ Ý không có `--fix` mode (khác /review-context) — finding 3 lăng kính
503
+ # là phán đoán DEV/SA/PO, phải qua người duyệt ở Board; auto_fixable ở đây CHỈ là gợi ý
531
504
  # quick-accept cho Board, KHÔNG để máy tự áp.
532
505
  status: "pending"
533
506
  applied_via: ""
@@ -543,7 +516,7 @@ findings:
543
516
  summary:
544
517
  total_findings: {N}
545
518
  by_severity: { critical: {N}, major: {N}, minor: {N} }
546
- by_lens: { QA: {N}, DEV: {N}, SA: {N}, PO: {N} }
519
+ by_lens: { DEV: {N}, SA: {N}, PO: {N} }
547
520
  recommendation: "APPROVED_WITH_MINOR_CHANGES | NEEDS_REVISION | BLOCKED"
548
521
  # Rule: critical ≥ 1 → BLOCKED
549
522
  # critical = 0, major ≥ 1 → NEEDS_REVISION
@@ -1,7 +1,7 @@
1
1
  # /report-bug — File một Bug có trace-spec (cho Tester & QC)
2
2
 
3
3
  Dành cho **tester và QC** — gồm cả **product-gap** lòi ra từ pipeline `/qc-*`
4
- (`/qc-run-test` FAIL phân loại product-gap, hoặc một spec-defect blocker `DOC_GAP` từ
4
+ (`/qc-run-test` FAIL phân loại product-gap, hoặc một spec-defect blocker `DOC_GAPS` từ
5
5
  `/qc-analyze`). Sinh một bug report có cấu trúc với đầy đủ spec context, phân loại layer
6
6
  khả nghi, và lưu lại để handoff cho team dev.
7
7
 
@@ -267,11 +267,10 @@ vòng không sinh thêm gì mới, *trước khi* ghi file findings.
267
267
 
268
268
  Lệnh gọi cung cấp hai thứ bắt buộc + hai tuỳ chọn:
269
269
  - **DIMENSIONS** — danh sách các chiều review để fan out
270
- (`/refine-prd` → 4 lăng kính; `/review-context` → các P-check hoặc B-check; `/qc-analyze` → 3 lăng kính quét gap).
270
+ (`/refine-prd` → 3 lăng kính; `/review-context` → các P-check hoặc B-check).
271
271
  - **FINDINGS SCHEMA** — dạng YAML mà mỗi finding phải theo (định nghĩa trong lệnh).
272
272
  - **GRANULARITY** *(tuỳ chọn, mặc định `auto`)* — `auto`: chọn độ mịn fan-out theo bảng ngưỡng kích thước ở Phase 1 (hành vi cũ). `per-uc`: **LUÔN** fan-out theo từng UC, **bỏ qua ngưỡng** — dùng cho review cần độ đầy đủ cao (`/refine-prd` truyền cái này để lần đầu đã quét sâu). Lệnh không truyền → `auto` → hành vi không đổi.
273
273
  - **CHANGED_SCOPE** *(tuỳ chọn)* — danh sách UC/section đã thay đổi (review **delta**). Nếu được truyền, Phase 1 chỉ fan-out trên các phạm vi này + PRD-global; Phase 2 critic vẫn quét **toàn doc** làm lưới an toàn. Không truyền → quét toàn bộ như thường.
274
- - **VERIFY** *(tuỳ chọn, mặc định `off`)* — `on` chèn **Phase 2.5** (`steps/gap-verify.md`) giữa critic và dedup: mỗi finding phải mở lại tài liệu nguồn tự chứng minh trước khi được giữ. Không truyền → hành vi không đổi.
275
274
 
276
275
  > **Bỏ qua ở chế độ sub-agent:** Nếu Gate Bước 0 đã set `_agent_mode: true`, toàn bộ
277
276
  > quy trình này bị **bỏ qua** — orchestrator đã chạy sẵn một dimension/UC cho mỗi
@@ -390,31 +389,6 @@ Ghi lại `convergence_rounds` (số vòng critic đã chạy) cho report.
390
389
 
391
390
  ---
392
391
 
393
- ## Phase 2.5 — Thẩm định *(chỉ chạy khi `VERIFY = on`)*
394
-
395
- **Vì sao có bước này.** Phase 1 và Phase 2 chỉ có **một chiều lực**: fan-out mở rộng bề
396
- ngang, critic lặp cho tới khi không còn gì mới — cả hai đều hỏi *"còn thiếu gì nữa?"*.
397
- Không có gì hỏi ngược lại *"cái vừa tìm ra có thật không?"*. Nên quy trình này đẩy **recall**
398
- lên mà **không có gì kéo precision lại**, và càng lặp critic thì tỉ lệ finding bịa càng cao —
399
- đúng thứ nó tự sinh ra: khẳng định hành vi tài liệu không nêu, trích evidence sai, hoặc gắn
400
- nhãn vấn đề cho thứ thực ra là chuyện làm-kỹ-hơn.
401
-
402
- Chạy `steps/gap-verify.md` trên `ALL_FINDINGS` với:
403
- - `FINDINGS` = `ALL_FINDINGS` (sau Phase 2)
404
- - `EVIDENCE_ROOT` = `{paths.specs_dir}` — hoặc giá trị lệnh gọi chỉ định
405
- - `VERDICT_FIELD` = trường trạng thái của FINDINGS SCHEMA mà lệnh định nghĩa
406
- - `RERATE` = `on`
407
-
408
- Finding bị `❌ INVALID` / `⚠️ RECLASSIFY` / `🔁 MERGE` **không đi tiếp sang Phase 3** — nhưng
409
- **KHÔNG bị xoá**: chúng vào file findings với trạng thái đóng + lý do, để người đọc kiểm chứng
410
- được vì sao chúng bị loại. Ghi lại số liệu verdict cho report.
411
-
412
- > **Chạy TRƯỚC Phase 3, không phải sau.** Dedup và giải quyết xung đột là việc tốn suy luận;
413
- > làm nó trên một tập còn lẫn finding bịa là vừa phí, vừa nguy hiểm — một finding ảo có thể
414
- > "thắng" một finding thật ở bước giữ-cái-severity-cao-hơn.
415
-
416
- ---
417
-
418
392
  ## Phase 3 — Dedup, giải quyết xung đột, merge
419
393
 
420
394
  Các sub-agent chạy **mù với nhau** (độc lập = độ phủ đa dạng). Chúng không bao giờ