@educa-corp/sdd-framework 0.6.0 → 0.7.1

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 (223) hide show
  1. package/bin/gate-trace.js +25 -2
  2. package/bin/index.js +32 -5
  3. package/bin/lint-trace.js +41 -0
  4. package/bin/self-check.js +430 -3
  5. package/bin/trace-schema.json +418 -31
  6. package/core/FRAMEWORK_VERSION +1 -1
  7. package/{commands/extend-prd.md → core/commands/amend-prd.md} +206 -173
  8. package/core/commands/dev-run-test.md +48 -10
  9. package/core/commands/extend-prd.md +39 -12
  10. package/core/commands/generate-bdd.md +52 -10
  11. package/core/commands/generate-code.md +35 -2
  12. package/core/commands/generate-tech-docs.md +36 -4
  13. package/core/commands/map-testids.md +1 -1
  14. package/core/commands/qc-run-test.md +29 -3
  15. package/core/commands/refine-prd.md +13 -2
  16. package/core/commands/review-context.md +43 -8
  17. package/core/commands/sync.md +105 -1
  18. package/core/commands/validate-traces.md +289 -16
  19. package/core/rules/workflow.md +34 -0
  20. package/core/steps/context-loader.md +27 -6
  21. package/core/templates/feature.template +1 -1
  22. package/core/templates/project-context.yaml +3 -3
  23. package/core/templates/tech-design.template.md +2 -2
  24. package/docs/02-concepts/architecture.md +37 -1
  25. package/docs/02-concepts/overview.md +1 -1
  26. package/docs/02-concepts/pipeline-steps/02-specification.md +13 -7
  27. package/docs/02-concepts/pipeline-steps/07-dev-selftest.md +2 -0
  28. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +1 -0
  29. package/docs/02-concepts/pipeline-steps/09-validate-traces.md +34 -3
  30. package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +10 -1
  31. package/docs/02-concepts/traceability.md +187 -183
  32. package/docs/03-guides/architect.md +13 -4
  33. package/docs/03-guides/developer.md +1 -0
  34. package/docs/03-guides/product-owner.md +89 -72
  35. package/docs/03-guides/tester-qa.md +81 -81
  36. package/docs/04-reference/commands.md +148 -134
  37. package/docs/04-reference/trace-schema.md +45 -1
  38. package/docs/explain/02b-extend-prd.md +1 -1
  39. package/docs/explain/02c-amend-prd.md +152 -0
  40. package/docs/explain/06-generate-bdd.md +1 -1
  41. package/docs/explain/13-dev-run-test.md +15 -1
  42. package/docs/explain/19-qc-run-test.md +91 -87
  43. package/docs/explain/21-validate-traces.md +79 -75
  44. package/docs/explain/28-sync.md +25 -0
  45. package/docs/explain/README.md +136 -135
  46. package/package.json +1 -8
  47. package/commands/debug.md +0 -529
  48. package/commands/debug.tmpl +0 -260
  49. package/commands/define-product.md +0 -438
  50. package/commands/define-product.tmpl +0 -225
  51. package/commands/dev-gen-test.md +0 -700
  52. package/commands/dev-gen-test.tmpl +0 -490
  53. package/commands/dev-run-test.md +0 -435
  54. package/commands/dev-run-test.tmpl +0 -225
  55. package/commands/dev-smoke-test.md +0 -374
  56. package/commands/dev-smoke-test.tmpl +0 -217
  57. package/commands/extend-prd.tmpl +0 -273
  58. package/commands/fix-bug.md +0 -519
  59. package/commands/fix-bug.tmpl +0 -197
  60. package/commands/generate-architecture.md +0 -354
  61. package/commands/generate-architecture.tmpl +0 -197
  62. package/commands/generate-bdd.md +0 -923
  63. package/commands/generate-bdd.tmpl +0 -590
  64. package/commands/generate-code.md +0 -859
  65. package/commands/generate-code.tmpl +0 -649
  66. package/commands/generate-design-spec.md +0 -737
  67. package/commands/generate-design-spec.tmpl +0 -524
  68. package/commands/generate-prd.md +0 -722
  69. package/commands/generate-prd.tmpl +0 -226
  70. package/commands/generate-spec-manifest.md +0 -321
  71. package/commands/generate-spec-manifest.tmpl +0 -164
  72. package/commands/generate-tech-docs.md +0 -920
  73. package/commands/generate-tech-docs.tmpl +0 -273
  74. package/commands/learn.md +0 -399
  75. package/commands/learn.tmpl +0 -130
  76. package/commands/map-testids.md +0 -238
  77. package/commands/map-testids.tmpl +0 -81
  78. package/commands/propose-scenario.md +0 -359
  79. package/commands/propose-scenario.tmpl +0 -202
  80. package/commands/qc-analyze.md +0 -269
  81. package/commands/qc-analyze.tmpl +0 -112
  82. package/commands/qc-design-test.md +0 -226
  83. package/commands/qc-design-test.tmpl +0 -69
  84. package/commands/qc-plan.md +0 -206
  85. package/commands/qc-plan.tmpl +0 -49
  86. package/commands/qc-report.md +0 -217
  87. package/commands/qc-report.tmpl +0 -60
  88. package/commands/qc-review.md +0 -210
  89. package/commands/qc-review.tmpl +0 -53
  90. package/commands/qc-run-test.md +0 -326
  91. package/commands/qc-run-test.tmpl +0 -116
  92. package/commands/refine-prd.md +0 -653
  93. package/commands/refine-prd.tmpl +0 -281
  94. package/commands/report-bug.md +0 -305
  95. package/commands/report-bug.tmpl +0 -148
  96. package/commands/review-code.md +0 -415
  97. package/commands/review-code.tmpl +0 -146
  98. package/commands/review-context.md +0 -902
  99. package/commands/review-context.tmpl +0 -530
  100. package/commands/review-tech-docs.md +0 -561
  101. package/commands/review-tech-docs.tmpl +0 -404
  102. package/commands/setup-ai-first.md +0 -602
  103. package/commands/setup-ai-first.tmpl +0 -450
  104. package/commands/sync.md +0 -430
  105. package/commands/sync.tmpl +0 -429
  106. package/commands/update-framework.md +0 -203
  107. package/commands/update-framework.tmpl +0 -202
  108. package/commands/validate-traces.md +0 -1077
  109. package/commands/validate-traces.tmpl +0 -920
  110. package/hooks/data-guard.js +0 -232
  111. package/hooks/settings.json +0 -19
  112. package/modules/android-compose/module.yaml +0 -13
  113. package/modules/android-compose/stack-profile.yaml +0 -57
  114. package/modules/angular/architecture-snippets/component-patterns.md +0 -187
  115. package/modules/angular/module.yaml +0 -6
  116. package/modules/angular/stack-profile.yaml +0 -38
  117. package/modules/context-engineering/architecture-snippets/context-design.md +0 -119
  118. package/modules/context-engineering/module.yaml +0 -9
  119. package/modules/context-engineering/stack-profile.yaml +0 -61
  120. package/modules/dotnet/architecture-snippets/clean-arch.md +0 -160
  121. package/modules/dotnet/module.yaml +0 -6
  122. package/modules/dotnet/stack-profile.yaml +0 -50
  123. package/modules/flutter/module.yaml +0 -14
  124. package/modules/flutter/stack-profile.yaml +0 -59
  125. package/modules/golang/architecture-snippets/domain-layout.md +0 -283
  126. package/modules/golang/module.yaml +0 -6
  127. package/modules/golang/stack-profile.yaml +0 -40
  128. package/modules/ios-swiftui/module.yaml +0 -13
  129. package/modules/ios-swiftui/stack-profile.yaml +0 -55
  130. package/modules/java-spring/architecture-snippets/layered-arch.md +0 -201
  131. package/modules/java-spring/module.yaml +0 -15
  132. package/modules/java-spring/stack-profile.yaml +0 -28
  133. package/modules/nextjs/architecture-snippets/app-router-patterns.md +0 -269
  134. package/modules/nextjs/module.yaml +0 -14
  135. package/modules/nextjs/stack-profile.yaml +0 -74
  136. package/modules/nuxt/module.yaml +0 -14
  137. package/modules/nuxt/stack-profile.yaml +0 -58
  138. package/modules/phaser-game/architecture-snippets/phaser-scene-patterns.md +0 -646
  139. package/modules/phaser-game/module.yaml +0 -15
  140. package/modules/phaser-game/stack-profile.yaml +0 -90
  141. package/modules/php-laravel/architecture-snippets/service-repository.md +0 -302
  142. package/modules/php-laravel/module.yaml +0 -15
  143. package/modules/php-laravel/stack-profile.yaml +0 -56
  144. package/modules/qc-playwright/stack-profile.yaml +0 -66
  145. package/modules/react/architecture-snippets/hooks-query-patterns.md +0 -254
  146. package/modules/react/module.yaml +0 -14
  147. package/modules/react/stack-profile.yaml +0 -63
  148. package/modules/react-native/module.yaml +0 -14
  149. package/modules/react-native/stack-profile.yaml +0 -56
  150. package/modules/vue/module.yaml +0 -14
  151. package/modules/vue/stack-profile.yaml +0 -65
  152. package/rules/data-protection.md +0 -80
  153. package/rules/workflow.md +0 -99
  154. package/skills/code/SKILL.md +0 -19
  155. package/skills/code/SKILL.tmpl +0 -19
  156. package/skills/debug/SKILL.md +0 -19
  157. package/skills/debug/SKILL.tmpl +0 -19
  158. package/skills/design-spec/SKILL.md +0 -11
  159. package/skills/design-spec/SKILL.tmpl +0 -11
  160. package/skills/discovery/SKILL.md +0 -14
  161. package/skills/discovery/SKILL.tmpl +0 -14
  162. package/skills/prd/SKILL.md +0 -19
  163. package/skills/prd/SKILL.tmpl +0 -19
  164. package/skills/qc/qa-analyst/DOC_GAPS.template.md +0 -63
  165. package/skills/qc/qa-analyst/acceptance-criteria.md +0 -60
  166. package/skills/qc/qa-analyst/business-rules.md +0 -59
  167. package/skills/qc/qa-analyst/data-flow.md +0 -64
  168. package/skills/qc/qa-analyst/spec-breakdown.md +0 -61
  169. package/skills/qc/qa-designer/e2e/journey.md +0 -41
  170. package/skills/qc/qa-designer/exploratory/charter.md +0 -68
  171. package/skills/qc/qa-designer/exploratory/explore-to-functional.md +0 -43
  172. package/skills/qc/qa-designer/functional/api.md +0 -45
  173. package/skills/qc/qa-designer/functional/gui-feature.md +0 -46
  174. package/skills/qc/qa-designer/functional/gui-screen.md +0 -52
  175. package/skills/qc/qa-designer/integration/api.md +0 -42
  176. package/skills/qc/qa-designer/integration/db.md +0 -39
  177. package/skills/qc/qa-designer/integration/gui.md +0 -40
  178. package/skills/qc/qa-designer/integration/kafka.md +0 -40
  179. package/skills/qc/qa-designer/non-functional.md +0 -40
  180. package/skills/qc/qa-planner/test-plan.md +0 -120
  181. package/skills/qc/qa-reviewer/script/e2e.md +0 -87
  182. package/skills/qc/qa-reviewer/script/exploratory.md +0 -45
  183. package/skills/qc/qa-reviewer/script/functional.md +0 -101
  184. package/skills/qc/qa-reviewer/script/integration.md +0 -91
  185. package/skills/qc/qa-reviewer/script/non-functional.md +0 -126
  186. package/skills/qc/qa-reviewer/test-case/e2e.md +0 -73
  187. package/skills/qc/qa-reviewer/test-case/exploratory.md +0 -43
  188. package/skills/qc/qa-reviewer/test-case/functional.md +0 -76
  189. package/skills/qc/qa-reviewer/test-case/integration.md +0 -69
  190. package/skills/qc/qa-reviewer/test-case/non-functional.md +0 -73
  191. package/skills/qc/qa-runner/e2e.md +0 -49
  192. package/skills/qc/qa-runner/exploratory/session.md +0 -36
  193. package/skills/qc/qa-runner/functional/api.md +0 -35
  194. package/skills/qc/qa-runner/functional/gui-feature.md +0 -51
  195. package/skills/qc/qa-runner/functional/gui-screen.md +0 -55
  196. package/skills/qc/qa-runner/integration.md +0 -47
  197. package/skills/qc/qa-runner/non-functional.md +0 -49
  198. package/skills/qc/qa-runner/report/report.md +0 -37
  199. package/skills/setup-ai-first/SKILL.md +0 -19
  200. package/skills/setup-ai-first/SKILL.tmpl +0 -19
  201. package/skills/spec/SKILL.md +0 -19
  202. package/skills/spec/SKILL.tmpl +0 -19
  203. package/skills/test/SKILL.md +0 -18
  204. package/skills/test/SKILL.tmpl +0 -18
  205. package/steps/business-language.md +0 -56
  206. package/steps/capture-lesson.md +0 -112
  207. package/steps/context-loader.md +0 -406
  208. package/steps/gate.md +0 -151
  209. package/steps/report-footer.md +0 -125
  210. package/steps/review-fanout.md +0 -159
  211. package/steps/spawn-agent.md +0 -129
  212. package/steps/trace-mirror.md +0 -53
  213. package/templates/README.md +0 -70
  214. package/templates/architecture.template.md +0 -394
  215. package/templates/ci/trace-gate.yml +0 -146
  216. package/templates/design-spec.template.md +0 -217
  217. package/templates/feature.template +0 -123
  218. package/templates/hooks/pre-push +0 -61
  219. package/templates/platform-guide.template.md +0 -145
  220. package/templates/prd.template.md +0 -283
  221. package/templates/product-definition.template.md +0 -188
  222. package/templates/project-context.yaml +0 -212
  223. package/templates/tech-design.template.md +0 -490
