@educa-corp/sdd-framework 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (243) hide show
  1. package/bin/build.js +113 -19
  2. package/bin/gate-trace.js +487 -0
  3. package/bin/index.js +445 -146
  4. package/bin/lint-trace.js +643 -0
  5. package/bin/self-check.js +804 -2
  6. package/bin/trace-schema.json +621 -10
  7. package/core/FRAMEWORK_VERSION +1 -1
  8. package/core/README.md +20 -0
  9. package/core/commands/amend-prd.md +518 -0
  10. package/core/commands/debug.md +123 -511
  11. package/core/commands/define-product.md +86 -510
  12. package/core/commands/dev-gen-test.md +86 -510
  13. package/core/commands/dev-run-test.md +133 -519
  14. package/core/commands/dev-smoke-test.md +86 -510
  15. package/core/commands/extend-prd.md +128 -522
  16. package/core/commands/fix-bug.md +118 -509
  17. package/core/commands/generate-architecture.md +94 -515
  18. package/core/commands/generate-bdd.md +128 -513
  19. package/core/commands/generate-code.md +119 -510
  20. package/core/commands/generate-design-spec.md +86 -510
  21. package/core/commands/generate-prd.md +89 -510
  22. package/core/commands/generate-spec-manifest.md +86 -510
  23. package/core/commands/generate-tech-docs.md +120 -512
  24. package/core/commands/learn.md +172 -496
  25. package/core/commands/map-testids.md +86 -510
  26. package/core/commands/propose-scenario.md +86 -510
  27. package/core/commands/qc-analyze.md +86 -510
  28. package/core/commands/qc-design-test.md +86 -510
  29. package/core/commands/qc-plan.md +86 -510
  30. package/core/commands/qc-report.md +86 -510
  31. package/core/commands/qc-review.md +86 -510
  32. package/core/commands/qc-run-test.md +115 -513
  33. package/core/commands/refine-prd.md +112 -522
  34. package/core/commands/report-bug.md +86 -510
  35. package/core/commands/review-code.md +123 -511
  36. package/core/commands/review-context.md +136 -522
  37. package/core/commands/review-tech-docs.md +90 -511
  38. package/core/commands/setup-ai-first.md +166 -138
  39. package/core/commands/sync.md +155 -107
  40. package/core/commands/update-framework.md +16 -103
  41. package/core/commands/validate-traces.md +426 -511
  42. package/core/hooks/data-guard.js +174 -83
  43. package/core/hooks/settings.json +2 -1
  44. package/core/rules/workflow.md +64 -4
  45. package/core/steps/capture-lesson.md +34 -1
  46. package/core/steps/context-loader.md +50 -8
  47. package/core/steps/gate.md +92 -35
  48. package/core/steps/report-footer.md +23 -0
  49. package/core/templates/README.md +24 -1
  50. package/core/templates/ci/trace-gate.yml +146 -0
  51. package/core/templates/feature.template +1 -1
  52. package/core/templates/hooks/pre-push +61 -0
  53. package/docs/02-concepts/architecture.md +61 -6
  54. package/docs/02-concepts/traceability.md +57 -0
  55. package/docs/03-guides/architect.md +63 -0
  56. package/docs/04-reference/commands.md +148 -134
  57. package/docs/04-reference/model-selection.md +32 -19
  58. package/docs/04-reference/trace-schema.md +39 -0
  59. package/docs/explain/02b-extend-prd.md +1 -1
  60. package/docs/explain/02c-amend-prd.md +152 -0
  61. package/docs/explain/21-validate-traces.md +2 -1
  62. package/docs/explain/27-learn.md +5 -3
  63. package/docs/explain/28-sync.md +25 -0
  64. package/docs/explain/README.md +136 -135
  65. package/package.json +5 -9
  66. package/commands/debug.md +0 -917
  67. package/commands/debug.tmpl +0 -257
  68. package/commands/define-product.md +0 -862
  69. package/commands/define-product.tmpl +0 -225
  70. package/commands/dev-gen-test.md +0 -1124
  71. package/commands/dev-gen-test.tmpl +0 -490
  72. package/commands/dev-run-test.md +0 -859
  73. package/commands/dev-run-test.tmpl +0 -225
  74. package/commands/dev-smoke-test.md +0 -798
  75. package/commands/dev-smoke-test.tmpl +0 -217
  76. package/commands/extend-prd.md +0 -907
  77. package/commands/extend-prd.tmpl +0 -270
  78. package/commands/fix-bug.md +0 -910
  79. package/commands/fix-bug.tmpl +0 -197
  80. package/commands/generate-architecture.md +0 -775
  81. package/commands/generate-architecture.tmpl +0 -194
  82. package/commands/generate-bdd.md +0 -1347
  83. package/commands/generate-bdd.tmpl +0 -590
  84. package/commands/generate-code.md +0 -1283
  85. package/commands/generate-code.tmpl +0 -649
  86. package/commands/generate-design-spec.md +0 -1161
  87. package/commands/generate-design-spec.tmpl +0 -524
  88. package/commands/generate-prd.md +0 -1143
  89. package/commands/generate-prd.tmpl +0 -223
  90. package/commands/generate-spec-manifest.md +0 -745
  91. package/commands/generate-spec-manifest.tmpl +0 -164
  92. package/commands/generate-tech-docs.md +0 -1344
  93. package/commands/generate-tech-docs.tmpl +0 -273
  94. package/commands/learn.md +0 -723
  95. package/commands/learn.tmpl +0 -63
  96. package/commands/map-testids.md +0 -662
  97. package/commands/map-testids.tmpl +0 -81
  98. package/commands/propose-scenario.md +0 -783
  99. package/commands/propose-scenario.tmpl +0 -202
  100. package/commands/qc-analyze.md +0 -693
  101. package/commands/qc-analyze.tmpl +0 -112
  102. package/commands/qc-design-test.md +0 -650
  103. package/commands/qc-design-test.tmpl +0 -69
  104. package/commands/qc-plan.md +0 -630
  105. package/commands/qc-plan.tmpl +0 -49
  106. package/commands/qc-report.md +0 -641
  107. package/commands/qc-report.tmpl +0 -60
  108. package/commands/qc-review.md +0 -634
  109. package/commands/qc-review.tmpl +0 -53
  110. package/commands/qc-run-test.md +0 -750
  111. package/commands/qc-run-test.tmpl +0 -116
  112. package/commands/refine-prd.md +0 -1074
  113. package/commands/refine-prd.tmpl +0 -278
  114. package/commands/report-bug.md +0 -729
  115. package/commands/report-bug.tmpl +0 -148
  116. package/commands/review-code.md +0 -803
  117. package/commands/review-code.tmpl +0 -143
  118. package/commands/review-context.md +0 -1323
  119. package/commands/review-context.tmpl +0 -527
  120. package/commands/review-tech-docs.md +0 -982
  121. package/commands/review-tech-docs.tmpl +0 -401
  122. package/commands/setup-ai-first.md +0 -574
  123. package/commands/setup-ai-first.tmpl +0 -378
  124. package/commands/sync.md +0 -486
  125. package/commands/sync.tmpl +0 -384
  126. package/commands/update-framework.md +0 -290
  127. package/commands/update-framework.tmpl +0 -188
  128. package/commands/validate-traces.md +0 -1435
  129. package/commands/validate-traces.tmpl +0 -854
  130. package/hooks/data-guard.js +0 -141
  131. package/hooks/settings.json +0 -18
  132. package/modules/android-compose/module.yaml +0 -13
  133. package/modules/android-compose/stack-profile.yaml +0 -57
  134. package/modules/angular/architecture-snippets/component-patterns.md +0 -187
  135. package/modules/angular/module.yaml +0 -6
  136. package/modules/angular/stack-profile.yaml +0 -38
  137. package/modules/context-engineering/architecture-snippets/context-design.md +0 -119
  138. package/modules/context-engineering/module.yaml +0 -9
  139. package/modules/context-engineering/stack-profile.yaml +0 -61
  140. package/modules/dotnet/architecture-snippets/clean-arch.md +0 -160
  141. package/modules/dotnet/module.yaml +0 -6
  142. package/modules/dotnet/stack-profile.yaml +0 -50
  143. package/modules/flutter/module.yaml +0 -14
  144. package/modules/flutter/stack-profile.yaml +0 -59
  145. package/modules/golang/architecture-snippets/domain-layout.md +0 -283
  146. package/modules/golang/module.yaml +0 -6
  147. package/modules/golang/stack-profile.yaml +0 -40
  148. package/modules/ios-swiftui/module.yaml +0 -13
  149. package/modules/ios-swiftui/stack-profile.yaml +0 -55
  150. package/modules/java-spring/architecture-snippets/layered-arch.md +0 -201
  151. package/modules/java-spring/module.yaml +0 -15
  152. package/modules/java-spring/stack-profile.yaml +0 -28
  153. package/modules/nextjs/architecture-snippets/app-router-patterns.md +0 -269
  154. package/modules/nextjs/module.yaml +0 -14
  155. package/modules/nextjs/stack-profile.yaml +0 -74
  156. package/modules/nuxt/module.yaml +0 -14
  157. package/modules/nuxt/stack-profile.yaml +0 -58
  158. package/modules/phaser-game/architecture-snippets/phaser-scene-patterns.md +0 -646
  159. package/modules/phaser-game/module.yaml +0 -15
  160. package/modules/phaser-game/stack-profile.yaml +0 -90
  161. package/modules/php-laravel/architecture-snippets/service-repository.md +0 -302
  162. package/modules/php-laravel/module.yaml +0 -15
  163. package/modules/php-laravel/stack-profile.yaml +0 -56
  164. package/modules/qc-playwright/stack-profile.yaml +0 -66
  165. package/modules/react/architecture-snippets/hooks-query-patterns.md +0 -254
  166. package/modules/react/module.yaml +0 -14
  167. package/modules/react/stack-profile.yaml +0 -63
  168. package/modules/react-native/module.yaml +0 -14
  169. package/modules/react-native/stack-profile.yaml +0 -56
  170. package/modules/vue/module.yaml +0 -14
  171. package/modules/vue/stack-profile.yaml +0 -65
  172. package/rules/data-protection.md +0 -80
  173. package/rules/workflow.md +0 -73
  174. package/scripts/init.sh +0 -49
  175. package/scripts/upgrade.sh +0 -94
  176. package/skills/code/SKILL.md +0 -19
  177. package/skills/code/SKILL.tmpl +0 -19
  178. package/skills/debug/SKILL.md +0 -19
  179. package/skills/debug/SKILL.tmpl +0 -19
  180. package/skills/design-spec/SKILL.md +0 -11
  181. package/skills/design-spec/SKILL.tmpl +0 -11
  182. package/skills/discovery/SKILL.md +0 -14
  183. package/skills/discovery/SKILL.tmpl +0 -14
  184. package/skills/prd/SKILL.md +0 -19
  185. package/skills/prd/SKILL.tmpl +0 -19
  186. package/skills/qc/qa-analyst/DOC_GAPS.template.md +0 -63
  187. package/skills/qc/qa-analyst/acceptance-criteria.md +0 -60
  188. package/skills/qc/qa-analyst/business-rules.md +0 -59
  189. package/skills/qc/qa-analyst/data-flow.md +0 -64
  190. package/skills/qc/qa-analyst/spec-breakdown.md +0 -61
  191. package/skills/qc/qa-designer/e2e/journey.md +0 -41
  192. package/skills/qc/qa-designer/exploratory/charter.md +0 -68
  193. package/skills/qc/qa-designer/exploratory/explore-to-functional.md +0 -43
  194. package/skills/qc/qa-designer/functional/api.md +0 -45
  195. package/skills/qc/qa-designer/functional/gui-feature.md +0 -46
  196. package/skills/qc/qa-designer/functional/gui-screen.md +0 -52
  197. package/skills/qc/qa-designer/integration/api.md +0 -42
  198. package/skills/qc/qa-designer/integration/db.md +0 -39
  199. package/skills/qc/qa-designer/integration/gui.md +0 -40
  200. package/skills/qc/qa-designer/integration/kafka.md +0 -40
  201. package/skills/qc/qa-designer/non-functional.md +0 -40
  202. package/skills/qc/qa-planner/test-plan.md +0 -120
  203. package/skills/qc/qa-reviewer/script/e2e.md +0 -87
  204. package/skills/qc/qa-reviewer/script/exploratory.md +0 -45
  205. package/skills/qc/qa-reviewer/script/functional.md +0 -101
  206. package/skills/qc/qa-reviewer/script/integration.md +0 -91
  207. package/skills/qc/qa-reviewer/script/non-functional.md +0 -126
  208. package/skills/qc/qa-reviewer/test-case/e2e.md +0 -73
  209. package/skills/qc/qa-reviewer/test-case/exploratory.md +0 -43
  210. package/skills/qc/qa-reviewer/test-case/functional.md +0 -76
  211. package/skills/qc/qa-reviewer/test-case/integration.md +0 -69
  212. package/skills/qc/qa-reviewer/test-case/non-functional.md +0 -73
  213. package/skills/qc/qa-runner/e2e.md +0 -49
  214. package/skills/qc/qa-runner/exploratory/session.md +0 -36
  215. package/skills/qc/qa-runner/functional/api.md +0 -35
  216. package/skills/qc/qa-runner/functional/gui-feature.md +0 -51
  217. package/skills/qc/qa-runner/functional/gui-screen.md +0 -55
  218. package/skills/qc/qa-runner/integration.md +0 -47
  219. package/skills/qc/qa-runner/non-functional.md +0 -49
  220. package/skills/qc/qa-runner/report/report.md +0 -37
  221. package/skills/setup-ai-first/SKILL.md +0 -19
  222. package/skills/setup-ai-first/SKILL.tmpl +0 -19
  223. package/skills/spec/SKILL.md +0 -19
  224. package/skills/spec/SKILL.tmpl +0 -19
  225. package/skills/test/SKILL.md +0 -18
  226. package/skills/test/SKILL.tmpl +0 -18
  227. package/steps/business-language.md +0 -56
  228. package/steps/capture-lesson.md +0 -79
  229. package/steps/context-loader.md +0 -385
  230. package/steps/gate.md +0 -94
  231. package/steps/report-footer.md +0 -102
  232. package/steps/review-fanout.md +0 -159
  233. package/steps/spawn-agent.md +0 -129
  234. package/steps/trace-mirror.md +0 -53
  235. package/templates/README.md +0 -47
  236. package/templates/architecture.template.md +0 -394
  237. package/templates/design-spec.template.md +0 -217
  238. package/templates/feature.template +0 -123
  239. package/templates/platform-guide.template.md +0 -145
  240. package/templates/prd.template.md +0 -283
  241. package/templates/product-definition.template.md +0 -188
  242. package/templates/project-context.yaml +0 -212
  243. package/templates/tech-design.template.md +0 -490
@@ -3,6 +3,9 @@
3
3
  Check read-only độ phủ giữa spec, code, và test — gồm cả PRD version drift.
4
4
 
5
5
  ## Gate
6
+
7
+ *Checkpoint: **không chặn** — read-only (ghi trace-report.json + TSV status, không đụng spec/code). Gate Bước 3 bỏ qua CHECKPOINT (Bước 3a).*
8
+
6
9
  # Gate — Quy trình vào chuẩn cho mọi lệnh
7
10
 
8
11
  Mọi lệnh PHẢI chạy gate này trước khi thực thi phần logic riêng của nó.
@@ -22,35 +25,31 @@ Trước tiên, kiểm tra xem `$ARGUMENTS` có phải là payload JSON từ m
22
25
  - Đi thẳng tới phần logic riêng của lệnh.
23
26
  3. Nếu `$ARGUMENTS` không phải JSON hoặc không có `_agent_mode` → tiếp tục sang Bước 1 (chế độ thường).
24
27
 
25
- ## Bước 0-B — Kiểm tra Model
28
+ ## Bước 0-B — Ghi nhận Model *(KHÔNG chặn)*
26
29
 
27
- *Bỏ qua bước này nếu `_agent_mode: true` (sub-agent — orchestrator đã kiểm tra rồi).*
30
+ *Bỏ qua nếu `_agent_mode: true` (sub-agent — orchestrator đã ghi nhận rồi).*
28
31
 
