akm-cli 0.9.0-rc.0 → 0.9.0-rc.13

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 (598) hide show
  1. package/CHANGELOG.md +1283 -22
  2. package/README.md +62 -37
  3. package/SECURITY.md +46 -31
  4. package/dist/akm +162 -38
  5. package/dist/akm-migrate +44 -0
  6. package/dist/assets/backends/schtasks-template.xml +2 -1
  7. package/dist/assets/hints/cli-hints-full.md +268 -118
  8. package/dist/assets/hints/cli-hints-short.md +87 -24
  9. package/dist/assets/{profiles → improve-strategies}/catchup.json +3 -1
  10. package/dist/assets/{profiles → improve-strategies}/consolidate.json +3 -1
  11. package/dist/assets/{profiles → improve-strategies}/default.json +6 -7
  12. package/dist/assets/improve-strategies/frequent.json +15 -0
  13. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +4 -2
  14. package/dist/assets/{profiles → improve-strategies}/memory-focus.json +4 -1
  15. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +5 -5
  16. package/dist/assets/{profiles → improve-strategies}/quick.json +4 -2
  17. package/dist/assets/improve-strategies/reflect-distill.json +30 -0
  18. package/dist/assets/{profiles → improve-strategies}/thorough.json +1 -1
  19. package/dist/assets/prompts/consolidate-system.md +5 -5
  20. package/dist/assets/prompts/extract-session.md +2 -6
  21. package/dist/assets/prompts/memory-infer-user.md +2 -3
  22. package/dist/assets/prompts/reflect-llm-framed-contract.md +11 -0
  23. package/dist/assets/prompts/reflect-llm-schema-contract.md +3 -0
  24. package/dist/assets/prompts/reflect-output-repair.md +3 -0
  25. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  26. package/dist/assets/stash-skeleton/README.md +38 -10
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +8 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +8 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +14 -1
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +13 -1
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +9 -1
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +11 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +9 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +9 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +8 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +100 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/domains.md +64 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/organization.md +136 -0
  39. package/dist/assets/tasks/core/extract.yml +3 -2
  40. package/dist/assets/tasks/core/improve.yml +2 -1
  41. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  42. package/dist/assets/tasks/core/sync.yml +1 -0
  43. package/dist/assets/tasks/core/version-check.yml +2 -1
  44. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  45. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  46. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  47. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  48. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  49. package/dist/assets/templates/html/health.html +5 -4
  50. package/dist/assets/workflows/workflow-template.md +31 -15
  51. package/dist/cli/invocation.js +279 -0
  52. package/dist/cli/parse-args.js +5 -90
  53. package/dist/cli/retired-commands.js +78 -0
  54. package/dist/cli/shared.js +158 -48
  55. package/dist/cli-node.mjs +2 -1
  56. package/dist/cli.js +747 -293
  57. package/dist/commands/agent/agent-dispatch.js +19 -18
  58. package/dist/commands/agent/agent-support.js +0 -24
  59. package/dist/commands/agent/contribute-cli.js +43 -97
  60. package/dist/commands/completions.js +80 -23
  61. package/dist/commands/config-cli.js +44 -281
  62. package/dist/commands/env/env-binding.js +99 -0
  63. package/dist/commands/env/env-cli.js +84 -224
  64. package/dist/commands/env/env.js +12 -163
  65. package/dist/commands/env/marker-path.js +6 -0
  66. package/dist/commands/env/secret-cli.js +45 -61
  67. package/dist/commands/env/secret.js +32 -62
  68. package/dist/commands/feedback-cli.js +179 -85
  69. package/dist/commands/health/accept-rate.js +58 -0
  70. package/dist/commands/health/advisories.js +7 -8
  71. package/dist/commands/health/checks.js +279 -94
  72. package/dist/commands/health/html-report.js +197 -578
  73. package/dist/commands/health/improve-metrics.js +277 -246
  74. package/dist/commands/health/llm-usage.js +19 -19
  75. package/dist/commands/health/md-report.js +16 -7
  76. package/dist/commands/health/metrics.js +67 -32
  77. package/dist/commands/health/renderers.js +47 -0
  78. package/dist/commands/health/report-view-model.js +508 -0
  79. package/dist/commands/health/stash-exposure.js +1 -1
  80. package/dist/commands/health/surfaces.js +16 -56
  81. package/dist/commands/health/task-runs.js +3 -67
  82. package/dist/{migrate-storage-node.mjs → commands/health/types-checks.js} +1 -5
  83. package/dist/commands/health/types-improve.js +29 -0
  84. package/dist/{output/text/save.js → commands/health/types-metrics.js} +1 -2
  85. package/dist/commands/health/types-result.js +7 -0
  86. package/dist/commands/health/types-runs.js +4 -0
  87. package/dist/commands/health/types-session-log.js +4 -0
  88. package/dist/commands/health/types-windows.js +4 -0
  89. package/dist/commands/health/types.js +26 -21
  90. package/dist/commands/health/windows.js +2 -3
  91. package/dist/commands/health.js +296 -167
  92. package/dist/commands/improve/anti-collapse.js +5 -5
  93. package/dist/commands/improve/autonomy-gate.js +68 -0
  94. package/dist/commands/improve/collapse-detector.js +65 -52
  95. package/dist/commands/improve/consolidate/chunking.js +9 -7
  96. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  97. package/dist/commands/improve/consolidate/merge.js +4 -0
  98. package/dist/commands/improve/consolidate.js +454 -1354
  99. package/dist/commands/improve/content-hash.js +39 -0
  100. package/dist/commands/improve/distill/content-repair.js +4 -10
  101. package/dist/commands/improve/distill/promote-memory.js +89 -64
  102. package/dist/commands/improve/distill/quality-gate.js +118 -42
  103. package/dist/commands/improve/distill-guards.js +1 -1
  104. package/dist/commands/improve/distill-promotion-policy.js +33 -888
  105. package/dist/commands/improve/distill.js +607 -363
  106. package/dist/commands/improve/eligibility.js +165 -79
  107. package/dist/commands/improve/extract-cli.js +35 -126
  108. package/dist/commands/improve/extract-prompt.js +6 -35
  109. package/dist/commands/improve/extract.js +640 -391
  110. package/dist/commands/improve/feedback-valence.js +2 -12
  111. package/dist/commands/improve/improve-cli.js +134 -135
  112. package/dist/commands/improve/improve-result-file.js +30 -50
  113. package/dist/commands/improve/improve-run-types.js +4 -0
  114. package/dist/commands/improve/improve-strategies.js +135 -0
  115. package/dist/commands/improve/improve.js +904 -701
  116. package/dist/commands/improve/locks.js +64 -111
  117. package/dist/commands/improve/loop-stages.js +1110 -923
  118. package/dist/commands/improve/memory/derived-ref.js +124 -0
  119. package/dist/commands/improve/memory/memory-belief.js +79 -7
  120. package/dist/commands/improve/memory/memory-contradiction-detect.js +49 -52
  121. package/dist/commands/improve/memory/memory-improve.js +25 -37
  122. package/dist/commands/improve/outcome-loop.js +25 -88
  123. package/dist/commands/improve/preparation.js +1034 -813
  124. package/dist/commands/improve/proactive-maintenance.js +34 -9
  125. package/dist/commands/improve/proposal-envelope.js +31 -0
  126. package/dist/commands/improve/reflect.js +983 -794
  127. package/dist/commands/improve/run-context.js +119 -0
  128. package/dist/commands/improve/salience.js +24 -127
  129. package/dist/commands/improve/session-asset.js +7 -3
  130. package/dist/commands/improve/shared.js +14 -34
  131. package/dist/commands/improve/source-identity.js +28 -0
  132. package/dist/commands/improve/triage.js +20 -17
  133. package/dist/commands/lint/base-linter.js +340 -313
  134. package/dist/commands/lint/env-key-rules.js +31 -47
  135. package/dist/commands/lint/index.js +185 -30
  136. package/dist/commands/{events.js → log.js} +28 -38
  137. package/dist/commands/migrate-cli.js +54 -0
  138. package/dist/commands/migration-tool.js +55 -0
  139. package/dist/commands/observability-cli.js +70 -208
  140. package/dist/commands/proposal/diff-format.js +50 -0
  141. package/dist/commands/proposal/drain-policies.js +0 -6
  142. package/dist/commands/proposal/drain.js +91 -40
  143. package/dist/commands/proposal/proposal-cli.js +134 -132
  144. package/dist/commands/proposal/proposal-types.js +56 -0
  145. package/dist/commands/proposal/proposal.js +83 -65
  146. package/dist/commands/proposal/propose-cli.js +88 -0
  147. package/dist/commands/proposal/propose.js +105 -88
  148. package/dist/commands/proposal/repository.js +1303 -278
  149. package/dist/commands/proposal/validators/proposal-quality-validators.js +16 -6
  150. package/dist/commands/proposal/validators/proposal-validators.js +61 -12
  151. package/dist/commands/proposal/validators/proposals.js +6 -8
  152. package/dist/commands/read/curate.js +78 -73
  153. package/dist/commands/read/knowledge.js +510 -13
  154. package/dist/commands/read/registry-search.js +2 -2
  155. package/dist/commands/read/remember-cli.js +84 -15
  156. package/dist/commands/read/search-cli.js +203 -96
  157. package/dist/commands/read/search.js +126 -94
  158. package/dist/commands/read/show.js +226 -250
  159. package/dist/commands/registry-cli.js +34 -60
  160. package/dist/commands/remember.js +18 -57
  161. package/dist/commands/sources/add-cli.js +104 -49
  162. package/dist/commands/sources/bundle-cli.js +166 -0
  163. package/dist/commands/sources/bundle-config-ops.js +63 -0
  164. package/dist/commands/sources/info.js +27 -15
  165. package/dist/commands/sources/init.js +30 -40
  166. package/dist/commands/sources/installed-stashes.js +469 -172
  167. package/dist/commands/sources/migration-help.js +7 -4
  168. package/dist/commands/sources/schema-repair.js +10 -9
  169. package/dist/commands/sources/self-update.js +182 -121
  170. package/dist/commands/sources/source-add.js +169 -178
  171. package/dist/commands/sources/source-clone.js +144 -41
  172. package/dist/commands/sources/source-manage.js +94 -59
  173. package/dist/commands/sources/sources-cli.js +64 -205
  174. package/dist/commands/sources/stash-cli.js +91 -54
  175. package/dist/commands/sources/stash-skeleton.js +1 -1
  176. package/dist/commands/tasks/tasks-cli.js +106 -104
  177. package/dist/commands/tasks/tasks.js +445 -262
  178. package/dist/commands/workflow-cli.js +232 -121
  179. package/dist/core/action-contributors.js +1 -1
  180. package/dist/core/activation-policy.js +49 -0
  181. package/dist/core/adapter/adapters/agent-skills-adapter.js +181 -0
  182. package/dist/core/adapter/adapters/akm-adapter.js +528 -0
  183. package/dist/core/adapter/adapters/akm-lint.js +392 -0
  184. package/dist/core/adapter/adapters/akm-metadata.js +387 -0
  185. package/dist/core/adapter/adapters/akm-task-adapter.js +149 -0
  186. package/dist/core/adapter/adapters/akm-workflow-adapter.js +180 -0
  187. package/dist/core/adapter/adapters/claude-adapter.js +61 -0
  188. package/dist/core/adapter/adapters/dotenv-adapter.js +187 -0
  189. package/dist/core/adapter/adapters/generic-files-adapter.js +119 -0
  190. package/dist/core/adapter/adapters/index.js +80 -0
  191. package/dist/core/adapter/adapters/llm-wiki-adapter.js +419 -0
  192. package/dist/core/adapter/adapters/okf-adapter.js +391 -0
  193. package/dist/core/adapter/adapters/opencode-adapter.js +68 -0
  194. package/dist/core/adapter/adapters/shared.js +286 -0
  195. package/dist/core/adapter/adapters/tool-dir-shared.js +217 -0
  196. package/dist/core/adapter/adapters/website-snapshot-adapter.js +155 -0
  197. package/dist/core/adapter/bundle-adapter.js +4 -0
  198. package/dist/core/adapter/detect-adapter.js +17 -0
  199. package/dist/core/adapter/recognize-match.js +44 -0
  200. package/dist/core/adapter/registry.js +56 -0
  201. package/dist/core/adapter/types.js +4 -0
  202. package/dist/core/asset/akm-markdown.js +30 -0
  203. package/dist/core/asset/asset-placement.js +243 -0
  204. package/dist/core/asset/asset-ref.js +110 -79
  205. package/dist/core/asset/asset-serialize.js +20 -0
  206. package/dist/core/asset/frontmatter.js +28 -12
  207. package/dist/core/asset/markdown.js +40 -51
  208. package/dist/core/asset/resolve-ref.js +274 -0
  209. package/dist/core/asset/stash-meta.js +2 -2
  210. package/dist/core/bundle-id.js +51 -0
  211. package/dist/core/common.js +281 -86
  212. package/dist/core/config/config-io.js +42 -128
  213. package/dist/core/config/config-schema.js +233 -834
  214. package/dist/core/config/config-sources.js +162 -39
  215. package/dist/core/config/config-types.js +16 -11
  216. package/dist/core/config/config-version.js +29 -0
  217. package/dist/core/config/config-walker.js +126 -37
  218. package/dist/core/config/config.js +154 -331
  219. package/dist/core/config/deep-merge.js +41 -0
  220. package/dist/core/config/engine-semantics.js +28 -0
  221. package/dist/core/config/experimental.js +21 -0
  222. package/dist/core/config/schema/embedding.js +38 -0
  223. package/dist/core/config/schema/engines.js +116 -0
  224. package/dist/core/config/schema/experimental.js +47 -0
  225. package/dist/core/config/schema/feedback.js +31 -0
  226. package/dist/core/config/schema/improve-processes.js +389 -0
  227. package/dist/core/config/schema/improve.js +94 -0
  228. package/dist/core/config/schema/index-config.js +176 -0
  229. package/dist/core/config/schema/output.js +18 -0
  230. package/dist/core/config/schema/primitives.js +94 -0
  231. package/dist/core/config/schema/search.js +30 -0
  232. package/dist/core/config/schema/setup.js +18 -0
  233. package/dist/core/config/schema/sources-bundles.js +169 -0
  234. package/dist/core/config/schema/workflow.js +29 -0
  235. package/dist/core/env-secret-ref.js +155 -20
  236. package/dist/core/errors.js +17 -15
  237. package/dist/core/events-types.js +4 -0
  238. package/dist/core/events.js +46 -128
  239. package/dist/core/extra-params.js +62 -0
  240. package/dist/core/file-change.js +17 -0
  241. package/dist/core/file-lock.js +202 -57
  242. package/dist/core/fs-txn.js +392 -0
  243. package/dist/core/git-message.js +59 -0
  244. package/dist/core/improve-result.js +167 -0
  245. package/dist/core/json-schema.js +142 -0
  246. package/dist/core/lesson-lint.js +1 -17
  247. package/dist/core/logs-db.js +1 -1
  248. package/dist/core/maintenance-barrier.js +135 -0
  249. package/dist/core/migration-operation.js +44 -0
  250. package/dist/core/mutation-target.js +78 -0
  251. package/dist/core/paths.js +22 -25
  252. package/dist/core/platform.js +10 -0
  253. package/dist/core/recognition-util.js +128 -0
  254. package/dist/core/redaction.js +392 -0
  255. package/dist/core/standards/resolve-standards-context.js +36 -65
  256. package/dist/core/standards/resolve-stash-standards.js +2 -2
  257. package/dist/core/standards/resolve-type-conventions.js +5 -5
  258. package/dist/core/state/migrations.js +242 -11
  259. package/dist/core/state-db.js +98 -10
  260. package/dist/core/structured.js +1 -1
  261. package/dist/core/subprocess.js +303 -0
  262. package/dist/core/text-truncation.js +9 -5
  263. package/dist/core/time.js +20 -0
  264. package/dist/core/type-presentation.js +130 -0
  265. package/dist/core/warn.js +0 -3
  266. package/dist/core/write-source.js +834 -118
  267. package/dist/indexer/bundle-identity-guard.js +92 -0
  268. package/dist/indexer/db/graph-db.js +1 -25
  269. package/dist/indexer/db/llm-cache.js +1 -1
  270. package/dist/indexer/ensure-index.js +30 -9
  271. package/dist/indexer/graph/graph-boost.js +9 -30
  272. package/dist/indexer/graph/graph-extraction.js +41 -27
  273. package/dist/indexer/graph/graph-types.js +4 -0
  274. package/dist/indexer/index-writer-lock.js +93 -49
  275. package/dist/indexer/index-written-assets.js +100 -53
  276. package/dist/indexer/indexer.js +746 -329
  277. package/dist/indexer/init.js +18 -25
  278. package/dist/indexer/installations.js +142 -0
  279. package/dist/indexer/passes/dir-staleness.js +18 -10
  280. package/dist/indexer/passes/memory-inference.js +25 -15
  281. package/dist/indexer/passes/metadata.js +412 -243
  282. package/dist/indexer/scan/doc-to-entry.js +160 -0
  283. package/dist/indexer/scan/drain-dir.js +134 -0
  284. package/dist/indexer/search/db-search.js +292 -108
  285. package/dist/indexer/search/fts-query.js +64 -0
  286. package/dist/indexer/search/ranking-contributors.js +145 -25
  287. package/dist/indexer/search/ranking-types.js +4 -0
  288. package/dist/indexer/search/ranking.js +28 -71
  289. package/dist/indexer/search/search-attribution.js +67 -0
  290. package/dist/indexer/search/search-fields.js +18 -3
  291. package/dist/indexer/search/search-hit-enrichers.js +30 -40
  292. package/dist/indexer/search/search-source.js +157 -111
  293. package/dist/indexer/search/semantic-status.js +4 -1
  294. package/dist/indexer/usage/usage-events.js +10 -30
  295. package/dist/indexer/walk/file-context.js +3 -45
  296. package/dist/indexer/walk/matchers.js +42 -34
  297. package/dist/indexer/walk/path-resolver.js +11 -5
  298. package/dist/indexer/walk/walker.js +42 -14
  299. package/dist/integrations/agent/builder-shared.js +7 -0
  300. package/dist/integrations/agent/builders.js +5 -56
  301. package/dist/integrations/agent/config.js +3 -143
  302. package/dist/integrations/agent/detect.js +17 -2
  303. package/dist/integrations/agent/engine-resolution.js +231 -0
  304. package/dist/integrations/agent/index.js +1 -2
  305. package/dist/integrations/agent/model-aliases.js +16 -2
  306. package/dist/integrations/agent/profiles.js +36 -62
  307. package/dist/integrations/agent/prompts.js +46 -18
  308. package/dist/integrations/agent/runner-dispatch.js +93 -4
  309. package/dist/integrations/agent/runner.js +76 -208
  310. package/dist/integrations/agent/spawn.js +88 -196
  311. package/dist/integrations/harnesses/aider/agent-builder.js +114 -0
  312. package/dist/integrations/harnesses/aider/index.js +48 -0
  313. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  314. package/dist/integrations/harnesses/amazonq/agent-builder.js +147 -0
  315. package/dist/integrations/harnesses/amazonq/index.js +45 -0
  316. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  317. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  318. package/dist/integrations/harnesses/claude/config-import.js +1 -3
  319. package/dist/integrations/harnesses/claude/index.js +24 -35
  320. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  321. package/dist/integrations/harnesses/claude/session-log.js +27 -75
  322. package/dist/integrations/harnesses/codex/agent-builder.js +138 -0
  323. package/dist/integrations/harnesses/codex/index.js +52 -0
  324. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  325. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  326. package/dist/integrations/harnesses/copilot/index.js +48 -0
  327. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  328. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  329. package/dist/integrations/harnesses/gemini/index.js +48 -0
  330. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  331. package/dist/integrations/harnesses/ids.js +24 -0
  332. package/dist/integrations/harnesses/index.js +54 -34
  333. package/dist/integrations/harnesses/opencode/agent-builder.js +23 -5
  334. package/dist/integrations/harnesses/opencode/config-import.js +1 -3
  335. package/dist/integrations/harnesses/opencode/index.js +14 -32
  336. package/dist/integrations/harnesses/opencode/session-log.js +67 -125
  337. package/dist/integrations/harnesses/opencode-sdk/harness.js +51 -0
  338. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  339. package/dist/integrations/harnesses/openhands/agent-builder.js +128 -0
  340. package/dist/integrations/harnesses/openhands/index.js +48 -0
  341. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  342. package/dist/integrations/harnesses/pi/agent-builder.js +97 -0
  343. package/dist/integrations/harnesses/pi/index.js +45 -0
  344. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  345. package/dist/integrations/harnesses/shared.js +17 -0
  346. package/dist/integrations/harnesses/types.js +43 -32
  347. package/dist/integrations/lockfile.js +211 -24
  348. package/dist/integrations/session-logs/index.js +36 -39
  349. package/dist/integrations/session-logs/provider-base.js +113 -0
  350. package/dist/llm/client.js +182 -110
  351. package/dist/llm/embedders/deterministic.js +2 -2
  352. package/dist/llm/embedders/remote.js +21 -9
  353. package/dist/llm/feature-gate.js +17 -57
  354. package/dist/llm/graph-extract.js +12 -13
  355. package/dist/llm/index-passes.js +8 -42
  356. package/dist/llm/memory-infer.js +144 -1
  357. package/dist/llm/metadata-enhance.js +45 -30
  358. package/dist/llm/structured-call.js +16 -8
  359. package/dist/llm/usage-persist.js +30 -5
  360. package/dist/llm/usage-telemetry.js +59 -6
  361. package/dist/output/cli-hints.js +1 -2
  362. package/dist/output/command-registry.js +27 -0
  363. package/dist/output/context.js +22 -7
  364. package/dist/output/format-exempt.js +80 -0
  365. package/dist/output/generic-render.js +251 -0
  366. package/dist/output/html-render.js +11 -16
  367. package/dist/output/render-registry.js +57 -0
  368. package/dist/output/renderers.js +14 -279
  369. package/dist/output/shapes/curate.js +10 -1
  370. package/dist/output/shapes/events.js +12 -7
  371. package/dist/output/shapes/helpers.js +58 -84
  372. package/dist/output/shapes/passthrough.js +11 -39
  373. package/dist/output/shapes/proposal/producer.js +15 -7
  374. package/dist/output/shapes/registry.js +12 -6
  375. package/dist/output/shapes.js +0 -9
  376. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  377. package/dist/output/text/bundle-show.js +7 -0
  378. package/dist/output/text/command-format.js +562 -0
  379. package/dist/output/text/env.js +1 -3
  380. package/dist/output/text/events.js +8 -7
  381. package/dist/output/text/helpers.js +15 -1164
  382. package/dist/output/text/proposal/producer.js +4 -2
  383. package/dist/output/text/proposal-format.js +202 -0
  384. package/dist/output/text/registry-commands.js +1 -2
  385. package/dist/output/text/registry.js +12 -6
  386. package/dist/output/text/show-directives.js +117 -0
  387. package/dist/output/text/show-format.js +103 -0
  388. package/dist/output/text/sync.js +5 -0
  389. package/dist/output/text/workflow-format.js +332 -0
  390. package/dist/output/text/workflow.js +3 -2
  391. package/dist/output/text.js +10 -19
  392. package/dist/registry/factory.js +4 -6
  393. package/dist/registry/origin-resolve.js +16 -27
  394. package/dist/registry/providers/skills-sh.js +3 -3
  395. package/dist/registry/providers/static-index.js +15 -25
  396. package/dist/registry/resolve.js +43 -94
  397. package/dist/registry/semver.js +43 -0
  398. package/dist/runtime.js +81 -12
  399. package/dist/scripts/akm-migrate.js +35529 -0
  400. package/dist/setup/detect.js +5 -7
  401. package/dist/setup/detected-engines.js +136 -0
  402. package/dist/setup/engine-config.js +100 -0
  403. package/dist/setup/registry-stash-loader.js +3 -3
  404. package/dist/setup/semantic-assets.js +12 -9
  405. package/dist/setup/setup.js +444 -208
  406. package/dist/setup/steps/connection-shared.js +120 -0
  407. package/dist/setup/steps/connection.js +108 -305
  408. package/dist/setup/steps/platforms.js +13 -12
  409. package/dist/setup/steps/semantic.js +15 -3
  410. package/dist/setup/steps/sources.js +21 -15
  411. package/dist/setup/steps/stashdir.js +6 -4
  412. package/dist/setup/steps/tasks.js +236 -119
  413. package/dist/setup/steps.js +3 -2
  414. package/dist/sources/freshness.js +39 -0
  415. package/dist/sources/provider-factory.js +11 -17
  416. package/dist/sources/providers/filesystem.js +2 -3
  417. package/dist/sources/providers/git-install.js +278 -34
  418. package/dist/sources/providers/git-provider.js +54 -56
  419. package/dist/sources/providers/git-stash.js +420 -91
  420. package/dist/sources/providers/git.js +2 -2
  421. package/dist/sources/providers/npm.js +16 -19
  422. package/dist/sources/providers/provider-utils.js +47 -22
  423. package/dist/sources/providers/sync-from-ref.js +3 -9
  424. package/dist/sources/providers/website.js +2 -2
  425. package/dist/sources/resolve.js +11 -10
  426. package/dist/sources/snapshot-fetchers/types.js +4 -0
  427. package/dist/sources/{website-ingest.js → snapshot-fetchers/website-ingest.js} +110 -41
  428. package/dist/storage/database.js +60 -4
  429. package/dist/storage/engines/sqlite-migrations.js +156 -5
  430. package/dist/storage/locations.js +1 -2
  431. package/dist/storage/repositories/canaries-repository.js +1 -1
  432. package/dist/storage/repositories/events-repository.js +51 -11
  433. package/dist/storage/repositories/improve-runs-repository.js +6 -32
  434. package/dist/storage/repositories/index-connection.js +79 -0
  435. package/dist/storage/repositories/index-db.js +4 -3
  436. package/dist/storage/repositories/index-entries-repository.js +863 -0
  437. package/dist/{indexer/db/entry-mapper.js → storage/repositories/index-entry-mapper.js} +19 -2
  438. package/dist/storage/repositories/index-entry-types.js +4 -0
  439. package/dist/storage/repositories/index-fts-repository.js +167 -0
  440. package/dist/storage/repositories/index-llm-cache-repository.js +108 -0
  441. package/dist/storage/repositories/index-meta-repository.js +49 -0
  442. package/dist/{indexer/db/schema.js → storage/repositories/index-schema.js} +226 -100
  443. package/dist/storage/repositories/index-sql.js +12 -0
  444. package/dist/storage/repositories/index-utility-repository.js +356 -0
  445. package/dist/storage/repositories/index-vec-repository.js +250 -0
  446. package/dist/storage/repositories/outcome-repository.js +119 -0
  447. package/dist/storage/repositories/proposals-repository.js +317 -75
  448. package/dist/storage/repositories/registry-cache.js +1 -1
  449. package/dist/storage/repositories/salience-repository.js +172 -0
  450. package/dist/storage/repositories/task-history-repository.js +110 -3
  451. package/dist/storage/repositories/workflow-runs-repository.js +240 -19
  452. package/dist/tasks/backends/cron.js +169 -46
  453. package/dist/tasks/backends/exec-utils.js +76 -3
  454. package/dist/tasks/backends/index.js +6 -9
  455. package/dist/tasks/backends/launchd.js +292 -55
  456. package/dist/tasks/backends/schtasks.js +557 -70
  457. package/dist/tasks/backends/types.js +4 -0
  458. package/dist/tasks/command-executable.js +93 -0
  459. package/dist/tasks/embedded.js +56 -38
  460. package/dist/tasks/parser.js +156 -64
  461. package/dist/tasks/resolve-akm-bin.js +144 -51
  462. package/dist/tasks/runner.js +377 -209
  463. package/dist/tasks/schedule.js +108 -19
  464. package/dist/tasks/scheduler-invocation.js +296 -0
  465. package/dist/tasks/schema.js +1 -1
  466. package/dist/tasks/task-id.js +35 -0
  467. package/dist/tasks/validator.js +30 -16
  468. package/dist/text-import-hook.mjs +1 -1
  469. package/dist/workflows/authoring/authoring.js +104 -43
  470. package/dist/workflows/authoring/scope-key.js +1 -1
  471. package/dist/workflows/cli.js +0 -16
  472. package/dist/workflows/concurrency-policy.js +15 -0
  473. package/dist/workflows/exec/brief.js +450 -0
  474. package/dist/workflows/exec/frozen-judge.js +47 -0
  475. package/dist/workflows/exec/native-executor.js +1038 -0
  476. package/dist/workflows/exec/param-secrets.js +115 -0
  477. package/dist/workflows/exec/report.js +1460 -0
  478. package/dist/workflows/exec/run-workflow.js +602 -0
  479. package/dist/workflows/exec/scheduler.js +71 -0
  480. package/dist/workflows/exec/step-work.js +1190 -0
  481. package/dist/workflows/exec/unit-writer.js +23 -0
  482. package/dist/workflows/exec/workflow-engine-gate.js +67 -0
  483. package/dist/workflows/exec/worktree.js +171 -0
  484. package/dist/workflows/ir/compile.js +246 -0
  485. package/dist/workflows/ir/freeze.js +233 -0
  486. package/dist/workflows/ir/params.js +54 -0
  487. package/dist/workflows/ir/plan-hash.js +68 -0
  488. package/dist/workflows/ir/schema.js +540 -0
  489. package/dist/workflows/parser.js +878 -304
  490. package/dist/workflows/program/expressions.js +181 -0
  491. package/dist/workflows/program/schema.js +51 -0
  492. package/dist/workflows/renderer.js +100 -45
  493. package/dist/workflows/resource-limits.js +22 -0
  494. package/dist/workflows/runtime/agent-identity.js +59 -14
  495. package/dist/workflows/runtime/checkin.js +1 -1
  496. package/dist/workflows/runtime/plan-classifier.js +131 -0
  497. package/dist/workflows/runtime/runs.js +376 -119
  498. package/dist/workflows/runtime/unit-checkin.js +45 -0
  499. package/dist/workflows/runtime/unit-phases.js +20 -0
  500. package/dist/workflows/runtime/workflow-asset-loader.js +241 -40
  501. package/dist/workflows/schema.js +1 -11
  502. package/dist/workflows/validate-summary.js +2 -3
  503. package/dist/workflows/validator.js +52 -30
  504. package/docs/README.md +42 -78
  505. package/docs/migration/README.md +8 -0
  506. package/docs/migration/release-notes/0.6.0.md +1 -1
  507. package/docs/migration/release-notes/0.7.0.md +9 -8
  508. package/docs/migration/release-notes/0.9.0.md +158 -14
  509. package/docs/migration/v0.7-to-v0.8.md +46 -47
  510. package/docs/migration/v0.8-to-v0.9.md +844 -0
  511. package/docs/reference/README.md +12 -0
  512. package/docs/reference/data-and-telemetry.md +333 -0
  513. package/package.json +21 -17
  514. package/schemas/akm-asset-envelope.json +93 -0
  515. package/schemas/akm-config.json +4636 -0
  516. package/schemas/akm-task.json +87 -0
  517. package/schemas/akm-workflow.json +373 -0
  518. package/dist/akm-migrate-storage +0 -38
  519. package/dist/assets/help/help-accept.md +0 -12
  520. package/dist/assets/help/help-improve.md +0 -84
  521. package/dist/assets/help/help-proposals.md +0 -17
  522. package/dist/assets/help/help-propose.md +0 -17
  523. package/dist/assets/help/help-reject.md +0 -11
  524. package/dist/assets/profiles/frequent.json +0 -13
  525. package/dist/assets/profiles/recombine-only.json +0 -21
  526. package/dist/assets/profiles/reflect-distill.json +0 -30
  527. package/dist/assets/profiles/synthesize.json +0 -15
  528. package/dist/assets/prompts/procedural-system.md +0 -44
  529. package/dist/assets/prompts/recombine-system.md +0 -40
  530. package/dist/assets/prompts/staleness-detect-system.md +0 -6
  531. package/dist/assets/tasks/core/backup.yml +0 -4
  532. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  533. package/dist/assets/templates/html/default.html +0 -78
  534. package/dist/assets/templates/html/vendor/echarts.min.js +0 -45
  535. package/dist/assets/wiki/index-template.md +0 -12
  536. package/dist/assets/wiki/ingest-workflow-template.md +0 -83
  537. package/dist/assets/wiki/log-template.md +0 -8
  538. package/dist/assets/wiki/schema-template.md +0 -61
  539. package/dist/cli/config-migrate.js +0 -150
  540. package/dist/cli/config-validate.js +0 -39
  541. package/dist/commands/graph/graph-cli.js +0 -124
  542. package/dist/commands/graph/graph.js +0 -487
  543. package/dist/commands/improve/calibration.js +0 -161
  544. package/dist/commands/improve/dedup.js +0 -482
  545. package/dist/commands/improve/extract-watch.js +0 -140
  546. package/dist/commands/improve/hot-probation.js +0 -45
  547. package/dist/commands/improve/improve-auto-accept.js +0 -276
  548. package/dist/commands/improve/improve-profiles.js +0 -168
  549. package/dist/commands/improve/procedural.js +0 -398
  550. package/dist/commands/improve/recombine.js +0 -818
  551. package/dist/commands/improve/schema-similarity-gate.js +0 -168
  552. package/dist/commands/lint/agent-linter.js +0 -44
  553. package/dist/commands/lint/command-linter.js +0 -44
  554. package/dist/commands/lint/default-linter.js +0 -16
  555. package/dist/commands/lint/fact-linter.js +0 -39
  556. package/dist/commands/lint/knowledge-linter.js +0 -16
  557. package/dist/commands/lint/memory-linter.js +0 -61
  558. package/dist/commands/lint/registry.js +0 -41
  559. package/dist/commands/lint/skill-linter.js +0 -45
  560. package/dist/commands/lint/task-linter.js +0 -50
  561. package/dist/commands/lint/workflow-linter.js +0 -81
  562. package/dist/commands/proposal/legacy-import.js +0 -115
  563. package/dist/commands/sources/history.js +0 -196
  564. package/dist/commands/tasks/default-tasks.js +0 -186
  565. package/dist/commands/wiki-cli.js +0 -292
  566. package/dist/core/asset/asset-registry.js +0 -76
  567. package/dist/core/asset/asset-spec.js +0 -259
  568. package/dist/core/config/config-migration.js +0 -602
  569. package/dist/core/deep-merge.js +0 -38
  570. package/dist/core/eval/rank-metrics.js +0 -113
  571. package/dist/core/ripgrep/install.js +0 -163
  572. package/dist/core/ripgrep/resolve.js +0 -81
  573. package/dist/indexer/db/db.js +0 -1413
  574. package/dist/indexer/manifest.js +0 -170
  575. package/dist/indexer/passes/metadata-contributors.js +0 -31
  576. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -94
  577. package/dist/integrations/harnesses/opencode-sdk/index.js +0 -49
  578. package/dist/llm/call-ai.js +0 -62
  579. package/dist/llm/memory-infer-impl.js +0 -138
  580. package/dist/output/shapes/distill.js +0 -14
  581. package/dist/output/shapes/history.js +0 -11
  582. package/dist/output/text/distill.js +0 -6
  583. package/dist/output/text/enable-disable.js +0 -8
  584. package/dist/output/text/history.js +0 -6
  585. package/dist/output/text/wiki.js +0 -16
  586. package/dist/registry/build-index.js +0 -386
  587. package/dist/scripts/migrate-storage.js +0 -19108
  588. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +0 -9411
  589. package/dist/scripts/migrations/v16-to-v17.js +0 -141
  590. package/dist/setup/legacy-config.js +0 -106
  591. package/dist/storage/repositories/consolidation-repository.js +0 -38
  592. package/dist/storage/repositories/recombine-repository.js +0 -213
  593. package/dist/wiki/wiki-templates.js +0 -15
  594. package/dist/wiki/wiki.js +0 -1012
  595. package/dist/workflows/db.js +0 -215
  596. package/docs/data-and-telemetry.md +0 -226
  597. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/registry.js +0 -0
  598. /package/dist/sources/{wiki-fetchers → snapshot-fetchers}/youtube.js +0 -0
