hammerkit 1.5.0 → 1.6.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (480) hide show
  1. package/.agents/FEEDBACK.md +74 -0
  2. package/.agents/skills/before-commit/SKILL.md +101 -0
  3. package/.agents/skills/do-the-work/SKILL.md +64 -0
  4. package/.agents/skills/fix-ci/SKILL.md +98 -0
  5. package/.agents/skills/speckit-git-commit/SKILL.md +53 -0
  6. package/.agents/skills/speckit-git-feature/SKILL.md +72 -0
  7. package/.agents/skills/speckit-git-initialize/SKILL.md +54 -0
  8. package/.agents/skills/speckit-git-remote/SKILL.md +50 -0
  9. package/.agents/skills/speckit-git-validate/SKILL.md +54 -0
  10. package/.gitbook.yaml +5 -0
  11. package/.specify/extensions/.registry +37 -0
  12. package/.specify/extensions/git/README.md +100 -0
  13. package/.specify/extensions/git/commands/speckit.git.commit.md +48 -0
  14. package/.specify/extensions/git/commands/speckit.git.feature.md +67 -0
  15. package/.specify/extensions/git/commands/speckit.git.initialize.md +49 -0
  16. package/.specify/extensions/git/commands/speckit.git.remote.md +45 -0
  17. package/.specify/extensions/git/commands/speckit.git.validate.md +49 -0
  18. package/.specify/extensions/git/config-template.yml +62 -0
  19. package/.specify/extensions/git/extension.yml +140 -0
  20. package/.specify/extensions/git/git-config.yml +62 -0
  21. package/.specify/extensions.yml +149 -0
  22. package/.specify/feature.json +3 -0
  23. package/.specify/init-options.json +10 -0
  24. package/.specify/integration.json +15 -0
  25. package/.specify/integrations/claude.manifest.json +16 -0
  26. package/.specify/integrations/speckit.manifest.json +17 -0
  27. package/.specify/memory/constitution.md +162 -0
  28. package/.specify/templates/checklist-template.md +40 -0
  29. package/.specify/templates/constitution-template.md +50 -0
  30. package/.specify/templates/plan-template.md +113 -0
  31. package/.specify/templates/spec-template.md +131 -0
  32. package/.specify/templates/tasks-template.md +252 -0
  33. package/.specify/workflows/speckit/workflow.yml +77 -0
  34. package/.specify/workflows/workflow-registry.json +13 -0
  35. package/AGENTS.md +94 -0
  36. package/Dockerfile +3 -3
  37. package/FOLLOWUPS.md +34 -0
  38. package/README.md +9 -7
  39. package/SUMMARY.md +30 -5
  40. package/VALUES.md +53 -0
  41. package/build.schema.json +542 -408
  42. package/dist/cache/auto-pull.spec.js +123 -0
  43. package/dist/cache/backends/local-cache-backend.js +56 -0
  44. package/dist/cache/backends/local-cache-backend.spec.js +72 -0
  45. package/dist/cache/backends/s3-cache-backend.js +124 -0
  46. package/dist/cache/backends/s3-cache-backend.spec.js +118 -0
  47. package/dist/cache/cache-backend-registry.js +21 -0
  48. package/dist/cache/cache-backend-registry.spec.js +22 -0
  49. package/dist/cache/cache-backend.js +3 -0
  50. package/dist/cache/resolve-cache.js +60 -0
  51. package/dist/cache/resolve-cache.spec.js +52 -0
  52. package/dist/cli.js +112 -82
  53. package/dist/cli.spec.js +20 -2
  54. package/dist/console/get-logger.spec.js +23 -0
  55. package/dist/docker/get-default-platform.spec.js +29 -0
  56. package/dist/docker/longest-common-prefix.spec.js +26 -0
  57. package/dist/docker/package.js +283 -0
  58. package/dist/docker/package.spec.js +145 -0
  59. package/dist/docker/pull.js +4 -4
  60. package/dist/docker/remove-container.spec.js +32 -0
  61. package/dist/docker/stream.js +3 -3
  62. package/dist/docker/using-container.js +29 -11
  63. package/dist/docker/using-container.spec.js +90 -0
  64. package/dist/environment/merge-environment-variables.js +21 -0
  65. package/dist/environment/merge-environment-variables.spec.js +24 -0
  66. package/dist/environment/replace-env-variables.js +43 -10
  67. package/dist/environment/replace-env-variables.spec.js +47 -0
  68. package/dist/executer/abort.spec.js +18 -0
  69. package/dist/executer/await-completed-dependencies.js +56 -0
  70. package/dist/executer/cache-metadata.js +11 -0
  71. package/dist/executer/check-readiness.js +1 -1
  72. package/dist/{planner/work-node-port.js → executer/container-bind.js} +1 -1
  73. package/dist/executer/docker-service.js +110 -86
  74. package/dist/executer/docker-task.js +132 -0
  75. package/dist/executer/environment-mock.js +3 -4
  76. package/dist/executer/environment-mock.spec.js +22 -0
  77. package/dist/executer/event-cache.js +60 -127
  78. package/dist/executer/execute-command.js +4 -3
  79. package/dist/executer/execute-docker.js +32 -14
  80. package/dist/executer/execute-work-service.js +61 -0
  81. package/dist/executer/execute-work-task.js +107 -0
  82. package/dist/executer/execute-work-tree.js +27 -0
  83. package/dist/executer/execute.spec.js +14 -10
  84. package/dist/executer/execution-steps.js +47 -31
  85. package/dist/executer/get-container-binds.js +19 -0
  86. package/dist/executer/get-docker-executor.js +12 -12
  87. package/dist/executer/get-scheduler-down-result.js +12 -0
  88. package/dist/executer/get-scheduler-execute-result.js +28 -0
  89. package/dist/executer/get-scheduler-up-result.js +12 -0
  90. package/dist/executer/get-service-containers.js +14 -0
  91. package/dist/executer/get-service-containers.spec.js +59 -0
  92. package/dist/executer/get-service-env-hints.js +35 -0
  93. package/dist/executer/get-service-env-hints.spec.js +65 -0
  94. package/dist/executer/kubernetes-service.js +116 -48
  95. package/dist/executer/label-values.js +29 -46
  96. package/dist/executer/label-values.spec.js +61 -0
  97. package/dist/executer/lazy-resolver.js +16 -0
  98. package/dist/executer/lazy-resolver.spec.js +20 -0
  99. package/dist/executer/local-task.js +44 -0
  100. package/dist/executer/process-manager.js +16 -70
  101. package/dist/executer/process-manager.spec.js +39 -76
  102. package/dist/executer/reset-work-tree.js +30 -0
  103. package/dist/executer/scheduler/check-for-loop.js +17 -7
  104. package/dist/executer/scheduler/{scheduler-state.js → dependency-state.js} +1 -1
  105. package/dist/executer/scheduler/enqueue-next.js +83 -19
  106. package/dist/executer/scheduler/enqueue-next.spec.js +136 -0
  107. package/dist/executer/scheduler/{node-state.js → need-state.js} +1 -1
  108. package/dist/{parser/build-file.js → executer/scheduler/task-state.js} +1 -1
  109. package/dist/executer/set-user-permission.js +1 -1
  110. package/dist/executer/state-resolver.js +61 -0
  111. package/dist/executer/state-resolver.spec.js +37 -0
  112. package/dist/executer/state.js +37 -31
  113. package/dist/executer/state.spec.js +66 -0
  114. package/dist/executer/states.spec.js +14 -0
  115. package/dist/executer/watch-loop.js +68 -0
  116. package/dist/executer/watch-state-key.js +44 -0
  117. package/dist/executer/work-scope.js +1 -5
  118. package/dist/file/get-file-context.js +24 -3
  119. package/dist/file/get-file-context.spec.js +178 -0
  120. package/dist/file/move-files.js +2 -2
  121. package/dist/file/move-files.spec.js +48 -0
  122. package/dist/index.js +7 -8
  123. package/dist/json-schema.js +12 -0
  124. package/dist/kubernetes/apply.js +34 -0
  125. package/dist/kubernetes/await-running-state.js +71 -0
  126. package/dist/kubernetes/ensure-http-route.js +53 -0
  127. package/dist/kubernetes/ensure-http-route.spec.js +70 -0
  128. package/dist/kubernetes/ensure-ingress.js +54 -0
  129. package/dist/kubernetes/ensure-kubernetes-deployment-exists.js +105 -0
  130. package/dist/kubernetes/ensure-kubernetes-persistent-volume-claim-exists.js +40 -0
  131. package/dist/kubernetes/ensure-kubernetes-service-exists.js +36 -0
  132. package/dist/kubernetes/ensure-namespace.js +43 -0
  133. package/dist/kubernetes/ensure-namespace.spec.js +39 -0
  134. package/dist/kubernetes/ensure-persistent-data.js +158 -0
  135. package/dist/kubernetes/get-service-ip.js +11 -0
  136. package/dist/kubernetes/kubernetes-instance.js +48 -0
  137. package/dist/kubernetes/remove-persistent-data.js +19 -0
  138. package/dist/kubernetes/resolve-pod-name.js +42 -0
  139. package/dist/kubernetes/resolve-pod-name.spec.js +81 -0
  140. package/dist/kubernetes/resources.js +12 -0
  141. package/dist/kubernetes/restore-kubernetes-data.js +42 -0
  142. package/dist/kubernetes/store-kubernetes-data.js +39 -0
  143. package/dist/kubernetes/volumes.js +64 -0
  144. package/dist/log.js +69 -69
  145. package/dist/log.spec.js +131 -0
  146. package/dist/logging/grouped-logger.js +17 -15
  147. package/dist/logging/grouped-logger.spec.js +84 -0
  148. package/dist/logging/interactive-logger.js +1 -1
  149. package/dist/logging/interactive-logger.spec.js +57 -0
  150. package/dist/logging/live-logger.js +2 -2
  151. package/dist/logging/live-logger.spec.js +37 -0
  152. package/dist/logging/loggers.spec.js +154 -0
  153. package/dist/optimizer/calculate-checksum.spec.js +34 -0
  154. package/dist/optimizer/get-cache-directory.js +6 -6
  155. package/dist/optimizer/get-cache-directory.spec.js +59 -0
  156. package/dist/optimizer/get-work-cache-stats.js +53 -0
  157. package/dist/optimizer/get-work-cache-stats.spec.js +79 -0
  158. package/dist/{parser/build-file-task.js → optimizer/work-cache-stats.js} +1 -1
  159. package/dist/optimizer/work-task-cache-description.js +25 -0
  160. package/dist/optimizer/work-task-cache-description.spec.js +45 -0
  161. package/dist/parser/default-build-file.js +14 -7
  162. package/dist/parser/default-build-file.spec.js +48 -0
  163. package/dist/parser/parse-label-arguments.spec.js +27 -0
  164. package/dist/parser/read-build-file.js +2 -13
  165. package/dist/parser/read-build-file.spec.js +89 -0
  166. package/dist/parser/read-env-file.js +4 -5
  167. package/dist/parser/read-env-file.spec.js +42 -0
  168. package/dist/planner/utils/append-work-dependencies.js +55 -0
  169. package/dist/planner/utils/append-work-environment.js +22 -0
  170. package/dist/planner/utils/append-work-needs.js +18 -0
  171. package/dist/planner/utils/append-work-service.js +107 -0
  172. package/dist/planner/utils/append-work-service.spec.js +71 -0
  173. package/dist/planner/utils/append-work-task.js +81 -0
  174. package/dist/planner/utils/get-container-user.js +14 -0
  175. package/dist/planner/utils/get-container-user.spec.js +30 -0
  176. package/dist/planner/utils/get-default-kube-config.js +10 -0
  177. package/dist/planner/utils/get-default-kube-config.spec.js +11 -0
  178. package/dist/planner/utils/get-work-runtime.js +44 -0
  179. package/dist/planner/utils/normalize-path.js +1 -1
  180. package/dist/planner/utils/normalize-path.spec.js +24 -0
  181. package/dist/planner/utils/parse-work-command.js +80 -0
  182. package/dist/planner/utils/parse-work-command.spec.js +21 -0
  183. package/dist/planner/utils/parse-work-generate.js +38 -0
  184. package/dist/planner/utils/parse-work-generate.spec.js +45 -0
  185. package/dist/planner/utils/{parse-work-node-mount.js → parse-work-mount.js} +10 -7
  186. package/dist/planner/utils/{parse-work-node-mount.spec.js → parse-work-mount.spec.js} +14 -11
  187. package/dist/planner/utils/parse-work-mounts.js +13 -0
  188. package/dist/planner/utils/parse-work-mounts.spec.js +19 -0
  189. package/dist/planner/utils/{parse-work-node-port.js → parse-work-port.js} +11 -8
  190. package/dist/planner/utils/parse-work-port.spec.js +24 -0
  191. package/dist/planner/utils/parse-work-ports.js +10 -0
  192. package/dist/planner/utils/parse-work-ports.spec.js +24 -0
  193. package/dist/planner/utils/parse-work-source.js +72 -0
  194. package/dist/planner/utils/parse-work-source.spec.js +58 -0
  195. package/dist/planner/utils/parse-work-volume.js +45 -0
  196. package/dist/planner/utils/parse-work-volume.spec.js +18 -0
  197. package/dist/planner/utils/plan-work-tasks.js +41 -0
  198. package/dist/planner/utils/plan-work-tasks.spec.js +30 -0
  199. package/dist/planner/utils/plan-work-volume.js +1 -0
  200. package/dist/planner/utils/plan-work-volume.spec.js +33 -0
  201. package/dist/planner/utils/split-name.js +2 -2
  202. package/dist/planner/utils/split-name.spec.js +16 -0
  203. package/dist/planner/utils/template-value.js +1 -2
  204. package/dist/planner/utils/template-value.spec.js +14 -0
  205. package/dist/planner/validate.js +71 -38
  206. package/dist/planner/validate.spec.js +251 -0
  207. package/dist/planner/work-cache-id.js +12 -0
  208. package/dist/planner/work-cache-id.spec.js +173 -0
  209. package/dist/planner/work-command.js +3 -0
  210. package/dist/planner/work-environment.js +3 -0
  211. package/dist/planner/work-healthcheck.js +3 -0
  212. package/dist/planner/{work-node-status.js → work-item-status.js} +16 -62
  213. package/dist/{parser/build-file-service.js → planner/work-item-validation.js} +1 -1
  214. package/dist/planner/work-item.js +16 -0
  215. package/dist/{optimizer/work-node-cache-stats.js → planner/work-kubernetes-selector.js} +1 -1
  216. package/dist/planner/{work-nodes.js → work-port.js} +1 -1
  217. package/dist/planner/work-runtime-docker.js +224 -0
  218. package/dist/planner/work-runtime-kubernetes.js +264 -0
  219. package/dist/planner/work-runtime-local.js +109 -0
  220. package/dist/planner/work-runtime-local.spec.js +193 -0
  221. package/dist/planner/work-service-id.js +15 -5
  222. package/dist/planner/work-source.js +3 -0
  223. package/dist/planner/work-task.js +8 -0
  224. package/dist/program-commands.spec.js +84 -0
  225. package/dist/program.js +219 -73
  226. package/dist/program.spec.js +34 -11
  227. package/dist/run-program.js +28 -0
  228. package/dist/{executer/process.js → runtime/runtime.js} +1 -1
  229. package/dist/schema/build-file-base-task-schema.js +27 -0
  230. package/dist/schema/build-file-container-service-schema.js +28 -0
  231. package/dist/schema/build-file-container-task-schema.js +13 -0
  232. package/dist/schema/build-file-environment-schema-ingress.js +14 -0
  233. package/dist/schema/build-file-environment-schema.js +23 -0
  234. package/dist/schema/build-file-kubernetes-service-schema.js +17 -0
  235. package/dist/schema/build-file-kubernetes-service-selector-schema.js +9 -0
  236. package/dist/schema/build-file-local-platform-schema.js +9 -0
  237. package/dist/schema/build-file-local-task-schema.js +12 -0
  238. package/dist/schema/build-file-need-schema.js +12 -0
  239. package/dist/schema/build-file-schema.js +23 -0
  240. package/dist/schema/build-file-schema.spec.js +61 -0
  241. package/dist/schema/build-file-service-container-healthcheck.js +8 -0
  242. package/dist/schema/build-file-service-schema.js +15 -0
  243. package/dist/schema/build-file-task-command-schema.js +12 -0
  244. package/dist/schema/build-file-task-schema.js +12 -0
  245. package/dist/schema/build-file-volume-schema.js +14 -0
  246. package/dist/schema/cache-schema.js +31 -0
  247. package/dist/schema/doc-build-files.spec.js +223 -0
  248. package/dist/schema/envs-schema.js +6 -0
  249. package/dist/schema/labels-schema.js +6 -0
  250. package/dist/schema/parse-context.js +3 -0
  251. package/dist/schema/parse-error.js +12 -0
  252. package/dist/schema/parse-error.spec.js +18 -0
  253. package/dist/schema/reference-parser.js +209 -0
  254. package/dist/schema/reference-parser.spec.js +83 -0
  255. package/dist/schema/schema-parser.js +66 -0
  256. package/dist/schema/schema-parser.spec.js +80 -0
  257. package/dist/schema/type-guards.spec.js +23 -0
  258. package/dist/schema/work-scope-parser.js +77 -0
  259. package/dist/schema/work-scope-parser.spec.js +66 -0
  260. package/dist/service/update-service-status.js +11 -0
  261. package/dist/service/update-service-status.spec.js +27 -0
  262. package/dist/testing/ensure-local-registry.js +134 -0
  263. package/dist/testing/example-test-suite.js +16 -18
  264. package/dist/testing/expect.js +46 -22
  265. package/dist/testing/http-get.js +21 -0
  266. package/dist/testing/integration/cache.spec.js +13 -14
  267. package/dist/testing/integration/cancellation.spec.js +14 -9
  268. package/dist/testing/integration/clean.spec.js +14 -11
  269. package/dist/testing/integration/cmd.spec.js +1 -1
  270. package/dist/testing/integration/concurrency.spec.js +3 -3
  271. package/dist/testing/integration/docker-package-registry.spec.js +44 -0
  272. package/dist/testing/integration/docker-service-crash.spec.js +36 -0
  273. package/dist/testing/integration/docker.spec.js +4 -3
  274. package/dist/testing/integration/env.spec.js +9 -8
  275. package/dist/testing/integration/environment.spec.js +97 -0
  276. package/dist/testing/integration/error.spec.js +5 -4
  277. package/dist/testing/integration/export.spec.js +22 -0
  278. package/dist/testing/integration/extend.spec.js +9 -12
  279. package/dist/testing/integration/glob.spec.js +12 -20
  280. package/dist/testing/integration/include.spec.js +3 -3
  281. package/dist/testing/integration/invalid.spec.js +2 -3
  282. package/dist/testing/integration/invalid_loop.spec.js +49 -6
  283. package/dist/testing/integration/k8s-state-cache.spec.js +41 -0
  284. package/dist/testing/integration/kubernetes-service.spec.js +148 -0
  285. package/dist/testing/integration/kubernetes.spec.js +93 -9
  286. package/dist/testing/integration/labels.spec.js +6 -11
  287. package/dist/testing/integration/local.spec.js +1 -1
  288. package/dist/testing/integration/monorepo.spec.js +4 -4
  289. package/dist/testing/integration/multi-line.spec.js +3 -3
  290. package/dist/testing/integration/reference.spec.js +4 -5
  291. package/dist/testing/integration/services.spec.js +22 -4
  292. package/dist/testing/integration/store.spec.js +15 -16
  293. package/dist/testing/integration/unknown.spec.js +20 -18
  294. package/dist/testing/integration/validate.spec.js +143 -26
  295. package/dist/testing/integration/watch-dependency.spec.js +11 -8
  296. package/dist/testing/integration/watch.spec.js +15 -16
  297. package/dist/testing/requires-kubernetes.js +18 -0
  298. package/dist/testing/requires-linux-containers.js +18 -0
  299. package/dist/testing/requires-windows-containers.js +18 -0
  300. package/dist/testing/test-case.js +75 -0
  301. package/dist/testing/test-streams.js +28 -0
  302. package/dist/testing/testing-timeout.js +32 -0
  303. package/dist/utils/abort-event.js +10 -4
  304. package/dist/utils/abort-event.spec.js +46 -0
  305. package/dist/utils/buffer-context.js +44 -0
  306. package/dist/utils/buffer-context.spec.js +38 -0
  307. package/dist/utils/ci.spec.js +53 -0
  308. package/dist/utils/emitter.spec.js +34 -0
  309. package/dist/utils/empty-writable.spec.js +18 -0
  310. package/dist/utils/environment-config.js +4 -1
  311. package/dist/utils/environment-config.spec.js +26 -0
  312. package/dist/utils/fail-never.spec.js +9 -0
  313. package/dist/version.js +9 -0
  314. package/dist/version.spec.js +12 -0
  315. package/package.json +41 -23
  316. package/specs/build-file/caches/spec.md +114 -0
  317. package/specs/build-file/environment-variables/spec.md +82 -0
  318. package/specs/build-file/includes/spec.md +60 -0
  319. package/specs/build-file/references/spec.md +56 -0
  320. package/specs/build-file/spec.md +62 -0
  321. package/specs/cli/clean/spec.md +85 -0
  322. package/specs/cli/down/spec.md +85 -0
  323. package/specs/cli/execute/spec.md +101 -0
  324. package/specs/cli/init/spec.md +64 -0
  325. package/specs/cli/ls/spec.md +66 -0
  326. package/specs/cli/package/spec.md +110 -0
  327. package/specs/cli/spec.md +73 -0
  328. package/specs/cli/store-restore/spec.md +74 -0
  329. package/specs/cli/up/spec.md +80 -0
  330. package/specs/cli/validate/spec.md +70 -0
  331. package/specs/labels/spec.md +91 -0
  332. package/specs/service/container/spec.md +120 -0
  333. package/specs/service/kubernetes/spec.md +97 -0
  334. package/specs/service/spec.md +105 -0
  335. package/specs/task/caching/spec.md +107 -0
  336. package/specs/task/container/spec.md +118 -0
  337. package/specs/task/dependencies/spec.md +70 -0
  338. package/specs/task/extending/spec.md +74 -0
  339. package/specs/task/kubernetes/spec.md +125 -0
  340. package/specs/task/needs/spec.md +59 -0
  341. package/specs/task/spec.md +87 -0
  342. package/specs/task/watching/spec.md +69 -0
  343. package/dist/console/console-context-mock.js +0 -14
  344. package/dist/environment/get-process-env.js +0 -15
  345. package/dist/executer/create-scheduler-state.js +0 -32
  346. package/dist/executer/docker-node.js +0 -123
  347. package/dist/executer/hierarchy-scheduler.js +0 -94
  348. package/dist/executer/local-node.js +0 -57
  349. package/dist/executer/schedule.js +0 -137
  350. package/dist/executer/start-node.js +0 -67
  351. package/dist/executer/watch-node.js +0 -56
  352. package/dist/executer/watch-service.js +0 -54
  353. package/dist/file/get-file-context-mock.js +0 -187
  354. package/dist/file/get-file-context-mock.spec.js +0 -56
  355. package/dist/get-work-context.js +0 -29
  356. package/dist/optimizer/get-work-node-cache-stats.js +0 -104
  357. package/dist/optimizer/read-work-node-cache.js +0 -19
  358. package/dist/optimizer/work-node-cache-description.js +0 -32
  359. package/dist/optimizer/write-work-node-cache.js +0 -21
  360. package/dist/parser/build-file-task-command.js +0 -3
  361. package/dist/parser/build-file-task-platform.js +0 -3
  362. package/dist/parser/build-file-task-source.js +0 -3
  363. package/dist/parser/get-build-file.js +0 -9
  364. package/dist/parser/parse-boolean.js +0 -29
  365. package/dist/parser/parse-boolean.spec.js +0 -48
  366. package/dist/parser/parse-build-file-references.js +0 -35
  367. package/dist/parser/parse-build-file-services.js +0 -58
  368. package/dist/parser/parse-build-file-task-command.js +0 -36
  369. package/dist/parser/parse-build-file-task-source.js +0 -63
  370. package/dist/parser/parse-build-file.js +0 -125
  371. package/dist/parser/parse-context.js +0 -13
  372. package/dist/parser/parse-envs.js +0 -29
  373. package/dist/parser/parse-healthcheck.js +0 -28
  374. package/dist/parser/parse-service-selector.js +0 -39
  375. package/dist/parser/parse-string-array.js +0 -20
  376. package/dist/parser/parse-string-map.js +0 -30
  377. package/dist/parser/parse-string.js +0 -20
  378. package/dist/planner/utils/get-container-mounts.js +0 -24
  379. package/dist/planner/utils/parse-work-service-volume.js +0 -23
  380. package/dist/planner/utils/plan-work-command.js +0 -21
  381. package/dist/planner/utils/plan-work-dependency.js +0 -36
  382. package/dist/planner/utils/plan-work-node.js +0 -293
  383. package/dist/planner/utils/plan-work-nodes.js +0 -78
  384. package/dist/planner/utils/plan-work-tree.js +0 -15
  385. package/dist/planner/work-context.js +0 -23
  386. package/dist/planner/work-node-command.js +0 -3
  387. package/dist/planner/work-node-id.js +0 -12
  388. package/dist/planner/work-node-id.spec.js +0 -106
  389. package/dist/planner/work-node-path.js +0 -2
  390. package/dist/planner/work-node-source.js +0 -3
  391. package/dist/planner/work-node-validation.js +0 -3
  392. package/dist/planner/work-node.js +0 -8
  393. package/dist/planner/work-service-volume.js +0 -3
  394. package/dist/planner/work-services.js +0 -3
  395. package/dist/start-watch-processes.js +0 -28
  396. package/dist/testing/cli.js +0 -73
  397. package/dist/testing/create-build-file.js +0 -9
  398. package/dist/testing/get-logger.js +0 -23
  399. package/dist/testing/get-node.js +0 -12
  400. package/dist/testing/get-test-context.js +0 -19
  401. package/dist/testing/integration/program.spec.js +0 -25
  402. package/dist/testing/lazy.js +0 -17
  403. package/dist/testing/start-watch-processes.js +0 -29
  404. package/dist/testing/test-suite-options.js +0 -3
  405. package/dist/utils/abortable-function.js +0 -71
  406. package/logs/cache-console.log +0 -0
  407. package/logs/cache-status.log +0 -6
  408. package/logs/cache-stout.log +0 -0
  409. package/logs/cancellation-console.log +0 -0
  410. package/logs/cancellation-status.log +0 -3
  411. package/logs/cancellation-stout.log +0 -3
  412. package/logs/clean-console.log +0 -0
  413. package/logs/clean-status.log +0 -37
  414. package/logs/clean-stout.log +0 -95
  415. package/logs/cmd-console.log +0 -0
  416. package/logs/cmd-status.log +0 -8
  417. package/logs/cmd-stout.log +0 -9
  418. package/logs/concurrency-console.log +0 -0
  419. package/logs/concurrency-status.log +0 -14
  420. package/logs/concurrency-stout.log +0 -88
  421. package/logs/docker-console.log +0 -0
  422. package/logs/docker-status.log +0 -14
  423. package/logs/docker-stout.log +0 -39
  424. package/logs/env-console.log +0 -1
  425. package/logs/env-status.log +0 -1
  426. package/logs/env-stout.log +0 -0
  427. package/logs/error-console.log +0 -0
  428. package/logs/error-status.log +0 -10
  429. package/logs/error-stout.log +0 -36
  430. package/logs/extend-console.log +0 -0
  431. package/logs/extend-status.log +0 -1
  432. package/logs/extend-stout.log +0 -0
  433. package/logs/glob-console.log +0 -28
  434. package/logs/glob-status.log +0 -23
  435. package/logs/glob-stout.log +0 -52
  436. package/logs/hello-world-node-console.log +0 -0
  437. package/logs/hello-world-node-status.log +0 -37
  438. package/logs/hello-world-node-stout.log +0 -41
  439. package/logs/include-console.log +0 -0
  440. package/logs/include-status.log +0 -3
  441. package/logs/include-stout.log +0 -0
  442. package/logs/invalid-console.log +0 -0
  443. package/logs/invalid-status.log +0 -1
  444. package/logs/invalid-stout.log +0 -0
  445. package/logs/invalid_loop-console.log +0 -0
  446. package/logs/invalid_loop-status.log +0 -1
  447. package/logs/invalid_loop-stout.log +0 -11
  448. package/logs/labels-console.log +0 -0
  449. package/logs/labels-status.log +0 -1
  450. package/logs/labels-stout.log +0 -0
  451. package/logs/local-console.log +0 -0
  452. package/logs/local-status.log +0 -7
  453. package/logs/local-stout.log +0 -8
  454. package/logs/monorepo-console.log +0 -0
  455. package/logs/monorepo-status.log +0 -152
  456. package/logs/monorepo-stout.log +0 -2354
  457. package/logs/multi-line-console.log +0 -0
  458. package/logs/multi-line-status.log +0 -1
  459. package/logs/multi-line-stout.log +0 -0
  460. package/logs/reference-console.log +0 -0
  461. package/logs/reference-status.log +0 -5
  462. package/logs/reference-stout.log +0 -0
  463. package/logs/services-console.log +0 -0
  464. package/logs/services-status.log +0 -129
  465. package/logs/services-stout.log +0 -639
  466. package/logs/store-restore-console.log +0 -0
  467. package/logs/store-restore-status.log +0 -40
  468. package/logs/store-restore-stout.log +0 -97
  469. package/logs/unknown-console.log +0 -3
  470. package/logs/unknown-status.log +0 -1
  471. package/logs/unknown-stout.log +0 -0
  472. package/logs/validate-console.log +0 -0
  473. package/logs/validate-status.log +0 -2
  474. package/logs/validate-stout.log +0 -0
  475. package/logs/watch-console.log +0 -0
  476. package/logs/watch-dependency-console.log +0 -0
  477. package/logs/watch-dependency-status.log +0 -176
  478. package/logs/watch-dependency-stout.log +0 -779
  479. package/logs/watch-status.log +0 -100
  480. package/logs/watch-stout.log +0 -349
