akm-cli 0.9.17-alpha.2 → 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 +756 -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 +363 -679
  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 -427
  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,18 +2,10 @@
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
- * Plan hashing for the frozen-plan contract (redesign addendum, R1).
6
- *
7
- * `workflow run` persists `plan_json` + `plan_hash` on the run row
8
- * (migration 006); every later invocation executes that snapshot. The hash is
9
- * the sha256 (hex) of the plan's CANONICAL JSON — object keys recursively
10
- * sorted — so two structurally-equal plans hash identically regardless of key
11
- * insertion order, and the same program always freezes to the same hash.
12
- *
13
- * Pure module: no IO beyond node:crypto, no engine imports.
5
+ * Plan hashing: sha256 of the plan's canonical JSON (keys recursively sorted),
6
+ * stored beside `plan_json` as information — a stored plan is never gated on it.
14
7
  */
15
8
  import { createHash } from "node:crypto";
16
- import { decodeWorkflowPlanV4, WORKFLOW_IR_V5_VERSION } from "./schema-v4.js";
17
9
  /** sha256 hex of the canonical (recursively sorted-keys) JSON of the plan. */
18
10
  export function computePlanHash(plan) {
19
11
  return createHash("sha256").update(canonicalPlanJson(plan)).digest("hex");
@@ -26,30 +18,6 @@ export function canonicalPlanJson(plan) {
26
18
  export function canonicalJson(value) {
27
19
  return JSON.stringify(sortKeys(value));
28
20
  }
29
- /** Decode, require stored canonical bytes, then verify the stored SHA-256. */
30
- export function decodeCanonicalPlan(runId, planJson, planHash, expectedVersion) {
31
- let parsed;
32
- try {
33
- parsed = JSON.parse(planJson);
34
- }
35
- catch {
36
- throw new Error(`Workflow run ${runId} has corrupt frozen plan JSON.`);
37
- }
38
- const canonicalWire = canonicalJson(parsed);
39
- if (planJson !== canonicalWire)
40
- throw new Error(`Workflow run ${runId} has noncanonical frozen plan JSON.`);
41
- const actual = createHash("sha256").update(planJson).digest("hex");
42
- if (!planHash || !/^[0-9a-f]{64}$/.test(planHash) || actual !== planHash)
43
- throw new Error(`Workflow run ${runId} frozen plan integrity check failed.`);
44
- if (expectedVersion !== undefined && expectedVersion !== null && expectedVersion !== WORKFLOW_IR_V5_VERSION) {
45
- throw new Error(`Workflow run ${runId} uses unsupported workflow IR version ${expectedVersion}; this runtime supports only workflow IR version 5.`);
46
- }
47
- const plan = decodeWorkflowPlanV4(parsed);
48
- const canonical = canonicalPlanJson(plan);
49
- if (planJson !== canonical)
50
- throw new Error(`Workflow run ${runId} has noncanonical frozen plan JSON.`);
51
- return plan;
52
- }
53
21
  function sortKeys(value) {
54
22
  if (value === null || typeof value === "string" || typeof value === "boolean")
55
23
  return value;
@@ -10,11 +10,12 @@
10
10
  * parsed with the `yaml` package's `parseDocument` + `LineCounter` (best-effort
11
11
  * per-key line anchoring), the body's heading list with `parseMarkdownToc`
12
12
  * (already fence-aware) — both already in the codebase. The parser
13
- * accumulates `WorkflowError`s rather than throwing.
13
+ * accumulates `WorkflowError`s rather than throwing, and emits a compiled
14
+ * {@link WorkflowPlan} directly (`plan.ts`).
14
15
  *
15
16
  * Frontmatter carries the orchestration graph (params/defaults/budget/steps);
16
17
  * the body carries per-step prose, joined to the graph by step id. Body rules
17
- * (spec §2.2), exactly three:
18
+ *, exactly three:
18
19
  *
19
20
  * 1. Every level-2 heading must be `## <step-id>` for a DECLARED step,
20
21
  * exactly (fenced code blocks are skipped when scanning for headings).
@@ -31,16 +32,45 @@
31
32
  * (`program/expressions.ts`): `map.over`, `route.input`, `inputs[]`.
32
33
  */
33
34
  import { LineCounter, parseDocument } from "yaml";
35
+ import { bundleRefToString, parseBundleRef } from "../core/asset/asset-ref.js";
34
36
  import { parseFrontmatterBlock } from "../core/asset/frontmatter.js";
35
37
  import { parseMarkdownToc } from "../core/asset/markdown.js";
36
- import { isContainedRelativePath, isRecord } from "../core/common.js";
38
+ import { compareCodePoints, isContainedRelativePath, isRecord } from "../core/common.js";
37
39
  import { formatExtraParamsIssue, validateExtraParams } from "../core/extra-params.js";
38
40
  import { checkJsonSchemaDefinition, JSON_SCHEMA_SUBSET_SUPPORTED_KEYWORDS } from "../core/json-schema.js";
41
+ import { INPUT_NAME_PATTERN } from "../execution/input-contract.js";
42
+ import { WORKFLOW_PLAN_VERSION, } from "./plan.js";
39
43
  import { parseReference } from "./program/expressions.js";
40
- import { PROGRAM_ISOLATION_KINDS, PROGRAM_ON_ERROR, PROGRAM_PARAM_NAME_PATTERN, PROGRAM_REDUCERS, PROGRAM_RETRY_REASONS, PROGRAM_STEP_ID_PATTERN, } from "./program/schema.js";
41
- import { jsonBytes, utf8Bytes, WORKFLOW_ENGINE_NAME_PATTERN, WORKFLOW_ENV_VAR_NAME_PATTERN, WORKFLOW_MAX_CONCURRENCY, WORKFLOW_MAX_ENGINE_NAME_LENGTH, WORKFLOW_MAX_EXTRA_PARAMS_BYTES, WORKFLOW_MAX_SCHEMA_BYTES, WORKFLOW_MAX_SOURCE_BYTES, WORKFLOW_MAX_TIMEOUT_MS, } from "./resource-limits.js";
42
- import { WORKFLOW_SCHEMA_VERSION, } from "./schema.js";
43
- import { runSemanticChecks } from "./validator.js";
44
+ import { jsonBytes, utf8Bytes, WORKFLOW_ENGINE_NAME_PATTERN, WORKFLOW_ENV_VAR_NAME_PATTERN, WORKFLOW_MAX_CONCURRENCY, WORKFLOW_MAX_ENGINE_NAME_LENGTH, WORKFLOW_MAX_EXTRA_PARAMS_BYTES, WORKFLOW_MAX_INSTRUCTION_BYTES, WORKFLOW_MAX_SCHEMA_BYTES, WORKFLOW_MAX_SOURCE_BYTES, WORKFLOW_MAX_TIMEOUT_MS, } from "./resource-limits.js";
45
+ // ── The Markdown grammar's vocabulary (pinned against schemas/akm-workflow.json) ──
46
+ export const PROGRAM_REDUCERS = ["collect", "vote"];
47
+ export const PROGRAM_ON_ERROR = ["fail", "continue"];
48
+ export const PROGRAM_ISOLATION_KINDS = ["none", "worktree"];
49
+ /** `retry.on`: exactly the persisted `AgentFailureReason` taxonomy (the `satisfies` fails typecheck on drift). */
50
+ const RETRY_REASON_SET = {
51
+ timeout: true,
52
+ spawn_failed: true,
53
+ non_zero_exit: true,
54
+ parse_error: true,
55
+ cooldown: true,
56
+ llm_rate_limit: true,
57
+ llm_content_filter: true,
58
+ llm_invalid_json: true,
59
+ content_policy_reject: true,
60
+ unsupported_type: true,
61
+ no_change: true,
62
+ quality_rejected: true,
63
+ aborted: true,
64
+ };
65
+ export const PROGRAM_RETRY_REASONS = Object.keys(RETRY_REASON_SET);
66
+ /**
67
+ * Step ids are exactly the `<ident>` grammar `steps.<id>.output` references
68
+ * accept: no dots, so every step is addressable and `<stepId>.gate` can never
69
+ * collide with a real step id.
70
+ */
71
+ export const PROGRAM_STEP_ID_PATTERN = /^[A-Za-z_][A-Za-z0-9_-]*$/;
72
+ /** Param (and output) names: plain `params.<ident>`-addressable identifiers, shared with task inputs. */
73
+ export const PROGRAM_PARAM_NAME_PATTERN = INPUT_NAME_PATTERN;
44
74
  /** Envelope keys every AKM markdown asset carries ($ref'd from schemas/akm-asset-envelope.json). */
45
75
  const ENVELOPE_KEYS = [
46
76
  "type",
@@ -174,7 +204,7 @@ export function parseWorkflow(markdown, source) {
174
204
  checkUnknownKeys(ctx, root, [], TOP_LEVEL_KEYS, "workflow frontmatter");
175
205
  checkEnvelopeFields(ctx, root, frontmatterEndLine);
176
206
  const description = typeof root.description === "string" ? root.description : undefined;
177
- const tags = readTags(ctx, root.tags, frontmatterEndLine);
207
+ readTags(ctx, root.tags, frontmatterEndLine);
178
208
  const params = parseParams(ctx, root.params);
179
209
  const outputs = parseOutputs(ctx, root.outputs);
180
210
  const defaults = parseDefaults(ctx, root.defaults);
@@ -194,46 +224,113 @@ export function parseWorkflow(markdown, source) {
194
224
  });
195
225
  }
196
226
  }
197
- else {
198
- if (step.route === undefined && !section.instructions) {
199
- errors.push({
200
- line: section.headingLine,
201
- message: `Step "${step.id}" section ("## ${step.id}") is empty. Add the step's instructions below the heading.`,
202
- });
227
+ else if (step.route === undefined && !section.instructions) {
228
+ errors.push({
229
+ line: section.headingLine,
230
+ message: `Step "${step.id}" section ("## ${step.id}") is empty. Add the step's instructions below the heading.`,
231
+ });
232
+ }
233
+ for (const [block, what] of [
234
+ [section?.instructions, "instructions exceed"],
235
+ [section?.gateRubric, "gate rubric exceeds"],
236
+ ]) {
237
+ if (block && utf8Bytes(block.text) > WORKFLOW_MAX_INSTRUCTION_BYTES) {
238
+ errors.push({ line: block.source.start, message: `Step "${step.id}" ${what} the 256 KiB resource limit.` });
203
239
  }
204
240
  }
205
- const gate = step.gate ?? (section?.gateRubric ? {} : undefined);
206
- const out = {
207
- id: step.id,
241
+ const rubric = section?.gateRubric?.text;
242
+ return {
243
+ stepId: step.id,
244
+ title: step.id,
208
245
  sequenceIndex: index,
209
- ...(step.unit ? { unit: step.unit } : {}),
210
- ...(step.map ? { map: step.map } : {}),
211
- ...(step.route ? { route: step.route } : {}),
212
- ...(step.inputs ? { inputs: step.inputs } : {}),
213
- ...(step.output !== undefined ? { output: step.output } : {}),
214
- ...(gate ? { gate } : {}),
215
- ...(section?.instructions ? { instructions: section.instructions } : {}),
216
- ...(section?.gateRubric ? { gateRubric: section.gateRubric } : {}),
217
- source: step.source,
246
+ spec: stepSpec(step, section),
247
+ ...(step.route
248
+ ? {
249
+ route: {
250
+ input: step.route.input,
251
+ when: Object.fromEntries(step.route.branches.map((branch) => [branch.match, branch.stepId])),
252
+ ...(step.route.defaultStepId !== undefined ? { defaultStepId: step.route.defaultStepId } : {}),
253
+ },
254
+ }
255
+ : {}),
256
+ ...(step.output !== undefined ? { outputSchema: step.output } : {}),
257
+ gate: {
258
+ kind: "gate",
259
+ id: `${step.id}.gate`,
260
+ stepId: step.id,
261
+ criteria: rubric?.trim() ? [rubric] : [],
262
+ maxLoops: step.gate?.maxLoops ?? 1,
263
+ frozenJudge: null,
264
+ },
218
265
  };
219
- return out;
220
266
  });
221
- const draft = {
222
- schemaVersion: WORKFLOW_SCHEMA_VERSION,
223
- ...(description ? { description } : {}),
224
- ...(tags ? { tags } : {}),
225
- ...(params ? { params } : {}),
226
- ...(outputs ? { outputs } : {}),
227
- ...(defaults ? { defaults } : {}),
267
+ checkCanonicalXrefs(root.xrefs, frontmatterEndLine, errors);
268
+ if (errors.length > 0)
269
+ return { ok: false, errors: sortErrors(errors) };
270
+ const paramNames = params ? Object.keys(params) : [];
271
+ const plan = {
272
+ irVersion: WORKFLOW_PLAN_VERSION,
273
+ title: source.title ?? path.replace(/^.*[\\/]/, "").replace(/\.[^.]*$/, ""),
274
+ ...(params && paramNames.length > 0 ? { params: paramNames, paramSchemas: params } : {}),
275
+ ...(outputs ? { outputs: sortedByName(outputs) } : {}),
228
276
  ...(budget ? { budget } : {}),
229
- steps,
277
+ ...(defaults ? { defaults } : {}),
278
+ ...(description ? { description } : {}),
230
279
  ...(preamble ? { preamble } : {}),
231
- source: { path, lineCount: totalLines },
280
+ steps,
232
281
  };
233
- runSemanticChecks(draft, root, frontmatterEndLine, errors);
234
- if (errors.length > 0)
235
- return { ok: false, errors: sortErrors(errors) };
236
- return { ok: true, document: draft };
282
+ return { ok: true, plan };
283
+ }
284
+ /** The authored step as the plan carries it. A prose step is inline `akm/command` literal content. */
285
+ function stepSpec(step, section) {
286
+ const prose = section?.instructions?.text;
287
+ if (step.route)
288
+ return { ...(prose?.trim() ? { instructions: prose } : {}), source: step.source };
289
+ const dispatchUnit = step.map?.unit ?? step.unit;
290
+ const { exec, source: _unitSource, ...unit } = dispatchUnit ?? { source: step.source };
291
+ return {
292
+ ...(exec
293
+ ? { exec: { ...exec }, ...(prose?.trim() ? { instructions: prose } : {}) }
294
+ : { uses: "akm/command", commandMode: "literal", with: { content: prose ?? "" } }),
295
+ ...(Object.keys(unit).length > 0 ? { unit } : {}),
296
+ ...(step.map
297
+ ? {
298
+ map: {
299
+ over: step.map.over,
300
+ ...(step.map.concurrency !== undefined ? { concurrency: step.map.concurrency } : {}),
301
+ ...(step.map.reducer !== undefined ? { reducer: step.map.reducer } : {}),
302
+ },
303
+ }
304
+ : {}),
305
+ ...(step.inputs ? { inputs: [...step.inputs] } : {}),
306
+ source: step.source,
307
+ };
308
+ }
309
+ /** `outputs:` in canonical wire order (code-point ascending by name). */
310
+ function sortedByName(entries) {
311
+ return Object.fromEntries(Object.keys(entries)
312
+ .sort(compareCodePoints)
313
+ .map((name) => [name, entries[name]]));
314
+ }
315
+ /** `xrefs` entries must be canonical asset refs. */
316
+ function checkCanonicalXrefs(value, line, errors) {
317
+ if (!Array.isArray(value))
318
+ return; // shape already flagged by checkXrefs
319
+ for (const ref of value) {
320
+ try {
321
+ if (typeof ref !== "string")
322
+ throw new Error("non-canonical ref");
323
+ const parsed = parseBundleRef(ref);
324
+ if (parsed.conceptId.includes(":") || ref !== bundleRefToString(parsed))
325
+ throw new Error("non-canonical ref");
326
+ }
327
+ catch {
328
+ errors.push({
329
+ line,
330
+ message: `Workflow frontmatter "xrefs" contains an invalid or non-canonical ref: ${String(ref)}.`,
331
+ });
332
+ }
333
+ }
237
334
  }
238
335
  function bindStepSections(headings, lines, bodyStartLine, totalLines, path, declaredIds, errors) {
239
336
  const sections = new Map();
@@ -425,9 +522,9 @@ function parseParams(ctx, raw) {
425
522
  return Object.keys(params).length > 0 ? params : undefined;
426
523
  }
427
524
  /**
428
- * `outputs:` (P3b, spec §4.2): named, optionally schema-validated projections
525
+ * `outputs:`: named, optionally schema-validated projections
429
526
  * of step artifacts, exported when the run completes. Symmetrical with
430
- * `parseParams` above (B-N4) — same authoring surface, same name grammar,
527
+ * `parseParams` above — same authoring surface, same name grammar,
431
528
  * same schema-subset validator, same per-schema byte bound — but each entry
432
529
  * is a STRUCTURED `{from, schema?}` mapping rather than a bare JSON Schema.
433
530
  *
@@ -435,7 +532,7 @@ function parseParams(ctx, raw) {
435
532
  * `steps.<id>.output(.<seg>)*` reference (never `params.<name>` — an output
436
533
  * projects a step artifact, never a param, B-07). Whether the named step is
437
534
  * actually DECLARED in this document is a semantic, cross-step check left to
438
- * `ir/compile.ts`'s reference validation (B-06), mirroring how `inputs[]` /
535
+ * `compile.ts`'s `checkWorkflowPlan`, mirroring how `inputs[]` /
439
536
  * `map.over` / `route.input` already split "syntax here, semantics there".
440
537
  */
441
538
  function parseOutputs(ctx, raw) {
@@ -1,4 +1,5 @@
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
- export {};
4
+ /** Stored plans record this; earlier releases wrote 4 and 5, which still decode. */
5
+ export const WORKFLOW_PLAN_VERSION = 6;
@@ -2,17 +2,15 @@
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
- * Show + indexing renderer for peer workflow sources. `.md` and `.yml` both
6
- * compile through `compileWorkflowSource` into source IR v1, which is projected
7
- * down to the public `ShowResponse` shape and into search hints for the indexer,
8
- * including a compact per-step orchestration summary (engine/model or an exec
9
- * unit's argv, `map.over` reference, route table) when the step declares one.
5
+ * Show renderer for peer workflow sources. `.md` and `.yml` both compile
6
+ * through `compileWorkflowSource` into a plan, which is projected down to the
7
+ * public `ShowResponse` shape, including a compact per-step orchestration
8
+ * summary (engine/model or an exec unit's argv, `map.over` reference, route
9
+ * table) when the step declares one.
10
10
  */
11
11
  import { displayRef } from "../core/asset/resolve-ref.js";
12
12
  import { UsageError } from "../core/errors.js";
13
- import { projectExecCore } from "./program/schema.js";
14
- import { compileWorkflowSource } from "./source-ir/compile.js";
15
- import { sourceStepInstructions, workflowShellCommand } from "./source-ir/program.js";
13
+ import { compileWorkflowSource, routeDescription, workflowStepInstructions } from "./compile.js";
16
14
  function shellQuote(value) {
17
15
  return `'${value.replace(/'/g, `'\\''`)}'`;
18
16
  }
@@ -26,36 +24,18 @@ function deriveName(ctx) {
26
24
  const ext = ctx.relPath.lastIndexOf(".");
27
25
  return ext > 0 ? ctx.relPath.slice(0, ext) : ctx.relPath;
28
26
  }
29
- function loadSourceIr(ctx) {
27
+ function loadPlan(ctx) {
30
28
  const result = compileWorkflowSource(ctx.content(), { path: ctx.relPath, workspaceRoot: ctx.stashRoot });
31
29
  if (result.ok)
32
- return result.ir;
30
+ return result.plan;
33
31
  const summary = result.errors.map((e) => `${e.path}:${e.line} — ${e.message}`).join("\n");
34
32
  throw new UsageError(`Workflow has errors:\n${summary}`);
35
33
  }
36
- /**
37
- * The instruction text a step contributes to the flat step projection. A
38
- * route step has no body requirement, so a deterministic description of the
39
- * routing table stands in when it declares no section (the spine still needs
40
- * a non-empty instructions string).
41
- */
42
- function stepInstructions(step) {
43
- const instructions = sourceStepInstructions(step);
44
- if (instructions)
45
- return instructions;
46
- if (step.route) {
47
- const branches = step.route.branches.map((b) => `"${b.match}" -> ${b.stepId}`);
48
- if (step.route.defaultStepId !== undefined)
49
- branches.push(`default -> ${step.route.defaultStepId}`);
50
- return `Route on ${step.route.input}: ${branches.join(", ")}.`;
51
- }
52
- return "";
53
- }
54
34
  /** Project the `params` block into the flat `WorkflowParameter` list. */
55
- function projectParameters(sourceIr) {
56
- if (!sourceIr.params)
35
+ function projectParameters(plan) {
36
+ if (!plan.paramSchemas)
57
37
  return undefined;
58
- const parameters = Object.entries(sourceIr.params).map(([name, schema]) => {
38
+ const parameters = Object.entries(plan.paramSchemas).map(([name, schema]) => {
59
39
  const description = schema.description;
60
40
  return { name, ...(typeof description === "string" && description !== "" ? { description } : {}) };
61
41
  });
@@ -91,15 +71,9 @@ function projectParameters(sourceIr) {
91
71
  * already carries whole.
92
72
  */
93
73
  function summarizeStepOrchestration(step, defaults) {
94
- const unit = step.unit;
95
- const exec = step.exec
96
- ? step.exec
97
- : step.run !== undefined
98
- ? {
99
- command: workflowShellCommand(step.shell ?? "sh", step.run),
100
- ...(step.workingDirectory ? { cwd: step.workingDirectory } : {}),
101
- }
102
- : undefined;
74
+ const spec = step.spec;
75
+ const unit = spec?.unit;
76
+ const exec = spec?.exec;
103
77
  const engine = exec ? undefined : (unit?.engine ?? defaults?.engine);
104
78
  const model = exec ? undefined : (unit?.model ?? defaults?.model);
105
79
  const timeoutMs = unit?.timeoutMs !== undefined ? unit.timeoutMs : defaults?.timeoutMs;
@@ -107,25 +81,24 @@ function summarizeStepOrchestration(step, defaults) {
107
81
  ...(engine !== undefined ? { engine } : {}),
108
82
  ...(model !== undefined ? { model } : {}),
109
83
  ...(timeoutMs !== undefined ? { timeoutMs } : {}),
110
- // Same projection the draft and the frozen plan use, so what `show` prints
111
- // cannot drift from what runs.
112
- ...(exec ? { exec: projectExecCore(exec) } : {}),
113
- ...(step.map
84
+ // Same projection the frozen plan uses, so what `show` prints cannot drift from what runs.
85
+ ...(exec ? { exec: { ...exec } } : {}),
86
+ ...(spec?.map
114
87
  ? {
115
88
  fanOut: {
116
- over: step.map.over,
117
- ...(step.map.concurrency !== undefined ? { concurrency: step.map.concurrency } : {}),
118
- reducer: step.map.reducer ?? "collect",
89
+ over: spec.map.over,
90
+ ...(spec.map.concurrency !== undefined ? { concurrency: spec.map.concurrency } : {}),
91
+ reducer: spec.map.reducer ?? "collect",
119
92
  },
120
93
  }
121
94
  : {}),
122
- ...(unit?.output !== undefined || step.output !== undefined ? { hasSchema: true } : {}),
95
+ ...(unit?.output !== undefined || step.outputSchema !== undefined ? { hasSchema: true } : {}),
123
96
  ...(unit?.env !== undefined ? { env: [...unit.env] } : {}),
124
97
  ...(step.route
125
98
  ? {
126
99
  route: {
127
100
  input: step.route.input,
128
- branches: step.route.branches.map((b) => ({ match: b.match, stepId: b.stepId })),
101
+ branches: Object.entries(step.route.when).map(([match, stepId]) => ({ match, stepId })),
129
102
  ...(step.route.defaultStepId !== undefined ? { defaultStepId: step.route.defaultStepId } : {}),
130
103
  },
131
104
  }
@@ -133,20 +106,15 @@ function summarizeStepOrchestration(step, defaults) {
133
106
  };
134
107
  return Object.keys(summary).length > 0 ? summary : undefined;
135
108
  }
136
- function projectStepDefinitions(sourceIr) {
137
- const flattened = sourceIr.jobs.flatMap((job) => job.steps.map((step) => ({ jobId: job.id, step })));
138
- const counts = new Map();
139
- for (const { step } of flattened)
140
- counts.set(step.id, (counts.get(step.id) ?? 0) + 1);
141
- return flattened.map(({ jobId, step }, sequenceIndex) => {
142
- const id = (counts.get(step.id) ?? 0) > 1 ? `${jobId}-${step.id}` : step.id;
143
- const orchestration = summarizeStepOrchestration(step, sourceIr.defaults);
109
+ function projectStepDefinitions(plan) {
110
+ return plan.steps.map((step, sequenceIndex) => {
111
+ const orchestration = summarizeStepOrchestration(step, plan.defaults);
112
+ const instructions = workflowStepInstructions(step);
144
113
  return {
145
- id,
146
- // No titles in the shared source IR — a step IS its id.
147
- title: step.id,
148
- instructions: stepInstructions(step),
149
- ...(step.gate?.rubric?.trim() ? { completionCriteria: [step.gate.rubric] } : {}),
114
+ id: step.stepId,
115
+ title: step.stepId,
116
+ instructions: instructions || (step.route ? routeDescription(step.route) : ""),
117
+ ...(step.gate.criteria.length > 0 ? { completionCriteria: [...step.gate.criteria] } : {}),
150
118
  sequenceIndex,
151
119
  ...(orchestration ? { orchestration } : {}),
152
120
  };
@@ -156,26 +124,25 @@ export const workflowMdRenderer = {
156
124
  name: "workflow-md",
157
125
  buildShowResponse(ctx) {
158
126
  const name = deriveName(ctx);
159
- const sourceIr = loadSourceIr(ctx);
127
+ const plan = loadPlan(ctx);
160
128
  // WI-8.5b (display flip): the `akm workflow run <ref>` action is DISPLAY
161
129
  // output — its spelling follows the D-R5 display rule (`displayRef`). A
162
130
  // primary/default-bundle workflow renders the SHORT conceptId
163
131
  // (`workflows/<name>`); a named source qualifies it as
164
132
  // (`<bundle>//workflows/<name>`).
165
133
  const ref = displayRef({ type: "workflow", name, bundleId: ctx.origin }, ctx.defaultBundle);
166
- const parameters = projectParameters(sourceIr);
134
+ const parameters = projectParameters(plan);
167
135
  return {
168
136
  type: "workflow",
169
137
  name,
170
138
  path: ctx.absPath,
171
139
  action: buildWorkflowAction(ref),
172
- ...(sourceIr.preamble ? { content: sourceIr.preamble } : {}),
173
- description: sourceIr.description,
174
- // No authored title in the shared source IR — the asset's human name is
175
- // its `description`/H1 like any other asset; this is its canonical name.
140
+ ...(plan.preamble ? { content: plan.preamble } : {}),
141
+ description: plan.description,
142
+ // No authored title — the asset's human name is its `description`/H1; this is its canonical name.
176
143
  workflowTitle: name,
177
144
  ...(parameters ? { parameters: parameters.map((p) => p.name), workflowParameters: parameters } : {}),
178
- steps: projectStepDefinitions(sourceIr),
145
+ steps: projectStepDefinitions(plan),
179
146
  };
180
147
  },
181
148
  };
@@ -6,18 +6,8 @@ export const WORKFLOW_MAX_SOURCE_BYTES = 1024 * 1024;
6
6
  export const WORKFLOW_MAX_INSTRUCTION_BYTES = 256 * 1024;
7
7
  export const WORKFLOW_MAX_SCHEMA_BYTES = 256 * 1024;
8
8
  export const WORKFLOW_MAX_EXTRA_PARAMS_BYTES = 64 * 1024;
9
- // ── Dispatch-significant bounds shared across validation layers ──────────────
10
- //
11
- // Defined ONCE here so the enforcement layers cannot drift:
12
- // 1. the parser (`../parser.ts`) — line-anchored authoring-time errors,
13
- // 2. the published JSON Schema (`schemas/akm-workflow.json`) — mirrored
14
- // `maximum`/`pattern`/`maxLength` values, pinned against these constants
15
- // by `tests/integration/workflows/schema-drift.test.ts`,
16
- // 3. the strict frozen-plan decoder (`./ir/schema.ts`) — the corruption
17
- // gate for persisted plans.
18
- // A bound enforced only by the decoder surfaces as a terse, unlocated
19
- // "Invalid frozen workflow plan" at `workflow run` — after lint and
20
- // `workflow create` already said the document was fine.
9
+ // ── Bounds the parser enforces, mirrored by schemas/akm-workflow.json ────────
10
+ // (pinned by tests/workflows/schema-drift.test.ts)
21
11
  /** Max per-step map fan-out concurrency (also the run-level concurrency ceiling). */
22
12
  export const WORKFLOW_MAX_CONCURRENCY = 64;
23
13
  /** Max timeout in milliseconds (setTimeout's 32-bit signed ceiling: 2^31-1, ~24.8 days). */
@@ -26,69 +16,21 @@ export const WORKFLOW_MAX_TIMEOUT_MS = EXECUTION_MAX_TIMEOUT_MS;
26
16
  export const WORKFLOW_ENGINE_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
27
17
  export const WORKFLOW_MAX_ENGINE_NAME_LENGTH = 63;
28
18
  // ── exec (shell) unit bounds ─────────────────────────────────────────────────
29
- /**
30
- * Grammar for an env var NAME in `pass_env:`. Matches the frozen-plan
31
- * `envPassthrough` grammar in `ir/schema.ts` so both allowlist surfaces accept
32
- * exactly the same identifiers.
33
- */
19
+ /** Grammar for an env var NAME (`pass_env:`, step `env:` keys, task env). */
34
20
  export const WORKFLOW_ENV_VAR_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;
35
21
  /**
36
- * Default wall-clock timeout for an exec unit that declares no `timeout:` and
37
- * inherits no document `defaults.timeout`.
38
- *
39
- * 10 minutes matches `DEFAULT_LLM_TIMEOUT_MS`. Unlike an agent harness (which
40
- * owns its own lifetime, hence `DEFAULT_AGENT_TIMEOUT_MS === null`), a shell
41
- * command has NO lifetime discipline of its own: an unbounded default would let
42
- * a hung `npm install` or an interactive prompt wedge a workflow run forever.
43
- * Authors who genuinely need an unbounded command write `timeout: none`.
22
+ * Default timeout for an exec unit with no `timeout:` of its own or in
23
+ * `defaults` (a command has no lifetime discipline; `timeout: none` opts out).
44
24
  */
45
25
  export const DEFAULT_EXEC_TIMEOUT_MS = 600_000;
46
26
  /**
47
- * Max BYTES of ONE captured pipe an exec unit RETAINS in memory (stdout and
48
- * stderr are retained separately).
49
- *
50
- * This is a RETENTION cap, not a permission to run. Without it the capture is
51
- * bounded only by the command's exit or the wall timeout, so a command that
52
- * writes continuously (`yes`, a verbose test loop) grows a string in the akm
53
- * process until the host runs out of memory — and the default budget gives it
54
- * ten minutes to do so. What the cap buys is a BOUND where there was none: the
55
- * retained prefix is promoted into the unit's outcome text and every outcome is
56
- * held until the step reduces, so the worst case is (units in the STEP × this
57
- * cap) — the step's width, not the in-flight width, is what sizes it.
58
- *
59
- * On reaching the cap the reader switches to DRAIN-AND-DISCARD: it keeps
60
- * pulling from the pipe (so the child never blocks on backpressure) and stops
61
- * RETAINING. The command therefore runs to completion and its real exit code
62
- * stands — a passing-but-chatty test suite is no longer failed over log volume.
63
- * What overflow costs is honesty about the artifact, and that is paid two ways
64
- * in `exec/exec-unit.ts`:
65
- *
66
- * - no declared `output:` schema → the unit succeeds and its artifact carries
67
- * an unmistakable {@link WORKFLOW_EXEC_OUTPUT_TRUNCATED_MARKER} block naming
68
- * the total and retained byte counts, so truncated text can never be
69
- * mistaken for the whole output;
70
- * - a declared `output:` schema → the unit fails `exec_output_limit`, because
71
- * validating a truncated JSON prefix is meaningless and promoting it would
72
- * corrupt every downstream reference. That is the residual failure the cap
73
- * genuinely justifies.
74
- *
75
- * 8 MiB is deliberately generous, so any output a step is likely to produce
76
- * fits many times over and an ordinary full test/build log is nowhere near
77
- * it.
27
+ * Max bytes of one captured pipe an exec unit retains in memory. Past it the
28
+ * reader drains and discards, so the command still runs to its real exit
29
+ * code; the artifact gets a {@link WORKFLOW_EXEC_OUTPUT_TRUNCATED_MARKER}
30
+ * block, or the unit fails `exec_output_limit` if it declared an `output:` schema.
78
31
  */
79
32
  export const WORKFLOW_MAX_EXEC_OUTPUT_BYTES = 8 * 1024 * 1024;
80
- /**
81
- * Marker stamped on an exec artifact that was RETAINED ONLY IN PART because the
82
- * command wrote past {@link WORKFLOW_MAX_EXEC_OUTPUT_BYTES}.
83
- *
84
- * Deliberately ugly and unique: a truncated value must NEVER be mistakable
85
- * for a complete one by a downstream `steps.<id>.output` reference, by a gate
86
- * judge, by `akm workflow status`, or by a human reading the row. The
87
- * artifact is TEXT here rather than a JSON value, so the marker is appended
88
- * as a trailing block instead of replacing the value with an envelope: the
89
- * retained prefix is still genuinely useful (it is the head of a real log),
90
- * and the block says exactly how much is missing.
91
- */
33
+ /** Appended to an exec artifact retained only in part, so it is never mistaken for complete output. */
92
34
  export const WORKFLOW_EXEC_OUTPUT_TRUNCATED_MARKER = "__akm_exec_output_truncated__";
93
35
  // Per-var: Win32 `SetEnvironmentVariable` caps one variable at 32 767 UTF-16
94
36
  // code units; measuring UTF-8 bytes is conservative in the right direction.
@@ -116,65 +58,15 @@ const EXEC_CONTEXT_LIMITS_POSIX = {
116
58
  export function execContextLimits(platform = process.platform) {
117
59
  return platform === "win32" ? EXEC_CONTEXT_LIMITS_WIN32 : EXEC_CONTEXT_LIMITS_POSIX;
118
60
  }
119
- /**
120
- * Max characters of the per-unit human diagnostic — the `error`/stderr text
121
- * journaled on a failed unit row and rendered by `akm workflow status --units`.
122
- *
123
- * ONE constant for the write side (`exec/native-executor.ts`, which clips before
124
- * journaling) and the read side (`runtime/runs.ts`, which clips whatever a row
125
- * already holds), so a diagnostic can never be stored larger than the surface
126
- * that displays it. Long enough for a real stack trace or a compiler's error
127
- * block; short enough that a runaway command cannot turn the journal into its
128
- * log file.
129
- */
61
+ /** Max characters of a unit's journaled diagnostic, on both the write and the display side. */
130
62
  export const WORKFLOW_UNIT_DIAGNOSTIC_CLIP = 2_000;
131
- /**
132
- * Truncate to `max` chars with an ellipsis marker.
133
- *
134
- * Lives with the bounds rather than with either caller: the write side
135
- * (`exec/step-work.ts`) and the read side (`runtime/runs.ts`) clip against the
136
- * same constants, and `runtime/runs.ts` cannot take the helper from
137
- * `exec/step-work.ts` — that module imports `runtime/runs.ts`.
138
- */
63
+ /** Truncate to `max` chars with an ellipsis marker. */
139
64
  export function clip(text, max) {
140
65
  return text.length > max ? `${text.slice(0, max)}…` : text;
141
66
  }
142
- // ── Persistence bounds ───────────────────────────────────────────────────────
143
- //
144
- // `workflow_run_steps.evidence_json` is persisted WHOLE and unclipped. It used
145
- // to be capped at 1 MiB, past which the row was replaced by a marked
146
- // truncation envelope — but the run still looked successful, and the NEXT
147
- // invocation (a resume, or any later step referencing the artifact) failed
148
- // permanently: the value was gone from the only place a resumed run can read
149
- // it from, so every prior paid step had to be re-run from scratch. SQLite has
150
- // no practical row-size problem here (its own ceiling is ~1 GB), so there is
151
- // nothing this cap protected that a full write does not already handle
152
- // correctly. `JSON.stringify` throwing on genuinely unserializable evidence
153
- // is unchanged.
154
67
  export function utf8Bytes(value) {
155
68
  return Buffer.byteLength(value, "utf8");
156
69
  }
157
70
  export function jsonBytes(value) {
158
71
  return utf8Bytes(JSON.stringify(value));
159
72
  }
160
- // ── Recursive child-workflow composition bounds (spec docs/plans/specs/
161
- // ── p3a-plan-v5-child-freeze.md §4.5, A-N6) ──────────────────────────────────
162
- //
163
- // Composition DEPTH is unbounded — `assertNoCompositionCycle` (freeze/targets/
164
- // child-workflow.ts) already makes infinite composition mathematically
165
- // impossible, so a depth ceiling on top of it only bounded how many
166
- // legitimately distinct workflows an author could nest.
167
- //
168
- // This section used to also cap the AGGREGATE canonical-JSON bytes of every
169
- // embedded child plan in one root freeze (`WORKFLOW_MAX_EMBEDDED_CHILD_PLAN_BYTES`,
170
- // enforced in `freeze/targets/child-workflow.ts` and re-checked on every
171
- // decode in `ir/schema-v4.ts`'s `decodeChildWorkflowTarget`), alongside a
172
- // standalone cap on any one frozen plan's own bytes
173
- // (`WORKFLOW_MAX_PLAN_BYTES`, enforced in `ir/plan-hash.ts` and
174
- // `ir/schema.ts`). A large plan is not a wrong plan: composing several
175
- // substantial workflows together is a legitimate, deliberate authoring
176
- // choice, and SQLite has no practical row-size problem here (its own
177
- // ceiling is ~1 GB) — there was nothing left for either cap to protect that
178
- // `planHash`/`contentHash` verification does not already cover. Both caps
179
- // are removed; the hash/canonical-JSON integrity checks they sat next to
180
- // are unchanged.