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
@@ -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
- }