akm-cli 0.9.17-alpha.3 → 0.9.17-alpha.4

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 (343) hide show
  1. package/CHANGELOG.md +731 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/proposal/drain.js +251 -644
  53. package/dist/commands/proposal/proposal-cli.js +3 -18
  54. package/dist/commands/proposal/proposal-types.js +20 -41
  55. package/dist/commands/proposal/proposal.js +1 -2
  56. package/dist/commands/proposal/propose.js +134 -160
  57. package/dist/commands/proposal/repository.js +502 -1487
  58. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  59. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  60. package/dist/commands/proposal/validators/proposals.js +13 -89
  61. package/dist/commands/read/curate.js +63 -413
  62. package/dist/commands/read/search-cli.js +16 -33
  63. package/dist/commands/read/search.js +17 -23
  64. package/dist/commands/read/show.js +2 -13
  65. package/dist/commands/sources/bundle-cli.js +25 -2
  66. package/dist/commands/sources/bundle-config-ops.js +7 -0
  67. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  68. package/dist/commands/sources/info.js +2 -11
  69. package/dist/commands/sources/installed-stashes.js +197 -746
  70. package/dist/commands/sources/schema-repair.js +98 -129
  71. package/dist/commands/sources/source-add.js +62 -12
  72. package/dist/commands/sources/stash-cli.js +1 -1
  73. package/dist/commands/tasks/explain.js +10 -13
  74. package/dist/commands/tasks/tasks-cli.js +9 -8
  75. package/dist/commands/tasks/tasks.js +326 -930
  76. package/dist/commands/tasks/validate.js +42 -21
  77. package/dist/commands/workflow/plan.js +22 -29
  78. package/dist/commands/workflow-cli.js +4 -4
  79. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  80. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  81. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  82. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  83. package/dist/core/adapter/execution-source.js +17 -29
  84. package/dist/core/asset/resolve-ref.js +1 -1
  85. package/dist/core/bundle-id.js +42 -5
  86. package/dist/core/bundle-rename.js +291 -0
  87. package/dist/core/config/config-io.js +1 -2
  88. package/dist/core/config/config-schema.js +1 -33
  89. package/dist/core/config/config-walker.js +1 -1
  90. package/dist/core/config/config.js +163 -68
  91. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  92. package/dist/core/config/schema/embedding.js +20 -5
  93. package/dist/core/config/schema/engines.js +5 -0
  94. package/dist/core/config/schema/execution.js +1 -1
  95. package/dist/core/config/schema/experimental.js +1 -1
  96. package/dist/core/config/schema/improve-processes.js +21 -95
  97. package/dist/core/config/schema/improve.js +4 -42
  98. package/dist/core/config/schema/scheduler.js +12 -12
  99. package/dist/core/config/schema/search.js +6 -22
  100. package/dist/core/env-secret-ref.js +0 -1
  101. package/dist/core/errors.js +8 -9
  102. package/dist/core/file-lock.js +76 -173
  103. package/dist/core/logs-db.js +2 -2
  104. package/dist/core/paths.js +0 -27
  105. package/dist/core/redaction.js +109 -2
  106. package/dist/core/run-lock.js +2 -5
  107. package/dist/core/spawn-env.js +1 -1
  108. package/dist/core/state/migrations.js +108 -61
  109. package/dist/core/state-db-scope.js +2 -4
  110. package/dist/core/state-db.js +126 -692
  111. package/dist/core/type-presentation.js +1 -9
  112. package/dist/core/write-source.js +293 -1012
  113. package/dist/execution/input-contract.js +1 -1
  114. package/dist/execution/resolved-request.js +135 -689
  115. package/dist/execution/source.js +63 -257
  116. package/dist/execution/target-ref.js +1 -1
  117. package/dist/indexer/bundle-identity-guard.js +2 -2
  118. package/dist/indexer/db/graph-db.js +106 -46
  119. package/dist/indexer/ensure-index.js +44 -85
  120. package/dist/indexer/graph/graph-extraction.js +340 -562
  121. package/dist/indexer/graph/graph-related.js +130 -0
  122. package/dist/indexer/index-rebuild-lock.js +3 -11
  123. package/dist/indexer/index-writer-lock.js +8 -17
  124. package/dist/indexer/index-written-assets.js +139 -151
  125. package/dist/indexer/indexer.js +524 -846
  126. package/dist/indexer/materialize-embeddings.js +60 -397
  127. package/dist/indexer/passes/memory-inference.js +81 -90
  128. package/dist/indexer/passes/metadata.js +132 -200
  129. package/dist/indexer/read-preflight.js +0 -7
  130. package/dist/indexer/scan/doc-to-entry.js +1 -3
  131. package/dist/indexer/scan/drain-dir.js +1 -1
  132. package/dist/indexer/search/db-search.js +181 -590
  133. package/dist/indexer/search/fts-query.js +30 -41
  134. package/dist/indexer/search/ranking.js +28 -154
  135. package/dist/indexer/search/search-attribution.js +12 -32
  136. package/dist/indexer/search/search-fields.js +11 -15
  137. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  138. package/dist/indexer/search/search-source.js +1 -4
  139. package/dist/indexer/usage/usage-events.js +2 -7
  140. package/dist/integrations/agent/engine-fallback.js +23 -40
  141. package/dist/integrations/agent/engine-resolution.js +93 -183
  142. package/dist/integrations/agent/execution.js +507 -0
  143. package/dist/integrations/agent/model-map.js +28 -156
  144. package/dist/integrations/agent/request-lowering.js +66 -141
  145. package/dist/integrations/agent/runner-dispatch.js +143 -321
  146. package/dist/integrations/agent/runner.js +54 -14
  147. package/dist/integrations/lockfile.js +53 -101
  148. package/dist/llm/embedders/deterministic.js +2 -3
  149. package/dist/llm/embedders/profile.js +71 -0
  150. package/dist/llm/embedders/remote.js +10 -15
  151. package/dist/llm/graph-extract.js +3 -12
  152. package/dist/llm/index-passes.js +3 -5
  153. package/dist/llm/memory-infer.js +1 -2
  154. package/dist/llm/metadata-enhance.js +1 -2
  155. package/dist/llm/structured-call.js +5 -24
  156. package/dist/output/generic-render.js +23 -11
  157. package/dist/output/html-render.js +13 -10
  158. package/dist/output/render-registry.js +3 -32
  159. package/dist/output/shapes/helpers.js +2 -34
  160. package/dist/output/shapes/passthrough.js +1 -9
  161. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  162. package/dist/output/text/command-format.js +60 -23
  163. package/dist/output/text/helpers.js +1 -1
  164. package/dist/output/text/migrate.js +5 -14
  165. package/dist/output/text/proposal-format.js +1 -2
  166. package/dist/output/text/workflow-format.js +0 -32
  167. package/dist/output/text.js +2 -0
  168. package/dist/registry/factory.js +4 -19
  169. package/dist/registry/network.js +66 -220
  170. package/dist/registry/providers/index.js +0 -2
  171. package/dist/registry/providers/skills-sh.js +3 -14
  172. package/dist/registry/providers/static-index.js +24 -26
  173. package/dist/registry/resolve.js +55 -131
  174. package/dist/scripts/akm-migrate-node.js +43937 -93313
  175. package/dist/scripts/akm-migrate.js +43697 -93071
  176. package/dist/setup/registry-stash-loader.js +4 -13
  177. package/dist/setup/semantic-assets.js +3 -44
  178. package/dist/setup/setup.js +1 -1
  179. package/dist/setup/steps/tasks.js +25 -15
  180. package/dist/sources/provider-factory.js +17 -18
  181. package/dist/sources/providers/filesystem.js +2 -3
  182. package/dist/sources/providers/git-install.js +7 -1
  183. package/dist/sources/providers/git-provider.js +0 -3
  184. package/dist/sources/providers/git-stash.js +0 -17
  185. package/dist/sources/providers/npm.js +2 -4
  186. package/dist/sources/providers/provider-utils.js +5 -10
  187. package/dist/sources/providers/website.js +0 -2
  188. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  189. package/dist/sources/website-url.js +2 -2
  190. package/dist/storage/database.js +9 -35
  191. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  192. package/dist/storage/repositories/index-connection.js +34 -70
  193. package/dist/storage/repositories/index-entries-repository.js +69 -111
  194. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  195. package/dist/storage/repositories/index-entry-schema.js +83 -269
  196. package/dist/storage/repositories/index-fts-repository.js +86 -256
  197. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  198. package/dist/storage/repositories/index-meta-repository.js +6 -4
  199. package/dist/storage/repositories/index-schema.js +192 -220
  200. package/dist/storage/repositories/index-utility-repository.js +8 -29
  201. package/dist/storage/repositories/index-vec-repository.js +133 -414
  202. package/dist/storage/repositories/outcome-repository.js +2 -1
  203. package/dist/storage/repositories/proposals-repository.js +35 -0
  204. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  205. package/dist/storage/repositories/task-history-repository.js +26 -4
  206. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  207. package/dist/storage/sqlite-migrations.js +136 -0
  208. package/dist/storage/sqlite-pragmas.js +11 -9
  209. package/dist/storage/sqlite-transaction.js +170 -0
  210. package/dist/storage/state-db-integrity.js +34 -27
  211. package/dist/tasks/activation-config.js +134 -62
  212. package/dist/tasks/backends/cron.js +129 -277
  213. package/dist/tasks/backends/exec-utils.js +2 -5
  214. package/dist/tasks/backends/launchd.js +125 -745
  215. package/dist/tasks/backends/schtasks.js +101 -620
  216. package/dist/tasks/prepare/prepare-support.js +5 -15
  217. package/dist/tasks/prepare/prepare.js +0 -2
  218. package/dist/tasks/resolve-akm-bin.js +20 -79
  219. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  220. package/dist/tasks/scheduler-binding.js +18 -238
  221. package/dist/tasks/scheduler-invocation.js +52 -52
  222. package/dist/tasks/scheduler-lock.js +53 -0
  223. package/dist/tasks/scheduler-sync.js +361 -751
  224. package/dist/tasks/source/parse-task-source.js +160 -10
  225. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  226. package/dist/tasks/source/task-to-v4.js +2 -2
  227. package/dist/workflows/authoring/authoring.js +3 -12
  228. package/dist/workflows/compile.js +211 -0
  229. package/dist/workflows/concurrency-policy.js +13 -74
  230. package/dist/workflows/exec/child-invocation.js +3 -17
  231. package/dist/workflows/exec/child-workflow.js +32 -141
  232. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  233. package/dist/workflows/exec/environment.js +98 -0
  234. package/dist/workflows/exec/exec-unit.js +33 -140
  235. package/dist/workflows/exec/frozen-judge.js +7 -59
  236. package/dist/workflows/exec/native-executor.js +82 -341
  237. package/dist/workflows/exec/param-secrets.js +29 -47
  238. package/dist/workflows/exec/run-workflow.js +154 -387
  239. package/dist/workflows/exec/scheduler.js +9 -36
  240. package/dist/workflows/exec/step-work.js +127 -430
  241. package/dist/workflows/exec/unit-dispatch.js +11 -63
  242. package/dist/workflows/exec/unit-writer.js +8 -52
  243. package/dist/workflows/exec/worktree.js +39 -273
  244. package/dist/workflows/freeze/child-output-references.js +4 -15
  245. package/dist/workflows/freeze/environment.js +99 -92
  246. package/dist/workflows/freeze/freeze.js +172 -0
  247. package/dist/workflows/freeze/step-values.js +19 -21
  248. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  249. package/dist/workflows/freeze/targets/command.js +10 -33
  250. package/dist/workflows/freeze/targets/script.js +5 -12
  251. package/dist/workflows/freeze/targets/shell.js +3 -6
  252. package/dist/workflows/freeze/targets/task.js +25 -80
  253. package/dist/workflows/freeze/task-bindings.js +20 -67
  254. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  255. package/dist/workflows/ir/params.js +6 -51
  256. package/dist/workflows/ir/plan-hash.js +2 -34
  257. package/dist/workflows/parser.js +140 -43
  258. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  259. package/dist/workflows/renderer.js +36 -69
  260. package/dist/workflows/resource-limits.js +12 -120
  261. package/dist/workflows/runtime/agent-identity.js +8 -40
  262. package/dist/workflows/runtime/run-outputs.js +3 -6
  263. package/dist/workflows/runtime/run-plan.js +316 -0
  264. package/dist/workflows/runtime/runs.js +48 -200
  265. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  266. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  267. package/dist/workflows/validate-summary.js +2 -7
  268. package/docs/integration/bundling-akm.md +49 -42
  269. package/docs/migration/README.md +1 -0
  270. package/docs/migration/release-notes/0.9.17.md +41 -0
  271. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  272. package/docs/reference/cli.md +182 -125
  273. package/docs/reference/configuration.md +49 -56
  274. package/docs/reference/data-and-telemetry.md +19 -20
  275. package/docs/reference/tasks.md +86 -38
  276. package/docs/reference/workflow-schema.md +14 -18
  277. package/docs/reference/workflows.md +6 -9
  278. package/package.json +1 -1
  279. package/schemas/akm-config.json +87 -406
  280. package/dist/commands/health/advisories.js +0 -150
  281. package/dist/commands/health/metrics.js +0 -329
  282. package/dist/commands/health/surfaces.js +0 -102
  283. package/dist/commands/improve/anti-collapse.js +0 -83
  284. package/dist/commands/improve/collapse-detector.js +0 -432
  285. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  286. package/dist/commands/improve/consolidate/merge.js +0 -146
  287. package/dist/commands/improve/distill/promote-memory.js +0 -329
  288. package/dist/commands/improve/distill/quality-gate.js +0 -500
  289. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  290. package/dist/commands/improve/proposal-envelope.js +0 -31
  291. package/dist/commands/improve/run-context.js +0 -123
  292. package/dist/commands/improve/shared.js +0 -21
  293. package/dist/commands/improve/source-identity.js +0 -28
  294. package/dist/commands/improve/triage.js +0 -96
  295. package/dist/commands/proposal/drain-policies.js +0 -151
  296. package/dist/commands/sources/update-transaction.js +0 -220
  297. package/dist/core/action-contributors.js +0 -28
  298. package/dist/core/config/config-version-shim.js +0 -101
  299. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  300. package/dist/core/fs-txn.js +0 -405
  301. package/dist/core/lexical-score.js +0 -25
  302. package/dist/core/maintenance-barrier.js +0 -167
  303. package/dist/execution/executable-identity.js +0 -105
  304. package/dist/execution/guarded-source.js +0 -441
  305. package/dist/indexer/graph/graph-boost.js +0 -427
  306. package/dist/indexer/graph/graph-dedup.js +0 -95
  307. package/dist/indexer/search/name-match.js +0 -35
  308. package/dist/indexer/search/ranking-contributors.js +0 -515
  309. package/dist/indexer/walk/project-context.js +0 -192
  310. package/dist/integrations/agent/execution-cascade.js +0 -566
  311. package/dist/integrations/agent/execution-definitions.js +0 -202
  312. package/dist/integrations/agent/execution-lowering.js +0 -841
  313. package/dist/integrations/agent/execution-preparation.js +0 -98
  314. package/dist/integrations/agent/inline-execution.js +0 -74
  315. package/dist/registry/create-provider-registry.js +0 -29
  316. package/dist/registry/pinned-request-helper.js +0 -247
  317. package/dist/registry/pinned-transport.js +0 -717
  318. package/dist/sources/providers/index.js +0 -14
  319. package/dist/storage/engines/sqlite-migrations.js +0 -271
  320. package/dist/storage/repositories/canaries-repository.js +0 -107
  321. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  322. package/dist/storage/repositories/registry-cache.js +0 -113
  323. package/dist/tasks/scheduler-sync-preview.js +0 -52
  324. package/dist/workflows/freeze/resolve-steps.js +0 -86
  325. package/dist/workflows/freeze/source-freeze.js +0 -64
  326. package/dist/workflows/ir/compile.js +0 -321
  327. package/dist/workflows/ir/environment-v4.js +0 -330
  328. package/dist/workflows/ir/freeze-v4.js +0 -153
  329. package/dist/workflows/ir/schema-v4.js +0 -745
  330. package/dist/workflows/ir/schema.js +0 -354
  331. package/dist/workflows/program/schema.js +0 -78
  332. package/dist/workflows/runtime/checkin.js +0 -57
  333. package/dist/workflows/runtime/plan-classifier.js +0 -196
  334. package/dist/workflows/runtime/unit-checkin.js +0 -45
  335. package/dist/workflows/runtime/unit-phases.js +0 -20
  336. package/dist/workflows/schema.js +0 -4
  337. package/dist/workflows/source-ir/compile.js +0 -200
  338. package/dist/workflows/source-ir/program.js +0 -50
  339. package/dist/workflows/source-ir/result.js +0 -26
  340. package/dist/workflows/source-ir/schema.js +0 -786
  341. package/dist/workflows/source-ir/triggers.js +0 -79
  342. package/dist/workflows/source-ir/uses.js +0 -40
  343. package/dist/workflows/validator.js +0 -60