@@ -2,24 +2,100 @@
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
  import { randomUUID } from "node:crypto";
5
- import { parseAssetRef } from "../../core/asset/asset-ref.js";
5
+ import { parseBundleRef } from "../../core/asset/asset-ref.js";
6
6
  import { loadConfig } from "../../core/config/config.js";
7
7
  import { NotFoundError, UsageError } from "../../core/errors.js";
8
8
  import { appendEvent } from "../../core/events.js";
9
+ import { warn } from "../../core/warn.js";
10
+ import { insertEventOnce } from "../../storage/repositories/events-repository.js";
9
11
  import { withWorkflowRunsRepo, } from "../../storage/repositories/workflow-runs-repository.js";
10
12
  import { getCurrentWorkflowScopeKey } from "../authoring/scope-key.js";
13
+ import { frozenSummaryJudge } from "../exec/frozen-judge.js";
14
+ import { detectSecretShapedParams } from "../exec/param-secrets.js";
15
+ import { collectWorkflowWarnings } from "../ir/compile.js";
16
+ import { compileResolveFreezeWorkflow } from "../ir/freeze.js";
17
+ import { validateWorkflowParams } from "../ir/params.js";
18
+ import { canonicalPlanJson, computePlanHash } from "../ir/plan-hash.js";
19
+ import { decodeWorkflowPlanV3, WORKFLOW_IR_VERSION } from "../ir/schema.js";
11
20
  import { validateStepSummary } from "../validate-summary.js";
