@lumenflow/cli 3.1.1 → 3.1.3

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 (389) hide show
  1. package/README.md +36 -35
  2. package/dist/agent-issues-query.js +13 -8
  3. package/dist/agent-log-issue.js +15 -4
  4. package/dist/agent-session-end.js +15 -4
  5. package/dist/agent-session.js +18 -6
  6. package/dist/backlog-prune.js +1 -1
  7. package/dist/commands/integrate.js +32 -18
  8. package/dist/config-get.js +27 -15
  9. package/dist/config-set.js +104 -37
  10. package/dist/delegation-list.js +1 -1
  11. package/dist/doctor.js +19 -13
  12. package/dist/file-delete.js +1 -1
  13. package/dist/file-edit.js +1 -1
  14. package/dist/file-read.js +1 -1
  15. package/dist/file-write.js +1 -1
  16. package/dist/flow-bottlenecks.js +1 -1
  17. package/dist/flow-report.js +10 -9
  18. package/dist/gates.js +3 -2
  19. package/dist/git-branch.js +1 -1
  20. package/dist/git-diff.js +1 -1
  21. package/dist/git-log.js +1 -1
  22. package/dist/init.js +244 -61
  23. package/dist/initiative-add-wu.js +1 -1
  24. package/dist/initiative-bulk-assign-wus.js +1 -1
  25. package/dist/initiative-create.js +2 -2
  26. package/dist/initiative-edit.js +1 -1
  27. package/dist/initiative-list.js +1 -1
  28. package/dist/initiative-plan.js +1 -3
  29. package/dist/initiative-status.js +47 -6
  30. package/dist/lane-edit.js +19 -10
  31. package/dist/lane-health.js +13 -24
  32. package/dist/lane-lock.js +4 -5
  33. package/dist/lane-setup.js +5 -5
  34. package/dist/lane-status.js +4 -5
  35. package/dist/lane-suggest.js +9 -7
  36. package/dist/lane-validate.js +4 -5
  37. package/dist/lumenflow-upgrade.js +17 -11
  38. package/dist/mem-checkpoint.js +1 -1
  39. package/dist/mem-cleanup.js +6 -23
  40. package/dist/mem-context.js +1 -1
  41. package/dist/mem-create.js +1 -1
  42. package/dist/mem-delete.js +1 -1
  43. package/dist/mem-export.js +1 -1
  44. package/dist/mem-inbox.js +1 -1
  45. package/dist/mem-init.js +1 -1
  46. package/dist/mem-ready.js +1 -1
  47. package/dist/mem-recover.js +1 -1
  48. package/dist/mem-signal.js +1 -1
  49. package/dist/mem-start.js +1 -1
  50. package/dist/mem-summarize.js +8 -7
  51. package/dist/mem-triage.js +7 -5
  52. package/dist/metrics-cli.js +1 -1
  53. package/dist/metrics-snapshot.js +1 -1
  54. package/dist/onboard.js +295 -120
  55. package/dist/orchestrate-init-status.js +12 -7
  56. package/dist/orchestrate-initiative.js +23 -12
  57. package/dist/orchestrate-monitor.js +20 -8
  58. package/dist/pack-scaffold.js +1 -1
  59. package/dist/plan-create.js +1 -3
  60. package/dist/plan-edit.js +1 -3
  61. package/dist/plan-link.js +1 -3
  62. package/dist/plan-promote.js +1 -3
  63. package/dist/release.js +1 -3
  64. package/dist/signal-cleanup.js +4 -18
  65. package/dist/state-bootstrap.js +11 -8
  66. package/dist/state-cleanup.js +5 -19
  67. package/dist/state-doctor.js +213 -9
  68. package/dist/task-claim.js +1 -1
  69. package/dist/validate.js +1 -1
  70. package/dist/workspace-init.js +61 -61
  71. package/dist/wu-block.js +1 -1
  72. package/dist/wu-brief.js +1 -1
  73. package/dist/wu-claim.js +1 -1
  74. package/dist/wu-cleanup.js +1 -1
  75. package/dist/wu-create.js +3 -3
  76. package/dist/wu-delegate.js +1 -1
  77. package/dist/wu-deps.js +1 -1
  78. package/dist/wu-done.js +66 -34
  79. package/dist/wu-edit.js +1 -1
  80. package/dist/wu-infer-lane.js +1 -1
  81. package/dist/wu-preflight.js +1 -1
  82. package/dist/wu-prep.js +1 -1
  83. package/dist/wu-proto.js +1 -1
  84. package/dist/wu-prune.js +1 -1
  85. package/dist/wu-recover.js +1 -1
  86. package/dist/wu-release.js +1 -1
  87. package/dist/wu-repair.js +1 -1
  88. package/dist/wu-sandbox.js +40 -27
  89. package/dist/wu-status.js +1 -1
  90. package/dist/wu-unblock.js +1 -1
  91. package/dist/wu-unlock-lane.js +1 -1
  92. package/dist/wu-validate.js +1 -1
  93. package/package.json +12 -8
  94. package/packs/software-delivery/constants.ts +10 -0
  95. package/packs/software-delivery/extensions.ts +140 -0
  96. package/packs/software-delivery/gate-policies.ts +134 -0
  97. package/packs/software-delivery/index.ts +8 -0
  98. package/packs/software-delivery/manifest-schema.ts +236 -0
  99. package/packs/software-delivery/manifest.ts +417 -0
  100. package/packs/software-delivery/manifest.yaml +711 -0
  101. package/packs/software-delivery/pack-registration.ts +113 -0
  102. package/packs/software-delivery/tool-impl/agent-tools.ts +263 -0
  103. package/packs/software-delivery/tool-impl/delegation-tools.ts +66 -0
  104. package/packs/software-delivery/tool-impl/flow-metrics-tools.ts +219 -0
  105. package/packs/software-delivery/tool-impl/git-runner.ts +113 -0
  106. package/packs/software-delivery/tool-impl/git-tools.ts +316 -0
  107. package/packs/software-delivery/tool-impl/index.ts +15 -0
  108. package/packs/software-delivery/tool-impl/initiative-orchestration-tools.ts +720 -0
  109. package/packs/software-delivery/tool-impl/lane-lock.ts +246 -0
  110. package/packs/software-delivery/tool-impl/memory-tools.ts +415 -0
  111. package/packs/software-delivery/tool-impl/pending-runtime-tools.ts +21 -0
  112. package/packs/software-delivery/tool-impl/runtime-cli-adapter.ts +328 -0
  113. package/packs/software-delivery/tool-impl/runtime-native-tools.ts +687 -0
  114. package/packs/software-delivery/tool-impl/worker-loader.ts +52 -0
  115. package/packs/software-delivery/tool-impl/worktree-tools.ts +46 -0
  116. package/packs/software-delivery/tool-impl/wu-lifecycle-tools.ts +759 -0
  117. package/packs/software-delivery/tools/delegation-tools.ts +23 -0
  118. package/packs/software-delivery/tools/git-tools.ts +55 -0
  119. package/packs/software-delivery/tools/index.ts +8 -0
  120. package/packs/software-delivery/tools/lane-lock-tool.ts +37 -0
  121. package/packs/software-delivery/tools/types.ts +71 -0
  122. package/packs/software-delivery/tools/worktree-tools.ts +49 -0
  123. package/templates/core/LUMENFLOW.md.template +3 -3
  124. package/templates/core/ai/onboarding/agent-invocation-guide.md.template +1 -1
  125. package/templates/core/ai/onboarding/lumenflow-force-usage.md.template +1 -1
  126. package/templates/core/ai/onboarding/quick-ref-commands.md.template +5 -5
  127. package/templates/core/ai/onboarding/starting-prompt.md.template +3 -3
  128. package/templates/core/ai/onboarding/vendor-support.md.template +1 -1
  129. package/templates/core/ai/onboarding/wu-create-checklist.md.template +1 -1
  130. package/dist/agent-issues-query.js.map +0 -1
  131. package/dist/agent-log-issue.js.map +0 -1
  132. package/dist/agent-session-end.js.map +0 -1
  133. package/dist/agent-session.js.map +0 -1
  134. package/dist/backlog-prune.js.map +0 -1
  135. package/dist/cli-entry-point.js +0 -149
  136. package/dist/cli-entry-point.js.map +0 -1
  137. package/dist/commands/integrate.js.map +0 -1
  138. package/dist/commands.js.map +0 -1
  139. package/dist/config-get.js.map +0 -1
  140. package/dist/config-set.js.map +0 -1
  141. package/dist/delegation-list.js.map +0 -1
  142. package/dist/deps-add.js +0 -259
  143. package/dist/deps-add.js.map +0 -1
  144. package/dist/deps-remove.js +0 -105
  145. package/dist/deps-remove.js.map +0 -1
  146. package/dist/docs-sync.js.map +0 -1
  147. package/dist/doctor.js.map +0 -1
  148. package/dist/file-delete.js.map +0 -1
  149. package/dist/file-edit.js.map +0 -1
  150. package/dist/file-read.js.map +0 -1
  151. package/dist/file-write.js.map +0 -1
  152. package/dist/flow-bottlenecks.js.map +0 -1
  153. package/dist/flow-report.js.map +0 -1
  154. package/dist/formatters.js +0 -151
  155. package/dist/formatters.js.map +0 -1
  156. package/dist/gate-defaults.js +0 -131
  157. package/dist/gate-defaults.js.map +0 -1
  158. package/dist/gate-registry.js +0 -73
  159. package/dist/gate-registry.js.map +0 -1
  160. package/dist/gates-graceful-degradation.js +0 -153
  161. package/dist/gates-graceful-degradation.js.map +0 -1
  162. package/dist/gates-plan-resolvers.js +0 -152
  163. package/dist/gates-plan-resolvers.js.map +0 -1
  164. package/dist/gates-runners.js +0 -509
  165. package/dist/gates-runners.js.map +0 -1
  166. package/dist/gates-types.js +0 -4
  167. package/dist/gates-types.js.map +0 -1
  168. package/dist/gates-utils.js +0 -323
  169. package/dist/gates-utils.js.map +0 -1
  170. package/dist/gates.js.map +0 -1
  171. package/dist/git-branch.js.map +0 -1
  172. package/dist/git-diff.js.map +0 -1
  173. package/dist/git-log.js.map +0 -1
  174. package/dist/git-status.js.map +0 -1
  175. package/dist/guard-locked.js +0 -172
  176. package/dist/guard-locked.js.map +0 -1
  177. package/dist/guard-main-branch.js +0 -217
  178. package/dist/guard-main-branch.js.map +0 -1
  179. package/dist/guard-worktree-commit.js +0 -163
  180. package/dist/guard-worktree-commit.js.map +0 -1
  181. package/dist/hooks/auto-checkpoint-utils.js +0 -54
  182. package/dist/hooks/auto-checkpoint-utils.js.map +0 -1
  183. package/dist/hooks/enforcement-checks.js +0 -399
  184. package/dist/hooks/enforcement-checks.js.map +0 -1
  185. package/dist/hooks/enforcement-generator.js +0 -139
  186. package/dist/hooks/enforcement-generator.js.map +0 -1
  187. package/dist/hooks/enforcement-sync.js +0 -385
  188. package/dist/hooks/enforcement-sync.js.map +0 -1
  189. package/dist/hooks/generators/auto-checkpoint.js +0 -125
  190. package/dist/hooks/generators/auto-checkpoint.js.map +0 -1
  191. package/dist/hooks/generators/enforce-worktree.js +0 -190
  192. package/dist/hooks/generators/enforce-worktree.js.map +0 -1
  193. package/dist/hooks/generators/index.js +0 -18
  194. package/dist/hooks/generators/index.js.map +0 -1
  195. package/dist/hooks/generators/pre-compact-checkpoint.js +0 -136
  196. package/dist/hooks/generators/pre-compact-checkpoint.js.map +0 -1
  197. package/dist/hooks/generators/require-wu.js +0 -117
  198. package/dist/hooks/generators/require-wu.js.map +0 -1
  199. package/dist/hooks/generators/session-start-recovery.js +0 -103
  200. package/dist/hooks/generators/session-start-recovery.js.map +0 -1
  201. package/dist/hooks/generators/signal-utils.js +0 -54
  202. package/dist/hooks/generators/signal-utils.js.map +0 -1
  203. package/dist/hooks/generators/warn-incomplete.js +0 -67
  204. package/dist/hooks/generators/warn-incomplete.js.map +0 -1
  205. package/dist/hooks/index.js +0 -10
  206. package/dist/hooks/index.js.map +0 -1
  207. package/dist/index.js.map +0 -1
  208. package/dist/init-detection.js +0 -230
  209. package/dist/init-detection.js.map +0 -1
  210. package/dist/init-lane-validation.js +0 -143
  211. package/dist/init-lane-validation.js.map +0 -1
  212. package/dist/init-scaffolding.js +0 -158
  213. package/dist/init-scaffolding.js.map +0 -1
  214. package/dist/init-templates.js +0 -1964
  215. package/dist/init-templates.js.map +0 -1
  216. package/dist/init.js.map +0 -1
  217. package/dist/initiative-add-wu.js.map +0 -1
  218. package/dist/initiative-bulk-assign-wus.js.map +0 -1
  219. package/dist/initiative-create.js.map +0 -1
  220. package/dist/initiative-edit.js.map +0 -1
  221. package/dist/initiative-list.js.map +0 -1
  222. package/dist/initiative-plan.js.map +0 -1
  223. package/dist/initiative-remove-wu.js.map +0 -1
  224. package/dist/initiative-status.js.map +0 -1
  225. package/dist/lane-edit.js.map +0 -1
  226. package/dist/lane-health.js.map +0 -1
  227. package/dist/lane-lifecycle-process.js +0 -366
  228. package/dist/lane-lifecycle-process.js.map +0 -1
  229. package/dist/lane-lock.js.map +0 -1
  230. package/dist/lane-setup.js.map +0 -1
  231. package/dist/lane-status.js.map +0 -1
  232. package/dist/lane-suggest.js.map +0 -1
  233. package/dist/lane-validate.js.map +0 -1
  234. package/dist/lifecycle-regression-harness.js +0 -181
  235. package/dist/lifecycle-regression-harness.js.map +0 -1
  236. package/dist/lumenflow-upgrade.js.map +0 -1
  237. package/dist/mem-checkpoint.js.map +0 -1
  238. package/dist/mem-cleanup.js.map +0 -1
  239. package/dist/mem-context.js.map +0 -1
  240. package/dist/mem-create.js.map +0 -1
  241. package/dist/mem-delete.js.map +0 -1
  242. package/dist/mem-export.js.map +0 -1
  243. package/dist/mem-inbox.js.map +0 -1
  244. package/dist/mem-index.js +0 -214
  245. package/dist/mem-index.js.map +0 -1
  246. package/dist/mem-init.js.map +0 -1
  247. package/dist/mem-profile.js +0 -210
  248. package/dist/mem-profile.js.map +0 -1
  249. package/dist/mem-promote.js +0 -257
  250. package/dist/mem-promote.js.map +0 -1
  251. package/dist/mem-ready.js.map +0 -1
  252. package/dist/mem-recover.js.map +0 -1
  253. package/dist/mem-signal.js.map +0 -1
  254. package/dist/mem-start.js.map +0 -1
  255. package/dist/mem-summarize.js.map +0 -1
  256. package/dist/mem-triage.js.map +0 -1
  257. package/dist/merge-block.js +0 -225
  258. package/dist/merge-block.js.map +0 -1
  259. package/dist/metrics-cli.js.map +0 -1
  260. package/dist/metrics-snapshot.js.map +0 -1
  261. package/dist/onboard.js.map +0 -1
  262. package/dist/onboarding-smoke-test.js +0 -418
  263. package/dist/onboarding-smoke-test.js.map +0 -1
  264. package/dist/orchestrate-init-status.js.map +0 -1
  265. package/dist/orchestrate-initiative.js.map +0 -1
  266. package/dist/orchestrate-monitor.js.map +0 -1
  267. package/dist/pack-author.js.map +0 -1
  268. package/dist/pack-hash.js.map +0 -1
  269. package/dist/pack-install.js.map +0 -1
  270. package/dist/pack-publish.js.map +0 -1
  271. package/dist/pack-scaffold.js.map +0 -1
  272. package/dist/pack-search.js.map +0 -1
  273. package/dist/pack-validate.js.map +0 -1
  274. package/dist/plan-create.js.map +0 -1
  275. package/dist/plan-edit.js.map +0 -1
  276. package/dist/plan-link.js.map +0 -1
  277. package/dist/plan-promote.js.map +0 -1
  278. package/dist/public-manifest.js +0 -920
  279. package/dist/public-manifest.js.map +0 -1
  280. package/dist/release.js.map +0 -1
  281. package/dist/rotate-progress.js +0 -253
  282. package/dist/rotate-progress.js.map +0 -1
  283. package/dist/session-coordinator.js +0 -303
  284. package/dist/session-coordinator.js.map +0 -1
  285. package/dist/shared-validators.js +0 -81
  286. package/dist/shared-validators.js.map +0 -1
  287. package/dist/signal-cleanup.js.map +0 -1
  288. package/dist/state-bootstrap.js.map +0 -1
  289. package/dist/state-cleanup.js.map +0 -1
  290. package/dist/state-doctor-fix.js +0 -226
  291. package/dist/state-doctor-fix.js.map +0 -1
  292. package/dist/state-doctor-stamps.js +0 -23
  293. package/dist/state-doctor-stamps.js.map +0 -1
  294. package/dist/state-doctor.js.map +0 -1
  295. package/dist/strict-progress.js +0 -255
  296. package/dist/strict-progress.js.map +0 -1
  297. package/dist/sync-templates.js.map +0 -1
  298. package/dist/task-claim.js.map +0 -1
  299. package/dist/trace-gen.js +0 -401
  300. package/dist/trace-gen.js.map +0 -1
  301. package/dist/validate-agent-skills.js +0 -223
  302. package/dist/validate-agent-skills.js.map +0 -1
  303. package/dist/validate-agent-sync.js +0 -151
  304. package/dist/validate-agent-sync.js.map +0 -1
  305. package/dist/validate-backlog-sync.js +0 -77
  306. package/dist/validate-backlog-sync.js.map +0 -1
  307. package/dist/validate-skills-spec.js +0 -211
  308. package/dist/validate-skills-spec.js.map +0 -1
  309. package/dist/validate.js.map +0 -1
  310. package/dist/validator-defaults.js +0 -107
  311. package/dist/validator-defaults.js.map +0 -1
  312. package/dist/validator-registry.js +0 -71
  313. package/dist/validator-registry.js.map +0 -1
  314. package/dist/workspace-init.js.map +0 -1
  315. package/dist/wu-block.js.map +0 -1
  316. package/dist/wu-brief.js.map +0 -1
  317. package/dist/wu-claim-branch.js +0 -123
  318. package/dist/wu-claim-branch.js.map +0 -1
  319. package/dist/wu-claim-cloud.js +0 -79
  320. package/dist/wu-claim-cloud.js.map +0 -1
  321. package/dist/wu-claim-mode.js +0 -82
  322. package/dist/wu-claim-mode.js.map +0 -1
  323. package/dist/wu-claim-output.js +0 -85
  324. package/dist/wu-claim-output.js.map +0 -1
  325. package/dist/wu-claim-repair-guidance.js +0 -12
  326. package/dist/wu-claim-repair-guidance.js.map +0 -1
  327. package/dist/wu-claim-resume-handler.js +0 -87
  328. package/dist/wu-claim-resume-handler.js.map +0 -1
  329. package/dist/wu-claim-state.js +0 -581
  330. package/dist/wu-claim-state.js.map +0 -1
  331. package/dist/wu-claim-validation.js +0 -457
  332. package/dist/wu-claim-validation.js.map +0 -1
  333. package/dist/wu-claim-worktree.js +0 -223
  334. package/dist/wu-claim-worktree.js.map +0 -1
  335. package/dist/wu-claim.js.map +0 -1
  336. package/dist/wu-cleanup-cloud.js +0 -78
  337. package/dist/wu-cleanup-cloud.js.map +0 -1
  338. package/dist/wu-cleanup.js.map +0 -1
  339. package/dist/wu-code-path-coverage.js +0 -83
  340. package/dist/wu-code-path-coverage.js.map +0 -1
  341. package/dist/wu-create-cloud.js +0 -30
  342. package/dist/wu-create-cloud.js.map +0 -1
  343. package/dist/wu-create-content.js +0 -264
  344. package/dist/wu-create-content.js.map +0 -1
  345. package/dist/wu-create-readiness.js +0 -59
  346. package/dist/wu-create-readiness.js.map +0 -1
  347. package/dist/wu-create-validation.js +0 -128
  348. package/dist/wu-create-validation.js.map +0 -1
  349. package/dist/wu-create.js.map +0 -1
  350. package/dist/wu-delegate.js.map +0 -1
  351. package/dist/wu-delete.js.map +0 -1
  352. package/dist/wu-deps.js.map +0 -1
  353. package/dist/wu-done-auto-cleanup.js +0 -203
  354. package/dist/wu-done-auto-cleanup.js.map +0 -1
  355. package/dist/wu-done-check.js +0 -38
  356. package/dist/wu-done-check.js.map +0 -1
  357. package/dist/wu-done-cloud.js +0 -48
  358. package/dist/wu-done-cloud.js.map +0 -1
  359. package/dist/wu-done-decay.js +0 -86
  360. package/dist/wu-done-decay.js.map +0 -1
  361. package/dist/wu-done.js.map +0 -1
  362. package/dist/wu-edit-operations.js +0 -399
  363. package/dist/wu-edit-operations.js.map +0 -1
  364. package/dist/wu-edit-validators.js +0 -282
  365. package/dist/wu-edit-validators.js.map +0 -1
  366. package/dist/wu-edit.js.map +0 -1
  367. package/dist/wu-infer-lane.js.map +0 -1
  368. package/dist/wu-preflight.js.map +0 -1
  369. package/dist/wu-prep.js.map +0 -1
  370. package/dist/wu-proto.js.map +0 -1
  371. package/dist/wu-prune.js.map +0 -1
  372. package/dist/wu-recover.js.map +0 -1
  373. package/dist/wu-release.js.map +0 -1
  374. package/dist/wu-repair.js.map +0 -1
  375. package/dist/wu-sandbox.js.map +0 -1
  376. package/dist/wu-spawn-completion.js +0 -33
  377. package/dist/wu-spawn-completion.js.map +0 -1
  378. package/dist/wu-spawn-prompt-builders.js +0 -1197
  379. package/dist/wu-spawn-prompt-builders.js.map +0 -1
  380. package/dist/wu-spawn-strategy-resolver.js +0 -322
  381. package/dist/wu-spawn-strategy-resolver.js.map +0 -1
  382. package/dist/wu-spawn.js +0 -59
  383. package/dist/wu-spawn.js.map +0 -1
  384. package/dist/wu-state-cloud.js +0 -41
  385. package/dist/wu-state-cloud.js.map +0 -1
  386. package/dist/wu-status.js.map +0 -1
  387. package/dist/wu-unblock.js.map +0 -1
  388. package/dist/wu-unlock-lane.js.map +0 -1
  389. package/dist/wu-validate.js.map +0 -1
