@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
package/bin/build.js CHANGED
@@ -23,6 +23,41 @@ const SKILLS_DIR = path.join(ROOT, 'skills');
23
23
  // Cache for step file contents
24
24
  const stepCache = {};
25
25
 
26
+ // ── Step nào ĐỌC LÚC CHẠY thay vì nướng vào từng lệnh (GAPS-v3 G45) ──────────
27
+ //
28
+ // Vì sao: build inline `{{include:}}` vào TỪNG file lệnh, nên 32 lệnh = 2069 KB mà chỉ
29
+ // 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.
30
+ // `/generate-code` = 108 KB ≈ 27.000 token đọc TRƯỚC khi làm gì; gần một nửa không nói
31
+ // gì về việc sinh code. `/refine-prd` thì 75% là nội dung dùng chung.
32
+ //
33
+ // Cái giá thật không phải tiền: trên PRD nhiều UC, nạp 27k token chỉ để đọc chỉ dẫn làm
34
+ // tăng rủi ro CẠN CONTEXT GIỮA CHỪNG — mà thứ đang được ghi lúc đó là sổ trace.
35
+ //
36
+ // PHẠM VI CÓ CHỦ Ý — chỉ hai step, không phải tất cả:
37
+ // context-loader (64% lãng phí) · report-footer (16%) → đọc lúc chạy
38
+ // gate (15%) → GIỮ INLINE
39
+ // Gate là lưới an toàn (model check · resolve target · CHECKPOINT). Bỏ sót gate thì lệnh
40
+ // VẪN CHẠY, chỉ là không còn cổng nào — hỏng ÂM THẦM. Bỏ sót context-loader thì lệnh dừng
41
+ // ngay vì thiếu path/config — hỏng ỒN ÀO, thấy liền. Đổi 15% lấy một lưới an toàn là lỗ.
42
+ //
43
+ // MỘT biến thể duy nhất kể từ khi G50 gỡ legacy mode. Trước đó phải build hai bản —
44
+ // `commands/*.md` inline đầy đủ cho legacy (nó copy thẳng file đó mà KHÔNG cài `.agent/`,
45
+ // nên không có `.agent/steps/` để đọc) và `core/commands/*.md` slim cho `--init`. Legacy
46
+ // đi rồi thì mọi bản cài đều có `.agent/steps/`, và nhánh build thứ hai là nợ chờ lệch.
47
+ const RUNTIME_READ = {
48
+ 'steps/context-loader.md':
49
+ '**BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,\n' +
50
+ 'rồi mới tiếp tục phần bên dưới.\n' +
51
+ '\n' +
52
+ 'Bỏ qua bước này thì `{paths.*}`, `{tech_stack.*}`, `{conventions.*}`, guardrail từ\n' +
53
+ '`project-lessons`, và routing service (chế độ umbrella) đều **chưa được phân giải** — mọi\n' +
54
+ 'placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.',
55
+
56
+ 'steps/report-footer.md':
57
+ '**Đọc `.agent/steps/report-footer.md`** và áp đúng khuôn footer trong đó (Status Badge ·\n' +
58
+ 'Output Artifacts · Next) cho report cuối, kèm khối bên dưới.',
59
+ };
60
+
26
61
  /**
27
62
  * Load a step file by name, caching the result.
28
63
  * @param {string} stepPath - e.g. "steps/gate.md"
@@ -54,10 +89,11 @@ function buildTemplate(tmplFile, label) {
54
89
  try {
55
90
  let content = fs.readFileSync(tmplFile, 'utf8');
56
91
 
57
- // Replace all {{include:...}} markers
92
+ // Replace all {{include:...}} markers — trừ những step nằm trong RUNTIME_READ,
93
+ // chúng được thay bằng một chỉ dẫn ĐỌC LÚC CHẠY thay vì nướng 33 KB vào đây (G45).
58
94
  content = content.replace(/\{\{include:([^}]+)\}\}/g, (match, includePath) => {
59
- const trimmed = includePath.trim();
60
- return loadStep(trimmed);
95
+ const key = includePath.trim();
96
+ return RUNTIME_READ[key] !== undefined ? RUNTIME_READ[key] : loadStep(key);
61
97
  });
62
98
 
63
99
  fs.writeFileSync(outFile, content, 'utf8');
@@ -155,6 +191,8 @@ if (skillTmplFiles.length === 0) {
155
191
  // It is gitignored but included in the npm package (via "files" whitelist).
156
192
 
157
193
  const CORE_DIR = path.join(ROOT, 'core');
194
+ const CORE_TMP = path.join(ROOT, 'core.tmp');
195
+ const CORE_BAK = path.join(ROOT, 'core.bak');
158
196
  const pkg = JSON.parse(fs.readFileSync(path.join(ROOT, 'package.json'), 'utf8'));
159
197
  const VERSION = pkg.version;
160
198
 
@@ -162,11 +200,48 @@ console.log('');
162
200
  console.log('Populating core/ (distributable) ...');
163
201
  console.log('');
164
202
 
165
- // Clean and recreate core/
166
- if (fs.existsSync(CORE_DIR)) {
167
- fs.rmSync(CORE_DIR, { recursive: true });
203
+ // Dựng vào core.tmp/ rồi SWAP — không bao giờ xoá core/ trước khi có bản thay thế
204
+ // hoàn chỉnh (GAPS-v3 G43).
205
+ //
206
+ // Bản cũ làm `rmSync(core/)` rồi mới dựng lại. Nếu bước dựng fail nửa đường — đĩa đầy,
207
+ // thư mục read-only, Ctrl-C — thì core/ ĐÃ MẤT và không có gì thay thế. Với consumer thì
208
+ // hậu quả nặng hơn hẳn: `--init` chạy build ngay trong thư mục npx cache, nên một lần fail
209
+ // làm hỏng package trong cache VĨNH VIỄN, và mọi lần chạy sau đều báo "core/ not found.
210
+ // The package may not have been built correctly" — một thông điệp dẫn sai hướng, vì package
211
+ // được build đúng, chính installer đã xoá nó.
212
+ //
213
+ // Dọn rác của lần chạy trước (nếu có) — không thì swap sẽ trượt vào thư mục cũ.
214
+ for (const stale of [CORE_TMP, CORE_BAK]) {
215
+ if (fs.existsSync(stale)) fs.rmSync(stale, { recursive: true, force: true });
216
+ }
217
+
218
+ // Kiểm quyền ghi TRƯỚC khi làm gì cả: fail ở đây thì chưa có gì bị đụng tới.
219
+ try {
220
+ fs.mkdirSync(CORE_TMP, { recursive: true });
221
+ } catch (err) {
222
+ console.error('');
223
+ console.error(`❌ Không ghi được vào thư mục package: ${ROOT}`);
224
+ console.error(` (${err.code || err.message})`);
225
+ console.error('');
226
+ console.error(' Đây là bản cài read-only. core/ hiện tại KHÔNG bị đụng tới.');
227
+ console.error(' Thử: npx -y @educa-corp/sdd-framework@latest --init');
228
+ console.error(' Hoặc xoá cache npx: rm -rf ~/.npm/_npx');
229
+ console.error('');
230
+ process.exit(1);
231
+ }
232
+
233
+ /** Đưa core.tmp/ vào chỗ core/. Fail ở bất kỳ đâu thì khôi phục bản cũ. */
234
+ function swapCoreIntoPlace() {
235
+ const hadOld = fs.existsSync(CORE_DIR);
236
+ if (hadOld) fs.renameSync(CORE_DIR, CORE_BAK);
237
+ try {
238
+ fs.renameSync(CORE_TMP, CORE_DIR);
239
+ } catch (err) {
240
+ if (hadOld) fs.renameSync(CORE_BAK, CORE_DIR); // core/ trở lại nguyên trạng
241
+ throw err;
242
+ }
243
+ if (hadOld) fs.rmSync(CORE_BAK, { recursive: true, force: true });
168
244
  }