12
21
  import { resolveAgentIdentity } from "./agent-identity.js";
13
22
  import { evaluateCheckin } from "./checkin.js";
14
- import { loadWorkflowAsset, resolveWorkflowEntryId } from "./workflow-asset-loader.js";
23
+ import { assertWorkflowSpineMatchesPlan, classifyWorkflowRunPlan, frozenStepRows, requireExecutableWorkflowPlan, } from "./plan-classifier.js";
24
+ import { evaluateStaleUnits } from "./unit-checkin.js";
25
+ import { canonicalizeWorkflowRefInput, loadWorkflowAsset, resolveWorkflowEntryId } from "./workflow-asset-loader.js";
26
+ /** Clip bound for a unit's `result_json` on the `--units` diagnostic surface. */
27
+ const UNIT_DIAGNOSTIC_CLIP = 2000;
28
+ function toUnitDiagnostic(row, stale, plannedEngine) {
29
+ let diagnostic = null;
30
+ if (row.result_json !== null) {
31
+ // `result_json` is a JSON-encoded value: a bare JSON string for a free-text
32
+ // unit, an object/array for a schema unit. Render the decoded string as-is
33
+ // (no surrounding quotes) and other shapes as compact JSON, then clip so a
34
+ // large artifact can't flood the diagnostic surface.
35
+ let text = row.result_json;
36
+ try {
37
+ const parsed = JSON.parse(row.result_json);
38
+ text = typeof parsed === "string" ? parsed : JSON.stringify(parsed);
39
+ }
40
+ catch {
41
+ /* leave the raw journaled text */
42
+ }
43
+ diagnostic = text.length > UNIT_DIAGNOSTIC_CLIP ? `${text.slice(0, UNIT_DIAGNOSTIC_CLIP)}…` : text;
44
+ }
45
+ return {
46
+ unitId: row.unit_id,
47
+ nodeId: row.node_id,
48
+ stepId: row.step_id,
49
+ phase: row.phase,
50
+ status: row.status,
51
+ attempts: row.attempts,
52
+ tokens: row.tokens,
53
+ failureReason: row.failure_reason,
54
+ sessionId: row.session_id,
55
+ diagnostic,
56
+ startedAt: row.started_at,
57
+ finishedAt: row.finished_at,
58
+ stale: stale !== undefined,
59
+ staleIdleMs: stale ? (Number.isFinite(stale.idleMs) ? stale.idleMs : null) : null,
60
+ claimHolder: row.claim_holder,
61
+ claimExpiresAt: row.claim_expires_at,
62
+ engine: row.engine ?? null,
63
+ runtimeKind: row.engine && (row.runner === "llm" || row.runner === "agent" || row.runner === "sdk") ? row.runner : null,
64
+ platform: plannedEngine?.kind === "agent" ? plannedEngine.platform : null,
65
+ };
66
+ }
15
67
  export async function startWorkflowRun(ref, params = {}, options) {
16
68
  const asset = await loadWorkflowAsset(ref);
69
+ // Frozen plan (redesign addendum, R1): compile the plan ONCE at start and
70
+ // persist it on the run row in the same transaction as the insert. Every
71
+ // later invocation executes this snapshot — the asset file is never re-read
72
+ // for an in-flight run; re-planning is an explicit new run.
73
+ const plan = decodeWorkflowPlanV3(compileResolveFreezeWorkflow(asset, loadConfig()).plan);
74
+ // Non-fatal WARNINGS: untyped-step and undeclared-param advisories surface
75
+ // as `warn()` lines at start (stderr, consistent with the repo's other
76
+ // author-facing warnings) without blocking the run.
77
+ for (const w of collectWorkflowWarnings(asset.document)) {
78
+ warn(`workflow start: ${asset.path}:${w.line} — ${w.message}`);
79
+ }
80
+ // Reviewer #12: validate supplied `--params` against the frozen param
81
+ // schemas BEFORE creating the run, so a type-mismatched param (e.g. a string
82
+ // for a `{ type: array }` param) is rejected with actionable errors instead
83
+ // of flowing silently into a unit prompt. Programs without declared param
84
+ // schemas (and every Markdown workflow) validate trivially.
85
+ const paramErrors = validateWorkflowParams(plan, params);
86
+ if (paramErrors.length > 0) {
87
+ throw new UsageError(`Cannot start ${asset.ref}: the supplied --params do not satisfy the workflow's declared parameter schemas:\n` +
88
+ paramErrors.map((e) => ` - ${e}`).join("\n"), "INVALID_JSON_ARGUMENT");
89
+ }
90
+ const planJson = canonicalPlanJson(plan);
91
+ const planHash = computePlanHash(plan);
92
+ const workflowRefs = await workflowRunRefSet(asset.ref, ref);
17
93
  return withWorkflowRunsRepo(async (repo) => {
18
94
  const now = new Date().toISOString();
19
95
  const runId = randomUUID();
20
96
  const scopeKey = getCurrentWorkflowScopeKey();
21
- const currentStepId = asset.steps[0]?.id ?? null;
22
- const workflowEntryId = resolveWorkflowEntryId(asset.sourcePath, asset.ref);
97
+ const currentStepId = plan.steps[0]?.stepId ?? null;
98
+ const workflowEntryId = resolveWorkflowEntryId(asset.sourcePath, asset.ref, asset.adapterId);
23
99
  // Capture the agent harness + session driving this run. Explicit options
24
100
  // win; otherwise fall back to best-effort environment detection. This is
25
101
  // identity-only — no background thread or timer is started here.
@@ -30,19 +106,20 @@ export async function startWorkflowRun(ref, params = {}, options) {
30
106
  // (workflow_ref, scope_key) pair, refuse to create a parallel run unless
31
107
  // `force: true` is set. Previously every call inserted unconditionally,
32
108
  // so two terminals running `akm workflow start <ref>` left two runs
33
- // racing; `akm workflow next` then non-deterministically picked one.
34
- if (!options?.force) {
35
- const existing = repo.findActiveRunForScope(asset.ref, scopeKey);
36
- if (existing) {
37
- throw new UsageError(`Workflow ${asset.ref} already has an active run in this scope (id=${existing.id}, step=${existing.current_step_id ?? "—"}). ` +
38
- `Use 'akm workflow next ${asset.ref}' to resume it, 'akm workflow abandon ${existing.id}' to give up on it, or pass --force to start a parallel run.`, "RESOURCE_ALREADY_EXISTS");
39
- }
40
- }
109
+ // racing; `akm workflow next` then non-deterministically picked one. The
110
+ // active-alias query and all inserts now share this immediate transaction.
41
111
  // #506: arm a file-signal check-in (a timestamp, NOT a background thread —
42
- // see docs/technical/workflow-agent-checkin-adr.md) so a stalled run can be
112
+ // per the workflow-agent check-in ADR) so a stalled run can be
43
113
  // re-targeted with a `continue` directive. The agent harness + session id
44
114
  // are already resolved above (agentHarness/agentSessionId, from #501).
45
- repo.transaction(() => {
115
+ repo.immediateTransaction(() => {
116
+ if (!options?.force) {
117
+ const existing = repo.findActiveRunForScope(workflowRefs, scopeKey);
118
+ if (existing) {
119
+ throw new UsageError(`Workflow ${asset.ref} already has an active run in this scope (id=${existing.id}, step=${existing.current_step_id ?? "—"}). ` +
120
+ `Use 'akm workflow next ${asset.ref}' to resume it, 'akm workflow abandon ${existing.id}' to give up on it, or pass --force to start a parallel run.`, "RESOURCE_ALREADY_EXISTS");
121
+ }
122
+ }
46
123
  repo.insertRun({
47
124
  id: runId,
48
125
  workflowRef: asset.ref,
@@ -57,16 +134,26 @@ export async function startWorkflowRun(ref, params = {}, options) {
57
134
  agentSessionId,
58
135
  checkinArmedAt: now,
59
136
  });
60
- repo.insertSteps(asset.steps.map((step) => ({
137
+ repo.insertSteps(frozenStepRows(plan).map((step) => ({
61
138
  runId,
62
- stepId: step.id,
63
- stepTitle: step.title,
139
+ stepId: step.stepId,
140
+ stepTitle: step.stepTitle,
64
141
  instructions: step.instructions,
65
- completionJson: step.completionCriteria ? JSON.stringify(step.completionCriteria) : null,
66
- sequenceIndex: step.sequenceIndex ?? 0,
142
+ completionJson: step.completionJson,
143
+ sequenceIndex: step.sequenceIndex,
67
144
  })));
145
+ // Same transaction as the insert: a run row never exists without its frozen plan.
146
+ repo.setRunPlan(runId, planJson, planHash, WORKFLOW_IR_VERSION);
68
147
  });
69
148
  const result = await getWorkflowStatus(runId);
149
+ // #13: params are declared non-secret (they are copied verbatim into every
150
+ // unit prompt and hashed into the unit identity, so they cannot be redacted
151
+ // without breaking the driver protocol). Surface a loud, best-effort warning
152
+ // when a param LOOKS like a credential so the author moves it to an env
153
+ // binding. Advisory only — never blocks the start.
154
+ const secretWarnings = detectSecretShapedParams(params);
155
+ if (secretWarnings.length > 0)
156
+ result.warnings = [...(result.warnings ?? []), ...secretWarnings];
70
157
  // 07 P1-B: emit only the run id + status — NOT the raw workflowTitle (which
71
158
  // comes verbatim from the workflow asset's frontmatter and is therefore
72
159
  // attacker-influenceable). Keeping raw titles out of the events stream
@@ -74,78 +161,145 @@ export async function startWorkflowRun(ref, params = {}, options) {
74
161
  // into agent context.
75
162
  appendEvent({
76
163
  eventType: "workflow_started",
77
- ref: ref,
164
+ ref: asset.ref,
78
165
  metadata: { runId: result.run.id, status: result.run.status },
79
166
  });
80
167
  return result;
81
168
  });
82
169
  }
83
- export async function getWorkflowStatus(runId) {
170
+ export async function getWorkflowStatus(runId, opts) {
84
171
  return withWorkflowRunsRepo((repo) => {
85
172
  const run = readWorkflowRun(repo, runId);
86
173
  const steps = readWorkflowRunSteps(repo, run.id);
87
- return buildWorkflowRunDetail(run, steps);
174
+ const detail = buildWorkflowRunDetail(run, steps);
175
+ if (opts?.includeUnits) {
176
+ // The honest diagnostic surface (#22): read the unit journal straight and
177
+ // project each row, INCLUDING failures whose diagnostic text the
178
+ // deterministic evidence graph drops. Read-only; never mutates the run.
179
+ const rows = repo.getUnitsForRun(run.id);
180
+ // Codex round-3 finding B: run the SAME pure stale-claim evaluator `brief`
181
+ // uses (`now` injected for deterministic tests) so a dead driver's claimed
182
+ // `running` unit surfaces as stale here too, not just as raw `running`.
183
+ const staleById = new Map(evaluateStaleUnits(rows, opts.now ?? Date.now()).map((u) => [u.unitId, u]));
184
+ const classified = classifyWorkflowRunPlan(run);
185
+ const engines = classified.support === "supported" ? classified.plan.execution?.engines : undefined;
186
+ detail.units = rows.map((row) => toUnitDiagnostic(row, staleById.get(row.unit_id), row.engine ? engines?.[row.engine] : undefined));
187
+ }
188
+ return detail;
88
189
  });
89
190
  }
90
191
  export async function hasWorkflowRun(runId) {
91
192
  return withWorkflowRunsRepo((repo) => repo.hasRun(runId));
92
193
  }
93
194
  export async function listWorkflowRuns(input) {
94
- return withWorkflowRunsRepo((repo) => {
95
- const scopeKey = getCurrentWorkflowScopeKey();
96
- let workflowRef;
97
- if (input?.workflowRef) {
98
- const parsed = parseAssetRef(input.workflowRef);
99
- if (parsed.type !== "workflow") {
100
- throw new UsageError(`Expected a workflow ref (workflow:<name>), got "${input.workflowRef}".`);
101
- }
102
- workflowRef = `${parsed.origin ? `${parsed.origin}//` : ""}workflow:${parsed.name}`;
195
+ const scopeKey = getCurrentWorkflowScopeKey();
196
+ const activeOnly = input?.activeOnly === true;
197
+ if (input?.workflowRef === undefined) {
198
+ return withWorkflowRunsRepo((repo) => ({
199
+ runs: repo.listRuns({ scopeKey, ...(activeOnly ? { activeOnly: true } : {}) }).map(toWorkflowRunSummary),
200
+ }));
201
+ }
202
+ const exactRef = input.workflowRef.trim();
203
+ if (!exactRef) {
204
+ throw new UsageError("Workflow ref filter cannot be empty.", "INVALID_FLAG_VALUE");
205
+ }
206
+ const parsedExactRef = parseBundleRef(exactRef);
207
+ if (parsedExactRef.fragment !== undefined) {
208
+ throw new UsageError("Workflow ref filters do not accept fragments.", "INVALID_FLAG_VALUE");
209
+ }
210
+ let workflowRefs = [exactRef];
211
+ try {
212
+ const canonicalRef = await canonicalizeWorkflowSpecifier(exactRef);
213
+ workflowRefs = await workflowRunRefSet(canonicalRef, exactRef);
214
+ }
215
+ catch (error) {
216
+ if (parsedExactRef.bundle !== undefined) {
217
+ const exactRows = await withWorkflowRunsRepo((repo) => repo.listRuns({ scopeKey, workflowRef: exactRef }));
218
+ if (exactRows.length === 0)
219
+ throw error;
220
+ return {
221
+ runs: exactRows.filter((row) => !activeOnly || row.status === "active").map(toWorkflowRunSummary),
222
+ };
103
223
  }
104
- const rows = repo.listRuns({
105
- scopeKey,
106
- ...(workflowRef ? { workflowRef } : {}),
107
- ...(input?.activeOnly ? { activeOnly: true } : {}),
108
- });
109
- return { runs: rows.map(toWorkflowRunSummary) };
110
- });
224
+ if (!(error instanceof NotFoundError))
225
+ throw error;
226
+ }
227
+ return withWorkflowRunsRepo((repo) => ({
228
+ runs: repo
229
+ .listRuns({ scopeKey, workflowRefs, ...(activeOnly ? { activeOnly: true } : {}) })
230
+ .map(toWorkflowRunSummary),
231
+ }));
111
232
  }
112
233
  export async function getNextWorkflowStep(specifier, params) {
113
234
  return withWorkflowRunsRepo(async (repo) => {
114
235
  const { run, autoStarted } = await resolveRunSpecifier(repo, specifier, params);
115
236
  const steps = readWorkflowRunSteps(repo, run.id);
116
- const currentStep = resolveCurrentStep(run, steps);
117
- const done = run.status === "completed" ? true : undefined;
118
- // #506: surface a check-in directive through the normal command output when
119
- // the run looks stalled. Pure timestamp evaluation — no background thread.
120
- const checkin = evaluateCheckin({
121
- status: run.status,
122
- updatedAt: run.updated_at,
123
- checkinArmedAt: run.checkin_armed_at,
124
- agentHarness: run.agent_harness,
125
- agentSessionId: run.agent_session_id,
126
- }) ?? undefined;
127
- return {
128
- run: toWorkflowRunSummary(run),
129
- workflow: {
130
- ref: run.workflow_ref,
131
- title: run.workflow_title,
132
- steps: steps.map(toWorkflowRunStepState),
133
- },
134
- step: currentStep ? toWorkflowRunStepState(currentStep) : null,
135
- ...(done ? { done } : {}),
136
- ...(autoStarted ? { autoStarted } : {}),
137
- ...(checkin ? { checkin } : {}),
138
- };
237
+ const plan = requireExecutableWorkflowPlan(run);
238
+ assertWorkflowSpineMatchesPlan(plan, run, steps);
239
+ return { ...projectNextResult(run, steps), ...(autoStarted ? { autoStarted: true } : {}) };
139
240
  });
140
241
  }
242
+ /**
243
+ * Project a run row + its step rows into a {@link WorkflowNextResult}. The pure
244
+ * read-shaping half of {@link getNextWorkflowStep}, extracted so the driver
245
+ * snapshot below reproduces the exact same projection without re-running the
246
+ * auto-start-capable {@link resolveRunSpecifier}.
247
+ */
248
+ function projectNextResult(run, steps) {
249
+ const currentStep = resolveCurrentStep(run, steps);
250
+ const done = run.status === "completed" ? true : undefined;
251
+ // #506: surface a check-in directive through the normal command output when
252
+ // the run looks stalled. Pure timestamp evaluation — no background thread.
253
+ const checkin = evaluateCheckin({
254
+ status: run.status,
255
+ updatedAt: run.updated_at,
256
+ checkinArmedAt: run.checkin_armed_at,
257
+ agentHarness: run.agent_harness,
258
+ agentSessionId: run.agent_session_id,
259
+ }) ?? undefined;
260
+ return {
261
+ run: toWorkflowRunSummary(run),
262
+ workflow: {
263
+ ref: run.workflow_ref,
264
+ title: run.workflow_title,
265
+ steps: steps.map(toWorkflowRunStepState),
266
+ },
267
+ step: currentStep ? toWorkflowRunStepState(currentStep) : null,
268
+ ...(done ? { done } : {}),
269
+ ...(checkin ? { checkin } : {}),
270
+ };
271
+ }
272
+ /**
273
+ * A consistent point-in-time snapshot of a run for the harness-neutral driver
274
+ * protocol (PR #714 review round 2, #14). `brief`/`report` previously read the
275
+ * spine (`getNextWorkflowStep`) and then, in a SEPARATE connection, the run row
276
+ * + unit journal — so a concurrent `report`/`run`/manual completion could change
277
+ * the active step BETWEEN the two reads, leaving the described work-list
278
+ * inconsistent with the run row it was stamped against. This reads the run row,
279
+ * its steps, AND its unit rows inside ONE transaction (one connection, one
280
+ * snapshot) so all three agree. It never auto-starts — `runId` must already
281
+ * resolve to a concrete run — because the driver protocol never mutates on read.
282
+ */
283
+ export async function snapshotRunForDriver(runId) {
284
+ return withWorkflowRunsRepo((repo) => repo.transaction(() => {
285
+ const run = readWorkflowRun(repo, runId);
286
+ const steps = readWorkflowRunSteps(repo, run.id);
287
+ const plan = requireExecutableWorkflowPlan(run);
288
+ assertWorkflowSpineMatchesPlan(plan, run, steps);
289
+ const units = repo.getUnitsForRun(run.id);
290
+ return { next: projectNextResult(run, steps), run, units };
291
+ }));
292
+ }
141
293
  export async function resumeWorkflowRun(runId) {
142
294
  return withWorkflowRunsRepo((repo) => {
143
295
  const run = readWorkflowRun(repo, runId);
296
+ const plan = requireExecutableWorkflowPlan(run);
297
+ const steps = readWorkflowRunSteps(repo, run.id);
298
+ assertWorkflowSpineMatchesPlan(plan, run, steps);
144
299
  if (run.status === "completed") {
145
300
  throw new UsageError(`Workflow run ${run.id} is already completed and cannot be resumed.`);
146
301
  }
147
302
  if (run.status === "active") {
148
- const steps = readWorkflowRunSteps(repo, run.id);
149
303
  return buildWorkflowRunDetail(run, steps);
150
304
  }
151
305
  // blocked or failed → flip back to active and re-open the current step so
@@ -158,8 +312,8 @@ export async function resumeWorkflowRun(runId) {
158
312
  repo.markRunActive(run.id, now);
159
313
  });
160
314
  const updated = { ...run, status: "active", updated_at: now };
161
- const steps = readWorkflowRunSteps(repo, run.id);
162
- return buildWorkflowRunDetail(updated, steps);
315
+ const refreshedSteps = readWorkflowRunSteps(repo, run.id);
316
+ return buildWorkflowRunDetail(updated, refreshedSteps);
163
317
  });
164
318
  }
165
319
  /**
@@ -170,18 +324,24 @@ export async function resumeWorkflowRun(runId) {
170
324
  */
171
325
  export async function abandonWorkflowRun(runId) {
172
326
  return withWorkflowRunsRepo((repo) => {
173
- const run = readWorkflowRun(repo, runId);
174
- if (run.status === "completed" || run.status === "failed") {
175
- throw new UsageError(`Workflow run ${run.id} is already ${run.status}.`);
176
- }
177
327
  const now = new Date().toISOString();
178
- repo.updateRunState({
179
- status: "failed",
180
- currentStepId: run.current_step_id,
181
- updatedAt: now,
182
- completedAt: now,
183
- checkinArmedAt: now,
184
- runId: run.id,
328
+ const run = repo.immediateTransaction((db) => {
329
+ const current = readWorkflowRun(repo, runId);
330
+ if (current.status === "completed" || current.status === "failed") {
331
+ throw new UsageError(`Workflow run ${current.id} is already ${current.status}.`);
332
+ }
333
+ if (!repo.markRunAbandoned(current.id, now)) {
334
+ throw new UsageError(`Workflow run ${current.id} is ${current.status} and cannot be abandoned.`);
335
+ }
336
+ insertEventOnce(db, {
337
+ eventType: "workflow_abandoned",
338
+ ts: now,
339
+ ref: current.workflow_ref,
340
+ metadata: { runId: current.id },
341
+ idempotencyKey: current.id,
342
+ idempotencyMetadataKey: "runId",
343
+ });
344
+ return current;
185
345
  });
186
346
  const updated = {
187
347
  ...run,
@@ -192,9 +352,6 @@ export async function abandonWorkflowRun(runId) {
192
352
  };
193
353
  const steps = readWorkflowRunSteps(repo, run.id);
194
354
  const detail = buildWorkflowRunDetail(updated, steps);
195
- // Same injectable-footprint rule as workflow_started (07 P1-B): ids and
196
- // status only, never the frontmatter-derived title.
197
- appendEvent({ eventType: "workflow_abandoned", ref: run.workflow_ref, metadata: { runId: run.id } });
198
355
  return detail;
199
356
  });
200
357
  }
@@ -203,9 +360,13 @@ export async function completeWorkflowStep(input) {
203
360
  // the write transaction — a slow/hung LLM must never hold a db write lock.
204
361
  const preflight = await withWorkflowRunsRepo((repo) => {
205
362
  const run = readWorkflowRun(repo, input.runId);
363
+ const plan = requireExecutableWorkflowPlan(run);
364
+ const steps = readWorkflowRunSteps(repo, run.id);
365
+ assertWorkflowSpineMatchesPlan(plan, run, steps);
206
366
  if (run.status !== "active") {
207
367
  throw new UsageError(`Workflow run ${run.id} is ${run.status} and cannot be updated.`);
208
368
  }
369
+ assertLeaseAllowsSpineAdvance(run, input.leaseHolder);
209
370
  const existing = repo.getStep(run.id, input.stepId);
210
371
  if (!existing) {
211
372
  throw new NotFoundError(`Step "${input.stepId}" was not found in workflow run ${run.id}.`);
@@ -216,7 +377,10 @@ export async function completeWorkflowStep(input) {
216
377
  if (run.current_step_id !== existing.step_id) {
217
378
  throw new UsageError(`Step "${input.stepId}" is not the current step for workflow run ${run.id}. Complete "${run.current_step_id}" first.`);
218
379
  }
219
- return { existing };
380
+ const stepPlan = plan.steps.find((step) => step.stepId === input.stepId);
381
+ if (!stepPlan)
382
+ throw new NotFoundError(`Step "${input.stepId}" was not found in workflow run ${run.id}.`);
383
+ return { existing, plan, stepPlan };
220
384
  });
221
385
  const summary = input.summary?.trim();
222
386
  // #506: completing a step requires a summary of the work done.
@@ -227,9 +391,11 @@ export async function completeWorkflowStep(input) {
227
391
  // completionCriteria via the configured LLM. Fail-open when no criteria or no
228
392
  // judge. Only a well-formed `complete: false` blocks completion.
229
393
  if (input.status === "completed" && summary) {
230
- const criteria = parseJsonArray(preflight.existing.completion_json) ?? [];
231
- const judge = input.summaryJudge === undefined ? buildDefaultSummaryJudge() : input.summaryJudge;
232
- const verdict = await validateStepSummary({ stepTitle: preflight.existing.step_title, completionCriteria: criteria, summary }, judge ?? undefined);
394
+ const criteria = preflight.stepPlan.gate.criteria;
395
+ const judge = input.summaryJudge === undefined
396
+ ? frozenSummaryJudge(preflight.plan, preflight.stepPlan.gate.judge)
397
+ : input.summaryJudge;
398
+ const verdict = await validateStepSummary({ stepTitle: preflight.stepPlan.title, completionCriteria: criteria, summary }, judge ?? undefined);
233
399
  if (!verdict.complete) {
234
400
  // Re-arm the check-in so a subsequent stall is still nudged, but leave the
235
401
  // step pending and return corrective feedback instead of completing.
@@ -250,9 +416,16 @@ export async function completeWorkflowStep(input) {
250
416
  let refreshedSteps = [];
251
417
  repo.transaction(() => {
252
418
  const run = readWorkflowRun(repo, input.runId);
419
+ const plan = requireExecutableWorkflowPlan(run);
420
+ const spine = readWorkflowRunSteps(repo, run.id);
421
+ assertWorkflowSpineMatchesPlan(plan, run, spine);
253
422
  if (run.status !== "active") {
254
423
  throw new UsageError(`Workflow run ${run.id} is ${run.status} and cannot be updated.`);
255
424
  }
425
+ // Re-checked inside the write transaction (like every other preflight
426
+ // condition): an engine may have claimed the run while the summary gate
427
+ // above was awaiting its LLM judge.
428
+ assertLeaseAllowsSpineAdvance(run, input.leaseHolder);
256
429
  const existing = repo.getStep(run.id, input.stepId);
257
430
  if (!existing) {
258
431
  throw new NotFoundError(`Step "${input.stepId}" was not found in workflow run ${run.id}.`);
@@ -295,10 +468,16 @@ export async function completeWorkflowStep(input) {
295
468
  };
296
469
  });
297
470
  const detail = buildWorkflowRunDetail(updatedRun, refreshedSteps);
471
+ // #11: emit `workflow_step_completed` ONLY for a genuine `completed`
472
+ // transition; every other non-pending status (failed/skipped/blocked)
473
+ // carries the honest `workflow_step_updated` name. The status is ALWAYS
474
+ // in metadata so consumers never infer it from the event name. Raw `notes`
475
+ // are workflow/model-authored content — an event-stream prompt-injection
476
+ // surface — and never enter the events log; they live on the step row only.
298
477
  appendEvent({
299
- eventType: "workflow_step_completed",
478
+ eventType: input.status === "completed" ? "workflow_step_completed" : "workflow_step_updated",
300
479
  ref: detail.run.workflowRef,
301
- metadata: { runId: input.runId, stepId: input.stepId, notes: input.notes },
480
+ metadata: { runId: input.runId, stepId: input.stepId, status: input.status },
302
481
  });
303
482
  if (detail.run.status === "completed") {
304
483
  appendEvent({ eventType: "workflow_finished", ref: detail.run.workflowRef, metadata: { runId: input.runId } });
@@ -314,16 +493,28 @@ async function resolveRunSpecifier(repo, specifier, params) {
314
493
  }
315
494
  return { run: explicitRun, autoStarted: false };
316
495
  }
317
- if (!specifier.includes(":")) {
318
- throw new NotFoundError(`Workflow run "${specifier}" not found.`, "WORKFLOW_NOT_FOUND");
496
+ const scopeKey = getCurrentWorkflowScopeKey();
497
+ const exactRef = specifier.trim();
498
+ const parsedExact = parseBundleRef(exactRef);
499
+ const qualifiedExact = parsedExact.bundle !== undefined && parsedExact.fragment === undefined;
500
+ const detached = qualifiedExact ? repo.getActiveRunRowForScope(exactRef, scopeKey) : undefined;
501
+ let ref;
502
+ try {
503
+ ref = await canonicalizeWorkflowSpecifier(specifier);
319
504
  }
320
- const parsed = parseAssetRef(specifier);
321
- if (parsed.type !== "workflow") {
322
- throw new UsageError(`Expected a workflow ref or workflow run id, got "${specifier}".`);
505
+ catch (error) {
506
+ if (detached) {
507
+ if (params && Object.keys(params).length > 0) {
508
+ throw new UsageError(`--params can only be set on a new run; ${specifier} already has an active run`);
509
+ }
510
+ return { run: detached, autoStarted: false };
511
+ }
512
+ if (error instanceof NotFoundError && !specifier.includes(":") && !specifier.includes("/")) {
513
+ throw new NotFoundError(`Workflow run or workflow "${specifier}" not found.`, "WORKFLOW_NOT_FOUND");
514
+ }
515
+ throw error;
323
516
  }
324
- const ref = `${parsed.origin ? `${parsed.origin}//` : ""}workflow:${parsed.name}`;
325
- const scopeKey = getCurrentWorkflowScopeKey();
326
- const active = repo.getActiveRunRowForScope(ref, scopeKey);
517
+ const active = repo.getActiveRunRowForScope(await workflowRunRefSet(ref, exactRef), scopeKey);
327
518
  if (active) {
328
519
  if (params && Object.keys(params).length > 0) {
329
520
  throw new UsageError(`--params can only be set on a new run; ${ref} already has an active run`);
@@ -333,6 +524,62 @@ async function resolveRunSpecifier(repo, specifier, params) {
333
524
  const started = await startWorkflowRun(ref, params ?? {});
334
525
  return { run: readWorkflowRun(repo, started.run.id), autoStarted: true };
335
526
  }
527
+ async function canonicalizeWorkflowSpecifier(specifier) {
528
+ return canonicalizeWorkflowRefInput(specifier);
529
+ }
530
+ async function workflowRunRefSet(canonicalRef, exactRef) {
531
+ const parsed = parseBundleRef(canonicalRef);
532
+ const refs = new Set([canonicalRef, exactRef.trim()]);
533
+ const exact = parseBundleRef(exactRef.trim());
534
+ if (exact.bundle === undefined) {
535
+ refs.add(parsed.conceptId);
536
+ }
537
+ else {
538
+ try {
539
+ if ((await canonicalizeWorkflowSpecifier(parsed.conceptId)) === canonicalRef)
540
+ refs.add(parsed.conceptId);
541
+ }
542
+ catch (error) {
543
+ if (!(error instanceof NotFoundError))
544
+ throw error;
545
+ }
546
+ }
547
+ return [...refs];
548
+ }
549
+ /** Resolve an existing active run without auto-starting or rebinding a detached short ref. */
550
+ export async function resolveExistingWorkflowRunId(target) {
551
+ return withWorkflowRunsRepo(async (repo) => {
552
+ const byId = repo.getRunById(target);
553
+ if (byId)
554
+ return byId.id;
555
+ const scopeKey = getCurrentWorkflowScopeKey();
556
+ const exactRef = target.trim();
557
+ if (!exactRef.includes(":") && !exactRef.includes("/")) {
558
+ throw new NotFoundError(`Workflow run or workflow "${target}" not found.`, "WORKFLOW_NOT_FOUND");
559
+ }
560
+ const parsedExact = parseBundleRef(exactRef);
561
+ const detached = parsedExact.bundle && parsedExact.fragment === undefined
562
+ ? repo.getActiveRunRowForScope(exactRef, scopeKey)
563
+ : undefined;
564
+ let canonicalRef;
565
+ try {
566
+ canonicalRef = await canonicalizeWorkflowSpecifier(exactRef);
567
+ }
568
+ catch (error) {
569
+ if (detached)
570
+ return detached.id;
571
+ if (error instanceof NotFoundError) {
572
+ throw new NotFoundError(`Workflow run or workflow "${target}" not found.`, "WORKFLOW_NOT_FOUND");
573
+ }
574
+ throw error;
575
+ }
576
+ const active = repo.getActiveRunRowForScope(await workflowRunRefSet(canonicalRef, exactRef), scopeKey);
577
+ if (!active) {
578
+ throw new NotFoundError(`No active workflow run for ${canonicalRef} in this scope.`, "WORKFLOW_NOT_FOUND");
579
+ }
580
+ return active.id;
581
+ });
582
+ }
336
583
  function readWorkflowRun(repo, runId) {
337
584
  const run = repo.getRunById(runId);
338
585
  if (!run) {
@@ -344,6 +591,16 @@ function readWorkflowRunSteps(repo, runId) {
344
591
  return repo.getStepsForRun(runId);
345
592
  }
346
593
  function buildWorkflowRunDetail(run, steps) {
594
+ // Review M1: `workflow status` (and every other detail-shaped response) now
595
+ // evaluates the check-in, not just `workflow next`. Pure timestamp check —
596
+ // no background thread (see checkin.ts).
597
+ const checkin = evaluateCheckin({
598
+ status: run.status,
599
+ updatedAt: run.updated_at,
600
+ checkinArmedAt: run.checkin_armed_at,
601
+ agentHarness: run.agent_harness,
602
+ agentSessionId: run.agent_session_id,
603
+ });
347
604
  return {
348
605
  run: toWorkflowRunSummary(run),
349
606
  workflow: {
@@ -351,9 +608,11 @@ function buildWorkflowRunDetail(run, steps) {
351
608
  title: run.workflow_title,
352
609
  steps: steps.map(toWorkflowRunStepState),
353
610
  },
611
+ ...(checkin ? { checkin } : {}),
354
612
  };
355
613
  }
356
614
  function toWorkflowRunSummary(run) {
615
+ const plan = classifyWorkflowRunPlan(run);
357
616
  return {
358
617
  id: run.id,
359
618
  workflowRef: run.workflow_ref,
@@ -368,8 +627,33 @@ function toWorkflowRunSummary(run) {
368
627
  params: parseJsonObject(run.params_json),
369
628
  agentHarness: run.agent_harness ?? null,
370
629
  agentSessionId: run.agent_session_id ?? null,
630
+ planIrVersion: plan.irVersion,
631
+ executionSupport: plan.support,
632
+ // Surface the engine lease (holder id + expiry — never workflow-authored
633
+ // content) so `workflow next`/`status` show who is driving the run.
634
+ ...(run.engine_lease_holder && run.engine_lease_until
635
+ ? { engineLease: { holder: run.engine_lease_holder, until: run.engine_lease_until } }
636
+ : {}),
371
637
  };
372
638
  }
639
+ /**
640
+ * Single-driver enforcement (R2 run lease): while a LIVE (unexpired) engine
641
+ * lease is held, only the holding engine may advance the gate spine. Manual
642
+ * `akm workflow complete` (no `leaseHolder`) — or a stale engine invocation
643
+ * whose lease was claimed by another — is refused with the holder + expiry.
644
+ * An EXPIRED lease never blocks: the engine that held it is presumed dead.
645
+ */
646
+ function assertLeaseAllowsSpineAdvance(run, leaseHolder) {
647
+ if (!run.engine_lease_holder || !run.engine_lease_until)
648
+ return;
649
+ if (leaseHolder === run.engine_lease_holder)
650
+ return;
651
+ if (run.engine_lease_until < new Date().toISOString())
652
+ return; // expired ⇒ claimable, not live
653
+ throw new UsageError(`Workflow run ${run.id} is being driven by engine ${run.engine_lease_holder} ` +
654
+ `(run lease expires ${run.engine_lease_until}). The engine owns the step spine while it runs — ` +
655
+ `wait for it to finish or for the lease to expire before advancing steps manually.`);
656
+ }
373
657
  function toWorkflowRunStepState(step) {
374
658
  return {
375
659
  id: step.step_id,
@@ -410,33 +694,6 @@ function deriveRunState(steps) {
410
694
  .at(-1);
411
695
  return { status: "completed", currentStepId: null, completedAt: completedAt ?? null };
412
696
  }
413
- /**
414
- /**
415
- * Build the default summary-validation judge from the configured LLM, or return
416
- * `null` when no LLM is configured (gate is then skipped — fail-open). Lazily
417
- * imports the client/config so the workflow engine has no hard LLM dependency.
418
- */
419
- function buildDefaultSummaryJudge() {
420
- let llm;
421
- try {
422
- const config = loadConfig();
423
- const { getDefaultLlmConfig } = require("../../core/config/config");
424
- llm = getDefaultLlmConfig(config);
425
- }
426
- catch {
427
- return null;
428
- }
429
- if (!llm)
430
- return null;
431
- const resolved = llm;
432
- return async ({ system, user }) => {
433
- const { chatCompletion } = require("../../llm/client");
434
- return chatCompletion(resolved, [
435
- { role: "system", content: system },
436
- { role: "user", content: user },
437
- ]);
438
- };
439
- }
440
697
  function parseJsonObject(value) {
441
698
  if (!value)
442
699
  return undefined;