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

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 (346) hide show
  1. package/CHANGELOG.md +760 -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/lint/base-linter.js +19 -5
  53. package/dist/commands/proposal/drain.js +251 -644
  54. package/dist/commands/proposal/proposal-cli.js +3 -18
  55. package/dist/commands/proposal/proposal-types.js +20 -41
  56. package/dist/commands/proposal/proposal.js +1 -2
  57. package/dist/commands/proposal/propose.js +134 -160
  58. package/dist/commands/proposal/repository.js +502 -1487
  59. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  60. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  61. package/dist/commands/proposal/validators/proposals.js +13 -89
  62. package/dist/commands/read/curate.js +63 -413
  63. package/dist/commands/read/search-cli.js +16 -33
  64. package/dist/commands/read/search.js +17 -23
  65. package/dist/commands/read/show.js +2 -13
  66. package/dist/commands/sources/bundle-cli.js +25 -2
  67. package/dist/commands/sources/bundle-config-ops.js +4 -0
  68. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  69. package/dist/commands/sources/info.js +2 -11
  70. package/dist/commands/sources/installed-stashes.js +197 -746
  71. package/dist/commands/sources/schema-repair.js +98 -129
  72. package/dist/commands/sources/source-add.js +62 -12
  73. package/dist/commands/sources/source-manage.js +9 -2
  74. package/dist/commands/sources/stash-cli.js +1 -1
  75. package/dist/commands/tasks/explain.js +10 -13
  76. package/dist/commands/tasks/tasks-cli.js +9 -8
  77. package/dist/commands/tasks/tasks.js +326 -930
  78. package/dist/commands/tasks/validate.js +42 -21
  79. package/dist/commands/workflow/plan.js +22 -29
  80. package/dist/commands/workflow-cli.js +4 -4
  81. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  82. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  83. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  84. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  85. package/dist/core/adapter/execution-source.js +17 -29
  86. package/dist/core/asset/asset-placement.js +4 -13
  87. package/dist/core/asset/resolve-ref.js +1 -1
  88. package/dist/core/bundle-id.js +42 -5
  89. package/dist/core/bundle-rename.js +291 -0
  90. package/dist/core/config/config-io.js +1 -2
  91. package/dist/core/config/config-schema.js +1 -33
  92. package/dist/core/config/config-walker.js +1 -1
  93. package/dist/core/config/config.js +163 -68
  94. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  95. package/dist/core/config/schema/embedding.js +20 -5
  96. package/dist/core/config/schema/engines.js +5 -0
  97. package/dist/core/config/schema/execution.js +1 -1
  98. package/dist/core/config/schema/experimental.js +1 -1
  99. package/dist/core/config/schema/improve-processes.js +21 -95
  100. package/dist/core/config/schema/improve.js +4 -42
  101. package/dist/core/config/schema/scheduler.js +12 -12
  102. package/dist/core/config/schema/search.js +6 -22
  103. package/dist/core/env-secret-ref.js +0 -1
  104. package/dist/core/errors.js +8 -9
  105. package/dist/core/file-lock.js +76 -173
  106. package/dist/core/logs-db.js +2 -2
  107. package/dist/core/paths.js +0 -27
  108. package/dist/core/redaction.js +109 -2
  109. package/dist/core/run-lock.js +2 -5
  110. package/dist/core/spawn-env.js +1 -1
  111. package/dist/core/state/migrations.js +108 -61
  112. package/dist/core/state-db-scope.js +2 -4
  113. package/dist/core/state-db.js +126 -692
  114. package/dist/core/type-presentation.js +1 -9
  115. package/dist/core/write-source.js +293 -1012
  116. package/dist/execution/input-contract.js +1 -1
  117. package/dist/execution/resolved-request.js +135 -689
  118. package/dist/execution/source.js +63 -257
  119. package/dist/execution/target-ref.js +1 -1
  120. package/dist/indexer/bundle-identity-guard.js +2 -2
  121. package/dist/indexer/db/graph-db.js +106 -46
  122. package/dist/indexer/ensure-index.js +44 -85
  123. package/dist/indexer/graph/graph-extraction.js +340 -562
  124. package/dist/indexer/graph/graph-related.js +130 -0
  125. package/dist/indexer/index-rebuild-lock.js +3 -11
  126. package/dist/indexer/index-writer-lock.js +8 -17
  127. package/dist/indexer/index-written-assets.js +139 -151
  128. package/dist/indexer/indexer.js +524 -846
  129. package/dist/indexer/materialize-embeddings.js +60 -397
  130. package/dist/indexer/passes/memory-inference.js +81 -90
  131. package/dist/indexer/passes/metadata.js +132 -200
  132. package/dist/indexer/read-preflight.js +0 -7
  133. package/dist/indexer/scan/doc-to-entry.js +1 -3
  134. package/dist/indexer/scan/drain-dir.js +1 -1
  135. package/dist/indexer/search/db-search.js +181 -590
  136. package/dist/indexer/search/fts-query.js +30 -41
  137. package/dist/indexer/search/ranking.js +28 -154
  138. package/dist/indexer/search/search-attribution.js +12 -32
  139. package/dist/indexer/search/search-fields.js +11 -15
  140. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  141. package/dist/indexer/search/search-source.js +1 -4
  142. package/dist/indexer/usage/usage-events.js +2 -7
  143. package/dist/integrations/agent/engine-fallback.js +23 -40
  144. package/dist/integrations/agent/engine-resolution.js +93 -183
  145. package/dist/integrations/agent/execution.js +507 -0
  146. package/dist/integrations/agent/model-map.js +28 -156
  147. package/dist/integrations/agent/request-lowering.js +66 -141
  148. package/dist/integrations/agent/runner-dispatch.js +143 -321
  149. package/dist/integrations/agent/runner.js +54 -14
  150. package/dist/integrations/lockfile.js +53 -101
  151. package/dist/llm/embedders/deterministic.js +2 -3
  152. package/dist/llm/embedders/profile.js +71 -0
  153. package/dist/llm/embedders/remote.js +10 -15
  154. package/dist/llm/graph-extract.js +3 -12
  155. package/dist/llm/index-passes.js +3 -5
  156. package/dist/llm/memory-infer.js +1 -2
  157. package/dist/llm/metadata-enhance.js +1 -2
  158. package/dist/llm/structured-call.js +5 -24
  159. package/dist/output/generic-render.js +23 -11
  160. package/dist/output/html-render.js +13 -10
  161. package/dist/output/render-registry.js +3 -32
  162. package/dist/output/shapes/helpers.js +2 -34
  163. package/dist/output/shapes/passthrough.js +1 -9
  164. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  165. package/dist/output/text/command-format.js +60 -23
  166. package/dist/output/text/helpers.js +1 -1
  167. package/dist/output/text/migrate.js +5 -14
  168. package/dist/output/text/proposal-format.js +1 -2
  169. package/dist/output/text/workflow-format.js +0 -32
  170. package/dist/output/text.js +2 -0
  171. package/dist/registry/factory.js +4 -19
  172. package/dist/registry/network.js +66 -220
  173. package/dist/registry/providers/index.js +0 -2
  174. package/dist/registry/providers/skills-sh.js +3 -14
  175. package/dist/registry/providers/static-index.js +24 -26
  176. package/dist/registry/resolve.js +55 -131
  177. package/dist/scripts/akm-migrate-node.js +43940 -93320
  178. package/dist/scripts/akm-migrate.js +43700 -93078
  179. package/dist/setup/registry-stash-loader.js +4 -13
  180. package/dist/setup/semantic-assets.js +3 -44
  181. package/dist/setup/setup.js +1 -1
  182. package/dist/setup/steps/tasks.js +25 -15
  183. package/dist/sources/provider-factory.js +17 -18
  184. package/dist/sources/providers/filesystem.js +2 -3
  185. package/dist/sources/providers/git-install.js +7 -1
  186. package/dist/sources/providers/git-provider.js +0 -3
  187. package/dist/sources/providers/git-stash.js +0 -17
  188. package/dist/sources/providers/npm.js +2 -4
  189. package/dist/sources/providers/provider-utils.js +5 -10
  190. package/dist/sources/providers/website.js +0 -2
  191. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  192. package/dist/sources/website-url.js +2 -2
  193. package/dist/storage/database.js +9 -35
  194. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  195. package/dist/storage/repositories/index-connection.js +34 -70
  196. package/dist/storage/repositories/index-entries-repository.js +69 -111
  197. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  198. package/dist/storage/repositories/index-entry-schema.js +83 -269
  199. package/dist/storage/repositories/index-fts-repository.js +86 -256
  200. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  201. package/dist/storage/repositories/index-meta-repository.js +6 -4
  202. package/dist/storage/repositories/index-schema.js +192 -220
  203. package/dist/storage/repositories/index-utility-repository.js +8 -29
  204. package/dist/storage/repositories/index-vec-repository.js +133 -414
  205. package/dist/storage/repositories/outcome-repository.js +2 -1
  206. package/dist/storage/repositories/proposals-repository.js +35 -0
  207. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  208. package/dist/storage/repositories/task-history-repository.js +26 -4
  209. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  210. package/dist/storage/sqlite-migrations.js +136 -0
  211. package/dist/storage/sqlite-pragmas.js +11 -9
  212. package/dist/storage/sqlite-transaction.js +170 -0
  213. package/dist/storage/state-db-integrity.js +34 -27
  214. package/dist/tasks/activation-config.js +134 -62
  215. package/dist/tasks/backends/cron.js +129 -277
  216. package/dist/tasks/backends/exec-utils.js +2 -5
  217. package/dist/tasks/backends/launchd.js +125 -745
  218. package/dist/tasks/backends/schtasks.js +101 -620
  219. package/dist/tasks/prepare/prepare-support.js +5 -15
  220. package/dist/tasks/prepare/prepare.js +0 -2
  221. package/dist/tasks/resolve-akm-bin.js +20 -79
  222. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  223. package/dist/tasks/scheduler-binding.js +18 -238
  224. package/dist/tasks/scheduler-invocation.js +52 -52
  225. package/dist/tasks/scheduler-lock.js +53 -0
  226. package/dist/tasks/scheduler-sync.js +361 -751
  227. package/dist/tasks/source/parse-task-source.js +160 -10
  228. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  229. package/dist/tasks/source/task-to-v4.js +2 -2
  230. package/dist/workflows/authoring/authoring.js +3 -12
  231. package/dist/workflows/compile.js +211 -0
  232. package/dist/workflows/concurrency-policy.js +13 -74
  233. package/dist/workflows/exec/child-invocation.js +3 -17
  234. package/dist/workflows/exec/child-workflow.js +32 -141
  235. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  236. package/dist/workflows/exec/environment.js +98 -0
  237. package/dist/workflows/exec/exec-unit.js +33 -140
  238. package/dist/workflows/exec/frozen-judge.js +7 -59
  239. package/dist/workflows/exec/native-executor.js +82 -341
  240. package/dist/workflows/exec/param-secrets.js +29 -47
  241. package/dist/workflows/exec/run-workflow.js +154 -387
  242. package/dist/workflows/exec/scheduler.js +9 -36
  243. package/dist/workflows/exec/step-work.js +127 -430
  244. package/dist/workflows/exec/unit-dispatch.js +11 -63
  245. package/dist/workflows/exec/unit-writer.js +8 -52
  246. package/dist/workflows/exec/worktree.js +39 -273
  247. package/dist/workflows/freeze/child-output-references.js +4 -15
  248. package/dist/workflows/freeze/environment.js +99 -92
  249. package/dist/workflows/freeze/freeze.js +172 -0
  250. package/dist/workflows/freeze/step-values.js +19 -21
  251. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  252. package/dist/workflows/freeze/targets/command.js +10 -33
  253. package/dist/workflows/freeze/targets/script.js +5 -12
  254. package/dist/workflows/freeze/targets/shell.js +3 -6
  255. package/dist/workflows/freeze/targets/task.js +25 -80
  256. package/dist/workflows/freeze/task-bindings.js +20 -67
  257. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  258. package/dist/workflows/ir/params.js +6 -51
  259. package/dist/workflows/ir/plan-hash.js +2 -34
  260. package/dist/workflows/parser.js +140 -43
  261. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  262. package/dist/workflows/renderer.js +36 -69
  263. package/dist/workflows/resource-limits.js +12 -120
  264. package/dist/workflows/runtime/agent-identity.js +8 -40
  265. package/dist/workflows/runtime/run-outputs.js +3 -6
  266. package/dist/workflows/runtime/run-plan.js +316 -0
  267. package/dist/workflows/runtime/runs.js +48 -200
  268. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  269. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  270. package/dist/workflows/validate-summary.js +2 -7
  271. package/docs/integration/bundling-akm.md +49 -42
  272. package/docs/migration/README.md +1 -0
  273. package/docs/migration/release-notes/0.9.17.md +41 -0
  274. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  275. package/docs/reference/cli.md +182 -125
  276. package/docs/reference/configuration.md +49 -56
  277. package/docs/reference/data-and-telemetry.md +19 -20
  278. package/docs/reference/tasks.md +86 -38
  279. package/docs/reference/workflow-schema.md +14 -18
  280. package/docs/reference/workflows.md +6 -9
  281. package/package.json +1 -1
  282. package/schemas/akm-config.json +87 -406
  283. package/dist/commands/health/advisories.js +0 -150
  284. package/dist/commands/health/metrics.js +0 -329
  285. package/dist/commands/health/surfaces.js +0 -102
  286. package/dist/commands/improve/anti-collapse.js +0 -83
  287. package/dist/commands/improve/collapse-detector.js +0 -432
  288. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  289. package/dist/commands/improve/consolidate/merge.js +0 -146
  290. package/dist/commands/improve/distill/promote-memory.js +0 -329
  291. package/dist/commands/improve/distill/quality-gate.js +0 -500
  292. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  293. package/dist/commands/improve/proposal-envelope.js +0 -31
  294. package/dist/commands/improve/run-context.js +0 -123
  295. package/dist/commands/improve/shared.js +0 -21
  296. package/dist/commands/improve/source-identity.js +0 -28
  297. package/dist/commands/improve/triage.js +0 -96
  298. package/dist/commands/proposal/drain-policies.js +0 -151
  299. package/dist/commands/sources/update-transaction.js +0 -220
  300. package/dist/core/action-contributors.js +0 -28
  301. package/dist/core/config/config-version-shim.js +0 -101
  302. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  303. package/dist/core/fs-txn.js +0 -405
  304. package/dist/core/lexical-score.js +0 -25
  305. package/dist/core/maintenance-barrier.js +0 -167
  306. package/dist/execution/executable-identity.js +0 -105
  307. package/dist/execution/guarded-source.js +0 -441
  308. package/dist/indexer/graph/graph-boost.js +0 -427
  309. package/dist/indexer/graph/graph-dedup.js +0 -95
  310. package/dist/indexer/search/name-match.js +0 -35
  311. package/dist/indexer/search/ranking-contributors.js +0 -515
  312. package/dist/indexer/walk/project-context.js +0 -192
  313. package/dist/integrations/agent/execution-cascade.js +0 -566
  314. package/dist/integrations/agent/execution-definitions.js +0 -202
  315. package/dist/integrations/agent/execution-lowering.js +0 -841
  316. package/dist/integrations/agent/execution-preparation.js +0 -98
  317. package/dist/integrations/agent/inline-execution.js +0 -74
  318. package/dist/registry/create-provider-registry.js +0 -29
  319. package/dist/registry/pinned-request-helper.js +0 -247
  320. package/dist/registry/pinned-transport.js +0 -717
  321. package/dist/sources/providers/index.js +0 -14
  322. package/dist/storage/engines/sqlite-migrations.js +0 -271
  323. package/dist/storage/repositories/canaries-repository.js +0 -107
  324. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  325. package/dist/storage/repositories/registry-cache.js +0 -113
  326. package/dist/tasks/scheduler-sync-preview.js +0 -52
  327. package/dist/workflows/freeze/resolve-steps.js +0 -86
  328. package/dist/workflows/freeze/source-freeze.js +0 -64
  329. package/dist/workflows/ir/compile.js +0 -321
  330. package/dist/workflows/ir/environment-v4.js +0 -330
  331. package/dist/workflows/ir/freeze-v4.js +0 -153
  332. package/dist/workflows/ir/schema-v4.js +0 -745
  333. package/dist/workflows/ir/schema.js +0 -354
  334. package/dist/workflows/program/schema.js +0 -78
  335. package/dist/workflows/runtime/checkin.js +0 -57
  336. package/dist/workflows/runtime/plan-classifier.js +0 -196
  337. package/dist/workflows/runtime/unit-checkin.js +0 -45
  338. package/dist/workflows/runtime/unit-phases.js +0 -20
  339. package/dist/workflows/schema.js +0 -4
  340. package/dist/workflows/source-ir/compile.js +0 -200
  341. package/dist/workflows/source-ir/program.js +0 -50
  342. package/dist/workflows/source-ir/result.js +0 -26
  343. package/dist/workflows/source-ir/schema.js +0 -786
  344. package/dist/workflows/source-ir/triggers.js +0 -79
  345. package/dist/workflows/source-ir/uses.js +0 -40
  346. package/dist/workflows/validator.js +0 -60