@@ -0,0 +1,80 @@
1
+ # Feature Specification: CLI: up
2
+
3
+ **Feature Branch**: `001-current-state-specs`
4
+
5
+ **Created**: 2026-05-30
6
+
7
+ **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
+
9
+ **Input**: Existing behavior documented in `docs/cli/up.md`
10
+
11
+ ## User Scenarios & Testing *(mandatory)*
12
+
13
+ ### User Story 1 - Starting shared services for local development (Priority: P1)
14
+
15
+ A developer needs a database running continuously while they iterate on several tasks. Instead of letting each task start and stop the database automatically, they run `hammerkit up` to start all services and keep them alive. When they are done for the day, they run `hammerkit down`.
16
+
17
+ **Why this priority**: Persistent service management during development is the primary use case for the `up` command; it directly reduces the startup overhead between task runs.
18
+
19
+ **Independent Test**: Given a build file with a service (e.g., `postgres`), run `hammerkit up` and verify the service container is running and remains running until the process is interrupted or `hammerkit down` is called.
20
+
21
+ **Acceptance Scenarios**:
22
+
23
+ 1. **Given** a build file with one or more services, **When** the user runs `hammerkit up`, **Then** all services start and the process blocks until the user sends an interrupt signal (Ctrl+C or equivalent).
24
+
25
+ 2. **Given** a build file with one or more services, **When** the user runs `hammerkit up --daemon`, **Then** all services start in the background and the command returns immediately with exit code 0.
26
+
27
+ 3. **Given** services started with `hammerkit up --daemon`, **When** the user subsequently runs `hammerkit down`, **Then** those services are stopped.
28
+
29
+ 4. **Given** a build file with multiple services and only some bearing a specific label, **When** the user runs `hammerkit up -f <label>`, **Then** only the services matching the label are started.
30
+
31
+ 5. **Given** a build file with multiple services and some bearing an excluded label, **When** the user runs `hammerkit up -e <label>`, **Then** services matching the excluded label are not started.
32
+
33
+ ### User Story 2 - Starting services in a named environment (Priority: P2)
34
+
35
+ A developer wants to start services inside a Kubernetes cluster configured under `environments:` in their build file. They pass `--env <name>` to target that environment instead of the local Docker runtime.
36
+
37
+ **Why this priority**: Multi-environment support is an important power-user scenario but not required for local Docker-based development.
38
+
39
+ **Independent Test**: Given a build file with a named Kubernetes environment, run `hammerkit up --env <name>` and verify that service start commands target the configured environment.
40
+
41
+ **Acceptance Scenarios**:
42
+
43
+ 1. **Given** a named environment declared in the build file, **When** the user runs `hammerkit up --env <name>`, **Then** services are started within that environment's runtime.
44
+
45
+ ### Edge Cases
46
+
47
+ - Running `hammerkit up` when no services are defined in the build file should exit cleanly (code 0) with an informational message.
48
+ - Running `hammerkit up --daemon` multiple times for already-running services should either be idempotent or report that services are already running.
49
+ - Interrupt signal during foreground `up` must cleanly stop the started services before exiting.
50
+
51
+ ## Requirements *(mandatory)*
52
+
53
+ ### Functional Requirements
54
+
55
+ - **FR-001**: System MUST start all services defined in the build file when `hammerkit up` is run without filter options.
56
+ - **FR-002**: System MUST keep services running and block the terminal until interrupted, unless `--daemon` is supplied.
57
+ - **FR-003**: System MUST start services in the background and return immediately when `--daemon` is supplied.
58
+ - **FR-004**: System MUST support label-based inclusion filtering via `-f / --filter <key=value>` to start only matching services.
59
+ - **FR-005**: System MUST support label-based exclusion filtering via `-e / --exclude <key=value>` to skip matching services.
60
+ - **FR-006**: System MUST support an `--env <name>` option to start services in a configured named environment.
61
+ - **FR-007**: System MUST support `-c / --concurrency <number>`, `-w / --watch`, `--cache <method>`, and `-l / --log <mode>` options (consistent with the `run` command option surface).
62
+
63
+ ### Key Entities
64
+
65
+ - **Service**: A long-running container (e.g., a database) declared in the build file under `services:`, started and kept running by `up`.
66
+
67
+ ## Success Criteria *(mandatory)*
68
+
69
+ ### Measurable Outcomes
70
+
71
+ - **SC-001**: After `hammerkit up` (foreground), the service container is reachable on its declared port while the process is running.
72
+ - **SC-002**: After `hammerkit up --daemon`, the command exits with code 0 and the service container is reachable on its declared port.
73
+ - **SC-003**: With `-f <label>`, only services bearing that label are started; other services remain stopped.
74
+ - **SC-004**: Services started with `hammerkit up` are stopped when the foreground process receives an interrupt signal.
75
+
76
+ ## Assumptions
77
+
78
+ - Services that are already running (e.g., started by a prior `up` invocation) are handled gracefully; exact behavior (skip, restart, error) is an implementation detail not specified in the docs.
79
+ - The `--watch` and `--cache` flags carry over from the `run` command option surface and apply to any tasks that services might trigger; their exact effect on pure-service `up` runs is not specified in the docs and assumed to be a no-op for services themselves.
80
+ - `--env` targets named environments declared under the `environments:` key in the build file (informed by the cross-reference to the Kubernetes doc).
@@ -0,0 +1,70 @@
1
+ # Feature Specification: CLI: validate
2
+
3
+ **Feature Branch**: `001-current-state-specs`
4
+
5
+ **Created**: 2026-05-30
6
+
7
+ **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
+
9
+ **Input**: Existing behavior documented in `docs/cli/validate.md`
10
+
11
+ ## User Scenarios & Testing *(mandatory)*
12
+
13
+ ### User Story 1 - Catch build-file errors before running tasks (Priority: P1)
14
+
15
+ A developer edits `.hammerkit.yaml` and wants to verify the configuration is correct before triggering a potentially long-running build. They run `hammerkit validate`, which checks the build file for invalid configurations or missing information and reports any problems immediately.
16
+
17
+ **Why this priority**: Early error detection prevents wasted time on builds that would fail partway through due to a misconfigured build file.
18
+
19
+ **Independent Test**: Introduce a deliberate misconfiguration in a build file (e.g., a task referencing a non-existent dependency). Run `hammerkit validate`. Confirm the command exits with a non-zero code and reports the error. Then fix the file and confirm `hammerkit validate` exits successfully.
20
+
21
+ **Acceptance Scenarios**:
22
+
23
+ 1. **Given** a build file with a valid configuration, **When** `hammerkit validate` is run, **Then** the command exits with a success code and reports no errors.
24
+
25
+ 2. **Given** a build file with an invalid configuration (e.g., missing required fields, unknown references), **When** `hammerkit validate` is run, **Then** the command exits with a non-zero code and reports a clear description of each problem found.
26
+
27
+ 3. **Given** a build file with missing required information, **When** `hammerkit validate` is run, **Then** the specific missing information is identified in the output.
28
+
29
+ ### User Story 2 - Use validate as a CI linting step (Priority: P2)
30
+
31
+ A CI pipeline operator adds `hammerkit validate` as an early linting job alongside other linters so that build-file errors are caught on every pull request before any build or test jobs run.
32
+
33
+ **Why this priority**: The docs explicitly recommend this usage pattern; catching errors in CI prevents broken build configurations from reaching the main branch.
34
+
35
+ **Independent Test**: Add `hammerkit validate` to a CI job. Introduce a build-file error on a branch. Confirm the CI job fails. Fix the error and confirm the CI job passes.
36
+
37
+ **Acceptance Scenarios**:
38
+
39
+ 1. **Given** `hammerkit validate` is included as a CI job step, **When** a pull request introduces an invalid build file, **Then** the CI job fails and the error is visible in the CI output before any build or test jobs run.
40
+
41
+ 2. **Given** a valid build file, **When** `hammerkit validate` runs in CI, **Then** the job passes and downstream jobs are not blocked.
42
+
43
+ ### Edge Cases
44
+
45
+ - Running `hammerkit validate` when no build file exists should exit with a non-zero code and report a clear error about the missing file.
46
+ - A build file that is syntactically valid YAML but contains semantically invalid hammerkit configuration (e.g., referencing an undefined task as a dependency) should be caught by validate.
47
+
48
+ ## Requirements *(mandatory)*
49
+
50
+ ### Functional Requirements
51
+
52
+ - **FR-001**: System MUST parse and validate the build file when `hammerkit validate` is run.
53
+ - **FR-002**: System MUST exit with a success code when the build file contains no invalid configurations or missing required information.
54
+ - **FR-003**: System MUST exit with a non-zero code and report all detected errors when the build file contains invalid configurations or missing required information.
55
+ - **FR-004**: Error output MUST describe each problem clearly enough for a developer to identify and fix it without additional tooling.
56
+
57
+ ## Success Criteria *(mandatory)*
58
+
59
+ ### Measurable Outcomes
60
+
61
+ - **SC-001**: `hammerkit validate` exits with code 0 for every valid build file accepted by `hammerkit run`.
62
+ - **SC-002**: `hammerkit validate` exits with a non-zero code for every build file that would cause `hammerkit run` to fail due to a configuration error.
63
+ - **SC-003**: Each error message produced by `hammerkit validate` identifies the location or nature of the problem sufficiently for a developer to correct it.
64
+
65
+ ## Assumptions
66
+
67
+ - `hammerkit validate` validates the build file in the current working directory (defaulting to `.hammerkit.yaml`); alternate file paths are not documented and are assumed unsupported by this command.
68
+ - The command performs static analysis only — it does not execute tasks, pull images, or make network calls to verify external resources.
69
+ - No `--filter`/`--exclude` options are documented for `validate`; the command always validates the entire build file.
70
+ - "Invalid configurations" includes but may not be limited to: undefined task/service references, missing required fields, and type mismatches in the schema.
@@ -0,0 +1,91 @@
1
+ # Feature Specification: Labels: Overview
2
+
3
+ **Feature Branch**: `001-current-state-specs`
4
+
5
+ **Created**: 2026-05-30
6
+
7
+ **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
+
9
+ **Input**: Existing behavior documented in `docs/labels/README.md`
10
+
11
+ ## User Scenarios & Testing *(mandatory)*
12
+
13
+ ### User Story 1 - Filtering tasks to run only a matching subset (Priority: P1)
14
+
15
+ A developer works in a monorepo with tasks for multiple projects. They tag each task with a `project` label and run `hammerkit task -f project=a` to execute only the tasks (and their dependencies) that carry that label.
16
+
17
+ **Why this priority**: Label-based task filtering is the primary value proposition of labels — enabling targeted runs in complex build files without manually listing task names.
18
+
19
+ **Independent Test**: Declare tasks `build-a` and `build-b` with labels `project: a` and `project: b` respectively. Run `hammerkit -f project=a` and confirm only tasks with `project=a` (and their deps) are executed, not `build-b`.
20
+
21
+ **Acceptance Scenarios**:
22
+
23
+ 1. **Given** tasks with different `project` label values, **When** `hammerkit -f project=a` is executed, **Then** only tasks whose `project` label equals `a` are included in the run.
24
+
25
+ 2. **Given** a task with label `project: a` has a dependency task without any labels, **When** `hammerkit -f project=a` is run, **Then** the unlabelled dependency is still executed as part of the filtered run.
26
+
27
+ ### User Story 2 - Excluding tasks by label (Priority: P1)
28
+
29
+ A CI pipeline runs on two separate runners — one macOS, one Linux. The macOS runner should skip all non-iOS tasks. The operator uses `hammerkit -e platform=ios` on the Linux runner to exclude all tasks labelled `platform: ios`.
30
+
31
+ **Why this priority**: Exclusion (`-e`) is equally important to inclusion (`-f`) for CI matrix setups and is documented side-by-side.
32
+
33
+ **Independent Test**: Declare tasks with and without `platform: ios`. Run `hammerkit -e platform=ios` and confirm tasks labelled `platform: ios` are skipped while unlabelled tasks run.
34
+
35
+ **Acceptance Scenarios**:
36
+
37
+ 1. **Given** a mix of tasks with and without `platform: ios`, **When** `hammerkit -e platform=ios` is run, **Then** tasks with `platform: ios` are excluded and all other tasks proceed normally.
38
+
39
+ 2. **Given** the macOS runner uses `hammerkit -f platform=ios`, **When** executed, **Then** only tasks labelled `platform: ios` run on that host.
40
+
41
+ ### User Story 3 - Grouping unrelated tasks under a common label for parallel or sequential execution (Priority: P2)
42
+
43
+ A developer wants a single command to start both an API server and a frontend watcher that have no dependency between them. They assign both a `task: dev` label and run `hammerkit -f task=dev` to start them together.
44
+
45
+ **Why this priority**: Grouping by purpose simplifies developer workflows and reduces the need to remember multiple task names.
46
+
47
+ **Independent Test**: Declare two independent tasks with `task: dev`. Run `hammerkit -f task=dev` and verify both tasks are executed in the same run.
48
+
49
+ **Acceptance Scenarios**:
50
+
51
+ 1. **Given** two independent tasks share `task: dev`, **When** `hammerkit -f task=dev` is executed, **Then** both tasks run in the same invocation.
52
+
53
+ 2. **Given** tasks labelled `task: release` are also present, **When** `hammerkit -f task=dev` is run, **Then** release tasks are not executed.
54
+
55
+ ### Edge Cases
56
+
57
+ - A label is a key-value pair; both key and value must be specified in filter/exclusion flags (e.g. `-f project=a`, not `-f project`).
58
+ - Labels can be defined at the task level or at the build-file level (build-file-level labels apply to all tasks in that file).
59
+ - When using store/restore across CI hosts with label-filtered runs, the `store` and `restore` commands accept the same `-f`/`-e` label arguments (e.g. `hammerkit store -e platform=ios`).
60
+ - A task with no labels is included in runs that use `-f` only if it is a dependency of a matching task; it is never excluded by `-e` (since it has no matching label).
61
+
62
+ ## Requirements *(mandatory)*
63
+
64
+ ### Functional Requirements
65
+
66
+ - **FR-001**: System MUST support defining labels as key-value pairs on individual tasks.
67
+ - **FR-002**: System MUST support defining labels at the build-file level, applying them to all tasks in that file.
68
+ - **FR-003**: System MUST provide a `-f <key>=<value>` CLI flag that restricts the run to tasks whose labels include the specified key-value pair (and their dependencies).
69
+ - **FR-004**: System MUST provide a `-e <key>=<value>` CLI flag that excludes tasks whose labels include the specified key-value pair from the run.
70
+ - **FR-005**: System MUST apply `-f` and `-e` label arguments to `store` and `restore` CLI commands in addition to task execution commands.
71
+ - **FR-006**: System MUST still execute unlabelled dependency tasks required by label-matched tasks when `-f` filtering is active.
72
+
73
+ ### Key Entities
74
+
75
+ - **Label**: A key-value pair attached to a task or build file. Both key and value are strings.
76
+
77
+ ## Success Criteria *(mandatory)*
78
+
79
+ ### Measurable Outcomes
80
+
81
+ - **SC-001**: Running with `-f <key>=<value>` results in only tasks carrying that label (and their transitive dependencies) being executed, confirmed by execution logs.
82
+ - **SC-002**: Running with `-e <key>=<value>` results in no task carrying that label being executed, confirmed by execution logs.
83
+ - **SC-003**: Build files with overlapping task graphs (monorepo scenario) can be filtered to isolated subsets using a single label flag without editing the build file.
84
+ - **SC-004**: `hammerkit store -e platform=ios` and `hammerkit store -f platform=ios` honour the same label filtering logic as task execution.
85
+
86
+ ## Assumptions
87
+
88
+ - Labels are case-sensitive for both key and value comparisons.
89
+ - Multiple `-f` or `-e` flags in a single invocation are assumed to be AND-combined (all specified labels must match), though the docs do not explicitly state this; noted as an assumption.
90
+ - Build-file-level labels are inherited by all tasks in the file but can be overridden at the task level; the docs mention both scopes without describing conflict resolution, so this is assumed additive (file labels + task labels merge).
91
+ - Labels are metadata only and have no effect on task execution order beyond determining which tasks are included or excluded.
@@ -0,0 +1,120 @@
1
+ # Feature Specification: Service: Container
2
+
3
+ **Feature Branch**: `001-current-state-specs`
4
+
5
+ **Created**: 2026-05-30
6
+
7
+ **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
+
9
+ **Input**: Existing behavior documented in `docs/service/container.md`
10
+
11
+ ## User Scenarios & Testing *(mandatory)*
12
+
13
+ ### User Story 1 - Declaring a container service with a healthcheck (Priority: P1)
14
+
15
+ A developer needs a postgres database that is fully ready before any dependent task starts. They declare a container service with an `image`, optional environment variables, and a `healthcheck` command. Hammerkit starts the container and repeatedly executes the healthcheck inside it; when the command exits 0, dependent tasks are unblocked.
16
+
17
+ **Why this priority**: The healthcheck is the central correctness guarantee for container services — without it tasks may fail due to a not-yet-ready service.
18
+
19
+ **Independent Test**: Declare a postgres service with `healthcheck.cmd: "pg_isready -U postgres"`. Run a task that needs it and confirm the task only begins after `pg_isready` returns 0.
20
+
21
+ **Acceptance Scenarios**:
22
+
23
+ 1. **Given** a container service with a `healthcheck` configured, **When** a task that needs the service is triggered, **Then** hammerkit does not start the task until the healthcheck command exits with code 0 inside the service container.
24
+
25
+ 2. **Given** a container service with no `healthcheck`, **When** a task that needs it is triggered, **Then** hammerkit starts the task as soon as the container is running (without waiting for application readiness).
26
+
27
+ ### User Story 2 - Exposing service ports to the host for debugging (Priority: P2)
28
+
29
+ A developer wants to connect a local SQL client to a running postgres service for inspection. They declare a `ports` mapping on the service so the container port is published to the host machine.
30
+
31
+ **Why this priority**: Port exposure is optional for task connectivity but commonly needed during development for direct access.
32
+
33
+ **Independent Test**: Declare a postgres service with `ports: [5432:5432]`. Start the service and verify that a connection to `localhost:5432` from the host succeeds.
34
+
35
+ **Acceptance Scenarios**:
36
+
37
+ 1. **Given** a service declares `ports: - 5432:5432`, **When** the service is running, **Then** port 5432 on the host machine is forwarded to the service container.
38
+
39
+ 2. **Given** no `ports` are declared on a service, **When** a container task that needs it runs, **Then** the task can still reach the service on all its container ports via the container network without any published ports.
40
+
41
+ ### User Story 3 - Persisting service data across restarts with volumes (Priority: P2)
42
+
43
+ A developer wants database data to survive service restarts. They add a `volumes` entry mapping a named volume to the data directory inside the container.
44
+
45
+ **Why this priority**: Without volumes, container services lose all state on every stop/start cycle, which breaks local development workflows.
46
+
47
+ **Independent Test**: Declare a postgres service with a volume, seed it with data, stop and restart the service, and verify the data is still present.
48
+
49
+ **Acceptance Scenarios**:
50
+
51
+ 1. **Given** a service declares a named volume (e.g. `postgres-db:/var/lib/postgresql/data`), **When** the service is stopped and restarted, **Then** data written during the first run is still available after restart.
52
+
53
+ ### User Story 4 - Mounting local config files into a service container (Priority: P3)
54
+
55
+ A developer needs to supply a custom postgres configuration file. They declare a `mounts` entry to bind-mount the local file into the container at the expected path.
56
+
57
+ **Why this priority**: Mounts are an advanced configuration option; most services do not require them.
58
+
59
+ **Independent Test**: Declare a service with a mount pointing to a local config file. Start the service and verify the file is accessible at the mounted path inside the container.
60
+
61
+ **Acceptance Scenarios**:
62
+
63
+ 1. **Given** a service declares `mounts: - "./postgres.conf:/etc/postgresql/postgresql.conf"`, **When** the service container starts, **Then** the file at `./postgres.conf` on the host is readable at `/etc/postgresql/postgresql.conf` inside the container.
64
+
65
+ ### User Story 5 - Exporting and importing service volume data (Priority: P3)
66
+
67
+ A developer seeds a local database from a known-good snapshot using `hammerkit store` / `hammerkit restore` to export and import the service's volume data.
68
+
69
+ **Why this priority**: Store/restore for services is a power feature enabling reproducible environments and data sharing between teammates.
70
+
71
+ **Independent Test**: Run a service, write data, run `hammerkit store`, wipe the volume, run `hammerkit restore`, verify data is back.
72
+
73
+ **Acceptance Scenarios**:
74
+
75
+ 1. **Given** a running service with volume data, **When** `hammerkit store` is executed for that service, **Then** the volume data is exported to a transferable archive.
76
+
77
+ 2. **Given** an exported archive, **When** `hammerkit restore` is executed, **Then** the service volume is populated with the archived data.
78
+
79
+ ### Edge Cases
80
+
81
+ - A service without a healthcheck may start a dependent task before the service application is ready to accept connections.
82
+ - Mounts of large or frequently changing directories on macOS and Windows incur significant CPU overhead due to filesystem sync; volumes are preferred for data.
83
+ - A service is stopped automatically when no further tasks in the current run require it (unless kept alive via `hammerkit up`).
84
+
85
+ ## Requirements *(mandatory)*
86
+
87
+ ### Functional Requirements
88
+
89
+ - **FR-001**: System MUST start container services using the specified `image`.
90
+ - **FR-002**: System MUST pass declared `envs` as environment variables into the service container.
91
+ - **FR-003**: System MUST publish declared `ports` mappings from the service container to the host machine.
92
+ - **FR-004**: System MUST execute the `healthcheck.cmd` inside the service container and only unblock dependent tasks once it exits with code 0.
93
+ - **FR-005**: System MUST mount declared `volumes` (named volumes and bind paths) into the service container to persist data across restarts.
94
+ - **FR-006**: System MUST bind-mount declared `mounts` (host path to container path) into the service container.
95
+ - **FR-007**: System MUST make all container ports of a service reachable from other container tasks in the same network, regardless of whether `ports` are published to the host.
96
+ - **FR-008**: System MUST support store/restore operations on service volumes via the `hammerkit store` and `hammerkit restore` CLI commands.
97
+
98
+ ### Key Entities
99
+
100
+ - **Container Service**: A service with an `image`, optional `envs`, `ports`, `healthcheck`, `volumes`, and `mounts`.
101
+ - **Healthcheck**: Contains a `cmd` string executed inside the service container. A zero exit code signals readiness.
102
+ - **Volume**: A named or path-based Docker volume entry mapping a volume identifier to a container path.
103
+ - **Mount**: A bind-mount entry mapping a host file/directory path to a container path.
104
+
105
+ ## Success Criteria *(mandatory)*
106
+
107
+ ### Measurable Outcomes
108
+
109
+ - **SC-001**: A task dependent on a service with a healthcheck does not start until the healthcheck exits 0, verified by log timestamps.
110
+ - **SC-002**: A declared port mapping results in a reachable port on the host machine while the service is running.
111
+ - **SC-003**: Data written to a volume-backed service directory is present after the service container is stopped and restarted.
112
+ - **SC-004**: A bind-mounted file is readable at its declared container path when the service container is inspected.
113
+ - **SC-005**: `hammerkit store` produces a restorable artifact and `hammerkit restore` repopulates the volume to a matching state.
114
+
115
+ ## Assumptions
116
+
117
+ - Container services run within the same Docker network as container tasks; no additional network configuration is needed for inter-container communication.
118
+ - The `healthcheck.cmd` is a shell command string executed inside the service container (not on the host).
119
+ - Services sharing volumes across multiple hammerkit projects run as a single container instance, not duplicated per project.
120
+ - `ports` are not required for container tasks to reach the service — they are solely for host-side access.
@@ -0,0 +1,97 @@
1
+ # Feature Specification: Service: Kubernetes
2
+
3
+ **Feature Branch**: `001-current-state-specs`
4
+
5
+ **Created**: 2026-05-30
6
+
7
+ **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
+
9
+ **Input**: Existing behavior documented in `docs/service/kubernetes.md`
10
+
11
+ ## User Scenarios & Testing *(mandatory)*
12
+
13
+ ### User Story 1 - Forwarding a Kubernetes deployment port to a local task (Priority: P1)
14
+
15
+ A developer wants to run a local task against a staging database that lives in a Kubernetes cluster. They declare an environment with the cluster context, declare a Kubernetes service with a `selector` pointing to the deployment and the ports to forward, then run their task with `--env staging`. Hammerkit establishes the port-forward and the task reaches the database on localhost.
16
+
17
+ **Why this priority**: This is the primary use case of Kubernetes services — accessing remote cluster resources locally without leaving the hammerkit workflow.
18
+
19
+ **Independent Test**: Declare a `prod` environment with a valid kubeconfig context and a service selecting a known deployment. Run `hammerkit <task> --env prod` and confirm the task can connect on the forwarded port.
20
+
21
+ **Acceptance Scenarios**:
22
+
23
+ 1. **Given** an environment declares a Kubernetes `context` and a service declares a `selector` with `type: deployment` and a `name`, **When** a task needing the service is run with `--env <env>`, **Then** hammerkit forwards the specified ports from the matching deployment to localhost.
24
+
25
+ 2. **Given** an environment named `default` exists in the build file, **When** a task is run without `--env`, **Then** hammerkit uses the `default` environment automatically.
26
+
27
+ 3. **Given** the same service definition, **When** the task is run with `--env local` versus `--env prod`, **Then** the port-forward targets the cluster configured in the respective environment.
28
+
29
+ ### User Story 2 - Overriding the namespace or context per service (Priority: P2)
30
+
31
+ A developer has a `prod` environment pointing to a GKE cluster with a default namespace of `databases`, but one service lives in the `orders` namespace. They add `namespace: orders` on that service to override only the namespace while inheriting everything else from the environment.
32
+
33
+ **Why this priority**: Per-service overrides allow a single build file to target resources spread across multiple namespaces without duplicating environment definitions.
34
+
35
+ **Independent Test**: Declare an environment with `namespace: databases` and a service that overrides `namespace: orders`. Verify the port-forward targets the `orders` namespace.
36
+
37
+ **Acceptance Scenarios**:
38
+
39
+ 1. **Given** an environment declares `namespace: databases` and a service declares `namespace: orders`, **When** the service is activated, **Then** the port-forward targets the `orders` namespace, not `databases`.
40
+
41
+ 2. **Given** a service declares neither `namespace`, `context`, nor `kubeconfig`, **When** it is activated, **Then** it inherits all three from the selected environment.
42
+
43
+ ### User Story 3 - Connecting to different cloud providers (Priority: P3)
44
+
45
+ A developer's team uses GKE for prod and EKS for staging. They configure two environments with the appropriate context names written by the cloud CLI tools. Services are declared once and are provider-agnostic.
46
+
47
+ **Why this priority**: Multi-provider support broadens the tool's applicability but is a configuration concern, not a behavioral distinction.
48
+
49
+ **Independent Test**: Declare two environments with provider-specific context names and run the same service-needing task against each; verify the correct cluster is targeted each time.
50
+
51
+ **Acceptance Scenarios**:
52
+
53
+ 1. **Given** a GKE environment with context `gke_<project>_<location>_<cluster>`, **When** a task is run with that environment, **Then** hammerkit connects to the GKE cluster via the matching kubeconfig entry.
54
+
55
+ 2. **Given** an AKS environment with a dedicated `kubeconfig` file, **When** a task is run with that environment, **Then** hammerkit uses the specified kubeconfig file rather than the default `~/.kube/config`.
56
+
57
+ ### Edge Cases
58
+
59
+ - If no matching deployment, service, or pod exists in the target cluster namespace, the port-forward fails and the task does not start.
60
+ - If neither the service nor the environment specifies a `namespace`, hammerkit defaults to the `default` Kubernetes namespace.
61
+ - The `kubectl` binary is not required; hammerkit performs port-forwarding directly through the Kubernetes client library.
62
+ - A service can override `context` or `kubeconfig` individually while still inheriting the other fields from the environment.
63
+
64
+ ## Requirements *(mandatory)*
65
+
66
+ ### Functional Requirements
67
+
68
+ - **FR-001**: System MUST forward ports from a Kubernetes resource to localhost using the `selector` (type and name) and `ports` declared on the service.
69
+ - **FR-002**: System MUST resolve the cluster connection (`context`, `kubeconfig`, `namespace`) from the environment selected via `--env`.
70
+ - **FR-003**: System MUST use the environment named `default` when no `--env` flag is provided and such an environment exists.
71
+ - **FR-004**: System MUST allow a service to override `namespace`, `context`, and/or `kubeconfig` independently, inheriting unoverridden fields from the environment.
72
+ - **FR-005**: System MUST support `selector.type` values of `deployment`, `service`, and `pod`.
73
+ - **FR-006**: System MUST establish port-forwarding without requiring the `kubectl` binary on the host.
74
+ - **FR-007**: System MUST default to the environment's `kubeconfig`, falling back to `$HOME/.kube/config` when neither the service nor the environment specifies one.
75
+ - **FR-008**: System MUST default to the environment's `namespace`, falling back to `default` when neither specifies one.
76
+
77
+ ### Key Entities
78
+
79
+ - **Kubernetes Service**: A service entry with `selector` (resource `type` and `name`), `ports` (host:container mappings), and optional `namespace`, `context`, `kubeconfig` overrides.
80
+ - **Environment**: A named block under `environments` containing a `kubernetes` sub-key with `context`, optional `kubeconfig`, and optional `namespace`.
81
+ - **Selector**: Specifies the Kubernetes resource to forward from. Fields: `type` (deployment | service | pod) and `name`.
82
+
83
+ ## Success Criteria *(mandatory)*
84
+
85
+ ### Measurable Outcomes
86
+
87
+ - **SC-001**: A task that needs a Kubernetes service can connect to the forwarded port on localhost while the task runs, verified by a successful network call.
88
+ - **SC-002**: Switching `--env` between two environments with different cluster contexts results in the port-forward targeting the correct cluster, confirmed by which database responds.
89
+ - **SC-003**: A per-service `namespace` override causes the forward to target the overriding namespace, not the environment's default namespace.
90
+ - **SC-004**: Port-forwarding succeeds on a machine that does not have `kubectl` installed.
91
+
92
+ ## Assumptions
93
+
94
+ - Each port mapping in `ports` follows `hostPort:containerPort` notation; hammerkit binds `hostPort` on localhost and forwards to `containerPort` on the selected Kubernetes resource.
95
+ - The `--env` flag is the only mechanism for selecting a Kubernetes environment at runtime; there is no interactive prompt.
96
+ - Kubernetes services in hammerkit are for port-forwarding only — they do not schedule workloads on the cluster (see task/kubernetes.md for running tasks on a cluster).
97
+ - A Kubernetes service without an explicit `image` field is distinguished from a container service by the presence of a `selector` field.
@@ -0,0 +1,105 @@
1
+ # Feature Specification: Service: Overview
2
+
3
+ **Feature Branch**: `001-current-state-specs`
4
+
5
+ **Created**: 2026-05-30
6
+
7
+ **Status**: Current State (reverse-specified from docs, hammerkit 1.6.0)
8
+
9
+ **Input**: Existing behavior documented in `docs/service/README.md`
10
+
11
+ ## User Scenarios & Testing *(mandatory)*
12
+
13
+ ### User Story 1 - Running a dependency service alongside a container task (Priority: P1)
14
+
15
+ A developer needs a database available before their application task starts. They declare a `postgres` service in the build file and add it to the task's `needs` list. When they run the task, hammerkit starts the service first (waiting for its healthcheck to pass if one is configured), then starts the task container — both share a container network so the task can reach the service by its name (e.g. `postgres:5432`).
16
+
17
+ **Why this priority**: Services are the primary mechanism for wiring runtime dependencies to tasks; this is the core use case documented first.
18
+
19
+ **Independent Test**: Create a build file with a postgres service and a container task that `needs` it. Run the task and confirm it connects to postgres using the service name as the hostname.
20
+
21
+ **Acceptance Scenarios**:
22
+
23
+ 1. **Given** a build file declares a `postgres` container service and a container task with `needs: [postgres]`, **When** the task is executed, **Then** hammerkit starts the postgres container before the task and the task can reach it at hostname `postgres`.
24
+
25
+ 2. **Given** the postgres service has a `healthcheck` configured, **When** the task is about to start, **Then** hammerkit waits until the healthcheck exits with code 0 before launching the task container.
26
+
27
+ ### User Story 2 - Using a service from a local (non-container) task (Priority: P2)
28
+
29
+ A developer runs a local task (no `image`) that needs a running service. Because the local task is not part of the container network, hammerkit injects connection details as environment variables so the process can still reach the service.
30
+
31
+ **Why this priority**: Local tasks are common for development workflows and the env-variable injection is the only way they can reach services.
32
+
33
+ **Independent Test**: Declare a container service and a local task that `needs` it. Run the local task and verify it receives `HAMMERKIT_<NAME>_HOST`, `HAMMERKIT_<NAME>_PORT`, and `HAMMERKIT_<NAME>_PORT_<containerPort>` environment variables.
34
+
35
+ **Acceptance Scenarios**:
36
+
37
+ 1. **Given** a local task (no `image`) declares `needs: [postgres]` where postgres exposes port `5432`, **When** the task is executed, **Then** the process environment contains `HAMMERKIT_POSTGRES_HOST`, `HAMMERKIT_POSTGRES_PORT`, and `HAMMERKIT_POSTGRES_PORT_5432`.
38
+
39
+ 2. **Given** a service exposes multiple ports, **When** a local task needing it runs, **Then** a `HAMMERKIT_<NAME>_PORT_<containerPort>` variable is injected for each exposed container port.
40
+
41
+ ### User Story 3 - Manually controlling service lifetime with `up` / `down` (Priority: P2)
42
+
43
+ A developer wants to keep a shared database running across several task runs without restarting it each time. They start the service with `hammerkit up` and stop it later with `hammerkit down`.
44
+
45
+ **Why this priority**: Manual lifecycle control is important for developer-experience workflows where repeated task runs reuse a warm service.
46
+
47
+ **Independent Test**: Run `hammerkit up`, verify the service container is running, then execute a task that needs it (it should skip starting the service), then run `hammerkit down` and verify the container stops.
48
+
49
+ **Acceptance Scenarios**:
50
+
51
+ 1. **Given** a service is defined, **When** `hammerkit up` is executed, **Then** the service container starts and remains running.
52
+
53
+ 2. **Given** a running service started via `hammerkit up`, **When** `hammerkit down` is executed, **Then** the service container stops.
54
+
55
+ ### User Story 4 - Service-to-service and service-to-task dependencies (Priority: P3)
56
+
57
+ A developer has a service that depends on another service (via `needs`) and/or requires a task to run first (via `deps`). Hammerkit resolves the dependency chain before starting the dependent service.
58
+
59
+ **Why this priority**: Dependency chaining for services mirrors task dependencies and is a secondary but documented capability.
60
+
61
+ **Independent Test**: Declare two services where one lists the other in `needs`, and the first also lists a task in `deps`. Verify hammerkit runs the task first, then starts the dependency service, then the dependent service.
62
+
63
+ **Acceptance Scenarios**:
64
+
65
+ 1. **Given** service `api` declares `deps: [install]` and `needs: [postgres]`, **When** a task needing `api` is run, **Then** hammerkit runs `install` first, then starts `postgres`, then starts `api`.
66
+
67
+ ### Edge Cases
68
+
69
+ - A local task that `needs` a Kubernetes-forwarded service receives the same `HAMMERKIT_<NAME>_*` environment variables, but hammerkit does not provide DNS resolution for it.
70
+ - If no healthcheck is defined, hammerkit may start a dependent task before the service is fully ready.
71
+ - A service name is uppercased when constructing environment variable names (e.g. service `my-db` becomes `HAMMERKIT_MY_DB_HOST`).
72
+
73
+ ## Requirements *(mandatory)*
74
+
75
+ ### Functional Requirements
76
+
77
+ - **FR-001**: System MUST start all services listed in a task's `needs` before executing that task.
78
+ - **FR-002**: System MUST expose services to container tasks via the container network using the service name as the DNS hostname.
79
+ - **FR-003**: System MUST inject `HAMMERKIT_<NAME>_HOST`, `HAMMERKIT_<NAME>_PORT`, and `HAMMERKIT_<NAME>_PORT_<containerPort>` environment variables into local tasks for each needed service, where `<NAME>` is the uppercased service name.
80
+ - **FR-004**: System MUST wait for a service's healthcheck command to exit with code 0 before launching any task that needs it, when a healthcheck is configured.
81
+ - **FR-005**: System MUST support `hammerkit up` to start services independently of task execution.
82
+ - **FR-006**: System MUST support `hammerkit down` to stop running services.
83
+ - **FR-007**: System MUST resolve service `deps` (tasks) and `needs` (other services) before starting a service, applying the same dependency-ordering rules as for tasks.
84
+ - **FR-008**: System MUST support both container services (image-based) and Kubernetes port-forwarding services within the same build file.
85
+
86
+ ### Key Entities
87
+
88
+ - **Service**: A continuous, long-running process declared under the `services` key in a build file. Has a `name`, optional `needs` (other services), optional `deps` (tasks), and is either container-based or Kubernetes-based.
89
+ - **Healthcheck**: An optional check attached to a container service. Contains a `cmd` executed inside the service container; exit code 0 means ready.
90
+
91
+ ## Success Criteria *(mandatory)*
92
+
93
+ ### Measurable Outcomes
94
+
95
+ - **SC-001**: A container task that `needs` a service starts only after that service is running (and its healthcheck passes, if present), verified across at least one postgres and one redis-style service.
96
+ - **SC-002**: A local task that `needs` a service receives the correct host/port environment variables, confirmed by the task process reading them and connecting successfully.
97
+ - **SC-003**: `hammerkit up` / `hammerkit down` start and stop services within a reasonable time, with no task execution triggered.
98
+ - **SC-004**: Service dependency chains (service needing another service, service needing a task) resolve in the correct order with zero manual intervention.
99
+
100
+ ## Assumptions
101
+
102
+ - The service name used as the DNS hostname is exactly as declared in the build file (case-sensitive for the network alias; uppercased only for the environment variable injection).
103
+ - `HAMMERKIT_<NAME>_PORT` refers to the first/primary published port when a service exposes multiple ports.
104
+ - Services are automatically stopped when they are no longer needed by any remaining task in the current run, unless started with `hammerkit up`.
105
+ - The `--env` flag for selecting Kubernetes environments applies equally to Kubernetes-type services; container services are not environment-scoped.