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
@@ -9,18 +9,23 @@
9
9
  * a bundle/adapter/concept id at all — it reads exactly the path it was
10
10
  * given and classifies it.
11
11
  *
12
- * Reuses the exact current-schema router `parseTaskSource`
13
- * (`src/tasks/source/parse-task-source.ts`) used by every other task-source
14
- * reader. Historical v2/v3 documents are deliberately rejected here and
15
- * point to `akm migrate apply`; validation does not embed a second legacy
16
- * parser or perform an in-memory migration.
12
+ * Reuses the exact version-routing shim `parseTaskSource`
13
+ * (`src/tasks/source/parse-task-source.ts`) already applies for every other
14
+ * task-source reader (`akm task sync`'s `compileTaskSources` included) —
15
+ * this module never forks a second parser or a second v2/v3 migration
16
+ * planner. `readBoundedTaskSourceYaml` / `peekTaskSourceVersion` / `own` are
17
+ * the SAME front-end helpers that shim itself calls first; they are used
18
+ * here only to recover the file's ORIGINALLY DECLARED schema version for
19
+ * the report, because `parseTaskSource`'s own `ParsedTaskSource.version` is
20
+ * always `4` post-shim — it cannot answer "was this a v2/v3/v4 file?" on
21
+ * its own once a v2/v3 source has been converted in memory.
17
22
  *
18
23
  * Beyond parsing, this module also runs the SAME two per-source gates
19
24
  * `akm task sync`'s `compileTaskSources` runs before it ever installs a
20
25
  * schedule — `assertTaskScheduleInputsSatisfyContract` and
21
26
  * `assertTaskScheduleCronValid` (both extracted from `scheduler-sync.ts` for
22
27
  * exactly this reuse) — so a file `sync` would reject can never
23
- * be reported `valid` here. Cron dialect is checked against
28
+ * be reported `valid`/`converts` here. Cron dialect is checked against
24
29
  * `backendNameForPlatform()`, the same platform default `sync` falls back
25
30
  * to whenever it has no injected/native-inspected backend to hand (see
26
31
  * `akmTasksAdd`, `src/commands/tasks/tasks.ts`); a bare file was never
@@ -43,12 +48,23 @@
43
48
  * table in its header, extended for the two gates above):
44
49
  * - `valid` — parses as task source v4 directly (declared `version: 4`)
45
50
  * and passes both sync gates.
46
- * - `blocked` — declared `version: 2` or `3`; runtime does not accept
47
- * it and the explicit migrator must rewrite it first.
51
+ * - `converts` — declared `version: 2` or `3`; the deterministic
52
+ * in-memory migrator produced a valid v4 document that
53
+ * passes both sync gates. A declared `version: 4`
54
+ * document whose only defect is a retired
55
+ * `schedule[].enabled` key also reports `converts`
56
+ * (`sourceVersion` is still `4`) — it read through the
57
+ * same in-memory shim, not the direct v4 parse path.
58
+ * - `blocked` — declared `version: 2` or `3`; the migrator itself
59
+ * could not convert it (an ambiguous/unmigratable
60
+ * shape) — the ONLY way `parseTaskSource` ever throws
61
+ * for those two version numbers, so no message-text
62
+ * sniffing is needed to tell this apart from `invalid`.
48
63
  * - `invalid` — the document declares SOME version (`4`, or anything
49
64
  * other than 2/3/4) but fails to parse/validate, OR it
50
- * parsed but fails one of the two sync gates above, OR
51
- * the YAML itself does not parse at all
65
+ * parsed (directly or via a SUCCESSFUL v2/v3
66
+ * conversion) but fails one of the two sync gates
67
+ * above, OR the YAML itself does not parse at all
52
68
  * (a genuine syntax error, not merely a non-task
53
69
  * shape) — reported with the parser's own reason.
54
70
  * - `not-a-task` — the document parses as YAML but never declares a
@@ -62,7 +78,7 @@ import { UsageError } from "../../core/errors.js";
62
78
  import { backendNameForPlatform } from "../../tasks/backends/index.js";
63
79
  import { assertTaskScheduleCronValid, assertTaskScheduleInputsSatisfyContract } from "../../tasks/scheduler-sync.js";
