@tdk-landscape/tdk-cli-core 1.3.53 → 1.3.55

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 (392) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +1 -1
  3. package/cli/dist/commands/doctor.js +21 -3
  4. package/cli/dist/commands/upgrade.js +7 -7
  5. package/cli/dist/generator/extension-fetch.js +16 -9
  6. package/cli/dist/utils/spinner.js +39 -0
  7. package/cli/package.json +1 -2
  8. package/package.json +16 -2
  9. package/.claude/skills/layer-autoresearch/SKILL.md +0 -48
  10. package/.claude/skills/tdk-troubleshoot/SKILL.md +0 -222
  11. package/.github/ISSUE_TEMPLATE/bug_report.yml +0 -40
  12. package/.github/ISSUE_TEMPLATE/config.yml +0 -11
  13. package/.github/ISSUE_TEMPLATE/feature_request.yml +0 -18
  14. package/.github/ISSUE_TEMPLATE/premium_license.yml +0 -43
  15. package/.github/dependabot.yml +0 -33
  16. package/.github/pull_request_template.md +0 -5
  17. package/.github/workflows/ci.yml +0 -200
  18. package/.github/workflows/release-binaries.yml +0 -139
  19. package/.github/zizmor.yml +0 -9
  20. package/.pre-commit-config.yaml +0 -57
  21. package/CODE_OF_CONDUCT.md +0 -9
  22. package/CONTRIBUTING.md +0 -42
  23. package/Makefile +0 -92
  24. package/SECURITY.md +0 -17
  25. package/benchmarks/results/container-scale-2026-09-25T20-54-49-736Z.json +0 -45
  26. package/benchmarks/results/container-scale-2026-09-25T21-01-20-540Z.json +0 -150
  27. package/benchmarks/results/container-scale-2026-09-25T21-15-01-441Z.json +0 -73
  28. package/benchmarks/results/container-scale-2026-09-25T22-09-01-630Z.json +0 -77
  29. package/benchmarks/results/container-scale-2026-09-25T22-18-28-371Z.json +0 -202
  30. package/cli/AGENTS.md +0 -390
  31. package/cli/DESIGN.md +0 -735
  32. package/cli/README.md +0 -292
  33. package/cli/biome.json +0 -40
  34. package/cli/dependency-graph.svg +0 -1135
  35. package/cli/dist/cli.d.ts +0 -3
  36. package/cli/dist/cli.d.ts.map +0 -1
  37. package/cli/dist/cli.js.map +0 -1
  38. package/cli/dist/commands/completion.d.ts +0 -3
  39. package/cli/dist/commands/completion.d.ts.map +0 -1
  40. package/cli/dist/commands/completion.js.map +0 -1
  41. package/cli/dist/commands/config.d.ts +0 -3
  42. package/cli/dist/commands/config.d.ts.map +0 -1
  43. package/cli/dist/commands/config.js.map +0 -1
  44. package/cli/dist/commands/doctor.d.ts +0 -19
  45. package/cli/dist/commands/doctor.d.ts.map +0 -1
  46. package/cli/dist/commands/doctor.js.map +0 -1
  47. package/cli/dist/commands/down.d.ts +0 -3
  48. package/cli/dist/commands/down.d.ts.map +0 -1
  49. package/cli/dist/commands/down.js.map +0 -1
  50. package/cli/dist/commands/help.d.ts +0 -2
  51. package/cli/dist/commands/help.d.ts.map +0 -1
  52. package/cli/dist/commands/help.js.map +0 -1
  53. package/cli/dist/commands/networks.d.ts +0 -3
  54. package/cli/dist/commands/networks.d.ts.map +0 -1
  55. package/cli/dist/commands/networks.js.map +0 -1
  56. package/cli/dist/commands/project.d.ts +0 -9
  57. package/cli/dist/commands/project.d.ts.map +0 -1
  58. package/cli/dist/commands/project.js.map +0 -1
  59. package/cli/dist/commands/projects.d.ts +0 -3
  60. package/cli/dist/commands/projects.d.ts.map +0 -1
  61. package/cli/dist/commands/projects.js.map +0 -1
  62. package/cli/dist/commands/resource.d.ts +0 -69
  63. package/cli/dist/commands/resource.d.ts.map +0 -1
  64. package/cli/dist/commands/resource.js.map +0 -1
  65. package/cli/dist/commands/resources.d.ts +0 -3
  66. package/cli/dist/commands/resources.d.ts.map +0 -1
  67. package/cli/dist/commands/resources.js.map +0 -1
  68. package/cli/dist/commands/stack.d.ts +0 -3
  69. package/cli/dist/commands/stack.d.ts.map +0 -1
  70. package/cli/dist/commands/stack.js.map +0 -1
  71. package/cli/dist/commands/stacks.d.ts +0 -3
  72. package/cli/dist/commands/stacks.d.ts.map +0 -1
  73. package/cli/dist/commands/stacks.js.map +0 -1
  74. package/cli/dist/commands/status.d.ts +0 -3
  75. package/cli/dist/commands/status.d.ts.map +0 -1
  76. package/cli/dist/commands/status.js.map +0 -1
  77. package/cli/dist/commands/ui.d.ts +0 -3
  78. package/cli/dist/commands/ui.d.ts.map +0 -1
  79. package/cli/dist/commands/ui.js.map +0 -1
  80. package/cli/dist/commands/up.d.ts +0 -3
  81. package/cli/dist/commands/up.d.ts.map +0 -1
  82. package/cli/dist/commands/up.js.map +0 -1
  83. package/cli/dist/commands/upgrade.d.ts +0 -14
  84. package/cli/dist/commands/upgrade.d.ts.map +0 -1
  85. package/cli/dist/commands/upgrade.js.map +0 -1
  86. package/cli/dist/commands/version.d.ts +0 -3
  87. package/cli/dist/commands/version.d.ts.map +0 -1
  88. package/cli/dist/commands/version.js.map +0 -1
  89. package/cli/dist/components/Accessible.d.ts +0 -8
  90. package/cli/dist/components/Accessible.d.ts.map +0 -1
  91. package/cli/dist/components/Accessible.js.map +0 -1
  92. package/cli/dist/components/BaseTooltip.d.ts +0 -4
  93. package/cli/dist/components/BaseTooltip.d.ts.map +0 -1
  94. package/cli/dist/components/BaseTooltip.js.map +0 -1
  95. package/cli/dist/components/DetailPanel.d.ts +0 -4
  96. package/cli/dist/components/DetailPanel.d.ts.map +0 -1
  97. package/cli/dist/components/DetailPanel.js.map +0 -1
  98. package/cli/dist/components/FileTree.d.ts +0 -4
  99. package/cli/dist/components/FileTree.d.ts.map +0 -1
  100. package/cli/dist/components/FileTree.js.map +0 -1
  101. package/cli/dist/components/ResourceSelectInput.d.ts +0 -4
  102. package/cli/dist/components/ResourceSelectInput.d.ts.map +0 -1
  103. package/cli/dist/components/ResourceSelectInput.js.map +0 -1
  104. package/cli/dist/components/ResourceTable.d.ts +0 -4
  105. package/cli/dist/components/ResourceTable.d.ts.map +0 -1
  106. package/cli/dist/components/ResourceTable.js.map +0 -1
  107. package/cli/dist/components/TabBar.d.ts +0 -4
  108. package/cli/dist/components/TabBar.d.ts.map +0 -1
  109. package/cli/dist/components/TabBar.js.map +0 -1
  110. package/cli/dist/components/Tooltip.d.ts +0 -12
  111. package/cli/dist/components/Tooltip.d.ts.map +0 -1
  112. package/cli/dist/components/Tooltip.js.map +0 -1
  113. package/cli/dist/components/index.d.ts +0 -8
  114. package/cli/dist/components/index.d.ts.map +0 -1
  115. package/cli/dist/components/index.js.map +0 -1
  116. package/cli/dist/config/platform-standards.d.ts +0 -159
  117. package/cli/dist/config/platform-standards.d.ts.map +0 -1
  118. package/cli/dist/config/platform-standards.js.map +0 -1
  119. package/cli/dist/generator/extension-fetch.d.ts +0 -25
  120. package/cli/dist/generator/extension-fetch.d.ts.map +0 -1
  121. package/cli/dist/generator/extension-fetch.js.map +0 -1
  122. package/cli/dist/generator/template-engine.d.ts +0 -62
  123. package/cli/dist/generator/template-engine.d.ts.map +0 -1
  124. package/cli/dist/generator/template-engine.js.map +0 -1
  125. package/cli/dist/index.d.ts +0 -11
  126. package/cli/dist/index.d.ts.map +0 -1
  127. package/cli/dist/index.js.map +0 -1
  128. package/cli/dist/types/index.d.ts +0 -257
  129. package/cli/dist/types/index.d.ts.map +0 -1
  130. package/cli/dist/types/index.js.map +0 -1
  131. package/cli/dist/utils/cache.d.ts +0 -19
  132. package/cli/dist/utils/cache.d.ts.map +0 -1
  133. package/cli/dist/utils/cache.js.map +0 -1
  134. package/cli/dist/utils/command-helpers.d.ts +0 -17
  135. package/cli/dist/utils/command-helpers.d.ts.map +0 -1
  136. package/cli/dist/utils/command-helpers.js.map +0 -1
  137. package/cli/dist/utils/constants.d.ts +0 -53
  138. package/cli/dist/utils/constants.d.ts.map +0 -1
  139. package/cli/dist/utils/constants.js.map +0 -1
  140. package/cli/dist/utils/discovery-context.d.ts +0 -4
  141. package/cli/dist/utils/discovery-context.d.ts.map +0 -1
  142. package/cli/dist/utils/discovery-context.js.map +0 -1
  143. package/cli/dist/utils/docker.d.ts +0 -4
  144. package/cli/dist/utils/docker.d.ts.map +0 -1
  145. package/cli/dist/utils/docker.js.map +0 -1
  146. package/cli/dist/utils/doctor-runtime.d.ts +0 -81
  147. package/cli/dist/utils/doctor-runtime.d.ts.map +0 -1
  148. package/cli/dist/utils/doctor-runtime.js.map +0 -1
  149. package/cli/dist/utils/env-validator.d.ts +0 -15
  150. package/cli/dist/utils/env-validator.d.ts.map +0 -1
  151. package/cli/dist/utils/env-validator.js.map +0 -1
  152. package/cli/dist/utils/errors.d.ts +0 -31
  153. package/cli/dist/utils/errors.d.ts.map +0 -1
  154. package/cli/dist/utils/errors.js.map +0 -1
  155. package/cli/dist/utils/file-helpers.d.ts +0 -14
  156. package/cli/dist/utils/file-helpers.d.ts.map +0 -1
  157. package/cli/dist/utils/file-helpers.js.map +0 -1
  158. package/cli/dist/utils/formatting.d.ts +0 -30
  159. package/cli/dist/utils/formatting.d.ts.map +0 -1
  160. package/cli/dist/utils/formatting.js.map +0 -1
  161. package/cli/dist/utils/paths.d.ts +0 -3
  162. package/cli/dist/utils/paths.d.ts.map +0 -1
  163. package/cli/dist/utils/paths.js.map +0 -1
  164. package/cli/dist/utils/port-assignment.d.ts +0 -6
  165. package/cli/dist/utils/port-assignment.d.ts.map +0 -1
  166. package/cli/dist/utils/port-assignment.js.map +0 -1
  167. package/cli/dist/utils/project-features.d.ts +0 -23
  168. package/cli/dist/utils/project-features.d.ts.map +0 -1
  169. package/cli/dist/utils/project-features.js.map +0 -1
  170. package/cli/dist/utils/project-templates.d.ts +0 -7
  171. package/cli/dist/utils/project-templates.d.ts.map +0 -1
  172. package/cli/dist/utils/project-templates.js.map +0 -1
  173. package/cli/dist/utils/prompt.d.ts +0 -28
  174. package/cli/dist/utils/prompt.d.ts.map +0 -1
  175. package/cli/dist/utils/prompt.js.map +0 -1
  176. package/cli/dist/utils/resource-features.d.ts +0 -29
  177. package/cli/dist/utils/resource-features.d.ts.map +0 -1
  178. package/cli/dist/utils/resource-features.js.map +0 -1
  179. package/cli/dist/utils/service-urls.d.ts +0 -30
  180. package/cli/dist/utils/service-urls.d.ts.map +0 -1
  181. package/cli/dist/utils/service-urls.js.map +0 -1
  182. package/cli/dist/utils/services.d.ts +0 -18
  183. package/cli/dist/utils/services.d.ts.map +0 -1
  184. package/cli/dist/utils/services.js.map +0 -1
  185. package/cli/dist/utils/stack-features.d.ts +0 -22
  186. package/cli/dist/utils/stack-features.d.ts.map +0 -1
  187. package/cli/dist/utils/stack-features.js.map +0 -1
  188. package/cli/dist/utils/tar.d.ts +0 -7
  189. package/cli/dist/utils/tar.d.ts.map +0 -1
  190. package/cli/dist/utils/tar.js.map +0 -1
  191. package/cli/dist/utils/tilt.d.ts +0 -20
  192. package/cli/dist/utils/tilt.d.ts.map +0 -1
  193. package/cli/dist/utils/tilt.js.map +0 -1
  194. package/cli/dist/utils/validation.d.ts +0 -16
  195. package/cli/dist/utils/validation.d.ts.map +0 -1
  196. package/cli/dist/utils/validation.js.map +0 -1
  197. package/cli/knip.json +0 -6
  198. package/cli/pnpm-lock.yaml +0 -2305
  199. package/cli/pnpm-workspace.yaml +0 -2
  200. package/cli/src/cli.ts +0 -72
  201. package/cli/src/commands/__tests__/always-enabled-infra.test.ts +0 -109
  202. package/cli/src/commands/__tests__/config.test.ts +0 -146
  203. package/cli/src/commands/__tests__/doctor.test.ts +0 -730
  204. package/cli/src/commands/__tests__/error-handling.test.ts +0 -78
  205. package/cli/src/commands/__tests__/phases-model.test.ts +0 -161
  206. package/cli/src/commands/__tests__/project-resource-features-e2e.test.ts +0 -187
  207. package/cli/src/commands/__tests__/project.test.ts +0 -68
  208. package/cli/src/commands/__tests__/resource.test.ts +0 -226
  209. package/cli/src/commands/__tests__/saas-starter-integration.test.ts +0 -194
  210. package/cli/src/commands/__tests__/stack-features-e2e.test.ts +0 -135
  211. package/cli/src/commands/__tests__/upgrade.test.ts +0 -198
  212. package/cli/src/commands/completion.ts +0 -300
  213. package/cli/src/commands/config.ts +0 -299
  214. package/cli/src/commands/doctor.ts +0 -926
  215. package/cli/src/commands/down.ts +0 -37
  216. package/cli/src/commands/help.ts +0 -143
  217. package/cli/src/commands/networks-design.md +0 -80
  218. package/cli/src/commands/networks.ts +0 -332
  219. package/cli/src/commands/project.ts +0 -456
  220. package/cli/src/commands/projects.ts +0 -82
  221. package/cli/src/commands/resource.ts +0 -652
  222. package/cli/src/commands/resources.ts +0 -95
  223. package/cli/src/commands/stack.ts +0 -128
  224. package/cli/src/commands/stacks.ts +0 -52
  225. package/cli/src/commands/status.ts +0 -88
  226. package/cli/src/commands/ui.tsx +0 -843
  227. package/cli/src/commands/up.ts +0 -169
  228. package/cli/src/commands/upgrade.ts +0 -628
  229. package/cli/src/commands/version.ts +0 -9
  230. package/cli/src/components/Accessible.tsx +0 -21
  231. package/cli/src/components/BaseTooltip.tsx +0 -66
  232. package/cli/src/components/DetailPanel.tsx +0 -146
  233. package/cli/src/components/FileTree.tsx +0 -89
  234. package/cli/src/components/ResourceSelectInput.tsx +0 -30
  235. package/cli/src/components/ResourceTable.tsx +0 -78
  236. package/cli/src/components/TabBar.tsx +0 -53
  237. package/cli/src/components/Tooltip.tsx +0 -11
  238. package/cli/src/components/index.ts +0 -7
  239. package/cli/src/config/platform-standards.test.ts +0 -11
  240. package/cli/src/config/platform-standards.ts +0 -196
  241. package/cli/src/generator/__tests__/extension-fetch.test.ts +0 -242
  242. package/cli/src/generator/__tests__/template-engine.test.ts +0 -70
  243. package/cli/src/generator/extension-fetch.ts +0 -286
  244. package/cli/src/generator/template-engine.ts +0 -752
  245. package/cli/src/index.ts +0 -86
  246. package/cli/src/templates.d.ts +0 -4
  247. package/cli/src/types/index.ts +0 -353
  248. package/cli/src/utils/__tests__/cache.test.ts +0 -98
  249. package/cli/src/utils/__tests__/formatting.test.ts +0 -170
  250. package/cli/src/utils/__tests__/paths.test.ts +0 -180
  251. package/cli/src/utils/__tests__/port-assignment.test.ts +0 -121
  252. package/cli/src/utils/__tests__/service-urls.test.ts +0 -174
  253. package/cli/src/utils/__tests__/services.test.ts +0 -531
  254. package/cli/src/utils/__tests__/tilt.test.ts +0 -172
  255. package/cli/src/utils/__tests__/validation.test.ts +0 -148
  256. package/cli/src/utils/cache.ts +0 -76
  257. package/cli/src/utils/command-helpers.ts +0 -54
  258. package/cli/src/utils/constants.ts +0 -130
  259. package/cli/src/utils/discovery-context.ts +0 -49
  260. package/cli/src/utils/docker.ts +0 -20
  261. package/cli/src/utils/doctor-runtime.ts +0 -644
  262. package/cli/src/utils/env-validator.ts +0 -139
  263. package/cli/src/utils/errors.ts +0 -168
  264. package/cli/src/utils/file-helpers.ts +0 -62
  265. package/cli/src/utils/formatting.ts +0 -215
  266. package/cli/src/utils/paths.ts +0 -53
  267. package/cli/src/utils/port-assignment.ts +0 -106
  268. package/cli/src/utils/project-features.ts +0 -118
  269. package/cli/src/utils/project-templates.ts +0 -29
  270. package/cli/src/utils/prompt.ts +0 -128
  271. package/cli/src/utils/resource-features.ts +0 -215
  272. package/cli/src/utils/service-urls.ts +0 -124
  273. package/cli/src/utils/services.ts +0 -364
  274. package/cli/src/utils/stack-features.ts +0 -30
  275. package/cli/src/utils/tar.ts +0 -24
  276. package/cli/src/utils/tilt.ts +0 -133
  277. package/cli/src/utils/validation.ts +0 -67
  278. package/cli/tsconfig.json +0 -25
  279. package/cli/tsconfig.typecheck.json +0 -8
  280. package/docs/FEATURES.md +0 -281
  281. package/docs/demo.svg +0 -217
  282. package/engine/docs/AUTODISCOVERY_TEST.md +0 -182
  283. package/engine/docs/C4-ARCHITECTURE-operations.md +0 -396
  284. package/engine/docs/C4-ARCHITECTURE-overview.md +0 -284
  285. package/engine/docs/C4-ARCHITECTURE.md +0 -17
  286. package/engine/docs/GOLDEN_IMAGE_INTEGRATION.md +0 -349
  287. package/engine/docs/MANIFEST_DRIVEN_ARCHITECTURE.md +0 -357
  288. package/engine/docs/README.md +0 -284
  289. package/install.sh +0 -19
  290. package/knip.json +0 -10
  291. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/.openspec.yaml +0 -2
  292. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/design.md +0 -102
  293. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/proposal.md +0 -31
  294. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-notifications/spec.md +0 -48
  295. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-orchestration/spec.md +0 -63
  296. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/specs/vscode-tdk-troubleshooting/spec.md +0 -56
  297. package/openspec/changes/add-vscode-tdk-troubleshooter-plugin/tasks.md +0 -61
  298. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/.openspec.yaml +0 -2
  299. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/design.md +0 -114
  300. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/proposal.md +0 -32
  301. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/manifest-field-migration/spec.md +0 -20
  302. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/resource-terminology-standard/spec.md +0 -20
  303. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/specs/stack-terminology-standard/spec.md +0 -20
  304. package/openspec/changes/archive/2026-04-28-rename-domain-to-stack-service-to-resource/tasks.md +0 -194
  305. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/.openspec.yaml +0 -2
  306. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/design.md +0 -111
  307. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/proposal.md +0 -33
  308. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/function-naming-alignment/spec.md +0 -12
  309. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/optional-parameter-patterns/spec.md +0 -18
  310. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-documentation/spec.md +0 -16
  311. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-naming-consistency/spec.md +0 -12
  312. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/specs/parameter-ordering-standard/spec.md +0 -12
  313. package/openspec/changes/archive/2026-04-28-star-files-parameter-fixes/tasks.md +0 -194
  314. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/.openspec.yaml +0 -2
  315. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/design.md +0 -84
  316. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/proposal.md +0 -31
  317. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/codebase-audit/spec.md +0 -27
  318. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/dead-code-cleanup/spec.md +0 -27
  319. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/test-gap-fill/spec.md +0 -23
  320. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/specs/todo-resolution/spec.md +0 -27
  321. package/openspec/changes/archive/2026-04-29-scan-repo-gaps-fix/tasks.md +0 -39
  322. package/openspec/changes/archive/2026-06-25-landscape-consolidation/.openspec.yaml +0 -2
  323. package/openspec/changes/archive/2026-06-25-landscape-consolidation/design.md +0 -80
  324. package/openspec/changes/archive/2026-06-25-landscape-consolidation/proposal.md +0 -40
  325. package/openspec/changes/archive/2026-06-25-landscape-consolidation/spec.md +0 -52
  326. package/openspec/changes/archive/2026-06-25-landscape-consolidation/tasks.md +0 -51
  327. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/.openspec.yaml +0 -2
  328. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/design.md +0 -43
  329. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/proposal.md +0 -29
  330. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/specs/self-contained-generated-projects/spec.md +0 -67
  331. package/openspec/changes/archive/2026-09-17-fix-generated-stack-boot/tasks.md +0 -27
  332. package/openspec/changes/optimize-runtime-docker-layers/.openspec.yaml +0 -3
  333. package/openspec/changes/optimize-runtime-docker-layers/design.md +0 -81
  334. package/openspec/changes/optimize-runtime-docker-layers/proposal.md +0 -44
  335. package/openspec/changes/optimize-runtime-docker-layers/specs/runtime-container-footprint/spec.md +0 -79
  336. package/openspec/changes/optimize-runtime-docker-layers/tasks.md +0 -36
  337. package/openspec/changes/prioritized-cold-start/.openspec.yaml +0 -4
  338. package/openspec/changes/prioritized-cold-start/design.md +0 -137
  339. package/openspec/changes/prioritized-cold-start/proposal.md +0 -41
  340. package/openspec/changes/prioritized-cold-start/specs/on-demand-resource-startup/spec.md +0 -98
  341. package/openspec/changes/prioritized-cold-start/tasks.md +0 -69
  342. package/openspec/config.yaml +0 -1
  343. package/openspec/specs/manifest-field-migration/spec.md +0 -24
  344. package/openspec/specs/resource-terminology-standard/spec.md +0 -24
  345. package/openspec/specs/self-contained-generated-projects/spec.md +0 -67
  346. package/openspec/specs/stack-terminology-standard/spec.md +0 -24
  347. package/platform/services/platform/proxy/service.yaml +0 -14
  348. package/scripts/audit-codebase.sh +0 -213
  349. package/scripts/autoresearch/layer-eval.sh +0 -65
  350. package/scripts/benchmark/README.md +0 -25
  351. package/scripts/benchmark/container-scale.ts +0 -503
  352. package/scripts/benchmark/pre-release-gate.sh +0 -9
  353. package/scripts/install-git-hooks.sh +0 -14
  354. package/scripts/measure-idle-footprint.sh +0 -58
  355. package/scripts/record-demo.sh +0 -43
  356. package/scripts/release-binaries.sh +0 -97
  357. package/scripts/setup-pre-commit.sh +0 -33
  358. package/scripts/stage-npm-engine.sh +0 -36
  359. package/tests/tilt-engine/__init__.py +0 -5
  360. package/tests/tilt-engine/conftest.py +0 -182
  361. package/tests/tilt-engine/fixtures/appointment-frontend-service.json +0 -11
  362. package/tests/tilt-engine/fixtures/identity-backend-service.json +0 -12
  363. package/tests/tilt-engine/fixtures/invalid-service.json +0 -4
  364. package/tests/tilt-engine/test_agents_md_generator.py +0 -341
  365. package/tests/tilt-engine/test_agents_md_integration.py +0 -378
  366. package/tests/tilt-engine/test_cache_operations.py +0 -181
  367. package/tests/tilt-engine/test_cache_persistence.py +0 -142
  368. package/tests/tilt-engine/test_code_executor.py +0 -152
  369. package/tests/tilt-engine/test_code_viewer.py +0 -127
  370. package/tests/tilt-engine/test_compose_port_anchors.py +0 -310
  371. package/tests/tilt-engine/test_config_inspector.py +0 -162
  372. package/tests/tilt-engine/test_daemon_focus_mode.py +0 -89
  373. package/tests/tilt-engine/test_daemon_polling.py +0 -122
  374. package/tests/tilt-engine/test_daemon_registration.py +0 -115
  375. package/tests/tilt-engine/test_daemon_validation.py +0 -107
  376. package/tests/tilt-engine/test_docker_generator_bugs.py +0 -493
  377. package/tests/tilt-engine/test_docker_prune.py +0 -119
  378. package/tests/tilt-engine/test_dockerignore_path_bug.py +0 -82
  379. package/tests/tilt-engine/test_file_browser.py +0 -193
  380. package/tests/tilt-engine/test_frontend_basepath_manifest.py +0 -27
  381. package/tests/tilt-engine/test_health_dependencies.py +0 -130
  382. package/tests/tilt-engine/test_health_endpoints.py +0 -118
  383. package/tests/tilt-engine/test_health_tilt_ui.py +0 -85
  384. package/tests/tilt-engine/test_runtime_container_footprint.py +0 -162
  385. package/tests/tilt-engine/test_sablier_defer_start.py +0 -237
  386. package/tests/tilt-engine/test_safety_aliases.py +0 -84
  387. package/tests/tilt-engine/test_snapshot_diff.py +0 -136
  388. package/tests/tilt-engine/test_snapshot_persistence.py +0 -163
  389. package/tests/tilt-engine/test_snapshot_scanning.py +0 -123
  390. package/tests/tilt-engine/test_standalone_project_boot.py +0 -61
  391. package/tests/tilt-engine/test_standalone_traefik.py +0 -160
  392. package/tests/tilt-engine/test_traefik_static_wake_route.py +0 -187