29
- Các lệnh sinh nội dung review phức tạp đòi hỏi khả năng suy luận mạnh.
30
- Dùng model nhỏ hơn sẽ rủi ro: bỏ sót edge case, phân tích spec thiếu sót, vi phạm kiến trúc.
32
+ Ghi lại **model bạn agent đang chạy lệnh này thực sự đang dùng**, rồi mang nó vào
33
+ dòng `Model:` của report cuối (xem `report-footer`). Nếu bạn biết mình **không** phải một
34
+ model Opus, gắn thêm cảnh báo ngay ở dòng đó.
31
35
 
32
- Hiển thị chờ phản hồi:
36
+ **KHÔNG hỏi người dùng. KHÔNG chờ. KHÔNG dừng.**
33
37
 
34
- ```
35
- ⚙️ MODEL CHECK
36
- ──────────────────────────────────────────────────────────────────
37
- Recommended : model Opus mới nhất
38
- Why needed : Phân tích spec, review kiến trúc, sinh code đòi hỏi
39
- suy luận sâu. Model nhỏ hơn (Haiku/Sonnet) dễ bỏ sót edge case.
40
-
41
- Cách đổi trong Claude Code:
42
- /model chọn model Opus
43
- • hoặc: Settings → Model
44
-
45
- Đang chạy một model Opus?
46
- Y — đúng → tiếp tục
47
- S — bỏ qua kiểm tra (tôi chấp nhận rủi ro chất lượng thấp hơn với model hiện tại)
48
- ──────────────────────────────────────────────────────────────────
49
- ```
38
+ > **Vì sao bước này từng là prompt chặn, và vì sao bỏ (GAPS-v3 G41):** bản cũ hiện khối
39
+ > `⚙️ MODEL CHECK` rồi chờ `Y/S/N`. Ba vấn đề cùng chỉ một hướng:
40
+ > **(1)** nó hỏi người dùng thứ mà **agent đã biết chính xác**;
41
+ > **(2)** câu trả lời **không kiểm chứng được** — gõ `Y` xong vẫn đang chạy Haiku thì không
42
+ > phát hiện;
43
+ > **(3)** **cả `Y` lẫn `S` đều đi tiếp** cách duy nhất để nó dừng là tự nguyện gõ `N`.
44
+ > Tức nó **không chặn được ai**, mà tốn một lần chặn ở **mọi** lệnh. Một feature đi hết
45
+ > pipeline dùng 20 lệnh; 30/32 lệnh chạy gate. Hai mươi lần bấm cho một tín hiệu tự-khai
46
+ > không kiểm chứng được — và chính cái giá đó làm mòn CHECKPOINT ở Bước 3, cổng có giá trị thật.
47
+ >
48
+ > Khai báo trong report **mạnh hơn** hỏi: đúng nguồn (agent, không phải người), và nằm
49
+ > **cạnh kết quả** để cân nhắc, thay vì nằm trước khi có kết quả để bấm cho xong.
50
50
 
51
- - "Y" tiếp tục sang Bước 1.
52
- - "S" tiếp tục sang Bước 1 (người dùng chấp nhận rủi ro, thêm ⚠️ vào report cuối).
53
- - "N" hoặc bất kỳ giá trị nào khác → **DỪNG.** Xuất: "Vui lòng chuyển sang một model Opus (`/model`) rồi chạy lại lệnh này."
51
+ **Vẫn khuyến nghị Opus:** phân tích spec, review kiến trúc và sinh code đòi hỏi suy luận sâu;
52
+ model nhỏ hơn dễ bỏ sót edge case vi phạm kiến trúc. Đổi: `/model` chọn Opus.
54
53
 
55
54
  ## Bước 1 — Xác định Target File
56
55
 
@@ -80,419 +79,154 @@ Lưu toàn bộ context đã nạp vào bộ nhớ để dùng xuyên suốt phi
80
79
 
81
80
  ## Bước 3 — CHECKPOINT
82
81
 
