akm-cli 0.9.16 → 0.9.17-alpha.10

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 (403) hide show
  1. package/CHANGELOG.md +2101 -0
  2. package/STABILITY.md +11 -10
  3. package/dist/akm +124 -193
  4. package/dist/akm-migrate +38 -19
  5. package/dist/assets/hints/cli-hints-full.md +6 -7
  6. package/dist/assets/improve-strategies/catchup.json +0 -3
  7. package/dist/assets/improve-strategies/consolidate.json +0 -1
  8. package/dist/assets/improve-strategies/default.json +1 -2
  9. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  10. package/dist/assets/improve-strategies/quick.json +1 -2
  11. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  12. package/dist/assets/improve-strategies/thorough.json +0 -3
  13. package/dist/assets/prompts/consolidate-pair.md +20 -0
  14. package/dist/assets/prompts/consolidate-system.md +4 -11
  15. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  17. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  18. package/dist/assets/templates/html/health.html +3 -5
  19. package/dist/cli/retired-commands.js +1 -1
  20. package/dist/cli/shared.js +6 -2
  21. package/dist/cli/unknown-flags.js +24 -1
  22. package/dist/cli.js +68 -10
  23. package/dist/commands/agent/agent-dispatch.js +1 -1
  24. package/dist/commands/command/command-execution.js +24 -62
  25. package/dist/commands/feedback-cli.js +0 -1
  26. package/dist/commands/health/accept-rate.js +6 -0
  27. package/dist/commands/health/archive-usage.js +92 -0
  28. package/dist/commands/health/checks.js +83 -74
  29. package/dist/commands/health/config-skew.js +38 -0
  30. package/dist/commands/health/data-dir-usage.js +25 -13
  31. package/dist/commands/health/egress.js +54 -0
  32. package/dist/commands/health/html-report.js +1 -42
  33. package/dist/commands/health/improve-metrics.js +136 -591
  34. package/dist/commands/health/md-report.js +1 -6
  35. package/dist/commands/health/plugin-staleness.js +53 -3
  36. package/dist/commands/health/renderers.js +12 -4
  37. package/dist/commands/health/report-view-model.js +14 -120
  38. package/dist/commands/health/types-improve.js +4 -19
  39. package/dist/commands/health/windows.js +64 -74
  40. package/dist/commands/health.js +145 -143
  41. package/dist/commands/improve/consolidate/chunking.js +26 -117
  42. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  43. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  44. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  45. package/dist/commands/improve/consolidate.js +589 -1127
  46. package/dist/commands/improve/content-hash.js +16 -24
  47. package/dist/commands/improve/distill/content-repair.js +18 -100
  48. package/dist/commands/improve/distill-guards.js +20 -81
  49. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  50. package/dist/commands/improve/distill.js +608 -1041
  51. package/dist/commands/improve/eligibility.js +126 -390
  52. package/dist/commands/improve/execution.js +8 -10
  53. package/dist/commands/improve/extract-prompt.js +1 -2
  54. package/dist/commands/improve/extract.js +487 -1046
  55. package/dist/commands/improve/feedback-valence.js +0 -25
  56. package/dist/commands/improve/improve-cli.js +75 -169
  57. package/dist/commands/improve/improve-result-file.js +10 -66
  58. package/dist/commands/improve/improve-strategies.js +52 -4
  59. package/dist/commands/improve/improve-usage-report.js +18 -64
  60. package/dist/commands/improve/improve.js +480 -1074
  61. package/dist/commands/improve/ledger.js +119 -0
  62. package/dist/commands/improve/locks.js +2 -8
  63. package/dist/commands/improve/loop-stages.js +415 -1073
  64. package/dist/commands/improve/memory/derived-ref.js +12 -77
  65. package/dist/commands/improve/memory/memory-belief.js +16 -118
  66. package/dist/commands/improve/memory/memory-improve.js +266 -14
  67. package/dist/commands/improve/outcome-loop.js +28 -156
  68. package/dist/commands/improve/planner.js +5 -15
  69. package/dist/commands/improve/preparation.js +779 -2319
  70. package/dist/commands/improve/proactive-maintenance.js +34 -101
  71. package/dist/commands/improve/reflect-noise.js +104 -280
  72. package/dist/commands/improve/reflect.js +642 -1353
  73. package/dist/commands/improve/retrieval-gate.js +127 -0
  74. package/dist/commands/improve/retrieval-scope.js +92 -0
  75. package/dist/commands/improve/salience.js +41 -240
  76. package/dist/commands/improve/session-asset.js +19 -100
  77. package/dist/commands/improve/stage.js +322 -0
  78. package/dist/commands/lint/base-linter.js +37 -15
  79. package/dist/commands/proposal/drain.js +261 -578
  80. package/dist/commands/proposal/proposal-cli.js +19 -20
  81. package/dist/commands/proposal/proposal-types.js +31 -24
  82. package/dist/commands/proposal/proposal.js +38 -8
  83. package/dist/commands/proposal/propose.js +134 -160
  84. package/dist/commands/proposal/repository.js +1097 -1394
  85. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  86. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  87. package/dist/commands/proposal/validators/proposals.js +22 -89
  88. package/dist/commands/read/curate.js +105 -462
  89. package/dist/commands/read/knowledge.js +3 -2
  90. package/dist/commands/read/search-cli.js +16 -33
  91. package/dist/commands/read/search.js +17 -23
  92. package/dist/commands/read/show.js +57 -108
  93. package/dist/commands/sources/bundle-cli.js +25 -2
  94. package/dist/commands/sources/bundle-config-ops.js +4 -0
  95. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  96. package/dist/commands/sources/info.js +127 -29
  97. package/dist/commands/sources/installed-stashes.js +197 -746
  98. package/dist/commands/sources/schema-repair.js +98 -129
  99. package/dist/commands/sources/source-add.js +62 -12
  100. package/dist/commands/sources/source-manage.js +9 -2
  101. package/dist/commands/sources/stash-cli.js +24 -4
  102. package/dist/commands/tasks/explain.js +10 -13
  103. package/dist/commands/tasks/tasks-cli.js +12 -13
  104. package/dist/commands/tasks/tasks.js +350 -936
  105. package/dist/commands/tasks/validate.js +26 -24
  106. package/dist/commands/workflow/plan.js +22 -29
  107. package/dist/commands/workflow-cli.js +4 -4
  108. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  109. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  110. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  111. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  112. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  113. package/dist/core/adapter/execution-source.js +17 -29
  114. package/dist/core/asset/asset-placement.js +4 -13
  115. package/dist/core/asset/frontmatter.js +106 -1
  116. package/dist/core/asset/resolve-ref.js +1 -1
  117. package/dist/core/bundle-id.js +42 -5
  118. package/dist/core/bundle-rename.js +285 -0
  119. package/dist/core/config/config-io.js +1 -2
  120. package/dist/core/config/config-schema.js +9 -34
  121. package/dist/core/config/config-walker.js +1 -1
  122. package/dist/core/config/config.js +184 -111
  123. package/dist/core/config/engine-semantics.js +0 -2
  124. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  125. package/dist/core/config/schema/embedding.js +20 -5
  126. package/dist/core/config/schema/engines.js +5 -0
  127. package/dist/core/config/schema/execution.js +1 -1
  128. package/dist/core/config/schema/experimental.js +1 -1
  129. package/dist/core/config/schema/improve-processes.js +54 -125
  130. package/dist/core/config/schema/improve.js +4 -42
  131. package/dist/core/config/schema/index-config.js +9 -48
  132. package/dist/core/config/schema/scheduler.js +12 -12
  133. package/dist/core/config/schema/search.js +6 -22
  134. package/dist/core/env-secret-ref.js +0 -1
  135. package/dist/core/errors.js +8 -9
  136. package/dist/core/file-change.js +13 -5
  137. package/dist/core/file-lock.js +76 -173
  138. package/dist/core/improve-result.js +35 -7
  139. package/dist/core/improve-types.js +0 -1
  140. package/dist/core/logs-db.js +2 -2
  141. package/dist/core/loopback.js +7 -12
  142. package/dist/core/non-task-input.js +20 -0
  143. package/dist/core/parse.js +13 -16
  144. package/dist/core/paths.js +0 -24
  145. package/dist/core/redaction.js +109 -2
  146. package/dist/core/run-lock.js +2 -5
  147. package/dist/core/spawn-env.js +1 -1
  148. package/dist/core/state/migrations.js +123 -61
  149. package/dist/core/state-db-scope.js +2 -4
  150. package/dist/core/state-db.js +126 -692
  151. package/dist/core/time.js +0 -20
  152. package/dist/core/type-presentation.js +1 -9
  153. package/dist/core/write-source.js +294 -1005
  154. package/dist/execution/input-contract.js +1 -1
  155. package/dist/execution/resolved-request.js +135 -689
  156. package/dist/execution/source.js +63 -257
  157. package/dist/execution/target-ref.js +1 -1
  158. package/dist/indexer/bundle-identity-guard.js +2 -2
  159. package/dist/indexer/db/llm-cache.js +2 -2
  160. package/dist/indexer/ensure-index.js +77 -73
  161. package/dist/indexer/index-rebuild-lock.js +3 -11
  162. package/dist/indexer/index-writer-lock.js +8 -17
  163. package/dist/indexer/index-written-assets.js +141 -154
  164. package/dist/indexer/indexer.js +400 -1124
  165. package/dist/indexer/links/declared-links.js +90 -0
  166. package/dist/indexer/materialize-embeddings.js +60 -397
  167. package/dist/indexer/passes/memory-inference.js +96 -90
  168. package/dist/indexer/passes/metadata.js +132 -219
  169. package/dist/indexer/read-preflight.js +0 -7
  170. package/dist/indexer/scan/doc-to-entry.js +2 -3
  171. package/dist/indexer/scan/drain-dir.js +1 -1
  172. package/dist/indexer/search/db-search.js +190 -590
  173. package/dist/indexer/search/fts-query.js +30 -41
  174. package/dist/indexer/search/ranking.js +28 -154
  175. package/dist/indexer/search/search-attribution.js +12 -32
  176. package/dist/indexer/search/search-fields.js +11 -15
  177. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  178. package/dist/indexer/search/search-source.js +1 -4
  179. package/dist/indexer/usage/usage-events.js +36 -7
  180. package/dist/indexer/walk/walker.js +3 -4
  181. package/dist/integrations/agent/engine-fallback.js +23 -40
  182. package/dist/integrations/agent/engine-resolution.js +93 -183
  183. package/dist/integrations/agent/execution.js +507 -0
  184. package/dist/integrations/agent/model-map.js +28 -156
  185. package/dist/integrations/agent/request-lowering.js +66 -141
  186. package/dist/integrations/agent/runner-dispatch.js +143 -321
  187. package/dist/integrations/agent/runner.js +54 -14
  188. package/dist/integrations/lockfile.js +53 -101
  189. package/dist/llm/client.js +18 -6
  190. package/dist/llm/embedders/deterministic.js +2 -3
  191. package/dist/llm/embedders/profile.js +71 -0
  192. package/dist/llm/embedders/remote.js +11 -17
  193. package/dist/llm/feature-gate.js +0 -8
  194. package/dist/llm/index-passes.js +3 -5
  195. package/dist/llm/memory-infer.js +1 -2
  196. package/dist/llm/structured-call.js +5 -24
  197. package/dist/output/generic-render.js +23 -11
  198. package/dist/output/html-render.js +13 -10
  199. package/dist/output/render-registry.js +3 -32
  200. package/dist/output/shapes/helpers.js +25 -38
  201. package/dist/output/shapes/passthrough.js +1 -9
  202. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  203. package/dist/output/text/command-format.js +69 -31
  204. package/dist/output/text/helpers.js +1 -1
  205. package/dist/output/text/migrate.js +5 -14
  206. package/dist/output/text/proposal-format.js +48 -3
  207. package/dist/output/text/show-format.js +13 -17
  208. package/dist/output/text/workflow-format.js +0 -32
  209. package/dist/output/text.js +2 -0
  210. package/dist/registry/factory.js +4 -19
  211. package/dist/registry/network.js +66 -220
  212. package/dist/registry/providers/index.js +0 -2
  213. package/dist/registry/providers/skills-sh.js +3 -14
  214. package/dist/registry/providers/static-index.js +24 -26
  215. package/dist/registry/resolve.js +55 -131
  216. package/dist/scripts/akm-migrate-node.js +42948 -92369
  217. package/dist/scripts/akm-migrate.js +42935 -92354
  218. package/dist/setup/registry-stash-loader.js +4 -13
  219. package/dist/setup/semantic-assets.js +3 -44
  220. package/dist/setup/setup.js +1 -1
  221. package/dist/setup/steps/connection.js +5 -6
  222. package/dist/setup/steps/platforms.js +2 -2
  223. package/dist/setup/steps/tasks.js +25 -15
  224. package/dist/sources/provider-factory.js +17 -18
  225. package/dist/sources/providers/filesystem.js +2 -3
  226. package/dist/sources/providers/git-install.js +7 -1
  227. package/dist/sources/providers/git-provider.js +0 -3
  228. package/dist/sources/providers/git-stash.js +83 -21
  229. package/dist/sources/providers/npm.js +2 -4
  230. package/dist/sources/providers/provider-utils.js +5 -10
  231. package/dist/sources/providers/website.js +0 -2
  232. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  233. package/dist/sources/website-url.js +2 -2
  234. package/dist/storage/database.js +9 -35
  235. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  236. package/dist/storage/repositories/index-connection.js +39 -72
  237. package/dist/storage/repositories/index-entries-repository.js +131 -129
  238. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  239. package/dist/storage/repositories/index-entry-schema.js +101 -268
  240. package/dist/storage/repositories/index-fts-repository.js +86 -256
  241. package/dist/storage/repositories/index-links-repository.js +143 -0
  242. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  243. package/dist/storage/repositories/index-meta-repository.js +6 -4
  244. package/dist/storage/repositories/index-schema.js +257 -325
  245. package/dist/storage/repositories/index-utility-repository.js +8 -29
  246. package/dist/storage/repositories/index-vec-repository.js +133 -414
  247. package/dist/storage/repositories/outcome-repository.js +2 -1
  248. package/dist/storage/repositories/proposals-repository.js +104 -1
  249. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  250. package/dist/storage/repositories/salience-repository.js +1 -19
  251. package/dist/storage/repositories/task-history-repository.js +26 -4
  252. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  253. package/dist/storage/sqlite-migrations.js +136 -0
  254. package/dist/storage/sqlite-pragmas.js +11 -9
  255. package/dist/storage/sqlite-transaction.js +170 -0
  256. package/dist/storage/state-db-integrity.js +130 -0
  257. package/dist/tasks/activation-config.js +134 -62
  258. package/dist/tasks/backends/cron.js +191 -302
  259. package/dist/tasks/backends/exec-utils.js +2 -5
  260. package/dist/tasks/backends/launchd.js +141 -748
  261. package/dist/tasks/backends/schtasks.js +119 -623
  262. package/dist/tasks/prepare/prepare-support.js +5 -15
  263. package/dist/tasks/prepare/prepare.js +0 -2
  264. package/dist/tasks/resolve-akm-bin.js +20 -79
  265. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  266. package/dist/tasks/run/load-task.js +1 -1
  267. package/dist/tasks/scheduler-binding.js +20 -238
  268. package/dist/tasks/scheduler-invocation.js +136 -244
  269. package/dist/tasks/scheduler-lock.js +53 -0
  270. package/dist/tasks/scheduler-sync.js +368 -679
  271. package/dist/tasks/source/parse-task-source.js +55 -9
  272. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  273. package/dist/tasks/source/task-to-v4.js +464 -88
  274. package/dist/workflows/authoring/authoring.js +3 -12
  275. package/dist/workflows/compile.js +211 -0
  276. package/dist/workflows/concurrency-policy.js +13 -74
  277. package/dist/workflows/exec/child-invocation.js +3 -17
  278. package/dist/workflows/exec/child-workflow.js +32 -141
  279. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  280. package/dist/workflows/exec/environment.js +98 -0
  281. package/dist/workflows/exec/exec-unit.js +33 -140
  282. package/dist/workflows/exec/frozen-judge.js +7 -59
  283. package/dist/workflows/exec/native-executor.js +82 -341
  284. package/dist/workflows/exec/param-secrets.js +29 -47
  285. package/dist/workflows/exec/run-workflow.js +154 -387
  286. package/dist/workflows/exec/scheduler.js +9 -36
  287. package/dist/workflows/exec/step-work.js +127 -430
  288. package/dist/workflows/exec/unit-dispatch.js +11 -63
  289. package/dist/workflows/exec/unit-writer.js +8 -52
  290. package/dist/workflows/exec/worktree.js +39 -273
  291. package/dist/workflows/freeze/child-output-references.js +4 -15
  292. package/dist/workflows/freeze/environment.js +99 -92
  293. package/dist/workflows/freeze/freeze.js +172 -0
  294. package/dist/workflows/freeze/step-values.js +19 -21
  295. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  296. package/dist/workflows/freeze/targets/command.js +10 -33
  297. package/dist/workflows/freeze/targets/script.js +5 -12
  298. package/dist/workflows/freeze/targets/shell.js +3 -6
  299. package/dist/workflows/freeze/targets/task.js +25 -80
  300. package/dist/workflows/freeze/task-bindings.js +20 -67
  301. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  302. package/dist/workflows/ir/params.js +6 -51
  303. package/dist/workflows/ir/plan-hash.js +2 -34
  304. package/dist/workflows/parser.js +140 -43
  305. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  306. package/dist/workflows/renderer.js +36 -69
  307. package/dist/workflows/resource-limits.js +12 -120
  308. package/dist/workflows/runtime/agent-identity.js +8 -40
  309. package/dist/workflows/runtime/run-outputs.js +3 -6
  310. package/dist/workflows/runtime/run-plan.js +316 -0
  311. package/dist/workflows/runtime/runs.js +48 -200
  312. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  313. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  314. package/dist/workflows/validate-summary.js +2 -7
  315. package/docs/integration/bundling-akm.md +49 -42
  316. package/docs/migration/README.md +1 -0
  317. package/docs/migration/release-notes/0.9.17.md +43 -0
  318. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  319. package/docs/reference/cli.md +232 -135
  320. package/docs/reference/configuration.md +71 -57
  321. package/docs/reference/data-and-telemetry.md +20 -21
  322. package/docs/reference/tasks.md +105 -39
  323. package/docs/reference/workflow-schema.md +14 -18
  324. package/docs/reference/workflows.md +6 -9
  325. package/package.json +1 -1
  326. package/schemas/akm-config.json +115 -738
  327. package/schemas/akm-workflow.json +1 -0
  328. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  329. package/dist/assets/prompts/contradiction-judge.md +0 -33
  330. package/dist/assets/prompts/graph-extract-system.md +0 -1
  331. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  332. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  333. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  334. package/dist/commands/health/advisories.js +0 -150
  335. package/dist/commands/health/metrics.js +0 -329
  336. package/dist/commands/health/surfaces.js +0 -102
  337. package/dist/commands/improve/anti-collapse.js +0 -83
  338. package/dist/commands/improve/collapse-detector.js +0 -432
  339. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  340. package/dist/commands/improve/consolidate/merge.js +0 -149
  341. package/dist/commands/improve/distill/promote-memory.js +0 -291
  342. package/dist/commands/improve/distill/quality-gate.js +0 -337
  343. package/dist/commands/improve/eval-cases.js +0 -52
  344. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  345. package/dist/commands/improve/proposal-envelope.js +0 -31
  346. package/dist/commands/improve/run-context.js +0 -123
  347. package/dist/commands/improve/shared.js +0 -31
  348. package/dist/commands/improve/source-identity.js +0 -28
  349. package/dist/commands/improve/triage.js +0 -96
  350. package/dist/commands/proposal/drain-policies.js +0 -151
  351. package/dist/commands/sources/update-transaction.js +0 -220
  352. package/dist/core/action-contributors.js +0 -28
  353. package/dist/core/config/config-version-shim.js +0 -101
  354. package/dist/core/fs-txn.js +0 -405
  355. package/dist/core/lexical-score.js +0 -25
  356. package/dist/core/maintenance-barrier.js +0 -167
  357. package/dist/execution/executable-identity.js +0 -105
  358. package/dist/execution/guarded-source.js +0 -427
  359. package/dist/indexer/db/graph-db.js +0 -444
  360. package/dist/indexer/graph/graph-boost.js +0 -427
  361. package/dist/indexer/graph/graph-dedup.js +0 -95
  362. package/dist/indexer/graph/graph-extraction.js +0 -1108
  363. package/dist/indexer/search/name-match.js +0 -35
  364. package/dist/indexer/search/ranking-contributors.js +0 -515
  365. package/dist/indexer/search/ranking-types.js +0 -4
  366. package/dist/indexer/walk/project-context.js +0 -192
  367. package/dist/integrations/agent/execution-cascade.js +0 -566
  368. package/dist/integrations/agent/execution-definitions.js +0 -202
  369. package/dist/integrations/agent/execution-lowering.js +0 -841
  370. package/dist/integrations/agent/execution-preparation.js +0 -98
  371. package/dist/integrations/agent/inline-execution.js +0 -74
  372. package/dist/llm/graph-extract.js +0 -728
  373. package/dist/llm/metadata-enhance.js +0 -96
  374. package/dist/registry/create-provider-registry.js +0 -29
  375. package/dist/registry/pinned-request-helper.js +0 -247
  376. package/dist/registry/pinned-transport.js +0 -717
  377. package/dist/sources/providers/index.js +0 -14
  378. package/dist/storage/engines/sqlite-migrations.js +0 -271
  379. package/dist/storage/repositories/canaries-repository.js +0 -107
  380. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  381. package/dist/storage/repositories/registry-cache.js +0 -113
  382. package/dist/tasks/scheduler-sync-preview.js +0 -52
  383. package/dist/tasks/source/task-to-v3.js +0 -507
  384. package/dist/workflows/freeze/resolve-steps.js +0 -86
  385. package/dist/workflows/freeze/source-freeze.js +0 -64
  386. package/dist/workflows/ir/compile.js +0 -321
  387. package/dist/workflows/ir/environment-v4.js +0 -330
  388. package/dist/workflows/ir/freeze-v4.js +0 -153
  389. package/dist/workflows/ir/schema-v4.js +0 -745
  390. package/dist/workflows/ir/schema.js +0 -354
  391. package/dist/workflows/program/schema.js +0 -77
  392. package/dist/workflows/runtime/checkin.js +0 -57
  393. package/dist/workflows/runtime/plan-classifier.js +0 -196
  394. package/dist/workflows/runtime/unit-checkin.js +0 -45
  395. package/dist/workflows/runtime/unit-phases.js +0 -20
  396. package/dist/workflows/schema.js +0 -4
  397. package/dist/workflows/source-ir/compile.js +0 -200
  398. package/dist/workflows/source-ir/program.js +0 -50
  399. package/dist/workflows/source-ir/result.js +0 -26
  400. package/dist/workflows/source-ir/schema.js +0 -786
  401. package/dist/workflows/source-ir/triggers.js +0 -79
  402. package/dist/workflows/source-ir/uses.js +0 -40
  403. package/dist/workflows/validator.js +0 -60
