@educa-corp/sdd-framework 0.7.0 → 0.7.2

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 (35) hide show
  1. package/bin/lint-trace.js +64 -3
  2. package/bin/self-check.js +8 -5
  3. package/bin/trace-schema.json +44 -4
  4. package/core/FRAMEWORK_VERSION +1 -1
  5. package/core/commands/amend-prd.md +3 -2
  6. package/core/commands/dev-run-test.md +1 -1
  7. package/core/commands/generate-bdd.md +9 -6
  8. package/core/commands/generate-code.md +2 -2
  9. package/core/commands/generate-tech-docs.md +2 -2
  10. package/core/commands/map-testids.md +1 -1
  11. package/core/commands/validate-traces.md +6 -6
  12. package/core/steps/context-loader.md +1 -1
  13. package/core/templates/ci/trace-gate.gitlab-ci.yml +169 -0
  14. package/core/templates/project-context.yaml +3 -3
  15. package/core/templates/tech-design.template.md +2 -2
  16. package/docs/02-concepts/architecture.md +1 -1
  17. package/docs/02-concepts/overview.md +1 -1
  18. package/docs/02-concepts/pipeline-steps/02-specification.md +13 -7
  19. package/docs/02-concepts/pipeline-steps/07-dev-selftest.md +2 -0
  20. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +1 -0
  21. package/docs/02-concepts/pipeline-steps/09-validate-traces.md +34 -3
  22. package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +10 -1
  23. package/docs/02-concepts/traceability.md +187 -183
  24. package/docs/03-guides/architect.md +15 -5
  25. package/docs/03-guides/developer.md +1 -0
  26. package/docs/03-guides/product-owner.md +89 -72
  27. package/docs/03-guides/tester-qa.md +81 -81
  28. package/docs/04-reference/commands.md +1 -1
  29. package/docs/04-reference/trace-schema.md +8 -1
  30. package/docs/explain/02c-amend-prd.md +1 -1
  31. package/docs/explain/06-generate-bdd.md +1 -1
  32. package/docs/explain/13-dev-run-test.md +15 -1
  33. package/docs/explain/19-qc-run-test.md +91 -87
  34. package/docs/explain/21-validate-traces.md +79 -75
  35. package/package.json +1 -1