@@ -1,65 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Ground-truth evaluation for L1-L4 layer experiments. DO NOT EDIT during an autoresearch run.
3
- # Re-vendors this engine into the ERP project, regenerates Dockerfiles through Tilt, rebuilds the
4
- # golden layers and a fixed sample of 20 real ERP services, runs the container benchmark and the
5
- # feature tests, then prints one summary block.
6
- set -uo pipefail
7
- CORE="$(cd "$(dirname "$0")/../.." && pwd)"
8
- ERP="${TDK_BENCH_PROJECT:-$CORE/../tdk-erp-system}"
9
- SAMPLE="${LAYER_EVAL_SAMPLE:-20}"
10
- TDK_CLI="${TDK_CLI:-$CORE/cli/bin/tdk.js}"
11
- PREFIX="$(python3 -c "import json;print(json.load(open('$ERP/.tdk/project.json'))['project']['name'])")"
12
- t() { date +%s; }
13
-
14
- cd "$ERP"
15
- TDK_EXTENSION_SOURCE="$CORE" node "$TDK_CLI" project --yes >/dev/null 2>&1 || { echo "status: crash (tdk project)"; exit 1; }
16
- tilt alpha tiltfile-result -f .tdk/.tdk-out/Tiltfile >/dev/null 2>&1 || { echo "status: crash (Tiltfile evaluation)"; exit 1; }
17
-
18
- s=$(t)
19
- for target in l1_golden:l1 l2_golden:l2 l3_backend_golden:l3-backend l3_frontend_golden:l3-frontend l4_backend_bun:l4-backend l4_frontend_golden:l4-frontend; do
20
- docker build -q -f .tdk/.tdk-out/golden-layers.Dockerfile --target "${target%%:*}" -t "$PREFIX-${target##*:}:latest" . >/dev/null 2>&1 \
21
- || { echo "status: crash (golden ${target%%:*})"; exit 1; }
22
- done
23
- golden_build_s=$(( $(t) - s ))
24
-
25
- s=$(t)
26
- ls services/*/*/service.json | sort | head -n "$SAMPLE" | while read -r manifest; do
27
- dir="$(dirname "$manifest")"; svc="$(basename "$dir")"; stack="$(basename "$(dirname "$dir")")"
28
- echo "docker build -q -f $dir/.autogenerated/Dockerfile.app.autogenerated --target production -t ${stack}_${svc}:dev . >/dev/null 2>&1 || echo FAIL $svc"
29
- done | xargs -P 4 -I{} sh -c '{}' | tee /tmp/layer-eval-build.log
30
- grep -q FAIL /tmp/layer-eval-build.log && { echo "status: crash (service build: $(grep FAIL /tmp/layer-eval-build.log | head -3 | tr '\n' ' '))"; exit 1; }
31
- service_build_s=$(( $(t) - s ))
32
-
33
- cd "$CORE"
34
- bun scripts/benchmark/container-scale.ts --synthetic --tiers "$SAMPLE" --gate "$SAMPLE" --settle 15 > /tmp/layer-eval-bench.log 2>&1
35
- result="$(ls -t benchmarks/results/container-scale-*.json | head -1)"
36
- tests=pass
37
- python3 -m pytest tests/tilt-engine/test_runtime_container_footprint.py -q -p no:cacheprovider >/tmp/layer-eval-tests.log 2>&1 || tests=fail
38
-
39
- python3 - "$result" "$ERP" "$SAMPLE" "$PREFIX" "$golden_build_s" "$service_build_s" "$tests" <<'PY'
40
- import json, subprocess, sys, glob, os
41
- result, erp, sample, prefix, gb, sb, tests = sys.argv[1:]
42
- r = json.load(open(result))["results"][0]
43
- def size(img):
44
- out = subprocess.run(["docker", "image", "inspect", img, "--format", "{{.Size}}"], capture_output=True, text=True).stdout.strip()
45
- return int(out) / 1048576 if out else 0
46
- base = size(f"{prefix}-l4-backend:latest")
47
- svcs = sorted(glob.glob(f"{erp}/services/*/*/service.json"))[: int(sample)]
48
- backend = [f"{os.path.basename(os.path.dirname(os.path.dirname(p)))}_{os.path.basename(os.path.dirname(p))}:dev" for p in svcs
49
- if json.load(open(p)).get("appType") != "frontend"]
50
- sizes = [size(i) for i in backend]
51
- ok = r["allHealthySeconds"] is not None and r["crashed"] == 0 and tests == "pass"
52
- print("---")
53
- print(f"mem_per_service_mib: {r['avgMemMiB']}")
54
- print(f"service_image_mib: {sum(sizes)/len(sizes):.1f}")
55
- print(f"unique_per_service_mib: {sum(sizes)/len(sizes) - base:.1f}")
56
- print(f"l4_backend_mib: {base:.1f}")
57
- print(f"all_healthy_s: {r['allHealthySeconds']}")
58
- print(f"crashed: {r['crashed']}")
59
- print(f"docker_ps_ms: {r['dockerPsMs']}")
60
- print(f"cpu_total_pct: {r['totalCpuPct']}")
61
- print(f"golden_build_s: {gb}")
62
- print(f"service_build_s: {sb}")
63
- print(f"tests: {tests}")
64
- print(f"status: {'ok' if ok else 'invalid'}")
65
- PY
@@ -1,25 +0,0 @@
1
- # Container scale benchmark
2
-
3
- Release gate for the north star: **100 ERP services on a 16 GB machine without removing features**.
4
-
5
- ```bash
6
- bun scripts/benchmark/container-scale.ts # tiers 5,10,20,50,80,100, gate on 100
7
- bun scripts/benchmark/container-scale.ts --tiers 5,10 --gate 10
8
- bun scripts/benchmark/container-scale.ts --legacy-healthcheck # old 10s probes, for comparison
9
- ```
10
-
11
- It uses the ERP images that `tdk up` already built in `../tdk-erp-system` (override with `--project` or `TDK_BENCH_PROJECT`). For each tier it starts N real service containers with the generated runtime settings (`--init`, 512 MiB / 0.5 CPU limits, healthchecks against `/health`). It then records:
12
-
13
- - launch time, first-healthy and all-healthy time
14
- - crashes and OOM kills, with each container's exit code and last log line
15
- - total and average memory, CPU
16
- - `docker ps` latency (`tdk doctor` fails above 10 s)
17
- - p50 `/health` latency and host load
18
-
19
- Containers are removed after each tier. Results are printed as a table and saved to `benchmarks/results/*.json`.
20
-
21
- **Gate:** at the gate tier, every container must be healthy within the timeout with no crashes. Total memory must fit `(Docker VM memory - 512 MiB) / 100` per service, and `docker ps` must answer in under 10 s. The exit code is 1 when the gate fails.
22
-
23
- **Git hook (opt-in):** call `scripts/benchmark/pre-release-gate.sh` from `pre-push` or `pre-commit`. It does nothing unless `TDK_BENCH_GATE=1`.
24
-
25
- Rebuild the ERP images after engine changes (`tdk project --yes && tdk up` in the ERP project) so the benchmark measures the current layers. The header line reports what share of images use a single-process `CMD`.
@@ -1,503 +0,0 @@
1
- #!/usr/bin/env bun
2
- /**
3
- * Container scale benchmark / release gate.
4
- *
5
- * Starts N real ERP service containers (images built by `tdk up` in the ERP
6
- * project) for each tier, measures time-to-healthy, crashes, memory, CPU and
7
- * Docker responsiveness, tears them down, and gates on the north star:
8
- * 100 services on a 16 GB machine.
9
- *
10
- * bun scripts/benchmark/container-scale.ts # 5,10,20,50,80,100
11
- * bun scripts/benchmark/container-scale.ts --tiers 5,10 --gate 10
12
- * bun scripts/benchmark/container-scale.ts --legacy-healthcheck # 10s probes, for comparison
13
- *
14
- * Exit code 1 when the gate tier fails.
15
- */
16
- import { execSync, spawnSync } from "node:child_process";
17
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
18
- import { basename, dirname, join, relative, resolve } from "node:path";
19
-
20
- const REPO_ROOT = resolve(import.meta.dir, "..", "..");
21
- const PREFIX = "tdkbench-";
22
-
23
- interface Options {
24
- mode: "compose" | "synthetic";
25
- project: string;
26
- tiers: number[];
27
- gate: number;
28
- timeoutSeconds: number;
29
- interval: number;
30
- startInterval: number;
31
- settleSeconds: number;
32
- }
33
-
34
- function parseArgs(): Options {
35
- const args = process.argv.slice(2);
36
- const get = (flag: string, fallback: string): string => {
37
- const i = args.indexOf(flag);
38
- return i >= 0 && args[i + 1] ? args[i + 1] : fallback;
39
- };
40
- const legacy = args.includes("--legacy-healthcheck");
41
- const tiers = get("--tiers", "5,10,20,50,80,100").split(",").map(Number).filter((n) => n > 0);
42
- return {
43
- mode: args.includes("--synthetic") ? "synthetic" : "compose",
44
- project: resolve(get("--project", process.env.TDK_BENCH_PROJECT ?? join(REPO_ROOT, "..", "tdk-erp-system"))),
45
- tiers,
46
- gate: Number(get("--gate", String(Math.max(...tiers)))),
47
- timeoutSeconds: Number(get("--timeout", "240")),
48
- interval: legacy ? 10 : Number(get("--interval", "30")),
49
- startInterval: legacy ? 0 : Number(get("--start-interval", "2")),
50
- settleSeconds: Number(get("--settle", "15")),
51
- };
52
- }
53
-
54
- function sh(command: string, timeoutMs = 120_000): string {
55
- return execSync(command, { encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"], timeout: timeoutMs }).trim();
56
- }
57
-
58
- function shOk(command: string, timeoutMs = 120_000): string {
59
- try {
60
- return sh(command, timeoutMs);
61
- } catch {
62
- return "";
63
- }
64
- }
65
-
66
- interface Service {
67
- name: string;
68
- stack: string;
69
- image: string;
70
- port: number;
71
- isFrontend: boolean;
72
- singleProcess: boolean;
73
- imageMiB: number;
74
- }
75
-
76
- function discoverServices(project: string): Service[] {
77
- const images = new Set(shOk("docker images --format '{{.Repository}}:{{.Tag}}'").split("\n"));
78
- const services: Service[] = [];
79
- const manifests = [...new Bun.Glob("services/*/*/service.json").scanSync({ cwd: project })].sort();
80
- for (const relative of manifests) {
81
- const manifestPath = join(project, relative);
82
- const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
83
- const name = basename(dirname(manifestPath));
84
- const stack = manifest.stack ?? basename(dirname(dirname(manifestPath)));
85
- const image = `${stack}_${name}:dev`;
86
- if (!images.has(image)) continue;
87
- const [cmd, bytes] = shOk(`docker image inspect ${image} --format '{{json .Config.Cmd}}|{{.Size}}'`).split("|");
88
- services.push({
89
- name,
90
- stack,
91
- image,
92
- port: Number(manifest.port ?? 4000),
93
- isFrontend: manifest.appType === "frontend",
94
- singleProcess: !(cmd ?? "").includes('"run","start"'),
95
- imageMiB: Math.round(Number(bytes ?? 0) / 1048576),
96
- });
97
- }
98
- return services;
99
- }
100
-
101
- function dbPassword(project: string): string {
102
- const envPath = join(project, ".env");
103
- if (!existsSync(envPath)) return "";
104
- const line = readFileSync(envPath, "utf-8").split("\n").find((l) => l.startsWith("DB_PASSWORD="));
105
- return line ? line.slice("DB_PASSWORD=".length).trim() : "";
106
- }
107
-
108
- function projectName(project: string): string {
109
- const pj = join(project, ".tdk", "project.json");
110
- const name = existsSync(pj) ? JSON.parse(readFileSync(pj, "utf-8")).project?.name : basename(project);
111
- return String(name).replace(/-/g, "_");
112
- }
113
-
114
- function toMiB(raw: string): number {
115
- const n = Number.parseFloat(raw);
116
- if (/GiB$/.test(raw)) return n * 1024;
117
- if (/KiB$/.test(raw)) return n / 1024;
118
- if (/MiB$/.test(raw)) return n;
119
- if (/[0-9]B$/.test(raw)) return n / 1048576;
120
- return n;
121
- }
122
-
123
- function timed<T>(fn: () => T): [T, number] {
124
- const start = performance.now();
125
- const value = fn();
126
- return [value, Math.round(performance.now() - start)];
127
- }
128
-
129
- function hostLoad(): number {
130
- return Number.parseFloat(shOk("sysctl -n vm.loadavg").replace(/[{}]/g, "").trim().split(/\s+/)[0] ?? "0");
131
- }
132
-
133
- function hostFreeMiB(): number {
134
- const out = shOk("vm_stat");
135
- const page = Number(out.match(/page size of (\d+)/)?.[1] ?? 16384);
136
- const pages = (key: string) => Number(out.match(new RegExp(`${key}:\\s+(\\d+)`))?.[1] ?? 0);
137
- return Math.round(((pages("Pages free") + pages("Pages speculative")) * page) / 1048576);
138
- }
139
-
140
- function removeBenchContainers(): void {
141
- const ids = shOk(`docker ps -aq --filter name=${PREFIX}`).split("\n").filter(Boolean);
142
- if (ids.length) shOk(`docker rm -f ${ids.join(" ")}`, 300_000);
143
- }
144
-
145
- interface TierResult {
146
- tier: number;
147
- launchSeconds: number;
148
- firstHealthySeconds: number | null;
149
- allHealthySeconds: number | null;
150
- healthy: number;
151
- unhealthy: number;
152
- starting: number;
153
- crashed: number;
154
- oomKilled: number;
155
- crashes: { name: string; exitCode: string; oom: boolean; lastLog: string }[];
156
- totalMemMiB: number;
157
- avgMemMiB: number;
158
- totalCpuPct: number;
159
- dockerPsMs: number;
160
- requestMsP50: number | null;
161
- loadAvg1m: number;
162
- hostFreeMiB: number;
163
- singleProcessShare: number;
164
- }
165
-
166
- function runTier(n: number, services: Service[], opts: Options, network: string, password: string, project: string): TierResult {
167
- removeBenchContainers();
168
- const picked = Array.from({ length: n }, (_, i) => ({ svc: services[i % services.length], name: `${PREFIX}${i}` }));
169
- const healthFlags = (probe: string) =>
170
- [
171
- `--health-cmd '${probe}'`,
172
- `--health-interval ${opts.interval}s`,
173
- opts.startInterval > 0 ? `--health-start-interval ${opts.startInterval}s` : "",
174
- "--health-timeout 5s --health-start-period 30s --health-retries 3",
175
- ].join(" ");
176
-
177
- const t0 = performance.now();
178
- for (const { svc, name } of picked) {
179
- const probe = svc.isFrontend ? "curl -fs http://localhost/ || exit 1" : `curl -fs http://localhost:${svc.port}/health || exit 1`;
180
- const env = svc.isFrontend
181
- ? ""
182
- : `-e PORT=${svc.port} -e NODE_ENV=development -e DATABASE_URL=postgresql://${project}:${password}@${project}_postgres:5432/${project}_${svc.stack} -e TILT_DATABASE_URL=postgresql://${project}:${password}@${project}_postgres:5432/${project}_${svc.stack} -e AUTO_MIGRATE=false`;
183
- const res = spawnSync(
184
- "sh",
185
- ["-c", `docker run -d --name ${name} --init --memory 512m --cpus 0.5 --network ${network} ${env} ${healthFlags(probe)} ${svc.image}`],
186
- { encoding: "utf-8" },
187
- );
188
- if (res.status !== 0) console.error(` ! ${name} (${svc.name}) failed to start: ${res.stderr.trim().split("\n").pop()}`);
189
- }
190
- const launchSeconds = (performance.now() - t0) / 1000;
191
-
192
- let firstHealthy: number | null = null;
193
- let allHealthy: number | null = null;
194
- const deadline = t0 + opts.timeoutSeconds * 1000;
195
- let states: string[] = [];
196
- while (performance.now() < deadline) {
197
- states = shOk(`docker ps -a --filter name=${PREFIX} --format '{{.Status}}'`, 60_000).split("\n").filter(Boolean);
198
- const healthyCount = states.filter((s) => s.includes("(healthy)")).length;
199
- const elapsed = (performance.now() - t0) / 1000;
200
- if (healthyCount > 0 && firstHealthy === null) firstHealthy = elapsed;
201
- if (healthyCount === n) {
202
- allHealthy = elapsed;
203
- break;
204
- }
205
- if (states.length === n && states.every((s) => !s.includes("health: starting") && !s.startsWith("Created"))) break;
206
- Bun.sleepSync(1000);
207
- }
208
-
209
- Bun.sleepSync(opts.settleSeconds * 1000);
210
- states = shOk(`docker ps -a --filter name=${PREFIX} --format '{{.Names}}\t{{.Status}}'`, 60_000).split("\n").filter(Boolean);
211
- const exitedNames = states.filter((s) => /\tExited|\tRestarting|\tDead/.test(s)).map((s) => s.split("\t")[0]);
212
- const crashes = exitedNames.map((name) => {
213
- const inspect = shOk(`docker inspect ${name} --format '{{.State.ExitCode}}|{{.State.OOMKilled}}'`).split("|");
214
- const lastLog = shOk(`docker logs --tail 3 ${name} 2>&1`).split("\n").filter(Boolean).pop() ?? "";
215
- const svc = picked.find((p) => p.name === name)?.svc.name ?? name;
216
- return { name: `${name} (${svc})`, exitCode: inspect[0] ?? "?", oom: inspect[1] === "true", lastLog: lastLog.slice(0, 160) };
217
- });
218
-
219
- const stats = shOk(`docker stats --no-stream --format '{{.Name}} {{.CPUPerc}} {{.MemUsage}}' $(docker ps -q --filter name=${PREFIX})`, 180_000)
220
- .split("\n")
221
- .filter(Boolean)
222
- .map((l) => l.split(/\s+/))
223
- .map(([, cpu, mem]) => ({ cpu: Number.parseFloat(cpu ?? "0"), mem: toMiB(mem ?? "0") }));
224
- const totalMem = stats.reduce((a, s) => a + s.mem, 0);
225
- const [, dockerPsMs] = timed(() => shOk("docker ps -q", 60_000));
226
-
227
- const running = picked.filter((p) => !exitedNames.includes(p.name)).slice(0, 10);
228
- const latencies = running
229
- .map(({ svc, name }) => {
230
- const url = svc.isFrontend ? "http://localhost/" : `http://localhost:${svc.port}/health`;
231
- const out = shOk(`docker exec ${name} curl -s -o /dev/null -w '%{http_code} %{time_total}' ${url}`, 30_000).split(" ");
232
- return out[0] === "200" ? Number.parseFloat(out[1] ?? "0") * 1000 : null;
233
- })
234
- .filter((v): v is number => v !== null)
235
- .sort((a, b) => a - b);
236
-
237
- const result: TierResult = {
238
- tier: n,
239
- launchSeconds: round(launchSeconds),
240
- firstHealthySeconds: firstHealthy === null ? null : round(firstHealthy),
241
- allHealthySeconds: allHealthy === null ? null : round(allHealthy),
242
- healthy: states.filter((s) => s.includes("(healthy)")).length,
243
- unhealthy: states.filter((s) => s.includes("(unhealthy)")).length,
244
- starting: states.filter((s) => s.includes("health: starting")).length,
245
- crashed: crashes.length,
246
- oomKilled: crashes.filter((c) => c.oom).length,
247
- crashes,
248
- totalMemMiB: Math.round(totalMem),
249
- avgMemMiB: stats.length ? round(totalMem / stats.length) : 0,
250
- totalCpuPct: round(stats.reduce((a, s) => a + s.cpu, 0)),
251
- dockerPsMs,
252
- requestMsP50: latencies.length ? round(latencies[Math.floor(latencies.length / 2)]) : null,
253
- loadAvg1m: hostLoad(),
254
- hostFreeMiB: hostFreeMiB(),
255
- singleProcessShare: round(picked.filter((p) => p.svc.singleProcess).length / n),
256
- };
257
- removeBenchContainers();
258
- return result;
259
- }
260
-
261
- // ---- compose mode: start services exactly like Tilt (generated compose, real infra, Traefik) ----
262
-
263
- function ensureExternalNetworks(composeArgs: string): void {
264
- const config = shOk(`docker compose ${composeArgs} config --format json`, 120_000);
265
- if (!config) return;
266
- for (const net of Object.values(JSON.parse(config).networks ?? {}) as { name?: string; external?: boolean }[]) {
267
- if (net.external && net.name && !shOk(`docker network inspect ${net.name} --format '{{.Name}}'`)) shOk(`docker network create ${net.name}`);
268
- }
269
- }
270
-
271
- function startInfra(project: string, root: string): string[] {
272
- const started: string[] = [];
273
- const envFile = join(root, ".env");
274
- const infra = [
275
- { container: `${project}_postgres`, file: join(root, "services", "platform", "database-management", "docker-compose.yml") },
276
- { container: `${project}_traefik`, file: join(root, ".tdk", ".tdk-out", "docker-compose.traefik.yml") },
277
- ];
278
- for (const { container, file } of infra) {
279
- if (shOk(`docker ps -q --filter name=^${container}$`)) continue;
280
- if (shOk(`docker ps -aq --filter name=^${container}$`)) shOk(`docker start ${container}`);
281
- else {
282
- const args = `-f ${file} --env-file ${envFile}`;
283
- ensureExternalNetworks(args);
284
- shOk(`docker compose ${args} up -d`, 300_000);
285
- }
286
- started.push(container);
287
- }
288
- for (let i = 0; i < 60 && !shOk(`docker exec ${project}_postgres pg_isready -U ${project}`); i++) Bun.sleepSync(1000);
289
- return started;
290
- }
291
-
292
- function composeArgs(root: string, stack: string): string {
293
- return `-p ${stack}-autogenerated -f ${join(root, "services", stack, ".autogenerated", "docker-compose.app.autogenerated.yml")} --env-file ${join(root, ".env")}`;
294
- }
295
-
296
- function routeRequest(container: string): { code: string; ms: number } | null {
297
- const labels = JSON.parse(shOk(`docker inspect ${container} --format '{{json .Config.Labels}}'`) || "{}") as Record<string, string>;
298
- const ruleKey = Object.keys(labels).find((k) => /^traefik\.http\.routers\.[^.]+\.rule$/.test(k));
299
- if (!ruleKey) return null;
300
- const rule = labels[ruleKey] ?? "";
301
- const host = rule.match(/Host\(`([^`]+)`\)/)?.[1];
302
- const prefix = rule.match(/PathPrefix\(`([^`]+)`\)/)?.[1] ?? "";
303
- const healthKey = Object.keys(labels).find((k) => k.endsWith(".loadbalancer.healthcheck.path"));
304
- const path = `${prefix.replace(/\/$/, "")}${healthKey ? labels[healthKey] : "/"}`;
305
- if (!host) return null;
306
- const out = shOk(`curl -s -o /dev/null -m 10 -w '%{http_code} %{time_total}' -H 'Host: ${host}' 'http://127.0.0.1:80${path}'`, 20_000).split(" ");
307
- return { code: out[0] ?? "000", ms: round(Number.parseFloat(out[1] ?? "0") * 1000) };
308
- }
309
-
310
- function runTierCompose(n: number, services: Service[], opts: Options, project: string): TierResult & { routed: Record<string, number>; restarts: number } {
311
- const picked = services.slice(0, Math.min(n, services.length));
312
- const byStack = new Map<string, string[]>();
313
- for (const svc of picked) byStack.set(svc.stack, [...(byStack.get(svc.stack) ?? []), svc.name]);
314
- for (const stack of byStack.keys()) {
315
- shOk(`docker exec ${project}_postgres createdb -U ${project} -O ${project} ${project}_${stack}`);
316
- ensureExternalNetworks(composeArgs(opts.project, stack));
317
- }
318
- const names = picked.map((svc) => `${svc.stack}-autogenerated-${svc.name}-1`);
319
-
320
- // Launch every stack's `compose up` in parallel, same as Tilt does across resources.
321
- // NOTE: do not use Bun.spawn() + a Bun.sleepSync() poll loop here - sleepSync blocks
322
- // Bun's event loop, so a spawned child's exit is never observed and the poll spins
323
- // forever even after the real process has finished. Shelling out one blocking
324
- // spawnSync (with the stack commands backgrounded and `wait`ed inside the shell)
325
- // avoids that: the parent shell, not our JS, is what waits for real completion.
326
- const launchScript = [...byStack]
327
- .map(([stack, svcs]) => `docker compose ${composeArgs(opts.project, stack)} up -d --no-build ${svcs.join(" ")} &`)
328
- .join("\n") + "\nwait\n";
329
- const t0 = performance.now();
330
- const launch = spawnSync("sh", ["-c", launchScript], { encoding: "utf-8", timeout: opts.timeoutSeconds * 1000 });
331
- const launchSeconds = (performance.now() - t0) / 1000;
332
- if (launch.status !== 0) console.error(` ! compose up failed (exit ${launch.status}): ${(launch.stderr ?? "").slice(0, 500)}`);
333
-
334
- const status = () => {
335
- const rows = shOk(`docker ps -a --format '{{.Names}}\t{{.Status}}'`, 60_000).split("\n").map((l) => l.split("\t"));
336
- return new Map(rows.filter(([name]) => names.includes(name ?? "")).map(([name, st]) => [name ?? "", st ?? ""]));
337
- };
338
- let firstHealthy: number | null = null;
339
- let allHealthy: number | null = null;
340
- const deadline = t0 + opts.timeoutSeconds * 1000;
341
- while (performance.now() < deadline) {
342
- const st = [...status().values()];
343
- const healthyCount = st.filter((s) => s.includes("(healthy)") || (s.startsWith("Up") && !s.includes("health"))).length;
344
- const elapsed = (performance.now() - t0) / 1000;
345
- if (healthyCount > 0 && firstHealthy === null) firstHealthy = elapsed;
346
- if (healthyCount === picked.length) {
347
- allHealthy = elapsed;
348
- break;
349
- }
350
- Bun.sleepSync(1000);
351
- }
352
- Bun.sleepSync(opts.settleSeconds * 1000);
353
-
354
- const final = status();
355
- const states = [...final.values()];
356
- const crashes = [...final]
357
- .filter(([name, st]) => /^(Exited|Restarting|Dead)/.test(st) || Number(shOk(`docker inspect ${name} --format '{{.RestartCount}}'`)) > 0)
358
- .map(([name]) => {
359
- const [exitCode, oom, restarts] = shOk(`docker inspect ${name} --format '{{.State.ExitCode}}|{{.State.OOMKilled}}|{{.RestartCount}}'`).split("|");
360
- const lastLog = shOk(`docker logs --tail 3 ${name} 2>&1`).split("\n").filter(Boolean).pop() ?? "";
361
- return { name: `${name} restarts=${restarts}`, exitCode: exitCode ?? "?", oom: oom === "true", lastLog: lastLog.slice(0, 160) };
362
- });
363
- const running = [...final].filter(([, st]) => st.startsWith("Up")).map(([name]) => name);
364
- const stats = shOk(`docker stats --no-stream --format '{{.CPUPerc}} {{.MemUsage}}' ${running.join(" ")}`, 180_000)
365
- .split("\n")
366
- .filter(Boolean)
367
- .map((l) => l.split(/\s+/))
368
- .map(([cpu, mem]) => ({ cpu: Number.parseFloat(cpu ?? "0"), mem: toMiB(mem ?? "0") }));
369
- const totalMem = stats.reduce((a, x) => a + x.mem, 0);
370
- const [, dockerPsMs] = timed(() => shOk("docker ps -q", 60_000));
371
-
372
- const routed: Record<string, number> = {};
373
- const latencies: number[] = [];
374
- for (const name of running) {
375
- const r = routeRequest(name);
376
- const code = r?.code ?? "no-route";
377
- routed[code] = (routed[code] ?? 0) + 1;
378
- if (r && r.code === "200") latencies.push(r.ms);
379
- }
380
- latencies.sort((a, b) => a - b);
381
-
382
- const result = {
383
- tier: n,
384
- launchSeconds: round(launchSeconds),
385
- firstHealthySeconds: firstHealthy === null ? null : round(firstHealthy),
386
- allHealthySeconds: allHealthy === null ? null : round(allHealthy),
387
- healthy: states.filter((st) => st.includes("(healthy)")).length,
388
- unhealthy: states.filter((st) => st.includes("(unhealthy)")).length,
389
- starting: states.filter((st) => st.includes("health: starting")).length,
390
- crashed: crashes.length,
391
- oomKilled: crashes.filter((c) => c.oom).length,
392
- crashes,
393
- totalMemMiB: Math.round(totalMem),
394
- avgMemMiB: stats.length ? round(totalMem / stats.length) : 0,
395
- totalCpuPct: round(stats.reduce((a, x) => a + x.cpu, 0)),
396
- dockerPsMs,
397
- requestMsP50: latencies.length ? latencies[Math.floor(latencies.length / 2)] ?? null : null,
398
- loadAvg1m: hostLoad(),
399
- hostFreeMiB: hostFreeMiB(),
400
- singleProcessShare: round(picked.filter((svc) => svc.singleProcess).length / picked.length),
401
- routed,
402
- restarts: crashes.length,
403
- };
404
- for (const [stack, svcs] of byStack) shOk(`docker compose ${composeArgs(opts.project, stack)} rm -sf ${svcs.join(" ")}`, 300_000);
405
- return result;
406
- }
407
-
408
- function round(v: number): number {
409
- return Math.round(v * 10) / 10;
410
- }
411
-
412
- function main(): void {
413
- const opts = parseArgs();
414
- const project = projectName(opts.project);
415
- const network = `${project}_database`;
416
- const dockerMemMiB = Math.round(Number(shOk("docker info --format '{{.MemTotal}}'")) / 1048576);
417
- const hostMemGiB = Math.round(Number(shOk("sysctl -n hw.memsize")) / 1073741824);
418
-
419
- if (shOk("pgrep -f 'tilt up'")) console.warn("⚠ Tilt is running - builds in the background will skew results.");
420
- shOk(`docker network inspect ${network} || docker network create ${network}`);
421
- const startedInfra = opts.mode === "compose" ? startInfra(project, opts.project) : [];
422
- if (opts.mode === "synthetic") shOk(`docker start ${project}_postgres`);
423
- const services = discoverServices(opts.project);
424
- if (services.length === 0) {
425
- console.error(`No built ERP images found for ${opts.project}. Run \`tdk up\` there once to build them.`);
426
- process.exit(2);
427
- }
428
- const baselineContainers = shOk("docker ps -q").split("\n").filter(Boolean).length;
429
- const singleShare = services.filter((s) => s.singleProcess).length / services.length;
430
-
431
- // North star math: 100 services must fit in the Docker VM with ~512 MiB left for infra.
432
- const imageSizes = (list: Service[]) => {
433
- const sizes = list.map((s) => s.imageMiB).sort((a, b) => a - b);
434
- return sizes.length ? { count: sizes.length, min: sizes[0], avg: Math.round(sizes.reduce((a, b) => a + b, 0) / sizes.length), max: sizes[sizes.length - 1] } : null;
435
- };
436
- const imageStats = {
437
- backend: imageSizes(services.filter((s) => !s.isFrontend)),
438
- frontend: imageSizes(services.filter((s) => s.isFrontend)),
439
- backendSingleProcess: imageSizes(services.filter((s) => !s.isFrontend && s.singleProcess)),
440
- backendWrapper: imageSizes(services.filter((s) => !s.isFrontend && !s.singleProcess)),
441
- };
442
- const diskUsage = shOk("docker system df --format '{{.Type}} {{.Size}}'").split("\n")[0] ?? "";
443
- const perServiceBudgetMiB = Math.floor((dockerMemMiB - 512) / 100);
444
- console.log(`Host ${hostMemGiB} GB, Docker VM ${dockerMemMiB} MiB, ${services.length} distinct ERP images (${Math.round(singleShare * 100)}% single-process CMD), ${baselineContainers} other containers running`);
445
- console.log(`North star budget: (${dockerMemMiB} - 512 infra) / 100 = ${perServiceBudgetMiB} MiB per service`);
446
- const fmt = (x: ReturnType<typeof imageSizes>) => (x ? `${x.count} × ${x.min}/${x.avg}/${x.max} MiB (min/avg/max)` : "none");
447
- console.log(`Images - backend new layers: ${fmt(imageStats.backendSingleProcess)}, backend old layers: ${fmt(imageStats.backendWrapper)}, frontend: ${fmt(imageStats.frontend)}; Docker disk: ${diskUsage}`);
448
- console.log(`Mode: ${opts.mode === "compose" ? "compose (generated compose files, real Postgres + Traefik, requests routed through Traefik)" : "synthetic (docker run, /health only)"}`);
449
- console.log(`Healthcheck: interval ${opts.interval}s, start-interval ${opts.startInterval || "off"}\n`);
450
-
451
- const results: TierResult[] = [];
452
- for (const tier of opts.tiers) {
453
- process.stdout.write(`▶ tier ${tier}... `);
454
- const r = opts.mode === "compose" ? runTierCompose(tier, services, opts, project) : runTier(tier, services, opts, network, dbPassword(opts.project), project);
455
- results.push(r);
456
- const routed = "routed" in r ? ` | via Traefik: ${JSON.stringify(r.routed)}` : "";
457
- console.log(`all healthy: ${r.allHealthySeconds ?? "NO"}s, crashed ${r.crashed}, mem ${r.totalMemMiB} MiB, docker ps ${r.dockerPsMs} ms${routed}`);
458
- }
459
-
460
- const header = "| tier | launch s | 1st healthy s | all healthy s | healthy | unhealthy | starting | crashed | OOM | mem MiB | avg MiB | CPU % | docker ps ms | req p50 ms | load 1m | host free MiB |";
461
- const rows = results.map(
462
- (r) =>
463
- `| ${r.tier} | ${r.launchSeconds} | ${r.firstHealthySeconds ?? "-"} | ${r.allHealthySeconds ?? "TIMEOUT"} | ${r.healthy} | ${r.unhealthy} | ${r.starting} | ${r.crashed} | ${r.oomKilled} | ${r.totalMemMiB} | ${r.avgMemMiB} | ${r.totalCpuPct} | ${r.dockerPsMs} | ${r.requestMsP50 ?? "-"} | ${r.loadAvg1m} | ${r.hostFreeMiB} |`,
464
- );
465
- console.log(`\n${header}\n|${"---|".repeat(16)}\n${rows.join("\n")}`);
466
- for (const r of results.filter((x) => x.crashes.length)) {
467
- console.log(`\nCrashes at tier ${r.tier}:`);
468
- for (const c of r.crashes.slice(0, 15)) console.log(` - ${c.name} exit=${c.exitCode}${c.oom ? " OOMKilled" : ""} :: ${c.lastLog}`);
469
- }
470
-
471
- const gate = results.find((r) => r.tier === opts.gate);
472
- const checks = gate
473
- ? [
474
- [`all ${gate.tier} healthy within ${opts.timeoutSeconds}s`, gate.allHealthySeconds !== null],
475
- ["no crashes / OOM kills", gate.crashed === 0],
476
- [`memory ${gate.totalMemMiB} MiB <= ${perServiceBudgetMiB * gate.tier} MiB budget`, gate.totalMemMiB <= perServiceBudgetMiB * gate.tier],
477
- [`docker ps ${gate.dockerPsMs} ms < 10000 ms (tdk doctor timeout)`, gate.dockerPsMs < 10_000],
478
- ]
479
- : [];
480
- if (gate && "routed" in gate) {
481
- const routed = gate.routed as Record<string, number>;
482
- const total = Object.values(routed).reduce((a, b) => a + b, 0);
483
- checks.push([`requests through Traefik: ${routed["200"] ?? 0}/${total} returned 200`, (routed["200"] ?? 0) === total]);
484
- }
485
- const smallest = results[0];
486
- console.log(`\nWebsite promise "full boot in under 5 seconds": tier ${smallest?.tier} all healthy in ${smallest?.allHealthySeconds ?? "TIMEOUT"}s (info only)`);
487
- console.log(`Gate (tier ${opts.gate}):`);
488
- for (const [label, ok] of checks) console.log(` ${ok ? "✓" : "✗"} ${label}`);
489
- const passed = checks.length > 0 && checks.every(([, ok]) => ok);
490
- for (const container of startedInfra) shOk(`docker stop ${container}`);
491
-
492
- const outDir = join(REPO_ROOT, "benchmarks", "results");
493
- mkdirSync(outDir, { recursive: true });
494
- const outFile = join(outDir, `container-scale-${new Date().toISOString().replace(/[:.]/g, "-")}.json`);
495
- writeFileSync(
496
- outFile,
497
- JSON.stringify({ date: new Date().toISOString(), hostMemGiB, dockerMemMiB, perServiceBudgetMiB, options: { ...opts, project: relative(REPO_ROOT, opts.project) }, images: services.length, imageStats, diskUsage, singleProcessImageShare: singleShare, results, gate: { tier: opts.gate, passed } }, null, 2),
498
- );
499
- console.log(`\n${passed ? "PASS" : "FAIL"} - results saved to ${outFile}`);
500
- process.exit(passed ? 0 : 1);
501
- }
502
-
503
- main();
@@ -1,9 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Opt-in container scale gate for git hooks. No-op unless TDK_BENCH_GATE=1.
3
- # TDK_BENCH_GATE=1 git push # run full gate (tiers up to 100)
4
- # TDK_BENCH_GATE=1 TDK_BENCH_TIERS=5,10,20 git commit ...
5
- set -euo pipefail
6
- [ "${TDK_BENCH_GATE:-0}" = "1" ] || exit 0
7
- cd "$(dirname "$0")/../.."
8
- tiers="${TDK_BENCH_TIERS:-5,10,20,50,80,100}"
9
- exec bun scripts/benchmark/container-scale.ts --tiers "$tiers" --gate "${tiers##*,}"
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- set -euo pipefail
4
-
5
- readonly ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
6
-
7
- # npm runs `prepare` when installing from a GitHub URL, but its temporary
8
- # checkout does not include the .git directory. Hooks are only needed in a
9
- # developer checkout, so leave package installation untouched.
10
- if ! git -C "${ROOT_DIR}" rev-parse --git-dir >/dev/null 2>&1; then
11
- exit 0
12
- fi
13
-
14
- git -C "${ROOT_DIR}" config core.hooksPath .githooks