@@ -2,15 +2,28 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
- * Pure, byte-producing task-v3 to task-source-v4 migration planner (spec
6
- * docs/plans/specs/p2b-input-bindings.md §1.3, §1.7 C-N1, §5). Mirrors
7
- * `task-to-v3.ts`'s fail-closed ladder exactly: the INPUT side is read as a
8
- * raw record by a vendored bounded-YAML reader (never the typed
9
- * `parseTaskV3Yaml`, which would normalize away exactly the value bytes this
10
- * migrator must preserve — a duration string like "5m", or a bare numeric
11
- * `timeout`, would be converted to milliseconds by the real parser). The
12
- * OUTPUT side is validated through the REAL `parseTaskSourceV4` before a
13
- * "changed" outcome is ever handed back (C-N1, B-71).
5
+ * Pure, byte-producing task-source migration planner: a v2, v3, or v4 task
6
+ * file straight to task source v4 (spec docs/plans/specs/p2b-input-bindings.md
7
+ * §1.3, §1.7 C-N1, §5). One planner, one outcome per file — the former
8
+ * two-generation chain (legacy task to v3, then v3 to v4, composed by
9
+ * `scripts/akm-migrate/migrate/task-files.ts`) is gone: a v2 file is read
10
+ * once and converted directly, with no intermediate v3 file ever written to
11
+ * disk or reported as its own outcome.
12
+ *
13
+ * A v3 document — real, or the v3-shape record a v2 file converts to in
14
+ * memory — is read as a raw record by a vendored bounded-YAML reader, never
15
+ * the typed `parseTaskV3Yaml`, which would normalize away exactly the value
16
+ * bytes this migrator must preserve (a duration string like "5m", or a bare
17
+ * numeric `timeout`, would be converted to milliseconds by the real parser).
18
+ * The one exception is a pre-validation gate: every v3-versioned document —
19
+ * real, or freshly built from v2 — is first checked against the REAL typed
20
+ * `parseTaskV3Yaml`, exactly as the prior two-generation chain did at each of
21
+ * its hops, so every blocked reason only the typed parser catches (an
22
+ * escaping `working-directory` symlink, a GitHub-expression schedule, an
23
+ * invalid builtin-command `with:` shape, and so on) still blocks here, with
24
+ * the same reason. The OUTPUT side is validated through the REAL
25
+ * `parseTaskSourceV4` before a "changed" outcome is ever handed back (C-N1,
26
+ * B-71).
14
27
  *