@@ -1,72 +1,89 @@
1
- [← Docs Home](../README.md) · [Guides](./)
2
-
3
- # Guide · Product Owner / BA
4
-
5
- > Bạn **định nghĩa cái gì đáng làm**. Vai trò của bạn nặng nhất ở **thượng nguồn** (Discovery → PRD → BDD) — nơi sai một ly đi một dặm.
6
-
7
- ---
8
-
9
- ## Chuỗi bước của bạn (Your path)
10
-
11
- ```mermaid
12
- flowchart LR
13
- A["/define-product<br/>🟢 Lead"] --> B["/generate-prd<br/>🟢 Lead"]
14
- B2["/extend-prd<br/>🟢 Lead — PRD đã có"] --> C
15
- B --> C["/refine-prd<br/>🟢 accept findings"]
16
- C --> D["/review-context PRD<br/>🔒 approve"]
17
- D --> E["/generate-design-spec<br/>🟡 review (FE/App)"]
18
- E --> F["/generate-bdd<br/>🟢 UC decomposition"]
19
- F --> G["/review-context BDD<br/>🔒 approve"]
20
- ```
21
-
22
- Sau khi BDD `approved`, bạn bàn giao xuống Dev/SA — nhưng vẫn nhận **product-gap** từ QC.
23
-
24
- ---
25
-
26
- ## Việc của bạn ở mỗi bước
27
-
28
- | Bước | Bạn làm gì | Quyết định |
29
- |------|-----------|------------|
30
- | [Discovery](../02-concepts/pipeline-steps/01-discovery.md) | Trả lời Q&A 8 chặng, **chốt từng chặng** | Vấn đề, user, UC, BR, AC, edge case, scope |
31
- | [Specification](../02-concepts/pipeline-steps/02-specification.md) | Duyệt PRD draft; **accept/reject từng finding** của `/refine-prd`; đặt `Status: approved` | Scope & terminology đúng chưa |
32
- | [Design-Spec](../02-concepts/pipeline-steps/03-design-spec.md) | Review spec visual bám Figma (FE/App) | Visual khớp intent |
33
- | [BDD](../02-concepts/pipeline-steps/04-bdd.md) | Chốt **UC decomposition**; đặt `@trace.status: approved` | Cấu trúc UC/SC đúng |
34
- | [QC](../02-concepts/pipeline-steps/08-qc-automation.md) | Nhận **product-gap**, quyết ưu tiên sửa | Gap nào lỗi sản phẩm |
35
-
36
- ---
37
-
38
- ## Nguyên tắc sống còn cho PO
39
-
40
- 1. **Viết thuần ngôn ngữ nghiệp vụ** — đừng nhét API/retry/timeout vào PRD/BDD. Business Language Guard sẽ chặn, nhưng bạn nên tự giữ altitude.
41
- 2. **Bốn ngăn không lộn**: AC (nghiệm thu) · BR/BL (cơ chế) · Scope (ranh giới) · Dictionary (định nghĩa).
42
- 3. **Gate trạng thái, không phải lệnh** — "duyệt" = bạn tự đặt `| Status | approved |`. Chỉ đặt khi **sạch finding critical**.
43
- 4. **Chốt từng chặng, đừng "để AI tự hiểu"** — AI sẽ suy diễn và bạn trả giá ở downstream.
44
- 5. Khi bạn sửa cùng một kiểu nhiều lần → gợi ý team `/learn` để ghi lesson.
45
-
46
- ---
47
-
48
- ## Câu hỏi bạn cần trả lời được
49
-
50
- - Tính năng này giải quyết pain point gì? Cho ai?
51
- - Gồm những UC/BR/AC nào? Edge case nào?
52
- - Cái gì **trong** scope, cái gì **ngoài**?
53
- - Mỗi scenario BDD có phủ đúng một AC/BR không?
54
-
55
- ---
56
-
57
- ## Anti-pattern
58
-
59
- - Đặt `approved` khi còn finding critical phá gate, code rác.
60
- - Bỏ `/refine-prd` "vì PRD trông ổn".
61
- - ❌ Chạy `/generate-prd` lại trên PRD đã có để "cập nhật" — nó **từ chối chạy**, và đúng vậy: ghi đè sẽ mất changelog + đánh số lại BR (phá liên kết ở mọi `.feature` đã sinh). Thêm yêu cầu thì dùng **`/extend-prd`**.
62
- - ❌ Viết dòng changelog kiểu `"cập nhật theo yêu cầu mới"` — dòng đó là **contract**. Mơ hồ thì `/generate-bdd` gen lại **toàn bộ**, và `/validate-traces` báo động oan cho **mọi** UC thay vì chỉ UC vừa đổi. Luôn nêu **UC/AC/BR bị ảnh hưởng**.
63
- - ❌ Để `feedback/prd-change-requests/` chất đống — đó là yêu cầu thật tester phát hiện từ sản phẩm chạy thật, và **không cờ trace nào bắt được** (chưa có AC thì theo mọi thước đo coverage nó *không tồn tại*). `/validate-traces` nhắc kèm số ngày chờ; xử bằng `/extend-prd`.
64
- - Nhảy từ ý tưởng thẳng sang yêu cầu Dev code.
65
-
66
- ---
67
-
68
- ## Lệnh của bạn (Your commands)
69
-
70
- `/define-product` · `/generate-prd` · `/extend-prd` · `/refine-prd` · `/review-context` · `/generate-design-spec` · `/generate-bdd`
71
-
72
- → [Bảng lệnh đầy đủ](../04-reference/commands.md) · [Glossary](../02-concepts/glossary.md)
1
+ [← Docs Home](../README.md) · [Guides](./)
2
+
3
+ # Guide · Product Owner / BA
4
+
5
+ > Bạn **định nghĩa cái gì đáng làm**. Vai trò của bạn nặng nhất ở **thượng nguồn** (Discovery → PRD → BDD) — nơi sai một ly đi một dặm.
6
+
7
+ ---
8
+
9
+ ## Chuỗi bước của bạn (Your path)
10
+
11
+ ```mermaid
12
+ flowchart LR
13
+ A["/define-product<br/>🟢 Lead"] --> B["/generate-prd<br/>🟢 Lead"]
14
+ B2["/extend-prd<br/>🟢 Lead — THÊM vào PRD đã có"] --> C
15
+ B3["/amend-prd<br/>🟢 Lead — ĐỔI yêu cầu đã có"] --> C
16
+ B --> C["/refine-prd<br/>🟢 accept findings"]
17
+ C --> D["/review-context PRD<br/>🔒 approve"]
18
+ D --> E["/generate-design-spec<br/>🟡 review (FE/App)"]
19
+ E --> F["/generate-bdd<br/>🟢 UC decomposition"]
20
+ F --> G["/review-context BDD<br/>🔒 approve"]
21
+ ```
22
+
23
+ Sau khi BDD `approved`, bạn bàn giao xuống Dev/SA — nhưng vẫn nhận **product-gap** từ QC.
24
+
25
+ ### Bốn nhánh chạm PRD — phân biệt bằng THAO TÁC GHI
26
+
27
+ | Bạn muốn | Lệnh | Ghi kiểu gì |
28
+ |---|---|---|
29
+ | PRD **chưa có** | `/generate-prd` | **Write** cả file |
30
+ | **THÊM** UC/AC/BR mới | `/extend-prd` | Edit **add-only** output **superset chặt**, đánh số **nối tiếp** |
31
+ | **ĐỔI** một yêu cầu đang đúng cú pháp *("BR8 nói tối đa 5 file, giờ đổi thành 20")* | **`/amend-prd`** | Edit **tại chỗ** output **KHÔNG** phải superset; bạn khai tường minh ID cần sửa |
32
+ | Sửa **vấn đề review chỉ ra** | `/refine-prd` Review Board `--resume` | Edit trong phạm vi finding |
33
+
34
+ **Bỏ hẳn một quy tắc** → `/amend-prd --retire {ID}`: khai tử **tại chỗ**, **giữ nguyên** row + ID. Không có lệnh xoá row, và có chủ ý — xoá làm `@trace.business_rules` trong `.feature` trỏ vào ID không còn ⇒ `TRACE_ORPHAN` 🔴.
35
+
36
+ > ⚠️ **Đừng sửa tay file `.md`** *(GAPS-v4 G54)*. Mọi cờ drift của framework so **nhãn version**, không so **nội dung** — nên sửa tay mà không bump version là điểm mù **tuyệt đối**: `/validate-traces` thấy version khớp ⇒ sạch · gate thấy report khớp sổ ⇒ PASS · CI thấy PR không chạm code ⇒ không đòi audit. Ba tầng xanh trên một yêu cầu code chưa hề làm theo.
37
+ >
38
+ > `/validate-traces` Step 3.9 canh cửa sau bằng cờ 🔴 `PRD_UNTRACKED_EDIT` (so `git diff` **và** `git status` với mốc lần audit trước). Đường sửa: bump `Version` + ghi một row changelog nêu UC — tức đúng việc `/amend-prd` làm hộ.
39
+
40
+ ---
41
+
42
+ ## Việc của bạn mỗi bước
43
+
44
+ | Bước | Bạn làm | Quyết định |
45
+ |------|-----------|------------|
46
+ | [Discovery](../02-concepts/pipeline-steps/01-discovery.md) | Trả lời Q&A 8 chặng, **chốt từng chặng** | Vấn đề, user, UC, BR, AC, edge case, scope |
47
+ | [Specification](../02-concepts/pipeline-steps/02-specification.md) | Duyệt PRD draft; **accept/reject từng finding** của `/refine-prd`; đặt `Status: approved` | Scope & terminology đúng chưa |
48
+ | [Design-Spec](../02-concepts/pipeline-steps/03-design-spec.md) | Review spec visual bám Figma (FE/App) | Visual khớp intent |
49
+ | [BDD](../02-concepts/pipeline-steps/04-bdd.md) | Chốt **UC decomposition**; đặt `@trace.status: approved` | Cấu trúc UC/SC đúng |
50
+ | [QC](../02-concepts/pipeline-steps/08-qc-automation.md) | Nhận **product-gap**, quyết ưu tiên sửa | Gap nào là lỗi sản phẩm |
51
+
52
+ ---
53
+
54
+ ## Nguyên tắc sống còn cho PO
55
+
56
+ 1. **Viết thuần ngôn ngữ nghiệp vụ** — đừng nhét API/retry/timeout vào PRD/BDD. Business Language Guard sẽ chặn, nhưng bạn nên tự giữ altitude.
57
+ 2. **Bốn ngăn không lộn**: AC (nghiệm thu) · BR/BL (cơ chế) · Scope (ranh giới) · Dictionary (định nghĩa).
58
+ 3. **Gate là trạng thái, không phải lệnh** — "duyệt" = bạn tự đặt `| Status | approved |`. Chỉ đặt khi **sạch finding critical**.
59
+ 4. **Chốt từng chặng, đừng "để AI tự hiểu"** AI sẽ suy diễn và bạn trả giá ở downstream.
60
+ 5. Khi bạn sửa cùng một kiểu nhiều lần → gợi ý team `/learn` để ghi lesson.
61
+
62
+ ---
63
+
64
+ ## Câu hỏi bạn cần trả lời được
65
+
66
+ - Tính năng này giải quyết pain point gì? Cho ai?
67
+ - Gồm những UC/BR/AC nào? Edge case nào?
68
+ - Cái **trong** scope, cái gì **ngoài**?
69
+ - Mỗi scenario BDD có phủ đúng một AC/BR không?
70
+
71
+ ---
72
+
73
+ ## Anti-pattern
74
+
75
+ - ❌ Đặt `approved` khi còn finding critical → phá gate, code rác.
76
+ - ❌ Bỏ `/refine-prd` "vì PRD trông ổn".
77
+ - ❌ Chạy `/generate-prd` lại trên PRD đã có để "cập nhật" — nó **từ chối chạy** (§Guard: *"Tồn tại → DỪNG. KHÔNG ghi, KHÔNG hỏi Y/N"*), và đúng vậy: ghi đè sẽ mất changelog + đánh số lại BR (phá liên kết ở mọi `.feature` đã sinh). **Thêm** yêu cầu → `/extend-prd`; **đổi** một yêu cầu đã có → `/amend-prd`.
78
+ - ❌ Mở file PRD `.md` ra sửa tay — xem cảnh báo G54 ở §Chuỗi bước. Framework **không thấy** thay đổi đó cho tới khi `/validate-traces` Step 3.9 bắt được, và mọi cờ drift ở giữa đều báo sạch.
79
+ - ❌ Viết dòng changelog kiểu `"cập nhật theo yêu cầu mới"` — dòng đó là **contract**. Mơ hồ thì `/generate-bdd` gen lại **toàn bộ**, và `/validate-traces` báo động oan cho **mọi** UC thay vì chỉ UC vừa đổi. Luôn nêu **UC/AC/BR bị ảnh hưởng**.
80
+ - ❌ Để `feedback/prd-change-requests/` chất đống — đó là yêu cầu thật tester phát hiện từ sản phẩm chạy thật, và **không cờ trace nào bắt được** (chưa có AC thì theo mọi thước đo coverage nó *không tồn tại*). `/validate-traces` nhắc kèm số ngày chờ; xử bằng `/extend-prd`.
81
+ - ❌ Nhảy từ ý tưởng thẳng sang yêu cầu Dev code.
82
+
83
+ ---
84
+
85
+ ## Lệnh của bạn (Your commands)
86
+
87
+ `/define-product` · `/generate-prd` · `/extend-prd` · `/refine-prd` · `/review-context` · `/generate-design-spec` · `/generate-bdd`
88
+
89
+ → [Bảng lệnh đầy đủ](../04-reference/commands.md) · [Glossary](../02-concepts/glossary.md)
@@ -1,81 +1,81 @@
1
- [← Docs Home](../README.md) · [Guides](./)
2
-
3
- # Guide · Tester / QA
4
-
5
- > Bạn **chạy kiểm thử chính thức** (dây chuyền `/qc-*`, Playwright) và là **kênh feedback** đưa bug/scenario ngược về spec. Bạn ghi `qc_status` — trạng thái QC chính thức, có evidence.
6
-
7
- ---
8
-
9
- ## Chuỗi bước của bạn (Your path)
10
-
11
- ```mermaid
12
- flowchart LR
13
- A["/qc-analyze"] --> P["/qc-plan"] --> D["/qc-design-test"]
14
- D --> R["/qc-review<br/>🛑 cổng"] --> RUN["/qc-run-test<br/>ghi qc_status"] --> REP["/qc-report<br/>product-gap"]
15
- REP --> FB["/report-bug · /propose-scenario"]
16
- FB --> SYNC["/sync"]
17
- ```
18
-
19
- ---
20
-
21
- ## Việc của bạn ở mỗi bước
22
-
23
- | Trạm | Bạn làm gì |
24
- |------|-----------|
25
- | [`/qc-analyze`](../02-concepts/pipeline-steps/08-qc-automation.md) | Phân rã yêu cầu + phát hiện **gap tài liệu** |
26
- | `/qc-plan` | Đánh giá rủi ro + câu hỏi cho dev |
27
- | `/qc-design-test` | Thiết kế test case Markdown (`*.Test.md`) |
28
- | `/qc-review` | 🛑 **Cổng review** case & script trước khi chạy |
29
- | `/qc-run-test` | Chạy pytest-playwright, ghi **`qc_status`**; phân loại FAIL |
30
- | `/qc-report` | Report + evidence, đẩy **product-gap** về PO/Dev |
31
- | [Feedback](../02-concepts/pipeline-steps/10-feedback-loop.md) | `/report-bug`, `/propose-scenario` — kênh có hồ sơ spec |
32
-
33
- Bạn cũng dùng `/validate-traces` để thấy **gap chưa phủ** (spec ↔ code ↔ test).
34
-
35
- ---
36
-
37
- ## Nguyên tắc sống còn cho QA
38
-
39
- 1. **`qc_status` ≠ `dev_selftest`** — bạn ghi QC chính thức (Playwright, evidence); dev smoke là trục độc lập.
40
- 2. **Không bao giờ fake-pass** — FAIL do product-gap thì **giữ FAIL + evidence**, đẩy về PO/Dev. Chỉ sửa script khi là script-bug (selector/logic).
41
- 3. **Không chạy test kém** — phải qua cổng `/qc-review` trước `/qc-run-test`.
42
- 4. **Bug phải spec-anchored** — `/report-bug` gắn `@trace` tới UC/SC để truy vết & regression.
43
- 5. **Bạn là người ĐÓNG bug** — `/fix-bug` của dev chỉ đặt `🟡 Fixed`; `🟢 Closed` do `/qc-run-test` đặt khi `qc_status` của SC liên kết flip `pass`. Dev không tự đóng bug của mình.
44
- - Ngoại lệ: SC pass mà bug còn `🟢 Open` (chưa ai fix) → **không đóng**, giữ `Open` + kiểm tra lại test. Test pass trên bug chưa fix là dấu hiệu **test sai**.
45
- 6. **`/propose-scenario` dùng đúng bộ tag canonical** — `@trace.scenario` (placeholder `SC?`, `/generate-bdd` gán số khi chèn) · `@trace.sc_version: 1.0` · `@trace.business_rules`. AC ghi thành comment `# Covers:`, **không** phải trace key. Thiếu `@trace.scenario`/`sc_version` thì scenario vào BDD mà **không có row trace** → vô hình với coverage.
46
- 7. Stack QC cố định: Python + pytest-playwright + Page Object (module `qc-playwright`), **độc lập** module của dev.
47
-
48
- ---
49
-
50
- ## Câu hỏi bạn cần trả lời được
51
-
52
- - Yêu cầu phân rã thành test case nào? Tài liệu có gap gì?
53
- - Rủi ro nào cao? Cần hỏi dev gì?
54
- - SC nào PASS/FAIL chính thức? FAIL là **script-bug** hay **product-gap**?
55
- - Bug này gắn với scenario/spec nào?
56
- - Scenario nào còn thiếu cần đề xuất (`/propose-scenario`)?
57
- - Hành vi phát hiện được có **AC nào phủ** không? → quyết định Case A hay Case B:
58
-
59
- | | Đi đâu | Ai xử |
60
- |---|---|---|
61
- | **Case A** — thiếu scenario cho AC **đã có** | `feedback/bdd-proposals/` | `/generate-bdd` tự chèn khi bạn đặt `Status: accepted` |
62
- | **Case B** — requirement **MỚI**, không AC nào phủ | `feedback/prd-change-requests/` | PO chạy `/extend-prd` |
63
-
64
- Case B **không tự vào BDD được** — scenario chưa có AC để trace tới. Và vì chưa có AC, **không cờ trace nào bắt được** thiếu sót đó. `/validate-traces` sẽ nhắc lại kèm **số ngày chờ** chừng nào request còn `Open`.
65
-
66
- ---
67
-
68
- ## Anti-pattern
69
-
70
- - ❌ Sửa script cho "xanh" khi thực chất là product-gap → giấu lỗi sản phẩm.
71
- - ❌ Chạy `/qc-run-test` khi chưa qua `/qc-review`.
72
- - ❌ Lẫn `qc_status` với `dev_selftest`.
73
- - ❌ Bug không gắn spec → khó truy vết, khó regression.
74
-
75
- ---
76
-
77
- ## Lệnh của bạn (Your commands)
78
-
79
- `/qc-analyze` · `/qc-plan` · `/qc-design-test` · `/qc-review` · `/qc-run-test` · `/qc-report` · `/report-bug` · `/propose-scenario` · `/validate-traces`
80
-
81
- → [Bảng lệnh đầy đủ](../04-reference/commands.md) · [Traceability](../02-concepts/traceability.md)
1
+ [← Docs Home](../README.md) · [Guides](./)
2
+
3
+ # Guide · Tester / QA
4
+
5
+ > Bạn **chạy kiểm thử chính thức** (dây chuyền `/qc-*`, Playwright) và là **kênh feedback** đưa bug/scenario ngược về spec. Bạn ghi `qc_status` — trạng thái QC chính thức, có evidence.
6
+
7
+ ---
8
+
9
+ ## Chuỗi bước của bạn (Your path)
10
+
11
+ ```mermaid
12
+ flowchart LR
13
+ A["/qc-analyze"] --> P["/qc-plan"] --> D["/qc-design-test"]
14
+ D --> R["/qc-review<br/>🛑 cổng"] --> RUN["/qc-run-test<br/>ghi qc_status"] --> REP["/qc-report<br/>product-gap"]
15
+ REP --> FB["/report-bug · /propose-scenario"]
16
+ FB --> SYNC["/sync"]
17
+ ```
18
+
19
+ ---
20
+
21
+ ## Việc của bạn ở mỗi bước
22
+
23
+ | Trạm | Bạn làm gì |
24
+ |------|-----------|
25
+ | [`/qc-analyze`](../02-concepts/pipeline-steps/08-qc-automation.md) | Phân rã yêu cầu + phát hiện **gap tài liệu** |
26
+ | `/qc-plan` | Đánh giá rủi ro + câu hỏi cho dev |
27
+ | `/qc-design-test` | Thiết kế test case Markdown (`*.Test.md`) |
28
+ | `/qc-review` | 🛑 **Cổng review** case & script trước khi chạy |
29
+ | `/qc-run-test` | Chạy pytest-playwright, ghi **`qc_status`**; phân loại FAIL. **Đọc cột `status` trước khi ghi `pass`** — row `DRIFT`/`ORPHANED` + test xanh → `not_run`, và **không đóng bug nào** ở lần chạy đó *(đóng bug dựa trên một lần QC chạy trên spec đã đổi là đóng sai)* |
30
+ | `/qc-report` | Report + evidence, đẩy **product-gap** về PO/Dev |
31
+ | [Feedback](../02-concepts/pipeline-steps/10-feedback-loop.md) | `/report-bug`, `/propose-scenario` — kênh có hồ sơ spec |
32
+
33
+ Bạn cũng dùng `/validate-traces` để thấy **gap chưa phủ** (spec ↔ code ↔ test).
34
+
35
+ ---
36
+
37
+ ## Nguyên tắc sống còn cho QA
38
+
39
+ 1. **`qc_status` ≠ `dev_selftest`** — bạn ghi QC chính thức (Playwright, evidence); dev smoke là trục độc lập.
40
+ 2. **Không bao giờ fake-pass** — FAIL do product-gap thì **giữ FAIL + evidence**, đẩy về PO/Dev. Chỉ sửa script khi là script-bug (selector/logic).
41
+ 3. **Không chạy test kém** — phải qua cổng `/qc-review` trước `/qc-run-test`.
42
+ 4. **Bug phải spec-anchored** — `/report-bug` gắn `@trace` tới UC/SC để truy vết & regression.
43
+ 5. **Bạn là người ĐÓNG bug** — `/fix-bug` của dev chỉ đặt `🟡 Fixed`; `🟢 Closed` do `/qc-run-test` đặt khi `qc_status` của SC liên kết flip `pass`. Dev không tự đóng bug của mình.
44
+ - Ngoại lệ: SC pass mà bug còn `🟢 Open` (chưa ai fix) → **không đóng**, giữ `Open` + kiểm tra lại test. Test pass trên bug chưa fix là dấu hiệu **test sai**.
45
+ 6. **`/propose-scenario` dùng đúng bộ tag canonical** — `@trace.scenario` (placeholder `SC?`, `/generate-bdd` gán số khi chèn) · `@trace.sc_version: 1.0` · `@trace.business_rules`. AC ghi thành comment `# Covers:`, **không** phải trace key. Thiếu `@trace.scenario`/`sc_version` thì scenario vào BDD mà **không có row trace** → vô hình với coverage.
46
+ 7. Stack QC cố định: Python + pytest-playwright + Page Object (module `qc-playwright`), **độc lập** module của dev.
47
+
48
+ ---
49
+
50
+ ## Câu hỏi bạn cần trả lời được
51
+
52
+ - Yêu cầu phân rã thành test case nào? Tài liệu có gap gì?
53
+ - Rủi ro nào cao? Cần hỏi dev gì?
54
+ - SC nào PASS/FAIL chính thức? FAIL là **script-bug** hay **product-gap**?
55
+ - Bug này gắn với scenario/spec nào?
56
+ - Scenario nào còn thiếu cần đề xuất (`/propose-scenario`)?
57
+ - Hành vi phát hiện được có **AC nào phủ** không? → quyết định Case A hay Case B:
58
+
59
+ | | Đi đâu | Ai xử |
60
+ |---|---|---|
61
+ | **Case A** — thiếu scenario cho AC **đã có** | `feedback/bdd-proposals/` | `/generate-bdd` tự chèn khi bạn đặt `Status: accepted` |
62
+ | **Case B** — requirement **MỚI**, không AC nào phủ | `feedback/prd-change-requests/` | PO chạy `/extend-prd` |
63
+
64
+ Case B **không tự vào BDD được** — scenario chưa có AC để trace tới. Và vì chưa có AC, **không cờ trace nào bắt được** thiếu sót đó. `/validate-traces` sẽ nhắc lại kèm **số ngày chờ** chừng nào request còn `Open`.
65
+
66
+ ---
67
+
68
+ ## Anti-pattern
69
+
70
+ - ❌ Sửa script cho "xanh" khi thực chất là product-gap → giấu lỗi sản phẩm.
71
+ - ❌ Chạy `/qc-run-test` khi chưa qua `/qc-review`.
72
+ - ❌ Lẫn `qc_status` với `dev_selftest`.
73
+ - ❌ Bug không gắn spec → khó truy vết, khó regression.
74
+
75
+ ---
76
+
77
+ ## Lệnh của bạn (Your commands)
78
+
79
+ `/qc-analyze` · `/qc-plan` · `/qc-design-test` · `/qc-review` · `/qc-run-test` · `/qc-report` · `/report-bug` · `/propose-scenario` · `/validate-traces`
80
+
81
+ → [Bảng lệnh đầy đủ](../04-reference/commands.md) · [Traceability](../02-concepts/traceability.md)
@@ -40,7 +40,7 @@ Mọi lệnh chạy chung một **Gate** (model check → target → context-loa
40
40
  > | **Đổi** một yêu cầu đang đúng cú pháp | **`/amend-prd`** | Edit **tại chỗ** — output **KHÔNG** phải superset; **không** đánh số mới, **không** xoá row |
41
41
  > | Sửa vấn đề **review đã soi ra** | `/refine-prd` → Review Board → `--resume` | Edit trong phạm vi finding |
42
42
  >
43
- > - `/generate-prd` chỉ có một cổng **chặn cứng** trên file đã có — `Y` vẫn ghi đè, ghi đè sẽ mất changelog + **đánh số lại BR** ⇒ hỏng `@trace.business_rules` trong mọi BDD đã sinh.
43
+ > - `/generate-prd` **DỪNG HẲN** trên file đã có — §Guard *"Tồn tại DỪNG. KHÔNG ghi, KHÔNG hỏi Y/N"*, vì ghi đè mất changelog + **đánh số lại BR** ⇒ hỏng `@trace.business_rules` trong mọi BDD đã sinh, và cả ba mất mát đều không hoàn tác được từ trong lệnh.
44
44
  > - `/refine-prd` **không** thêm/đổi được theo ý định mới (nó tự cấm đụng section ngoài findings).
45
45
  > - `/extend-prd` **không** sửa được nội dung cũ, trừ một cửa **phái sinh**: khi phần THÊM làm một BR cũ sai (Bước 3.2).
46
46
  > - **Xoá hẳn** một BR/AC → không có lệnh, và **có chủ ý**: xoá row biến `@trace.business_rules` trong `.feature` thành `TRACE_ORPHAN` 🔴. Dùng `/amend-prd --retire {ID}` — nó khai tử **tại chỗ**, giữ nguyên row + ID.
@@ -17,7 +17,7 @@
17
17
  | Tag | Ý nghĩa | Bắt buộc |
18
18
  |-----|---------|:--------:|
19
19
  | `@trace.id` | UC-ID — `{TICKET-ID}-UC{N}` (vd `SEG01-UC1`) | ✅ |
20
- | `@trace.platform` | `web` / `app` / `system` — **mọi mode, kể cả umbrella** | ✅ |
20
+ | `@trace.platform` | `web` / `app` / `system` / `webview` — **mọi mode, kể cả umbrella**. Đây là **delivery surface**, không phải tech stack: `webview` (bundle web nhúng trong app native — Phaser game, mini-app) là surface riêng, không phải `web` (browser) cũng không phải `app` (native) | ✅ |
21
21
  | `@trace.domain` | Domain nghiệp vụ | ✅ |
22
22
  | `@trace.prd` | TICKET-ID của PRD nguồn | ✅ |
23
23
  | `@trace.prd_version` | Version PRD lúc sinh BDD | ✅ |
@@ -172,6 +172,7 @@ Shared code dò qua **import chain** từ boundary → tránh tag explosion.
172
172
 
173
173
  | Cờ | Nguồn | Nghĩa |
174
174
  |---|---|---|
175
+ | `PRD_UNTRACKED_EDIT` 🔴 | Step 3.9 | **nội dung PRD đổi mà nhãn `Version` KHÔNG đổi** — có người sửa ngoài `/generate-prd` · `/extend-prd` · `/amend-prd` · `/refine-prd` · `/review-context`. So bằng `git diff` **và** `git status` với mốc `spec_baseline`. Cố ý **không** vào `gate.blocking` — xem dưới |
175
176
  | `PRD_DRIFT` | Step 4 | version PRD lệch **và** changelog **có** nêu UC này → nội dung đổi thật |
176
177
  | `PRD_STALE_REF` ⓘ | Step 4 | version lệch nhưng changelog **không** nêu UC này → chỉ con trỏ cũ. `--realign-prd-version` |
177
178
  | `BDD_DRIFT` | Step 5c | code mang `@trace.bdd_version` cũ hơn `.feature` |
@@ -186,8 +187,14 @@ Shared code dò qua **import chain** từ boundary → tránh tag explosion.
186
187
 
187
188
  > 🔴 = **chặn PR**. Build xanh, test từng-UC xanh, coverage đẹp — nhưng luồng ghép chạy vào no-op hoặc code trỏ vào scenario đã bị xoá.
188
189
  >
190
+ > 🔴 **`PRD_UNTRACKED_EDIT` là ngoại lệ có chủ ý — 🔴 nhưng KHÔNG chặn PR.** `gate.blocking` nghĩa hẹp là *code đang hỏng*; cờ này nói về **spec**, và code có thể đang hoàn toàn đúng. Thêm nữa: mọi project đang chạy đều đã có PRD sửa tay ⇒ một cờ chặn mới sẽ đỏ khắp nơi ở lần đầu ⇒ người ta **tắt cổng** ⇒ mất luôn 4 cờ chặn thật. Đó đúng là thất bại `self-check` R9(e) được viết ra để chống, chỉ đến bằng một cửa khác. Team đã dọn sạch nợ tồn thì tự thêm `prd_untracked_edit_count` vào `gate.blocking` (kèm `why`; R13(e) canh).
191
+ >
192
+ > **Đường ra tự lành, cố ý không có `--accept-edit`:** bump `Version` + ghi một row changelog nêu UC (tức đúng việc `/amend-prd` làm hộ) ⇒ `version != version_at_audit` ⇒ cờ tự tắt, và `PRD_DRIFT` bình thường tiếp quản. Một cờ escape sẽ là một đường **dán nhãn lên thay đổi chưa ai xem** — đúng cái ba rào của `--realign-*` tồn tại để chặn.
193
+ >
189
194
  > ⓘ = **không phải lỗi.** Hai cờ `*_STALE_REF` tồn tại vì version PRD/tech-doc là **MỘT số cho cả tài liệu nhiều UC** — thêm một UC làm mọi UC cũ lệch số dù không đổi một chữ. Không lọc thì cả loạt UC ăn cờ đỏ oan, và làm theo hướng dẫn cũng không tắt được (`/generate-code` skip row đang `OK`). Sạch bằng `--realign-*`: chỉ sửa dòng `@trace.*`, **không đụng logic**, và **từ chối chạy** nếu UC đó đang thật sự `DRIFT`/`ORPHANED`.
190
195
  >
196
+ > **Không kiểm được ≠ sạch.** `lint-trace` **T13** phát khi không hỏi được `git` (image CI thiếu git, hoặc sổ ngoài repo) — và `gate-trace` G0 **fail**, thay vì khẳng định `sổ được bảo vệ — 2/2 điều kiện` trên bằng chứng chưa bao giờ thu. Cùng nguyên tắc `spec_edit_detection.degraded`: *không bịa cờ, và không im lặng*.
197
+
191
198
  > **Mọi cờ đều PHẢI có counter `{flag}_count`** trong Step 7 + `summary` của `trace-report.json` — `bin/self-check.js` R7 ép, không có ngoại lệ. Thiếu counter = cờ vô hình với dashboard.
192
199
 
193
200
  ---
@@ -23,7 +23,7 @@ Ba lệnh có sẵn đều **từ chối đúng việc đó**:
23
23
 
24
24
  | Lệnh | Vì sao không dùng được |
25
25
  |---|---|
26
- | `/generate-prd` | Chỉ một cổng **chặn cứng** rồi vẫn ghi đè. Ghi đè mất changelog và **đánh số lại BR** ⇒ phá `@trace.business_rules` trong mọi `.feature` đã sinh. Đường mìn, không phải đường dùng được |
26
+ | `/generate-prd` | **DỪNG HẲN** §Guard *"Tồn tại DỪNG. KHÔNG ghi, KHÔNG hỏi Y/N"*. Đúng vậy: ghi đè mất changelog và **đánh số lại BR** ⇒ phá `@trace.business_rules` trong mọi `.feature` đã sinh, cả ba đều không hoàn tác được từ trong lệnh |
27
27
  | `/extend-prd` | **Add-only.** Bước 5 §3 đòi output là *"superset chặt"* của bản cũ. Có **một** cửa sửa nội dung cũ (Bước 3.2 case 1, "mâu thuẫn rule") nhưng nó **phái sinh** — chỉ mở khi phần THÊM làm BR cũ sai |
28
28
  | `/refine-prd` | Resume Mode Phase 2 tự cấm đụng section nào không được một finding trỏ tới, và findings sinh từ việc soi PRD hiện có ⇒ **không có đường nào để một ý định MỚI của PO đi vào** |
29
29
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  # 06 · `/generate-bdd` — Sinh kịch bản BDD (.feature)
4
4
 
5
- > **Một câu.** Phân rã PRD thành các file `.feature` (Gherkin) mang `@trace.*`, theo platform (web/app/system), với fan-out per-UC và tổng hợp **System BDD** từ BDD FE/App.
5
+ > **Một câu.** Phân rã PRD thành các file `.feature` (Gherkin) mang `@trace.*`, theo platform (web/app/webview/system), với fan-out per-UC và tổng hợp **System BDD** từ BDD FE/App.
6
6
 
7
7
  ---
8
8
 
@@ -37,7 +37,21 @@ Sinh test chưa đủ — phải chạy và biết pass/fail. Command chạy tes
37
37
  | web-frontend | Vitest / Jest; E2E Playwright / Cypress |
38
38
  | mobile | Flutter test, React Native, iOS (xcodebuild), Android |
39
39
  3. **Analyze Failures** — chẩn lỗi theo platform (điều gì fail, tại sao).
40
- 4. **Write Trace State** — set `dev_selftest` (pass/fail) trong `.tsv`.
40
+ 4. **Write Trace State** — **ĐỌC cột `status` của row TRƯỚC KHI GHI**, rồi set `dev_selftest`:
41
+
42
+ | `status` của row | Ghi gì |
43
+ |---|---|
44
+ | `OK` · `GAP` · `UNTRACKED` | `pass` / `fail` như thường |
45
+ | **`DRIFT`** | test **pass** → **`not_run`** *(KHÔNG ghi `pass`)* · test **fail** → **`fail`** như thường |
46
+ | **`ORPHANED`** | **`not_run`** — scenario đã bị xoá khỏi `.feature` |
47
+
48
+ > **Vì sao (GAPS-v4 G55).** `pass` **không** mang nghĩa *"test đã chạy và xanh"* — nó mang nghĩa *"scenario này đã được nghiệm thu theo spec **hiện tại**"*. Trên row `DRIFT` nghĩa thứ nhất đúng và nghĩa thứ hai **sai**.
49
+ >
50
+ > Bản cũ ghi `pass` chỉ dựa vào *test có xanh không*, nên chuỗi này báo xanh sai: PO đổi AC → `/generate-bdd` đặt `status = DRIFT` và **hạ** `dev_selftest → not_run` → sáng sau dev chạy lệnh này theo thói quen (chưa `/generate-code`, chưa `/dev-gen-test`) → test cũ + code cũ xanh hết → ghi `pass` **kèm ngày hôm nay**. Tức lệnh kế tiếp trong vòng lặp dev bình thường **dựng lại** đúng tín hiệu vừa bị hạ.
51
+ >
52
+ > **Tin xấu luôn hợp lệ:** đây là guard cho lời khẳng định **DƯƠNG**, không phải lệnh *"bỏ qua kết quả khi DRIFT"*. `fail` vẫn được ghi — chặn cả `fail` là biến guard chống-báo-cáo-sai thành guard **che tin xấu**.
53
+ >
54
+ > **Tầng thứ hai độc lập:** `lint-trace` **T12** bắt trạng thái này ở sổ thật (`status ∈ {DRIFT, ORPHANED}` mà `dev_selftest`/`qc_status = pass`), bất kể lệnh nào ghi ra — kể cả sổ sửa tay hoặc sổ sinh bởi version framework cũ.
41
55
  5. **Refresh Panel Mirror** — Living Docs local (umbrella).
42
56
 
43
57
  ---
@@ -1,87 +1,91 @@
1
- [← /qc-review](18-qc-review.md) · [Explain Home](README.md) · [Next: /qc-report →](20-qc-report.md)
2
-
3
- # 19 · `/qc-run-test` — Trạm 5: Sinh & chạy Playwright, ghi `qc_status`
4
-
5
- > **Một câu.** Biến `.Test.md` đã review thành **Python pytest-playwright**, chạy thật, rồi ghi **`qc_status` chính thức** (có evidence) vào trace TSV.
6
-
7
- ---
8
-
9
- ## Vấn đề giải quyết
10
-
11
- Đây là nơi QC trở thành **chính thức**: chạy test thật trên Playwright, phân loại FAIL (script-bug vs product-gap, **không fake-pass**), và đóng dấu `qc_status` — trạng thái QC authoritative.
12
-
13
- ---
14
-
15
- ## Vị trí & tiền đề
16
-
17
- - **Vị trí:** Phase QC (trạm 5), sau `/qc-review` (case APPROVED).
18
- - **Stack:** module `qc-playwright` (Python + pytest-playwright + Page Object) — **độc lập** module dev.
19
-
20
- ---
21
-
22
- ## Input / Output
23
-
24
- **Input:** `.Test.md` đã review + skill `qa-runner` + bảng Test Selectors §4.5.6 (**giá trị** test-id, từ `/map-testids`) + **`@trace.testid_attr`** ở header tech-doc (**tên thuộc tính** chứa chúng).
25
-
26
- **Output:** script Python + kết quả + cột `qc_status` trong `.trace/…/{UC-ID}-{platform}.tsv` + panel mirror (`.trace-mirror/`).
27
-
28
- > **`@trace.testid_attr` — đọc, KHÔNG suy từ platform.** §4.5.6 cho **giá trị** test-id; field này cho **tên thuộc tính** chứa chúng. `get_by_test_id()` của Playwright mặc định dò `data-testid` **nhưng cấu hình được** — dự án dùng `data-test`/`data-qa` thì phải `set_test_id_attribute("{attr}")` trước, không thì **trượt 100% locator**.
29
- >
30
- > Suy từ platform là **phát biểu lại một sự thật đã ghi ở nơi khác** (`/map-testids` đã phân giải một lần cho cả feature, `/generate-code` đọc chính field đó để emit). Và nó hỏng **im lặng theo kiểu tệ nhất**: test fail `element not found` — trông y hệt một bug sản phẩm, nên QC đi mở bug thay vì sửa selector. Thiếu field → **cảnh báo mềm nêu rõ rủi ro** rồi mới fallback.
31
-
32
- ---
33
-
34
- ## Các bước xử lý (chi tiết)
35
-
36
- 1. **Role & stack** — qc-playwright (`stack-profile.yaml`): Python, pytest-playwright fixture, Page Object; mỗi test độc lập; gom theo (role, account) để auth không xen kẽ.
37
- 2. **Skills** — nạp một file skill `qa-runner` theo layer.
38
- 3. **Sinh script** từ `.Test.md`; tag `@trace.verifies={UC-ID}-SC{N}`.
39
- 4. **Chạy** — phân loại mỗi FAIL: **script-bug** (fix selector/logic) vs **product-gap** (giữ FAIL + evidence, **không bao giờ fake-pass**).
40
- 5. **Write Trace State — `qc_status`** (kết quả QC chính thức) + `qc_run_at`, `qc_owner`, `qc_blocked_by`, `last_updated`.
41
- 6. **Đóng bug đã verify** — chạy **TRƯỚC** bước clear cột (xem dưới).
42
- 7. **Refresh Panel Mirror**Living Docs local.
43
-
44
- ---
45
-
46
- ## Checkpoint & Gate
47
-
48
- - Tiền đề: case đã APPROVED ở `/qc-review`. Script sinh ra → review lại ở `/qc-review` (script) trước PR.
49
-
50
- ---
51
-
52
- ## chế đặc biệt
53
-
54
- - **`qc_status` là trục authoritative** — khác `dev_selftest`; có evidence.
55
- - **Không fake-pass** — product-gap giữ nguyên FAIL, đẩy về PO/Dev.
56
- - **Stack QC tách hẳn dev** — `@trace.verifies` nối script ↔ SC.
57
- - **`active_platform` khoá sổ trace** — `qc_status` ghi đúng `{UC-ID}-{platform}.tsv`.
58
- - **Chủ sở hữu bước `🟡 Fixed → 🟢 Closed`.** `/report-bug` mở bug (`🟢 Open`), `/fix-bug` đặt `🟡 Fixed`, và **chỉ QC re-verify mới đóng được** dev không tự đóng bug của mình.
59
-
60
- ### sao đóng bug phải chạy TRƯỚC khi clear cột
61
-
62
- Khi `qc_status` flip `pass`, lệnh clear `qc_owner`/`qc_blocked_by` về `—`. Nhưng `qc_blocked_by` **chính con trỏ tới `{BUG-ID}`** clear xong mất đường về, đối chiếu tay cũng không làm được. Nên thứ tự bắt buộc: **đọc `qc_blocked_by` → đóng bug rồi mới clear**.
63
-
64
- | `State` của bug | SC vừa `pass` làm gì |
65
- |---|---|
66
- | `🟡 Fixed` | `🟢 Closed` + dòng `Verified: /qc-run-test {today} {UC-ID}-SC{N} pass` |
67
- | `🟢 Open` (chưa ai fix) | **KHÔNG đóng.** Giữ `Open` + ghi chú kiểm tra lại test |
68
- | `GAP-*` thay `BUG-*` | không đụng spec-gap thuộc PO, không phải QC |
69
-
70
- > Ca `Open` ngoại lệ **có chủ đích**: test pass trên một bug chưa ai fix là dấu hiệu **test sai**, không phải bug hết. Tự đóng ở đây sẽ **chôn một defect thật**.
71
-
72
- Bug report đã đổi phải **commit + push** vào spec repo — file local là dead drop, PO/Dev chỉ thấy sau khi push.
73
-
74
- ---
75
-
76
- ## 👓 Góc nhìn tối ưu
77
-
78
- - **Trạm nặng nhất của QC** — sinh + chạy + phân loại + ghi trace. Chạy thật phụ thuộc môi trường (browser, data, service lên).
79
- - **Phân loại script-bug vs product-gap phụ thuộc AI/reviewer** — sai loại → hoặc giấu lỗi sản phẩm hoặc báo nhầm. Đáng có tiêu chí rõ.
80
- - **Selector phụ thuộc `/map-testids`** — nếu chưa map, script giòn.
81
- - **Chạy lại tốn tài nguyên** — cân nhắc scoped run như dev-run-test.
82
-
83
- ---
84
-
85
- ## Kết nối
86
-
87
- **Trước:** [`/qc-review`](18-qc-review.md) (case) · **Sau:** [`/qc-report`](20-qc-report.md) rồi [`/qc-review`](18-qc-review.md) (script).
1
+ [← /qc-review](18-qc-review.md) · [Explain Home](README.md) · [Next: /qc-report →](20-qc-report.md)
2
+
3
+ # 19 · `/qc-run-test` — Trạm 5: Sinh & chạy Playwright, ghi `qc_status`
4
+
5
+ > **Một câu.** Biến `.Test.md` đã review thành **Python pytest-playwright**, chạy thật, rồi ghi **`qc_status` chính thức** (có evidence) vào trace TSV.
6
+
7
+ ---
8
+
9
+ ## Vấn đề giải quyết
10
+
11
+ Đây là nơi QC trở thành **chính thức**: chạy test thật trên Playwright, phân loại FAIL (script-bug vs product-gap, **không fake-pass**), và đóng dấu `qc_status` — trạng thái QC authoritative.
12
+
13
+ ---
14
+
15
+ ## Vị trí & tiền đề
16
+
17
+ - **Vị trí:** Phase QC (trạm 5), sau `/qc-review` (case APPROVED).
18
+ - **Stack:** module `qc-playwright` (Python + pytest-playwright + Page Object) — **độc lập** module dev.
19
+
20
+ ---
21
+
22
+ ## Input / Output
23
+
24
+ **Input:** `.Test.md` đã review + skill `qa-runner` + bảng Test Selectors §4.5.6 (**giá trị** test-id, từ `/map-testids`) + **`@trace.testid_attr`** ở header tech-doc (**tên thuộc tính** chứa chúng).
25
+
26
+ **Output:** script Python + kết quả + cột `qc_status` trong `.trace/…/{UC-ID}-{platform}.tsv` + panel mirror (`.trace-mirror/`).
27
+
28
+ > **`@trace.testid_attr` — đọc, KHÔNG suy từ platform.** §4.5.6 cho **giá trị** test-id; field này cho **tên thuộc tính** chứa chúng. `get_by_test_id()` của Playwright mặc định dò `data-testid` **nhưng cấu hình được** — dự án dùng `data-test`/`data-qa` thì phải `set_test_id_attribute("{attr}")` trước, không thì **trượt 100% locator**.
29
+ >
30
+ > Suy từ platform là **phát biểu lại một sự thật đã ghi ở nơi khác** (`/map-testids` đã phân giải một lần cho cả feature, `/generate-code` đọc chính field đó để emit). Và nó hỏng **im lặng theo kiểu tệ nhất**: test fail `element not found` — trông y hệt một bug sản phẩm, nên QC đi mở bug thay vì sửa selector. Thiếu field → **cảnh báo mềm nêu rõ rủi ro** rồi mới fallback.
31
+
32
+ ---
33
+
34
+ ## Các bước xử lý (chi tiết)
35
+
36
+ 1. **Role & stack** — qc-playwright (`stack-profile.yaml`): Python, pytest-playwright fixture, Page Object; mỗi test độc lập; gom theo (role, account) để auth không xen kẽ.
37
+ 2. **Skills** — nạp một file skill `qa-runner` theo layer.
38
+ 3. **Sinh script** từ `.Test.md`; tag `@trace.verifies={UC-ID}-SC{N}`.
39
+ 4. **Chạy** — phân loại mỗi FAIL: **script-bug** (fix selector/logic) vs **product-gap** (giữ FAIL + evidence, **không bao giờ fake-pass**).
40
+ 5. **Write Trace State — `qc_status`** (kết quả QC chính thức) + `qc_run_at`, `qc_owner`, `qc_blocked_by`, `last_updated`.
41
+
42
+ ⚠️ **ĐỌC cột `status` TRƯỚC KHI GHI `pass`** *(GAPS-v4 G55, đối xứng `/dev-run-test`)*: row `DRIFT`/`ORPHANED` + test xanh → **`not_run`**, không bao giờ `pass`. `fail` và `skip` ghi bình thường chỉ giá trị **khẳng định** cần giấy phép.
43
+
44
+ Và ở lệnh này hậu quả đi **xa hơn** `/dev-run-test`: một `pass` sai còn **đóng một bug** (§Đóng bug đã verify). Nên khi hạ về `not_run`, lệnh **KHÔNG** clear `qc_owner`/`qc_blocked_by` và **KHÔNG** chạy bước đóng bug — đóng bug dựa trên một lần QC chạy trên spec đã đổi là đóng sai.
45
+ 6. **Đóng bug đã verify** — chạy **TRƯỚC** bước clear cột (xem dưới).
46
+ 7. **Refresh Panel Mirror** — Living Docs local.
47
+
48
+ ---
49
+
50
+ ## Checkpoint & Gate
51
+
52
+ - Tiền đề: case đã APPROVED ở `/qc-review`. Script sinh ra → review lại ở `/qc-review` (script) trước PR.
53
+
54
+ ---
55
+
56
+ ## chế đặc biệt
57
+
58
+ - **`qc_status` trục authoritative** — khác `dev_selftest`; evidence.
59
+ - **Không fake-pass** — product-gap giữ nguyên FAIL, đẩy về PO/Dev.
60
+ - **Stack QC tách hẳn dev** `@trace.verifies` nối script ↔ SC.
61
+ - **`active_platform` khoá sổ trace** — `qc_status` ghi đúng `{UC-ID}-{platform}.tsv`.
62
+ - **Chủ sở hữu bước `🟡 Fixed 🟢 Closed`.** `/report-bug` mở bug (`🟢 Open`), `/fix-bug` đặt `🟡 Fixed`, **chỉ QC re-verify mới đóng được** dev không tự đóng bug của mình.
63
+
64
+ ### sao đóng bug phải chạy TRƯỚC khi clear cột
65
+
66
+ Khi `qc_status` flip `pass`, lệnh clear `qc_owner`/`qc_blocked_by` về `—`. Nhưng `qc_blocked_by` **chính con trỏ tới `{BUG-ID}`** — clear xong là mất đường về, và đối chiếu tay cũng không làm được. Nên thứ tự bắt buộc: **đọc `qc_blocked_by` → đóng bug → rồi mới clear**.
67
+
68
+ | `State` của bug | SC vừa `pass` làm |
69
+ |---|---|
70
+ | `🟡 Fixed` | `🟢 Closed` + dòng `Verified: /qc-run-test {today} {UC-ID}-SC{N} pass` |
71
+ | `🟢 Open` (chưa ai fix) | **KHÔNG đóng.** Giữ `Open` + ghi chú kiểm tra lại test |
72
+ | `GAP-*` thay `BUG-*` | không đụng spec-gap thuộc PO, không phải QC |
73
+
74
+ > Ca `Open` là ngoại lệ **có chủ đích**: test pass trên một bug chưa ai fix là dấu hiệu **test sai**, không phải bug hết. Tự đóng ở đây sẽ **chôn một defect thật**.
75
+
76
+ Bug report đã đổi phải **commit + push** vào spec repo — file local là dead drop, PO/Dev chỉ thấy sau khi push.
77
+
78
+ ---
79
+
80
+ ## 👓 Góc nhìn tối ưu
81
+
82
+ - **Trạm nặng nhất của QC** — sinh + chạy + phân loại + ghi trace. Chạy thật phụ thuộc môi trường (browser, data, service lên).
83
+ - **Phân loại script-bug vs product-gap phụ thuộc AI/reviewer** — sai loại → hoặc giấu lỗi sản phẩm hoặc báo nhầm. Đáng có tiêu chí rõ.
84
+ - **Selector phụ thuộc `/map-testids`** — nếu chưa map, script giòn.
85
+ - **Chạy lại tốn tài nguyên** — cân nhắc scoped run như dev-run-test.
86
+
87
+ ---
88
+
89
+ ## Kết nối
90
+
91
+ **Trước:** [`/qc-review`](18-qc-review.md) (case) · **Sau:** [`/qc-report`](20-qc-report.md) rồi [`/qc-review`](18-qc-review.md) (script).