@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
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
# Kế hoạch sáp nhập dây chuyền QC vào framework
|
|
2
|
+
|
|
3
|
+
> Đội QC đang chạy một dây chuyền AI automation test **7 agent / 45 skill** bên ngoài framework
|
|
4
|
+
> (`D:\projects\ui-automation-testing`). Framework có **6 trạm QC / 35 skill file**.
|
|
5
|
+
> Tài liệu này giải thích cả hai đang làm gì, ba chỗ chúng va nhau, và cách gộp lại.
|
|
6
|
+
|
|
7
|
+
| | |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Repo | `sdd-framework @ main` |
|
|
10
|
+
| Nguồn QC | `ui-automation-testing` (`.claude/agents/3-qa-tc-analyst.md` + `.claude/skills/qa-tc-analyst/`) |
|
|
11
|
+
| Trạng thái | chờ 3 quyết định (§5) |
|
|
12
|
+
| Cập nhật | 2026-08-22 |
|
|
13
|
+
|
|
14
|
+
**Mục lục**
|
|
15
|
+
|
|
16
|
+
1. [Cốt lõi: framework cần *sổ cái*, không cần report](#1)
|
|
17
|
+
2. [Framework: sáu trạm, đi theo một UC](#2)
|
|
18
|
+
3. [Đội QC: bảy agent, ba lane](#3)
|
|
19
|
+
4. [Ánh xạ hai bên](#4)
|
|
20
|
+
5. [Ba quyết định](#5)
|
|
21
|
+
6. [Món quà đi ngược chiều](#6)
|
|
22
|
+
7. [Plan năm phase](#7)
|
|
23
|
+
8. [Bốn lỗi tìm được khi đọc `/qc-analyze`](#8)
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
<a id="1"></a>
|
|
28
|
+
## 1 · Cốt lõi: framework cần *sổ cái*, không cần report
|
|
29
|
+
|
|
30
|
+
> Hiểu điều này trước, mọi thiết kế còn lại tự giải thích.
|
|
31
|
+
|
|
32
|
+
Trước khi QC vào việc, framework đã có sẵn ba thứ do các phase trước sinh ra.
|
|
33
|
+
Ví dụ thật: feature `FT-001-free-trial`, use case `FT-001-UC4` (làm survey), platform `web`.
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
specs/onboarding/free-trial/
|
|
37
|
+
├── FT-001-free-trial.md PRD — PO viết, đã approved
|
|
38
|
+
├── bdd/web/FT-001-UC4.feature Gherkin — mỗi scenario một ID:
|
|
39
|
+
│ FT-001-UC4-SC1, SC2, SC3…
|
|
40
|
+
└── tech-docs/FT-001-tech-design.md §4.5.6 có bảng test-id
|
|
41
|
+
|
|
42
|
+
.trace/onboarding/free-trial/
|
|
43
|
+
└── FT-001-UC4-web.tsv ← SỔ CÁI. 1 dòng = 1 scenario.
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
File `.tsv` đó là trái tim của framework:
|
|
47
|
+
|
|
48
|
+
| sc_id | status | dev_selftest | qc_status | qc_owner | qc_blocked_by |
|
|
49
|
+
|---|---|---|---|---|---|
|
|
50
|
+
| FT-001-UC4-SC1 | OK | pass | `not_run` | `—` | `—` |
|
|
51
|
+
| FT-001-UC4-SC2 | OK | pass | `not_run` | `—` | `—` |
|
|
52
|
+
| FT-001-UC4-SC3 | DRIFT | pass | `not_run` | `—` | `—` |
|
|
53
|
+
|
|
54
|
+
> **Điểm mấu chốt:** toàn bộ 6 trạm QC tồn tại để điền **ba cột `qc_*`** đó.
|
|
55
|
+
> Không phải để ra report.
|
|
56
|
+
|
|
57
|
+
Khác biệt giữa report và sổ cái không phải khác biệt hình thức — nó là khác biệt **cơ chế**:
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
A · DÂY CHUYỀN QC ĐỘC LẬP
|
|
61
|
+
|
|
62
|
+
spec ─► phân tích ─► test case ─► script ─► chạy ─► report.html ─┤
|
|
63
|
+
│
|
|
64
|
+
tài liệu chết — ai không mở thì không biết
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
B · FRAMEWORK
|
|
68
|
+
|
|
69
|
+
spec ─► phân tích ─► test case ─► script ─► chạy ─GHI─► ┌──────────────┐
|
|
70
|
+
│ SỔ CÁI │
|
|
71
|
+
│ .tsv │
|
|
72
|
+
│ qc_status │
|
|
73
|
+
└──┬────────┬──┘
|
|
74
|
+
tín hiệu │ │
|
|
75
|
+
sống │ │
|
|
76
|
+
┌───────────────◄┘ └►───────────────┐
|
|
77
|
+
│ dashboard PO / PM │ │ cổng chặn PR │
|
|
78
|
+
└───────────────────┘ └───────────────┘
|
|
79
|
+
|
|
80
|
+
qc_status = fail → cổng chặn merge
|
|
81
|
+
qc_owner = dev → dashboard hiện "SC này đang chờ dev"
|
|
82
|
+
qc_owner = po → đang chờ PO làm rõ tài liệu
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Report là tài liệu chết; `qc_status` là tín hiệu sống.**
|
|
86
|
+
Report nằm im chờ người mở. Sổ cái thì chặn PR, điều khiển dashboard, và nói được
|
|
87
|
+
"scenario này đang chờ ai".
|
|
88
|
+
|
|
89
|
+
→ Nên khi đưa dây chuyền QC vào framework, thứ framework thực sự cần từ nó **không phải**
|
|
90
|
+
DOC_GAP hay TEST_PLAN — mà là: *cuối cùng có ghi được `qc_status` đúng cho từng scenario
|
|
91
|
+
hay không?* Mọi thứ khác là phương tiện.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
<a id="2"></a>
|
|
96
|
+
## 2 · Framework: sáu trạm, đi theo một UC
|
|
97
|
+
|
|
98
|
+
> Mỗi trạm là một lệnh riêng, giao việc cho trạm sau **bằng file**.
|
|
99
|
+
|
|
100
|
+
```
|
|
101
|
+
TRẠM 1 TRẠM 2 TRẠM 3 TRẠM 4 TRẠM 5 TRẠM 6
|
|
102
|
+
/qc-analyze ─►/qc-plan ─► /qc-design-test ─►/qc-review ─►/qc-run-test ─►/qc-report
|
|
103
|
+
│ │ │ ↑ │ │ │
|
|
104
|
+
│ │ │ └──┘ │ │
|
|
105
|
+
│ │ │ chạy 2 lần: │ │
|
|
106
|
+
│ │ │ review test-case, │ │
|
|
107
|
+
│ │ │ rồi review script │ │
|
|
108
|
+
▼ ▼ ▼ ▼ ▼
|
|
109
|
+
┌─────────────┐ ┌──────────┐ ┌────────────┐ ┌───────────┐ ┌────────────┐
|
|
110
|
+
│REQUIREMENT_ │ │TEST_PLAN │ │*.Test.md │ │tests/*.py │ │report.html │
|
|
111
|
+
│ANALYSIS.md │ │.md │ │@trace. │ └─────┬─────┘ │trace.zip │
|
|
112
|
+
├─────────────┤ └──────────┘ │ verifies= │ │ └────────────┘
|
|
113
|
+
│DOC_GAPS.md │ │ SC2 │ GHI qc_status
|
|
114
|
+
└──────┬──────┘ └────────────┘ │
|
|
115
|
+
│ ▼
|
|
116
|
+
│ ┌──────────────────────────────┐ ┌──────────────────┐
|
|
117
|
+
└─►│ trạm 2 VÀ trạm 3 đều ĐỌC lại │ │ SỔ CÁI .tsv │
|
|
118
|
+
│ file này ←── đây là CÂU 1 │ └──────────────────┘
|
|
119
|
+
└──────────────────────────────┘
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**Trạm 5 còn hai đặc quyền riêng:**
|
|
123
|
+
|
|
124
|
+
- **Chủ sở hữu duy nhất** của bước bug `🟡 Fixed → 🟢 Closed`.
|
|
125
|
+
`/report-bug` mở bug, `/fix-bug` đặt `Fixed`, và **chỉ QC chạy lại mới được đóng**.
|
|
126
|
+
- **Guard G55 — quyền được nói "pass".**
|
|
127
|
+
SC3 test XANH, nhưng `status = DRIFT` (spec đã đổi sau khi sinh code)
|
|
128
|
+
→ ghi `not_run`, **KHÔNG** ghi `pass`, **KHÔNG** đóng bug nào.
|
|
129
|
+
*Test xanh trên spec cũ không được quyền nói "đã nghiệm thu".*
|
|
130
|
+
|
|
131
|
+
### Từng trạm, bằng một câu
|
|
132
|
+
|
|
133
|
+
| # | Lệnh | Câu hỏi nó trả lời | Ghi ra |
|
|
134
|
+
|---|---|---|---|
|
|
135
|
+
| 1 | `/qc-analyze` | Yêu cầu này **là gì?** Bóc thành BR-01…, AC-01…, mỗi cái ghi rõ thuộc scenario nào. | `REQUIREMENT_ANALYSIS.md`<br>`DOC_GAPS.md` |
|
|
136
|
+
| 2 | `/qc-plan` | Rủi ro **ở đâu**, phải hỏi dev gì? Risk matrix, what-if, entry/exit criteria. | `TEST_PLAN.md` |
|
|
137
|
+
| 3 | `/qc-design-test` | Test case cụ thể. Markdown, chưa có code. TC bị chặn bởi gap vẫn viết, đánh dấu `🚫 Block: GAP-03`. | `test-cases/*.Test.md` |
|
|
138
|
+
| 4 | `/qc-review` | Cổng. Chạy **hai lần** — lần 1 review test case, lần 2 review script Python. | verdict |
|
|
139
|
+
| 5 | `/qc-run-test` | Sinh Python, chạy, **ghi sổ cái**. Trạm quan trọng nhất. | `tests/*.py` + `qc_status` |
|
|
140
|
+
| 6 | `/qc-report` | Evidence + đẩy defect thật về spec (in ra lệnh `/report-bug` sẵn chạy). | `report.html`, `trace.zip` |
|
|
141
|
+
|
|
142
|
+
### Sau khi trạm 5 chạy, sổ cái trông thế này
|
|
143
|
+
|
|
144
|
+
| sc_id | status | qc_status | qc_owner | qc_blocked_by | Nghĩa là |
|
|
145
|
+
|---|---|---|---|---|---|
|
|
146
|
+
| SC1 | OK | `pass` | `—` | `—` | Xong. Nghiệm thu theo spec hiện tại. |
|
|
147
|
+
| SC2 | OK | `fail` | **dev** | BUG-012 | Lỗi thật. Dashboard hiện "chờ dev". Cổng chặn PR. |
|
|
148
|
+
| SC3 | DRIFT | `not_run` | `—` | `—` | Test xanh, nhưng spec đã đổi → không được quyền nói pass. |
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
<a id="3"></a>
|
|
153
|
+
## 3 · Đội QC: bảy agent, ba lane
|
|
154
|
+
|
|
155
|
+
> Cùng công việc, nhưng cắt theo một **trục khác**.
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
┌───── REJECT → sửa lại ─────┐
|
|
159
|
+
▼ │
|
|
160
|
+
inputs/ ─►(1)──────────►(2)───────────►(3)──────────►(4)───────────►(5)──────►(6)──────►(7)
|
|
161
|
+
qa-io-contract qa-orchestrator qa-tc-analyst qa-tc-designer qa-tc- qa-runner qa-gate
|
|
162
|
+
2 skill 2 skill 18 skill 5 skill reviewer 2 skill 5 skill
|
|
163
|
+
validate input, routing 3 lane 11 skill
|
|
164
|
+
quét injection
|
|
165
|
+
└────────────────────────────────────┘
|
|
166
|
+
3 LANE XUYÊN SUỐT: UI · API · E2E
|
|
167
|
+
lane API tự SKIP hoàn toàn nếu
|
|
168
|
+
không có openapi / dbml / tdd
|
|
169
|
+
│
|
|
170
|
+
▼
|
|
171
|
+
┌──────────────────────┐
|
|
172
|
+
│ QA_GATE_REPORT.md │──┤
|
|
173
|
+
└──────────────────────┘
|
|
174
|
+
không có sổ cái
|
|
175
|
+
|
|
176
|
+
AUTO-CHAINING: gọi agent 6 mà thiếu output 3/4/5 → tự chạy 3 → 4 → 5 trước.
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
**Ba lane là trục chính của đội QC, xuất hiện trong mọi agent.**
|
|
180
|
+
Framework không có trục này — nó cắt theo *layer* (functional / integration / e2e /
|
|
181
|
+
non-functional / exploratory) **nhân với** *platform* (web / app / system).
|
|
182
|
+
Hai trục không mâu thuẫn, nhưng cắt chéo nhau — xem §4.
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
<a id="4"></a>
|
|
187
|
+
## 4 · Ánh xạ hai bên
|
|
188
|
+
|
|
189
|
+
> Bốn trạm khớp gần như hoàn hảo. Vấn đề dồn hết vào một chỗ.
|
|
190
|
+
|
|
191
|
+
| # | Đội QC | Framework | Tình trạng |
|
|
192
|
+
|---|---|---|---|
|
|
193
|
+
| 1 | `qa-io-contract` — 2 skill, validate input + quét prompt-injection | *không có* — thay bằng `gate` + `context-loader` | ⚠️ **thiếu** Framework mất phần chống injection. Spec là dữ liệu để đọc, không phải mệnh lệnh — đội bạn có gác, framework không. |
|
|
194
|
+
| 2 | `qa-orchestrator` — 2 skill, routing 3 lane | *không có* — thay bằng `Platform Resolution` | ⚠️ **lệch trục** lane ↔ platform |
|
|
195
|
+
| **3** | **`qa-tc-analyst` — 18 skill** | **`/qc-analyze` (4) + `/qc-plan` (1)** | 🔴 **CHỖ ĐANG LÀM.** Framework cắt 1 agent thành 2 trạm, và chỉ lấy 5/18 skill. |
|
|
196
|
+
| 4 | `qa-tc-designer` — 5 skill | `/qc-design-test` — 11 file | ✅ khớp, framework còn phong phú hơn |
|
|
197
|
+
| 5 | `qa-tc-reviewer` — 11 skill | `/qc-review` — 10 file | ✅ khớp |
|
|
198
|
+
| 6 | `qa-runner` — 2 skill | `/qc-run-test` — 7 file *+ ghi sổ cái + đóng bug* | ✅ framework làm nhiều hơn |
|
|
199
|
+
| 7 | `qa-gate` — 5 skill | `/qc-report` — 1 file | ⚠️ **mỏng hơn** framework thiếu `coverage-evaluator`, `risk-matrix-builder`, `gate-decision` |
|
|
200
|
+
|
|
201
|
+
### Điều có lẽ chưa biết
|
|
202
|
+
|
|
203
|
+
Framework hiện tại port từ nhánh **`lms_autotest`**, không phải nhánh hợp nhất đang chạy.
|
|
204
|
+
Bằng chứng nằm trong chính file agent của đội QC:
|
|
205
|
+
|
|
206
|
+
```
|
|
207
|
+
## Skill bổ sung (hợp nhất từ lms_autotest)
|
|
208
|
+
spec-breakdown · business-rules · data-flow · acceptance-criteria · test-plan
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
**Đúng năm cái đó, không hơn không kém, là toàn bộ những gì framework đang có ở trạm 1 + 2.**
|
|
212
|
+
Framework lấy đúng phần *phụ trợ* của nhánh cũ và bỏ lại 13 skill lõi của nhánh mới.
|
|
213
|
+
(`test-plan` thì bị đẩy sang `qa-planner/` = trạm 2.)
|
|
214
|
+
|
|
215
|
+
### Trục cắt chéo nhau — nhìn cụ thể
|
|
216
|
+
|
|
217
|
+
```
|
|
218
|
+
LAYER CỦA FRAMEWORK LANE CỦA ĐỘI QC
|
|
219
|
+
UI API E2E (không có lane)
|
|
220
|
+
functional/gui-screen [✓]
|
|
221
|
+
functional/gui-feature [✓]
|
|
222
|
+
integration/gui [✓]
|
|
223
|
+
functional/api [✓]
|
|
224
|
+
integration/api [✓]
|
|
225
|
+
e2e/journey [✓]
|
|
226
|
+
integration/db [░░░░░░░░] ┐
|
|
227
|
+
integration/kafka [░░░░░░░░] │ 4 layer framework
|
|
228
|
+
non-functional [░░░░░░░░] │ không có lane
|
|
229
|
+
exploratory [░░░░░░░░] ┘ tương ứng
|
|
230
|
+
|
|
231
|
+
+ TRỤC THỨ HAI mà QC không có: platform (web / app / system)
|
|
232
|
+
→ mỗi QC pass của framework khoá đúng MỘT platform, vì SC3 của web
|
|
233
|
+
và SC3 của app là hai scenario khác nhau, ghi vào hai file sổ cái khác nhau.
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
<a id="5"></a>
|
|
239
|
+
## 5 · Ba quyết định
|
|
240
|
+
|
|
241
|
+
> Cả ba đều **không phải lỗi kỹ thuật** — chúng là chỗ hai đội định nghĩa "giao hàng" khác nhau.
|
|
242
|
+
|
|
243
|
+
Kỳ vọng đặt ra cho `/qc-analyze` là hai chế độ:
|
|
244
|
+
|
|
245
|
+
- **Mặc định** — trả GAP theo đúng định dạng mà `/refine-prd` sinh ra, như một lăng kính của QA.
|
|
246
|
+
- **`--standard`** — trả đúng kết quả gốc mà đội automation test đang làm.
|
|
247
|
+
|
|
248
|
+
Ba câu dưới đây là những chỗ hai chế độ đó chạm vào ranh giới đang có của framework.
|
|
249
|
+
|
|
250
|
+
### Hình chung cho câu 1 và câu 2
|
|
251
|
+
|
|
252
|
+
```
|
|
253
|
+
CÁCH ĐỘI QC CẮT CÁCH FRAMEWORK CẮT
|
|
254
|
+
|
|
255
|
+
┌──────────────────────┐ ┌────────────────────┐
|
|
256
|
+
│ qa-tc-analyst │ │ /qc-analyze │
|
|
257
|
+
│ một agent làm cả 3 │ │ ① bóc yêu cầu │──► REQUIREMENT_ANALYSIS.md
|
|
258
|
+
│ ───────────────── │ │ ② tìm gap │──► DOC_GAPS.md
|
|
259
|
+
│ ① bóc yêu cầu │ └─────────┬──────────┘
|
|
260
|
+
│ ② tìm gap tài liệu │ ▼
|
|
261
|
+
│ ③ lập test plan │ ┌────────────────────┐
|
|
262
|
+
└──────────┬───────────┘ │ /qc-plan │
|
|
263
|
+
│ │ ③ lập test plan │──► TEST_PLAN.md
|
|
264
|
+
├──► ✕ requirement analysis └────────────────────┘
|
|
265
|
+
│ — giấy nháp, KHÔNG xuất
|
|
266
|
+
├──► DOC_GAP_<UC>.md
|
|
267
|
+
└──► TEST_PLAN_<UC>.md
|
|
268
|
+
|
|
269
|
+
VÌ SAO CẢ HAI ĐỀU ĐÚNG
|
|
270
|
+
Đội QC làm ba việc trong MỘT agent → file ① là nháp nội bộ, không cần giao cho ai.
|
|
271
|
+
Framework tách thành BA lệnh riêng → file ① phải tồn tại vì trạm 2 và trạm 3 đọc nó.
|
|
272
|
+
→ "đúng gốc" theo nghĩa chữ sẽ làm trạm 2 và trạm 3 MẤT INPUT.
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
### CÂU 1 — `REQUIREMENT_ANALYSIS.md`: giữ hay bỏ?
|
|
278
|
+
|
|
279
|
+
Đội QC nói file này là **giấy nháp**:
|
|
280
|
+
*"phân tích rủi ro / AC / TCOND chỉ là bước trung gian nội bộ, KHÔNG phải deliverable"*.
|
|
281
|
+
Framework coi nó là **hợp đồng giao hàng** — `qc-plan.md:165` và `qc-design-test.md:165`
|
|
282
|
+
đều khai rõ là đọc nó.
|
|
283
|
+
|
|
284
|
+
> **Hỏi lại cho dễ:** khi chạy `--standard`, có chấp nhận nó vẫn âm thầm ghi ra một file nháp
|
|
285
|
+
> mà QC không cần mở — chỉ để trạm 2 và trạm 3 đọc được — hay "đúng gốc" nghĩa là
|
|
286
|
+
> *tuyệt đối* không có file đó?
|
|
287
|
+
|
|
288
|
+
| Chọn | Nghĩa là | Giá phải trả |
|
|
289
|
+
|---|---|---|
|
|
290
|
+
| **Vẫn ghi** *(khuyến nghị)* | Giữ hợp đồng giữa các trạm. Coi nó là handoff nội bộ, không phải deliverable của QC. | Không phải sửa gì downstream. QC chỉ cần không mở nó. |
|
|
291
|
+
| Bỏ, trạm 2/3 đọc DOC_GAP | Trung thành 100% với gốc QC. | Sửa cả `qc-plan` + `qc-design-test`, và dựng lại phần requirement chúng đang cần từ nguồn khác. |
|
|
292
|
+
| Bỏ, thay bằng `findings.yaml` | Trạm 2/3 đọc dữ liệu máy-đọc-được thay vì hai file `.md`. | Refactor ba lệnh cùng lúc. Sạch nhất về lâu dài, đắt nhất bây giờ. |
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
### CÂU 2 — `TEST_PLAN`: của trạm 1 hay trạm 2?
|
|
297
|
+
|
|
298
|
+
Đội QC: analyst xuất **cả** DOC_GAP **và** TEST_PLAN, xong việc phân tích.
|
|
299
|
+
Framework: DOC_GAP ở trạm 1, TEST_PLAN ở trạm 2 (skill `qa-planner/test-plan.md` —
|
|
300
|
+
chính là skill `test-plan` của đội QC, đã bị đẩy sang trạm khác).
|
|
301
|
+
|
|
302
|
+
> **Hỏi lại cho dễ:** muốn đội QC gõ **một lệnh** ra cả hai file (giống họ đang làm),
|
|
303
|
+
> hay **hai lệnh liền nhau** ra hai file (giữ đúng sáu trạm của framework)?
|
|
304
|
+
|
|
305
|
+
| Chọn | Nghĩa là | Giá phải trả |
|
|
306
|
+
|---|---|---|
|
|
307
|
+
| **Thêm `--standard` cho cả `/qc-plan`** *(khuyến nghị)* | QC gõ hai lệnh liền nhau, kết quả giống dây gốc của họ. Ranh giới sáu trạm giữ nguyên. | Đội QC phải học là "phân tích" giờ là hai lệnh. |
|
|
308
|
+
| Trạm 1 xuất luôn cả hai file | Khớp gốc bằng đúng một lệnh. | Trạm 2 thành no-op khi đã `--standard` — phải khai rõ để không ai chạy trùng và ghi đè. |
|
|
309
|
+
| Gộp trạm 1 + trạm 2 | Thừa nhận framework đã tách sai so với thực tế đội QC. | Đụng `docs/explain`, `report-footer`, và mọi chỗ khai "pipeline 6 trạm". |
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
### CÂU 3 — Kết quả GAP đi vào file nào?
|
|
314
|
+
|
|
315
|
+
Framework có một thứ gọi là **Review Board** — bảng mà PO click chuột phải vào file `.yaml`
|
|
316
|
+
để mở, đọc từng finding, bấm accept hoặc reject, rồi lệnh `--resume` *tự sửa PRD* theo
|
|
317
|
+
những cái đã accept.
|
|
318
|
+
|
|
319
|
+
Hiện `/refine-prd` sinh file `{prd-slug}-findings.yaml` với ba lăng kính **DEV · SA · PO**.
|
|
320
|
+
Có một **lăng kính QA thứ tư đã viết sẵn nhưng bị comment lại** — kèm công thức bật lại
|
|
321
|
+
năm bước, ở `commands/refine-prd.tmpl:60-79`. Nội dung nó hỏi:
|
|
322
|
+
*"AC này có nêu được kết quả quan sát / kiểm chứng được không?"* — đúng góc nhìn QC.
|
|
323
|
+
|
|
324
|
+
```
|
|
325
|
+
CÁCH A · FILE RIÊNG, CÙNG ĐỊNH DẠNG
|
|
326
|
+
|
|
327
|
+
/refine-prd ──► {prd-slug}-findings.yaml (DEV, SA, PO) ─┐
|
|
328
|
+
├─► Review Board
|
|
329
|
+
/qc-analyze ──► {UC-ID}-qa-findings.yaml (QA) ─┘ đọc được cả hai —
|
|
330
|
+
vì cùng một schema
|
|
331
|
+
|
|
332
|
+
CÁCH B · CHUNG MỘT FILE
|
|
333
|
+
|
|
334
|
+
/refine-prd ──┐
|
|
335
|
+
├─► {prd-slug}-findings.yaml
|
|
336
|
+
/qc-analyze ──┘ applied_to_version: ???
|
|
337
|
+
│
|
|
338
|
+
▼
|
|
339
|
+
refine-prd MẤT KHẢ NĂNG PHÂN BIỆT
|
|
340
|
+
"PRD đổi là do CHÍNH TÔI áp fix" ↔ "có người lạ sửa"
|
|
341
|
+
→ mỗi lần chạy: quét lại TOÀN BỘ + bắn cảnh báo giả
|
|
342
|
+
|
|
343
|
+
Và lời hứa ở dòng đầu refine-prd — "chỉ áp được fix cho vấn đề
|
|
344
|
+
mà CHÍNH NÓ tìm ra" — bị phá.
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Cùng một schema, khác file — đó là toàn bộ khác biệt.**
|
|
348
|
+
Review Board đọc theo schema, không theo tên file, nên cách A không mất gì về mặt
|
|
349
|
+
trải nghiệm PO. Thêm nữa: `/refine-prd` chạy lúc PRD chưa có BDD, `/qc-analyze` chạy
|
|
350
|
+
sau khi code đã xong — cùng lăng kính, hai thời điểm khác nhau, nên hai file là phản ánh
|
|
351
|
+
đúng thực tế.
|
|
352
|
+
|
|
353
|
+
> **Hỏi lại cho dễ:** muốn QC gap nằm **cùng file** với gap của DEV / SA / PO,
|
|
354
|
+
> hay **file riêng cùng định dạng** để Board vẫn đọc được cả hai?
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
<a id="6"></a>
|
|
359
|
+
## 6 · Món quà đi ngược chiều
|
|
360
|
+
|
|
361
|
+
> Đội QC có một thứ framework đang thiếu, và nên lấy **bất kể ba câu trên trả lời thế nào**.
|
|
362
|
+
|
|
363
|
+
Framework có một engine fan-out dùng chung, `steps/review-fanout.md`: nhận `DIMENSIONS` +
|
|
364
|
+
`FINDINGS SCHEMA`, spawn nhiều sub-agent song song, rồi chạy vòng lặp
|
|
365
|
+
**completeness-critic** tới khi hai vòng liền không sinh thêm gì mới.
|
|
366
|
+
|
|
367
|
+
Vòng critic đó chỉ đẩy *recall* lên — tìm thêm, tìm thêm.
|
|
368
|
+
**Không có gì kéo *precision* lại.**
|
|
369
|
+
|
|
370
|
+
`gap-verifier` của đội QC là đúng nửa còn thiếu đó:
|
|
371
|
+
|
|
372
|
+
> **Nguyên tắc tối thượng:** một gap chỉ hợp lệ khi có ĐỦ 2 vế —
|
|
373
|
+
> **(1)** spec nguồn nêu / ngụ ý hành vi X **VÀ** **(2)** không tài liệu nào trong `inputs/`
|
|
374
|
+
> trả lời X. Thiếu 1 trong 2 → KHÔNG phải gap.
|
|
375
|
+
>
|
|
376
|
+
> ⚠️ Cấm dùng chính cột Evidence trong DOC_GAP làm bằng chứng.
|
|
377
|
+
|
|
378
|
+
```
|
|
379
|
+
steps/review-fanout.md
|
|
380
|
+
|
|
381
|
+
PHASE 1 PHASE 2 PHASE 2.5 · THÊM VÀO PHASE 3
|
|
382
|
+
┌──────────────┐ ┌────────────────────┐ ┌─────────────────────────┐ ┌──────────────┐
|
|
383
|
+
│ fan-out │──►│ completeness-critic│──►│ gap-verifier T1–T6 │─►│ dedup + ghi │
|
|
384
|
+
│ song song │ └────────────────────┘ │ mở lại nguồn, │ │ file │
|
|
385
|
+
└──────────────┘ ↑ đẩy RECALL │ không tin Evidence │ └──────────────┘
|
|
386
|
+
└─────────────────────────┘
|
|
387
|
+
↑ kéo PRECISION
|
|
388
|
+
|
|
389
|
+
AI ĐƯỢC HƯỞNG: /refine-prd · /review-context · /qc-analyze
|
|
390
|
+
— port một lần, ba lệnh dùng chung
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
**Sáu phép kiểm T1–T6 giết gap ảo:** evidence bịa hoặc trích sai · yêu cầu tự bịa ·
|
|
394
|
+
gap spec đã trả lời ở chỗ khác · hai rule khác UC/pha bị coi là mâu thuẫn ·
|
|
395
|
+
rule đã có ở tài liệu gốc mà đòi con lặp lại · gap thực ra là "làm kỹ test" chứ không
|
|
396
|
+
phải spec thiếu.
|
|
397
|
+
|
|
398
|
+
Cùng họ với `T13` ("không kiểm được ≠ sạch") và `R15` ("nhắc tên ≠ dùng") đã có trong framework.
|
|
399
|
+
|
|
400
|
+
**Tin tốt về chi phí port:** chỉ khoảng **41 dòng** hardcode `inputs/…` trên toàn bộ
|
|
401
|
+
23 skill file — tám file chỉ một dòng; nặng nhất là `full-lane-dispatcher` (13) và
|
|
402
|
+
`exhaustive-gap-scanner` (11). Không phải viết lại 3.980 dòng.
|
|
403
|
+
|
|
404
|
+
---
|
|
405
|
+
|
|
406
|
+
<a id="7"></a>
|
|
407
|
+
## 7 · Plan năm phase
|
|
408
|
+
|
|
409
|
+
> **P0 và P1 không phụ thuộc ba quyết định — làm được ngay.**
|
|
410
|
+
|
|
411
|
+
### P0 · Chuẩn hoá nền — chưa đụng `qc-analyze`
|
|
412
|
+
|
|
413
|
+
- Port `gap-verifier` T1–T6 → `steps/gap-verify.md`, cắm vào `review-fanout.md` làm
|
|
414
|
+
**Phase 2.5** (opt-in qua tham số `VERIFY=on`).
|
|
415
|
+
- Port 6 anti-pattern + 3 câu hỏi lọc bắt buộc vào cùng file đó.
|
|
416
|
+
- Sửa hai lỗi đã tìm ra: hoán vị **Guard ↔ Platform Resolution**; khai `*Checkpoint:*`
|
|
417
|
+
và sửa `docs/explain/15-qc-analyze.md` (đang ghi "không gate chặn" trong khi hành vi
|
|
418
|
+
thật là chặn thường).
|
|
419
|
+
|
|
420
|
+
### P1 · Đưa 18 skill còn thiếu vào
|
|
421
|
+
|
|
422
|
+
- Copy 23 file → `core/skills/qc/qa-analyst/`, thay ~41 dòng `inputs/…` →
|
|
423
|
+
`{paths.specs_dir}` / `{spec_source}`.
|
|
424
|
+
- Thêm frontmatter provenance (`upstream_sha` + `upstream_path`) để **phát hiện drift**.
|
|
425
|
+
Repo QC vẫn là chủ, `qc_skills_dir` vẫn override được — nhưng không có mốc này thì
|
|
426
|
+
hai bản trôi xa nhau trong im lặng, đúng bệnh mà `R10` tồn tại để chặn.
|
|
427
|
+
- Bổ sung bước **verify `qc_skills_dir` tồn tại + đủ file** vào `context-loader`.
|
|
428
|
+
Hiện nó chỉ đọc giá trị và gán default, không kiểm — trỏ sai thì lệnh vẫn chạy,
|
|
429
|
+
vẫn ghi file, phân tích rỗng logic, và *không cờ nào bật*.
|
|
430
|
+
|
|
431
|
+
### P2 · Chế độ mặc định = lăng kính QA — *phụ thuộc câu 1 và câu 3*
|
|
432
|
+
|
|
433
|
+
- `/qc-analyze` gọi `review-fanout` với `DIMENSIONS` = 5 chiều của `exhaustive-gap-scanner`
|
|
434
|
+
(Business Rules · Error Handling · UI/UX · Test Data & Config · Cross-Document),
|
|
435
|
+
`GRANULARITY=per-uc`, `VERIFY=on`.
|
|
436
|
+
- Ghi `{refinement_dir}/{UC-ID}-qa-findings.yaml`, `lens: "QA"`.
|
|
437
|
+
- `DOC_GAPS.md` trở thành **bản render người-đọc** của cùng dữ liệu đó — một nguồn, hai mặt.
|
|
438
|
+
|
|
439
|
+
### P3 · Chế độ `--standard` — *phụ thuộc câu 1 và câu 2*
|
|
440
|
+
|
|
441
|
+
- Bypass `review-fanout`, chạy nguyên dây QC: `full-lane-dispatcher` → lane UI/API/E2E →
|
|
442
|
+
`exhaustive-gap-scanner` → `gap-verifier`.
|
|
443
|
+
- Ghi `DOC_GAP_{UC-ID}.md` 10 cột theo template gốc, thêm `DOC_GAPS.standard.template.md`.
|
|
444
|
+
|
|
445
|
+
### P4 · Canh bằng máy
|
|
446
|
+
|
|
447
|
+
- Rule `self-check` mới: hai template GAP phải khớp số cột đã khai trong `trace-schema.json`;
|
|
448
|
+
enum `lens` phải đồng bộ giữa `refine-prd` và `qc-analyze`; provenance sha của skill port
|
|
449
|
+
không được thiếu.
|
|
450
|
+
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
<a id="8"></a>
|
|
454
|
+
## 8 · Bốn lỗi tìm được khi đọc `/qc-analyze`
|
|
455
|
+
|
|
456
|
+
> Độc lập với việc sáp nhập — đúng sai vẫn nên sửa.
|
|
457
|
+
|
|
458
|
+
### (a) 🔴 Doc nói ngược với hành vi thật
|
|
459
|
+
|
|
460
|
+
`/qc-analyze` không có dòng `*Checkpoint:*` và không nằm trong
|
|
461
|
+
`trace-schema.json → gate.checkpoint_levels.none` → theo luật gate là **chặn thường**.
|
|
462
|
+
Nhưng `docs/explain/15-qc-analyze.md` ghi *"Không gate chặn"*.
|
|
463
|
+
|
|
464
|
+
Cái sai là explain doc — lệnh này ghi file nên chặn thường là đúng.
|
|
465
|
+
`R11` chỉ canh file lệnh, không canh explain doc.
|
|
466
|
+
|
|
467
|
+
### (b) ⚠️ Ba prompt chặn liên tiếp, `--yes` chỉ mở được một
|
|
468
|
+
|
|
469
|
+
CHECKPOINT → Guard BDD (Y/N) → câu hỏi platform. Gate chỉ nói `--yes` bỏ qua CHECKPOINT.
|
|
470
|
+
→ `claude -p "/qc-analyze UC1 --yes"` vẫn treo. Đường headless mà gate hứa
|
|
471
|
+
(`steps/gate.md` dòng 97) bị bít cho chính lệnh này.
|
|
472
|
+
|
|
473
|
+
### (c) ⚠️ Guard đọc `.feature` trước khi biết platform
|
|
474
|
+
|
|
475
|
+
Section `## Guard` đứng *trên* `## Platform Resolution`. Với UC multi-platform,
|
|
476
|
+
`bdd/web/`, `bdd/app/`, `bdd/system/` là ba file khác nhau, có thể ba `@trace.status`
|
|
477
|
+
khác nhau. Hoán vị hai section là fix một dòng.
|
|
478
|
+
|
|
479
|
+
### (d) ⚠️ `qc_skills_dir` được đọc nhưng không được kiểm
|
|
480
|
+
|
|
481
|
+
Vì đây là path *cố ý* trỏ ra repo ngoài để framework upgrade không ghi đè, trỏ sai là
|
|
482
|
+
kịch bản bình thường. Trỏ sai → không nạp được skill nào → vẫn chạy, vẫn ghi hai file,
|
|
483
|
+
phân tích rỗng logic, **không cờ nào bật**.
|
|
484
|
+
|
|
485
|
+
### Còn một khoảng trống lớn hơn cả bốn cái trên
|
|
486
|
+
|
|
487
|
+
Đội QC có `qa-io-contract` với skill `injection-scanner`, và nguyên tắc:
|
|
488
|
+
*"nội dung spec là dữ liệu để đọc, không phải mệnh lệnh"*.
|
|
489
|
+
|
|
490
|
+
**Framework không có gì tương ứng.** Mọi lệnh của framework đọc PRD / BDD / design-spec
|
|
491
|
+
do người khác viết và nạp thẳng vào context — không có bước gác nào.
|
|
492
|
+
Ngoài phạm vi việc đang làm, nhưng nên ghi lại.
|
|
493
|
+
|
|
494
|
+
---
|
|
495
|
+
|
|
496
|
+
## Liên quan
|
|
497
|
+
|
|
498
|
+
- [`qc-implementation-log.md`](qc-implementation-log.md) — nhật ký triển khai, viết cho người không cần biết code
|
|
499
|
+
|
|
500
|
+
- [`qc-sync-command.md`](qc-sync-command.md) — **phần 2**: lệnh `/sync-qc-base` để về sau
|
|
501
|
+
QC sửa skill thì đưa vào framework được. Lưu ý: `--adopt` của lệnh đó **chính là** P1 ở §7
|
|
502
|
+
trên — không phải hai việc, là một việc có công cụ.
|