169
- fs.mkdirSync(CORE_DIR, { recursive: true });
170
245
 
171
246
  /**
172
247
  * Recursively copy src → dest, optionally filtering files by name.
@@ -190,29 +265,31 @@ function copyDirToCore(src, dest, fileFilter) {
190
265
 
191
266
  const coreMappings = [
192
267
  // Built command .md files (not .tmpl sources)
193
- { src: COMMANDS_DIR, dest: path.join(CORE_DIR, 'commands'), filter: f => f.endsWith('.md') },
268
+ { src: COMMANDS_DIR, dest: path.join(CORE_TMP, 'commands'), filter: f => f.endsWith('.md') },
194
269
  // Step files (gate, context-loader, spawn-agent, report-footer)
195
- { src: path.join(ROOT, 'steps'), dest: path.join(CORE_DIR, 'steps'), filter: null },
270
+ { src: path.join(ROOT, 'steps'), dest: path.join(CORE_TMP, 'steps'), filter: null },
196
271
  // Hook scripts
197
- { src: path.join(ROOT, 'hooks'), dest: path.join(CORE_DIR, 'hooks'), filter: null },
272
+ { src: path.join(ROOT, 'hooks'), dest: path.join(CORE_TMP, 'hooks'), filter: null },
198
273
  // Data-protection and workflow rules
199
- { src: path.join(ROOT, 'rules'), dest: path.join(CORE_DIR, 'rules'), filter: null },
274
+ { src: path.join(ROOT, 'rules'), dest: path.join(CORE_TMP, 'rules'), filter: null },
200
275
  // PRD / architecture templates
201
- { src: path.join(ROOT, 'templates'), dest: path.join(CORE_DIR, 'templates'), filter: null },
276
+ { src: path.join(ROOT, 'templates'), dest: path.join(CORE_TMP, 'templates'), filter: null },
202
277
  // Stack module profiles
203
- { src: path.join(ROOT, 'modules'), dest: path.join(CORE_DIR, 'modules'), filter: null },
278
+ { src: path.join(ROOT, 'modules'), dest: path.join(CORE_TMP, 'modules'), filter: null },
204
279
  // Built skill .md files (not .tmpl sources)
205
- { src: SKILLS_DIR, dest: path.join(CORE_DIR, 'skills'), filter: f => !f.endsWith('.tmpl') },
280
+ { src: SKILLS_DIR, dest: path.join(CORE_TMP, 'skills'), filter: f => !f.endsWith('.tmpl') },
206
281
  ];
207
282
 
208
283
  for (const { src, dest, filter } of coreMappings) {
209
284
  copyDirToCore(src, dest, filter);
210
- const rel = path.relative(ROOT, dest);
211
- console.log(` ✅ ${rel}/`);
285
+ // Báo theo ĐÍCH CUỐI (core/…), không phải thư mục dựng trung gian. core.tmp/ là chi
286
+ // tiết của bước swap (G43) — người đọc log quan tâm cái gì được cài, không quan tâm
287
+ // nó đi qua đâu.
288
+ console.log(` ✅ core/${path.basename(dest)}/`);
212
289
  }
213
290
 
214
- // Write FRAMEWORK_VERSION so upgrade.sh can compare installed vs latest
215
- fs.writeFileSync(path.join(CORE_DIR, 'FRAMEWORK_VERSION'), VERSION + '\n', 'utf8');
291
+ // Write FRAMEWORK_VERSION so /update-framework can compare installed vs latest
292
+ fs.writeFileSync(path.join(CORE_TMP, 'FRAMEWORK_VERSION'), VERSION + '\n', 'utf8');
216
293
  console.log(` ✅ core/FRAMEWORK_VERSION (v${VERSION})`);
217
294
 
218
295
  // .agent/README.md — which paths under .agent/ a project may edit, and which the
@@ -220,10 +297,27 @@ console.log(` ✅ core/FRAMEWORK_VERSION (v${VERSION})`);
220
297
  // land inside .agent/templates/.
221
298
  const agentReadmeSrc = path.join(ROOT, 'AGENT_README.md');
222
299
  if (fs.existsSync(agentReadmeSrc)) {
223
- fs.copyFileSync(agentReadmeSrc, path.join(CORE_DIR, 'README.md'));
300
+ fs.copyFileSync(agentReadmeSrc, path.join(CORE_TMP, 'README.md'));
224
301
  console.log(' ✅ core/README.md (← AGENT_README.md)');
225
302
  }
226
303
 
304
+ // ── 3b. Swap core.tmp/ → core/ ───────────────────────────────────────────────
305
+ //
306
+ // Tới đây core.tmp/ đã hoàn chỉnh. Đây là điểm duy nhất core/ bị thay, và nó là
307
+ // một phép rename — gần như nguyên tử, không có cửa sổ nào core/ vắng mặt-và-không-
308
+ // có-bản-thay-thế. Fail thì swapCoreIntoPlace() khôi phục bản cũ (G43).
309
+ try {
310
+ swapCoreIntoPlace();
311
+ } catch (err) {
312
+ console.error('');
313
+ console.error(`❌ Không thay được core/: ${err.code || err.message}`);
314
+ console.error(' core/ cũ đã được khôi phục — package vẫn dùng được.');
315
+ console.error(` Bản vừa dựng còn ở: ${path.relative(ROOT, CORE_TMP)}/`);
316
+ console.error(' Nguyên nhân thường gặp: file trong core/ đang bị process khác giữ (antivirus, editor).');
317
+ console.error('');
318
+ process.exit(1);
319
+ }
320
+
227
321
  // ── 4. Sync local dogfood .agent/ (dev repo only) ────────────────────────────
228
322
  //
229
323
  // In THIS framework repo, .agent/ is a pure mirror of core/ used to dogfood the