@@ -2,23 +2,13 @@
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
- * The pure `with:` -> `TaskInputBinding[]` normalizer, plus the pure
6
- * re-binder a recursive composition needs on top of it.
7
- * {@link freezeTaskInputBindings} classifies a genuinely AUTHORED `with:`
8
- * record against a contract (called by `freeze/targets/task.ts`'s
9
- * `taskDispatch`, this step's own `with:` against the composed task's own
10
- * contract only — no merge across a composition chain).
11
- * {@link rebindTaskInputBindings} instead re-binds an ALREADY-classified
12
- * `TaskInputBinding[]` by name against a DIFFERENT contract, trusting each
13
- * entry's existing `kind` rather than re-deriving it from the value's shape
14
- * (`freeze/targets/child-workflow.ts`'s only caller). Everything decidable
15
- * at FREEZE time is decided here; a reference's *resolved* value is
16
- * validated PRE-ATTEMPT instead (`exec/step-work.ts`). Pure function: no IO,
17
- * no config reads.
18
- *
19
- * See docs/architecture/decisions/0008-task-binding-normalization.md for the
20
- * full design history, including why re-binding cannot reuse the
21
- * shape-driven normalizer (a code-review finding).
5
+ * Pure freeze-time binding of a composing step's inputs.
6
+ * {@link freezeTaskInputBindings} classifies an authored `with:` against a
7
+ * contract; {@link rebindTaskInputBindings} re-binds already-classified
8
+ * bindings by name against another contract (a task's effective inputs
9
+ * against a child workflow's `params:`) without re-deriving their kind. A
10
+ * reference's resolved value is validated before each attempt (`exec/step-work.ts`).
11
+ * See docs/architecture/decisions/0008-task-binding-normalization.md.
22
12
  */
23
13
  import { isRecord } from "../../core/common.js";
24
14
  import { UsageError } from "../../core/errors.js";
@@ -32,14 +22,9 @@ function unknownBindingNameError(stepId, targetRef, name, declaredNames) {
32
22
  `Declared inputs: ${declaredNames.length > 0 ? declaredNames.join(", ") : "(none)"}.`);
33
23
  }
34
24
  /**
35
- * Normalize the workflow source front end's authored `with:` record into the
36
- * `TaskInputBinding[]` a task-composing step's frozen target carries (spec
37
- * §3.3). Throws `UsageError`/`INPUT_BINDING_INVALID` for the first violation
38
- * found — per authored entry first (B-11, B-15, B-16, B-17, B-18), then over
39
- * the whole contract (B-12, B-13). The result is sorted by name; an entry
40
- * exists only for a declared input with an effective value (authored
41
- * literal, authored reference, or an applied default) — never for an
42
- * unsupplied optional input with no default (B-20).
25
+ * Normalize an authored `with:` into the sorted `TaskInputBinding[]` a frozen
26
+ * target carries: one entry per declared input with an effective value
27
+ * (literal, reference, or default). Throws `INPUT_BINDING_INVALID` on the first violation.
43
28
  */
44
29
  export function freezeTaskInputBindings(input) {
45
30
  const { stepId, targetRef, contract, earlierStepIds, declaredParamNames } = input;
@@ -57,28 +42,11 @@ export function freezeTaskInputBindings(input) {
57
42
  return finalizeBindings(stepId, targetRef, contract, byName);
58
43
  }
59
44
  /**
60
- * Re-bind an ALREADY-NORMALIZED `TaskInputBinding[]` — a v4 task's own
61
- * effective inputs, classified once against the TASK's own declared
62
- * `inputs:` contract by {@link freezeTaskInputBindings} — against a
63
- * DIFFERENT contract (the child workflow's declared `params:`) by NAME,
64
- * without re-deriving each entry's literal/reference classification from
65
- * its value's shape (a code-review finding — see
66
- * docs/architecture/decisions/0008-task-binding-normalization.md for why a
67
- * shape-shifting round-trip through {@link normalizeOneEntry} would
68
- * silently misclassify a literal value shaped like a reference).
69
- *
70
- * Per-entry rules, otherwise identical to {@link freezeTaskInputBindings}:
71
- * an entry naming a key the new `contract` does not declare is
72
- * `INPUT_BINDING_INVALID` (same message shape); a `kind: "literal"` entry
73
- * keeps its value verbatim and is validated against the NEW contract's
74
- * declared schema for that name; a `kind: "reference"` entry keeps its
75
- * `from` verbatim (the reference target — an earlier step or declared param
76
- * of the composing workflow — does not change with which contract it is
77
- * bound against) and its `schema` is re-derived from the NEW contract,
78
- * matching {@link normalizeOneEntry}'s existing rule that a reference
79
- * binding's schema always comes from the contract it is bound against; a
80
- * contract key absent from `bindings` is defaulted or required exactly as
81
- * {@link freezeTaskInputBindings} does.
45
+ * Re-bind already-normalized bindings by name against a different contract,
46
+ * keeping each entry's kind (a literal shaped like `{from}` stays a literal).
47
+ * A literal is validated against the new contract; a reference keeps its
48
+ * `from` and takes the new contract's schema; undeclared names are
49
+ * `INPUT_BINDING_INVALID`; missing keys are defaulted or required as usual.
82
50
  */
83
51
  export function rebindTaskInputBindings(input) {
84
52
  const { stepId, targetRef, contract } = input;
@@ -97,14 +65,7 @@ export function rebindTaskInputBindings(input) {
97
65
  }
98
66
  return finalizeBindings(stepId, targetRef, contract, byName);
99
67
  }
100
- /**
101
- * The tail shared by {@link freezeTaskInputBindings} and
102
- * {@link rebindTaskInputBindings} once `byName` holds one classified entry
103
- * per AUTHORED/bound name: apply declared defaults for every remaining
104
- * contract key (or throw for a required one with none), schema-validate
105
- * every literal (authored, re-bound, or defaulted) against the contract, and
106
- * return the result sorted by name.
107
- */
68
+ /** Shared tail: apply defaults (or require), schema-validate every literal, and sort by name. */
108
69
  function finalizeBindings(stepId, targetRef, contract, byName) {
109
70
  for (const [name, declaration] of Object.entries(contract)) {
110
71
  if (byName.has(name))
@@ -118,11 +79,7 @@ function finalizeBindings(stepId, targetRef, contract, byName) {
118
79
  `supply it with with.${name}.`);
119
80
  }
120
81
  }
121
- // Every LITERAL value (authored, re-bound, or defaulted) is validated
122
- // against its own declared schema — a contract NARROWED to just the
123
- // literal-bound names, so a required input bound via REFERENCE (whose
124
- // value is not known until pre-attempt, §3.6) is never wrongly flagged
125
- // "missing" here.
82
+ // Validate literals only: a reference's value is not known until the attempt.
126
83
  const literalContract = {};
127
84
  const literalValues = {};
128
85
  for (const binding of byName.values()) {
@@ -141,13 +98,9 @@ function finalizeBindings(stepId, targetRef, contract, byName) {
141
98
  return Object.freeze(sorted);
142
99
  }
143
100
  /**
144
- * Classify and validate ONE authored `with:` entry whose key is already
145
- * known to be a declared input name. §3.3 point 2/3: a value is a
146
- * `{kind:"reference"}` binding IFF it is a non-null, non-array plain object
147
- * whose OWN key set is exactly `["from"]` and whose `from` is a string
148
- * `parseReference` accepts — the hard-fail band (B-15, B-16) means any OTHER
149
- * shape carrying an own `from` key is `INPUT_BINDING_INVALID`, never
150
- * reinterpreted as a literal.
101
+ * Classify one authored `with:` entry: exactly `{ from: <valid reference> }`
102
+ * is a reference; any other object with an own `from` key is
103
+ * `INPUT_BINDING_INVALID`, never a literal.
151
104
  */
152
105
  function normalizeOneEntry(stepId, targetRef, name, value, schema, earlierStepIds, declaredParamNames) {
153
106
  if (!isRecord(value) || !Object.hasOwn(value, "from")) {
@@ -1,19 +1,32 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
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
+ /**
5
+ * The GitHub-shaped YAML workflow grammar: a strict `name`/`on`/`jobs`
6
+ * document with exactly one job, compiled straight to a {@link WorkflowPlan}.
7
+ * It is an AKM workflow format executed by AKM's own engine, not GitHub
8
+ * Actions: no expressions, no service events, `runs-on: [self-hosted]` only.
9
+ */
4
10
  import { isAlias, isMap, isScalar, isSeq, LineCounter, parseDocument } from "yaml";
5
- import { utf8Bytes, WORKFLOW_MAX_SOURCE_BYTES } from "../resource-limits.js";
6
- import { WorkflowSourceFailure } from "./result.js";
7
- import { WORKFLOW_SOURCE_HOST_SHELLS, } from "./schema.js";
8
- import { canonicalizeWorkflowCron, canonicalizeWorkflowRun, canonicalizeWorkflowWorkingDirectory, classifyWorkflowStepUses, validateWorkflowBuiltinCommand, WorkflowSourceSemanticError, } from "./semantics.js";
9
- import { classifyWorkflowSourceUses, } from "./uses.js";
11
+ import { WORKFLOW_PLAN_VERSION, } from "./plan.js";
12
+ import { utf8Bytes, WORKFLOW_MAX_SOURCE_BYTES } from "./resource-limits.js";
13
+ import { canonicalizeWorkflowCron, canonicalizeWorkflowRun, canonicalizeWorkflowWorkingDirectory, classifyWorkflowStepUses, validateWorkflowBuiltinCommand, WorkflowSourceSemanticError, workflowShellCommand, } from "./source-semantics.js";
14
+ /** A located compile failure; `compile.ts` turns it into a `WorkflowSourceError`. */
15
+ export class WorkflowSourceFailure extends Error {
16
+ error;
17
+ constructor(code, message, source) {
18
+ super(message);
19
+ this.name = "WorkflowSourceFailure";
20
+ this.error = { code, message, path: source.path, line: source.start };
21
+ }
22
+ }
10
23
  const ROOT_KEYS = ["name", "on", "jobs"];
11
24
  const TRIGGER_KEYS = ["schedule", "workflow_dispatch"];
12
25
  const SCHEDULE_KEYS = ["cron"];
13
26
  const JOB_KEYS = ["name", "needs", "runs-on", "steps"];
14
27
  const STEP_KEYS = ["id", "name", "uses", "run", "with", "env", "shell", "working-directory"];
15
28
  const UNSAFE_KEYS = new Set(["__proto__", "prototype", "constructor"]);
16
- const HOST_SHELLS = new Set(WORKFLOW_SOURCE_HOST_SHELLS);
29
+ const HOST_SHELLS = new Set(["bash", "sh", "zsh", "pwsh", "powershell", "cmd"]);
17
30
  const SOURCE_ID = /^[A-Za-z_][A-Za-z0-9_-]{0,127}$/;
18
31
  const INPUT_KEY = /^[A-Za-z_][A-Za-z0-9_.-]{0,127}$/;
19
32
  const ENV_KEY = /^[A-Za-z_][A-Za-z0-9_]*$/;
@@ -80,16 +93,10 @@ export function parseGithubWorkflowSource(source, options) {
80
93
  reader.rejectAliases(parsedRoot);
81
94
  reader.checkTree(parsedRoot);
82
95
  const root = reader.fields(parsedRoot, ROOT_KEYS, "workflow");
83
- const name = reader.requiredString(root, "name", "workflow");
84
- const triggers = parseTriggers(reader, parsedRoot, reader.required(root, "on", "workflow"), options);
85
- const jobs = parseJobs(reader, reader.required(root, "jobs", "workflow"), options);
86
- return {
87
- sourceIrVersion: 1,
88
- name,
89
- triggers,
90
- jobs,
91
- source: documentSource,
92
- };
96
+ reader.requiredString(root, "name", "workflow");
97
+ const schedules = parseTriggers(reader, reader.required(root, "on", "workflow"));
98
+ const steps = parseJob(reader, reader.required(root, "jobs", "workflow"), options);
99
+ return { irVersion: WORKFLOW_PLAN_VERSION, title: options.title, schedules, steps };
93
100
  }
94
101
  class StrictYamlReader {
95
102
  filePath;
@@ -226,29 +233,6 @@ class StrictYamlReader {
226
233
  }
227
234
  this.fail("invalid-yaml-node", `${context} contains an unsupported YAML node.`, node);
228
235
  }
229
- lineAt(root, structuralPath) {
230
- let current = root;
231
- for (const [index, segment] of structuralPath.entries()) {
232
- if (typeof segment === "string" && isMap(current)) {
233
- const pair = current.items.find((candidate) => isScalar(candidate.key) && candidate.key.value === segment);
234
- if (!pair)
235
- return undefined;
236
- if (pair.value === null) {
237
- return index === structuralPath.length - 1 ? this.span(pair.key).start : undefined;
238
- }
239
- current = pair.value;
240
- continue;
241
- }
242
- if (typeof segment === "number" && isSeq(current)) {
243
- current = current.items[segment] ?? null;
244
- if (current === null)
245
- return undefined;
246
- continue;
247
- }
248
- return undefined;
249
- }
250
- return this.span(current).start;
251
- }
252
236
  required(fields, key, context) {
253
237
  const pair = fields.get(key);
254
238
  if (!pair)
@@ -301,7 +285,7 @@ class StrictYamlReader {
301
285
  throw new WorkflowSourceFailure(code, message, this.span(node));
302
286
  }
303
287
  }
304
- function parseTriggers(reader, root, node, options) {
288
+ function parseTriggers(reader, node) {
305
289
  const fields = reader.arbitraryFields(node, "workflow.on");
306
290
  for (const [key, pair] of fields) {
307
291
  if (!TRIGGER_KEYS.includes(key)) {
@@ -310,7 +294,7 @@ function parseTriggers(reader, root, node, options) {
310
294
  }
311
295
  if (fields.size === 0)
312
296
  reader.fail("trigger-required", "workflow.on must declare schedule or workflow_dispatch.", node);
313
- const triggers = [];
297
+ const schedules = [];
314
298
  const schedule = fields.get("schedule");
315
299
  if (schedule) {
316
300
  const records = reader.sequence(schedule.value, "workflow.on.schedule", 64);
@@ -318,7 +302,7 @@ function parseTriggers(reader, root, node, options) {
318
302
  const scheduleFields = reader.fields(record, SCHEDULE_KEYS, `workflow.on.schedule[${ordinal}]`);
319
303
  const cronNode = reader.required(scheduleFields, "cron", `workflow.on.schedule[${ordinal}]`);
320
304
  const cron = validateCron(reader, reader.string(cronNode, `workflow.on.schedule[${ordinal}].cron`), cronNode);
321
- triggers.push({ kind: "schedule", cron, ordinal, source: reader.span(cronNode) });
305
+ schedules.push({ cron, ordinal, line: reader.span(cronNode).start });
322
306
  }
323
307
  }
324
308
  const manual = fields.get("workflow_dispatch");
@@ -332,62 +316,8 @@ function parseTriggers(reader, root, node, options) {
332
316
  reader.fail("workflow-dispatch-inputs-unsupported", "workflow_dispatch inputs are not supported for local execution.", manual.value.items[0]?.key);
333
317
  }
334
318
  }
335
- triggers.push({ kind: "workflow_dispatch", source: reader.span(manual.key) });
336
- }
337
- if (options.classifyTriggers) {
338
- verifyOwnerTriggerPlan(reader, root, node, triggers, options);
339
- }
340
- return triggers;
341
- }
342
- function verifyOwnerTriggerPlan(reader, root, onNode, triggers, options) {
343
- const classifier = options.classifyTriggers;
344
- if (!classifier)
345
- return;
346
- let lastLine = reader.span(onNode).start;
347
- let plan;
348
- try {
349
- plan = classifier({ on: reader.plain(onNode, "workflow.on") }, {
350
- filePath: options.path,
351
- lineAt: (structuralPath) => {
352
- const line = reader.lineAt(root, structuralPath);
353
- if (line !== undefined)
354
- lastLine = line;
355
- return line;
356
- },
357
- });
358
- }
359
- catch (cause) {
360
- const message = cause instanceof Error ? cause.message : String(cause);
361
- throw new WorkflowSourceFailure("invalid-trigger", message, {
362
- path: options.path,
363
- start: lastLine,
364
- end: lastLine,
365
- });
366
- }
367
- const expectedSchedules = triggers.filter((trigger) => trigger.kind === "schedule");
368
- const expectedManual = triggers.some((trigger) => trigger.kind === "workflow_dispatch");
369
- const matches = plan !== null &&
370
- typeof plan === "object" &&
371
- plan.manual === expectedManual &&
372
- Array.isArray(plan.schedules) &&
373
- plan.schedules.length === expectedSchedules.length &&
374
- plan.schedules.every((binding, index) => {
375
- const expected = expectedSchedules[index];
376
- let canonicalBindingCron;
377
- try {
378
- canonicalBindingCron = canonicalizeWorkflowCron(binding.cron);
379
- }
380
- catch {
381
- return false;
382
- }
383
- return (expected !== undefined &&
384
- canonicalBindingCron === expected.cron &&
385
- binding.ordinal === expected.ordinal &&
386
- binding.source === `on.schedule[${expected.ordinal}].cron`);
387
- });
388
- if (!matches) {
389
- throw new WorkflowSourceFailure("trigger-classifier-drift", "The workflow trigger parser disagrees with the canonical workflow YAML trigger classifier.", reader.span(onNode));
390
319
  }
320
+ return schedules;
391
321
  }
392
322
  function validateCron(reader, cron, node) {
393
323
  try {
@@ -398,77 +328,45 @@ function validateCron(reader, cron, node) {
398
328
  }
399
329
  }
400
330
  /**
401
- * The ONE place a job-count or job-dependency policy is enforced (P4 §3.3,
402
- * docs/plans/specs/p4-deletions-closeout.md): AKM's YAML adapter accepts a
403
- * familiar GitHub-step-shaped `name:`/`on:`/`jobs:` document, but requires
404
- * exactly one job (brief §10) — it is an AKM workflow format executed by
405
- * AKM's native engine, not a GitHub Actions graph. Job ordering, dependency
406
- * validation and the 256-job bound all existed only to support MULTIPLE
407
- * jobs; they are gone with the machinery, not relocated.
331
+ * Exactly one job: AKM's YAML is an AKM workflow format executed by AKM's
332
+ * native engine, not a GitHub Actions job graph.
408
333
  */
409
- function parseJobs(reader, node, options) {
410
- const fields = reader.arbitraryFields(node, "workflow.jobs");
411
- let first;
412
- let second;
413
- for (const entry of fields) {
414
- if (!first)
415
- first = entry;
416
- else if (!second)
417
- second = entry;
418
- }
419
- if (fields.size !== 1 || !first) {
420
- reader.fail("multi-job-unsupported", `AKM workflow YAML requires exactly one job; this document declares ${fields.size}. AKM's YAML is an AKM workflow format executed by AKM's native engine, not GitHub Actions — split the jobs into separate workflows.`, second ? second[1].key : node);
334
+ function parseJob(reader, node, options) {
335
+ const jobs = reader.arbitraryFields(node, "workflow.jobs");
336
+ const [first, second] = [...jobs];
337
+ if (jobs.size !== 1 || !first) {
338
+ reader.fail("multi-job-unsupported", `AKM workflow YAML requires exactly one job; this document declares ${jobs.size}. AKM's YAML is an AKM workflow format executed by AKM's native engine, not GitHub Actions — split the jobs into separate workflows.`, second ? second[1].key : node);
421
339
  }
422
340
  const [id, pair] = first;
423
- const job = parseJob(reader, id, pair, options);
424
- if (job.needs.length > 0) {
425
- reader.fail("multi-job-unsupported", `Job ${job.id} declares needs, but an AKM workflow has exactly one job; remove needs.`, pair.key);
426
- }
427
- return [job];
428
- }
429
- function parseJob(reader, id, pair, options) {
430
- const node = pair.value;
341
+ const jobNode = pair.value;
431
342
  if (!SOURCE_ID.test(id))
432
- reader.fail("invalid-job-id", `Invalid job id ${JSON.stringify(id)}.`, node);
433
- const fields = reader.fields(node, JOB_KEYS, `workflow.jobs.${id}`);
434
- validateRunner(reader, reader.required(fields, "runs-on", `workflow.jobs.${id}`), id);
435
- const needs = parseNeeds(reader, fields.get("needs"), id);
343
+ reader.fail("invalid-job-id", `Invalid job id ${JSON.stringify(id)}.`, jobNode);
344
+ const fields = reader.fields(jobNode, JOB_KEYS, `workflow.jobs.${id}`);
345
+ const runner = reader.required(fields, "runs-on", `workflow.jobs.${id}`);
346
+ if (!isSeq(runner) ||
347
+ runner.items.length !== 1 ||
348
+ !isScalar(runner.items[0]) ||
349
+ runner.items[0].value !== "self-hosted") {
350
+ reader.fail("unsupported-runner", `Job ${JSON.stringify(id)} must declare exactly runs-on: [self-hosted].`, runner);
351
+ }
352
+ const needs = fields.get("needs");
353
+ if (needs) {
354
+ const values = isSeq(needs.value)
355
+ ? reader
356
+ .sequence(needs.value, `workflow.jobs.${id}.needs`, 256)
357
+ .map((item) => reader.string(item, `workflow.jobs.${id}.needs`))
358
+ : [reader.string(needs.value, `workflow.jobs.${id}.needs`)];
359
+ for (const need of values)
360
+ if (!SOURCE_ID.test(need))
361
+ reader.fail("invalid-job-id", `Invalid needs id ${need}.`, needs.value);
362
+ if (values.length > 0) {
363
+ reader.fail("multi-job-unsupported", `Job ${id} declares needs, but an AKM workflow has exactly one job; remove needs.`, pair.key);
364
+ }
365
+ }
366
+ reader.optionalString(fields, "name", `workflow.jobs.${id}`);
436
367
  const stepNodes = reader.sequence(reader.required(fields, "steps", `workflow.jobs.${id}`), `workflow.jobs.${id}.steps`, MAX_STEPS_PER_JOB);
437
368
  const stepIds = new Set();
438
- const steps = stepNodes.map((step, index) => parseStep(reader, step, id, index, stepIds, options));
439
- const name = reader.optionalString(fields, "name", `workflow.jobs.${id}`);
440
- const keySource = reader.span(pair.key);
441
- const valueSource = reader.span(node);
442
- return {
443
- id,
444
- ...(name ? { name } : {}),
445
- needs,
446
- steps,
447
- extensions: { "github.com/actions-workflow": { runsOn: ["self-hosted"] } },
448
- source: { path: keySource.path, start: keySource.start, end: valueSource.end },
449
- };
450
- }
451
- function validateRunner(reader, node, jobId) {
452
- if (!isSeq(node) || node.items.length !== 1 || !isScalar(node.items[0]) || node.items[0].value !== "self-hosted") {
453
- reader.fail("unsupported-runner", `Job ${JSON.stringify(jobId)} must declare exactly runs-on: [self-hosted].`, node);
454
- }
455
- }
456
- function parseNeeds(reader, pair, jobId) {
457
- if (!pair)
458
- return [];
459
- const values = isSeq(pair.value)
460
- ? reader
461
- .sequence(pair.value, `workflow.jobs.${jobId}.needs`, 256)
462
- .map((item) => reader.string(item, `workflow.jobs.${jobId}.needs`))
463
- : [reader.string(pair.value, `workflow.jobs.${jobId}.needs`)];
464
- for (const need of values)
465
- if (!SOURCE_ID.test(need))
466
- reader.fail("invalid-job-id", `Invalid needs id ${need}.`, pair.value);
467
- // Duplicate-entry checking (code duplicate-job-dependency) deleted with the
468
- // rest of the multi-job dependency machinery (P4 §3.3): ANY non-empty
469
- // needs — duplicated or not — is multi-job-unsupported at the caller
470
- // (parseJobs), since a single-job workflow has nothing to depend on.
471
- return values.sort();
369
+ return stepNodes.map((step, index) => parseStep(reader, step, id, index, stepIds, options));
472
370
  }
473
371
  function parseStep(reader, node, jobId, index, stepIds, options) {
474
372
  const context = `workflow.jobs.${jobId}.steps[${index}]`;
@@ -484,33 +382,35 @@ function parseStep(reader, node, jobId, index, stepIds, options) {
484
382
  if ((usesPair === undefined) === (runPair === undefined)) {
485
383
  reader.fail("step-target-xor", `${context} must declare exactly one of uses or run.`, node);
486
384
  }
487
- const common = parseStepCommon(reader, fields, context);
488
- if (usesPair)
489
- return parseUsesStep(reader, usesPair, fields, options, { id, ...common, source: reader.span(node) });
490
- if (!runPair)
491
- reader.fail("step-target-xor", `${context} must declare exactly one of uses or run.`, node);
492
- return parseRunStep(reader, runPair, fields, options, { id, ...common, source: reader.span(node) });
493
- }
494
- function parseStepCommon(reader, fields, context) {
495
- const name = reader.optionalString(fields, "name", context);
385
+ reader.optionalString(fields, "name", context);
496
386
  const env = parseScalarMap(reader, fields.get("env"), `${context}.env`, ENV_KEY, false);
497
- return { ...(name ? { name } : {}), ...(env ? { env } : {}) };
387
+ const target = usesPair
388
+ ? parseUsesStep(reader, usesPair, fields)
389
+ : parseRunStep(reader, runPair, fields, options);
390
+ const spec = { ...target, ...(env ? { env } : {}), source: reader.span(node) };
391
+ return {
392
+ stepId: id,
393
+ title: id,
394
+ sequenceIndex: index,
395
+ spec,
396
+ gate: { kind: "gate", id: `${id}.gate`, stepId: id, criteria: [], maxLoops: 1, frozenJudge: null },
397
+ };
498
398
  }
499
- function parseUsesStep(reader, usesPair, fields, options, common) {
399
+ function parseUsesStep(reader, usesPair, fields) {
500
400
  if (fields.has("shell") || fields.has("working-directory")) {
501
401
  reader.fail("uses-field-conflict", "shell and working-directory are legal only with run.", usesPair.value);
502
402
  }
503
403
  const uses = reader.string(usesPair.value, "step.uses");
504
- const target = classifyUses(reader, uses, usesPair.value, options.classifyUses ?? classifyWorkflowSourceUses);
505
- // A-N3 (P2b, docs/plans/specs/p2b-input-bindings.md §1.7), widened in P3a
506
- // (docs/plans/specs/p3a-plan-v5-child-freeze.md §4.2 step 7, row B-10) to
507
- // ALSO cover a workflows/<ref> target: a tasks/<ref> or workflows/<ref>
508
- // step's with: may bind any JSON value the composed target's declared
509
- // input/param needs (an object/array literal, or a {from: "..."}
510
- // reference) — decoding it through the scalar-only parseScalarMap would
511
- // reject the very shapes both A-N3 and A-N8 exist to accept before
512
- // decodeWorkflowSourceIrV1 (schema.ts) is ever reached. Every other target
513
- // keeps the byte-identical scalar-only grammar.
404
+ let target;
405
+ try {
406
+ target = classifyWorkflowStepUses(uses);
407
+ }
408
+ catch (cause) {
409
+ semanticReaderFail(reader, cause, usesPair.value);
410
+ }
411
+ // A tasks/<ref> or workflows/<ref> step's with: may bind any JSON value the
412
+ // composed target's declared input/param needs (an object/array literal, or
413
+ // a {from: "..."} reference); every other target keeps the scalar grammar.
514
414
  const withValues = target.kind === "task" || target.kind === "workflow"
515
415
  ? parsePlainMap(reader, fields.get("with"), "step.with", INPUT_KEY)
516
416
  : parseScalarMap(reader, fields.get("with"), "step.with", INPUT_KEY, true);
@@ -518,20 +418,11 @@ function parseUsesStep(reader, usesPair, fields, options, common) {
518
418
  ? validateBuiltinCommand(reader, withValues, fields.get("with")?.value ?? usesPair.value)
519
419
  : undefined;
520
420
  return {
521
- ...common,
522
421
  uses,
523
422
  ...(commandMode ? { commandMode } : {}),
524
423
  ...(withValues ? { with: withValues } : {}),
525
424
  };
526
425
  }
527
- function classifyUses(reader, uses, node, classifier) {
528
- try {
529
- return classifyWorkflowStepUses(uses, classifier);
530
- }
531
- catch (cause) {
532
- semanticReaderFail(reader, cause, node);
533
- }
534
- }
535
426
  function validateBuiltinCommand(reader, values, node) {
536
427
  try {
537
428
  const action = validateWorkflowBuiltinCommand(values);
@@ -543,7 +434,8 @@ function validateBuiltinCommand(reader, values, node) {
543
434
  semanticReaderFail(reader, cause, node);
544
435
  }
545
436
  }
546
- function parseRunStep(reader, runPair, fields, options, common) {
437
+ /** A `run:` step lowers to its shell's argv now; `instructions` keeps the authored command for display. */
438
+ function parseRunStep(reader, runPair, fields, options) {
547
439
  if (fields.has("with"))
548
440
  reader.fail("run-field-conflict", "with is legal only with uses.", fields.get("with")?.value);
549
441
  let run;
@@ -568,10 +460,8 @@ function parseRunStep(reader, runPair, fields, options, common) {
568
460
  }
569
461
  }
570
462
  return {
571
- ...common,
572
- run,
573
- ...(shell ? { shell: shell } : {}),
574
- ...(workingDirectory ? { workingDirectory } : {}),
463
+ exec: { command: workflowShellCommand(shell ?? "sh", run), ...(workingDirectory ? { cwd: workingDirectory } : {}) },
464
+ instructions: `Run ${run}.`,
575
465
  };
576
466
  }
577
467
  function parseScalarMap(reader, pair, context, keyPattern, allowNull) {
@@ -586,15 +476,7 @@ function parseScalarMap(reader, pair, context, keyPattern, allowNull) {
586
476
  }
587
477
  return out;
588
478
  }
589
- /**
590
- * Like {@link parseScalarMap} but accepts an arbitrary JSON value per key —
591
- * a task-composition with: binding may be the declared input's own shape (an
592
- * object/array literal, or a `{from: "..."}` reference), not just a scalar
593
- * (A-N3). Depth/node bounds are already enforced document-wide by
594
- * `checkTree`/`rejectAliases` before any field-level parsing runs, so this
595
- * adds no new bound. `decodeWorkflowSourceIrV1` (schema.ts) decides what a
596
- * declared input actually accepts.
597
- */
479
+ /** Like {@link parseScalarMap} but any JSON value per key (bounded document-wide by `checkTree`). */
598
480
  function parsePlainMap(reader, pair, context, keyPattern) {
599
481
  if (!pair)
600
482
  return undefined;
@@ -2,33 +2,13 @@
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
- * Run-parameter validation against the frozen plan's param schemas. A workflow
6
- * can declare `params.files: { type: array }`; supplying a non-array through
7
- * the exact-name `--files` flag must be rejected at start rather than silently
8
- * flowing into a unit prompt. The schemas are frozen into the plan, so
9
- * validation is a pure function of the frozen plan and supplied params.
10
- *
11
- * P2a (docs/plans/specs/p2a-task-source-v4.md §4.3, D3): this module is now a
12
- * THIN CONSUMER of the shared input contract, `src/execution/input-contract.ts`
13
- * (§4, D3, D3-N1/D3-N2/D3-N3). Every export below keeps its existing name,
14
- * signature, message, code, and hint byte-identically —
15
- * `tests/workflows/workflow-param-flags.test.ts` and
16
- * `tests/integration/workflows/params-validation.test.ts` pin that with zero
17
- * diff. `contractFromPlan` adapts a `WorkflowParameterPlan` into an
18
- * `InputContract` (every declared param, `required: false` — workflow params
19
- * declare nothing required). `WORKFLOW_PARAMETER_DIAGNOSTICS` reproduces
20
- * today's five workflow-parameter messages/codes for the shared
21
- * `materializeInputFlags` (D3-N3); its `contractViolation` formatter
22
- * re-roots each `"$"`-prefixed error from the shared module's internal check
23
- * to `"params."`, matching what `validateWorkflowParams` (called directly,
24
- * already `"params."`-rooted) has always produced. No coercion or validation
25
- * logic lives in this file — it lives once, in the shared module.
26
- *
27
- * Pure module: no IO, no engine imports.
5
+ * Run-parameter validation against the frozen plan's param schemas, a thin
6
+ * consumer of the shared input contract (`src/execution/input-contract.ts`):
7
+ * every declared param is optional, and the messages are the workflow ones.
28
8
  */
29
9
  import { UsageError } from "../../core/errors.js";
30
10
  import { materializeInputFlags, validateInputs, } from "../../execution/input-contract.js";
31
- /** Every declared workflow param as an `InputDeclaration`, `required: false` — workflow params declare nothing required (§4.3). */
11
+ /** Every declared workflow param as an `InputDeclaration`, `required: false` — workflow params declare nothing required. */
32
12
  function contractFromPlan(plan) {
33
13
  const names = plan.params ?? Object.keys(plan.paramSchemas ?? {});
34
14
  const contract = {};
@@ -37,16 +17,7 @@ function contractFromPlan(plan) {
37
17
  }
38
18
  return contract;
39
19
  }
40
- /**
41
- * `contractFromPlan`, exported under its P3a-facing name (spec
42
- * docs/plans/specs/p3a-plan-v5-child-freeze.md A-N8): a child workflow has no
43
- * `inputs:` contract of its own (that is task source v4's vocabulary) — it
44
- * declares `params:`, and this is the SAME adapter this file already uses
45
- * for run-parameter validation, reused so `freezeTaskInputBindings`
46
- * (`src/workflows/freeze/task-bindings.ts`, generic over `InputContract`) has
47
- * exactly one normalizer for every binding surface. Additive; no behavior
48
- * change to this file's own exports.
49
- */
20
+ /** A child workflow's `params:` as an input contract, so one normalizer binds every composition surface. */
50
21
  export { contractFromPlan as workflowParamContract };
51
22
  function invalidParameter(name, message) {
52
23
  return new UsageError(`Workflow parameter "--${name}" ${message}.`, "INVALID_FLAG_VALUE");
@@ -81,25 +52,9 @@ export function materializeWorkflowParameterFlags(plan, flags) {
81
52
  */
82
53
  export function validateWorkflowParams(plan, params) {
83
54
  // A plan with no schemas must not start emitting `properties: {}` noise —
84
- // preserved as an explicit early return (§4.3 binding constraint), even
55
+ // preserved as an explicit early return, even
85
56
  // though an empty contract would validate to `[]` regardless.
86
57
  if (!plan.paramSchemas || Object.keys(plan.paramSchemas).length === 0)
87
58
  return [];
88
59
  return validateInputs(contractFromPlan(plan), params, { pathRoot: "params" });
89
60
  }
90
- /**
91
- * Run-integrity assert (reviewer #12): the journaled `params_json`
92
- * row must STILL satisfy the frozen param schemas. `startWorkflowRun` already
93
- * validated the params it stored, so a violation here means the row was edited
94
- * after the run started — loud corruption, exactly like the frozen-plan hash
95
- * mismatch and the tampered-params replay-divergence path. Refuse to describe
96
- * or drive the run rather than resolve prompts from schema-violating params.
97
- */
98
- export function assertRunParamsSatisfyPlan(runId, plan, params) {
99
- const errors = validateWorkflowParams(plan, params);
100
- if (errors.length === 0)
101
- return;
102
- throw new UsageError(`Workflow run ${runId} failed the frozen param-schema integrity check: the journaled params row no longer ` +
103
- `satisfies the workflow's declared parameter schemas (edited after the run started). Refusing to execute it. ` +
104
- `Start a new run.\n${errors.map((e) => ` - ${e}`).join("\n")}`);
105
- }