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,71 +2,27 @@
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 child workflow executor (P3b, spec docs/plans/specs/p3b-child-executor.md
6
- * §3). `driveChildWorkflowUnit` is the ONE place a `child-workflow`-targeted
7
- * unit is published (idempotently) and driven: no second executor, no second
8
- * scheduler, no second journal writer. It is reached from the ONE dispatch
9
- * seam in `native-executor.ts`'s `dispatchJournaledAttempt` (§3.2).
5
+ * The child workflow executor. `driveChildWorkflowUnit` is the one place a
6
+ * `child-workflow`-targeted unit is published (idempotently) and driven,
7
+ * reached from `native-executor.ts`'s dispatch seam: (1) validate the resolved
8
+ * `with:` bindings against the child's `params:`; (2) derive the deterministic
9
+ * invocation key; (3) publish the child run idempotently; (4) drive it with
10
+ * the same engine as a top-level run unless it is already `blocked`/`failed`;
11
+ * (5) map the child's final status onto this unit's outcome.
10
12
  *
11
- * Ordered algorithm (§3.3): (1) re-verify the embedded child plan's integrity;
12
- * (2) validate the resolved `with:` bindings against the child's declared
13
- * `params:`; (3) derive the deterministic invocation key; (4) publish the
14
- * child run idempotently (`publishChildWorkflowRun`, P3a); (5) read the
15
- * published row's status; (6) drive it with the SAME engine the top-level path
16
- * uses (`runWorkflowSteps`) unless it is already terminal-for-this-invocation
17
- * (`blocked`/`failed`, rows A-22/A-23); (7) map the child's FINAL status
18
- * through §3.4's table onto this unit's outcome.
19
- *
20
- * ## Why `runWorkflowSteps` is reached through a LAZY dynamic import, not a
21
- * static one (B-N5, and the §7 preservation-gate contingency)
22
- *
23
- * `native-executor.ts` must import `driveChildWorkflowUnit` FROM this file
24
- * (the dispatch seam calls it inline, §3.2) — that edge is fixed. `run-
25
- * workflow.ts` imports `native-executor.ts` (existing, load-bearing:
26
- * `executeStepPlan`). If this file ALSO imported `runWorkflowSteps` from
27
- * `./run-workflow` STATICALLY, the three edges would close a static cycle
28
- * (native-executor.ts -> child-workflow.ts -> run-workflow.ts ->
29
- * native-executor.ts), which `tests/architecture/import-cycle-ratchet.test.ts`
30
- * (shrink-only, EMPTY baseline — an absolute gate) forbids outright; adding an
31
- * entry to admit it is not an option the ratchet allows. Per this spec's own
32
- * §7 checklist ("if the ratchet objects, the drive is reached through an
33
- * injected function value, the pattern `ir/freeze-v4.ts`'s `ChildFreezeFn`
34
- * already establishes"), the drive is instead reached through
35
- * {@link driveWithRealEngine}'s `await import("./run-workflow.js")` — a
36
- * DYNAMIC import, invisible to the static-graph cycle ratchet by design (its
37
- * own doc: "dynamic `import()` is excluded because it is the repo's
38
- * sanctioned lazy-loading escape hatch"), registered in
39
- * `DYNAMIC_IMPORT_BASELINE` (scripts/lint-import-cycles.ts) as a genuine
40
- * lazy-load: the vast majority of workflow runs compose no child at all, so
41
- * loading `run-workflow.ts`'s full engine (lease heartbeat, retry loop) is
42
- * deferred until a `child-workflow` unit is actually dispatched. Bun/Node
43
- * cache a module on first dynamic import, so this costs nothing on repeat
44
- * calls, and it resolves the SAME module namespace object a test's
45
- * `import * as runWorkflowModule from "./run-workflow.js"` holds — a
46
- * `spyOn(runWorkflowModule, "runWorkflowSteps")` is therefore observed
47
- * exactly as if this module had imported it statically. Unlike a registered
48
- * function value (which would depend on `run-workflow.ts` having already
49
- * been loaded by SOME OTHER file — fragile for a test file exercising this
50
- * seam in isolation), a dynamic import always resolves correctly regardless
51
- * of what the rest of the process has loaded. This is the ONLY runtime
52
- * indirection in the whole drive: no second executor is created, and
53
- * `driveRun` itself is never exported (B-N5's "no second executor" holds).
13
+ * `runWorkflowSteps` is reached through a lazy dynamic import: a static one
14
+ * would close the cycle native-executor -> child-workflow -> run-workflow ->
15
+ * native-executor, and most runs compose no child at all.
54
16
  */
55
17
  import { randomUUID } from "node:crypto";
56
18
  import { TransientError } from "../../core/errors.js";
57
19
  import { withWorkflowRunsRepo } from "../../storage/repositories/workflow-runs-repository.js";
58
20
  import { validateWorkflowParams } from "../ir/params.js";
59
- import { canonicalPlanJson, computePlanHash } from "../ir/plan-hash.js";
60
- import { frozenStepRows } from "../runtime/plan-classifier.js";
21
+ import { canonicalPlanJson } from "../ir/plan-hash.js";
61
22
  import { workflowRunExportedResult } from "../runtime/run-outputs.js";
23
+ import { frozenStepRows } from "../runtime/run-plan.js";
62
24
  import { computeChildInvocationKey } from "./child-invocation.js";
63
- /**
64
- * The real engine's `runWorkflowSteps`, reached ONLY through a dynamic
65
- * import — see the module doc for why. The return value is deliberately
66
- * unused by the caller: this module always RE-READS the child run row from
67
- * the repository afterward (spec step 7) rather than trusting the driver's
68
- * return value, so no result shape needs to be shared across the seam.
69
- */
25
+ /** The real engine, via a dynamic import (see the module doc). The caller re-reads the child row afterward. */
70
26
  async function driveWithRealEngine(options) {
71
27
  const { runWorkflowSteps } = await import("./run-workflow.js");
72
28
  await runWorkflowSteps(options);
@@ -74,39 +30,19 @@ async function driveWithRealEngine(options) {
74
30
  function errorMessage(err) {
75
31
  return err instanceof Error ? err.message : String(err);
76
32
  }
77
- /** `acquireRunLease`'s exact refusal shape (run-workflow.ts) — matched by text, since this module cannot import that private helper. */
33
+ /** Another live process holds the child run's lock file (run-workflow.ts). */
78
34
  function isLeaseBusyError(err) {
79
- return err instanceof TransientError && err.message.includes("is already being driven by engine");
35
+ return err instanceof TransientError && err.code === "RUN_LEASE_HELD";
80
36
  }
81
- /** §3.4's exact `child_workflow_failed` message. */
37
+ /** The `child_workflow_failed` message. */
82
38
  function childWorkflowFailedMessage(input) {
83
39
  return (`Child workflow run ${input.childRunId} (${input.childRef}) failed at step "${input.childStepId}". ` +
84
40
  `Inspect it with \`akm workflow status ${input.childRunId}\`; the parent run's step ` +
85
41
  `"${input.parentStepId}" cannot advance until it succeeds.`);
86
42
  }
87
- /**
88
- * Steps 1-3 (spec §3.3): integrity re-check, param validation, and the
89
- * deterministic invocation key. Returns either the key or an already-shaped
90
- * `child_workflow_publish_failed` outcome.
91
- */
43
+ /** Param validation and the deterministic invocation key, or a `child_workflow_publish_failed` outcome. */
92
44
  function precheckAndDeriveInvocationKey(input) {
93
45
  const { request, target, ctx, childParams, inputHash } = input;
94
- // Step 1 — integrity re-check (row A-10).
95
- const recomputedPlanHash = computePlanHash(target.frozenPlan);
96
- if (recomputedPlanHash !== target.planHash) {
97
- return {
98
- ok: false,
99
- outcome: {
100
- unitId: request.unitId,
101
- ok: false,
102
- failureReason: "child_workflow_publish_failed",
103
- error: `Workflow step "${request.stepId}" composes child workflow ${target.ref}, but its embedded plan's ` +
104
- `recomputed hash (${recomputedPlanHash}) does not match the frozen target's planHash (${target.planHash}). ` +
105
- "The frozen plan has been corrupted or tampered with.",
106
- },
107
- };
108
- }
109
- // Step 2 — resolved params against the child's declared param schemas (row A-11).
110
46
  const paramErrors = validateWorkflowParams(target.frozenPlan, childParams);
111
47
  if (paramErrors.length > 0) {
112
48
  return {
@@ -120,7 +56,6 @@ function precheckAndDeriveInvocationKey(input) {
120
56
  },
121
57
  };
122
58
  }
123
- // Step 3 — the deterministic invocation key (B-N8: parentUnitId is request.unitId, the parent unit's journalBaseId).
124
59
  return {
125
60
  ok: true,
126
61
  invocationKey: computeChildInvocationKey({
@@ -131,11 +66,8 @@ function precheckAndDeriveInvocationKey(input) {
131
66
  };
132
67
  }
133
68
  /**
134
- * Step 4/5 (spec §3.3): publish the child run idempotently and return the
135
- * pre-drive status read (the returned row IS that read). B-N16: no
136
- * transaction open on this connection — this seam is reached from
137
- * dispatchJournaledAttempt, outside resumeWorkflowRun's and
138
- * completeWorkflowStep's own transactions.
69
+ * Publish the child run idempotently and return the pre-drive row. Runs with
70
+ * no transaction open on this connection (reached from the dispatch seam).
139
71
  */
140
72
  async function publishChildRun(input, invocationKey) {
141
73
  const { request, target, ctx, childParams } = input;
@@ -162,7 +94,6 @@ async function publishChildRun(input, invocationKey) {
162
94
  updatedAt: now,
163
95
  agentHarness: parentRow.agent_harness,
164
96
  agentSessionId: parentRow.agent_session_id,
165
- checkinArmedAt: now,
166
97
  },
167
98
  steps: frozenStepRows(target.frozenPlan).map((row) => ({ ...row, runId: childRunId })),
168
99
  planJson: canonicalPlanJson(target.frozenPlan),
@@ -183,11 +114,9 @@ async function publishChildRun(input, invocationKey) {
183
114
  }
184
115
  }
185
116
  /**
186
- * Step 6 (spec §3.3): drive the published child run with the real engine,
187
- * unless it is already terminal-for-this-invocation (`blocked`/`failed`,
188
- * rows A-22/A-23 — never re-driven, no lease taken). Returns the FINAL row
189
- * (re-read after the drive) or an already-shaped `child_workflow_busy` /
190
- * `child_workflow_drive_failed` outcome.
117
+ * Drive the published child run with the real engine unless it is already
118
+ * `blocked`/`failed` (never re-driven). Returns the final re-read row, or a
119
+ * `child_workflow_busy` / `child_workflow_drive_failed` outcome.
191
120
  */
192
121
  async function driveChildRun(input, childRow) {
193
122
  const { request, target, ctx } = input;
@@ -201,27 +130,13 @@ async function driveChildRun(input, childRow) {
201
130
  ...(ctx.dispatcher ? { dispatcher: ctx.dispatcher } : {}),
202
131
  ...(ctx.maxConcurrency !== undefined ? { maxConcurrency: ctx.maxConcurrency } : {}),
203
132
  ...(ctx.eventSource !== undefined ? { eventSource: ctx.eventSource } : {}),
204
- // B-N6: a no-op, distinct from the real registry drain — the PARENT's
205
- // own `finally` remains the single owner of the process-lifecycle
206
- // drain for the whole process (row A-24).
133
+ // The parent's own `finally` owns the process-lifecycle drain; no maxSteps/maxRetries.
207
134
  disposeDispatchResources: () => { },
208
- // B-N7: deliberately no maxSteps, no maxRetries (rows A-25, A-26).
209
135
  };
210
136
  try {
211
- // The re-read is INSIDE the same try as the drive (code-review round 4,
212
- // finding 1; Review log R1): every throw between here and a mapped
213
- // UnitOutcome — the drive itself, OR this immediately-following
214
- // getRunById — must be caught. Left to escape, it skips past
215
- // dispatchJournaledAttempt's finishJournaledDispatch (no try/catch
216
- // wraps this seam there by design), so the parent's reserved attempt
217
- // row is never finished; the throw then propagates through runUnit
218
- // into concurrentMap's worker (src/core/concurrent.ts), which SWALLOWS
219
- // it and leaves the unit's outcome slot `undefined`, which
220
- // executeStepPlanInConnection then maps to the false diagnostic
221
- // "unit was not dispatched (aborted or scheduler failure)" — losing
222
- // the real cause and leaving the composing attempt row stuck
223
- // `running` forever (unrecoverable by inspection; a resume + re-drive
224
- // reproduces the identical false diagnostic).
137
+ // The re-read stays inside this try: an escaped throw would skip the
138
+ // parent attempt's finish and leave its row `running` with a false
139
+ // "not dispatched" diagnostic.
225
140
  await driveWithRealEngine(driveOptions);
226
141
  const finalRow = (await withWorkflowRunsRepo((repo) => repo.getRunById(childRow.id))) ?? childRow;
227
142
  return { ok: true, finalRow };
@@ -244,22 +159,8 @@ async function driveChildRun(input, childRow) {
244
159
  },
245
160
  };
246
161
  }
247
- // EVERY other throw is mapped here too — never rethrown. §3.5's
248
- // original premise ("classified by the existing dispatch_error
249
- // handling") was false: no handling exists at this seam
250
- // (dispatchJournaledAttempt awaits this call with no try of its own),
251
- // so an uncaught throw here escaped all the way into the scheduler and
252
- // was silently swallowed (R1, above). Reachable causes include the
253
- // child's own LeaseHeartbeat.assertAlive() firing mid-drive,
254
- // requireExecutableWorkflowPlan rejecting a
255
- // tampered child plan_json, and the child's status changing between
256
- // this function's own step 5 read and the drive's internal
257
- // getNextWorkflowStep re-read — none of which match
258
- // isLeaseBusyError's text. child_workflow_drive_failed is a SIBLING of
259
- // child_workflow_publish_failed (row A-10…A-12): same shape, same
260
- // errorMessage(err) content, but naming the child run id and ref
261
- // (already known at this point, unlike the publish arm above) since
262
- // driving — not publishing — is what failed.
162
+ // Every other throw (a repository error mid-drive, a status race) maps
163
+ // to child_workflow_drive_failed — never rethrown into the scheduler.
263
164
  return {
264
165
  ok: false,
265
166
  outcome: {
@@ -278,10 +179,7 @@ async function driveChildRun(input, childRow) {
278
179
  };
279
180
  }
280
181
  }
281
- /**
282
- * `driveChildWorkflowUnit` — the ONE child drive (spec §3.3). Every failure
283
- * before step 6 (publication) produces `child_workflow_publish_failed`.
284
- */
182
+ /** The one child drive. Every failure before the drive produces `child_workflow_publish_failed`. */
285
183
  export async function driveChildWorkflowUnit(input) {
286
184
  const { request, ctx } = input;
287
185
  const precheck = precheckAndDeriveInvocationKey(input);
@@ -304,10 +202,7 @@ export async function driveChildWorkflowUnit(input) {
304
202
  status: finalRow.status,
305
203
  currentStepId: finalRow.current_step_id,
306
204
  };
307
- // A-28/A-29: the child did not reach a terminal state, and the parent's
308
- // own dispatch signal is what aborted it — checked against the RE-READ
309
- // status (not the signal alone) so an already-terminal child is never
310
- // misreported as aborted.
205
+ // Checked against the re-read status, so an already-terminal child is never misreported as aborted.
311
206
  if (finalRow.status === "active" && ctx.signal?.aborted) {
312
207
  return {
313
208
  unitId: request.unitId,
@@ -351,12 +246,8 @@ export async function driveChildWorkflowUnit(input) {
351
246
  childRun: childRunSummary,
352
247
  };
353
248
  default:
354
- // The child's own gate loop exhausted without reaching a terminal
355
- // status (a genuine gate rejection on the child's own step, never
356
- // reached by this phase's fixtures — B-N7 forwards no maxSteps/
357
- // maxRetries, so nothing else can leave a driven child non-terminal
358
- // without an abort). Treated conservatively as a failure so the
359
- // parent never silently advances on an unresolved child.
249
+ // A driven child left non-terminal (its own gate loop exhausted) fails
250
+ // the unit, so the parent never advances on an unresolved child.
360
251
  return {
361
252
  unitId: request.unitId,
362
253
  ok: false,
@@ -2,37 +2,17 @@
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 ONE redaction contract every frozen-workflow dispatch is held to before
6
- * anything about its outcome reaches durable state.
7
- *
8
- * This is a LEAF module on purpose. Both dispatch paths need it — the unit path
9
- * (`exec/native-executor.ts`) and the gate-judge path (`exec/frozen-judge.ts`) —
10
- * and the judge path is reached from `runtime/runs.ts`, which the executor's own
11
- * dependency chain runs back into. Keeping the helpers here (importing only
12
- * `core/redaction` plus erased types) means the judge can reuse the exact unit
13
- * scrub without opening a runtime import cycle.
14
- *
15
- * @module workflows/exec/dispatch-redaction
5
+ * The one redaction contract every frozen-workflow dispatch (unit or gate
6
+ * judge) is held to before anything about its outcome reaches durable state.
7
+ * A leaf module, so the judge path reuses it without an import cycle.
16
8
  */
17
9
  import { collectSensitiveValues, isEnvPassthroughValueSafeToExpose, redactSensitiveText, redactSensitiveValue, } from "../../core/redaction.js";
18
10
  import { lookupApiKeyFileValue, lookupApiKeySecretRefValue } from "../../integrations/agent/engine-resolution.js";
19
11
  /**
20
- * Every exact value that must never survive into the journal from ONE frozen
21
- * dispatch: the resolved `env` bindings injected into the child, the selected
22
- * engine's (and its SDK fallback's) credential — an env value, a file-backed
23
- * one (#905), or a secret-store-backed one (#953) — and any `envPassthrough`
24
- * value the redaction policy does not consider safe to expose.
25
- *
26
- * Shared by the unit path and the gate-judge path. There is deliberately ONE
27
- * collector: a second, parallel implementation is exactly how a dispatch path
28
- * silently loses the scrub.
29
- *
30
- * The credential values are read from `process.env` (or disk / the secret
31
- * store, for a file- or store-backed one) AT CALL TIME, so a caller must
32
- * collect no earlier than the dispatch whose outcome it scrubs. A snapshot
33
- * taken when the dispatch was merely *planned* can predate a credential the
34
- * dispatch then resolves live, leaving the exact value it must remove out of
35
- * the set.
12
+ * Every exact value that must never reach the journal from one dispatch: the
13
+ * resolved `env` bindings, the engine's (and SDK fallback's) credential from
14
+ * env, file, or secret store, and unsafe passthrough values. Read at call time,
15
+ * so collect no earlier than the dispatch being scrubbed.
36
16
  */
37
17
  export function collectWorkflowDispatchSensitiveValues(dispatch, env) {
38
18
  const values = new Set([...Object.values(env ?? {}), ...(dispatch.sensitiveValues ?? [])]);
@@ -87,18 +67,8 @@ export function collectWorkflowDispatchSensitiveValues(dispatch, env) {
87
67
  return collectSensitiveValues(values);
88
68
  }
89
69
  /**
90
- * Scrub a dispatch outcome before ANYTHING about it is journaled.
91
- *
92
- * The `failureReason` downgrade is part of the contract: if redaction ALTERED
93
- * the reason, the reason itself carried a secret, and the persisted failure
94
- * vocabulary must not become a side channel for it.
95
- *
96
- * Structurally typed over `{ failureReason? }` rather than importing
97
- * `UnitOutcome` from `step-work.ts`: this module must stay a LEAF, and even an
98
- * erased `import type` edge here would put `frozen-judge → step-work →
99
- * runtime/runs → frozen-judge` back on the static import graph (the
100
- * import-cycle ratchet is shrink-only). Callers keep their exact outcome type
101
- * through the generic.
70
+ * Scrub a dispatch outcome before anything about it is journaled. A
71
+ * `failureReason` the scrub altered carried a secret, so it is downgraded.
102
72
  */
103
73
  export function redactUnitOutcome(outcome, sensitiveValues) {
104
74
  const redacted = redactSensitiveValue(outcome, sensitiveValues);
@@ -107,13 +77,7 @@ export function redactUnitOutcome(outcome, sensitiveValues) {
107
77
  }
108
78
  return redacted;
109
79
  }
110
- /**
111
- * Scrub a value THROWN out of a dispatch. A rejection is as durable as a
112
- * resolved failure — the message becomes the blocked step's notes — so it goes
113
- * through the same value set. Re-wrapped ONLY when redaction actually changed
114
- * the message, so an untouched throw keeps its original object (and its type,
115
- * which callers branch on) byte-identical.
116
- */
80
+ /** Scrub a value thrown out of a dispatch; re-wrapped only when the scrub changed its message. */
117
81
  function redactDispatchError(err, sensitiveValues) {
118
82
  if (sensitiveValues.length === 0 || !(err instanceof Error))
119
83
  return err;
@@ -125,13 +89,9 @@ function redactDispatchError(err, sensitiveValues) {
125
89
  return replacement;
126
90
  }
127
91
  /**
128
- * Wrap a dispatcher so BOTH its exits are scrubbed with the request's own
129
- * `sensitiveValues` — the outcome it resolves to and the error it throws — so a
130
- * caller holding the wrapped dispatcher cannot observe (or journal) either one
131
- * unredacted. Used at seams whose results head STRAIGHT for durable state (the
132
- * gate judge, on both its agent and its llm branch); the unit path instead
133
- * scrubs once at its own journal boundary (`dispatchJournaledAttempt`), AFTER
134
- * the structured-output parse loop has seen the raw text.
92
+ * Wrap a dispatcher so both its outcome and its thrown error are scrubbed with
93
+ * the request's `sensitiveValues` (the gate judge; the unit path scrubs at its
94
+ * own journal boundary after the structured-output parse).
135
95
  */
136
96
  export function withDispatchRedaction(inner) {
137
97
  return async (request, feedback) => {
@@ -0,0 +1,98 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /**
5
+ * Materialize a unit's frozen environment at dispatch. Env-file values are the
6
+ * one deliberately live input to a frozen plan: freeze records each ref's
7
+ * owner, key set and `${secret:…}` names; the values (and the secrets they
8
+ * name) are read here, per dispatch, and nothing is returned until every read
9
+ * has succeeded.
10
+ */
11
+ import fs from "node:fs";
12
+ import path from "node:path";
13
+ import dotenv from "dotenv";
14
+ import { assetPathForName } from "../../core/asset/asset-placement.js";
15
+ import { compareCodePoints, isWithin } from "../../core/common.js";
16
+ import { NotFoundError, UsageError } from "../../core/errors.js";
17
+ export const SECRET_TOKEN_RE = /\$\{secret:([A-Za-z0-9_./-]+)\}/g;
18
+ export function materializeFrozenWorkflowEnvironment(descriptors, options = {}) {
19
+ const values = {};
20
+ const sensitive = new Set();
21
+ const audits = [];
22
+ for (const descriptor of descriptors) {
23
+ if (descriptor.kind === "literal") {
24
+ values[descriptor.name] = descriptor.value;
25
+ if (descriptor.value)
26
+ sensitive.add(descriptor.value);
27
+ continue;
28
+ }
29
+ if (descriptor.kind === "pass-through") {
30
+ const value = options.readPassThrough ? options.readPassThrough(descriptor.name) : process.env[descriptor.name];
31
+ if (value !== undefined) {
32
+ values[descriptor.name] = value;
33
+ if (value)
34
+ sensitive.add(value);
35
+ }
36
+ continue;
37
+ }
38
+ const source = options.readEnvFile ? options.readEnvFile(descriptor) : readEnvFile(descriptor.owner);
39
+ const parsed = dotenv.parse(typeof source === "string" ? source : Buffer.from(source));
40
+ const keys = Object.keys(parsed).sort(compareCodePoints);
41
+ if (!sameStrings(keys, descriptor.keys))
42
+ invalid(`environment ${descriptor.ref} key set changed after it was frozen`);
43
+ const secretValues = new Map();
44
+ for (const name of descriptor.secretNames) {
45
+ const raw = options.readSecret ? options.readSecret({ name, descriptor }) : readSecret(descriptor.owner, name);
46
+ if (raw === undefined) {
47
+ throw new NotFoundError(`Environment ${descriptor.ref} references missing secret ${name}; nothing was materialized.`, "FILE_NOT_FOUND");
48
+ }
49
+ const value = typeof raw === "string" ? raw : Buffer.from(raw).toString("utf8");
50
+ secretValues.set(name, value);
51
+ if (value)
52
+ sensitive.add(value);
53
+ }
54
+ for (const [key, rawValue] of Object.entries(parsed)) {
55
+ const resolved = rawValue.replace(SECRET_TOKEN_RE, (_token, name) => {
56
+ const secret = secretValues.get(name);
57
+ if (secret === undefined)
58
+ invalid(`environment ${descriptor.ref} secret ${name} was not frozen`);
59
+ return secret;
60
+ });
61
+ values[key] = resolved;
62
+ if (resolved)
63
+ sensitive.add(resolved);
64
+ }
65
+ audits.push({
66
+ eventType: "env_access",
67
+ ref: descriptor.ref,
68
+ keys: [...descriptor.keys],
69
+ secretNames: [...descriptor.secretNames],
70
+ });
71
+ }
72
+ return { values, sensitiveValues: [...sensitive], audits };
73
+ }
74
+ function readEnvFile(owner) {
75
+ if (!isWithin(owner.requestedPath, owner.requestedRoot))
76
+ invalid(`env file ${owner.relativePath} escapes its bundle`);
77
+ return fs.readFileSync(owner.requestedPath);
78
+ }
79
+ function readSecret(owner, name) {
80
+ const secretsRoot = path.join(owner.requestedRoot, "secrets");
81
+ const secretPath = assetPathForName("secret", secretsRoot, name);
82
+ if (!isWithin(secretPath, secretsRoot))
83
+ invalid(`secret name ${name} escapes its bundle`);
84
+ try {
85
+ return fs.readFileSync(secretPath);
86
+ }
87
+ catch (cause) {
88
+ if (cause.code === "ENOENT")
89
+ return undefined;
90
+ throw cause;
91
+ }
92
+ }
93
+ function sameStrings(left, right) {
94
+ return left.length === right.length && left.every((value, index) => value === right[index]);
95
+ }
96
+ function invalid(message) {
97
+ throw new UsageError(`Invalid frozen workflow environment: ${message}.`, "WORKFLOW_SOURCE_INVALID");
98
+ }