@@ -0,0 +1,152 @@
1
+ [← /extend-prd](02b-extend-prd.md) · [Explain Home](README.md) · [Next: /refine-prd →](03-refine-prd.md)
2
+
3
+ # 02c · `/amend-prd` — Đổi một yêu cầu đã duyệt
4
+
5
+ > **Một câu.** Sửa **tại chỗ** nội dung của một AC/BR/UC đã duyệt — PO khai tường minh ID, lệnh kiểm va chạm, ghi bằng `Edit` với guard **hai chiều**, bump version + `Status → draft`, và nói rõ UC nào phải làm lại.
6
+
7
+ ---
8
+
9
+ ## Vấn đề giải quyết
10
+
11
+ Có bốn tình huống PO chạm PRD. Trước GAPS-v4 G54 chỉ ba cái có lệnh.
12
+
13
+ | Tình huống | Lệnh | Có từ |
14
+ |---|---|---|
15
+ | PRD chưa có | `/generate-prd` | đầu |
16
+ | **Thêm** UC/AC/BR mới | `/extend-prd` | v0.4.3 |
17
+ | Sửa vấn đề **review chỉ ra** | `/refine-prd` | đầu |
18
+ | **Đổi một yêu cầu đang đúng cú pháp** | *(không có)* | — |
19
+
20
+ Nhánh thứ tư là *"BR8 nói tối đa 5 file, giờ đổi thành 20"*. Không thêm gì mới. Không phải AI phát hiện lỗi. Chỉ đổi một con số đã duyệt.
21
+
22
+ Ba lệnh có sẵn đều **từ chối đúng việc đó**:
23
+
24
+ | Lệnh | Vì sao không dùng được |
25
+ |---|---|
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
+ | `/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
+ | `/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
+
30
+ Nên hành vi hợp lý duy nhất còn lại là **mở file `.md` ra gõ**.
31
+
32
+ ---
33
+
34
+ ## Vì sao nhánh thiếu này nặng hơn nó trông (GAPS-v4 G54)
35
+
36
+ Toàn bộ lưới an toàn của framework so **nhãn version**, không so **nội dung**. Kiểm bằng máy: `grep "content_hash|checksum|sha256|md5"` trên toàn bộ lệnh + schema + step → **0 kết quả**.
37
+
38
+ Nên khi PO sửa tay: bìa không đổi, trang changelog trắng, con dấu `approved` vẫn còn.
39
+
40
+ | Tầng canh | Nó hỏi gì | Trả lời | Kết quả |
41
+ |---|---|---|---|
42
+ | `/validate-traces` Step 4 | *"PRD Version == cột `prd_version`?"* | `1.3 == 1.3` | ✅ sạch |
43
+ | `gate-trace` G2 | *"report khớp sổ TSV?"* | khớp | ✅ PASS |
44
+ | `require-fresh-audit` | *"PR có chạm file mang tag trace?"* | PO chỉ sửa `.md` | ✅ không đòi audit |
45
+
46
+ **Ba tầng xanh, và ba tầng đều đúng theo định nghĩa của chính chúng.** Code vẫn chặn ở 5, test vẫn assert 5 và vẫn PASS — nó đang test đúng code, chỉ là code sai spec. Dashboard hiện `OK · ✅ 10 tests · qc pass`.
47
+
48
+ So với hai họ hàng gần:
49
+
50
+ | | Hỏng kiểu gì | Có gì để phát hiện? |
51
+ |---|---|---|
52
+ | G52 | **Ồn** — báo oan cho mọi UC | Có, quá nhiều |
53
+ | G53 | **Im lặng** — nhưng version vẫn lệch, vẫn còn cờ ⓘ để người tinh ý thấy | Có, một chút |
54
+ | **G54** | **Im lặng tuyệt đối** | **Không có gì** |
55
+
56
+ Và đây là nhánh **dùng nhiều nhất**: trên một sản phẩm đang sống, *"đổi một yêu cầu đã có"* xảy ra thường hơn *"thêm một UC hoàn toàn mới"* rất nhiều.
57
+
58
+ ---
59
+
60
+ ## Vị trí & tiền đề
61
+
62
+ - **Vị trí:** Phase Specification — nhánh *"PRD đã tồn tại, đổi nội dung"*.
63
+ - **Tiền đề:** có PRD. Không có → `/generate-prd`.
64
+ - **Mức chặn:** **CỨNG** (`--yes` không bỏ qua được). Đây là thao tác ghi **duy nhất** trong framework được phép làm output **không phải superset** của bản cũ.
65
+
66
+ ---
67
+
68
+ ## Nó làm gì, theo thứ tự
69
+
70
+ | Bước | Làm gì | Điểm đáng chú ý |
71
+ |---|---|---|
72
+ | **1** | Nạp PRD + **PO khai tường minh `amend_targets`** | Trình danh sách UC/BR/AC để PO chọn. **Không tự suy** target từ mô tả mơ hồ — đoán sai là sửa sai một yêu cầu đã duyệt. Mỗi ID được phân giải về **UC sở hữu** ngay |
73
+ | **2** | **Kiểm va chạm** (3 câu) | Tái dùng `/extend-prd` Bước 3.2, **đảo hướng**: ở đó là *"phần THÊM có làm cái cũ sai không"*, ở đây là *"cái SỬA có làm phần còn lại sai không"*. Mỗi "có" **mở rộng `amend_targets`** |
74
+ | **3** | Altitude | Cơ chế xuống BR/BL, AC chỉ giữ outcome + ref. Đây là chỗ dễ trôi nhất khi sửa tại chỗ |
75
+ | **4** | Ghi + **guard sau-ghi HAI CHIỀU** | Xem dưới — đây là phần cốt lõi |
76
+ | **5** | Bump version + `{changelog_scope}` + `Status → draft` | Đổi một giới hạn nghiệp vụ là **major**: code hiện tại đang sai so với spec mới |
77
+ | **6** | Report | Nêu **UC PHẢI làm lại** kèm lệnh, và **cấm tường minh** `--realign` cho chúng |
78
+
79
+ ### Guard hai chiều — chỗ lệnh này khác mọi thao tác ghi khác
80
+
81
+ `/extend-prd` guard bằng *"output là **superset chặt**"*. Ở đây output **cố ý không** phải superset, nên guard phải đảo:
82
+
83
+ | Chiều | Kiểm gì | Fail nghĩa là |
84
+ |---|---|---|
85
+ | **Bảo toàn** | Mọi UC/BR/AC-ID + row changelog cũ **vẫn còn** | Đã xoá thứ không được xoá |
86
+ | **Giới hạn** | **Mọi** nội dung đã đổi đều thuộc một ID trong `amend_targets` | Đã sửa **lan ra ngoài** phạm vi PO chốt |
87
+
88
+ Chiều **Giới hạn** quan trọng bằng chiều Bảo toàn, và vì một lý do cụ thể: `{changelog_scope}` dựng từ `amend_targets`. Nếu bản ghi lỡ sửa một UC không có trong danh sách đó thì changelog **không nêu** UC ấy ⇒ `/validate-traces` xếp nó vào ⓘ `PRD_STALE_REF` ⇒ `--realign-prd-version` **mở cửa** và dán nhãn version lại lên một thay đổi chưa ai implement. Đúng hình dạng **G53**, chỉ đến từ một hướng khác.
89
+
90
+ Fail chiều nào → **khôi phục file**, dừng, **không** sang Bước 5.
91
+
92
+ ---
93
+
94
+ ## Hai chế độ, và những gì nó **không** làm
95
+
96
+ | Chế độ | Cờ | Làm gì |
97
+ |---|---|---|
98
+ | Sửa nội dung | *(mặc định)* | Đổi nội dung của ID đã có. ID giữ nguyên |
99
+ | **Khai tử tại chỗ** | `--retire {ID}` | Đánh dấu ID không còn hiệu lực **nhưng GIỮ NGUYÊN row + ID** |
100
+
101
+ `--retire` tồn tại vì **xoá hẳn một BR là bẫy**: `@trace.business_rules` trong mọi `.feature` đã sinh đang trỏ vào ID đó, nên xoá row biến một liên kết hợp lệ thành `TRACE_ORPHAN` 🔴. Khai tử tại chỗ đạt cùng mục đích nghiệp vụ mà không phá liên kết.
102
+
103
+ | PO muốn | Lệnh đúng |
104
+ |---|---|
105
+ | Thêm UC/AC/BR mới | `/extend-prd` — lệnh này **không đánh số mới** bao giờ |
106
+ | **Xoá hẳn** một dòng | *(không có, có chủ ý)* → `--retire` |
107
+ | Sửa lỗi `/refine-prd` vừa chỉ ra | `/refine-prd --resume` |
108
+
109
+ ---
110
+
111
+ ## Cửa sau vẫn có chuông
112
+
113
+ Mở cửa chính không có nghĩa không ai đi cửa sau. `/validate-traces` **Step 3.9** canh:
114
+
115
+ - Đọc mốc `spec_baseline` (`prd_path` · `sha_at_audit` · `version_at_audit`) mà lần audit trước đã ghi.
116
+ - So bằng **hai nguồn**: `git diff` (sửa đã commit) **và** `git status` (sửa **chưa** commit — ca thường gặp nhất, vì PO đang gõ).
117
+ - Nội dung đổi **mà** `Version` không đổi → 🔴 **`PRD_UNTRACKED_EDIT`**.
118
+
119
+ **Không báo oan:** sửa **và** bump version ⇒ `version != version_at_audit` ⇒ im.
120
+
121
+ **Đường ra tự lành, cố ý không có `--accept-edit`:** cách sửa đúng là bump version + ghi row changelog nêu UC — tức đúng việc lệnh này làm hộ. Làm xong thì cờ tự tắt và logic `PRD_DRIFT` bình thường tiếp quản. Thêm một cờ escape sẽ là thêm 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.
122
+
123
+ **Vì sao cờ này không chặn PR:** `gate.blocking` nghĩa hẹp là *code đang hỏng*; cờ này nói về *spec*. 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ờ 🔴 thật. Đó đúng là thất bại mà `self-check` R9(e) được viết ra để chặn, chỉ đến bằng một cửa khác. Team đã dọn sạch nợ tồn thì tự thêm counter vào `gate.blocking`; R13(e) canh việc đó có kèm `why`.
124
+
125
+ ---
126
+
127
+ ## Sau đó chạy gì
128
+
129
+ ```
130
+ /amend-prd {prd-file} UC3-BR8
131
+ → /review-context {prd-file} ← kiểm chất lượng phần vừa sửa
132
+ → PO đặt Status: approved
133
+ → /generate-bdd {prd-file} ← CHỈ cho UC bị sửa
134
+ → /generate-code {UC-ID}
135
+ → /dev-gen-test → /dev-run-test ← test cũ đang assert giá trị cũ và vẫn PASS
136
+ ```
137
+
138
+ ❌ **Không** `--realign-prd-version` cho UC bị sửa — nội dung đổi thật.
139
+
140
+ ---
141
+
142
+ ## Máy canh gì
143
+
144
+ | Rule | Canh gì |
145
+ |---|---|
146
+ | `self-check` **R11** | Lệnh phải tự khai mức `chặn CỨNG`, khớp `gate.checkpoint_levels.hard` |
147
+ | `self-check` **R12** | Là producer dòng changelog → phải có **dòng template** mang `{changelog_scope}` |
148
+ | `self-check` **R13** | `spec_baseline` phải được **GHI** như một key trong `trace-report.json` — nếu không, cờ `PRD_UNTRACKED_EDIT` không bao giờ bật mà R6/R7 vẫn ✅ |
149
+
150
+ ---
151
+
152
+ [← /extend-prd](02b-extend-prd.md) · [Explain Home](README.md) · [Next: /refine-prd →](03-refine-prd.md)
@@ -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).
@@ -1,75 +1,79 @@
1
- [← /qc-report](20-qc-report.md) · [Explain Home](README.md) · [Next: /generate-spec-manifest →](22-generate-spec-manifest.md)
2
-
3
- # 21 · `/validate-traces` — Ma trận độ phủ spec ↔ code ↔ test
4
-
5
- > **Một câu.** Check **read-only** độ phủ giữa spec, code, test (gồm PRD version drift); phân loại mỗi SC và làm mới Living Docs. Không sửa gì.
6
-
7
- ---
8
-
9
- ## Vấn đề giải quyết
10
-
11
- Traceability chỉ có giá trị khi kiểm được. Command cho bức tranh toàn cục: SC nào có code, có test, hay còn hở — để không "tưởng xong mà chưa xong".
12
-
13
- ---
14
-
15
- ## Vị trí & tiền đề
16
-
17
- - **Vị trí:** Phase Trace Audit (xuyên suốt).
18
- - **Đặc biệt:** read-only — an toàn chạy bất kỳ lúc nào.
19
-
20
- ---
21
-
22
- ## Input / Output
23
-
24
- **Input:** `.trace/…/{UC-ID}-{platform}.tsv` + spec + code + test.
25
-
26
- **Output:** ma trận coverage + `code_coverage`; `trace-report.json` (ghi đè) + **`trace-history.jsonl`** (append 1 dòng delta — **phải commit**, không regenerate được); làm mới Living Docs dashboard.
27
-
28
- **Flag:** `--realign-prd-version {UC-ID}` · `--realign-techdoc-revision {UC-ID}` ngoại lệ kiểm soát của "read-only": chỉ sửa **dòng `@trace.*`** trong code, **từ chối chạy** nếu UC đang `DRIFT`/`ORPHANED`.
29
-
30
- ---
31
-
32
- ## Các bước xử (chi tiết)
33
-
34
- 1. Quét trace `.tsv` + đối chiếu spec/code/test.
35
- 2. Phân loại mỗi SC theo **thứ tự ưu tiên** (rule sớm thắng):
36
- | # | Trạng thái | Điều kiện |
37
- |---|-----------|-----------|
38
- | 1 | UNTRACKED | `gen_ver == —` |
39
- | 2 | DRIFT | code + `spec_ver != gen_ver` |
40
- | 3 | GAP | code + `test_count == —/0` |
41
- | 4 | OK | version khớp + có code + có test |
42
- 3. Dựng dashboard: `dev_selftest` (DEV smoke) **và** `qc_status` (QC chính thức) hiển thị cạnh nhau — **không merge**; cột `qc_owner` + `qc_blocked_by` ("Waiting on"); và **hai trục chia nhóm**: **`by_service`** (coverage theo từng đội — cột `service`) · **`by_platform`** (coverage theo `web`/`app`/`system`).
43
- > `by_platform` trả lời *"web xong bao nhiêu %, system xong bao nhiêu %"* — câu thường ngày khi làm FE và BE song song. Trước v0.5.1 không trả lời được **từ `summary`**: `by_service` là bảng chia nhóm duy nhất, mà cột `service` là `—` ở mọi row của dự án single-service ⇒ nó gộp tất cả vào một ô.
44
- 4. **Lọc báo động oan (Step 4/5).** PRD và tech-doc là tài liệu **gộp** nhiều UC nhưng chỉ **một** số version — thêm UC7 làm mọi UC cũ lệch số dù không đổi một chữ. Đọc **scope của row changelog**: UC trong danh sách → `PRD_DRIFT` 🟠 · không có → `PRD_STALE_REF` ⓘ (sạch bằng `--realign-*`) · row **mơ hồ** → 🟠 cho mọi UC (lưới an toàn).
45
- 5. **Step 5d design-spec drift** *(chỉ FE/App)*: 2 chiều, design-spec→BDD design-spec→code.
46
- 6. **Step 7b hàng đợi**: đếm PRD change request còn `Open` kèm **số ngày chờ** (hàng đợi duy nhất không lệnh nào quét lại mỗi lần chạy).
47
- 7. **Step 8cnhật ký**: append delta vào `trace-history.jsonl` in khối `📈 So lần chạy trước` (đo **tốc độ**, không chỉ trạng thái).
48
-
49
- ---
50
-
51
- ## Checkpoint & Gate
52
-
53
- - ⚪ Read-only, không gate.
54
-
55
- ---
56
-
57
- ## chế đặc biệt
58
-
59
- - **DRIFT xét trước GAP** — code lỗi thời chưa test phải hiện DRIFT (regen) không phải GAP.
60
- - **`dev_selftest` ≠ `qc_status`** — hai cột riêng, không trộn.
61
- - **"Waiting on" column** — `qc_owner`/`qc_blocked_by` trả lời "case nào chờ ai".
62
-
63
- ---
64
-
65
- ## 👓 Góc nhìn tối ưu
66
-
67
- - **Chỉ báo cáo, không hành động** — hành động ở `/generate-code` (regen DRIFT) & QC (bù GAP). Chuỗi phụ thuộc người chạy tiếp.
68
- - **Nguồn sự thật của Living Docs** — chất lượng dashboard phụ thuộc `.tsv` được các lệnh code/test ghi đúng.
69
- - **Là "single pane" để PM/PO nhìn trạng thái** — ứng viên tốt cho UI viewer (blueprint có gợi ý).
70
-
71
- ---
72
-
73
- ## Kết nối
74
-
75
- **Trước:** bất kỳ (đặc biệt sau [`/qc-report`](20-qc-report.md)) · **Sau:** DRIFT/UNTRACKED → [`/generate-code`](09-generate-code.md); GAP → [`/dev-gen-test`](12-dev-gen-test.md); OK → PR.
1
+ [← /qc-report](20-qc-report.md) · [Explain Home](README.md) · [Next: /generate-spec-manifest →](22-generate-spec-manifest.md)
2
+
3
+ # 21 · `/validate-traces` — Ma trận độ phủ spec ↔ code ↔ test
4
+
5
+ > **Một câu.** Check **read-only** độ phủ giữa spec, code, test (gồm PRD version drift); phân loại mỗi SC và làm mới Living Docs. Không sửa gì.
6
+
7
+ ---
8
+
9
+ ## Vấn đề giải quyết
10
+
11
+ Traceability chỉ có giá trị khi kiểm được. Command cho bức tranh toàn cục: SC nào có code, có test, hay còn hở — để không "tưởng xong mà chưa xong".
12
+
13
+ ---
14
+
15
+ ## Vị trí & tiền đề
16
+
17
+ - **Vị trí:** Phase Trace Audit (xuyên suốt).
18
+ - **Đặc biệt:** read-only — an toàn chạy bất kỳ lúc nào.
19
+
20
+ ---
21
+
22
+ ## Input / Output
23
+
24
+ **Input:** `.trace/…/{UC-ID}-{platform}.tsv` + spec + code + test.
25
+
26
+ **Output:** ma trận coverage + `code_coverage`; `trace-report.json` (ghi đè) + **`trace-history.jsonl`** (append 1 dòng delta — **phải commit**, không regenerate được); làm mới Living Docs dashboard.
27
+
28
+ **Flag — phạm vi:** `--domain {d}` · `--prd {TICKET-ID}` · `--uc {UC-ID}`; không cờ nào = **toàn bộ**. Report mang field `scope`, `gate-trace` **G2 fail** nếu `scope.kind !== "all"` **không ngoại lệ**, không đếm số domain trên đĩa. Biên bản có scope **không bao giờ** biên bản đầy đủ; muốn tạo PR thì phải có một lần audit toàn bộ đã commit. *(`/sync` Step 1e nói cho bạn biết scope là gì.)*
29
+
30
+ **Flag — realign:** `--realign-prd-version {UC-ID}` · `--realign-techdoc-revision {UC-ID}` — ngoại lệ có kiểm soát của "read-only": chỉ sửa **dòng `@trace.*`** trong code, **từ chối chạy** nếu UC đang `DRIFT`/`ORPHANED` hoặc bị Step 4/5 xếp 🟠.
31
+
32
+ **Cờ mới `PRD_UNTRACKED_EDIT`** 🔴 *(Step 3.9, chạy **trước** Step 4)* — 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` *(nguồn thứ hai bắt ca sửa **chưa** commit — ca thường gặp nhất)* với mốc `spec_baseline` mà Step 6b ghi. **Không chặn PR** có chủ ý, và **không có `--accept-edit`**: đường ra là bump `Version` + row changelog ⇒ cờ tự tắt.
33
+
34
+ ---
35
+
36
+ ## Các bước xử (chi tiết)
37
+
38
+ 1. Quét trace `.tsv` + đối chiếu spec/code/test.
39
+ 2. Phân loại mỗi SC theo **thứ tự ưu tiên** (rule sớm thắng):
40
+ | # | Trạng thái | Điều kiện |
41
+ |---|-----------|-----------|
42
+ | 1 | UNTRACKED | `gen_ver == —` |
43
+ | 2 | DRIFT | code + `spec_ver != gen_ver` |
44
+ | 3 | GAP |code + `test_count == —/0` |
45
+ | 4 | OK | version khớp + code + test |
46
+ 3. Dựng dashboard: `dev_selftest` (DEV smoke) **và** `qc_status` (QC chính thức) hiển thị cạnh nhau **không merge**; cột `qc_owner` + `qc_blocked_by` ("Waiting on"); và **hai trục chia nhóm**: **`by_service`** (coverage theo từng đội cột `service`) · **`by_platform`** (coverage theo `web`/`app`/`system`).
47
+ > `by_platform` trả lời *"web xong bao nhiêu %, system xong bao nhiêu %"* câu thường ngày khi làm FE và BE song song. Trước v0.5.1 không trả lời được **từ `summary`**: `by_service` bảng chia nhóm duy nhất, mà cột `service` `—` mọi row của dự án single-service ⇒ nó gộp tất cả vào một ô.
48
+ 4. **Lọc báo động oan (Step 4/5).** PRD và tech-doc là tài liệu **gộp** nhiều UC nhưng chỉ **một** số version — thêm UC7 làm mọi UC cũ lệch số dù không đổi một chữ. Đọc **scope của row changelog**: UC có trong danh sách → `PRD_DRIFT` 🟠 · không có → `PRD_STALE_REF` ⓘ (sạch bằng `--realign-*`) · row **mơ hồ** → 🟠 cho mọi UC (lưới an toàn).
49
+ 5. **Step 5d — design-spec drift** *(chỉ FE/App)*: 2 chiều, design-spec→BDD và design-spec→code.
50
+ 6. **Step 7b — hàng đợi**: đếm PRD change request còn `Open` kèm **số ngày chờ** (hàng đợi duy nhất không có lệnh nào quét lại mỗi lần chạy).
51
+ 7. **Step 8c — nhật ký**: append delta vào `trace-history.jsonl` → in khối `📈 So lần chạy trước` (đo **tốc độ**, không chỉ trạng thái).
52
+
53
+ ---
54
+
55
+ ## Checkpoint & Gate
56
+
57
+ - Read-only, không gate.
58
+
59
+ ---
60
+
61
+ ## chế đặc biệt
62
+
63
+ - **DRIFT xét trước GAP** — code lỗi thời chưa test phải hiện DRIFT (regen) không phải GAP.
64
+ - **`dev_selftest` ≠ `qc_status`** — hai cột riêng, không trộn.
65
+ - **"Waiting on" column** `qc_owner`/`qc_blocked_by` trả lời "case nào chờ ai".
66
+
67
+ ---
68
+
69
+ ## 👓 Góc nhìn tối ưu
70
+
71
+ - **Chỉ báo cáo, không hành động** — hành động ở `/generate-code` (regen DRIFT) & QC (bù GAP). Chuỗi phụ thuộc người chạy tiếp.
72
+ - **Nguồn sự thật của Living Docs** — chất lượng dashboard phụ thuộc `.tsv` được các lệnh code/test ghi đúng.
73
+ - **Là "single pane" để PM/PO nhìn trạng thái** — ứng viên tốt cho UI viewer (blueprint có gợi ý).
74
+
75
+ ---
76
+
77
+ ## Kết nối
78
+
79
+ **Trước:** bất kỳ (đặc biệt sau [`/qc-report`](20-qc-report.md)) · **Sau:** DRIFT/UNTRACKED → [`/generate-code`](09-generate-code.md); GAP → [`/dev-gen-test`](12-dev-gen-test.md); OK → PR.
@@ -68,3 +68,28 @@ Umbrella nhiều submodule + một spec repo dùng chung dễ lệch nhau. `/syn
68
68
  ## Kết nối