@@ -1,24 +1,14 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * File-private helpers prepareTaskV3Execution depends on, moved body-intact
6
- * out of the pre-P1b src/tasks/runtime-v3.ts (spec
7
- * docs/plans/specs/p1b-model-extraction.md §4.1, Lane B / D4 module map).
8
- * None of these was exported at head either; only the subset prepare.ts's
9
- * moved function body calls is exported here.
10
- */
11
- import path from "node:path";
12
4
  import { makeBundleRef, parseBundleRef } from "../../core/asset/asset-ref.js";
13
5
  import { COMPOSITION_INVALID_MULTI_JOB_HINT, ConfigError, NotFoundError, UsageError } from "../../core/errors.js";
14
6
  import { DURATION_UNITS, parseDuration } from "../../core/time.js";
15
7
  import { isPortableExecutionAgentSelector } from "../../execution/source.js";
16
- import { requireAuthorizedExecutionPlan } from "../../integrations/agent/execution-cascade.js";
17
- import { lowerResolvedExecutionRequest } from "../../integrations/agent/execution-lowering.js";
8
+ import { buildExecution } from "../../integrations/agent/execution.js";
18
9
  import { resolveAssetPath } from "../../sources/resolve.js";
10
+ import { checkWorkflowPlan, compileWorkflowSource } from "../../workflows/compile.js";
19
11
  import { detectSecretShapedParams } from "../../workflows/exec/param-secrets.js";