15
28
  * `inputs:` is never invented — the migrator translates structure, not
16
29
  * intent (spec §5.3).
@@ -18,9 +31,14 @@
18
31
  import crypto from "node:crypto";
19
32
  import path from "node:path";
20
33
  import { isMap, isSeq, LineCounter, parseDocument, stringify as stringifyYaml } from "yaml";
21
- import { assertBoundedTaskYamlDocument } from "./bounded-document.js";
22
- import { classifyTaskV3Uses } from "./task-source-v3-frozen.js";
34
+ import { bundleRefToString, parseBundleRef } from "../../core/asset/asset-ref.js";
35
+ import { formatExtraParamsIssue, validateExtraParams } from "../../core/extra-params.js";
36
+ import { WORKFLOW_ENV_VAR_NAME_PATTERN, WORKFLOW_MAX_TIMEOUT_MS } from "../../workflows/resource-limits.js";
37
+ import { validateTaskId } from "../task-id.js";
38
+ import { assertBoundedTaskYamlDocument, TASK_V3_MAX_REDACT_NAMES } from "./bounded-document.js";
39
+ import { classifyTaskV3Uses, parseTaskV3Yaml } from "./task-source-v3-frozen.js";
23
40
  import { parseTaskSourceV4 } from "./task-source-v4.js";