69
69
 
70
70
  **Trước:** bất kỳ (vận hành) · **Sau:** [`/validate-traces`](21-validate-traces.md); xử lý `📥 tester feedback` nổi lên → [`/fix-bug`](23-fix-bug.md)/[`/generate-bdd`](06-generate-bdd.md).
71
+
72
+ ---
73
+
74
+ ## Step 1e — "tài liệu nào vừa đổi" *(mới, GAPS-v4 G56)*
75
+
76
+ Câu hỏi số **một** của dev sau mỗi lần sync. Trước đó `/sync` **không** trả lời: nó diff đúng ba đường dẫn `feedback/*` và **bỏ qua `specs/`** — tức hỏi *"có góp ý gì mới"* rồi bỏ qua chính tài liệu mà mọi lệnh downstream đọc (PRD · BDD · tech-doc · design-spec). Range `{old_sha}..{new_sha}` đã có sẵn từ Step 1c, nên thêm nó là thêm **một tham số đường dẫn**.
77
+
78
+ Step này trả lời **hai câu khác nhau**, bằng **hai mốc khác nhau**:
79
+
80
+ | | Mốc | Trả lời | Vấn đề nếu chỉ có cái này |
81
+ |---|---|---|---|
82
+ | **1e-A** | `{old_sha}..{new_sha}` | *"đổi gì kể từ lần **PULL** trước"* | Mốc **reset mỗi lần pull**. Pull 4 ngày liền không audit → ngày thứ 5 chỉ thấy delta của **một ngày** |
83
+ | **1e-B** | `spec_baseline.sha_at_audit` | *"đổi gì kể từ lần **AUDIT** gần nhất"* | — con số này **tích luỹ đúng** |
84
+
85
+ 1e-B đọc khối `spec_baseline` mà `/validate-traces` Step 6b ghi (cùng khối dùng cho cờ `PRD_UNTRACKED_EDIT` của G54). `/sync` **chỉ đọc, không bao giờ ghi** — nếu nó cũng ghi thì mốc audit trượt theo mỗi lần pull, tức phá đúng thứ 1e-B tồn tại để cung cấp. `self-check` R13 canh cả lời khai này.
86
+
87
+ ### Và dòng `Next` không còn là hằng số
88
+
89
+ Bản cũ in cứng `/validate-traces (full coverage check) | /generate-code {UC-ID}` — **y hệt nhau** dù 0 file đổi hay 12 file đổi. Một lời nhắc không bao giờ thay đổi thì không mang thông tin nên bị lướt; cộng thêm việc lệnh duy nhất nó gợi ý là lệnh **đắt nhất** (quét cả repo), nên con đường duy nhất được chỉ là con đường người ta sẽ không đi.
90
+
91
+ Giờ nó rẽ nhánh: có nợ audit → `/validate-traces {các PRD đó}` · chỉ 1e-A có đổi → `/generate-code` cho phần đó · cả hai sạch → `✅ Spec khớp audit — không cần audit lại`.
92
+
93
+ > Đây là nguyên tắc `gate.md` Bước 3b đã áp cho CHECKPOINT (*"cổng luôn in ra một bảng giống hệt nhau … nên `Y` thành phản xạ và cổng hỏng âm thầm"*) — G56 là chỗ nó còn thiếu.
94
+ >
95
+ > **Lưu ý về bản chất:** G56 **không** phải một detector bị hỏng. Mọi detector đều đúng. Đây là một **công tắc bị thiếu** — không ai biết là cần bật.