20
- import { compileWorkflowPlan } from "../../workflows/ir/compile.js";
21
- import { compileWorkflowSource } from "../../workflows/source-ir/compile.js";
22
12
  import { isInferredSecretName } from "../log-redaction.js";
23
13
  import { SCHEDULED_TASK_CONTEXT_KEYS } from "../scheduler-invocation.js";
24
14
  function own(value, key) {
@@ -118,14 +108,14 @@ export function defaultTaskShell(platform) {
118
108
  return platform === "win32" ? "powershell" : "sh";
119
109
  }
120
110
  export function validatePreparedCommand(invocation, context) {
121
- const request = requireAuthorizedExecutionPlan(invocation.plan);
111
+ const { request } = invocation;
122
112
  if (!request.engine.name) {
123
113
  throw new ConfigError(`Task ${JSON.stringify(context.taskRef)} has no resolved execution engine. Configure defaults.engine or akm.engine before running it.`, "INVALID_CONFIG_FILE");
124
114
  }
125
115
  // Lowering is pure. Running it before the durable-attempt boundary proves
126
116
  // the selected target is transport-projectable; dispatch repeats the same
127
117
  // deterministic projection from this frozen request/config snapshot.
128
- lowerResolvedExecutionRequest(request, invocation.config);
118
+ buildExecution(request, invocation.runner);
129
119
  return invocation;
130
120
  }
131
121
  export function validateWorkflowRuntimeSource(file, workspaceRoot, readFile) {
@@ -141,7 +131,7 @@ export function validateWorkflowRuntimeSource(file, workspaceRoot, readFile) {
141
131
  const code = isMultiJob ? "COMPOSITION_INVALID" : "WORKFLOW_SOURCE_INVALID";
142
132
  throw new UsageError(`Task workflow target is not projectable: ${detail}`, code, isMultiJob ? COMPOSITION_INVALID_MULTI_JOB_HINT : undefined);
143
133
  }
144
- const planned = compileWorkflowPlan(compiled.ir, path.basename(file, path.extname(file)));
134
+ const planned = checkWorkflowPlan(compiled.plan);
145
135
  if (!planned.ok) {
146
136
  // Same mapping applied for consistency; `compiled.ir` is already
147
137
  // guaranteed exactly one job here, so this arm always resolves to
@@ -54,7 +54,6 @@ export async function prepareTaskV3Execution(document, context) {
54
54
  const invocation = validatePreparedCommand(await (context.prepareCommand ?? prepareCommandInvocation)({
55
55
  action,
56
56
  config: context.config,
57
- invocationKind: "task",
58
57
  current: currentExecutionValues(document, context, commandEnvironment),
59
58
  ...(context.commandSourceLoader ? { sourceLoader: context.commandSourceLoader } : {}),
60
59
  }), context);
@@ -71,7 +70,6 @@ export async function prepareTaskV3Execution(document, context) {
71
70
  const invocation = validatePreparedCommand(await (context.prepareCommand ?? prepareCommandInvocation)({
72
71
  action: { ref: qualified },
73
72
  config: context.config,
74
- invocationKind: "task",
75
73
  current: currentExecutionValues(document, context, commandEnvironment),
76
74
  ...(context.commandSourceLoader ? { sourceLoader: context.commandSourceLoader } : {}),
77
75
  }), context);
@@ -10,17 +10,16 @@
10
10
  * Resolution order:
11
11
  *
12
12
  * 1. `process.execPath` alone for a Bun standalone executable.
13
- * 2. Absolute Node plus the public `dist/akm` package launcher — eligible
14
- * when it is the active npm global install, or when this process
15
- * cannot write to the directory containing it (a read-only mount, e.g.
16
- * an image-baked install, gives the same "won't change out from under
17
- * the scheduler" guarantee npm-global ownership does).
18
- * 3. Absolute runtime plus the source/build CLI entry, classified as a
19
- * checkout that requires explicit `--rebind` for scheduler writes.
13
+ * 2. Absolute Node plus the public `dist/akm` package launcher.
14
+ * 3. Absolute runtime plus the source/build CLI entry of a checkout.
15
+ *
16
+ * `via` names which one was used. `checkout` — a package launcher inside a
17
+ * git work tree, or a source/build entry — runs whatever the checkout holds
18
+ * when the scheduler fires; `task sync` says so once when it writes one.
20
19
  *
21
20
  * Returns the argv array the scheduler should execute (e.g.
22
- * `["/usr/local/bin/node", "/repo/dist/cli-node.mjs"]`). The caller appends
23
- * subcommand args (`"task", "run", "<id>"`).
21
+ * `["/usr/local/bin/node", "/usr/lib/node_modules/akm-cli/dist/akm"]`). The
22
+ * caller appends subcommand args (`"task", "run", "<id>"`).
24
23
  */
25
24
  import { spawnSync } from "node:child_process";
26
25
  import fs from "node:fs";
@@ -28,6 +27,8 @@ import path from "node:path";
28
27
  import { fileURLToPath } from "node:url";
29
28
  import { ConfigError } from "../core/errors.js";
30
29
  import { mainPath as runtimeMainPath } from "../runtime.js";
30
+ /** A source entry or local build run by a runtime: `src/cli.ts`, `dist/cli.js`, `dist/cli-node.mjs`. */
31
+ const CHECKOUT_ENTRY = /(?:^|[\\/])src[\\/]cli\.ts$|(?:^|[\\/])dist[\\/](?:cli\.js|cli-node\.mjs)$/i;
31
32
  /** True only for Bun's virtual main path inside a compiled standalone executable. */
32
33
  export function isBunStandaloneMain(mainPath = runtimeMainPath) {
33
34
  return (mainPath?.startsWith("/$bunfs/") === true || (mainPath !== undefined && /^[A-Za-z]:[\\/]~BUN[\\/]/i.test(mainPath)));
@@ -37,44 +38,27 @@ export function resolveAkmInvocation(options = {}) {
37
38
  const runtime = options.runtime ?? (process.versions.bun ? "bun" : "node");
38
39
  const execPath = options.execPath ?? process.execPath;
39
40
  const mainPath = options.mainPath ?? runtimeMainPath;
40
- const isStandaloneMain = isBunStandaloneMain(mainPath);
41
- if (runtime === "bun" && isStandaloneMain && execPath) {
42
- return { argv: [absoluteInvocationPath(execPath)], via: "standalone", kind: "standalone", eligible: true };
41
+ if (runtime === "bun" && isBunStandaloneMain(mainPath) && execPath) {
42
+ return { argv: [absoluteInvocationPath(execPath)], via: "standalone" };
43
43
  }
44
44
  const launcherPath = options.launcherPath ?? env.AKM_LAUNCHER_PATH?.trim();
45
45
  const nodePath = options.nodePath ?? env.AKM_LAUNCHER_NODE?.trim() ?? (runtime === "node" ? execPath : undefined);
46
46
  if (launcherPath && nodePath && isPublicPackageLauncher(launcherPath)) {
47
- const checkout = isCheckoutLauncher(launcherPath);
48
- let npmGlobalRoot;
49
- if (!checkout) {
50
- try {
51
- npmGlobalRoot = (options.resolveNpmGlobalRoot ?? ((node) => resolveNpmGlobalRoot(node, env)))(nodePath);
52
- }
53
- catch {
54
- // An unprovable package installation is intentionally ineligible.
55
- }
56
- }
57
- const npmGlobal = !checkout && packageBelongsToNpmGlobalRoot(launcherPath, npmGlobalRoot);
58
- const readOnlyInstall = !checkout && !npmGlobal && !(options.isPathWritable ?? isPathWritable)(path.dirname(launcherPath));
59
- const kind = checkout ? "checkout" : npmGlobal ? "npm" : "package-local";
60
47
  return {
61
48
  argv: [absoluteInvocationPath(nodePath), absoluteInvocationPath(launcherPath)],
62
- via: kind,
63
- kind,
64
- eligible: npmGlobal || readOnlyInstall,
49
+ via: isCheckoutLauncher(launcherPath) ? "checkout" : "npm",
65
50
  };
66
51
  }
67
52
  const checkoutEntry = resolveCheckoutEntry(options.cliEntryUrl ?? import.meta.url, runtime, mainPath);
68
53
  if (checkoutEntry && execPath) {
69
- return {
70
- argv: [absoluteInvocationPath(execPath), absoluteInvocationPath(checkoutEntry)],
71
- via: "checkout",
72
- kind: "checkout",
73
- eligible: false,
74
- };
54
+ return { argv: [absoluteInvocationPath(execPath), absoluteInvocationPath(checkoutEntry)], via: "checkout" };
75
55
  }
76
56
  throw new ConfigError("Cannot resolve absolute path to the akm binary for scheduler registration.", "INVALID_CONFIG_FILE", "Run the npm-global launcher or a standalone akm executable.");
77
57
  }
58
+ /** Whether an installed launcher argv runs akm out of a source checkout. */
59
+ export function isCheckoutInvocation(argv) {
60
+ return argv.some((part) => CHECKOUT_ENTRY.test(part) || (isPublicPackageLauncher(part) && isCheckoutLauncher(part)));
61
+ }
78
62
  function resolveCheckoutEntry(moduleUrl, runtime, mainPath) {
79
63
  try {
80
64
  const modulePath = fileURLToPath(moduleUrl);
@@ -111,6 +95,7 @@ function isPublicPackageLauncher(file) {
111
95
  path.basename(path.dirname(file)).toLowerCase() === "dist" &&
112
96
  fs.existsSync(file));
113
97
  }
98
+ /** A package launcher whose package root is a git work tree (`npm link`, a local clone). */
114
99
  function isCheckoutLauncher(file) {
115
100
  let launcher = path.resolve(file);
116
101
  try {
@@ -122,52 +107,8 @@ function isCheckoutLauncher(file) {
122
107
  const packageRoot = path.dirname(path.dirname(launcher));
123
108
  return fs.existsSync(path.join(packageRoot, ".git"));
124
109
  }
125
- /** Whether this process can write to `dir` — false also covers a read-only mount. */
126
- function isPathWritable(dir) {
127
- try {
128
- fs.accessSync(dir, fs.constants.W_OK);
129
- return true;
130
- }
131
- catch {
132
- return false;
133
- }
134
- }
135
- function packageBelongsToNpmGlobalRoot(launcherPath, npmGlobalRoot) {
136
- if (!npmGlobalRoot)
137
- return false;
138
- try {
139
- const packageRoot = path.dirname(path.dirname(fs.realpathSync(launcherPath)));
140
- const globalRoot = fs.realpathSync(npmGlobalRoot);
141
- const metadata = JSON.parse(fs.readFileSync(path.join(packageRoot, "package.json"), "utf8"));
142
- return metadata.name === "akm-cli" && samePath(path.dirname(packageRoot), globalRoot);
143
- }
144
- catch {
145
- return false;
146
- }
147
- }
148
- function samePath(left, right) {
149
- const normalize = (value) => {
150
- const resolved = path.resolve(value);
151
- return process.platform === "win32" ? resolved.toLowerCase() : resolved;
152
- };
153
- return normalize(left) === normalize(right);
154
- }
155
- // #901: the global root can't change within a process lifetime, and every
156
- // `resolveAkmInvocation()` call in a scheduler-sync process re-derives the
157
- // same `nodePath` — so this spawns `npm root --global` at most once per
158
- // process instead of once per call. Keyed by nodePath (rather than a bare
159
- // once-only flag) so a differently-invoked probe later in the same process
160
- // still gets its own answer instead of a stale one.
161
- let cachedNpmGlobalRoot;
110
+ /** `npm root --global` for the npm that ships with `nodePath`; `akm upgrade` uses it to detect a global install. */
162
111
  export function resolveNpmGlobalRoot(nodePath, env) {
163
- if (cachedNpmGlobalRoot && cachedNpmGlobalRoot.nodePath === nodePath) {
164
- return cachedNpmGlobalRoot.value;
165
- }
166
- const value = resolveNpmGlobalRootUncached(nodePath, env);
167
- cachedNpmGlobalRoot = { nodePath, value };
168
- return value;
169
- }
170
- function resolveNpmGlobalRootUncached(nodePath, env) {
171
112
  const npmCli = resolveAssociatedNpmCli(nodePath);
172
113
  if (!npmCli)
173
114
  return undefined;
@@ -105,7 +105,6 @@ const SAFE_TASK_ATTEMPT_ERROR_CODES = new Set([
105
105
  "STASH_DIR_UNREADABLE",
106
106
  "LLM_NOT_CONFIGURED",
107
107
  "INVALID_CONFIG_FILE",
108
- "UNSUPPORTED_CONFIG_VERSION",
109
108
  "TEST_ISOLATION_MISSING",
110
109
  "INVALID_FLAG_VALUE",
111
110
  "MISSING_REQUIRED_ARGUMENT",
@@ -2,12 +2,12 @@
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
- * Secret-free input to every native scheduler backend.
5
+ * Secret-free input to every native scheduler backend, and the backend
6
+ * contract itself.
6
7
  *
7
- * The binding deliberately contains only stable source identity, trigger
8
- * identity, and the public CLI tail. Source content, action inputs,
9
- * environment values, resolved requests, and credentials must never cross
10
- * this boundary or be persisted by an OS scheduler.
8
+ * A binding carries only stable source identity, trigger identity, and the
9
+ * public CLI tail. Source content, action inputs, environment values, and
10
+ * credentials never cross this boundary or reach an OS scheduler.
11
11
  */
12
12
  import { createHash } from "node:crypto";
13
13
  import { bundleRefToString, parseBundleRef } from "../core/asset/asset-ref.js";
@@ -20,20 +20,8 @@ export function compileTaskSchedulerBindings(input) {
20
20
  const bundle = parseBundleRef(ref).bundle;
21
21
  if (!bundle)
22
22
  throw new Error("invariant: qualified scheduler task ref lost its bundle");
23
- // P2b Lane B (B-N3): the invocation is compiled PER schedule entry now —
24
- // each entry's own `inputs` produces its own trailing flag tail, so it can
25
- // no longer be hoisted as one binding shared by every entry.
26
23
  return Object.freeze(input.schedules.map((schedule) => {
27
24
  const bindingId = schedule.ordinal === 0 ? id : digestBindingId("task", ref, schedule.ordinal);
28
- const invocation = Object.freeze([
29
- "task",
30
- "run",
31
- id,
32
- "--bundle",
33
- bundle,
34
- "--scheduled",
35
- ...schedulerInputFlagTail(schedule.inputs),
36
- ]);
37
25
  return freezeBinding({
38
26
  id: bindingId,
39
27
  nativeId: schedulerNativeBindingId(bindingId),
@@ -42,39 +30,21 @@ export function compileTaskSchedulerBindings(input) {
42
30
  source: schedule.source,
43
31
  ordinal: schedule.ordinal,
44
32
  enabled: true,
45
- invocation,
33
+ invocation: ["task", "run", id, "--bundle", bundle, "--scheduled", ...schedulerInputFlagTail(schedule.inputs)],
46
34
  });
47
35
  }));
48
36
  }
49
37
  /**
50
38
  * The canonically-sorted `--<name> <value>` flag tail for one schedule
51
- * entry's `inputs` (spec §1.7 B-N3). Empty/absent `inputs` yields an empty
52
- * tail — the fixed six-token invocation, byte-identical to every schedule
53
- * entry before P2b (B-03).
54
- *
55
- * Code-review finding (scheduler-binding.ts:536): a value whose exact text
56
- * begins with `-` (a negative number, or a string that just happens to
57
- * start with a dash) is indistinguishable from a NEW flag in the two-token
58
- * `--<name> <value>` form — `isValidSchedulerInputFlagTail`
59
- * (scheduler-invocation.ts) refuses it outright, and even a looser parser
60
- * would still be wrong for a non-numeric dash-leading string: the real `akm
61
- * task run` flag parser (`parseTaskInputFlags`, tasks-cli.ts) only
62
- * special-cases a dash-DIGIT lead, so it would silently treat `--scope
63
- * -urgent` as the boolean flag `--scope` followed by an orphaned,
64
- * dropped `-urgent` token. The inline `--<name>=<value>` form has no such
65
- * ambiguity on EITHER side — `isValidSchedulerInputFlagTail` accepts it
66
- * unconditionally, and `parseTaskInputFlags`'s own inline-`=` branch splits
67
- * on the first `=` without ever inspecting the value's leading character —
68
- * so it is used whenever the value's exact text would otherwise be
69
- * ambiguous. Every other value keeps the existing two-token form
70
- * byte-identical to before this fix (B-03/B-45).
39
+ * entry's `inputs`. A value whose text starts with `-` uses the inline
40
+ * `--<name>=<value>` form, the only spelling `parseTaskInputFlags` cannot
41
+ * mistake for a flag.
71
42
  */
72
43
  function schedulerInputFlagTail(inputs) {
73
44
  if (!inputs)
74
45
  return [];
75
- const names = Object.keys(inputs).sort();
76
46
  const tail = [];
77
- for (const name of names) {
47
+ for (const name of Object.keys(inputs).sort()) {
78
48
  const text = schedulerInputFlagValueText(inputs[name]);
79
49
  if (text.startsWith("-"))
80
50
  tail.push(`--${name}=${text}`);
@@ -83,14 +53,6 @@ function schedulerInputFlagTail(inputs) {
83
53
  }
84
54
  return tail;
85
55
  }
86
- /**
87
- * One input value's argv text. A scalar is its exact text — `String(value)`
88
- * for a number/boolean (a `true` boolean is `"true"`, never a bare flag, so
89
- * the tail round-trips through one parser), the string itself for a string.
90
- * An object/array value is its `canonicalInputJson` text, which
91
- * `materializeInputFlags`'s JSON-shorthand path already coerces back through
92
- * the declaration.
93
- */
94
56
  function schedulerInputFlagValueText(value) {
95
57
  if (typeof value === "string")
96
58
  return value;
@@ -100,7 +62,6 @@ function schedulerInputFlagValueText(value) {
100
62
  }
101
63
  export function compileWorkflowSchedulerBindings(input) {
102
64
  const ref = assertQualifiedRef(input.qualifiedRef, "workflow");
103
- const invocation = Object.freeze(["workflow", "run", ref]);
104
65
  return Object.freeze(input.schedules.map((schedule) => {
105
66
  const bindingId = digestBindingId("workflow", ref, schedule.ordinal);
106
67
  return freezeBinding({
@@ -111,23 +72,14 @@ export function compileWorkflowSchedulerBindings(input) {
111
72
  source: schedule.source,
112
73
  ordinal: schedule.ordinal,
113
74
  enabled: true,
114
- invocation,
115
- ...(input.executionEvidenceDigest !== undefined
116
- ? { executionEvidenceDigest: assertSchedulerExecutionEvidenceDigest(input.executionEvidenceDigest) }
117
- : {}),
75
+ invocation: ["workflow", "run", ref],
118
76
  });
119
77
  }));
120
78
  }
121
- export function assertSchedulerExecutionEvidenceDigest(value) {
122
- if (!/^[a-f0-9]{64}$/u.test(value)) {
123
- throw new UsageError("Scheduler workflow execution evidence must be a lowercase SHA-256 digest.", "INVALID_FLAG_VALUE");
124
- }
125
- return value;
126
- }
127
79
  /**
128
80
  * Map a logical binding id to the flat portable token persisted by native
129
- * schedulers. Existing flat ids are byte-for-byte stable; only standalone
130
- * component-relative ids need an encoded native spelling.
81
+ * schedulers. Flat ids are byte-for-byte stable; only nested standalone
82
+ * component ids need an encoded native spelling.
131
83
  */
132
84
  export function schedulerNativeBindingId(id) {
133
85
  if (!id.includes("/"))
@@ -138,183 +90,17 @@ export function schedulerNativeBindingId(id) {
138
90
  .slice(0, 32);
139
91
  return `task-${digest}`;
140
92
  }
141
- /** Portable collision key for scheduler namespaces with Windows semantics. */
142
- export function schedulerNativeArtifactKey(nativeId) {
143
- return nativeId.toLowerCase().replace(/\.+$/u, "");
144
- }
145
- export function schedulerNativeArtifactsForKey(artifacts, nativeId) {
146
- const key = schedulerNativeArtifactKey(nativeId);
147
- return artifacts.filter((artifact) => schedulerNativeArtifactKey(artifact.nativeId) === key);
148
- }
149
- export function assertSchedulerNativeArtifactCardinality(artifacts, nativeId, expectedCount) {
150
- const matches = schedulerNativeArtifactsForKey(artifacts, nativeId);
151
- if (matches.length !== expectedCount) {
152
- const expectedState = expectedCount === 0 ? "absence" : "one present owner";
153
- throw new UsageError(`Native scheduler artifact ${JSON.stringify(nativeId)} changed: normalized cardinality ${matches.length} violates the expected ${expectedState} before mutation.`, "RESOURCE_ALREADY_EXISTS");
154
- }
155
- return matches[0];
156
- }
157
- /** Return a logical owner only when the public invocation proves the mapping. */
158
- export function schedulerLogicalBindingOwner(nativeId, invocation) {
159
- return schedulerNativeArtifactOwner(nativeId, invocation)?.logicalId;
160
- }
161
- export function schedulerNativeArtifactOwner(nativeId, invocation) {
162
- if (invocation[0] === "workflow" && invocation[1] === "run" && invocation.length === 3) {
163
- const ref = invocation[2];
164
- if (ref && workflowRefDigestsToNativeId(ref, nativeId)) {
165
- return { logicalId: nativeId, logicalKind: "workflow" };
166
- }
167
- return undefined;
168
- }
169
- const taskId = invocation[0] === "task" && invocation[1] === "run" ? invocation[2] : undefined;
170
- if (!taskId)
171
- return undefined;
172
- return {
173
- logicalId: schedulerNativeBindingId(taskId) === nativeId ? taskId : nativeId,
174
- logicalKind: "task",
175
- };
176
- }
177
- export function assertSchedulerNativeArtifactOwner(nativeId, intended, invocation) {
178
- const intendedNativeId = intended.nativeId ?? schedulerNativeBindingId(intended.id);
179
- if (nativeId === intendedNativeId && invocation && sameInvocation(invocation, intended.invocation))
180
- return;
181
- const description = invocation ? JSON.stringify(invocation) : "an unproven or malformed invocation";
182
- throw new UsageError(`Native scheduler artifact ${JSON.stringify(nativeId)} belongs to ${description}, not the exact ${intended.logicalSource.kind} owner ${JSON.stringify(intended.logicalSource.ref)} with binding ${JSON.stringify(intended.id)}.`, "RESOURCE_ALREADY_EXISTS");
183
- }
184
- /** Recover binding identity without treating the native hash as source ownership. */
185
- export function schedulerLogicalBindingId(nativeId, invocation) {
186
- return schedulerLogicalBindingOwner(nativeId, invocation) ?? nativeId;
187
- }
188
93
  export function schedulerBindingNativeId(binding) {
189
94
  return binding.nativeId ?? schedulerNativeBindingId(binding.id);
190
95
  }
191
- export function assertSchedulerRemovalArtifact(nativeId, expected, invocation, fingerprint) {
192
- const normalized = normalizeRemovalExpectation(expected);
193
- assertSchedulerExpectationIdentity(normalized);
194
- const fingerprintMatches = normalized.fingerprint === undefined || normalized.fingerprint === fingerprint;
195
- if (nativeId === normalized.nativeId &&
196
- invocation !== undefined &&
197
- sameInvocation(invocation, normalized.invocation) &&
198
- fingerprintMatches) {
199
- return;
200
- }
201
- throw new UsageError(`Native scheduler artifact ${JSON.stringify(nativeId)} changed owner or fingerprint; refusing to remove an unverified ${normalized.logicalSource.kind} owner ${JSON.stringify(normalized.logicalSource.ref)}.`, "RESOURCE_ALREADY_EXISTS");
202
- }
203
- export function assertSchedulerExpectationIdentity(expected, binding) {
204
- const identity = canonicalSchedulerIdentity(expected.logicalSource, expected.ordinal, expected.invocation);
205
- if (identity.bindingId !== expected.bindingId ||
206
- identity.nativeId !== expected.nativeId ||
207
- !sameInvocation(identity.invocation, expected.invocation) ||
208
- (binding !== undefined &&
209
- (binding.id !== expected.bindingId ||
210
- schedulerBindingNativeId(binding) !== expected.nativeId ||
211
- binding.ordinal !== expected.ordinal ||
212
- binding.logicalSource.kind !== expected.logicalSource.kind ||
213
- binding.logicalSource.ref !== expected.logicalSource.ref ||
214
- !sameInvocation(binding.invocation, expected.invocation)))) {
215
- throw new UsageError("Scheduler mutation expectation has a forged or inconsistent binding identity.", "INVALID_FLAG_VALUE");
216
- }
217
- if (expected.state === "absent" && expected.fingerprint !== undefined) {
218
- throw new UsageError("An absent scheduler expectation cannot carry prior artifact state.", "INVALID_FLAG_VALUE");
219
- }
220
- }
221
- export function assertSchedulerMutationArtifact(artifact, expected) {
222
- assertSchedulerExpectationIdentity(expected);
223
- if (expected.state === "absent") {
224
- if (artifact === undefined)
225
- return;
226
- }
227
- else if (artifact !== undefined &&
228
- artifact.nativeId === expected.nativeId &&
229
- artifact.bindingId === expected.bindingId &&
230
- artifact.invocation !== undefined &&
231
- expected.fingerprint !== undefined &&
232
- artifact.fingerprint === expected.fingerprint) {
233
- return;
234
- }
235
- throw new UsageError(`Native scheduler artifact ${JSON.stringify(expected.nativeId)} changed from its frozen ${expected.state} state; refusing mutation.`, "RESOURCE_ALREADY_EXISTS");
236
- }
237
- export function assertSchedulerRollbackArtifact(artifact, expected) {
238
- const matches = expected.allowed.some((state) => {
239
- if (state.state === "absent")
240
- return artifact === undefined;
241
- if (!artifact || artifact.nativeId !== expected.nativeId)
242
- return false;
243
- if (artifact.fingerprint !== state.fingerprint || artifact.bindingId !== state.bindingId)
244
- return false;
245
- return state.invocation === undefined
246
- ? artifact.invocation === undefined
247
- : artifact.invocation !== undefined && sameInvocation(artifact.invocation, state.invocation);
248
- });
249
- if (matches)
250
- return;
251
- throw new UsageError(`Native scheduler artifact ${JSON.stringify(expected.nativeId)} changed after the transaction began; refusing rollback over a concurrent owner or fingerprint.`, "RESOURCE_ALREADY_EXISTS");
252
- }
253
96
  /**
254
- * Validate a rollback target from one complete native inventory. Rollback
255
- * guards may allow either the transaction-written state or the prior state,
256
- * but neither state permits two portable-key-equivalent artifacts.
97
+ * Recover the logical id of an installed row from its native spelling and
98
+ * public invocation: an ordinal-zero task row is its task id (possibly a
99
+ * nested one behind a digest spelling); every other row is its native id.
257
100
  */
258
- export function assertSchedulerRollbackArtifactCardinality(artifacts, expected) {
259
- const matches = schedulerNativeArtifactsForKey(artifacts, expected.nativeId);
260
- if (matches.length > 1) {
261
- throw new UsageError(`Native scheduler artifact ${JSON.stringify(expected.nativeId)} changed after the transaction began: normalized rollback cardinality ${matches.length} exceeds one.`, "RESOURCE_ALREADY_EXISTS");
262
- }
263
- const artifact = matches[0];
264
- assertSchedulerRollbackArtifact(artifact, expected);
265
- return artifact;
266
- }
267
- export function canonicalSchedulerIdentity(logicalSource, ordinal, invocation) {
268
- const parsed = parseBundleRef(logicalSource.ref);
269
- if (!parsed.bundle || parsed.fragment !== undefined || bundleRefToString(parsed) !== logicalSource.ref) {
270
- throw new UsageError("Scheduler expectation requires one canonical fully-qualified source.", "INVALID_FLAG_VALUE");
271
- }
272
- if (!Number.isSafeInteger(ordinal) || ordinal < 0) {
273
- throw new UsageError("Scheduler expectation ordinal must be a non-negative integer.", "INVALID_FLAG_VALUE");
274
- }
275
- if (logicalSource.kind === "workflow") {
276
- const canonicalInvocation = ["workflow", "run", logicalSource.ref];
277
- if (!sameInvocation(invocation, canonicalInvocation)) {
278
- throw new UsageError("Workflow scheduler expectation invocation does not match its qualified source.", "INVALID_FLAG_VALUE");
279
- }
280
- const bindingId = digestBindingId("workflow", logicalSource.ref, ordinal);
281
- return Object.freeze({ bindingId, nativeId: bindingId, invocation: Object.freeze(canonicalInvocation) });
282
- }
101
+ export function schedulerLogicalBindingId(nativeId, invocation) {
283
102
  const taskId = invocation[0] === "task" && invocation[1] === "run" ? invocation[2] : undefined;
284
- if (!taskId || normaliseTaskConceptId(taskId) !== taskId) {
285
- throw new UsageError("Task scheduler expectation has an invalid public task id.", "INVALID_FLAG_VALUE");
286
- }
287
- const canonicalInvocation = ["task", "run", taskId, "--bundle", parsed.bundle, "--scheduled"];
288
- if (!sameInvocation(invocation, canonicalInvocation)) {
289
- throw new UsageError(`Task scheduler expectation invocation does not match its qualified source: expected ${JSON.stringify(canonicalInvocation)}, got ${JSON.stringify(invocation)}.`, "INVALID_FLAG_VALUE");
290
- }
291
- if (parsed.conceptId !== taskId && parsed.conceptId !== `tasks/${taskId}`) {
292
- throw new UsageError("Task scheduler expectation id does not match its qualified source.", "INVALID_FLAG_VALUE");
293
- }
294
- const bindingId = ordinal === 0 ? taskId : digestBindingId("task", logicalSource.ref, ordinal);
295
- return Object.freeze({
296
- bindingId,
297
- nativeId: schedulerNativeBindingId(bindingId),
298
- invocation: Object.freeze(canonicalInvocation),
299
- });
300
- }
301
- /** Recover the authored schedule ordinal from the exact source/binding identity. */
302
- export function schedulerBindingOrdinal(bindingId, logicalSource, invocation) {
303
- if (logicalSource.kind === "task" && bindingId === invocation[2])
304
- return 0;
305
- for (let ordinal = 0; ordinal < 4096; ordinal += 1) {
306
- if (digestBindingId(logicalSource.kind, logicalSource.ref, ordinal) === bindingId)
307
- return ordinal;
308
- }
309
- return undefined;
310
- }
311
- /** Whether some schedule ordinal on `ref` would digest to `nativeId` (mirrors schedulerBindingOrdinal). */
312
- function workflowRefDigestsToNativeId(ref, nativeId) {
313
- for (let ordinal = 0; ordinal < 4096; ordinal += 1) {
314
- if (digestBindingId("workflow", ref, ordinal) === nativeId)
315
- return true;
316
- }
317
- return false;
103
+ return taskId !== undefined && schedulerNativeBindingId(taskId) === nativeId ? taskId : nativeId;
318
104
  }
319
105
  function digestBindingId(kind, ref, ordinal) {
320
106
  const prefix = kind === "workflow" ? "wf" : "task";
@@ -338,9 +124,3 @@ function freezeBinding(binding) {
338
124
  invocation: Object.freeze([...binding.invocation]),
339
125
  });
340
126
  }
341
- function sameInvocation(left, right) {
342
- return left.length === right.length && left.every((value, index) => value === right[index]);
343
- }
344
- function normalizeRemovalExpectation(expected) {
345
- return { ...expected, state: "present" };
346
- }