@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
@@ -17,35 +17,31 @@ Trước tiên, kiểm tra xem `$ARGUMENTS` có phải là payload JSON từ m
17
17
  - Đi thẳng tới phần logic riêng của lệnh.
18
18
  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).
19
19
 
20
- ## Bước 0-B — Kiểm tra Model
20
+ ## Bước 0-B — Ghi nhận Model *(KHÔNG chặn)*
21
21
 
22
- *Bỏ qua bước này nếu `_agent_mode: true` (sub-agent — orchestrator đã kiểm tra rồi).*
22
+ *Bỏ qua nếu `_agent_mode: true` (sub-agent — orchestrator đã ghi nhận rồi).*
23
23
 
24
- Các lệnh sinh nội dung review phức tạp đòi hỏi khả năng suy luận mạnh.
25
- 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.
24
+ 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
25
+ 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
26
+ model Opus, gắn thêm cảnh báo ngay ở dòng đó.
26
27
 
27
- Hiển thị chờ phản hồi:
28
+ **KHÔNG hỏi người dùng. KHÔNG chờ. KHÔNG dừng.**
28
29
 
29
- ```
30
- ⚙️ MODEL CHECK
31
- ──────────────────────────────────────────────────────────────────
32
- Recommended : model Opus mới nhất
33
- Why needed : Phân tích spec, review kiến trúc, sinh code đòi hỏi
34
- suy luận sâu. Model nhỏ hơn (Haiku/Sonnet) dễ bỏ sót edge case.
35
-
36
- Cách đổi trong Claude Code:
37
- /model chọn model Opus
38
- • hoặc: Settings → Model
39
-
40
- Đang chạy một model Opus?
41
- Y — đúng → tiếp tục
42
- 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)
43
- ──────────────────────────────────────────────────────────────────
44
- ```
30
+ > **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
31
+ > `⚙️ MODEL CHECK` rồi chờ `Y/S/N`. Ba vấn đề cùng chỉ một hướng:
32
+ > **(1)** nó hỏi người dùng thứ mà **agent đã biết chính xác**;
33
+ > **(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
34
+ > phát hiện;
35
+ > **(3)** **cả `Y` lẫn `S` đều đi tiếp** cách duy nhất để nó dừng là tự nguyện gõ `N`.
36
+ > 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
37
+ > 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
38
+ > 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.
39
+ >
40
+ > Khai báo trong report **mạnh hơn** hỏi: đúng nguồn (agent, không phải người), và nằm
41
+ > **cạnh kết quả** để cân nhắc, thay vì nằm trước khi có kết quả để bấm cho xong.
45
42
 
46
- - "Y" tiếp tục sang Bước 1.
47
- - "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).
48
- - "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."
43
+ **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;
44
+ model nhỏ hơn dễ bỏ sót edge case vi phạm kiến trúc. Đổi: `/model` chọn Opus.
49
45
 
50
46
  ## Bước 1 — Xác định Target File
51
47
 
@@ -75,20 +71,81 @@ Lưu toàn bộ context đã nạp vào bộ nhớ để dùng xuyên suốt phi
75
71
 
76
72
  ## Bước 3 — CHECKPOINT
77
73
 
78
- 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:
74
+ *Bỏ qua nếu `_agent_mode: true`.*
75
+
76
+ ### 3a — Lệnh này có phải chặn không?
77
+
78
+ | Mức | Lệnh nào | `--yes` bỏ qua được? |
79
+ |---|---|:---:|
80
+ | **Không chặn** | Lệnh read-only: `/review-code` · `/validate-traces` · `/debug` · `/review-context` · `/review-tech-docs` | — (vốn không có) |
81
+ | **Chặn thường** | Mọi lệnh sinh/sửa artifact | ✅ |
82
+ | **Chặn CỨNG** | Ghi đè file đã tồn tại · `--resume` áp findings · migrate · prune | ❌ **không bao giờ** |
83
+
84
+ `--yes` trong `$ARGUMENTS` → bỏ qua CHECKPOINT mức *chặn thường*. (Bước 1 đã tách mọi token
85
+ `--` 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
86
+ headless: `claude -p "/generate-code UC1 --yes"`.
87
+
88
+ > **KHÔNG tự suy mức từ bảng này.** Mỗi lệnh **tự khai** mức của nó ở một dòng `*Checkpoint: …*`
89
+ > 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
90
+ > **nghĩa là gì**.
91
+ > Nguồn máy đọc: `bin/trace-schema.json` → `gate.checkpoint_levels`; `self-check` **R11** fail
92
+ > 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.
93
+ > *(Lệnh không có dòng nào = mức **chặn thường**, mặc định.)*
94
+
95
+ > **Mức *không chặn* là thực thi đúng miễn trừ mà `rules/workflow.md` đã cấp từ trước** —
96
+ > trước G41 file đó viết *"read-only commands may skip CHECKPOINT"* còn gate thì luôn đòi.
97
+ > 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.
98
+
99
+ ### 3b — In gì
100
+
101
+ **KHÔNG lặp lại những gì `[CTX LOADED]` vừa in.** Recap của context-loader (Bước 7) đã hiện
102
+ Stack · Platform · Layers · CLAUDE.md · Dict · Entities · Lessons · Service · Status ngay phía
103
+ trên. CHECKPOINT chỉ thêm **một** thông tin mới là `Target`.
104
+
105
+ **Mọi thứ sạch** — recap báo `Status: FULL`, không cờ nào bật → in đúng hai dòng:
79
106
 
80
107
  ```
81
- CHECKPOINT
82
- -----------
83
- Target : {resolved file path}
84
- Project : {project.name từ project-context.yaml}
85
- Tech stack : {language} / {framework}
86
- Module : {module nếu có, else "not configured"}
87
- Domains : {danh sách domain, ngăn cách bởi dấu phẩy}
108
+ CHECKPOINT — Target: {resolved file path}
109
+ Tiếp tục? (Y/N)
110
+ ```
111
+
112
+ **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:
88
113
 
114
+ ```
115
+ CHECKPOINT
116
+ 🔴 Service : unresolved — {lý do context-loader đã ghi}
117
+ ⚠️ CLAUDE.md: service overlay THIẾU — dùng root (code sinh ra có thể sai stack)
118
+ ⚠️ Target : resolve bằng wildcard — {n} file khớp, chọn {file}
119
+ ⚠️ Module : not configured — code sinh ra sẽ dùng default
120
+ Status : PARTIAL — thiếu: {danh sách}
121
+ Target : {resolved file path}
89
122
  Tiếp tục? (Y/N)
90
123
  ```
91
124
 
92
- Chờ người dùng trả lời ràng "Y" hoặc "N" rồi mới tiếp tục.
93
- - "Y" → tiếp tục sang các bước riêng của lệnh bên dưới.
94
- - "N" dừng lại hỏi người dùng muốn thay đổi gì.
125
+ ### 3c Cờ nào bật, cờ nào KHÔNG
126
+
127
+ 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
128
+ đ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:
129
+
130
+ | Bật cờ khi | Nguồn | Mức |
131
+ |---|---|:---:|
132
+ | `active_service = unresolved` | context-loader Bước 2b/2c/Fallback | 🔴 |
133
+ | `Status = MINIMAL` | recap Bước 7 | 🔴 |
134
+ | `Status = PARTIAL` | recap Bước 7 | ⚠️ |
135
+ | CLAUDE.md thiếu, hoặc service overlay thiếu | context-loader Bước 3 | ⚠️ |
136
+ | Target resolve qua wildcard, hoặc nhiều file khớp mà lệnh tự chọn | Bước 1 ở trên | ⚠️ |
137
+ | `module` không cấu hình | recap Bước 7 | ⚠️ |
138
+
139
+ **KHÔNG bật cờ cho:** `Lessons: chưa có` · `Dict: missing` · `Entities: missing`. Đó là
140
+ *"dự án chưa điền"*, không phải *"có gì đó sai"* — chúng ở lại trong recap.
141
+
142
+ > **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**,
143
+ > 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ư
144
+ > 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.
145
+
146
+ ### 3d — Chờ trả lời
147
+
148
+ - "Y" → tiếp tục sang các bước riêng của lệnh.
149
+ - "N" → dừng, hỏi người dùng muốn thay đổi gì.
150
+ - Có `--yes` và mức *chặn thường* → coi như "Y", **nhưng vẫn IN khối CHECKPOINT** nếu có cờ
151
+ 🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
@@ -2,6 +2,29 @@
2
2
 
3
3
  Mọi report của lệnh phải kết thúc bằng section footer chuẩn này.
4
4
 
5
+ ## Model *(bắt buộc, một dòng)*
6
+
7
+ In model mà **bạn — agent vừa chạy lệnh này — thực sự đang dùng** (ghi nhận ở Gate Bước 0-B):
8
+
9
+ ```
10
+ Model: {tên model đang chạy}
11
+ ```
12
+
13
+ Nếu bạn biết mình **không** phải một model Opus, thêm cảnh báo ngay trên cùng dòng:
14
+
15
+ ```
16
+ Model: {tên model} ⚠️ lệnh này khuyến nghị Opus — model nhỏ hơn dễ bỏ sót edge case,
17
+ phân tích spec thiếu sót, vi phạm kiến trúc. Cân nhắc chạy lại
18
+ với /model → Opus trước khi dùng kết quả này.
19
+ ```
20
+
21
+ > **Vì sao ở ĐÂY chứ không phải một prompt ở đầu lệnh (GAPS-v3 G41):** trước đây Gate hiện
22
+ > `⚙️ MODEL CHECK` rồi chờ `Y/S/N`. Nó **hỏi người dùng thứ agent đã biết**, câu trả lời
23
+ > **không kiểm chứng được**, và **cả `Y` lẫn `S` đều đi tiếp** — tức không chặn được ai, mà
24
+ > tốn một lần chặn ở mọi lệnh (20 lệnh cho một feature). Khai báo ở footer đúng nguồn hơn
25
+ > (agent tự khai, không phải người tự khai) và đúng chỗ hơn: nó nằm **cạnh kết quả** để
26
+ > người đọc cân nhắc có nên tin, thay vì nằm trước khi có kết quả để bấm cho xong.
27
+
5
28
  ## Status Badge
6
29
 
7
30
  Chọn một theo kết quả:
@@ -17,7 +17,28 @@ commands/generate-bdd.md
17
17
 
18
18
  Không lệnh nào đọc một path template lúc chạy. `paths.feature_template` / `paths.prd_template` từng tồn tại trong `project-context.yaml` nhưng chưa bao giờ có tác dụng — đã được gỡ bỏ (xem `GAPS.md` G9).
19
19
 
20
- **Thêm nữa:** `.agent/` là vùng bị ghi đè. `/update-framework` chạy `npx … --init`, và `--init` copy `core/` → `.agent/` **vô điều kiện** (`bin/index.js` → `copyDirRecursive`, không có existence check, không backup). File duy nhất được giữ lại là `.agent/project-context.yaml`. Nên mọi chỉnh sửa ở `.agent/templates/` sẽ **biến mất im lặng** ở lần nâng cấp kế tiếp.
20
+ **Thêm nữa:** `.agent/` là vùng bị ghi đè. `/update-framework` chạy `npx … --init`, và `--init` copy `core/` → `.agent/` **vô điều kiện** (`bin/index.js` → `installCore`). File duy nhất được giữ lại là `.agent/project-context.yaml`. Nên mọi chỉnh sửa ở `.agent/templates/` sẽ **biến mất** ở lần nâng cấp kế tiếp — từ v0.4.2 thì không còn im lặng: bản cũ được lưu vào `.agent/.overwritten-{version}-{date}/` và được liệt kê ra (`GAPS.md` G24). Nhưng vẫn phải áp lại bằng tay mỗi version, nên đây không phải chỗ để đặt thay đổi.
21
+
22
+ ---
23
+
24
+ ## Ngoại lệ: `ci/` và `hooks/` — template để COPY RA, không phải để build
25
+
26
+ Hai thư mục này **không** giống phần còn lại của `templates/`. Chúng không được `{{include}}` vào lệnh nào, và **không** được đọc lúc chạy. Chúng là file **hoàn chỉnh, dùng ngay**, chờ một người copy ra khỏi `.agent/`:
27
+
28
+ | File | Copy tới | Làm gì |
29
+ |---|---|---|
30
+ | `ci/trace-gate.yml` | `.github/workflows/` của project | Chặn PR khi trace có cờ 🔴 (`--gate-trace`) |
31
+ | `hooks/pre-push` | `.git/hooks/pre-push` (rồi `chmod +x`) | Chặn push khi sổ trace hỏng cấu trúc (`--lint-trace`) |
32
+
33
+ ```bash
34
+ # từ gốc project
35
+ mkdir -p .github/workflows && cp .agent/templates/ci/trace-gate.yml .github/workflows/
36
+ cp .agent/templates/hooks/pre-push .git/hooks/pre-push && chmod +x .git/hooks/pre-push
37
+ ```
38
+
39
+ **Phải copy RA, không dùng tại chỗ** — vì đúng cái lý do cả file README này nói: `.agent/` bị ghi đè mỗi lần nâng cấp, và `.git/hooks/` thì git không bao giờ chạy từ chỗ khác. Copy ra rồi thì chúng là file của project: sửa tuỳ ý, nâng cấp framework không đụng tới.
40
+
41
+ Vì sao chúng tồn tại → `GAPS-v3.md` G39: framework phát hiện được một lớp lỗi mà build xanh + test xanh không thấy, nhưng trước đó việc phát hiện phụ thuộc vào có người tự nguyện chạy một lệnh chat. Hai file này là chỗ nó chặn bằng máy.
21
42
 
22
43
  ## Muốn đổi cấu trúc artifact sinh ra thì làm gì
23
44
 
@@ -43,5 +64,7 @@ Rồi phát hành version mới; project chạy `/update-framework` để nhận
43
64
  | `product-definition.template.md` | `commands/define-product.tmpl` | product definition |
44
65
  | `platform-guide.template.md` | (tham khảo) | — |
45
66
  | `project-context.yaml` | **không** include — được copy thẳng làm file config khởi tạo | `.agent/project-context.yaml` |
67
+ | `ci/trace-gate.yml` | **không** include — người dùng copy ra | `.github/workflows/trace-gate.yml` |
68
+ | `hooks/pre-push` | **không** include — người dùng copy ra | `.git/hooks/pre-push` |
46
69
 
47
70
  > Lưu ý `project-context.yaml` là ngoại lệ duy nhất: nó **được** copy ra làm file thật của project, và **được bảo vệ** khỏi ghi đè khi nâng cấp (chỉ tạo nếu chưa tồn tại).
@@ -0,0 +1,146 @@
1
+ # ─────────────────────────────────────────────────────────────────────────────
2
+ # SDD Framework — Trace Gate (GitHub Actions)
3
+ #
4
+ # COPY file này vào .github/workflows/ của project. Nó KHÔNG tự chạy từ
5
+ # .agent/templates/ — mọi thứ trong .agent/ là bản sinh ra, bị ghi đè mỗi lần
6
+ # /update-framework.
7
+ #
8
+ # VÌ SAO CẦN (GAPS-v3 G39): framework phát hiện được một lớp lỗi mà build xanh +
9
+ # test từng-UC xanh KHÔNG thấy — luồng ghép chạy vào hàm rỗng (SEAM_UNWIRED,
10
+ # STUB_UNRESOLVED), hoặc code trỏ vào scenario đã bị xoá (ORPHANED, TRACE_ORPHAN).
11
+ # Nhưng trước G39 việc phát hiện đó phụ thuộc vào có người TỰ NGUYỆN chạy
12
+ # /validate-traces trong Claude Code rồi đọc report bằng mắt. Cái gì không chặn
13
+ # thì sau sprint thứ ba không ai làm. Đây là chỗ nó chặn.
14
+ #
15
+ # GIỚI HẠN — đọc trước khi tin:
16
+ # Job này chứng minh "report khớp SỔ, và sổ không có cờ 🔴".
17
+ # Nó KHÔNG chứng minh "sổ khớp CODE" — việc đó cần quét tag trong source, đọc
18
+ # .feature, so version, tức cần /validate-traces (một lệnh LLM, không chạy được
19
+ # trong CI thường). Nên nó bắt ca phổ biến "quên chạy lại /validate-traces",
20
+ # nhưng KHÔNG bắt ca "sửa code mà không đụng sổ".
21
+ # Muốn bịt nốt: xem job `require-fresh-audit` ở cuối file.
22
+ # ─────────────────────────────────────────────────────────────────────────────
23
+
24
+ name: Trace Gate
25
+
26
+ on:
27
+ pull_request:
28
+ push:
29
+ branches: [main, master, develop]
30
+
31
+ jobs:
32
+ trace-gate:
33
+ runs-on: ubuntu-latest
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ with:
37
+ # Cần lịch sử để job require-fresh-audit so được diff. Bỏ nếu không dùng job đó.
38
+ fetch-depth: 0
39
+ # Spec/trace nằm trong submodule (umbrella + spec_source)? Bỏ comment:
40
+ # submodules: recursive
41
+
42
+ - uses: actions/setup-node@v4
43
+ with:
44
+ node-version: '20'
45
+
46
+ # ── 1. Cấu trúc sổ ───────────────────────────────────────────────────────
47
+ # Sổ trace 24 cột do LLM ghi bằng tay. Một dấu tab thiếu dồn mọi ô sang trái
48
+ # và ô `status` nhận một ngày tháng — trước G38 không gì báo. Bước này chặn.
49
+ # Cũng bắt marker conflict git lọt vào sổ (T7) và sổ thiếu luật merge (T10).
50
+ - name: Lint sổ trace
51
+ run: npx -y @educa-corp/sdd-framework@latest --lint-trace
52
+
53
+ # ── 2. Cổng chặn PR ──────────────────────────────────────────────────────
54
+ # --gate-trace tự chạy lại lint ở tầng G1, nên bước 1 ở trên là để có log
55
+ # riêng dễ đọc khi đỏ. Muốn gọn thì bỏ bước 1 và chỉ giữ bước này.
56
+ - name: Trace gate (cờ 🔴 chặn PR)
57
+ run: npx -y @educa-corp/sdd-framework@latest --gate-trace
58
+
59
+ # ── 3. (tuỳ chọn) Đưa kết quả vào PR summary ─────────────────────────────
60
+ - name: Ghi kết quả vào job summary
61
+ if: always()
62
+ run: |
63
+ npx -y @educa-corp/sdd-framework@latest --gate-trace --json --warn-only \
64
+ > gate.json || true
65
+ {
66
+ echo '## Trace Gate'
67
+ echo '```json'
68
+ cat gate.json
69
+ echo '```'
70
+ } >> "$GITHUB_STEP_SUMMARY"
71
+
72
+ # ───────────────────────────────────────────────────────────────────────────
73
+ # Ép audit phải TƯƠI khi thứ report đang KHẲNG ĐỊNH bị đổi.
74
+ #
75
+ # Bịt cái lỗ mà trace-gate không bịt được: gate chứng minh "report khớp SỔ",
76
+ # không chứng minh "sổ khớp CODE" — việc đó cần /validate-traces, một lệnh LLM
77
+ # không chạy được ở đây. Nên job này dùng một PROXY: không verify được thì ĐÒI
78
+ # BẰNG CHỨNG có người vừa verify.
79
+ #
80
+ # ĐO BẰNG TAG, KHÔNG BẰNG `src/**`:
81
+ # Framework có luật boundary-only tagging — chỉ Controller/Handler/Middleware/
82
+ # Steps mang tag @trace; Entity/Repository/DTO/Interface/Base KHÔNG. Nên câu
83
+ # hỏi "PR có sửa src/ không?" chặn cả PR chỉ thêm một field vào DTO — một file
84
+ # không mang lời khẳng định trace nào. Cái gì báo oan thì bị tắt, rồi mất luôn
85
+ # phần thật sự cần chặn.
86
+ # Câu hỏi đúng: "PR có chạm dòng nào mang tag mà report đang khẳng định không?"
87
+ #
88
+ # sửa log trong Controller → cho qua (bản `src/**` cũ: chặn oan)
89
+ # thêm field vào DTO → cho qua (bản cũ: chặn oan)
90
+ # thêm method + @implements → CHẶN
91
+ # lấp một @trace.stub → CHẶN
92
+ #
93
+ # KHÔNG cần sửa gì theo layout project — tag là tag, ở đâu cũng vậy.
94
+ #
95
+ # BẢY TAG dưới đây là NGUỒN của đúng 4 cờ chặn PR. Chúng được khai trong
96
+ # bin/trace-schema.json → gate.audit_invalidating_tags, và self-check R10 fail
97
+ # build nếu file này không nhắc đủ — tag đổi tên mà đây không biết thì grep
98
+ # không khớp gì, job LUÔN XANH, và cổng mù trong im lặng.
99
+ #
100
+ # GIỚI HẠN ĐÃ BIẾT: đổi tên class (AuthService → AuthenticationService) không
101
+ # chạm dòng tag ⇒ job này bỏ lọt, dù cột implemented_by giờ trỏ vào tên không
102
+ # còn. Chấp nhận có chủ ý: ca đó ít gặp và KHÔNG im lặng (/validate-traces lần
103
+ # sau báo ngay), còn báo oan thì xảy ra mỗi ngày.
104
+ # Muốn chặt hơn (bắt cả rename, giá là chặn cả việc sửa log trong Controller):
105
+ # đổi bước dưới thành — lấy danh sách file đã đổi, rồi `grep -l` bảy tag đó
106
+ # TRÊN NỘI DUNG FILE thay vì trên diff.
107
+ # ───────────────────────────────────────────────────────────────────────────
108
+ require-fresh-audit:
109
+ if: github.event_name == 'pull_request'
110
+ runs-on: ubuntu-latest
111
+ steps:
112
+ - uses: actions/checkout@v4
113
+ with: { fetch-depth: 0 }
114
+
115
+ - name: Đổi thứ report khẳng định thì audit phải đổi theo
116
+ shell: bash
117
+ run: |
118
+ BASE="origin/${{ github.base_ref }}"
119
+
120
+ # 7 tag sinh ra 4 cờ chặn PR — khai ở bin/trace-schema.json,
121
+ # gate.audit_invalidating_tags (self-check R10 canh danh sách này khớp).
122
+ TAGS='@trace\.(implements|verifies|seam_port|seam_pending|stub|stub_owner|stub_for)'
123
+
124
+ # Chạm dòng mang tag = report có thể đã hết đúng. -U0 để chỉ lấy dòng thật đổi.
125
+ touched=$(git diff -U0 "$BASE"...HEAD | grep -E "^[+-].*${TAGS}" | head -5 || true)
126
+ audit=$(git diff --name-only "$BASE"...HEAD | grep -E 'trace-report\.json$' | head -1 || true)
127
+
128
+ if [ -n "$touched" ] && [ -z "$audit" ]; then
129
+ echo "::error::PR đổi tag trace nhưng không kèm trace-report.json được sinh lại."
130
+ echo ""
131
+ echo "Những dòng này đã đổi:"
132
+ echo "$touched" | sed 's/^/ /'
133
+ echo ""
134
+ echo "Trace gate chỉ chứng minh 'report khớp SỔ'. Tag vừa đổi mà chưa audit lại"
135
+ echo "thì mọi cờ 🔴 trong report nói về trạng thái TRƯỚC khi bạn sửa."
136
+ echo ""
137
+ echo "Chạy trong Claude Code: /validate-traces"
138
+ echo "Rồi commit: {trace_dir}/trace-report.json + *.tsv"
139
+ exit 1
140
+ fi
141
+
142
+ if [ -n "$touched" ]; then
143
+ echo "✅ Tag trace có đổi, và audit đã được sinh lại cùng PR."
144
+ else
145
+ echo "✅ PR không chạm tag nào mà report đang khẳng định — audit vẫn còn đúng."
146
+ fi
@@ -4,7 +4,7 @@
4
4
  # @trace.revision: 1 ← field tĩnh; version theo dõi bằng @trace.bdd_version
5
5
  # @trace.domain: <domain>
6
6
  # @trace.platform: {active_platform — web | app | system} ← BẮT BUỘC mọi mode; phải khớp segment bdd/{platform}/ của path
7
- # @trace.service: {active_service BẮT BUỘC mọi mode. "" single-service/spec repo mode; "multi" nếu chưa chốt; "unresolved" nếu routing sai. Nguồn của cột TSV `service` trace gộp không tách theo service nên đây là chỗ DUY NHẤT mang thông tin sở hữu}
7
+ # @trace.service: {service của ĐÚNG platform file nàyBẮT BUỘC mọi mode. Nguồn của cột TSV `service`; trace gộp không tách theo service nên đây là chỗ DUY NHẤT mang thông tin sở hữu ở cấp row. Bốn giá trị: {path} · "unrouted" (chưa ai quyết repo — HỢP LỆ, cờ 🟠, KHÔNG chặn) · "unresolved" (config sai cấu trúc — bug) · "—" (single-service). KHÔNG ghi "multi": file này đã có MỘT platform xác định nên service_candidates.{platform}.path đã biết — ghi path đó (G51)}
8
8
  # @trace.module: {active_module trong umbrella mode; "unknown" trong spec repo mode}
9
9
  # @trace.status: draft
10
10
  # @trace.author: AI-generated
@@ -0,0 +1,61 @@
1
+ #!/usr/bin/env sh
2
+ # ─────────────────────────────────────────────────────────────────────────────
3
+ # SDD Framework — git pre-push hook
4
+ #
5
+ # CÀI (từ gốc project):
6
+ # cp .agent/templates/hooks/pre-push .git/hooks/pre-push
7
+ # chmod +x .git/hooks/pre-push
8
+ #
9
+ # Trên Windows: Git for Windows chạy hook bằng sh nên file này dùng được như vậy.
10
+ #
11
+ # VÌ SAO CÓ HOOK NÀY khi đã có CI (GAPS-v3 G39/G40): nó chặn ở chỗ RẺ NHẤT.
12
+ # Cụ thể là T7 — marker conflict git (`<<<<<<< HEAD`) lọt vào sổ trace. Một khi
13
+ # thứ đó vào nhánh chung thì mọi người kéo về đều có sổ hỏng, và sổ trace là dữ
14
+ # liệu KHÔNG dựng lại được. Bắt trước lúc push tốn 2 giây; bắt ở CI thì đã muộn
15
+ # một vòng, bắt bằng mắt thì thường là ba tuần sau.
16
+ #
17
+ # CỐ Ý CHỈ LINT, KHÔNG GATE:
18
+ # --lint-trace = cấu trúc sổ. Nhanh, offline được sau lần đầu, và một lỗi ở đây
19
+ # LUÔN là lỗi thật (tab lệch, marker conflict, enum sai).
20
+ # --gate-trace = cờ 🔴. Cần trace-report.json còn tươi, mà giữa lúc làm việc thì
21
+ # nó thường chưa tươi — đỏ liên tục ⇒ người ta gõ --no-verify ⇒
22
+ # mất luôn cả phần lint. Cờ 🔴 để CI chặn.
23
+ #
24
+ # Bỏ qua một lần (dùng có ý thức, đừng thành phản xạ): git push --no-verify
25
+ # ─────────────────────────────────────────────────────────────────────────────
26
+
27
+ # Không có node thì im lặng cho qua — hook không được làm người ta không push được
28
+ # vì lý do không liên quan tới việc họ đang làm.
29
+ command -v node >/dev/null 2>&1 || exit 0
30
+
31
+ # Không có sổ trace thì không có gì để kiểm (project chưa chạy /generate-bdd lần nào).
32
+ # Sửa đường dẫn nếu trace_dir của project khác (vd ../.trace, hay {spec_source}/.trace).
33
+ TRACE_DIR=".trace"
34
+ [ -d "$TRACE_DIR" ] || exit 0
35
+
36
+ echo "→ Lint sổ trace trước khi push ..."
37
+
38
+ if npx -y @educa-corp/sdd-framework@latest --lint-trace --trace "$TRACE_DIR"; then
39
+ exit 0
40
+ fi
41
+
42
+ cat <<'MSG'
43
+
44
+ ──────────────────────────────────────────────────────────────────────
45
+ 🔴 PUSH BỊ CHẶN — sổ trace hỏng cấu trúc.
46
+
47
+ Sổ trace là dữ liệu KHÔNG regenerate được. Đẩy một sổ hỏng lên nhánh
48
+ chung thì mọi người kéo về đều nhận bản hỏng.
49
+
50
+ Thường gặp nhất — marker conflict git chưa giải (T7):
51
+ 1. Mở file lint vừa nêu, xoá 3 dòng <<<<<<< ======= >>>>>>>
52
+ 2. GIỮ CẢ HAI BÊN, đừng chọn một bên (mất row là mất vĩnh viễn)
53
+ 3. npx @educa-corp/sdd-framework --lint-trace # xác nhận sạch
54
+ 4. /validate-traces trong Claude Code # reconcile row trùng
55
+
56
+ Playbook đầy đủ: docs/02-concepts/traceability.md
57
+ Bỏ qua một lần: git push --no-verify
58
+ ──────────────────────────────────────────────────────────────────────
59
+
60
+ MSG
61
+ exit 1
@@ -61,15 +61,70 @@ Spec-driven thành/bại phụ thuộc **~80%** vào việc context được n
61
61
  ## Template Pipeline
62
62
 
63
63
  ```
64
- .tmpl (source) + steps/*.md --[node bin/build.js, {{include:...}}]--> commands/*.md + core/
65
-
66
- │ templates/*.template* cũng được inline
67
- ▼ ▼
68
- bin/self-check.js (fail build) .agent/commands/*.md (runtime)
64
+ .tmpl + steps/*.md ──build──► commands/*.md ──► core/* ──► .agent/ 904 KB
65
+
66
+ bin/self-check.js (fail build) --init cài vào đây
69
67
  ```
70
68
 
71
- - chế `{{include:steps/...}}` ghép phẳng single source of truth `.tmpl` + `steps/`.
69
+ **Vì sao slim (G45):** build inline `{{include:}}` vào **từng** file lệnh. Với 32 lệnh, kết quả
70
+ 2069 KB mà chỉ 580 KB là nội dung riêng của chúng — **72% là vài step giống hệt nhau, chép 30 lần**.
71
+ `/generate-code` từng nặng 108 KB (≈27k token đọc **trước** khi làm gì), gần một nửa không nói gì về
72
+ việc sinh code. Cái giá thật không phải tiền: trên PRD nhiều UC nó làm tăng rủi ro **cạn context
73
+ giữa lúc ghi sổ trace**.
74
+
75
+ | Step | Xử lý trong `core/` | Vì sao |
76
+ |---|---|---|
77
+ | `context-loader.md` (33 K × 29) | **đọc lúc chạy** | 64% lãng phí. Bỏ sót ⇒ lệnh **dừng ngay** vì thiếu path/config — hỏng ồn ào |
78
+ | `report-footer.md` (7 K × 32) | **đọc lúc chạy** | 16%. Bỏ sót ⇒ report kém cấu trúc, không hỏng gì |
79
+ | `gate.md` (7 K × 30) | **giữ inline** | Chỉ 15%, nhưng là **lưới an toàn** (model check · resolve target · CHECKPOINT). Bỏ sót ⇒ lệnh **vẫn chạy** mà không còn cổng nào — hỏng **âm thầm** |
80
+
81
+ Kết quả: `/generate-code` 108 KB → **69 KB**, `/refine-prd` 85 KB → **45 KB**.
82
+
83
+ > **Một biến thể duy nhất.** Bản đầu của G45 phải build **hai** bản: `commands/*.md` inline đầy đủ
84
+ > cho legacy mode (`--project` / không cờ) — vì nó copy thẳng file đó vào `.claude/commands/` mà
85
+ > **không** cài `.agent/`, nên không có `.agent/steps/` để đọc — và bản slim cho `--init`.
86
+ > **G50 gỡ hẳn legacy mode**, nên giờ mọi bản cài đều có `.agent/steps/` và nhánh build thứ hai
87
+ > biến mất. Hai nhánh build gần giống nhau là nợ chờ lệch.
88
+
89
+ ### Tập publish — chỉ ship MỘT bản *(GAPS-v4 G59)*
90
+
91
+ `npm pack` chỉ mang **`bin/` · `core/` · `scripts/` · `docs/`**.
92
+
93
+ Trước G59, `files` có **10 mục**, và **7 trong 10** là bản sao của thứ đã có trong `core/` —
94
+ `commands/` (33/33 file `.md` **byte-identical** với `core/commands/`), cộng
95
+ `hooks/ modules/ rules/ skills/ steps/ templates/` (`diff -rq` không khác gì). Installer đọc
96
+ **chỉ `core/`** (`installCore(coreDir, agentDir, …)`), nên bản thứ hai không bao giờ được dùng.
97
+
98
+ | | Trước | Sau |
99
+ |---|---:|---:|
100
+ | Tarball nén | 1.2 MB | **710 kB** |
101
+ | Giải nén | 4.5 MB | **2.3 MB** |
102
+ | Số file | 395 | **215** |
103
+
104
+ **Nhưng cái đáng sửa hơn là một phép phân biệt bị vô hiệu.** `bin/index.js` dùng
105
+ `hasSources = exists(commands/generate-code.tmpl)` để biết *"đây là dev checkout hay bản cài từ
106
+ npm"*, và chú thích của nó viết thẳng: *"Chỉ nói trong DEV CHECKOUT. **Consumer không cần biết bước
107
+ này tồn tại**"*. Nhưng `commands/` được ship ⇒ `.tmpl` có mặt ⇒ phép thử **luôn đúng** ⇒ **mọi**
108
+ người dùng `npx` thấy `"Vừa sửa commands/*.tmpl ? Chạy npm run build trước"` — một câu họ không thể
109
+ làm gì với nó. *(Kiểm bằng cách `npm pack` rồi chạy tarball thật, không phải suy đoán.)*
110
+
111
+ Bốn nhánh sau khi sửa, `hasSources` đặt tên **một lần**:
112
+
113
+ | `corePrebuilt` | `hasSources` | Nghĩa | Làm gì |
114
+ |:---:|:---:|---|---|
115
+ | ✗ | ✓ | dev checkout, `core/` vắng/lệch | build từ nguồn |
116
+ | ✗ | ✗ | **bản cài npm bị thiếu/hỏng** | **lỗi rõ ràng + `exit 1`** — không cố build (G43: build ghi vào npx cache / global `node_modules`, có thể read-only, và hai `--init` song song sẽ đua nhau) |
117
+ | ✓ | ✓ | dev checkout, đã khớp version | in lời nhắc *"sửa `.tmpl` thì build lại"* |
118
+ | ✓ | ✗ | **bản cài npm, mọi thứ đúng** | **im lặng** ← đường của consumer |
119
+
120
+ Bất biến được `test/run.js` canh, viết theo **hình dạng** chứ không theo danh sách tên nên tự khớp
121
+ với dir thêm sau này: *không mục nào trong `files` được có bản mirror dưới `core/`*.
122
+
123
+ `scripts/` **phải giữ** — `bin/index.js` `require('../scripts/migrate-specs.js')` cho `--migrate-*`.
124
+
125
+ - Cơ chế `{{include:steps/...}}` → single source of truth ở `.tmpl` + `steps/`.
72
126
  - **Không sửa tay** `commands/*.md` / `.agent/` — sửa `.tmpl`/`steps` rồi `node bin/build.js`. *(Quy ước + memory bảo vệ, không phải hook.)*
127
+ - **Sửa `steps/context-loader.md` hay `report-footer.md` giờ có hiệu lực NGAY** ở project đã cài — chúng được đọc lúc chạy, không còn phải build + publish + `/update-framework`.
73
128
  - **Template artifact cũng bị inline lúc build.** `templates/feature.template` và `prd.template.md` được `{{include}}` **nướng cứng** vào file lệnh, nên lệnh không đọc path template lúc chạy — sửa `.agent/templates/` **không có tác dụng**. Đổi cấu trúc `.feature`/PRD = sửa `templates/*` trong repo framework rồi build lại.
74
129
 
75
130
  ### Self-check — contract trace không được lệch
@@ -119,6 +119,63 @@ flowchart LR
119
119
 
120
120
  ---
121
121
 
122
+ ## Sổ trace trong git — và khi nó conflict
123
+
124
+ Sổ trace là **dữ liệu không dựng lại được**, nằm trong git, và **được nhiều người ghi trên
125
+ nhiều nhánh song song**. Ba thứ đó cộng lại nghĩa là git phải được dạy cách merge nó.
126
+
127
+ ### Luật đã cài sẵn
128
+
129
+ `/setup-ai-first` tạo `{trace_dir}/.gitattributes`; `/sync` Step 4c kiểm và tạo hộ nếu thiếu:
130
+
131
+ ```gitattributes
132
+ *.tsv text eol=lf merge=union
133
+ *.jsonl text eol=lf merge=union
134
+ ```
135
+
136
+ | Luật | Không có nó thì sao |
137
+ |---|---|
138
+ | `merge=union` | Hai nhánh cùng ghi một sổ → **conflict**. Giải bằng "take mine" = **mất row của người kia, im lặng**. Với `trace-history.jsonl` (append cuối file) thì **mọi** cặp nhánh song song đều conflict. |
139
+ | `text eol=lf` | Một máy ghi CRLF → git thấy **mọi dòng** đã đổi → union giữ cả hai bản → **nhân đôi cả file**, gồm cả dòng header. Team mixed Windows/macOS gặp ca này mà không ai làm gì sai. |
140
+
141
+ `union` là driver **built-in** của git — không ai cần chạy `git config` gì thêm.
142
+
143
+ > **`*.json` cố ý KHÔNG có trong danh sách.** `trace-report.json` nằm cùng thư mục và union
144
+ > trên JSON tạo ra **JSON không hợp lệ** → panel VS Code parse lỗi. Nó **sinh lại được**:
145
+ > conflict ở đó thì chạy lại `/validate-traces`, đừng merge tay.
146
+
147
+ ### Đánh đổi có chủ ý: trùng row thay vì mất row
148
+
149
+ `merge=union` **có thể** tạo hai row cùng `sc_id`. Đó là **thiết kế**, không phải lỗi:
150
+
151
+ ```
152
+ trùng row → --lint-trace T4 bắt được → /validate-traces reconcile → sạch
153
+ mất row → KHÔNG có gì bắt được → phát hiện sau 3 tuần → khôi phục bằng tay
154
+ ```
155
+
156
+ Đây là lý do luật merge và `--lint-trace` là **một cặp**: union chuyển lỗi từ dạng *im lặng*
157
+ sang dạng *bắt được*, và cái bắt nó phải tồn tại. Có union mà không có lint thì chỉ là đổi chỗ lỗi.
158
+
159
+ ### Playbook — 4 bước
160
+
161
+ Vẫn thấy conflict trong `{trace_dir}/` (nhánh phân kỳ quá xa, hoặc luật vừa mới được thêm):
162
+
163
+ 1. **GIỮ CẢ HAI BÊN.** Xoá ba dòng marker (`<<<<<<<`, `=======`, `>>>>>>>`), giữ toàn bộ row
164
+ của cả hai phía. **Đừng chọn một bên.**
165
+ 2. `npx @educa-corp/sdd-framework --lint-trace` — T7 xác nhận không còn marker, T4 chỉ ra row nào trùng.
166
+ 3. `/validate-traces` — reconcile row trùng về một row đúng, tính lại `status`.
167
+ 4. Commit.
168
+
169
+ **Ba điều không bao giờ làm:**
170
+
171
+ | ❌ | Vì sao |
172
+ |---|---|
173
+ | `git checkout --ours/--theirs` trên file trace | Xoá sạch row của một phía. Không có gì báo. |
174
+ | Xoá file trace để "cho sạch" rồi chạy lại pipeline | Sổ **không regenerate được** — `trace-history.jsonl` mất là mất vĩnh viễn. |
175
+ | Commit khi còn marker | Marker thành một "row" TSV. `--lint-trace` T7 bắt, nhưng nếu chưa cắm vào CI thì nó vào nhánh chung. |
176
+
177
+ ---
178
+
122
179
  ## Đọc tiếp (Next)
123
180
 
124
181
  - [Architecture](architecture.md) — 3 lớp & context-loader