41
+ // ── v3 grammar (real v3 input, and the v3-shape record a v2 file builds) ────
24
42
  /** The closed v3 top-level key set (`src/tasks/source-v3.ts`'s own, vendored — not exported there). */
25
43
  const V3_TOP_LEVEL_KEYS = new Set([
26
44
  "version",
@@ -69,6 +87,100 @@ const AKM_HOIST_KEYS = [
69
87
  "maxSteps",
70
88
  "maxRetries",
71
89
  ];
90
+ // ── v2 grammar ────────────────────────────────────────────────────────────────
91
+ const V2_KEYS = new Set([
92
+ "version",
93
+ "name",
94
+ "description",
95
+ "when_to_use",
96
+ "tags",
97
+ "schedule",
98
+ "enabled",
99
+ "workflow",
100
+ "prompt",
101
+ "command",
102
+ "params",
103
+ "engine",
104
+ "model",
105
+ "timeoutMs",
106
+ "maxSteps",
107
+ "maxRetries",
108
+ "llm",
109
+ "redact",
110
+ ]);
111
+ const V2_SHARED_KEYS = new Set([
112
+ "version",
113
+ "name",
114
+ "description",
115
+ "when_to_use",
116
+ "tags",
117
+ "schedule",
118
+ "enabled",
119
+ "redact",
120
+ ]);
121
+ const V2_LLM_KEYS = new Set([
122
+ "temperature",
123
+ "maxTokens",
124
+ "supportsJsonSchema",
125
+ "extraParams",
126
+ "contextLength",
127
+ "enableThinking",
128
+ "reasoningEffort",
129
+ ]);
130
+ const SAFE_V2_COMMAND_TOKEN = /^[A-Za-z0-9_./:=+,-]+$/;
131
+ const SHELL_ASSIGNMENT_WORD = /^[A-Za-z_][A-Za-z0-9_]*=/;
132
+ /**
133
+ * V2 executed argv directly, while v3 `run:` enters a host shell. An explicit
134
+ * path bypasses shell aliases/builtins; `akm` is the one bare executable whose
135
+ * v3 runtime resolution is contractually pinned to the current installation.
136
+ */
137
+ function shellStableV2Executable(executable) {
138
+ return executable === "akm" || executable.includes("/");
139
+ }
140
+ /**
141
+ * `env NAME=value... cmd args...` is env(1) itself resolving and exec'ing
142
+ * `cmd` via its own PATH search — that lookup happens inside env's execvp()
143
+ * regardless of whether env was launched by direct execve (v2) or by a host
144
+ * shell (v3 `run:`). The shell-vs-argv divergence `shellStableV2Executable`
145
+ * guards against (bare names shadowed by shell aliases/builtins/functions)
146
+ * therefore does not apply to whatever env ultimately invokes, so skip past
147
+ * a leading `env` and its `NAME=value` assignments to find the real target.
148
+ * Returns the original tokens, unchanged, when there is no such target
149
+ * (e.g. `env` with nothing after its assignments).
150
+ */
151
+ function skipEnvAssignmentPrefix(tokens) {
152
+ if (tokens[0] !== "env")
153
+ return { tokens, envWrapped: false };
154
+ let index = 1;
155
+ while (index < tokens.length && SHELL_ASSIGNMENT_WORD.test(tokens[index]))
156
+ index += 1;
157
+ if (index >= tokens.length)
158
+ return { tokens, envWrapped: false };
159
+ return { tokens: tokens.slice(index), envWrapped: true };
160
+ }
161
+ const KNOWN_PROMPT_REF_FAMILIES = new Set([
162
+ "agents",
163
+ "commands",
164
+ "env",
165
+ "facts",
166
+ "instructions",
167
+ "knowledge",
168
+ "lessons",
169
+ "memories",
170
+ "scripts",
171
+ "secrets",
172
+ "sessions",
173
+ "skills",
174
+ "tasks",
175
+ "workflows",
176
+ ]);
177
+ /**
178
+ * #902: the one blocker with an unambiguous remedy. The sibling shell-safety
179
+ * reasons need a case-by-case judgement and stay reason-only.
180
+ */
181
+ const ARGV_ARRAY_BLOCK_DETAIL = "Manual conversion required: an array `command:` has no safe v3 `run:` string. Rewrite it by hand as " +
182
+ "`run:` (string) plus `shell:` — see docs/migration/v0.9.1-to-v0.9.2.md for the full v2 to v4 field mapping.";
183
+ // ── shared helpers ────────────────────────────────────────────────────────────
72
184
  function hash(bytes) {
73
185
  return crypto.createHash("sha256").update(bytes).digest("hex");
74
186
  }
@@ -105,15 +217,22 @@ function exactString(value, label, nonempty = false) {
105
217
  }
106
218
  return value;
107
219
  }
220
+ function optionalString(value, label) {
221
+ if (value === undefined || value === null)
222
+ return undefined;
223
+ return exactString(value, label);
224
+ }
108
225
  /**
109
- * Vendored raw-record reader (mirrors `task-to-v3.ts`'s `parseLegacyTaskYaml`
110
- * exactly). Reading the RAW decoded record — rather than the typed
111
- * `parseTaskV3Yaml` — keeps every field's original value bytes (a duration
112
- * string, a bare millisecond integer, an env value's exact type) intact for
113
- * verbatim re-emission; a typed v3 parse would normalize several of these
114
- * away (C-N1).
226
+ * Bounded-YAML raw-record reader shared by every version and every
227
+ * generation (v2 grammar, real v3, and the v3-shape record a v2 file
228
+ * builds): reading the RAW decoded record — rather than a typed parser —
229
+ * keeps every field's original value bytes (a duration string, a bare
230
+ * millisecond integer, an env value's exact type) intact for verbatim
231
+ * re-emission; a typed parse would normalize several of these away (C-N1).
232
+ * Which grammar applies to the result is entirely up to the caller, decided
233
+ * after `data.version` is known.
115
234
  */
116
- function parseV3RawYaml(input) {
235
+ function parseRawTaskYaml(input) {
117
236
  let source;
118
237
  try {
119
238
  source = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(input.bytes);
@@ -137,12 +256,12 @@ function parseV3RawYaml(input) {
137
256
  throw new Error(`unsupported YAML construct: ${parseWarning.message}`);
138
257
  assertBoundedTaskYamlDocument(document, {
139
258
  filePath: input.filePath,
140
- sourceLabel: "task v3 migration source",
259
+ sourceLabel: "task migration source",
141
260
  lineCounter,
142
261
  });
143
262
  return { data: plainRecord(document.toJS({ maxAliasCount: 0 }), "task YAML"), source };
144
263
  }
145
- /** Convert one already-validated v3 raw record to final task source v4 bytes. */
264
+ /** Convert one already-validated v3 raw record (real or v2-derived) to final task source v4 bytes. */
146
265
  function planV3DataToV4(input, data) {
147
266
  const unknownTop = Object.keys(data).filter((key) => !V3_TOP_LEVEL_KEYS.has(key));
148
267
  if (unknownTop.length > 0) {
@@ -318,8 +437,8 @@ function planV3DataToV4(input, data) {
318
437
  // `uses: workflows/` it was equally inert there — nothing ever consumed
319
438
  // it. Hoisting it unconditionally would therefore emit bytes the real
320
439
  // parseTaskSourceV4 below rejects, blocking a valid, previously-runnable
321
- // v3 file — and one blocked file aborts the whole plan
322
- // (`applyTaskToV4MigrationPlan`, ./task-files-to-v4.ts). Dropping an
440
+ // v3 file from `akm migrate apply`
441
+ // (scripts/akm-migrate/migrate/task-files.ts). Dropping an
323
442
  // already-inert field and SAYING SO is the faithful translation, and
324
443
  // keeps spec row B-66 / §5.3's `changed` guarantee intact.
325
444
  if (usesTarget !== undefined && (usesTarget.kind === "command" || usesTarget.kind === "builtin-command")) {
@@ -353,80 +472,349 @@ function planV3DataToV4(input, data) {
353
472
  ...(notice ? { notice } : {}),
354
473
  });
355
474
  }
356
- /** Plan exactly one source file without touching disk. */
357
- export function planTaskToV4File(input) {
358
- let data;
359
- let source;
475
+ // ── v2 → v3-shape record ───────────────────────────────────────────────────
476
+ function validateCommonV2(data) {
477
+ const unknown = Object.keys(data).filter((key) => !V2_KEYS.has(key));
478
+ if (unknown.length > 0)
479
+ throw new Error(`unknown v2 field(s): ${unknown.join(", ")}`);
480
+ exactString(data.schedule, "schedule", true);
481
+ if (data.enabled !== undefined && typeof data.enabled !== "boolean")
482
+ throw new Error("enabled must be a boolean");
483
+ for (const key of ["name", "description", "when_to_use"])
484
+ optionalString(data[key], key);
485
+ if (data.tags !== undefined && data.tags !== null) {
486
+ if (!Array.isArray(data.tags) || data.tags.some((entry) => typeof entry !== "string" || entry.length === 0)) {
487
+ throw new Error("tags must be an array of non-empty strings");
488
+ }
489
+ }
490
+ if (data.timeoutMs !== undefined && data.timeoutMs !== null) {
491
+ if (!Number.isInteger(data.timeoutMs) ||
492
+ data.timeoutMs < 1 ||
493
+ data.timeoutMs > WORKFLOW_MAX_TIMEOUT_MS) {
494
+ throw new Error(`timeoutMs must be null or an integer from 1 through ${WORKFLOW_MAX_TIMEOUT_MS}`);
495
+ }
496
+ }
497
+ if (data.redact !== undefined && data.redact !== null) {
498
+ if (!Array.isArray(data.redact) ||
499
+ data.redact.length > TASK_V3_MAX_REDACT_NAMES ||
500
+ data.redact.some((entry) => typeof entry !== "string" || !WORKFLOW_ENV_VAR_NAME_PATTERN.test(entry))) {
501
+ throw new Error("redact must contain only bounded environment variable names");
502
+ }
503
+ }
504
+ }
505
+ function validateTargetFields(data, allowed) {
506
+ const targetFields = new Set([...allowed, "workflow", "prompt", "command"]);
507
+ const invalid = Object.keys(data).filter((key) => !V2_SHARED_KEYS.has(key) && !targetFields.has(key));
508
+ if (invalid.length > 0)
509
+ throw new Error(`field(s) not valid for this target: ${invalid.join(", ")}`);
510
+ }
511
+ function validateV2Llm(value) {
512
+ if (value === undefined)
513
+ return undefined;
514
+ const llm = plainRecord(value, "llm");
515
+ const unknown = Object.keys(llm).filter((key) => !V2_LLM_KEYS.has(key));
516
+ if (unknown.length > 0)
517
+ throw new Error(`llm has unknown field(s): ${unknown.join(", ")}`);
518
+ if (llm.temperature !== undefined && (typeof llm.temperature !== "number" || !Number.isFinite(llm.temperature))) {
519
+ throw new Error("llm.temperature must be a finite number");
520
+ }
521
+ for (const key of ["maxTokens", "contextLength"]) {
522
+ if (llm[key] !== undefined && (!Number.isInteger(llm[key]) || llm[key] <= 0)) {
523
+ throw new Error(`llm.${key} must be a positive integer`);
524
+ }
525
+ }
526
+ for (const key of ["supportsJsonSchema", "enableThinking"]) {
527
+ if (llm[key] !== undefined && typeof llm[key] !== "boolean")
528
+ throw new Error(`llm.${key} must be a boolean`);
529
+ }
530
+ if (llm.reasoningEffort !== undefined && (typeof llm.reasoningEffort !== "string" || !llm.reasoningEffort.trim())) {
531
+ throw new Error("llm.reasoningEffort must be a non-empty string");
532
+ }
533
+ if (llm.extraParams !== undefined) {
534
+ const issue = validateExtraParams(llm.extraParams)[0];
535
+ if (issue)
536
+ throw new Error(formatExtraParamsIssue("llm.extraParams", issue));
537
+ }
538
+ return llm;
539
+ }
540
+ function commonAkm(data) {
541
+ const akm = {
542
+ schedule: exactString(data.schedule, "schedule", true),
543
+ enabled: data.enabled === undefined ? true : data.enabled,
544
+ };
545
+ for (const key of ["description", "when_to_use", "tags"]) {
546
+ if (data[key] !== undefined && data[key] !== null)
547
+ akm[key] = data[key];
548
+ }
549
+ return akm;
550
+ }
551
+ function addRuntimeOverrides(data, akm) {
552
+ for (const key of ["engine", "model"]) {
553
+ const value = optionalString(data[key], key);
554
+ if (value)
555
+ akm[key] = value;
556
+ }
557
+ const llm = validateV2Llm(data.llm);
558
+ if (llm !== undefined)
559
+ akm.inference = llm;
560
+ if (data.timeoutMs !== undefined)
561
+ akm.timeout = data.timeoutMs;
562
+ if (data.redact !== undefined && data.redact !== null) {
563
+ akm.redact = [...new Set(data.redact)];
564
+ }
565
+ }
566
+ function addSharedNonPromptOverrides(data, akm) {
567
+ if (data.timeoutMs !== undefined)
568
+ akm.timeout = data.timeoutMs;
569
+ if (data.redact !== undefined && data.redact !== null) {
570
+ akm.redact = [...new Set(data.redact)];
571
+ }
572
+ }
573
+ function promptSourceKind(raw) {
574
+ const trimmed = raw.trim();
575
+ if (trimmed.startsWith("./") ||
576
+ trimmed.startsWith("../") ||
577
+ path.isAbsolute(trimmed) ||
578
+ /^[A-Za-z]:[\\/]/.test(trimmed)) {
579
+ return "file";
580
+ }
360
581
  try {
361
- ({ data, source } = parseV3RawYaml(input));
582
+ const parsed = parseBundleRef(trimmed);
583
+ const family = parsed.conceptId.split("/", 1)[0] ?? "";
584
+ if (bundleRefToString(parsed) !== trimmed || !parsed.conceptId.includes("/")) {
585
+ return "inline";
586
+ }
587
+ if (!KNOWN_PROMPT_REF_FAMILIES.has(family))
588
+ return parsed.bundle === undefined ? "inline" : "other-ref";
589
+ if (family === "agents")
590
+ return "agent";
591
+ if (family === "commands")
592
+ return "command";
593
+ return "other-ref";
594
+ }
595
+ catch {
596
+ return "inline";
597
+ }
598
+ }
599
+ /**
600
+ * Convert one already-normalized legacy record to a v3-shape record, in
601
+ * memory — never written to disk, never reported as its own outcome. Returns
602
+ * a blocked reason string in place of the record when the v2 document has no
603
+ * safe v4 representation.
604
+ */
605
+ function migratedObject(data) {
606
+ validateCommonV2(data);
607
+ const targets = ["workflow", "prompt", "command"].filter((key) => Object.hasOwn(data, key) && data[key] !== null && data[key] !== "");
608
+ if (targets.length !== 1)
609
+ throw new Error("v2 task must declare exactly one of workflow, prompt, or command");
610
+ const output = { version: 3 };
611
+ if (data.name !== undefined && data.name !== null)
612
+ output.name = data.name;
613
+ const akm = commonAkm(data);
614
+ if (targets[0] === "workflow") {
615
+ validateTargetFields(data, ["params", "timeoutMs", "maxSteps", "maxRetries"]);
616
+ const ref = exactString(data.workflow, "workflow", true).trim();
617
+ let target;
618
+ try {
619
+ target = classifyTaskV3Uses(ref);
620
+ }
621
+ catch {
622
+ throw new Error("workflow is not a canonical v3 asset ref");
623
+ }
624
+ if (target.kind !== "workflow")
625
+ throw new Error("workflow target is not a workflows/ ref");
626
+ output.uses = ref;
627
+ if (data.params !== undefined && data.params !== null) {
628
+ const params = plainRecord(data.params, "params");
629
+ output.with = params;
630
+ }
631
+ if (data.maxSteps !== undefined && data.maxSteps !== null) {
632
+ if (!Number.isSafeInteger(data.maxSteps) || data.maxSteps < 1)
633
+ throw new Error("maxSteps must be positive");
634
+ akm.maxSteps = data.maxSteps;
635
+ }
636
+ if (data.maxRetries !== undefined && data.maxRetries !== null) {
637
+ if (!Number.isSafeInteger(data.maxRetries) || data.maxRetries < 0) {
638
+ throw new Error("maxRetries must be a non-negative integer");
639
+ }
640
+ akm.maxRetries = data.maxRetries;
641
+ }
642
+ addSharedNonPromptOverrides(data, akm);
643
+ }
644
+ else if (targets[0] === "prompt") {
645
+ validateTargetFields(data, ["engine", "model", "timeoutMs", "llm"]);
646
+ const prompt = exactString(data.prompt, "prompt", true).trim();
647
+ const kind = promptSourceKind(prompt);
648
+ if (kind === "file")
649
+ return "dynamic-file-read-cannot-be-inlined-without-changing-semantics";
650
+ if (kind === "agent")
651
+ return "agent-ref-has-persona-but-no-command-work";
652
+ if (kind === "other-ref")
653
+ return "non-command-asset-has-no-v3-command-ref-equivalent";
654
+ if (kind === "command")
655
+ output.uses = prompt;
656
+ else {
657
+ output.uses = "akm/command";
658
+ output.with = { content: prompt };
659
+ }
660
+ addRuntimeOverrides(data, akm);
661
+ }
662
+ else {
663
+ validateTargetFields(data, ["timeoutMs"]);
664
+ if (Array.isArray(data.command))
665
+ return "argv-array-has-no-portable-shell-string";
666
+ const command = exactString(data.command, "command", true).trim();
667
+ if (/['"\\]/.test(command))
668
+ return "shell-quoting-changes-v2-whitespace-split-semantics";
669
+ const tokens = command.split(/\s+/).filter(Boolean);
670
+ if (tokens.length === 0 || tokens.some((token) => !SAFE_V2_COMMAND_TOKEN.test(token))) {
671
+ return "shell-operators-change-v2-literal-argv-semantics";
672
+ }
673
+ const { tokens: targetTokens, envWrapped } = skipEnvAssignmentPrefix(tokens);
674
+ const executable = targetTokens[0];
675
+ if (SHELL_ASSIGNMENT_WORD.test(executable) || (!envWrapped && !shellStableV2Executable(executable))) {
676
+ return "shell-command-resolution-changes-v2-literal-argv-semantics";
677
+ }
678
+ output.run = tokens.join(" ");
679
+ addSharedNonPromptOverrides(data, akm);
680
+ }
681
+ output.akm = akm;
682
+ return output;
683
+ }
684
+ function isReason(value) {
685
+ return typeof value === "string";
686
+ }
687
+ /**
688
+ * v2 straight to v4: build the v3-shape record in memory, validate it
689
+ * through the REAL typed v3 parser exactly as the prior v2-to-v3 generation
690
+ * did (the same safety net, now inline), then hoist it to v4 through the
691
+ * same `planV3DataToV4` every real v3 file goes through. `before`/
692
+ * `beforeHash` on the result are always the original v2 bytes (`base(input)`,
693
+ * computed from the untouched `input`).
694
+ */
695
+ function planV2DataToV4(input, data) {
696
+ if (!input.writable || input.onDiskWritable === false) {
697
+ return blocked(input, "read-only-source", !input.writable ? "the owning source is not writable" : "the source file or publication directory is read-only");
698
+ }
699
+ try {
700
+ validateTaskId(path.basename(input.filePath, ".yml"));
701
+ }
702
+ catch (cause) {
703
+ return blocked(input, "invalid-v2-task", causeMessage(cause));
704
+ }
705
+ let migrated;
706
+ try {
707
+ migrated = migratedObject(data);
708
+ }
709
+ catch (cause) {
710
+ return blocked(input, "invalid-v2-task", causeMessage(cause));
711
+ }
712
+ if (isReason(migrated)) {
713
+ const detail = migrated === "argv-array-has-no-portable-shell-string" ? ARGV_ARRAY_BLOCK_DETAIL : undefined;
714
+ return blocked(input, migrated, detail);
715
+ }
716
+ const v3Yaml = stringifyYaml(migrated);
717
+ try {
718
+ parseTaskV3Yaml({
719
+ yaml: v3Yaml,
720
+ filePath: input.filePath,
721
+ ...(input.containmentRoot ? { workspaceRoot: input.containmentRoot } : {}),
722
+ });
723
+ }
724
+ catch (cause) {
725
+ return blocked(input, "generated-v3-validation-failed", causeMessage(cause));
726
+ }
727
+ let v3Data;
728
+ try {
729
+ ({ data: v3Data } = parseRawTaskYaml({ ...input, bytes: Buffer.from(v3Yaml, "utf8") }));
362
730
  }
363
731
  catch (cause) {
364
732
  return blocked(input, "invalid-task-yaml", causeMessage(cause));
365
733
  }
366
- if (data.version === 4) {
367
- const document = parseDocument(source, { uniqueKeys: true });
368
- const schedule = document.get("schedule", true);
369
- let removed = false;
370
- if (isSeq(schedule)) {
371
- for (const entry of schedule.items) {
372
- if (!isMap(entry) || !entry.has("enabled"))
373
- continue;
374
- entry.delete("enabled");
375
- removed = true;
376
- }
734
+ const v4Outcome = planV3DataToV4(input, v3Data);
735
+ return v4Outcome.status === "changed" ? { ...v4Outcome, reason: "task-converted" } : v4Outcome;
736
+ }
737
+ // ── v4 (cleanup only) ───────────────────────────────────────────────────────
738
+ /** A v4 document: strip 0.9.15's retired per-schedule `enabled`, if present; otherwise already current. */
739
+ function planV4Cleanup(input, source) {
740
+ const document = parseDocument(source, { uniqueKeys: true });
741
+ const schedule = document.get("schedule", true);
742
+ let removed = false;
743
+ if (isSeq(schedule)) {
744
+ for (const entry of schedule.items) {
745
+ if (!isMap(entry) || !entry.has("enabled"))
746
+ continue;
747
+ entry.delete("enabled");
748
+ removed = true;
377
749
  }
378
- if (removed) {
379
- if (!input.writable || input.onDiskWritable === false) {
380
- return blocked(input, "read-only-source", !input.writable
381
- ? "the owning source is not writable"
382
- : "the source file or publication directory is read-only");
383
- }
384
- const after = Buffer.from(document.toString(), "utf8");
385
- try {
386
- parseTaskSourceV4({
387
- yaml: after.toString("utf8"),
388
- filePath: input.filePath,
389
- ...(input.containmentRoot ? { workspaceRoot: input.containmentRoot } : {}),
390
- });
391
- }
392
- catch (cause) {
393
- return blocked(input, "generated-v4-validation-failed", causeMessage(cause));
394
- }
395
- return Object.freeze({
396
- status: "changed",
397
- ...base(input),
398
- reason: "source-enablement-removed",
399
- after,
400
- afterHash: hash(after),
401
- notice: "Removed source-owned schedule enablement; scheduler activation is now host-local config.",
402
- });
750
+ }
751
+ if (removed) {
752
+ if (!input.writable || input.onDiskWritable === false) {
753
+ return blocked(input, "read-only-source", !input.writable ? "the owning source is not writable" : "the source file or publication directory is read-only");
403
754
  }
755
+ const after = Buffer.from(document.toString(), "utf8");
404
756
  try {
405
757
  parseTaskSourceV4({
406
- yaml: source,
758
+ yaml: after.toString("utf8"),
407
759
  filePath: input.filePath,
408
760
  ...(input.containmentRoot ? { workspaceRoot: input.containmentRoot } : {}),
409
761
  });
410
- return Object.freeze({ status: "skipped", ...base(input), reason: "already-v4" });
411
762
  }
412
763
  catch (cause) {
413
- return blocked(input, "invalid-v4-task", causeMessage(cause));
764
+ return blocked(input, "generated-v4-validation-failed", causeMessage(cause));
414
765
  }
415
- }
416
- // Not this generation's document to validate — v2 grammar is entirely
417
- // generation 1's domain (task-to-v3.ts). Reported skipped, not blocked
418
- // (see TaskToV4Skipped's own header).
419
- if (data.version === 2) {
420
766
  return Object.freeze({
421
- status: "skipped",
767
+ status: "changed",
422
768
  ...base(input),
423
- reason: "pending-v2-to-v3-migration",
769
+ reason: "source-enablement-removed",
770
+ after,
771
+ afterHash: hash(after),
772
+ notice: "Removed source-owned schedule enablement; scheduler activation is now host-local config.",
773
+ });
774
+ }
775
+ try {
776
+ parseTaskSourceV4({
777
+ yaml: source,
778
+ filePath: input.filePath,
779
+ ...(input.containmentRoot ? { workspaceRoot: input.containmentRoot } : {}),
424
780
  });
781
+ return Object.freeze({ status: "skipped", ...base(input), reason: "already-v4" });
782
+ }
783
+ catch (cause) {
784
+ return blocked(input, "invalid-v4-task", causeMessage(cause));
785
+ }
786
+ }
787
+ // ── dispatcher ───────────────────────────────────────────────────────────────
788
+ /** Plan exactly one source file — v2, v3, or v4 — straight to v4, without touching disk. */
789
+ export function planTaskToV4File(input) {
790
+ let data;
791
+ let source;
792
+ try {
793
+ ({ data, source } = parseRawTaskYaml(input));
794
+ }
795
+ catch (cause) {
796
+ return blocked(input, "invalid-task-yaml", causeMessage(cause));
425
797
  }
426
- if (data.version !== 3) {
427
- return blocked(input, "unsupported-task-version", `expected version 2, 3, or 4, got ${String(data.version)}`);
798
+ if (data.version === 4) {
799
+ return planV4Cleanup(input, source);
428
800
  }
429
- return planV3DataToV4(input, data);
801
+ if (data.version === 3) {
802
+ try {
803
+ parseTaskV3Yaml({
804
+ yaml: source,
805
+ filePath: input.filePath,
806
+ ...(input.containmentRoot ? { workspaceRoot: input.containmentRoot } : {}),
807
+ });
808
+ }
809
+ catch (cause) {
810
+ return blocked(input, "invalid-v3-task", causeMessage(cause));
811
+ }
812
+ return planV3DataToV4(input, data);
813
+ }
814
+ if (data.version === 2) {
815
+ return planV2DataToV4(input, data);
816
+ }
817
+ return blocked(input, "unsupported-task-version", `expected version 2, 3, or 4, got ${String(data.version)}`);
430
818
  }
431
819
  function generationFor(files) {
432
820
  const digest = crypto.createHash("sha256");
@@ -473,15 +861,3 @@ export function taskToV4PlanFromOutcomes(outcomes) {
473
861
  }
474
862
  return Object.freeze({ schemaVersion: 1, generation: generationFor(files), files: Object.freeze(files) });
475
863
  }
476
- /** Plan a complete, stable file set. Input order cannot change the result. */
477
- export function planTaskToV4Migration(inputs) {
478
- const sorted = [...inputs].sort((left, right) => left.filePath < right.filePath ? -1 : left.filePath > right.filePath ? 1 : 0);
479
- let previous;
480
- for (const current of sorted) {
481
- if (previous && path.resolve(previous.filePath) === path.resolve(current.filePath)) {
482
- throw new Error(`duplicate task migration file path: ${current.filePath}`);
483
- }
484
- previous = current;
485
- }
486
- return taskToV4PlanFromOutcomes(sorted.map(planTaskToV4File));
487
- }