83
- Sau khi hoàn thành Bước 1 và 2, hiển thị bản tóm tắt và chờ xác nhận:
84
-
85
- ```
86
- CHECKPOINT
87
- -----------
88
- Target : {resolved file path}
89
- Project : {project.name từ project-context.yaml}
90
- Tech stack : {language} / {framework}
91
- Module : {module nếu có, else "not configured"}
92
- Domains : {danh sách domain, ngăn cách bởi dấu phẩy}
93
-
94
- Tiếp tục? (Y/N)
95
- ```
96
-
97
- Chờ người dùng trả lời rõ ràng "Y" hoặc "N" rồi mới tiếp tục.
98
- - "Y" → tiếp tục sang các bước riêng của lệnh bên dưới.
99
- - "N" → dừng lại và hỏi người dùng muốn thay đổi gì.
100
-
101
-
102
- *Lưu ý: Với lệnh này, target ở Bước 1 là một tên domain hoặc UC-ID cụ thể từ `$ARGUMENTS`. Không có một file đơn để phân giải — lệnh quét nhiều thư mục.*
103
-
104
- ## Context
105
- # Context Loader — Nạp toàn bộ context dự án
106
-
107
- Thực hiện các bước theo đúng thứ tự. Lưu mọi thứ vào bộ nhớ trong suốt phiên làm việc của lệnh.
108
-
109
- **Hướng dẫn ưu tiên (chống lost-in-middle):**
110
- - Bước 1–2 là PROJECT-CONFIG — nạp trước, phân giải mọi path và metadata.
111
- - Bước 3 là CRITICAL — kiến trúc + coding standards, là các sự thật ưu tiên cao nhất khi sinh nội dung.
112
- - Bước 4 là SAFETY — quy tắc bảo vệ dữ liệu, thực thi ngầm suốt cả phiên.
113
- - Bước 5–6 là DOMAIN KNOWLEDGE — thuật ngữ và định nghĩa entity.
114
- - Bước 7 là WORKING MEMORY RECAP — chốt các sự thật quan trọng lên đầu bộ nhớ làm việc.
115
-
116
- ---
117
-
118
- ## Bước 1 — [PROJECT-CONFIG] Nạp project-context.yaml
119
-
120
- Đọc `.agent/project-context.yaml`. Trích xuất và lưu:
121
-
122
- **Tech Stack:**
123
- - `tech_stack.language` → ngôn ngữ đang dùng (vd: Java 17, TypeScript, C#, Go)
124
- - `tech_stack.framework` → framework đang dùng (vd: Spring Boot 3.2, Angular 17, .NET 8)
125
- - `tech_stack.build_tool` → build tool (vd: Maven, npm, dotnet, go)
126
- - `tech_stack.test_framework` → test framework (vd: JUnit 5 + Mockito, Jest, xUnit)
127
- - `tech_stack.database` → database (vd: PostgreSQL, MySQL, MongoDB)
128
- - `tech_stack.module` → module profile đang dùng (vd: java-spring, angular, dotnet, golang, context-engineering)
129
-
130
- **Conventions:**
131
- - `conventions.build_command` → cách compile/build
132
- - `conventions.test_command` → cách chạy test
133
- - `conventions.service_run` → cách khởi động service
134
- - `conventions.ticket_prefix` → tiền tố ticket ID (vd: PROJ, FEAT, UC)
135
-
136
- **Domains:**
137
- - `domains` → danh sách các business domain đang hoạt động
138
-
139
- **Paths (nếu có):**
140
- - `paths.specs_dir` → gốc của spec artifact — PRD, BDD, tech-docs, design-spec. Cấu trúc: `{specs_dir}/{domain}/{prd-slug}/{ {TICKET-ID}-{prd-slug}.md | bdd/ | tech-docs/ | design-spec/}` (file PRD đặt tên `{TICKET-ID}-{prd-slug}.md`, là file `.md` duy nhất ở gốc feature folder)
141
- - `paths.refinement_dir` → thư mục output cho findings/review
142
- - `paths.qc_dir` → gốc artifact QC automation (hiện ở top-level, mỗi UC một thư mục con: `{qc_dir}/{UC-ID}/`)
143
- - `paths.qc_skills_dir` → nơi các lệnh qc-* nạp QC skill (mặc định bundled `.agent/skills/qc`; override sang repo/submodule riêng của team QC để bản nâng cấp framework không ghi đè)
144
- - `paths.product_definitions_dir` → gốc product definition
145
- - `paths.domain_knowledge_dir` → gốc domain knowledge
146
- - `paths.business_dictionary` → path tới business-dictionary.md
147
- - `paths.core_entities` → path tới core-entities.md
148
- - `paths.tech_docs_dir` → gốc tài liệu kỹ thuật (gộp với specs_dir trong bố cục feature-package — tech-docs nằm dưới `{specs_dir}/{domain}/{prd-slug}/tech-docs/`)
149
- - `paths.src_dir` → gốc mã nguồn (nơi generate-code đặt & quét code; nguồn chính cho FE + phạm vi reuse-scan của DS5)
150
- - `paths.trace_dir` → thư mục trạng thái trace; cấu trúc: `.trace/{domain}/{prd-slug}/{UC-ID}-{platform}.tsv` (mỗi UC × platform một sổ)
151
-
152
- Nếu không có section `paths`, dùng các giá trị mặc định:
153
- - `specs_dir` = `specs`
154
- - `refinement_dir` = `.agent/review`
155
- - `qc_dir` = `docs`
156
- - `qc_skills_dir` = `.agent/skills/qc`
157
- - `product_definitions_dir` = `specs/product-definition`
158
- - `domain_knowledge_dir` = `specs/domain-knowledge`
159
- - `business_dictionary` = `specs/domain-knowledge/business-dictionary.md`
160
- - `core_entities` = `specs/domain-knowledge/core-entities.md`
161
- - `tech_docs_dir` = `specs`
162
- - `src_dir` = `src`
163
- - `trace_dir` = `.trace`
164
-
165
- Lưu ý: Trong bố cục feature-package, `specs_dir` là gốc thống nhất. Mọi loại spec artifact (PRD, BDD, tech-docs, design-spec) đều nằm dưới `{specs_dir}/{domain}/{prd-slug}/`. `prd-slug` là tên folder feature-package, không phải một biến config riêng.
166
-
167
- **Cách trích xuất `prd_slug` (đúng cho MỌI target file, bất kể độ sâu lồng nhau):** với một path target dạng `{specs_dir}/{domain}/{prd-slug}/...`, lấy **segment path đầu tiên sau `{specs_dir}/{domain}/`** — tức vị trí `{prd-slug}`. KHÔNG dùng folder cha trực tiếp của file, vì artifact BDD/tech-docs/design-spec lồng sâu hơn một hoặc hai cấp bên trong package. Ví dụ:
168
- - `specs/payment/create-invoice/PAY01-create-invoice.md` → `prd_slug = create-invoice`
169
- - `specs/payment/create-invoice/bdd/system/PAY-UC1.feature` → `prd_slug = create-invoice` *(KHÔNG phải `system`)*
170
- - `specs/payment/create-invoice/bdd/web/PAY-UC1.feature` → `prd_slug = create-invoice` *(KHÔNG phải `web`)*
171
- - `specs/payment/create-invoice/tech-docs/PAY01-tech-design.md` → `prd_slug = create-invoice` *(KHÔNG phải `tech-docs`)*
172
- - `specs/payment/create-invoice/design-spec/PAY-design-spec-web.md` → `prd_slug = create-invoice`
173
-
174
- Mọi artifact cùng cấp của một feature (PRD, BDD của từng platform, tech-docs BE + FE, design-spec, và trace TSV) đều phân giải về **cùng một `prd_slug`** — nên một BDD **system** hay tech-doc **system/BE** được tổng hợp sẽ nằm chung package `{specs_dir}/{domain}/{prd-slug}/` với các artifact web/app mà nó được suy ra từ đó.
175
-
176
- Nếu `tech_stack.module` được đặt, đồng thời nạp `.agent/modules/{module}/stack-profile.yaml` nếu file tồn tại.
177
-
178
- ---
179
-
180
- ## Bước 1.5 — [SERVICE ROUTING] Phân giải path service (chế độ umbrella)
181
-
182
- *Bỏ qua hoàn toàn bước này nếu `setup.mode` không phải `"umbrella"` và không có section `services` trong project-context.yaml.*
82
+ *Bỏ qua nếu `_agent_mode: true`.*
183
83
 
184
- Nếusection `services`:
84
+ ### 3a — Lệnh này phải chặn không?
185
85
 
186
- **1. Phát hiện active domain** (theo thứ tự ưu tiên):
187
- - Đọc `@trace.domain` từ frontmatter của target file (nếu Gate đã nạp một target file)
188
- - Trích xuất từ path target file: `domain` = segment đầu tiên sau base path `specs_dir`; `prd_slug` = segment kế tiếp (folder feature-package). Điều này đúng ở mọi độ sâu target xem quy tắc trích xuất `prd_slug` ở Bước 1.
189
- *(vd: `specs/user/create-account/USR01-create-account.md` **và** `specs/user/create-account/bdd/system/UC1.feature` đều domain = `user`, prd_slug = `create-account`)*
190
- - Nếu `$ARGUMENTS` chứa một path, trích xuất segment domain sau `specs_dir`
86
+ | Mức | Lệnh nào | `--yes` bỏ qua được? |
87
+ |---|---|:---:|
88
+ | **Không chặn** | Lệnh read-only: `/review-code` · `/validate-traces` · `/debug` · `/review-context` · `/review-tech-docs` |(vốn không có) |
89
+ | **Chặn thường** | Mọi lệnh sinh/sửa artifact | |
90
+ | **Chặn CỨNG** | Ghi đè file đã tồn tại · `--resume` áp findings · migrate · prune | **không bao giờ** |
191
91
 
192
- **1b. Phát hiện active platform** (chỉ cần khi service dạng map-theo-platform bước 2b):
193
- - Đọc `@trace.platform` từ header của target `.feature` (`system` | `web` | `app`) Gate đã resolve target trước bước này.
194
- - Nếu target không mang `@trace.platform` (vd target PRD `.md`), thử suy từ segment `bdd/{platform}/` trong path target.
195
- - Nếu vẫn không xác định được → `active_platform = null`.
92
+ `--yes` trong `$ARGUMENTS` bỏ qua CHECKPOINT mức *chặn thường*. (Bước 1 đã tách mọi token
93
+ `--` khỏi phần resolve target, nên cờ này không ảnh hưởng việc tìm file.) Mở đường chạy
94
+ headless: `claude -p "/generate-code UC1 --yes"`.
196
95
 
197
- **2. Route tới service** nếu active domain khớp một key trong `services`.
96
+ > **KHÔNG tự suy mức từ bảng này.** Mỗi lệnh **tự khai** mức của một dòng `*Checkpoint: …*`
97
+ > ngay dưới `## Gate` của chính nó — đọc dòng đó, đừng suy diễn. Bảng trên chỉ giải thích ba mức
98
+ > **nghĩa là gì**.
99
+ > Nguồn máy đọc: `bin/trace-schema.json` → `gate.checkpoint_levels`; `self-check` **R11** fail
100
+ > build nếu nhãn trong file lệnh lệch với schema, hoặc nếu một lệnh `hard`/`none` thiếu nhãn.
101
+ > *(Lệnh không có dòng nào = mức **chặn thường**, mặc định.)*
198
102
 
199
- Hình dung việc này như **tra địa chỉ**: đi từ `domain`, thể qua `platform`, có thể qua `prd_slug`, cho tới khi chỉ còn đúng một submodule.
103
+ > **Mức *không chặn* thực thi đúng miễn trừ `rules/workflow.md` đã cấp từ trước**
104
+ > trước G41 file đó viết *"read-only commands may skip CHECKPOINT"* còn gate thì luôn đòi.
105
+ > Hai file cùng được nạp vào mọi lệnh mà nói ngược nhau; agent theo cái nào là tuỳ lúc.
200
106
 
201
- Thứ trỏ tới đích gọi là một **service entry**. Nó chỉ có hai kiểu:
107
+ ### 3b In
202
108
 
203
- | Kiểu | Nhận ra bằng | Nghĩa |
204
- |---|---|---|
205
- | **Đã chốt** | `path` | Xong — đây submodule cần tìm |
206
- | **Tra tiếp** | có `by_prd_slug` | Ô này còn nhiều submodule → tra thêm một nấc bằng `prd_slug` (2c) |
207
-
208
- **Kiểm tra hợp lệ TRƯỚC khi dùng một entry** (làm ngay, đừng đợi tới 2a/2b/2c — sai ở đây mà đi tiếp là route nhầm repo mà không báo gì):
209
-
210
- | Entry trông thế nào | Xử lý |
211
- |---|---|
212
- | có `path`, không có `by_prd_slug` | hợp lệ → dùng |
213
- | có `by_prd_slug`, không có `path` | hợp lệ → tra tiếp (2c) |
214
- | **có CẢ HAI** | ❌ lỗi cấu hình → `active_service = unresolved`. **KHÔNG** ưu tiên `path` rồi bỏ qua `by_prd_slug` — như vậy mọi feature sẽ âm thầm route về cùng một repo. Báo đúng key sai để người dùng sửa. |
215
- | **không có cái nào** (và cũng không phải map platform) | ❌ lỗi cấu hình → `unresolved`, nêu rõ entry thiếu `path`/`by_prd_slug` |
216
- | `by_prd_slug` chứa entry lại có `by_prd_slug` | ❌ lỗi cấu hình → `unresolved`. Chỉ tra đúng **một** nấc slug, không đệ quy |
109
+ **KHÔNG lặp lại những `[CTX LOADED]` vừa in.** Recap của context-loader (Bước 7) đã hiện
110
+ Stack · Platform · Layers · CLAUDE.md · Dict · Entities · Lessons · Service · Status ngay phía
111
+ trên. CHECKPOINT chỉ thêm **một** thông tin mới`Target`.
217
112
 
218
- Còn `services.{domain}` thì có thể là **một service entry** (chốt luôn cấp domain), hoặc **một map platformservice entry** (phải qua nấc platform trước). Nhận dạng theo đúng thứ tự này:
113
+ **Mọi thứ sạch** recap báo `Status: FULL`, không cờ nào bậtin đúng hai dòng:
219
114
 
220
- | Thấy gì trong `services.{domain}` | Đi nhánh |
221
- |---|---|
222
- | có `path` | **2a** — chốt luôn |
223
- | có `by_prd_slug` | **2c** — tra bằng `prd_slug` |
224
- | không có cả hai (chỉ có các sub-key `system`/`web`/`app`…) | **2b** — tra bằng `platform`, rồi lặp lại đúng bảng này cho entry con |
225
-
226
- **2a. Dạng phẳng** — `services.{domain}` có **trực tiếp** `path`/`module` (một domain ↔ một service, mọi platform về cùng submodule). Route như cũ:
227
- - Lưu `active_service` = `services.{domain}.path`
228
- - Lưu `active_service_module` = `services.{domain}.module`
229
- - Nếu service có `module` riêng → dùng nó làm `active_module` (override `tech_stack.module`)
230
-
231
- **2b. Dạng map-theo-platform** — `services.{domain}` **KHÔNG** có `path` trực tiếp mà chứa các sub-key platform (`system` / `web` / `app`), mỗi cái là một **service entry** (một business-domain trải trên nhiều platform/submodule). Route theo `active_platform` (bước 1b):
232
- - Nếu `active_platform` khớp một sub-key → `entry = services.{domain}.{active_platform}`. Nếu `entry` có `path` → lưu `active_service = entry.path`, `active_service_module = entry.module` (→ `active_module`, override `tech_stack.module`). Nếu `entry` có `by_prd_slug` → **đi tiếp sang 2c** với entry đó.
233
- - Nếu `active_platform = null` (chưa xác định platform, vd đang thao tác cấp PRD) → **KHÔNG** chốt một service; đặt `active_service = multi`, `service_candidates_kind = platform`, và lưu `service_candidates` = map platform→`{path, module}`. **Làm phẳng luôn ở đây:** platform nào có entry `by_prd_slug` thì giải bằng `prd_slug` hiện tại (target cấp PRD vẫn nằm trong một feature-package nên `prd_slug` đã biết từ bước 1) → `service_candidates.{platform}` vẫn là `{path, module}` phẳng. Nếu `prd_slug` không khớp key nào, ghi platform đó là `unresolved` kèm lý do thay vì bỏ im. Nhờ vậy **mọi lệnh downstream chỉ cần biết một kiểu `service_candidates`**. Lệnh cần một service cụ thể (`/generate-code`, `/dev-*`, `/fix-bug`) luôn chạy trên target `.feature` có platform nên sẽ resolve được ở lần chạy đó; lệnh cấp PRD (`/generate-prd`, `/refine-prd`) không cần service cụ thể.
234
- - Nếu `active_platform` xác định nhưng không có sub-key tương ứng → `active_service = unresolved` (xem Fallback) với lý do "domain `{domain}` chưa cấu hình platform `{active_platform}`".
235
-
236
- **2c. Dạng map-theo-prd_slug** — một service entry chứa `by_prd_slug` thay cho `path`: **một ô của bảng định tuyến ứng với NHIỀU submodule**, mỗi feature-package một submodule. Dùng khi một platform (hoặc cả một domain) bị chia thành nhiều repo theo feature — ví dụ mỗi mini-game webview là một repo riêng.
237
-
238
- Đến đây `prd_slug` đã được trích ở bước 1 (không cần detect thêm gì). Route:
239
-
240
- - Nếu `prd_slug` khớp một key dưới `by_prd_slug` → `entry = {…}.by_prd_slug.{prd_slug}`; lưu `active_service = entry.path`, `active_service_module = entry.module` (→ `active_module`, override `tech_stack.module`).
241
- **"Khớp" ở đây là khớp CHÍNH XÁC toàn chuỗi, phân biệt hoa/thường.** KHÔNG prefix, KHÔNG bỏ hậu tố, KHÔNG so gần đúng: `dap-chuot-v2` **không** khớp key `dap-chuot`; `Ban-Cung` **không** khớp `ban-cung`. Feature mới tách ra từ một feature cũ là một repo khác cho tới khi có người khai nó vào bảng.
242
- - Nếu `prd_slug = null` (chưa xác định feature-package — chỉ xảy ra khi không có target file, vd `$ARGUMENTS` rỗng) → **KHÔNG** chốt một service; đặt `active_service = multi`, `service_candidates_kind = prd_slug`, và lưu `service_candidates` = toàn map `by_prd_slug` (slug → `{path, module}`).
243
- ⚠️ Đây là kiểu `service_candidates` **khác** với 2b — lệnh nào duyệt `service_candidates` theo platform (vd `/generate-bdd` sinh `bdd/{platform}/`) phải kiểm `service_candidates_kind = platform` trước; gặp `prd_slug` thì DỪNG và yêu cầu người dùng chỉ rõ target, đừng coi slug là platform.
244
- - Nếu `prd_slug` xác định nhưng **không** có key tương ứng → `active_service = unresolved` với lý do rõ: "domain `{domain}`{, platform `{active_platform}`} chưa cấu hình prd_slug `{prd_slug}`". **KHÔNG** tự đoán submodule gần đúng theo tên.
245
-
246
- Vị trí đặt `by_prd_slug` — hợp lệ ở **cả hai cấp**:
247
- - **Dưới một platform** (lồng trong 2b): `services.{domain}.{platform}.by_prd_slug` — platform đó có nhiều repo, các platform khác vẫn `{path, module}` như thường.
248
- - **Ngay dưới domain** (thay cho `path` của 2a): `services.{domain}.by_prd_slug` — domain không chia platform nhưng vẫn nhiều repo theo feature.
249
-
250
- *(`by_prd_slug` lồng trong `by_prd_slug` là vô nghĩa — nếu gặp, coi là lỗi cấu hình: `active_service = unresolved`, nêu rõ để người dùng sửa file. Một entry vừa có `path` vừa có `by_prd_slug` cũng là lỗi cấu hình — báo lỗi, không âm thầm ưu tiên cái nào.)*
251
-
252
- Ví dụ (một domain trải nhiều platform, riêng `webview` chia theo feature):
253
- ```yaml
254
- services:
255
- learning:
256
- system: { path: "backend", module: "java-spring" }
257
- web: { path: "web-app", module: "nextjs" }
258
- webview:
259
- by_prd_slug:
260
- dap-chuot: { path: "games/whac-a-mole", module: "phaser-game" }
261
- ban-cung: { path: "games/archery", module: "phaser-game" }
262
115
  ```
263
- target `specs/learning/dap-chuot/bdd/webview/UC1.feature` cho `domain = learning`,
264
- `active_platform = webview`, `prd_slug = dap-chuot` → `active_service = games/whac-a-mole`.
265
-
266
- *(Cả 2a/2b/2c: override `paths.specs_dir`/`paths.tech_docs_dir` per-service CHỈ khi `setup.spec_source` KHÔNG được đặt. Khi `spec_source` ĐƯỢC đặt, MỌI BDD/tech-doc là artifact liên team → để bước 4 route sang spec repo; KHÔNG pin per-service ở đây.)*
267
-
268
- **3. Fallback**:
269
- - Không phát hiện được domain, hoặc domain không khớp key nào trong `services` → giữ path mặc định từ Bước 1, đặt `active_service = unresolved`.
270
- - Domain khớp một map-theo-platform (2b) nhưng `active_platform` xác định mà thiếu sub-key tương ứng → `active_service = unresolved`, ghi lý do rõ để lệnh DỪNG báo lỗi cấu hình (không tự đoán platform).
271
- - Entry là map-theo-prd_slug (2c) nhưng `prd_slug` xác định mà thiếu key tương ứng → `active_service = unresolved`, ghi lý do rõ (không tự đoán submodule).
272
- - Entry sai cấu trúc (vừa có `path` vừa có `by_prd_slug`, hoặc `by_prd_slug` lồng nhau) → `active_service = unresolved`, nêu đúng key sai để người dùng sửa `project-context.yaml`.
273
-
274
- **4. Tự động override theo spec source** — nếu `setup.spec_source` được đặt VÀ path tương ứng chưa được set tường minh trong `paths:`:
275
- - Override `paths.specs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt.** Mọi spec artifact (PRD, BDD, tech-docs, design-spec) nằm dưới gốc spec thống nhất trong spec repo dùng chung theo bố cục feature-package: `{spec_source}/specs/{domain}/{prd-slug}/`. Mọi umbrella (FE/App/BE) đều đọc từ đây. *(`specs/` theo service chỉ khi không có `spec_source`.)*
276
- - Override `paths.tech_docs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt** (bước 2 không còn pin tech-docs theo service trong trường hợp này). Tech-docs nằm tại `{spec_source}/specs/{domain}/{prd-slug}/tech-docs/`. Tech-design CHÍNH LÀ API contract liên team: BE viết ở đây, FE/App đọc nó từ cùng spec submodule tại `/generate-code --phase=integration`. *(tech-docs theo service chỉ xảy ra khi không có `spec_source` — repo BE thuần đa-service không có spec module dùng chung.)*
277
- - Override `paths.domain_knowledge_dir` → `{spec_source}/specs/domain-knowledge`
278
- - Override `paths.business_dictionary` → `{spec_source}/specs/domain-knowledge/business-dictionary.md`
279
- - Override `paths.core_entities` → `{spec_source}/specs/domain-knowledge/core-entities.md`
280
- - Override `paths.bug_reports_dir` → `{spec_source}/feedback/bug-reports`
281
- - Override `paths.bdd_proposals_dir` → `{spec_source}/feedback/bdd-proposals`
282
- - Override `paths.prd_change_requests_dir` → `{spec_source}/feedback/prd-change-requests`
283
- - Override `paths.trace_dir` → `{spec_source}/.trace` — **luôn khi `spec_source` được đặt.** Trace TSV được gộp vào spec repo (một nơi authoritative duy nhất, không tách theo service) để PM/PO có một chỗ duy nhất quản lý trạng thái. Cấu trúc bên trong: `.trace/{domain}/{prd-slug}/{UC-ID}-{platform}.tsv`. Các lệnh phía code (`/generate-code`, `/dev-run-test`, `/qc-run-test`) chạy từ `service_root` nhưng **ghi trace row của chúng vào `{spec_source}/.trace/{domain}/{prd-slug}/`** — giống như chúng đã push `feedback/` vào đó. *(`.trace` theo service chỉ khi không có `spec_source`.)*
284
- - Override `paths.refinement_dir` → `{spec_source}/.agent/review` — **luôn khi `spec_source` được đặt.** Findings review (`/refine-prd`, `/review-context`, `/review-tech-docs`) là artifact liên-team *về* tài liệu trong spec repo (PRD/BDD/tech-design) — thuộc cùng khu vực ghi với `.trace/` và `feedback/`. Các lệnh review chạy từ working dir của service (BE repo) nhưng **ghi findings vào `{spec_source}/.agent/review/`**, KHÔNG phải `.agent/review` của service repo. Bên trong flat, phân biệt bằng tên file đã prefix `{prd-slug}`/`{UC-ID}`/`{TICKET-ID}`. *(`.agent/review` theo service chỉ khi không có `spec_source`.)*
285
-
286
- > **Vì sao đặt dưới `spec_source`:** PRD, BDD, tech-docs, design-spec, domain knowledge, feedback của tester, **trạng thái coverage `.trace/`**, **và findings review `.agent/review/`** đều là **artifact liên team** — chúng nằm trong **spec repo dùng chung** theo bố cục feature-package để mọi umbrella (FE/App/BE) và PM đọc từ một nguồn qua `/sync`. Trong bố cục feature-package, một folder `specs/{domain}/{prd-slug}/` gom tất cả loại artifact của một PRD, giúp spec repo tự đủ và dễ điều hướng theo feature. Service submodule chỉ chứa **code** (+ tooling build/test). `.trace/`, `.agent/review/` và `feedback/` là khu vực **ghi** của dev/QC/reviewer trong spec repo. Ở chế độ single-service (không có `spec_source`), mọi thứ mặc định dưới gốc repo — vẫn là một repo.
287
-
288
- ---
289
-
290
- ## Bước 1.6 — [SERVICE CONVENTIONS] Nạp convention riêng của service (chế độ umbrella)
291
-
292
- *Bỏ qua hoàn toàn bước này nếu `active_service` là `"unresolved"` hoặc `"multi"` (chưa chốt một service — dạng map-theo-platform ở cấp PRD) hoặc context ở chế độ single-service.*
293
-
294
- Khi `active_service` đã được phân giải thành một path thật ở Bước 1.5 (vd: `user-service/`):
295
-
296
- **1. Định vị config của service** — thử theo thứ tự ưu tiên:
297
- - `{active_service}/.agent/project-context.yaml`
298
- - `{active_service}/project-context.yaml`
299
-
300
- **2. Nếu tìm thấy, override bằng giá trị riêng của service:**
301
-
302
- | Biến | Nguồn |
303
- |----------|--------|
304
- | `conventions.test_command` | `conventions.test_command` của service |
305
- | `conventions.build_command` | `conventions.build_command` của service |
306
- | `paths.trace_dir` | **Nếu `spec_source` được đặt → giữ route spec-repo của bước 4 (`{spec_source}/.trace`); bỏ qua mọi `trace_dir` cấp service.** Chỉ khi không có `spec_source`: `{active_service}/{service paths.trace_dir}` (mặc định `{active_service}/.trace`). |
307
- | `paths.specs_dir` | **Nếu `spec_source` được đặt → giữ route spec-repo của bước 4 (`{spec_source}/specs`); bỏ qua mọi `specs_dir` cấp service** (mọi spec artifact đều liên team, không bao giờ theo service ở chế độ này). Chỉ khi không có `spec_source`: `{active_service}/{service paths.specs_dir}` nếu được set, else dùng override ở Bước 1.5. |
308
- | `paths.refinement_dir` | **Nếu `spec_source` được đặt → giữ route spec-repo của bước 4 (`{spec_source}/.agent/review`); bỏ qua mọi `refinement_dir` cấp service** (findings review là artifact liên team). Chỉ khi không có `spec_source`: `{active_service}/.agent/review`. |
309
-
310
- **3. Lưu** `service_root = {active_service}` làm mốc thư mục làm việc cho mọi lệnh phía sau:
311
- - Các lệnh shell (`/dev-run-test`, `/dev-gen-test`) chạy **bên trong** `service_root`
312
- - **File source/test** được ghi tương đối với `service_root`; **trace TSV** được ghi vào `{paths.trace_dir}` (là spec repo khi `spec_source` được đặt — một thao tác ghi liên-repo, commit/push vào spec submodule giống như `feedback/`).
313
-
314
- **4. Nếu không tìm thấy config của service** — giữ mặc định umbrella, vẫn set `service_root = {active_service}` (luôn cần mốc path kể cả khi không có config override).
315
-
316
- ---
317
-
318
- ## Bước 2 — [PROJECT-CONFIG] Nạp module stack profile (có điều kiện)
319
-
320
- Nếu `tech_stack.module` được đặt, đọc `.agent/modules/{module}/stack-profile.yaml`.
321
- Merge các convention riêng của framework (layer pattern, test pattern, quy tắc đặt tên) vào context đã nạp.
322
- Nếu file không tồn tại → bỏ qua âm thầm.
323
-
324
- ---
325
-
326
- ## Bước 3 — [CRITICAL] Nạp CLAUDE.md (phân tầng: root + service overlay)
327
-
328
- *Đây là context ưu tiên cao nhất — nó định nghĩa CÁCH viết code và tài liệu cho dự án này.*
329
-
330
- CLAUDE.md được nạp theo **hai tầng** để các quy tắc toàn-umbrella và kiến trúc/coding standards
331
- riêng của service kết hợp đúng cách. Agent luôn đứng ở gốc umbrella, nhưng code triển khai nằm
332
- trong một service submodule với stack, kiến trúc, và convention RIÊNG của nó — nên CLAUDE.md của
333
- service phải thắng khi sinh code.
334
-
335
- **Tầng 1 — [BASE] Root CLAUDE.md (toàn umbrella).**
336
- Đọc `CLAUDE.md` ở gốc repo. Coi nội dung của nó là **nền tảng dùng chung** cho cả umbrella —
337
- git convention, tư thế bảo vệ dữ liệu, quy tắc xuyên suốt, và (ở chế độ single-service) là
338
- kiến trúc + coding standards duy nhất của dự án.
339
-
340
- **Tầng 2 — [OVERLAY] Service CLAUDE.md (chỉ chế độ umbrella).**
341
- *Chỉ chạy nếu `service_root` đã được set ở Bước 1.6 (tức đã route tới một service thật).*
342
- Đọc `{service_root}/CLAUDE.md`. File này định nghĩa kiến trúc + coding standards của **stack
343
- thực sự đang được triển khai** (vd: `user-service` = java-spring, `web` = nextjs).
344
- Overlay nó lên trên Tầng 1: **khi có xung đột, giá trị của service THẮNG** cho kiến trúc,
345
- coding standards, và error handling. Các giá trị Tầng 1 mà service không định nghĩa lại
346
- (vd: git convention, banned pattern dùng chung toàn tổ chức) vẫn có hiệu lực.
347
-
348
- Từ kết quả **đã merge**, trích xuất và lưu:
116
+ CHECKPOINT Target: {resolved file path}
117
+ Tiếp tục? (Y/N)
118
+ ```
349
119
 
350
- - **§1 Project Overview** → tên dự án, ngôn ngữ, framework, lệnh build/test, domains
351
- - **§2 Architecture** → thứ tự layer (vd: Controller → Facade → Service → Repository), quy tắc kiến trúc — *service overlay thắng*
352
- - **§2 Package Layout** → **base package** (vd `vn.edupia.{service}`) + **chiến lược đặt package** (by-layer / by-feature) + nơi code một domain sống. Đây là **quy ước đặt code trên đĩa**, PHẢI enforce khi sinh code. Nếu §2 chỉ nêu base package + thứ tự layer mà **không** nói tới sub-package theo feature → hiểu là **by-layer**: các layer đặt **TRỰC TIẾP** dưới base package (vd `vn.edupia.{service}.service`, `.repository`); feature/UC/prd-slug **KHÔNG** thành sub-package, chỉ phân biệt ở **tên class**. Lưu `code_base_package` + `package_strategy` — *service overlay thắng*.
353
- - **§3 Coding Standards** → quy tắc đặt tên (class, method), kiểu response wrapper, pattern bị cấm — *service overlay thắng*
354
- - **§5 Error Handling** → kiểu exception, mapping HTTP status code, tên class not-found exception — *service overlay thắng*
355
- - **§7 Git Conventions** → pattern đặt tên branch, format commit message — *lấy theo root trừ khi service định nghĩa lại*
120
+ **Có bất thường** → thêm một dòng cho **mỗi** trạng thái, nặng nhất lên đầu:
356
121
 
357
- **Quy tắc phân giải:**
358
- - Nếu cả hai tầng tồn tại → merge như trên; ghi `claude_md_source = root + {service_root}`.
359
- - Nếu chỉ service overlay (không root CLAUDE.md) → dùng file service một mình; `claude_md_source = {service_root}`.
360
- - Nếu `service_root` được set nhưng `{service_root}/CLAUDE.md` **thiếu** → fallback về root CLAUDE.md và gắn cờ ⚠️ trong recap Bước 7 (service không định nghĩa kiến trúc/coding-standards việc sinh code sẽ dùng mặc định umbrella, có thể sai stack).
361
- - Nếu cả hai đều không tồn tại ghi nhận CLAUDE.md thiếu và tiếp tục chỉ với dữ liệu từ project-context.yaml.
122
+ ```
123
+ CHECKPOINT
124
+ 🔴 Service : unresolved {lý do context-loader đã ghi}
125
+ ⚠️ CLAUDE.md: service overlay THIẾUdùng root (code sinh ra có thể sai stack)
126
+ ⚠️ Target : resolve bằng wildcard {n} file khớp, chọn {file}
127
+ ⚠️ Module : not configured — code sinh ra sẽ dùng default
128
+ Status : PARTIAL — thiếu: {danh sách}
129
+ Target : {resolved file path}
130
+ Tiếp tục? (Y/N)
131
+ ```
362
132
 
363
- ---
133
+ ### 3c — Cờ nào bật, cờ nào KHÔNG
364
134
 
365
- ## Bước 4 [SAFETY] Nạp quy tắc bảo vệ dữ liệu
135
+ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader đã tính rồi** — không phát minh
136
+ điều kiện mới, chỉ mang thứ đang bị giấu lên chỗ người dùng phải quyết định:
366
137
 
367
- Đọc `.agent/rules/data-protection.md` (hoặc `rules/data-protection.md` từ bản cài đặt framework).
138
+ | Bật cờ khi | Nguồn | Mức |
139
+ |---|---|:---:|
140
+ | `active_service = unresolved` | context-loader Bước 2b/2c/Fallback | 🔴 |
141
+ | `Status = MINIMAL` | recap Bước 7 | 🔴 |
142
+ | `Status = PARTIAL` | recap Bước 7 | ⚠️ |
143
+ | CLAUDE.md thiếu, hoặc service overlay thiếu | context-loader Bước 3 | ⚠️ |
144
+ | Target resolve qua wildcard, hoặc nhiều file khớp mà lệnh tự chọn | Bước 1 ở trên | ⚠️ |
145
+ | `module` không cấu hình | recap Bước 7 | ⚠️ |
368
146
 
369
- Lưu các pattern file nhạy cảm — bạn **tuyệt đối không** đọc, ghi, hiển thị, hay tham chiếu nội dung từ các file khớp những pattern đó trong suốt cả phiên.
147
+ **KHÔNG bật cờ cho:** `Lessons: chưa có` · `Dict: missing` · `Entities: missing`. Đó
148
+ *"dự án chưa điền"*, không phải *"có gì đó sai"* — chúng ở lại trong recap.
370
149
 
371
- Nếu cả hai file đều không tồn tại áp dụng mặc định built-in: không bao giờ truy cập `.env*`, `*.key`, `*.pem`, `*secret*`, `*password*`, `*credential*`.
150
+ > **Nguyên tắc một câu:** cờ dành cho thứ **framework không chắc chắn hoặc đã phải đoán**,
151
+ > không dành cho thứ **người dùng chưa làm**. Đẩy hết mọi thứ lên thì CHECKPOINT lại đầy như
152
+ > cũ, và ta quay về đúng chỗ xuất phát: một cổng luôn giống nhau thì bị lướt qua.
372
153
 
373
- ---
154
+ ### 3d — Chờ trả lời
374
155
 
375
- ## Bước 5 [DOMAIN] Nạp Business Dictionary (có điều kiện)
156
+ - "Y" tiếp tục sang các bước riêng của lệnh.
157
+ - "N" → dừng, hỏi người dùng muốn thay đổi gì.
158
+ - Có `--yes` và mức *chặn thường* → coi như "Y", **nhưng vẫn IN khối CHECKPOINT** nếu có cờ
159
+ 🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
376
160
 
377
- Kiểm tra file business dictionary có tồn tại không (dùng `paths.business_dictionary` đã phân giải ở Bước 1).
378
161
 
379
- Nếu tồn tại, đọc trích xuất:
380
- - **Canonical Terms** → danh sách đầy đủ các thuật ngữ chuẩn và định nghĩa
381
- - **Banned Terms** → danh sách đầy đủ các thuật ngữ bị cấm và bản thay thế chuẩn
382
- - **Status / Enum Registry** → các giá trị enum được phép theo từng entity
162
+ *Lưu ý: Lệnh này **không có target file đơn** — nó quét nhiều thư mục, nên gate Bước 1 không phân giải file. Phạm vi audit do **Step 0-A** phân giải từ `$ARGUMENTS` (`--domain` / `--prd` / `--uc`; không có cờ nào = toàn bộ).*
383
163
 
384
- Lưu danh sách banned term để **thực thi chủ động** suốt phiên làm việc của lệnh:
385
- - Khi sinh bất kỳ văn bản nào (PRD, BDD, comment code, tech docs), kiểm tra không có banned term nào xuất hiện
386
- - Tự động thay banned term bằng bản chuẩn tương đương
164
+ ## Context
165
+ **BẮT BUỘC đọc `.agent/steps/context-loader.md` thực thi TOÀN BỘ quy trình trong đó**,
166
+ rồi mới tiếp tục phần bên dưới.
387
167
 
388
- Nếu file không tồn tại bỏ qua âm thầm. Không cảnh báo hay chặn.
168
+ Bỏ qua bước này thì `{paths.*}`, `{tech_stack.*}`, `{conventions.*}`, guardrail từ
169
+ `project-lessons`, và routing service (chế độ umbrella) đều **chưa được phân giải** — mọi
170
+ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
389
171
 
390
172
  ---
391
173
 
392
- ## Bước 6 — [DOMAIN] Nạp Core Entities (có điều kiện)
174
+ ## Process
393
175
 
394
- Kiểm tra file core entities tồn tại tại `paths.core_entities` không (đã phân giải ở Bước 1).
395
- Path mặc định: `specs/domain-knowledge/core-entities.md`.
176
+ ### Step 0-A Phân giải **phạm vi audit** *(chạy TRƯỚC Step 0)*
396
177
 
397
- Nếu tồn tại, đọc lưu:
398
- - **Entity catalog** → với mỗi entity: tên, mục đích, service sở hữu, các field chính (tên + kiểu), business invariant, và quan hệ
399
- - **Field name registry** → tên field chuẩn dùng trong code và tài liệu được sinh ra
400
- - **Relationship map** → cách các entity liên hệ với nhau (1:N, N:N, embedded, v.v.)
178
+ *Contract: `bin/trace-schema.json` `gate.report_root_keys`. Đây là đầu PRODUCER của một sợi dây mà đầu CONSUMER (`gate-trace`) đã có sẵn từ trước.*
401
179
 
402
- **Cách dùng catalog này:**
403
- - Khi sinh code: dùng tên field, kiểu, và quan hệ định nghĩa ở đây — KHÔNG suy đoán từ code có sẵn
404
- - Khi sinh PRD/BDD: tham chiếu tên entity từ catalog này để nhất quán
405
- - Khi sinh tech-docs: dùng catalog này làm nguồn chân lý cho định nghĩa entity
180
+ Parse `$ARGUMENTS` (đã tách các cờ khác ở gate Bước 1):
406
181
 
407
- Nếu file không tồn tại bỏ qua âm thầm.
182
+ | Cờ | `scope.kind` | `scope.value` | Hẹp lại những gì |
183
+ |---|---|---|---|
184
+ | *(không có)* | `all` | `"all"` | Không hẹp — audit toàn bộ |
185
+ | `--domain {domain}` | `domain` | tên domain | Chỉ sổ + spec dưới `{domain}/` |
186
+ | `--prd {TICKET-ID}` | `prd` | TICKET-ID | Chỉ **một** feature-package (phân giải `{domain}/{prd-slug}` từ TICKET-ID) |
187
+ | `--uc {UC-ID}` | `uc` | UC-ID | Chỉ **một** UC — **mọi platform của nó** (`{UC-ID}-*.tsv`) |
408
188
 
409
- ---
189
+ Nhiều cờ scope cùng lúc → **DỪNG**, báo lỗi: chúng loại trừ nhau, và tự ý ưu tiên một cái là hẹp phạm vi mà người dùng không biết.
410
190
 
411
- ## Bước 6.5 [PLATFORM] Suy ra active_moduleplatform_type
191
+ `--prd`/`--uc` không phân giải được về một package/UC có thật → **DỪNG** và liệt kê ứng viên. **KHÔNG** âm thầm rơi về `all` (chạy toàn bộ khi người ta xin một phần là đốt 30 phút không ai muốn) **KHÔNG** âm thầm audit rỗng (báo cáo "sạch" trên 0 row).
412
192
 
413
- Dùng `tech_stack.module` đã nạp ở Bước 1, suy ra và lưu hai biến để mọi lệnh phía sau dùng:
193
+ Lưu `scope` mọi step sau dùng nó:
414
194
 
415
- ```
416
- active_module = tech_stack.module (vd: "java-spring", "react", "flutter")
417
- ```
418
-
419
- | `platform_type` | Modules |
195
+ | Step | Hẹp thế nào |
420
196
  |---|---|
421
- | `backend` | `java-spring`, `golang`, `dotnet`, `php-laravel`, `context-engineering` |
422
- | `web-frontend` | `react`, `nextjs`, `vue`, `nuxt`, `angular`, `phaser-game` |
423
- | `mobile` | `flutter`, `react-native`, `ios-swiftui`, `android-compose` |
424
-
425
- Nếu `tech_stack.module` rỗng hoặc không nhận diện được → set `platform_type = "unknown"` gắn cờ ⚠️ trong recap Bước 7.
426
-
427
- Hai biến này (`active_module`, `platform_type`) là nguồn chuẩn cho mọi logic rẽ nhánh trong các lệnh cần hành vi riêng theo platform (dev-gen-test, debug, fix-bug, dev-smoke-test).
428
-
429
- ---
430
-
431
- ## Bước 6.7 — [GUARDRAILS] Nạp Project Lessons (có điều kiện)
432
-
433
- *Các lỗi tích luỹ mà AI không được lặp lại trong dự án này. Chúng được bổ sung dần qua `/learn`
434
- hoặc được chấp nhận trong `/review-code`, `/fix-bug`, `/debug`.*
435
-
436
- Phân giải path file lessons:
437
- - Dùng `paths.lessons_file` nếu được set (có thể bị service override ở chế độ umbrella, Bước 1.6)
438
- - Else mặc định `specs/domain-knowledge/lessons-learned.md`
439
- - Ở chế độ umbrella/service (khi `service_root` được set), nếu `paths.lessons_file` chưa set, mặc định `{service_root}/.agent/project-lessons.md`
440
-
441
- Nếu file tồn tại, đọc và lưu TẤT CẢ lesson làm **GUARDRAIL ĐANG HOẠT ĐỘNG** cho phiên:
442
- - Coi **Rule** của mỗi lesson là ràng buộc cứng — cùng mức ưu tiên với coding standards trong CLAUDE.md (Bước 3).
443
- - Trước khi sinh hoặc sửa bất kỳ artifact nào (PRD, BDD, tech-doc, code, test), đối chiếu output với mọi lesson có `category` khớp lệnh hiện tại VÀ `scope` khớp target (domain / file).
444
- - Nếu output sinh ra vi phạm một lesson → sửa **trước khi** trình bày, và ghi rõ lesson nào (`L-NNN`) đã được áp dụng.
445
-
446
- Nếu file không tồn tại → bỏ qua âm thầm (chưa có lesson nào được ghi nhận).
197
+ | Step 0 / Step 1 | `all_trace_dirs` giữ nguyên, nhưng chỉ đọc TSV **khớp scope**: `{trace_dir}/{domain}/**` · `{trace_dir}/{domain}/{prd-slug}/**` · `{trace_dir}/**/{UC-ID}-*.tsv` |
198
+ | Step 1.0 (lint) | truyền `--trace` như **lint luôn chạy toàn bộ**. Sổ hỏng ở domain khác vẫn là sổ hỏng, và lint rẻ (không LLM) |
199
+ | Step 2b · 3.9 · 4 · 5* · 7 | chỉ các PRD/UC trong scope |
200
+ | Step 6 · 6b | chỉ ghi lại TSV + mốc của phần trong scope |
201
+ | Step 8 | ghi `scope` **và** `domain` vào biên bản (xem dưới) |
447
202
 
448
- ---
449
-
450
- ## Bước 7 — [RECAP] Working Memory Recap (chống lost-in-middle)
451
-
452
- Sau khi nạp toàn bộ context, tổng hợp và xuất một khối tóm tắt gọn.
453
- Recap này đảm bảo các sự thật quan trọng nhất được nêu ở CUỐI quá trình nạp context
454
- (hiệu ứng recency — tươi mới nhất trong bộ nhớ làm việc khi bắt đầu task).
455
-
456
- Xuất đúng khối này:
203
+ **In phạm vi ngay đầu run**, trước khi làm gì:
457
204
  ```
458
- [CTX LOADED]
459
- Stack : {language} / {framework} / {database}
460
- Platform : {active_module} ({platform_type})
461
- Layers : {thứ tự layer từ CLAUDE.md §2 đã merge, vd: Controller → Facade → Service → Repository}
462
- Package : {code_base_package}.{layer} · {by-layer | by-feature} ← feature/UC → tên class, KHÔNG thành package (nếu by-layer)
463
- CLAUDE.md : {root + {service_root} | chỉ {service_root} | chỉ root | ⚠️ service overlay THIẾU — dùng root | missing}
464
- Ticket : {ticket_prefix}-
465
- Dict : {loaded — N canonical terms, M banned terms | missing}
466
- Entities : {loaded — EntityA, EntityB, EntityC | missing}
467
- Lessons : {loaded — N guardrails | chưa có}
468
- Platform : {active_platform: system | web | app | — nếu chưa xác định}
469
- Service : {active_service} ({active_service_module}) [← domain{/platform}{/prd_slug} nếu route qua by_prd_slug] | multi (map-theo-platform hoặc map-theo-prd_slug, chốt khi target đủ platform/prd_slug) | single-service
470
- Svc Root : {service_root} — đã nạp conventions + trace_dir từ config service | —
471
- Status : {FULL | PARTIAL — thiếu: CLAUDE.md / business-dict / core-entities | MINIMAL}
205
+ Phạm vi: {all | domain={d} | prd={TICKET-ID} | uc={UC-ID}}
206
+ {n} PRD · {m} UC · {k} sổ → {ước lượng: toàn bộ repo | một phần}
472
207
  ```
473
-
474
- Nếu bất kỳ file CRITICAL nào thiếu (CLAUDE.md), gắn cờ rõ ràng để người dùng quyết định có tiếp tục hay không.
475
-
476
- ---
477
-
478
- ## Hoàn tất nạp Context
479
-
480
- Sau khi hoàn thành tất cả các bước, bạn đã nạp:
481
- - Định danh dự án, tech stack, convention module
482
- - Quy tắc kiến trúc thứ tự layer ← **[CRITICAL giữ trong bộ nhớ làm việc]**
483
- - Coding standards quy tắc đặt tên ← **[CRITICAL giữ trong bộ nhớ làm việc]**
484
- - Quy tắc bảo vệ dữ liệu (pattern file nhạy cảm không bao giờ truy cập)
485
- - Quy tắc thuật ngữ kèm danh sách banned term ← **[DOMAIN — áp dụng cho mọi từ được sinh ra]**
486
- - Entity catalog (tên field, kiểu, invariant) ← **[DOMAINdùng khi sinh code]**
487
- - Toàn bộ path đã cấu hình
488
-
489
- Tiếp tục sang bước kế tiếp của lệnh đang gọi.
490
-
208
+ *Không có dòng này thì người dùng không biết mình vừa gọi một lệnh cỡ nào — và đây là lệnh đắt nhất trong 33 lệnh (~33k token chỉ dẫn trước khi mở artifact nào).*
209
+
210
+ > **Vì sao step này là "nối dây", không phải "thêm tính năng" (GAPS-v4 G57).**
211
+ > Trước nó, **năm** chỗ trong lệnh này mô tả một *"domain argument"* / *"domain filter"* **không tồn
212
+ > tại**: ghi chú Gate (*"target là một tên domain hoặc UC-ID cụ thể từ `$ARGUMENTS`"*) · Step 1 đọc TSV
213
+ > *"khớp domain target"* · schema biên bản `"<domain argument, or 'all' if no filter>"` · Step 8
214
+ > *"nếu có domain filter, chỉ gồm các PRD đó"* · dòng `trace-history` mang field `domain`.
215
+ > `gate-trace` **đã** đọc `report.domain` rồi **chặn PR** nếu nó khác `all`.
216
+ >
217
+ > Nhưng Step 0 đặt `all_trace_dirs` = toàn bộ ** điều kiện**, chỗ duy nhất parse `$ARGUMENTS` là
218
+ > Step 5e cho hai cờ `--realign`. Nên ô đó **luôn** ghi `all`, phần kiểm của gate **chưa bao giờ
219
+ > chạy một lần nào**.
220
+ >
221
+ > Đây đúng hình dạng **R1 fail build vì nó** *"field consumer mà không có producer"*, ca
222
+ > `@trace.sc_version` (3 consumer, 0 producer, sống qua nhiều version không ai bắt được). Nó sống
223
+ > được vì R1 chỉ canh field trong **sổ TSV** và tag trong **code**, không canh key ở cấp gốc **biên
224
+ > bản JSON**. `self-check` **R9(h)** giờ canh cả hai đầu.
225
+ >
226
+ > Và ghi chú Gate còn **tệ hơn im lặng** — nó gây nhầm: một agent đọc *"target là một tên domain hoặc
227
+ > UC-ID"* sẽ **tin rằng** scoping hoạt động.
491
228
 
492
229
  ---
493
-
494
- ## Process
495
-
496
230
  ### Step 0 — Umbrella Mode Detection
497
231
 
498
232
  Kiểm tra mảng `services` có tồn tại trong `project-context.yaml` không.
@@ -517,6 +251,44 @@ Kiểm tra mảng `services` có tồn tại trong `project-context.yaml` không
517
251
 
518
252
  ---
519
253
 
254
+ ### Step 1.0 — Lint sổ trace TRƯỚC khi đọc *(bắt buộc, không bỏ qua được)*
255
+
256
+ Chạy checker xác định trên mọi trace dir đã phân giải ở Step 0:
257
+
258
+ ```bash
259
+ npx @educa-corp/sdd-framework --lint-trace --trace {all_trace_dirs, ngăn cách bởi dấu phẩy} --specs {paths.specs_dir}
260
+ ```
261
+
262
+ *`bin/` sống trong package npm, không được cài vào project — nên `npx` là đường duy nhất. Không có mạng / npx fail → **bỏ qua step này**, in `⚠️ Chưa lint được sổ trace (npx không khả dụng) — kết quả dưới đây chưa được kiểm cấu trúc` vào report, rồi tiếp Step 1. Đừng để nó chặn cả lệnh.*
263
+
264
+ **Exit 0 → tiếp Step 1.**
265
+
266
+ **Exit 1 → DỪNG NGAY.** Đừng nạp, đừng tính `status`, đừng ghi lại gì:
267
+
268
+ ```
269
+ 🔴 SỔ TRACE HỎNG — không phán trạng thái trên dữ liệu này.
270
+
271
+ {nguyên văn output của lint-trace}
272
+
273
+ Vì sao dừng thay vì cố đọc tiếp: Step 3 tính lại `status` rồi Step 6 GHI NGƯỢC
274
+ vào TSV. Chạy tiếp trên một row đã lệch cột sẽ nướng cái lệch đó vào sổ vĩnh viễn —
275
+ và sổ trace là dữ liệu KHÔNG regenerate được.
276
+
277
+ Sửa:
278
+ 1. Xem lần ghi nào làm hỏng : git log -p {file}
279
+ 2. Sửa file (thường là thêm/bớt một dấu tab, hoặc giữ cả hai row sau merge)
280
+ 3. Kiểm lại : npx @educa-corp/sdd-framework --lint-trace
281
+ 4. Rồi chạy lại /validate-traces
282
+ ```
283
+
284
+ > **Vì sao step này tồn tại (G38):** `bin/self-check.js` canh **contract** — nó đọc file lệnh
285
+ > và kiểm "lệnh có gọi đúng tên cột không". Nó không bao giờ mở một `.tsv` thật. Trong khi sổ
286
+ > 24 cột được ghi **bằng tay**, hàng chục lần mỗi feature. Một dấu tab thiếu ở ô 17 dồn mọi ô
287
+ > sau đó sang trái một bậc — ô 21 `status` nhận một ngày tháng — và **trước step này không gì
288
+ > báo lỗi**: lệnh đọc tiếp, in ra số, số chảy vào `trace-report.json` rồi vào dashboard.
289
+
290
+ ---
291
+
520
292
  ### Step 1 — Nạp dữ liệu TSV
521
293
 
522
294
  **Umbrella mode:** đọc tất cả file `{trace_dir}/**/*.tsv` từ mọi dir trong `all_trace_dirs`. Với mỗi TSV, gắn tag row với tên service gốc.
@@ -539,6 +311,29 @@ Nếu version SC trong `.feature` khác `spec_ver` của `.tsv` → cập nhật
539
311
 
540
312
  Cũng phát hiện SC có trong `.feature` (platform đó) nhưng thiếu trong `.tsv` → thêm row mới với `status: UNTRACKED`. *(sc_id trùng số giữa các platform là 2 scenario khác nhau → mỗi sổ platform giữ tập SC riêng, không dedupe chéo platform.)*
541
313
 
314
+ ### Step 2c — Phân giải lại `service` từ config hiện tại *(G51)*
315
+
316
+ *Cùng tinh thần Step 2 với `spec_ver`: cột TSV là **cache**, `services:` trong `project-context.yaml`
317
+ là **nguồn**. Mỗi lần chạy, đối chiếu lại.*
318
+
319
+ Với mỗi row có `service` ∈ (`unrouted`, `unresolved`), tra lại `services:` theo
320
+ `domain` + `platform` (từ tên file sổ) + `prd_slug`:
321
+
322
+ | Kết quả tra | Hành động |
323
+ |---|---|
324
+ | Giờ **khớp** một entry | **Cập nhật `service` = path đó** (in memory, ghi lại ở Step 6). Không cần chạy lại `/generate-bdd`. |
325
+ | Vẫn không khớp | Giữ `unrouted` → gắn cờ 🟠 `SERVICE_UNROUTED` |
326
+ | Config vẫn sai cấu trúc | Giữ `unresolved` → cùng cờ, nhưng lý do khác (bug config, không phải chờ quyết) |
327
+
328
+ > **Vì sao lệnh này được ghi một cột do `/generate-bdd` sở hữu:** đây là **ngoại lệ có chủ ý** với luật
329
+ > *"mỗi cột một chủ"* (`rules/workflow.md` §Trace Contract), và nó **không vi phạm tinh thần** của luật:
330
+ > lệnh này **không ghi một giá trị mới** — nó chỉ **phân giải một placeholder mà `/generate-bdd` đã cố ý
331
+ > để lại**. Khai tường minh trong `bin/trace-schema.json`: `service.written_by = [generate-bdd, validate-traces]`.
332
+ >
333
+ > Đây là thứ làm sổ **tự lành**: architect thêm mapping → lần `/validate-traces` kế tiếp nâng
334
+ > `unrouted` → path. Không sửa tay, không sinh lại BDD. Không có bước này thì `unrouted` **đọng lại
335
+ > vĩnh viễn** — đúng bệnh `TBD` mà G1/G28 đã chỉ ra.
336
+
542
337
  ### Step 2b — Reverse audit (bắt tag mồ côi)
543
338
 
544
339
  *Step 2 đi chiều **spec → code** (mỗi row TSV, SC đó implement tới đâu). Step này đi **chiều ngược** — bắt lớp lỗi mà Step 2 cấu trúc không thể thấy: code trỏ vào một scenario **không còn tồn tại**. Xảy ra khi gen lại BDD làm một SC biến mất (gộp / đổi số / xoá) trong khi code implement nó vẫn nằm đó, vẫn được caller gọi.*
@@ -573,6 +368,84 @@ Không tìm thấy tag mồ côi nào → bỏ qua im lặng.
573
368
 
574
369
  > **Vì sao DRIFT xét trước GAP:** một scenario đã có code, chưa test, **và** spec vừa drift phải hiện `DRIFT` (không phải `GAP`) — vì `generate-code` xử `GAP` = "skip codegen, chạy /dev-gen-test" còn `DRIFT` = "regenerate". Nếu GAP thắng, code lỗi thời bị bỏ qua và test lại sinh trên code cũ. UNTRACKED vẫn phải là Rule 1 để scenario chưa code (gen_ver `—`) không lọt vào DRIFT.
575
370
 
371
+ ### Step 3.9 — Phát hiện sửa spec ngoài đường chính thức *(chạy TRƯỚC Step 4)*
372
+
373
+ *Contract: `bin/trace-schema.json` → `spec_edit_detection`. Cờ: `PRD_UNTRACKED_EDIT`.*
374
+
375
+ **Vì sao bước này đứng TRƯỚC Step 4.** Step 4 và mọi bước sau nó đều dựa trên một giả định
376
+ **chưa được kiểm**: *nhãn `Version` của PRD phản ánh đúng nội dung hiện tại của nó.* Nếu ai sửa nội
377
+ dung mà không bump nhãn thì giả định đó sai, và Step 4 sẽ phán *"version khớp ⇒ sạch"* trên một tài
378
+ liệu đã đổi. Kiểm cấu trúc drift trên một nhãn không còn đúng thì cũng vô nghĩa như G1 kiểm cấu trúc
379
+ một quyển sổ sắp mất — nên hỏi trước.
380
+
381
+ **Điểm mù mà nó bịt (GAPS-v4 G54).** Toàn bộ lưới an toàn của framework so **nhãn version**, không so
382
+ **nội dung** — không có một content hash nào ở đâu. Nên một PRD bị sửa tay là điểm mù **tuyệt đối**:
383
+ Step 4 thấy `PRD Version == prd_version` ⇒ sạch · `gate-trace` thấy report khớp sổ ⇒ PASS ·
384
+ `require-fresh-audit` thấy PR không chạm tag ⇒ không đòi audit. **Cả ba tầng xanh, và cả ba đúng theo
385
+ định nghĩa của chính chúng.**
386
+
387
+ #### Đọc mốc của lần audit trước
388
+
389
+ Đọc khối **`spec_baseline`** trong `trace-report.json` của lần chạy trước (path:
390
+ `{living_docs_dir}/trace-report.json`). Mỗi entry: `prd_path` · `sha_at_audit` · `version_at_audit`.
391
+
392
+ - Khối **vắng** (lần audit đầu, hoặc report sinh bởi version cũ hơn) → **bỏ qua so sánh**, chỉ **ghi
393
+ mốc mới** ở Step 6b. In một dòng: `ⓘ spec_baseline: lần đầu ghi mốc — check sửa-ngoài-đường bắt đầu có hiệu lực từ lần chạy sau.`
394
+
395
+ #### So — hai nguồn bằng chứng, cần cả hai
396
+
397
+ Với mỗi file PRD trong phạm vi, ở repo chứa `{paths.specs_dir}`:
398
+
399
+ | Nguồn | Lệnh | Bắt ca nào |
400
+ |---|---|---|
401
+ | **git diff** | `git -C {specs repo} diff --name-only {sha_at_audit}..HEAD -- {prd_path}` | sửa **đã commit** |
402
+ | **git status** | `git -C {specs repo} status --porcelain -- {prd_path}` | sửa **CHƯA commit** — ca thường gặp nhất, vì PO đang gõ |
403
+
404
+ Thiếu nguồn thứ hai là mù với cả một lớp ca: PO sửa xong, chưa commit, chạy audit — và audit nói sạch.
405
+
406
+ #### Phán
407
+
408
+ | Nội dung đổi? | `Version` hiện tại vs `version_at_audit` | Kết luận |
409
+ |:---:|---|---|
410
+ | **có** | **BẰNG NHAU** | 🔴 **`PRD_UNTRACKED_EDIT`** — có người sửa ngoài `/generate-prd` · `/extend-prd` · `/amend-prd` · `/refine-prd` · `/review-context` |
411
+ | có | khác | ✅ không cờ — đã đi đường chính thức. Step 4 tiếp quản bình thường |
412
+ | không | bất kỳ | ✅ không cờ |
413
+
414
+ **Không có báo oan:** ca duy nhất bật cờ là *"đổi nội dung, giữ nguyên nhãn"*. Sửa **và** bump
415
+ version thì `version != version_at_audit` ⇒ im.
416
+
417
+ #### Ca không kiểm được → nói rõ là đang mù
418
+
419
+ Không phải git repo · `sha_at_audit` không còn (history bị rewrite) · `git` không khả dụng →
420
+ **bỏ qua** check này và in:
421
+ ```
422
+ ⚠️ Chưa kiểm được sửa-ngoài-đường cho {n} PRD ({lý do}) — điểm mù G54 đang MỞ ở các file này.
423
+ ```
424
+ **KHÔNG** bịa cờ, và **KHÔNG** im lặng. Im lặng là lựa chọn rẻ nhất và tệ nhất trong ba.
425
+
426
+ #### Đường ra — tự lành, cố ý KHÔNG có lệnh escape
427
+
428
+ Cách sửa đúng là **bump version + ghi một row changelog nêu UC bị ảnh hưởng** — tức đúng việc
429
+ `/amend-prd` làm hộ. Làm xong thì `version != version_at_audit` ⇒ cờ tự tắt, và logic `PRD_DRIFT`
430
+ bình thường tiếp quản (đúng, vì nội dung có đổi thật).
431
+
432
+ > **Vì sao KHÔNG có `--accept-edit`:** thêm nó là thêm một đường **dán nhãn lên thay đổi chưa ai
433
+ > xem** — đúng cái sai mà ba rào của `--realign` (Step 5e) tồn tại để chặn.
434
+
435
+ #### Vì sao cờ này KHÔNG nằm trong `gate.blocking`
436
+
437
+ Quyết định có chủ ý, ghi ở `spec_edit_detection.$comment`:
438
+
439
+ | | |
440
+ |---|---|
441
+ | `gate.blocking` nghĩa hẹp là **code đang hỏng** | Cờ này nói về **spec**; code có thể đang hoàn toàn đúng. R9(e) tồn tại để giữ đúng ranh giới đó |
442
+ | Nợ tồn khi mới bật | Mọi project đang chạy đều đã có PRD sửa tay ⇒ 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 R9(e) được viết ra để chặn, chỉ đến bằng một cửa khác |
443
+ | Nhưng nó vẫn 🔴 | In mỗi lần chạy, có khối riêng trong report + counter trong `summary` — đủ để thấy, không đủ để làm tắt cổng |
444
+
445
+ Team nào đã dọn sạch nợ tồn thì tự thêm `prd_untracked_edit_count` vào `gate.blocking` (kèm `why`) —
446
+ `self-check` R13(e) canh việc đó.
447
+
448
+ ---
576
449
  ### Step 4 — PRD version drift check
577
450
 
578
451
  Với mỗi UC, so:
@@ -586,13 +459,43 @@ Nếu layer nào sau version PRD hiện tại → trích các changelog entry k
586
459
 
587
460
  *PRD là tài liệu **cấp feature** phủ nhiều UC, nhưng version của nó là **một scalar**. Nên thêm UC7 — không đụng một chữ nào của UC1–UC6 — vẫn làm cả 6 UC cũ lệch version. So version thuần thì cả 6 ăn cờ đỏ oan.*
588
461
 
589
- Đọc các row `# Change Log` của PRD **từ version của layer cũ nhất tới hiện tại**, trích tập UC/AC/BR được nêu. *(`/refine-prd` Phase 3 `/extend-prd` **bắt buộc** ghi thông tin này vào mỗi row — chính vì mục đích này. `/generate-bdd` Version Check đã dùng nó từ trước; ở đây chỉ là đọc cùng một dữ liệu.)*
462
+ Đọc các row `# Change Log` của PRD **từ version của layer cũ nhất tới hiện tại**. Mỗi row mang một `{changelog_scope}` — contract máy đọc, khai ở `bin/trace-schema.json` → `changelog_row_contract`; producer là `/refine-prd` Phase 3, `/extend-prd` Bước 6, `/review-context` Fix/Resume Phase 3, và **bắt buộc** ghi thông tin này chính vì mục đích ở đây. *(`/generate-bdd` Version Check đọc cùng dữ liệu.)*
463
+
464
+ **Dựng `affected_ucs` theo ba bước — KHÔNG bỏ bước 2:**
465
+
466
+ **1. Tách mệnh đề.** Mỗi row `{changelog_scope}` gồm các mệnh đề ngăn bằng `;`, mỗi mệnh đề mở đầu bằng đơn vị sở hữu: `{UC-ID}: {mô tả}` hoặc `PRD-global: {mô tả}`.
467
+
468
+ **2. Chuẩn hoá về UC — phép phân giải `BR/AC → UC sở hữu`.** Trong mỗi mệnh đề, ngoài UC-ID mở đầu còn có thể có BR/AC được nêu. Đưa **mọi** BR/AC về UC sở hữu **trước khi** so:
469
+
470
+ | Gặp | Phân giải thành |
471
+ |---|---|
472
+ | `BR{n}` | UC có `BR{n}` trong **bảng Business Rule** của nó (PRD §3) |
473
+ | `AC{n}` | UC có `AC{n}` ở dòng **`**AC liên quan:**`** của nó (PRD §3) |
474
+ | `PRD-global` | **không** thuộc UC nào — không thêm gì vào `affected_ucs` |
475
+ | BR/AC **không** phân giải được về UC nào *(ID đã bị xoá, hoặc PRD lệch cấu trúc)* | coi **cả row** là **mơ hồ** → hàng 3 dưới đây. **KHÔNG** bỏ qua im lặng mệnh đề đó |
476
+
477
+ *Không phát sinh I/O: Step 4 đã mở file PRD này ở đầu bước.*
478
+
479
+ **3. Phân loại từng UC** *(first-match-wins)*:
590
480
 
591
481
  | Điều kiện | Cờ | Hành động |
592
482
  |---|---|---|
593
- | UC này **có** trong tập bị ảnh hưởng | `PRD_DRIFT` 🟠 | Cần regen thật theo bảng `drifted_layers` bên dưới |
594
- | UC này **không** trong tập, **và** mọi row changelog trong khoảng đều nêu rõ scope | `PRD_STALE_REF` | Nội dung không đổi, chỉ con trỏ version cũ. **KHÔNG** route regen — dùng `--realign-prd-version` |
595
- | **Bất kỳ** row nào trong khoảng hồ (không nêu UC/AC/BR) | `PRD_DRIFT` 🟠 cho **MỌI** UC | Không suy đoán được thì quét rộng |
483
+ | **Bất kỳ** row nào trong khoảng **mơ hồ** (không mệnh đề nào nêu được đơn vị sở hữu, hoặc bước 2 không phân giải được) | `PRD_DRIFT` 🟠 cho **MỌI** UC | Không suy đoán được thì quét rộng |
484
+ | UC này **có** trong `affected_ucs` | `PRD_DRIFT` 🟠 | Cần regen thật theo bảng `drifted_layers` bên dưới |
485
+ | UC này trong `affected_ucs` **CHỈ** qua (các) mệnh đề mang hậu tố **`[no-behavior]`** | `PRD_STALE_REF` | Producer đã **chứng minh** thay đổi không đổi hành vi (chỉ thêm/bỏ vỏ cấu trúc). Xem `changelog_row_contract.neutral_checks` |
486
+ | UC này **không** có trong `affected_ucs`, **và** mọi row trong khoảng đều nêu rõ scope | `PRD_STALE_REF` ⓘ | Nội dung không đổi, chỉ con trỏ version cũ. **KHÔNG** route regen — dùng `--realign-prd-version` |
487
+
488
+ > **Vì sao bước 2 là bắt buộc (G53).** Bản cũ viết *"trích tập UC/AC/BR được nêu"* rồi so bằng phép thử *"**UC** này có trong tập?"*. Hai câu đó **không khớp nhau**: tập chứa lẫn UC, AC và BR, nhưng phép thử chỉ hỏi về UC. Nên một row như `thêm UC7: AC12-AC14; sửa BR8` cho tập `{UC7, AC12-14, BR8}` — và **UC3, chủ sở hữu BR8, không có trong đó**.
489
+ >
490
+ > Row đó nêu rất nhiều ID nên **không** rơi vào hàng "mơ hồ". Kết quả: UC3 → ⓘ *"không phải lỗi"*, trong khi BR mà nó sở hữu vừa đổi hành vi. Và ⓘ **mở cửa** cho `--realign-prd-version` (rào an toàn của nó chỉ **từ chối khi UC là 🟠**) ⇒ nhãn `prd_version` bị dán lại ở cả TSV lẫn tag code ⇒ **cờ sạch vĩnh viễn trên thay đổi chưa ai implement**.
491
+ >
492
+ > Đây là G1 đúng nghĩa — cờ **im lặng** — chỉ khác là lần này có thêm một lệnh tự động đóng dấu lên nó.
493
+
494
+ > **Vì sao hàng "mơ hồ" lên ĐẦU bảng.** Nó là điều kiện **cấp row**, không phải cấp UC: một row mơ hồ làm mọi phán đoán per-UC trong khoảng đó vô giá trị. Xét nó sau các hàng kia thì một UC có thể được xếp ⓘ **trước khi** ta biết là không suy đoán được gì — và ⓘ là hạng mở cửa cho `--realign`.
495
+
496
+ > **Hàng "mơ hồ" là lưới an toàn — hỏng theo hướng an toàn.** Changelog viết ẩu thì ta mất tính năng *lọc*, KHÔNG mất tính năng *cảnh báo*. Đồng bộ với `generate-bdd` Version Check: *"changelog row không nêu rõ scope (mơ hồ) → khuyến nghị F (gen lại toàn bộ)"*.
497
+ >
498
+ > ⚠️ **Nhưng lưới an toàn KHÔNG phải cái cớ để producer ghi bừa.** Nó đúng khi **thiếu** thông tin; nó sai khi producer **có** thông tin mà không ghi. Đó là G52: `/review-context --fix` từng ghi cứng `Auto-fix: applied {N} findings` — 0 scope — trong khi findings YAML của nó có `uc_id` bắt buộc cho từng finding. Một sửa từ trong UC5 làm **cả 8 UC** ăn 🟠. `self-check` R12 giờ fail build nếu producer nào không nhắc `{changelog_scope}`.
596
499
 
597
500
  > **Hàng thứ ba là lưới an toàn — hỏng theo hướng an toàn.** Changelog viết ẩu thì ta mất tính năng *lọc*, KHÔNG mất tính năng *cảnh báo*. Đồng bộ với `generate-bdd` Version Check: *"changelog row không nêu rõ UC/AC/BR (mơ hồ) → khuyến nghị F (gen lại toàn bộ)"*.
598
501
  >
@@ -622,13 +525,18 @@ Skip cột nào chưa có revision đã lưu (`—`), hoặc cả UC chưa có t
622
525
 
623
526
  *Tech-doc có **đúng cùng hình dạng** với PRD: một doc **GỘP cấp PRD**, một `@trace.revision` chung cho nhiều UC. Chế độ **APPEND** (`generate-tech-docs` Bước 1) thêm UC mới vào doc đã có → revision bump → **mọi UC cũ lệch**, dù phần của chúng không đổi một dòng.*
624
527
 
625
- Đọc **§10 UC Coverage** và **Changelog** của tech-doc, trích tập UC row changelog trong khoảng revision đó nêu:
528
+ Đọc **§10 UC Coverage** và **Changelog** của tech-doc. Mỗi row Changelog mang một `{changelog_scope}` cùng contract với PRD (`changelog_row_contract`); producer là `/generate-tech-docs` Bước 1b, đơn vị "global" đây tên `doc-global` thay cho `PRD-global`.
529
+
530
+ Dựng `affected_ucs` **theo đúng ba bước của Step 4**, kể cả bước 2 (`BR/AC → UC sở hữu`) — tech-doc cũng nêu §/SC/BR trong mô tả, và một mệnh đề nêu `§5.9` mà bỏ UC thì UC đó rơi vào ⓘ y như ca BR8 ở Step 4. Rồi phân loại *(first-match-wins, cùng thứ tự)*:
626
531
 
627
532
  | Điều kiện | Cờ |
628
533
  |---|---|
629
- | UC này **có** trong tập bị ảnh hưởng | `TECHDOC_DRIFT` / `FE_TECHDOC_DRIFT` 🟠 |
630
- | UC này **không** có, changelog nêu rõ scope | `TECHDOC_STALE_REF` dùng `--realign-techdoc-revision` |
631
- | Row changelog hồ | 🟠 cho **mọi** UC (lưới an toàn) |
534
+ | **Bất kỳ** row nào trong khoảng **mơ hồ** (không nêu được đơn vị sở hữu, hoặc bước 2 không phân giải được) | 🟠 cho **MỌI** UC (lưới an toàn) |
535
+ | UC này **có** trong `affected_ucs` | `TECHDOC_DRIFT` / `FE_TECHDOC_DRIFT` 🟠 |
536
+ | UC này trong `affected_ucs` **CHỈ** qua mệnh đề mang hậu tố `[no-behavior]` | `TECHDOC_STALE_REF` ⓘ |
537
+ | UC này **không** có trong `affected_ucs`, mọi row nêu rõ scope | `TECHDOC_STALE_REF` ⓘ — dùng `--realign-techdoc-revision` |
538
+
539
+ > **`doc-global` là đường ra bình thường cho tech-doc, không phải ngoại lệ.** Sửa §11 Cross-cutting hay §2 kiến trúc chung thì không mệnh đề nào nêu UC ⇒ `affected_ucs` rỗng ⇒ mọi UC ở lại ⓘ. Nên `/generate-tech-docs` **không cần** dùng `[no-behavior]`; marker đó dành cho producer biết chính xác `check_id` của từng fix (`/review-context --fix`). Consumer vẫn phải **hiểu** marker vì cùng một hàng bảng phục vụ cả hai artifact.
632
540
 
633
541
  ### Step 5e — Realign mode *(chỉ chạy khi có flag)*
634
542
 
@@ -744,6 +652,22 @@ Với mỗi file `.tsv` đã xử lý: ghi `spec_ver`, `status`, `last_updated`
744
652
  Và **đồng bộ `prd_status` ← `| **Status** |`** của PRD tương ứng (`{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md`) — đối xứng với `uc_status`: PRD Metadata là nguồn-sự-thật về duyệt PRD. Không có bước này thì `prd_status` là **write-once** (chỉ `/generate-bdd` ghi một lần) và sẽ giữ `approved` vĩnh viễn sau khi `/refine-prd` hay `/review-context --fix` reset PRD về `draft`. *(Step 4 đã đọc file PRD này rồi — không phát sinh I/O.)*
745
653
  **Đừng** sửa `dev_selftest`/`dev_selftest_at` (do `/dev-run-test` sở hữu) hay `qc_status`/`qc_run_at`/`qc_owner`/`qc_blocked_by` (do `/qc-run-test` + `/report-bug` sở hữu); lệnh này chỉ đọc chúng cho report.
746
654
 
655
+ ### Step 6b — Ghi mốc `spec_baseline` cho lần audit sau
656
+
657
+ *Đây là **nửa GHI** của check ở Step 3.9. Thiếu nó thì cờ `PRD_UNTRACKED_EDIT` **không bao giờ bật** — và mọi rule khác vẫn ✅, vì R6 chỉ canh "giá trị enum có xuất hiện" và R7 chỉ canh "cờ có counter". `self-check` R13 canh đúng nửa này.*
658
+
659
+ Với **mỗi** file PRD trong phạm vi lần chạy này, ghi một entry vào khối `spec_baseline` của `trace-report.json` (Step 8):
660
+
661
+ | Key | Giá trị |
662
+ |---|---|
663
+ | `prd_path` | path tương đối tới file PRD trong specs repo |
664
+ | `sha_at_audit` | `git -C {specs repo} rev-parse HEAD` — commit tại thời điểm audit |
665
+ | `version_at_audit` | `\| **Version** \|` hiện tại của PRD |
666
+
667
+ Không phải git repo / `git` không khả dụng → ghi `sha_at_audit: null` và **vẫn ghi** `version_at_audit` (lần sau Step 3.9 sẽ báo đang mù, không im lặng).
668
+
669
+ > **Ghi mốc là thao tác cuối, sau khi đã phán xong.** Ghi trước thì một lần chạy bị ngắt giữa đường sẽ để lại mốc mới mà chưa phán gì — và lần sau so với mốc đó thì thay đổi trong khoảng đó **biến mất khỏi tầm quan sát vĩnh viễn**.
670
+
747
671
  ### Step 7 — Tính aggregate cho dashboard
748
672
 
749
673
  ```
@@ -768,6 +692,9 @@ fe_integrated = rows where fe_phase == integrated # FE đã wire adapter th
768
692
  # công việc CHƯA XONG dù status có thể đã là OK (có code + có test trên mock).
769
693
  orphaned_count = rows where status == ORPHANED # code còn, scenario đã bị xoá khỏi .feature (Step 2b/Rule 0)
770
694
  trace_orphan_count = số tag @trace.implements/@trace.verifies trỏ vào SC không tồn tại VÀ không có row TSV (Step 2b)
695
+ prd_untracked_edit_count = số file PRD có nội dung đổi kể từ `sha_at_audit` mà `Version` KHÔNG đổi
696
+ (Step 3.9). 🔴 — có người sửa ngoài đường chính thức, nên MỌI phán đoán version
697
+ của Step 4/5 trên file đó đang dựa vào một nhãn không còn đúng.
771
698
  prd_drift_count = số UC bị cờ PRD_DRIFT (lệch version VÀ changelog nêu UC này — Step 4)
772
699
  prd_stale_ref_count = số UC bị cờ PRD_STALE_REF (lệch version nhưng changelog KHÔNG nêu UC này —
773
700
  nội dung không đổi, chỉ con trỏ cũ). ⓘ không phải lỗi; sạch bằng --realign-prd-version
@@ -782,10 +709,28 @@ by_service = map {service → {total_scs, coded_scs, tested_scs, drift_cou
782
709
  # Trả lời câu số MỘT của dự án nhiều đội: "đội nào còn bao nhiêu việc".
783
710
  # Trước khi có cột 23, trace gộp (spec_source) KHÔNG mang thông tin sở hữu ở
784
711
  # cấp row nên câu này không trả lời được. Bỏ qua map này ở single-service.
712
+ by_platform = map {platform → {total_scs, coded_scs, tested_scs, drift_count}} — gom theo
713
+ platform (lấy từ TÊN FILE sổ `{UC-ID}-{platform}.tsv`, cùng nguồn với field
714
+ `platform` của mỗi scenario ở Step 8)
715
+ # CHỈ tạo ô cho platform THỰC SỰ có scenario — dự án chỉ có `web` thì chỉ một ô.
716
+ # Trả lời "web xong bao nhiêu %, system xong bao nhiêu %" — câu thường ngày khi
717
+ # làm FE và BE song song. Trước đó KHÔNG trả lời được từ `summary`: `by_service`
718
+ # là bảng chia nhóm DUY NHẤT, mà cột `service` là `—` ở mọi row của dự án
719
+ # single-service ⇒ nó gộp tất cả vào MỘT ô, và platform hoàn toàn vô hình.
720
+ # Đây là hình dạng của G33: dữ liệu có ở cấp row (G48 vừa thêm `platform` vào
721
+ # từng scenario) nhưng KHÔNG có ô tổng ⇒ dashboard chỉ đọc `summary` thì mù.
722
+ # Bắt dashboard tự duyệt prds[].ucs[].scenarios[] mà cộng lại chính là cái bẫy
723
+ # G33 đã chỉ ra: người viết dashboard đọc `summary`, thấy đủ, rồi tưởng xong.
724
+ # KHÔNG thay `by_service` — hai TRỤC khác nhau, cùng hữu ích ở umbrella nhiều đội.
785
725
  seam_unwired_count = số seam bị cờ SEAM_UNWIRED (hàng thật đã có nhưng consumer còn wire vào stub — Step 5b)
786
726
  seam_pending_count = số seam bị cờ SEAM_PENDING (owner UC chưa gen — ⓘ chưa phải lỗi; PM dùng để xếp thứ tự gen)
787
727
  stub_unresolved_count = số stub bị cờ STUB_UNRESOLVED (method còn trắng dù owner đã gen / có hàm song song — Step 5b)
788
728
  stub_pending_count = số stub bị cờ STUB_PENDING (owner BDD chưa gen — ⓘ chưa phải lỗi)
729
+ service_unrouted_count = số row có `service` ∈ (unrouted, unresolved) SAU khi đã phân giải lại ở
730
+ Step 2c. 🟠 KHÔNG chặn PR — "chưa ai quyết repo" là trạng thái hợp lệ ở feature
731
+ đầu tiên của một domain mới, không phải code hỏng. Nhưng phải NHÌN THẤY ĐƯỢC:
732
+ không có counter thì `unrouted` đọng lại vĩnh viễn và `by_service` có một ô rác
733
+ mà không ai để ý — đúng bệnh `TBD` (G51).
789
734
  # LUẬT (rules/workflow.md §Trace Contract): MỌI giá trị trong vocabularies.audit_flags phải có
790
735
  # một counter {flag_lowercase}_count ở đây VÀ trong summary của JSON. bin/self-check.js R7 ép
791
736
  # điều này — thiếu counter = cờ không quan sát được ở tầng tổng hợp, dashboard không thấy.
@@ -829,7 +774,11 @@ Schema:
829
774
  ```json
830
775
  {
831
776
  "generated_at": "<ISO-8601 timestamp>",
832
- "domain": "<domain argument, or 'all' if no filter>",
777
+ "scope": {
778
+ "kind": "all | domain | prd | uc",
779
+ "value": "<giá trị scope, hoặc 'all'>"
780
+ },
781
+ "domain": "<domain trong scope, hoặc 'all'>",
833
782
  "summary": {
834
783
  "total_prds": 0,
835
784
  "approved_prds": 0,
@@ -848,6 +797,7 @@ Schema:
848
797
  "fe_integrated": 0,
849
798
  "orphaned_count": 0,
850
799
  "trace_orphan_count": 0,
800
+ "prd_untracked_edit_count": 0,
851
801
  "prd_drift_count": 0,
852
802
  "prd_stale_ref_count": 0,
853
803
  "bdd_drift_count": 0,
@@ -861,6 +811,7 @@ Schema:
861
811
  "seam_pending_count": 0,
862
812
  "stub_unresolved_count": 0,
863
813
  "stub_pending_count": 0,
814
+ "service_unrouted_count": 0,
864
815
  "dev_selftest_passing": 0,
865
816
  "dev_selftest_failing": 0,
866
817
  "dev_selftest_not_run": 0,
@@ -875,6 +826,11 @@ Schema:
875
826
  "<service path, e.g. user-service>": {
876
827
  "total_scs": 0, "coded_scs": 0, "tested_scs": 0, "drift_count": 0
877
828
  }
829
+ },
830
+ "by_platform": {
831
+ "<web | app | system — CHỈ platform thực sự có scenario>": {
832
+ "total_scs": 0, "coded_scs": 0, "tested_scs": 0, "drift_count": 0
833
+ }
878
834
  }
879
835
  },
880
836
  "prds": [
@@ -894,6 +850,7 @@ Schema:
894
850
  "scenarios": [
895
851
  {
896
852
  "sc_id": "<e.g. PAY-UC01-SC1>",
853
+ "platform": "web | app | system",
897
854
  "sc_title": "<title>",
898
855
  "spec_ver": "<current version from .feature>",
899
856
  "gen_ver": "<version at codegen time>",
@@ -921,7 +878,24 @@ Schema:
921
878
  ]
922
879
  }
923
880
  ],
881
+ "spec_baseline": [
882
+ {
883
+ "prd_path": "<specs/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md>",
884
+ "sha_at_audit": "<git rev-parse HEAD của specs repo, hoặc null nếu không phải git repo>",
885
+ "version_at_audit": "<Version của PRD tại thời điểm audit>"
886
+ }
887
+ ],
924
888
  "issues": {
889
+ "prd_untracked_edit": [
890
+ {
891
+ "prd_path": "<specs/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md>",
892
+ "version": "<Version hiện tại — KHÔNG đổi kể từ lần audit>",
893
+ "sha_at_audit": "<commit lần audit trước>",
894
+ "evidence": "git diff | git status | cả hai",
895
+ "affected_ucs": ["<mọi UC của PRD này — không suy đoán được ai bị đụng>"],
896
+ "fix": "bump Version + ghi row changelog nêu UC bị ảnh hưởng (đó là việc /amend-prd làm hộ). KHÔNG có --accept-edit: dán nhãn lên thay đổi chưa ai xem là đúng cái rào của --realign tồn tại để chặn."
897
+ }
898
+ ],
925
899
  "drift": [
926
900
  {
927
901
  "sc_id": "<SC-ID>",
@@ -1060,6 +1034,16 @@ Schema:
1060
1034
  "fix": "Trỏ binding của <consumer_uc> sang <RealClass> (xoá/thay stub), build lại"
1061
1035
  }
1062
1036
  ],
1037
+ "service_unrouted": [
1038
+ {
1039
+ "sc_id": "<SC-ID>",
1040
+ "platform": "web | app | system",
1041
+ "domain": "<domain của PRD>",
1042
+ "value": "unrouted | unresolved",
1043
+ "reason": "<lý do context-loader đã ghi — vd: domain chưa có entry trong services:>",
1044
+ "fix": "thêm mapping cho domain <domain> vào services: của .agent/project-context.yaml, rồi chạy lại /validate-traces (nó tự nâng unrouted → path). KHÔNG cần sinh lại BDD."
1045
+ }
1046
+ ],
1063
1047
  "stub_unresolved": [
1064
1048
  {
1065
1049
  "artifact": "<ClassName#method>",
@@ -1075,6 +1059,11 @@ Schema:
1075
1059
  ```
1076
1060
 
1077
1061
  **Rules:**
1062
+ - **`by_platform` là bắt buộc** (nếu có ít nhất một scenario). Cùng hình dạng `by_service`, khoá **động** — một ô cho mỗi platform tìm thấy, không phải một ô cố định cho mỗi giá trị vocabulary. Thêm platform thứ tư vào vocabulary thì nó tự có ô, không cần sửa gì ở đây.
1063
+ > Đây là lý do `by_platform` **không cần** một rule `self-check` riêng: khác cờ audit (mỗi cờ cần một counter mang tên riêng, nên R7 phải canh từng cái), ở đây không có gì để lệch.
1064
+ - **`platform` (bắt buộc, mỗi scenario):** lấy từ **tên file sổ** `{UC-ID}-{platform}.tsv` — Step 2 đã đọc nó để tìm đúng `.feature`. Giá trị: `web` | `app` | `system`.
1065
+ > **Vì sao bắt buộc (G48):** `sc_id` **một mình không định danh được** một scenario. Chính Step 2 phát biểu điều đó: *"sc_id trùng số giữa các platform là 2 scenario khác nhau"*. Sổ TSV giải quyết bằng tên file; JSON thì làm phẳng mọi platform vào chung một cây `scenarios[]`, nên thiếu field này thì `AUTH-UC1-SC1` của web và của app **không phân biệt được** — panel hiện trùng lặp hoặc đè nhau, và không ai trả lời được *"SC1 của app xong chưa"*.
1066
+ > Bất đối xứng cũ: `issues.orphaned[]` và `issues.fe_techdoc_drift[]` **đã** mang `platform`; chỉ cây dữ liệu chính là không.
1078
1067
  - `implemented_by`: dùng `null` (không phải `"—"`) trong JSON khi không có giá trị
1079
1068
  - `test_count`: dùng integer `0` (không phải `"—"`) khi không có test
1080
1069
  - `test_classes`: dùng `[]` (không phải `"—"`) khi không có test class
@@ -1084,7 +1073,11 @@ Schema:
1084
1073
  Row `ORPHANED` xuất ra JSON là: `"status": "DRIFT"` + `"orphaned": true`. Panel chưa hỗ trợ vẫn hiện nó như `DRIFT` — đủ đúng về nghĩa ("code không khớp spec, cần xử lý") và **không im lặng**; panel có đọc `orphaned` thì hiện nhãn riêng. Chi tiết đầy đủ luôn có ở `orphaned[]` và ở report terminal.
1085
1074
  **TSV giữ nguyên chữ `ORPHANED`** trong cột `status` — TSV là nguồn-sự-thật, JSON chỉ là bản xuất cho panel.
1086
1075
  - `orphaned` (boolean): `true` chỉ khi cột `status` của TSV là `ORPHANED`; mọi row khác ghi `false` (đừng bỏ trống — panel đọc field vắng dễ ra `undefined`).
1087
- - Luôn ghi vào `{paths.trace_dir}/trace-report.json` bất kể domain filter — nếu có domain filter, chỉ gồm các PRD đó trong `prds[]` nhưng ghi domain vào field `domain`
1076
+ - Luôn ghi vào `{paths.trace_dir}/trace-report.json` bất kể phạm vi — nếu có scope, chỉ gồm các PRD/UC đó trong `prds[]`, ghi **cả hai** field:
1077
+ - **`scope`** = `{kind, value}` từ Step 0-A. Đây là field `gate-trace` dùng để quyết chặn — xem dưới.
1078
+ - **`domain`** = domain trong scope (`--domain` → chính nó · `--prd`/`--uc` → domain phân giải được · không scope → `all`). Giữ cho **tương thích ngược** với `gate-trace` bản cũ.
1079
+
1080
+ > ⚠️ **Biên bản có scope KHÔNG BAO GIỜ được coi là biên bản đầy đủ.** `gate-trace` G2 fail nếu `scope.kind !== "all"`, **không ngoại lệ** — nó không nhìn xem trên đĩa có bao nhiêu domain. Vì sao tuyệt đối: bản cũ hỏi *"còn domain nào khác không"*, nên trong repo **một domain** thì `others` là **rỗng** ⇒ không fail ⇒ một biên bản hẹp-theo-PRD được nhận là *"toàn bộ"*. Đó đúng là *"cấp giấy xanh cho thứ chưa ai xem"* mà chú thích của chính gate cảnh báo. Thêm cờ scope mà không siết G2 là biến cổng thành **sân khấu** — đúng cái G39 dựng lên để chống.
1088
1081
  - **TSV `"—"` mapping**: khi đọc file TSV, map giá trị dash sang kiểu JSON: `implemented_by: "—"` → `null`; `test_count: "—"` → `0`; `test_classes: "—"` → `[]`; `tech_doc_revision: "—"` → `0`; `fe_tech_doc_revision: "—"` → `0`; `dev_selftest: "—"` → `"not_run"`; `dev_selftest_at: "—"` → `null`; `qc_status: "—"` → `"not_run"`; `qc_run_at: "—"` → `null`; `qc_owner: "—"` → `null`; `qc_blocked_by: "—"` → `null`; `service: "—"` → `null`; `design_spec_version: "—"` → `null`
1089
1082
  - **Backward-compat:** TSV cũ có thể thiếu cột mới hơn trong header — coi cột vắng nào là giá trị rỗng của nó (**đừng báo lỗi, đừng bỏ qua cả file**): `qc_owner`/`qc_blocked_by` (pre-19-col) → `null`; `fe_tech_doc_revision` (pre-22-col) → `0`; `service`/`design_spec_version` (pre-24-col) → `null`. Lần `/generate-bdd` gen lại tiếp theo nâng header lên layout **24 cột** hiện tại.
1090
1083
  > **Đọc theo TÊN CỘT ở header row, KHÔNG theo vị trí.** Header là dòng đầu mỗi `.tsv` — parse nó rồi tra theo tên. Đếm vị trí sẽ vỡ ở đúng file cũ mà luật này sinh ra để đỡ. Header thiếu hoàn toàn (file hỏng) → mới báo lỗi cho file đó và đi tiếp, không abort cả lệnh.
@@ -1150,11 +1143,12 @@ mỗi scenario row mang service sở hữu ở **cột `service`** (cột 23, do
1150
1143
 
1151
1144
  **Rotate:** file vượt **2000 dòng** → đổi tên thành `trace-history.{YYYY-MM}.jsonl` rồi bắt đầu file mới. Đừng xoá.
1152
1145
 
1153
- **Bốn ràng buộc — đây là phần dễ làm sai:**
1146
+ **Năm ràng buộc — đây là phần dễ làm sai:**
1154
1147
 
1155
1148
  | Ràng buộc | Vì sao |
1156
1149
  |---|---|
1157
1150
  | Ghi **CHỈ** vào `{paths.trace_dir}` (nơi authoritative). **KHÔNG** copy sang `{panel_mirror}` hay `{living_docs_dir}` | Nó là dữ liệu tích luỹ, không phải thứ regenerate được. Nhân bản nó ra chỗ sinh-ra là tạo hai lịch sử lệch nhau. |
1151
+ | Dòng vừa append **PHẢI kết thúc bằng newline** — file không bao giờ được kết thúc giữa dòng | File này là append-only và được merge bằng `merge=union` (xem `{paths.trace_dir}/.gitattributes`). Thiếu newline cuối thì lần append sau — hoặc một lần union merge — **nối hai bản ghi JSON thành một dòng**, và dòng đó không parse được. `--lint-trace` T8 bắt, nhưng đây là ca phòng được bằng một ký tự. |
1158
1152
  | File này **PHẢI được commit** cùng TSV | Nó là **dữ liệu**, không phải mirror. Regenerate lại không được — mất là mất vĩnh viễn. ⚠️ Đừng để nó dính vào luật gitignore của `.living-docs/` hay panel mirror; hai cái đó là bản sinh ra, cái này thì không. |
1159
1153
  | **Không đụng** TSV và `trace-report.json` | TSV là bảng **trạng thái** — giữ nó phẳng. Lịch sử là file riêng, format riêng, vòng đời riêng. `trace-report.json` là contract với panel VS Code. |
1160
1154
  | **Không lệnh nào được ra quyết định dựa trên file này** | Nó để **quan sát**, không phải để gác cổng. Một cái cổng phụ thuộc file có thể bị xoá là cổng dở. Cổng chặn PR vẫn chỉ là 4 cờ 🔴. |
@@ -1163,109 +1157,8 @@ Ghi thất bại (không có quyền, đĩa đầy) → **cảnh báo mềm mộ
1163
1157
 
1164
1158
  ## Output
1165
1159
 
1166
- # Report Footer Định dạng output chuẩn cho mọi lệnh
1167
-
1168
- Mọi report của lệnh phải kết thúc bằng section footer chuẩn này.
1169
-
1170
- ## Status Badge
1171
-
1172
- Chọn một theo kết quả:
1173
- - `✅ Complete` — mọi bước thành công, không có vấn đề
1174
- - `❌ Failed` — lệnh không hoàn thành được do lỗi chặn
1175
- - `⚠️ Warnings` — hoàn thành nhưng có vấn đề không chặn, nên review lại
1176
-
1177
- ## Output Artifacts
1178
-
1179
- Liệt kê mọi file được tạo hoặc sửa bởi lệnh này:
1180
- ```
1181
- Output Artifacts:
1182
- {created|updated} {file-path} ({mô tả ngắn})
1183
- {created|updated} {file-path} ({mô tả ngắn})
1184
- ```
1185
-
1186
- Nếu không ghi file nào (vd: lệnh review hoặc phân tích) → ghi `Output Artifacts: none (read-only)`.
1187
-
1188
- ## Pipeline Position
1189
-
1190
- In một sơ đồ pipeline một dòng, đánh dấu phase của lệnh HIỆN TẠI bằng `◀ bạn ở đây`,
1191
- để người dùng luôn thấy lệnh này nằm ở đâu trong luồng end-to-end:
1192
-
1193
- ```
1194
- Discovery → PRD → [Design Spec] → BDD → Tech Design → Code → Dev Self-Check → QC → Trace Audit
1195
- ```
1196
-
1197
- Tìm lệnh hiện tại trong bảng phase dưới đây và đánh dấu **phase của nó** trong sơ đồ trên:
1198
-
1199
- | Phase | Commands |
1200
- |-------|----------|
1201
- | Discovery | `/define-product` |
1202
- | PRD | `/generate-prd` · `/extend-prd` · `/refine-prd` · `/review-context` (PRD) |
1203
- | Design Spec | `/generate-design-spec` |
1204
- | BDD | `/generate-bdd` · `/review-context` (BDD) |
1205
- | Tech Design | `/generate-tech-docs` · `/map-testids` · `/review-tech-docs` |
1206
- | Code | `/generate-code` · `/review-code` |
1207
- | Dev Self-Check | `/dev-gen-test` · `/dev-run-test` · `/dev-smoke-test` |
1208
- | QC | `/qc-analyze` · `/qc-plan` · `/qc-design-test` · `/qc-review` · `/qc-run-test` · `/qc-report` |
1209
- | Trace Audit | `/validate-traces` |
1210
-
1211
- Với **lệnh review**, thêm vòng review 3 bước và đánh dấu bước hiện tại, vd:
1212
- `Vòng review: [① phân tích ◀] → ② Review Board → ③ --resume`.
1213
-
1214
- **Lệnh xuyên suốt** (`/sync`, `/update-framework`, `/fix-bug`, `/debug`, `/learn`,
1215
- `/report-bug`, `/propose-scenario`, `/generate-spec-manifest`) nằm ngoài pipeline tuyến tính —
1216
- **bỏ hẳn dòng Pipeline** cho các lệnh này (đừng cố nhét chúng vào sơ đồ).
1217
-
1218
- ## Gợi ý lệnh tiếp theo
1219
-
1220
- Gợi ý lệnh kế tiếp hợp lý theo phase của workflow:
1221
-
1222
- | Lệnh hiện tại | Gợi ý lệnh tiếp theo |
1223
- |-------------------------|-----------------------------------------------|
1224
- | /setup-ai-first | `/define-product` để bắt đầu feature đầu tiên |
1225
- | /define-product | `/generate-prd {product-definition-file}` |
1226
- | /generate-prd | `/refine-prd {prd-file}` rồi `/review-context {prd-file}` |
1227
- | /extend-prd | `/refine-prd {prd-file}` (soi phần vừa thêm) rồi `/review-context {prd-file}` → PO duyệt → `/generate-bdd` **chỉ cho UC MỚI**; UC cũ dùng `/validate-traces --realign-prd-version {UC-ID}` |
1228
- | /refine-prd | Mở Review Board → cập nhật PRD → `/review-context {prd-file}` |
1229
- | /review-context (PRD) | Khi 0 critical → PO đặt `Status: approved`, rồi FE/App: `/generate-design-spec {prd-file}` (→ design sign-off → BDD); BE: `/generate-bdd {prd-file}`. Còn critical/NEEDS_FIX → sửa PRD (giữ draft) |
1230
- | /generate-design-spec | Designer review → xác nhận link Figma → PO + Designer sign-off → `/generate-bdd {prd-file}` |
1231
- | /generate-bdd | `/review-context {feature-file}` để kiểm tra độ phủ |
1232
- | /review-context (BDD) | `/generate-tech-docs {UC-ID}` nếu APPROVED; sinh lại nếu NEEDS_FIX |
1233
- | /qc-analyze | `/qc-plan {UC-ID}` (xử lý các gap blocker 🔴 trước) |
1234
- | /qc-plan | `/qc-design-test {UC-ID}` |
1235
- | /qc-design-test | `/qc-review {UC-ID}` (review test-case) |
1236
- | /qc-review (test-case) | `/qc-run-test {UC-ID}` nếu APPROVED; sửa TC nếu NEEDS_FIX |
1237
- | /qc-run-test | `/qc-report {UC-ID}` rồi `/qc-review {UC-ID}` (review script) |
1238
- | /qc-review (script) | `/qc-report {UC-ID}` rồi tạo PR nếu APPROVED |
1239
- | /qc-report | `/validate-traces {UC-ID}` để làm mới Living Docs (qc_status) |
1240
- | /map-testids | `/qc-design-test {UC-ID}` (QC dựng Page Object từ contract §4.5.6 vừa ghi) |
1241
- | /generate-tech-docs | `/review-tech-docs {tech-design-file}` |
1242
- | /review-tech-docs | `/generate-code {feature-file}` nếu APPROVED; sửa doc nếu NEEDS_FIX |
1243
- | /generate-code | Lần gen đầu → `/review-code {UC-ID}`; gen lại → `/dev-gen-test {UC-ID}` |
1244
- | /dev-gen-test | `/dev-run-test {UC-ID}` |
1245
- | /dev-run-test (passing) | `/review-code {UC-ID}` |
1246
- | /dev-run-test (failing) | `/fix-bug {ticket-id}` hoặc `/debug {error}` |
1247
- | /review-code | `/dev-smoke-test {UC-ID}` hoặc tạo PR |
1248
- | /dev-smoke-test | Tạo PR và link tới ticket |
1249
- | /validate-traces | **Cờ 🔴 trước (chặn PR):** SEAM_UNWIRED → nối binding sang class thật, xoá/thay stub · STUB_UNRESOLVED → `/generate-code {owner_uc}` (lấp logic tại chỗ + xoá hàm song song) · ORPHANED/TRACE_ORPHAN → quyết định thủ công (xoá code+test, đưa scenario trở lại `.feature`, hoặc sửa `sc_id` của tag). **Rồi:** DRIFT/UNTRACKED → `/generate-code {UC-ID}` · BDD_DRIFT → `/generate-code {feature-file}` · tech-doc lỗi thời vs BDD → `/generate-tech-docs` → `/review-tech-docs` · PRD drift → `/generate-bdd {prd-file}` · GAP → `/dev-gen-test {UC-ID}`. **Chỉ tạo PR khi mọi cờ 🔴 = 0** |
1250
- | /fix-bug | `/dev-run-test {UC-ID}` (dev_selftest vừa reset về not_run) → tạo PR; nếu fix một `{BUG-ID}` → QC chạy `/qc-run-test {UC-ID}` để verify + đóng bug |
1251
- | /debug | `/fix-bug {ticket-id}` nếu cần sửa |
1252
- | /report-bug | Gửi cho dev (`/fix-bug {BUG-ID}`); nếu thiếu coverage → `/propose-scenario {UC-ID}` |
1253
- | /propose-scenario | **Case A** (thiếu scenario cho AC có sẵn) → báo PO/Dev review trong `feedback/bdd-proposals/`; `/generate-bdd` tự chèn khi `Status: accepted`. **Case B** (requirement mới) → `feedback/prd-change-requests/` — PO phải đưa vào PRD trước, KHÔNG tự vào BDD được; `/validate-traces` nhắc lại kèm số ngày chờ chừng nào `Status: Open` |
1254
- | /learn | Tiếp tục làm việc — lesson áp dụng ở lệnh kế tiếp |
1255
- | /sync | `/validate-traces` để xem độ phủ đầy đủ; xử lý mọi `📥 tester feedback` được nêu |
1256
- | /update-framework | Review `git diff .agent/`, commit; `/sync` để đồng bộ nội dung dự án |
1257
-
1258
- Định dạng footer như sau:
1259
- ```
1260
- ---
1261
- Status : {badge}
1262
- {khối Output Artifacts}
1263
- Pipeline : Discovery → PRD → [BDD ◀ bạn ở đây] → Tech Design → Code → Dev Self-Check → QC → Trace Audit
1264
- (lệnh review) Vòng review: [① phân tích ◀] → ② Review Board → ③ --resume
1265
- Next : {lệnh gợi ý kèm ví dụ tham số}
1266
- ```
1267
- *(Bỏ dòng `Pipeline` cho các lệnh xuyên suốt liệt kê ở trên.)*
1268
-
1160
+ **Đọc `.agent/steps/report-footer.md`** áp đúng khuôn footer trong đó (Status Badge ·
1161
+ Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
1269
1162
 
1270
1163
  ```
1271
1164
  /validate-traces — {domain}
@@ -1349,6 +1242,20 @@ Trace orphan (tag trỏ vào SC không tồn tại, KHÔNG có row .tsv nào):
1349
1242
  (Không lệnh nào khác bắt được cái này — row .tsv đã bị xoá bởi version cũ,
1350
1243
  hoặc tag ghi sai id ngay từ đầu.)
1351
1244
 
1245
+ 🔴 PRD_UNTRACKED_EDIT — nội dung PRD đổi mà nhãn Version KHÔNG đổi ({n} file):
1246
+ specs/payment/create-invoice/PAY01-create-invoice.md Version 1.3 (không đổi từ lần audit)
1247
+ Bằng chứng : git status — sửa CHƯA commit
1248
+ Mốc cũ : sha a1b2c3d · Version 1.3
1249
+ ⚠️ MỌI phán đoán version bên dưới cho PRD này đang dựa vào một nhãn không còn đúng.
1250
+ Không suy đoán được UC nào bị đụng → phải coi cả {n} UC của nó là chưa rõ.
1251
+ Sửa: bump Version + ghi một row changelog NÊU UC bị ảnh hưởng.
1252
+ Đó đúng là việc /amend-prd làm hộ (kèm kiểm va chạm + guard sau-ghi).
1253
+ Không có --accept-edit: dán nhãn lên thay đổi chưa ai xem là đúng cái rào của
1254
+ --realign tồn tại để chặn.
1255
+
1256
+ (hoặc: ⚠️ Chưa kiểm được sửa-ngoài-đường cho {n} PRD ({lý do}) — điểm mù G54 đang MỞ)
1257
+ (hoặc: ⓘ spec_baseline: lần đầu ghi mốc — check có hiệu lực từ lần chạy sau)
1258
+
1352
1259
  PRD Version Drift (changelog CÓ nêu UC này — nội dung đổi thật):
1353
1260
  {UC}-UC2 — code ở PRD v1.0, PRD giờ ở v1.2 [lệch: tsv, code]
1354
1261
  Thay đổi kể từ v1.0:
@@ -1384,6 +1291,14 @@ Seam & Stub Audit (mồ côi khi ghép luồng):
1384
1291
  → /generate-code {owner_uc} lấp logic vào {ClassName#method} tại chỗ, xoá hàm song song, build lại
1385
1292
  ⓘ STUB_PENDING — {ClassName#method} ({stub_for}): owner {owner_uc} chưa gen (chưa phải lỗi)
1386
1293
 
1294
+ {khối dưới CHỈ in khi service_unrouted_count > 0 — else bỏ cả khối}
1295
+ Routing chưa chốt ({service_unrouted_count} scenario) — 🟠 KHÔNG chặn PR:
1296
+ 🟠 SERVICE_UNROUTED — {sc_id} ({platform}), domain "{domain}": {reason}
1297
+ → thêm mapping cho domain "{domain}" vào `services:` của .agent/project-context.yaml,
1298
+ rồi chạy lại /validate-traces — nó tự nâng unrouted → path. KHÔNG cần sinh lại BDD.
1299
+ BDD của các scenario này KHÔNG sai. Đây là bước cấu hình của architect, và nó chỉ CHẶN
1300
+ ở /generate-code (lệnh đó buộc phải biết ghi file vào repo nào).
1301
+
1387
1302
  {khối dưới CHỈ in khi Step 7b tìm thấy ≥1 request Status: Open — else bỏ cả khối}
1388
1303
  📥 Yêu cầu đổi PRD đang chờ ({n} — chưa ai xử lý):
1389
1304
  {UC-ID} — "{title}" chờ {days_waiting} ngày