package/dist/wu-done.js CHANGED
@@ -126,6 +126,63 @@ const MEMORY_CHECKPOINT_NOTES = {
126
126
  PRE_GATES: 'Pre-gates checkpoint for recovery if gates fail',
127
127
  };
128
128
  const MEMORY_SIGNAL_WINDOW_MS = 60 * 60 * 1000; // 1 hour for recent signals
129
+ export const CHECKPOINT_GATE_MODES = {
130
+ OFF: 'off',
131
+ WARN: 'warn',
132
+ BLOCK: 'block',
133
+ };
134
+ const CHECKPOINT_GATE_CONFIG = {
135
+ PATH: 'memory.enforcement.require_checkpoint_for_done',
136
+ COMMAND_PREFIX: 'pnpm mem:checkpoint --wu',
137
+ WARN_TAG: 'WU-1998',
138
+ };
139
+ function buildCheckpointGateBlockMessage(id) {
140
+ return (`${STRING_LITERALS.NEWLINE}${LOG_PREFIX.DONE} ${EMOJI.FAILURE} No checkpoints found for ${id} session.${STRING_LITERALS.NEWLINE}` +
141
+ `${LOG_PREFIX.DONE} ${CHECKPOINT_GATE_CONFIG.PATH} is set to '${CHECKPOINT_GATE_MODES.BLOCK}'.${STRING_LITERALS.NEWLINE}` +
142
+ `${LOG_PREFIX.DONE} Create a checkpoint before completing: ${CHECKPOINT_GATE_CONFIG.COMMAND_PREFIX} ${id}${STRING_LITERALS.NEWLINE}`);
143
+ }
144
+ function buildCheckpointGateWarnMessages(id) {
145
+ return [
146
+ `${STRING_LITERALS.NEWLINE}${LOG_PREFIX.DONE} ${EMOJI.INFO} ${CHECKPOINT_GATE_CONFIG.WARN_TAG}: No prior checkpoints recorded for ${id} in this session.`,
147
+ `${LOG_PREFIX.DONE} A pre-gates checkpoint will be created automatically by wu:done.`,
148
+ `${LOG_PREFIX.DONE} For earlier crash recovery, run '${CHECKPOINT_GATE_CONFIG.COMMAND_PREFIX} ${id}' after each acceptance criterion, before gates, or every 30 tool calls.${STRING_LITERALS.NEWLINE}`,
149
+ ];
150
+ }
151
+ export function resolveCheckpointGateMode(mode) {
152
+ if (mode === CHECKPOINT_GATE_MODES.OFF) {
153
+ return CHECKPOINT_GATE_MODES.OFF;
154
+ }
155
+ if (mode === CHECKPOINT_GATE_MODES.BLOCK) {
156
+ return CHECKPOINT_GATE_MODES.BLOCK;
157
+ }
158
+ return CHECKPOINT_GATE_MODES.WARN;
159
+ }
160
+ export async function enforceCheckpointGateForDone({ id, workspacePath, mode, queryByWuFn = queryByWu, hasSessionCheckpointsFn = hasSessionCheckpoints, log = console.log, blocker = (message) => {
161
+ die(message);
162
+ }, }) {
163
+ if (mode === CHECKPOINT_GATE_MODES.OFF) {
164
+ return;
165
+ }
166
+ let wuNodes;
167
+ try {
168
+ wuNodes = await queryByWuFn(workspacePath, id);
169
+ if (hasSessionCheckpointsFn(id, wuNodes)) {
170
+ return;
171
+ }
172
+ }
173
+ catch {
174
+ // Fail-open: checkpoint discovery issues should not block wu:done.
175
+ return;
176
+ }
177
+ if (mode === CHECKPOINT_GATE_MODES.BLOCK) {
178
+ blocker(buildCheckpointGateBlockMessage(id));
179
+ return;
180
+ }
181
+ const warnMessages = buildCheckpointGateWarnMessages(id);
182
+ for (const message of warnMessages) {
183
+ log(message);
184
+ }
185
+ }
129
186
  function normalizeWUDocLike(doc) {
130
187
  if (!doc || typeof doc !== 'object') {
131
188
  return {};
@@ -2078,7 +2135,7 @@ function printStateHUD({ id, docMain, isBranchOnly, isDocsOnly, derivedWorktree,
2078
2135
  console.log(`\n${LOG_PREFIX.DONE} HUD: WU=${id} status=${yamlStatus} stamp=${stampExists} locked=${yamlLocked} mode=${mode} branch=${branch} worktree=${worktreeDisplay}`);
2079
2136
  }
2080
2137
  // eslint-disable-next-line sonarjs/cognitive-complexity -- Pre-existing complexity, refactor tracked separately
2081
- async function main() {
2138
+ export async function main() {
2082
2139
  // Allow pre-push hook to recognize wu:done automation (WU-1030)
2083
2140
  process.env.LUMENFLOW_WU_TOOL = 'wu-done';
2084
2141
  // Validate CLI arguments and WU ID format (extracted to wu-done-validators.ts)
@@ -2198,39 +2255,14 @@ async function main() {
2198
2255
  });
2199
2256
  // Step 0: Run gates (WU-1215: extracted to executeGates function)
2200
2257
  const worktreePath = effectiveWorktreePath;
2201
- // WU-1471 AC3: Config-driven checkpoint gate (replaces WU-1943 hardcoded warn)
2202
- // Reads memory.enforcement.require_checkpoint_for_done from config:
2203
- // 'off' - skip checkpoint check entirely
2204
- // 'warn' - warn but allow wu:done (default, fail-open)
2205
- // 'block' - fail wu:done if no checkpoints found
2206
- try {
2207
- const checkpointGateConfig = getConfig();
2208
- const requireCheckpoint = checkpointGateConfig.memory?.enforcement?.require_checkpoint_for_done ?? 'warn';
2209
- if (requireCheckpoint !== 'off') {
2210
- const wuNodes = await queryByWu(worktreePath || mainCheckoutPath, id);
2211
- if (!hasSessionCheckpoints(id, wuNodes)) {
2212
- if (requireCheckpoint === 'block') {
2213
- die(`\n${LOG_PREFIX.DONE} ${EMOJI.FAILURE} No checkpoints found for ${id} session.\n` +
2214
- `${LOG_PREFIX.DONE} memory.enforcement.require_checkpoint_for_done is set to 'block'.\n` +
2215
- `${LOG_PREFIX.DONE} Create a checkpoint before completing: pnpm mem:checkpoint --wu ${id}\n`);
2216
- }
2217
- else {
2218
- // 'warn' mode (default)
2219
- console.log(`\n${LOG_PREFIX.DONE} ${EMOJI.WARNING} WU-1943: No checkpoints found for ${id} session.`);
2220
- console.log(`${LOG_PREFIX.DONE} Consider using 'pnpm mem:checkpoint --wu ${id}' periodically for crash recovery.`);
2221
- console.log(`${LOG_PREFIX.DONE} Checkpoint triggers: after each acceptance criterion, before gates, every 30 tool calls.\n`);
2222
- }
2223
- }
2224
- }
2225
- }
2226
- catch (checkpointErr) {
2227
- // Non-blocking in 'warn' mode: checkpoint check failure should not block wu:done
2228
- // In 'block' mode, die() already exited, so this only catches non-die errors
2229
- if (checkpointErr instanceof Error && checkpointErr.message?.includes('No checkpoints found')) {
2230
- throw checkpointErr; // Re-throw die() errors
2231
- }
2232
- // Otherwise silently allow - fail-open
2233
- }
2258
+ // WU-1471 AC3 + WU-1998: Config-driven checkpoint gate with accurate warn-mode messaging.
2259
+ const checkpointGateConfig = getConfig();
2260
+ const requireCheckpoint = resolveCheckpointGateMode(checkpointGateConfig.memory?.enforcement?.require_checkpoint_for_done);
2261
+ await enforceCheckpointGateForDone({
2262
+ id,
2263
+ workspacePath: worktreePath || mainCheckoutPath,
2264
+ mode: requireCheckpoint,
2265
+ });
2234
2266
  // WU-1663: Preparation complete - transition to gating state
2235
2267
  pipelineActor.send({ type: WU_DONE_EVENTS.PREPARATION_COMPLETE });
2236
2268
  // WU-1663: Wrap gates in try/catch to send pipeline failure event
package/dist/wu-edit.js CHANGED
@@ -247,7 +247,7 @@ async function ensureCleanWorkingTree() {
247
247
  * Main entry point
248
248
  */
249
249
  // eslint-disable-next-line sonarjs/cognitive-complexity -- Pre-existing complexity, refactor tracked separately
250
- async function main() {
250
+ export async function main() {
251
251
  const opts = parseArgs();
252
252
  const { id } = opts;
253
253
  console.log(`${PREFIX} Starting WU edit for ${id}`);
@@ -97,7 +97,7 @@ function loadWuYaml(id) {
97
97
  ` 2. Fix YAML errors manually and retry`);
98
98
  }
99
99
  }
100
- async function main() {
100
+ export async function main() {
101
101
  const args = parseArgs(process.argv);
102
102
  let codePaths = [];
103
103
  let description = '';
@@ -54,7 +54,7 @@ function detectWorktreePath(id) {
54
54
  /**
55
55
  * Main entry point
56
56
  */
57
- async function main() {
57
+ export async function main() {
58
58
  const PREFIX = LOG_PREFIX.PREFLIGHT;
59
59
  // WU-1180: Use createWUParser for proper Commander help output
60
60
  const args = createWUParser({
package/dist/wu-prep.js CHANGED
@@ -317,7 +317,7 @@ function printSuccessMessage(wuId, mainCheckout) {
317
317
  /**
318
318
  * Main entry point.
319
319
  */
320
- async function main() {
320
+ export async function main() {
321
321
  // Parse arguments
322
322
  const args = createWUParser({
323
323
  name: 'wu-prep',
package/dist/wu-proto.js CHANGED
@@ -220,7 +220,7 @@ function claimWU(wuId, lane) {
220
220
  die(`Failed to claim WU: ${error.message}`);
221
221
  }
222
222
  }
223
- async function main() {
223
+ export async function main() {
224
224
  const args = createWUParser({
225
225
  name: 'wu-proto',
226
226
  description: 'Create and claim a prototype WU with relaxed validation (rapid prototyping)',
package/dist/wu-prune.js CHANGED
@@ -123,7 +123,7 @@ async function validateWorktree(wt) {
123
123
  }
124
124
  return { valid: true, warnings, errors };
125
125
  }
126
- async function main() {
126
+ export async function main() {
127
127
  const args = parseArgs(process.argv);
128
128
  const PREFIX = LOG_PREFIX.PRUNE;
129
129
  if (args.help) {
@@ -473,7 +473,7 @@ export async function executeRecoveryAction(action, wuId) {
473
473
  /**
474
474
  * Main entry point
475
475
  */
476
- async function main() {
476
+ export async function main() {
477
477
  const args = createWUParser({
478
478
  name: 'wu-recover',
479
479
  description: 'Analyze and fix WU state inconsistencies (WU-1090)',
@@ -47,7 +47,7 @@ export function clearClaimMetadataOnRelease(doc) {
47
47
  export function shouldUseBranchPrReleasePath(doc) {
48
48
  return shouldUseBranchPrStatePath(doc);
49
49
  }
50
- async function main() {
50
+ export async function main() {
51
51
  const args = createWUParser({
52
52
  name: 'wu-release',
53
53
  description: 'Release an orphaned WU from in_progress back to ready state for reclaiming',
package/dist/wu-repair.js CHANGED
@@ -212,7 +212,7 @@ async function routeToRepairMode(options) {
212
212
  }
213
213
  return runConsistencyRepairMode(options);
214
214
  }
215
- async function main() {
215
+ export async function main() {
216
216
  const options = createProgram();
217
217
  validateOptions(options);
218
218
  validateModeRequirements(options);
@@ -11,14 +11,14 @@ import { existsSync, readFileSync } from 'node:fs';
11
11
  import path from 'node:path';
12
12
  import { spawn } from 'node:child_process';
13
13
  import { parse as parseYaml } from 'yaml';
14
- import { createWUParser, WU_OPTIONS, resolveLocation, readWURaw, buildSandboxProfile, resolveSandboxBackendForPlatform, SANDBOX_BACKEND_IDS, createLinuxSandboxBackend, createMacosSandboxBackend, createWindowsSandboxBackend, } from '@lumenflow/core';
14
+ import { createWUParser, WU_OPTIONS, resolveLocation, readWURaw, buildSandboxProfile, resolveSandboxBackendForPlatform, SANDBOX_BACKEND_IDS, createLinuxSandboxBackend, createMacosSandboxBackend, createWindowsSandboxBackend, WORKSPACE_CONFIG_FILE_NAME, WORKSPACE_V2_KEYS, } from '@lumenflow/core';
15
15
  import { WU_PATHS, defaultWorktreeFrom } from '@lumenflow/core/wu-paths';
16
16
  import { die } from '@lumenflow/core/error-handler';
17
17
  import { LOG_PREFIX, EXIT_CODES } from '@lumenflow/core/wu-constants';
18
18
  import { runCLI } from './cli-entry-point.js';
19
19
  const PREFIX = LOG_PREFIX.CLAIM.replace('wu-claim', 'wu:sandbox');
20
- const CONFIG_FILE = '.lumenflow.config.yaml';
21
20
  const DEFAULT_ALLOW_UNSANDBOXED_ENV_VAR = 'LUMENFLOW_SANDBOX_ALLOW_UNSANDBOXED';
21
+ const SOFTWARE_DELIVERY_KEY = WORKSPACE_V2_KEYS.SOFTWARE_DELIVERY;
22
22
  function toNormalizedAbsolute(targetPath) {
23
23
  const normalized = path.resolve(targetPath);
24
24
  return normalized.length > 1 && normalized.endsWith(path.sep)
@@ -36,41 +36,54 @@ function parsePolicyConfig(value) {
36
36
  }
37
37
  return value;
38
38
  }
39
- export function readSandboxPolicy(projectRoot) {
40
- const configPath = path.join(projectRoot, CONFIG_FILE);
39
+ function readWorkspaceSoftwareDelivery(projectRoot) {
40
+ const configPath = path.join(projectRoot, WORKSPACE_CONFIG_FILE_NAME);
41
41
  if (!existsSync(configPath)) {
42
- return {
43
- allowUnsandboxedEnvVar: DEFAULT_ALLOW_UNSANDBOXED_ENV_VAR,
44
- extraWritableRoots: [],
45
- denyWritableRoots: [],
46
- };
42
+ return null;
47
43
  }
48
44
  try {
49
- const raw = parseYaml(readFileSync(configPath, 'utf8'));
50
- const sandbox = parsePolicyConfig(raw?.sandbox);
51
- const allowUnsandboxedEnvVar = typeof sandbox.allow_unsandboxed_fallback_env === 'string' &&
52
- sandbox.allow_unsandboxed_fallback_env.trim() !== ''
53
- ? sandbox.allow_unsandboxed_fallback_env.trim()
54
- : DEFAULT_ALLOW_UNSANDBOXED_ENV_VAR;
55
- const extraWritableRoots = Array.isArray(sandbox.extra_writable_roots)
56
- ? sandbox.extra_writable_roots.filter((value) => typeof value === 'string')
57
- : [];
58
- const denyWritableRoots = Array.isArray(sandbox.deny_writable_roots)
59
- ? sandbox.deny_writable_roots.filter((value) => typeof value === 'string')
60
- : [];
61
- return {
62
- allowUnsandboxedEnvVar,
63
- extraWritableRoots,
64
- denyWritableRoots,
65
- };
45
+ const parsed = parseYaml(readFileSync(configPath, 'utf8'));
46
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
47
+ return null;
48
+ }
49
+ const workspace = parsed;
50
+ const softwareDelivery = workspace[SOFTWARE_DELIVERY_KEY];
51
+ if (!softwareDelivery ||
52
+ typeof softwareDelivery !== 'object' ||
53
+ Array.isArray(softwareDelivery)) {
54
+ return null;
55
+ }
56
+ return softwareDelivery;
66
57
  }
67
58
  catch {
59
+ return null;
60
+ }
61
+ }
62
+ export function readSandboxPolicy(projectRoot) {
63
+ const softwareDelivery = readWorkspaceSoftwareDelivery(projectRoot);
64
+ if (!softwareDelivery) {
68
65
  return {
69
66
  allowUnsandboxedEnvVar: DEFAULT_ALLOW_UNSANDBOXED_ENV_VAR,
70
67
  extraWritableRoots: [],
71
68
  denyWritableRoots: [],
72
69
  };
73
70
  }
71
+ const sandbox = parsePolicyConfig(softwareDelivery.sandbox);
72
+ const allowUnsandboxedEnvVar = typeof sandbox.allow_unsandboxed_fallback_env === 'string' &&
73
+ sandbox.allow_unsandboxed_fallback_env.trim() !== ''
74
+ ? sandbox.allow_unsandboxed_fallback_env.trim()
75
+ : DEFAULT_ALLOW_UNSANDBOXED_ENV_VAR;
76
+ const extraWritableRoots = Array.isArray(sandbox.extra_writable_roots)
77
+ ? sandbox.extra_writable_roots.filter((value) => typeof value === 'string')
78
+ : [];
79
+ const denyWritableRoots = Array.isArray(sandbox.deny_writable_roots)
80
+ ? sandbox.deny_writable_roots.filter((value) => typeof value === 'string')
81
+ : [];
82
+ return {
83
+ allowUnsandboxedEnvVar,
84
+ extraWritableRoots,
85
+ denyWritableRoots,
86
+ };
74
87
  }
75
88
  export function extractSandboxCommandFromArgv(argv) {
76
89
  const separator = argv.indexOf('--');
@@ -244,7 +257,7 @@ export async function runWuSandbox(input) {
244
257
  }
245
258
  return runCommand(commandToRun, commandArgs, worktreePath);
246
259
  }
247
- async function main() {
260
+ export async function main() {
248
261
  const options = parseWuSandboxOptions(process.argv);
249
262
  const exitCode = await runWuSandbox(options);
250
263
  process.exit(exitCode);
package/dist/wu-status.js CHANGED
@@ -152,7 +152,7 @@ export function getStatusExitCode(context) {
152
152
  /**
153
153
  * Main entry point
154
154
  */
155
- async function main() {
155
+ export async function main() {
156
156
  const args = createWUParser({
157
157
  name: 'wu-status',
158
158
  description: 'Show WU status, location, and valid commands (WU-1090)',
@@ -114,7 +114,7 @@ function handleWorktreeCreation(args, doc) {
114
114
  }
115
115
  createWorktree(doc, worktreePath, branchName);
116
116
  }
117
- async function main() {
117
+ export async function main() {
118
118
  const args = createWUParser({
119
119
  name: 'wu-unblock',
120
120
  description: 'Unblock a work unit and move it from blocked to in-progress status',
@@ -96,7 +96,7 @@ function showLaneStatus(lane) {
96
96
  console.log(` pnpm wu:unlock-lane --lane "${lane}" --reason "<explanation>" --force`);
97
97
  }
98
98
  }
99
- async function main() {
99
+ export async function main() {
100
100
  const args = createWUParser({
101
101
  name: 'wu-unlock-lane',
102
102
  description: 'Safely unlock a lane lock with audit logging',
@@ -139,7 +139,7 @@ function validateAllWUs({ strict = true } = {}) {
139
139
  /**
140
140
  * Main entry point
141
141
  */
142
- async function main() {
142
+ export async function main() {
143
143
  const args = createWUParser({
144
144
  name: 'wu-validate',
145
145
  description: 'Validate WU YAML files against schema (strict mode by default, WU-1329)',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumenflow/cli",
3
- "version": "3.1.1",
3
+ "version": "3.1.3",
4
4
  "description": "Command-line interface for LumenFlow workflow framework",
5
5
  "keywords": [
6
6
  "lumenflow",
@@ -142,11 +142,13 @@
142
142
  "workspace-init": "./dist/workspace-init.js",
143
143
  "config-set": "./dist/config-set.js",
144
144
  "config-get": "./dist/config-get.js",
145
+ "cloud-connect": "./dist/init.js",
145
146
  "lumenflow-onboard": "./dist/onboard.js",
146
147
  "onboard": "./dist/onboard.js"
147
148
  },
148
149
  "files": [
149
150
  "dist",
151
+ "packs",
150
152
  "templates",
151
153
  "LICENSE",
152
154
  "README.md"
@@ -176,12 +178,12 @@
176
178
  "xstate": "^5.28.0",
177
179
  "yaml": "^2.8.2",
178
180
  "zod": "^4.3.6",
179
- "@lumenflow/agent": "3.1.1",
180
- "@lumenflow/memory": "3.1.1",
181
- "@lumenflow/metrics": "3.1.1",
182
- "@lumenflow/kernel": "3.1.1",
183
- "@lumenflow/initiatives": "3.1.1",
184
- "@lumenflow/core": "3.1.1"
181
+ "@lumenflow/agent": "3.1.3",
182
+ "@lumenflow/core": "3.1.3",
183
+ "@lumenflow/initiatives": "3.1.3",
184
+ "@lumenflow/metrics": "3.1.3",
185
+ "@lumenflow/memory": "3.1.3",
186
+ "@lumenflow/kernel": "3.1.3"
185
187
  },
186
188
  "devDependencies": {
187
189
  "@vitest/coverage-v8": "^4.0.18",
@@ -197,8 +199,10 @@
197
199
  "access": "public"
198
200
  },
199
201
  "scripts": {
202
+ "sync:bundled-packs": "node scripts/sync-bundled-packs.mjs",
200
203
  "build": "tsc && node scripts/check-shebangs.mjs",
201
- "build:dist": "tsup && node scripts/fix-entry-points.mjs && node scripts/check-shebangs.mjs",
204
+ "build:dist:deps": "pnpm --filter @lumenflow/metrics build && pnpm --filter @lumenflow/kernel build && pnpm --filter @lumenflow/core build && pnpm --filter @lumenflow/memory build && pnpm --filter @lumenflow/agent build && pnpm --filter @lumenflow/initiatives build",
205
+ "build:dist": "pnpm run clean && pnpm run build:dist:deps && tsup && node scripts/fix-entry-points.mjs && node scripts/check-shebangs.mjs",
202
206
  "pack:dist": "pnpm pack",
203
207
  "clean": "rm -rf dist *.tgz",
204
208
  "test": "vitest run",
@@ -0,0 +1,10 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ export const SOFTWARE_DELIVERY_PACK_ID = 'software-delivery' as const;
5
+ export const SOFTWARE_DELIVERY_PACK_VERSION = '0.1.0' as const;
6
+ export const SOFTWARE_DELIVERY_DOMAIN = SOFTWARE_DELIVERY_PACK_ID;
7
+ export const SOFTWARE_DELIVERY_POLICY_ID_PREFIX = `${SOFTWARE_DELIVERY_PACK_ID}.gate` as const;
8
+ export const SOFTWARE_DELIVERY_MANIFEST_FILE_NAME = 'manifest.yaml' as const;
9
+ export const SHA256_ALGORITHM = 'sha256' as const;
10
+ export const UTF8_ENCODING = 'utf8' as const;
@@ -0,0 +1,140 @@
1
+ // Copyright (c) 2026 Hellmai Ltd
2
+ // SPDX-License-Identifier: AGPL-3.0-only
3
+
4
+ import { SOFTWARE_DELIVERY_DOMAIN } from './constants.js';
5
+
6
+ export const SOFTWARE_DELIVERY_EXTENSION_KEY = 'software_delivery';
7
+ export const SOFTWARE_DELIVERY_EXPOSURES = ['ui', 'api', 'backend-only', 'documentation'] as const;
8
+
9
+ interface Parser<T> {
10
+ parse(input: unknown): T;
11
+ }
12
+
13
+ export interface SoftwareDeliveryTests {
14
+ unit: string[];
15
+ e2e: string[];
16
+ manual: string[];
17
+ }
18
+
19
+ export interface SoftwareDeliveryTaskExtensions {
20
+ code_paths: string[];
21
+ tests: SoftwareDeliveryTests;
22
+ exposure: (typeof SOFTWARE_DELIVERY_EXPOSURES)[number];
23
+ worktree: string;
24
+ branch: string;
25
+ }
26
+
27
+ export interface SoftwareDeliveryTask {
28
+ domain: typeof SOFTWARE_DELIVERY_DOMAIN;
29
+ extensions: Record<string, unknown> & {
30
+ [SOFTWARE_DELIVERY_EXTENSION_KEY]: SoftwareDeliveryTaskExtensions;
31
+ };
32
+ }
33
+
34
+ function asRecord(input: unknown, label: string): Record<string, unknown> {
35
+ if (!input || typeof input !== 'object' || Array.isArray(input)) {
36
+ throw new Error(`${label} must be an object.`);
37
+ }
38
+ return input as Record<string, unknown>;
39
+ }
40
+
41
+ function assertOnlyKeys(
42
+ record: Record<string, unknown>,
43
+ allowedKeys: readonly string[],
44
+ label: string,
45
+ ): void {
46
+ const allowed = new Set(allowedKeys);
47
+ for (const key of Object.keys(record)) {
48
+ if (!allowed.has(key)) {
49
+ throw new Error(`${label} has unrecognized key "${key}".`);
50
+ }
51
+ }
52
+ }
53
+
54
+ function parseNonEmptyString(value: unknown, label: string): string {
55
+ if (typeof value !== 'string' || value.trim().length === 0) {
56
+ throw new Error(`${label} must be a non-empty string.`);
57
+ }
58
+ return value;
59
+ }
60
+
61
+ function parseStringArray(value: unknown, label: string): string[] {
62
+ if (!Array.isArray(value)) {
63
+ throw new Error(`${label} must be an array.`);
64
+ }
65
+ return value.map((entry, index) => parseNonEmptyString(entry, `${label}[${index}]`));
66
+ }
67
+
68
+ function parseTests(input: unknown): SoftwareDeliveryTests {
69
+ const tests = asRecord(input, 'tests');
70
+ assertOnlyKeys(tests, ['unit', 'e2e', 'manual'], 'tests');
71
+ return {
72
+ unit: parseStringArray(tests.unit ?? [], 'tests.unit'),
73
+ e2e: parseStringArray(tests.e2e ?? [], 'tests.e2e'),
74
+ manual: parseStringArray(tests.manual ?? [], 'tests.manual'),
75
+ };
76
+ }
77
+
78
+ const ExposureSet = new Set<string>(SOFTWARE_DELIVERY_EXPOSURES);
79
+
80
+ export const SoftwareDeliveryTaskExtensionsSchema: Parser<SoftwareDeliveryTaskExtensions> = {
81
+ parse(input: unknown): SoftwareDeliveryTaskExtensions {
82
+ const extension = asRecord(input, 'software delivery extension');
83
+ assertOnlyKeys(
84
+ extension,
85
+ ['code_paths', 'tests', 'exposure', 'worktree', 'branch'],
86
+ 'software delivery extension',
87
+ );
88
+
89
+ const exposure = parseNonEmptyString(extension.exposure, 'exposure');
90
+ if (!ExposureSet.has(exposure)) {
91
+ throw new Error(`exposure must be one of: ${SOFTWARE_DELIVERY_EXPOSURES.join(', ')}`);
92
+ }
93
+
94
+ return {
95
+ code_paths: parseStringArray(extension.code_paths, 'code_paths'),
96
+ tests: parseTests(extension.tests),
97
+ exposure: exposure as (typeof SOFTWARE_DELIVERY_EXPOSURES)[number],
98
+ worktree: parseNonEmptyString(extension.worktree, 'worktree'),
99
+ branch: parseNonEmptyString(extension.branch, 'branch'),
100
+ };
101
+ },
102
+ };
103
+
104
+ export const TaskExtensionsOpaqueRecordSchema: Parser<Record<string, unknown>> = {
105
+ parse(input: unknown): Record<string, unknown> {
106
+ return asRecord(input, 'extensions');
107
+ },
108
+ };
109
+
110
+ export function extractSoftwareDeliveryExtensions(
111
+ extensions: Record<string, unknown> | undefined,
112
+ ): SoftwareDeliveryTaskExtensions {
113
+ const parsedExtensions = TaskExtensionsOpaqueRecordSchema.parse(extensions ?? {});
114
+ return SoftwareDeliveryTaskExtensionsSchema.parse(
115
+ parsedExtensions[SOFTWARE_DELIVERY_EXTENSION_KEY],
116
+ );
117
+ }
118
+
119
+ export const SoftwareDeliveryTaskSchema: Parser<SoftwareDeliveryTask> = {
120
+ parse(input: unknown): SoftwareDeliveryTask {
121
+ const task = asRecord(input, 'task');
122
+
123
+ if (task.domain !== SOFTWARE_DELIVERY_DOMAIN) {
124
+ throw new Error(`task.domain must be "${SOFTWARE_DELIVERY_DOMAIN}".`);
125
+ }
126
+
127
+ const parsedExtensions = TaskExtensionsOpaqueRecordSchema.parse(task.extensions);
128
+ const parsedSoftwareDeliveryExtensions = SoftwareDeliveryTaskExtensionsSchema.parse(
129
+ parsedExtensions[SOFTWARE_DELIVERY_EXTENSION_KEY],
130
+ );
131
+
132
+ return {
133
+ domain: SOFTWARE_DELIVERY_DOMAIN,
134
+ extensions: {
135
+ ...parsedExtensions,
136
+ [SOFTWARE_DELIVERY_EXTENSION_KEY]: parsedSoftwareDeliveryExtensions,
137
+ },
138
+ };
139
+ },
140
+ };