64
80
  import { own, readBoundedTaskSourceYaml } from "../../tasks/source/bounded-document.js";
65
- import { parseTaskSource, peekTaskSourceVersion } from "../../tasks/source/parse-task-source.js";
81
+ import { parseTaskSource, peekTaskSourceVersion, v4ScheduleHasRetiredEnabledKey, } from "../../tasks/source/parse-task-source.js";
66
82
  /** True when `root` is a YAML mapping that itself declares a `version:` key, regardless of that key's type/value. */
67
83
  function declaresVersionKey(root) {
68
84
  return root !== null && typeof root === "object" && !Array.isArray(root) && own(root, "version");
@@ -126,14 +142,11 @@ export async function akmTaskValidate(filePath) {
126
142
  if (!(cause instanceof UsageError))
127
143
  throw cause;
128
144
  const reason = cause.message;
145
+ // `parseTaskSource` only ever throws for a declared version 2/3 via the
146
+ // unmigratable-conversion branch (see this file's header) — no separate
147
+ // message check needed to recognize "blocked" here.
129
148
  if (declaredVersion === 2 || declaredVersion === 3) {
130
- return {
131
- ok: false,
132
- path: resolvedPath,
133
- sourceVersion: declaredVersion,
134
- outcome: "blocked",
135
- reason: `${reason} Run \`akm migrate apply --dry-run\`, review the plan, then run \`akm migrate apply\`.`,
136
- };
149
+ return { ok: false, path: resolvedPath, sourceVersion: declaredVersion, outcome: "blocked", reason };
137
150
  }
138
151
  if (peekFailed) {
139
152
  return { ok: false, path: resolvedPath, outcome: "invalid", reason };
@@ -152,8 +165,11 @@ export async function akmTaskValidate(filePath) {
152
165
  // Success is unreachable from any path that leaves `declaredVersion`
153
166
  // undefined — the router requires a numeric 2/3/4 version to reach here.
154
167
  const sourceVersion = declaredVersion ?? 4;
155
- // The current document parsed; now run the two gates `compileTaskSources`
156
- // applies before accepting it.
168
+ // The document itself parsed (directly, or via a successful v2/v3
169
+ // conversion) — now the two gates `compileTaskSources` runs before
170
+ // accepting it. A violation here is `invalid`, never `blocked`: the
171
+ // migrator already succeeded, so this is the same kind of defect a
172
+ // native v4 document with the identical schedule would have.
157
173
  try {
158
174
  assertTaskScheduleInputsSatisfyContract(parsed.v4, resolvedPath);
159
175
  assertTaskScheduleCronValid(parsed.v4, backend);
@@ -163,12 +179,17 @@ export async function akmTaskValidate(filePath) {
163
179
  throw cause;
164
180
  return { ok: false, path: resolvedPath, sourceVersion, outcome: "invalid", reason: cause.message };
165
181
  }
182
+ // A declared `version: 4` document with a retired `schedule[].enabled`
183
+ // key parsed through `parseTaskSource`'s in-memory shim, not the direct
184
+ // v4 path, even though `sourceVersion` reads `4` — report it the same
185
+ // way a converted v2/v3 document is reported.
186
+ const convertedFromRetiredScheduleEnabled = sourceVersion === 4 && !peekFailed && v4ScheduleHasRetiredEnabledKey(root);
166
187
  const id = path.parse(resolvedPath).name;
167
188
  return {
168
189
  ok: true,
169
190
  path: resolvedPath,
170
191
  sourceVersion,
171
- outcome: "valid",
192
+ outcome: sourceVersion === 2 || sourceVersion === 3 || convertedFromRetiredScheduleEnabled ? "converts" : "valid",
172
193
  resolved: buildResolved(id, parsed.v4),
173
194
  };
174
195
  }
@@ -2,13 +2,11 @@
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
- * `akm workflow plan <ref>` — compile + freeze WITHOUT publishing (P3b, spec
6
- * docs/plans/specs/p3b-child-executor.md §4.6). Zero durable writes, zero
7
- * usage/event rows (row B-48): this module calls exactly the same two
8
- * functions `startWorkflowRun` does to reach a frozen plan
9
- * (`loadWorkflowAsset`, `compileResolveFreezeWorkflowV4`) and NOTHING else —
10
- * never `publishWorkflowRunV4`, `startWorkflowRun`, `warn()`, `appendEvent`,
11
- * or `akmIndex`.
5
+ * `akm workflow plan <ref>` — compile + freeze WITHOUT publishing. Zero
6
+ * durable writes, zero usage/event rows: this module calls exactly the same
7
+ * two functions `startWorkflowRun` does to reach a frozen plan
8
+ * (`loadWorkflowAsset`, `freezeWorkflow`) and NOTHING else — never
9
+ * `publishWorkflowRunV4`, `startWorkflowRun`, `appendEvent`, or `akmIndex`.
12
10
  *
13
11
  * SECRET-FREE, by construction (§4.6's closed print list): a resolved
14
12
  * reference VALUE is never printed (references resolve at pre-attempt, not
@@ -20,9 +18,8 @@
20
18
  */
21
19
  import path from "node:path";
22
20
  import { loadConfig } from "../../core/config/config.js";
23
- import { lowerResolvedExecutionRequest } from "../../integrations/agent/execution-lowering.js";
24
- import { collectWorkflowWarnings } from "../../workflows/ir/compile.js";
25
- import { compileResolveFreezeWorkflowV4 } from "../../workflows/ir/freeze-v4.js";
21
+ import { buildExecutionFromWire } from "../../integrations/agent/execution.js";
22
+ import { freezeWorkflow } from "../../workflows/freeze/freeze.js";
26
23
  import { computePlanHash } from "../../workflows/ir/plan-hash.js";
27
24
  import { loadWorkflowAsset } from "../../workflows/runtime/workflow-asset-loader.js";
28
25
  /** The step's dispatch unit — the map template for a fan-out, else the root unit. Undefined for a route step. */
@@ -49,7 +46,7 @@ function childExportedOutputNames(frozenPlan) {
49
46
  }
50
47
  /**
51
48
  * A `child-workflow` target's `expansion`. Recurses into the embedded
52
- * plan's own steps in the identical shape (§4.6) — `sourceStepsById` is
49
+ * plan's own steps in the identical shape — `usesById` is
53
50
  * omitted for the recursive call because a nested child's own authored
54
51
  * source is not available here (only its already-frozen plan is), so a
55
52
  * task-wrapped step nested inside a child conservatively reports `via:
@@ -67,15 +64,15 @@ function childExpansion(target) {
67
64
  };
68
65
  }
69
66
  /** The task/child expansion boundary for one step (§4.6). */
70
- function stepExpansion(step, frozenTarget, sourceStepsById) {
67
+ function stepExpansion(step, frozenTarget, usesById) {
71
68
  if (frozenTarget?.kind === "child-workflow")
72
69
  return childExpansion(frozenTarget);
73
- const uses = sourceStepsById?.get(step.stepId)?.uses;
70
+ const uses = usesById?.get(step.stepId);
74
71
  if (uses?.startsWith("tasks/"))
75
72
  return { via: "task", taskRef: uses };
76
73
  return { via: "direct" };
77
74
  }
78
- function projectStep(step, sequenceIndex, sourceStepsById) {
75
+ function projectStep(step, sequenceIndex, usesById) {
79
76
  const unit = stepUnit(step);
80
77
  const frozenTarget = unit?.frozenTarget;
81
78
  const kind = step.route ? "route" : step.root?.kind === "map" ? "map" : "unit";
@@ -95,7 +92,7 @@ function projectStep(step, sequenceIndex, sourceStepsById) {
95
92
  judgeEngine: step.gate.frozenJudge ? step.gate.frozenJudge.request.engine.name : null,
96
93
  },
97
94
  ...(step.outputSchema !== undefined ? { outputSchema: step.outputSchema } : {}),
98
- expansion: stepExpansion(step, frozenTarget, sourceStepsById),
95
+ expansion: stepExpansion(step, frozenTarget, usesById),
99
96
  };
100
97
  }
101
98
  /**
@@ -103,30 +100,27 @@ function projectStep(step, sequenceIndex, sourceStepsById) {
103
100
  * from its own already-frozen `request` (the identical computation
104
101
  * `freeze/targets/command.ts`'s `commandResult` already performs at freeze
105
102
  * time and discards) — walked over the whole plan, including gate judges and
106
- * recursively into every embedded child plan. Read-only: `lowerResolvedExecutionRequest`
107
- * takes no config it could write through and dispatches nothing.
103
+ * recursively into every embedded child plan. Read-only: `buildExecutionFromWire`
104
+ * reads no config and dispatches nothing.
108
105
  */
109
106
  function collectLoweringNotices(plan, config) {
110
107
  const notices = [];
111
- const lower = (request) => {
112
- notices.push(...lowerResolvedExecutionRequest(request, config).notices);
108
+ const lower = (target) => {
109
+ notices.push(...buildExecutionFromWire(target).notices);
113
110
  };
114
111
  for (const step of plan.steps) {
115
112
  const unit = stepUnit(step);
116
113
  if (unit) {
117
114
  if (unit.frozenTarget.kind === "command")
118
- lower(unit.frozenTarget.request);
115
+ lower(unit.frozenTarget);
119
116
  else if (unit.frozenTarget.kind === "child-workflow")
120
117
  notices.push(...collectLoweringNotices(unit.frozenTarget.frozenPlan, config));
121
118
  }
122
119
  if (step.gate.frozenJudge)
123
- lower(step.gate.frozenJudge.request);
120
+ lower(step.gate.frozenJudge);
124
121
  }
125
122
  return notices;
126
123
  }
127
- function relativeSourceReadSet(plan) {
128
- return plan.sourceReadSet.map((snapshot) => snapshot.identity.file);
129
- }
130
124
  /**
131
125
  * Compile + freeze `ref` and project the frozen plan into the read-only
132
126
  * `akm workflow plan` envelope. Never publishes, never writes, never warns.
@@ -134,9 +128,9 @@ function relativeSourceReadSet(plan) {
134
128
  export async function akmWorkflowPlan(ref) {
135
129
  const asset = await loadWorkflowAsset(ref);
136
130
  const config = loadConfig();
137
- const frozen = await compileResolveFreezeWorkflowV4(asset, config);
131
+ const frozen = await freezeWorkflow(asset, config);
138
132
  const plan = frozen.plan;
139
- const sourceStepsById = new Map((asset.sourceIr.jobs[0]?.steps ?? []).map((step) => [step.id, step]));
133
+ const usesById = new Map(asset.plan.steps.map((step) => [step.stepId, step.spec?.uses]));
140
134
  const sourceFormat = path.extname(asset.path).toLowerCase() === ".md" ? "markdown" : "github-yaml";
141
135
  return {
142
136
  ok: true,
@@ -151,9 +145,8 @@ export async function akmWorkflowPlan(ref) {
151
145
  ...(plan.budget ? { budget: plan.budget } : {}),
152
146
  ...(plan.params ? { params: plan.params } : {}),
153
147
  ...(plan.outputs ? { outputs: plan.outputs } : {}),
154
- steps: plan.steps.map((step, index) => projectStep(step, index, sourceStepsById)),
155
- sourceReadSet: relativeSourceReadSet(plan),
148
+ steps: plan.steps.map((step, index) => projectStep(step, index, usesById)),
156
149
  notices: collectLoweringNotices(plan, config),
157
- warnings: collectWorkflowWarnings(asset.sourceIr).map((warning) => warning.message),
150
+ warnings: frozen.warnings.map((warning) => warning.message),
158
151
  };
159
152
  }
@@ -15,7 +15,7 @@ import { NotFoundError, TransientError, UsageError } from "../core/errors.js";
15
15
  import { warn } from "../core/warn.js";
16
16
  import { akmIndex } from "../indexer/indexer.js";
17
17
  import { assertWorkflowMarkdownName, createWorkflowAsset, getWorkflowTemplate } from "../workflows/authoring/authoring.js";
18
- import { WORKFLOW_MAX_TIMEOUT_MS } from "../workflows/ir/schema.js";
18
+ import { WORKFLOW_MAX_TIMEOUT_MS } from "../workflows/resource-limits.js";
19
19
  import { abandonWorkflowRun, getWorkflowStatus, listWorkflowRuns, resolveWorkflowRunTarget, resumeWorkflowRun, } from "../workflows/runtime/runs.js";
20
20
  import { akmWorkflowPlan } from "./workflow/plan.js";
21
21
  const workflowStatusCommand = defineJsonCommand({
@@ -174,8 +174,8 @@ const workflowCreateCommand = defineJsonCommand({
174
174
  * `--skip-if-locked` (#948) eligibility: only these two named, retryable
175
175
  * `TransientError` codes (#948 addendum — moved off UsageError, exit 75) turn
176
176
  * a `workflow run` failure into a graceful skip — `RUN_LEASE_HELD` (another
177
- * engine invocation is driving THIS run, `workflow-runs-repository.ts`'s
178
- * single-driver lease) and `STATE_DB_CONTENDED` (an unrelated akm process is
177
+ * live akm process holds THIS run's lock file, `exec/run-workflow.ts`) and
178
+ * `STATE_DB_CONTENDED` (an unrelated akm process is
179
179
  * writing state.db right now, `core/state-db.ts`'s BEGIN IMMEDIATE retry
180
180
  * exhaustion). Every other error — a bad flag, an unresolvable target —
181
181
  * still fails loudly.
@@ -202,7 +202,7 @@ const workflowRunCommand = defineJsonCommand({
202
202
  },
203
203
  "skip-if-locked": {
204
204
  type: "boolean",
205
- description: "If another akm process already holds this run's engine lease, or state.db is busy with another " +
205
+ description: "If another akm process is already driving this run, or state.db is busy with another " +
206
206
  "writer, skip gracefully (exit 0) instead of failing (exit 75). Use for high-frequency scheduled runs " +
207
207
  "so they don't pile up failures while a longer-running invocation is in progress.",
208
208
  default: false,
@@ -192,7 +192,6 @@ const DOCUMENT_JSON_CARRIED_FIELDS = [
192
192
  "cwd",
193
193
  "wikiRole",
194
194
  "sources",
195
- "generation",
196
195
  "evidenceSources",
197
196
  // D2 (#730): the OKF v0.2 provenance `promoteProposal` stamps onto AKM-native
198
197
  // writes (generated/verified/sources, namespaced — see `types.ts`'s
@@ -54,8 +54,7 @@ import path from "node:path";
54
54
  import { isDangerousEnvKey } from "../../../commands/lint/env-key-rules.js";
55
55
  import { parseTaskSource } from "../../../tasks/source/parse-task-source.js";
56
56
  import { taskSourceErrorDetail } from "../../../tasks/source-v3.js";
57
- import { compileWorkflowPlan } from "../../../workflows/ir/compile.js";
58
- import { compileWorkflowSource } from "../../../workflows/source-ir/compile.js";
57
+ import { checkWorkflowPlan, compileWorkflowSource } from "../../../workflows/compile.js";
59
58
  import { conceptIdForStashFile } from "../../asset/resolve-ref.js";
60
59
  import { isAkmRegistryCachePath, scanEnvKeyNames } from "../../common.js";
61
60
  /** Recommended `category` values for facts — `commands/lint/fact-linter.ts:9`. */
@@ -370,7 +369,7 @@ export function workflowFrontendDiagnostics(relPath, raw, parsePath) {
370
369
  }
371
370
  return { errors, warnings };
372
371
  }
373
- const compiled = compileWorkflowPlan(result.ir, path.basename(parsePath, path.extname(parsePath)));
372
+ const compiled = checkWorkflowPlan(result.plan);
374
373
  if (!compiled.ok) {
375
374
  for (const err of compiled.errors) {
376
375
  errors.push({
@@ -56,8 +56,7 @@
56
56
  */
57
57
  import { scanEnvKeyNames } from "../../../commands/env/env.js";
58
58
  import { parseTaskSource } from "../../../tasks/source/parse-task-source.js";
59
- import { compileWorkflowSource } from "../../../workflows/source-ir/compile.js";
60
- import { sourceStepInstructions } from "../../../workflows/source-ir/program.js";
59
+ import { compileWorkflowSource, workflowStepInstructions } from "../../../workflows/compile.js";
61
60
  import { parseFrontmatter } from "../../asset/frontmatter.js";
62
61
  import { parseMarkdownToc } from "../../asset/markdown.js";
63
62
  import { nonEmptyString } from "./shared.js";
@@ -320,19 +319,19 @@ export function foldRecognizedMetadata(rendererName, file) {
320
319
  const result = compileWorkflowSource(file.content(), { path: file.relPath, workspaceRoot: file.stashRoot });
321
320
  if (!result.ok)
322
321
  return out;
323
- const sourceIr = result.ir;
322
+ const plan = result.plan;
324
323
  const hints = new Set();
325
- if (sourceIr.preamble)
326
- hints.add(sourceIr.preamble);
327
- for (const step of sourceIr.jobs.flatMap((job) => job.steps)) {
328
- hints.add(step.id);
329
- hints.add(sourceStepInstructions(step));
330
- if (step.gate?.rubric)
331
- hints.add(step.gate.rubric);
324
+ if (plan.preamble)
325
+ hints.add(plan.preamble);
326
+ for (const step of plan.steps) {
327
+ hints.add(step.stepId);
328
+ hints.add(workflowStepInstructions(step));
329
+ if (step.gate.criteria[0])
330
+ hints.add(step.gate.criteria[0]);
332
331
  }
333
332
  out.searchHints = Array.from(hints).filter(Boolean);
334
- if (sourceIr.params) {
335
- const parameters = Object.entries(sourceIr.params).map(([name, schema]) => {
333
+ if (plan.paramSchemas) {
334
+ const parameters = Object.entries(plan.paramSchemas).map(([name, schema]) => {
336
335
  const description = schema.description;
337
336
  return { name, ...(typeof description === "string" && description ? { description } : {}) };
338
337
  });
@@ -21,7 +21,7 @@
21
21
  */
22
22
  import fs from "node:fs";
23
23
  import path from "node:path";
24
- import { looksLikeGithubWorkflowSource } from "../../../workflows/source-ir/compile.js";
24
+ import { looksLikeGithubWorkflowSource } from "../../../workflows/compile.js";
25
25
  import { parseFrontmatter } from "../../asset/frontmatter.js";
26
26
  import { toPosix } from "../../common.js";
27
27
  import { perTypeValidateChecks, workflowYamlSourceDiagnostics } from "./akm-lint.js";
@@ -4,7 +4,7 @@
4
4
  import { createHash } from "node:crypto";
5
5
  import { isMap, isScalar, parseDocument, visit } from "yaml";
6
6
  import { cloneExecutionJson, cloneExecutionJsonObject, } from "../../execution/json.js";
7
- import { assertSnapshotKeys, snapshotStrictRecord } from "../../execution/record.js";
7
+ import { snapshotStrictRecord } from "../../execution/record.js";
8
8
  import { cloneToolSelection, createAdapterRenderedExecutionSource, } from "../../execution/source.js";
9
9
  import { UsageError } from "../errors.js";
10
10
  import { warnOnce } from "../warn.js";
@@ -137,20 +137,16 @@ function nullableTimeout(value, path) {
137
137
  /** Adapter helper for the common frontmatter fields fixed by the 0.9.2 design. */
138
138
  export function executionDefaultsFromFrontmatter(data, options) {
139
139
  const frontmatter = snapshotStrictRecord(data, "frontmatter");
140
- const optionRecord = snapshotStrictRecord(options, "execution frontmatter projection options");
141
- assertSnapshotKeys(optionRecord, ["kind", "allowTopLevelEngine", "toolsKeys"], "execution frontmatter projection options");
142
- const kind = optionRecord.kind;
140
+ const kind = options.kind;
143
141
  if (kind !== "command" && kind !== "persona") {
144
142
  throw new TypeError("execution frontmatter projection options.kind is invalid");
145
143
  }
146
- const allowTopLevelEngine = Object.hasOwn(optionRecord, "allowTopLevelEngine")
147
- ? optionRecord.allowTopLevelEngine
148
- : false;
144
+ const allowTopLevelEngine = Object.hasOwn(options, "allowTopLevelEngine") ? options.allowTopLevelEngine : false;
149
145
  if (typeof allowTopLevelEngine !== "boolean") {
150
146
  throw new TypeError("execution frontmatter projection options.allowTopLevelEngine must be a boolean");
151
147
  }
152
- const configuredToolsKeys = Object.hasOwn(optionRecord, "toolsKeys")
153
- ? cloneExecutionJson(optionRecord.toolsKeys, "execution frontmatter projection options.toolsKeys")
148
+ const configuredToolsKeys = Object.hasOwn(options, "toolsKeys")
149
+ ? cloneExecutionJson(options.toolsKeys, "execution frontmatter projection options.toolsKeys")
154
150
  : ["tools"];
155
151
  if (!Array.isArray(configuredToolsKeys) || configuredToolsKeys.some((key) => typeof key !== "string" || !key)) {
156
152
  throw new TypeError("execution frontmatter projection options.toolsKeys must be an array of non-empty strings");
@@ -272,43 +268,35 @@ export function executionDefaultsFromFrontmatter(data, options) {
272
268
  }
273
269
  return out;
274
270
  }
275
- function snapshotRendererInput(input) {
276
- const inputSnapshot = snapshotStrictRecord(input, "adapter execution source");
277
- assertSnapshotKeys(inputSnapshot, ["kind", "raw", "identity", "defaults", "extensions"], "adapter execution source");
278
- const identity = snapshotStrictRecord(inputSnapshot.identity, "adapter execution source identity");
279
- assertSnapshotKeys(identity, ["ref", "bundle", "adapter", "file"], "adapter execution source identity");
280
- return { input: inputSnapshot, identity };
281
- }
282
271
  export function renderMarkdownExecutionSource(input) {
283
- const snapshots = snapshotRendererInput(input);
284
- const raw = snapshots.input.raw;
272
+ const raw = input.raw;
285
273
  if (typeof raw !== "string")
286
274
  throw new TypeError("adapter execution source.raw must be a string");
287
- const kind = snapshots.input.kind;
275
+ const kind = input.kind;
288
276
  if (kind !== "command" && kind !== "persona")
289
277
  throw new TypeError("adapter execution source.kind is invalid");
290
- const identityFile = snapshots.identity.file;
291
- const parsed = parseExecutionMarkdown(raw, typeof identityFile === "string" ? identityFile : undefined);
292
- const hasDefaults = Object.hasOwn(snapshots.input, "defaults");
293
- const defaultsProjection = snapshots.input.defaults;
278
+ const identity = input.identity;
279
+ const parsed = parseExecutionMarkdown(raw, identity.file);
280
+ const hasDefaults = Object.hasOwn(input, "defaults");
281
+ const defaultsProjection = input.defaults;
294
282
  const defaults = typeof defaultsProjection === "function" ? defaultsProjection(parsed.data) : defaultsProjection;
295
283
  if (hasDefaults && defaults === undefined) {
296
284
  throw new TypeError("adapter execution source defaults must be omitted or resolve to an object");
297
285
  }
298
- const extensionsProjection = snapshots.input.extensions;
286
+ const extensionsProjection = input.extensions;
299
287
  const extensionsAreProjected = typeof extensionsProjection === "function";
300
288
  const extensions = extensionsAreProjected ? extensionsProjection(parsed.data) : extensionsProjection;
301
- if (Object.hasOwn(snapshots.input, "extensions") && !extensionsAreProjected && extensions === undefined) {
289
+ if (Object.hasOwn(input, "extensions") && !extensionsAreProjected && extensions === undefined) {
302
290
  throw new TypeError("adapter execution source extensions must be omitted or be an adapter-owned extension object");
303
291
  }
304
292
  return createAdapterRenderedExecutionSource({
305
293
  kind,
306
294
  content: parsed.content,
307
295
  identity: {
308
- ref: snapshots.identity.ref,
309
- bundle: snapshots.identity.bundle,
310
- adapter: snapshots.identity.adapter,
311
- file: snapshots.identity.file,
296
+ ref: identity.ref,
297
+ bundle: identity.bundle,
298
+ adapter: identity.adapter,
299
+ file: identity.file,
312
300
  hash: createHash("sha256").update(raw, "utf8").digest("hex"),
313
301
  },
314
302
  ...(hasDefaults ? { defaults } : {}),
@@ -89,7 +89,7 @@ function notFound(conceptId, triedForms, ctx) {
89
89
  *
90
90
  * Exported (Chunk-8 WI-8.5c) as the ONE conceptId derivation the improve
91
91
  * correlation sites (`eligibility.ts` candidate refs, `salience.ts`
92
- * last-use lookup, `collapse-detector.ts` canary mint/score) share with the
92
+ * last-use lookup) share with the
93
93
  * display rule — the permanent successor to the retired transient
94
94
  * `legacyConceptId`.
95
95
  */
@@ -4,18 +4,55 @@
4
4
  import crypto from "node:crypto";
5
5
  import path from "node:path";
6
6
  import { isBundleSlug } from "./asset/asset-ref.js";
7
+ import { UsageError } from "./errors.js";
7
8
  /** Deterministic, filesystem-safe bundle slug from a source path. */
8
9
  export function slugForPath(sourcePath) {
9
10
  const resolved = path.resolve(sourcePath);
10
- const base = path
11
- .basename(resolved)
12
- .toLowerCase()
13
- .replace(/[^a-z0-9]+/g, "-")
14
- .replace(/^-+|-+$/g, "");
11
+ const base = slugify(path.basename(resolved));
15
12
  if (base.length > 0)
16
13
  return base;
17
14
  return `bundle-${shortHash(resolved)}`;
18
15
  }
16
+ /**
17
+ * Bundle slug from the package/repo name a registry install id names
18
+ * (`npm:@scope/pkg` → `pkg`, `github:owner/repo` → `repo`,
19
+ * `git:https://host/owner/repo` → `repo`); empty when it names none.
20
+ */
21
+ export function slugForRegistryId(registryId) {
22
+ const locator = registryId.slice(registryId.indexOf(":") + 1);
23
+ return slugify(locator.split(/[/:]/).filter(Boolean).pop() ?? "");
24
+ }
25
+ function slugify(name) {
26
+ return name
27
+ .toLowerCase()
28
+ .replace(/[^a-z0-9]+/g, "-")
29
+ .replace(/^-+|-+$/g, "");
30
+ }
31
+ /**
32
+ * Enforce the `--name` contract on every add path (D6): an explicit name is
33
+ * not a silent hint that may fall back to a derived id — it is a request
34
+ * that either succeeds exactly or fails loudly, before anything is written.
35
+ *
36
+ * `existingKeyForThisInstall` is the bundle key this exact source (path/URL/
37
+ * registry id) is already configured under, if any. Re-adding that same
38
+ * source under a *different* explicit name is rejected in favor of
39
+ * `akm bundle rename`, which is the one command allowed to change a bundle's
40
+ * key. A caller with no existing install for this source (the common case)
41
+ * omits it.
42
+ */
43
+ export function validateExplicitBundleName(bundles, name, existingKeyForThisInstall) {
44
+ if (!isBundleSlug(name)) {
45
+ throw new UsageError(`"${name}" is not a legal bundle name: it must not contain whitespace or the characters ":" "." "#" "/".`, "INVALID_FLAG_VALUE");
46
+ }
47
+ if (existingKeyForThisInstall !== undefined && name !== existingKeyForThisInstall) {
48
+ throw new UsageError(`This source is already installed as bundle "${existingKeyForThisInstall}". Re-adding it under a ` +
49
+ `different name is not supported — run \`akm bundle rename ${existingKeyForThisInstall} ${name}\` instead.`, "INVALID_FLAG_VALUE");
50
+ }
51
+ if (Object.hasOwn(bundles, name) && name !== existingKeyForThisInstall) {
52
+ throw new UsageError(`Bundle name "${name}" already exists. Choose another name, or run ` +
53
+ `\`akm bundle rename ${name} <new-name>\` first.`, "INVALID_FLAG_VALUE");
54
+ }
55
+ }
19
56
  /** Derive one batch-unique bundle id. */
20
57
  export function deriveBundleId(registryId, sourcePath, usedIds) {
21
58
  const preferred = registryId && registryId.length > 0 && isBundleSlug(registryId) ? registryId : slugForPath(sourcePath);