akm-cli 0.9.17-alpha.3 → 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 +731 -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 +361 -751
  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 -441
  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,31 +1,6 @@
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
- * Symmetric valence weighting for the improve eligibility sort (#614).
6
- *
7
- * BACKGROUND. The improve attention/eligibility ranking historically combined
8
- * utility with a NEGATIVE-ONLY feedback term: `negative / (positive + negative)`.
9
- * Under that formula a strong-positive asset contributes a feedback ratio of
10
- * `0` — i.e. positive feedback never drives attention. Only complaints could
11
- * lift an asset up the ranking, so a heavily-praised, heavily-used asset that
12
- * deserves REINFORCEMENT (distill / promote the win) is treated identically to
13
- * a never-rated one.
14
- *
15
- * FIX (gated, default-off). When symmetric valence is enabled we replace the
16
- * negative-only ratio with a |valence| MAGNITUDE term so that BOTH strong
17
- * positive and strong negative feedback drive attention. Utility remains the
18
- * dominant ordering factor — valence is a secondary attention nudge with a
19
- * small fixed weight, never a utility override.
20
- *
21
- * This module is intentionally pure and storage-free: it takes pre-aggregated
22
- * positive/negative counts plus a utility lookup and returns a deterministic
23
- * score and lane. All DB access stays in the caller.
24
- */
25
- /** Weight on utility in the combined eligibility score. Utility is dominant. */
26
- export const UTILITY_WEIGHT = 0.7;
27
- /** Weight on the feedback attention term in the combined eligibility score. */
28
- export const FEEDBACK_WEIGHT = 0.3;
29
4
  /**
30
5
  * Compute the symmetric-valence attention score for one asset's feedback.
31
6
  *
@@ -31,15 +31,9 @@ export function _setAkmImproveForTests(fake) {
31
31
  akmImproveForRun = fake ?? akmImprove;
32
32
  }
33
33
  /**
34
- * Handle the `--auto-accept` flag retired in 0.9.0, returning the scope the run
35
- * should actually use.
36
- *
37
- * citty is non-strict, so the removed flag is silently absorbed rather than
38
- * rejected — which is the dangerous case. The SPACE-separated spelling
39
- * (`--auto-accept 90`) leaves `90` sitting in the positional slot, where it is
40
- * read as the asset-type scope: the run then matches nothing and exits 0, so a
41
- * 0.8-era crontab goes dark with no error at all. Warn about the flag, and drop
42
- * the poisoned positional so the run behaves as an unscoped improve instead.
34
+ * `--auto-accept` (removed in 0.9): citty absorbs it silently, and
35
+ * `--auto-accept 90` would leave `90` as the scope — a 0.8-era crontab would
36
+ * match nothing and exit 0. Warn, and drop that positional.
43
37
  */
44
38
  function resolveScopeAfterRetiredAutoAccept(scopeArg) {
45
39
  const invocation = getParsedInvocation();
@@ -55,41 +49,22 @@ function resolveScopeAfterRetiredAutoAccept(scopeArg) {
55
49
  }
56
50
  return scopeArg;
57
51
  }
58
- /**
59
- * `akm improve canary` was removed in 0.9 (moved to
60
- * `scripts/refresh-canary-set.ts`). Without this check "canary" falls through
61
- * to the generic scope positional, where resolveImproveScope treats any bare
62
- * word as a type filter that matches zero entries — so an unmigrated caller
63
- * silently acquires the improve lock and exits 0 having done nothing, instead
64
- * of getting an error.
65
- */
52
+ /** `akm improve canary` (removed in 0.9) would otherwise be a type scope matching nothing, exiting 0. */
66
53
  function rejectRetiredCanaryScope(scopeArg) {
67
54
  if (scopeArg !== "canary")
68
55
  return;
69
- throw new UsageError('"akm improve canary" was removed in 0.9. Use `bun scripts/refresh-canary-set.ts [--refresh]` instead.', "INVALID_FLAG_VALUE");
56
+ throw new UsageError('"akm improve canary" was removed in 0.9; the collapse-detector canary set it managed no longer exists.', "INVALID_FLAG_VALUE");
70
57
  }
71
- /**
72
- * `--target` was renamed to `--bundle` on `improve` in 0.9 (S8). citty is
73
- * non-strict, so the retired spelling is silently absorbed rather than
74
- * rejected — accepted proposals then write into the default bundle instead
75
- * of the one the caller named, with exit 0 and no error. Reject it
76
- * explicitly instead.
77
- */
58
+ /** `--target` (renamed `--bundle` in 0.9) would otherwise be absorbed and write to the default bundle. */
78
59
  function rejectRetiredImproveTargetFlag() {
79
60
  if (!getParsedInvocation().hasFlag("--target"))
80
61
  return;
81
62
  throw new UsageError("`akm improve --target` was renamed to `--bundle` in 0.9. Use `--bundle <name>` instead.", "INVALID_FLAG_VALUE");
82
63
  }
83
64
  /**
84
- * `--require-engines` (#957): abort before any lock, log, or index side
85
- * effect when the resolved plan already knows a process the active strategy
86
- * would enable cannot run. Without this flag improve degrades gracefully —
87
- * it skips the affected processes and reports them in `skippedProcesses` —
88
- * which is right for an interactive run but wrong for a scheduled one that
89
- * would rather fail loudly than burn its budget re-indexing and then skip
90
- * everything. Names the unresolved credential reference per process (not
91
- * just the process name) so an operator whose own shell passes config
92
- * validation can see exactly what the scheduler's environment is missing.
65
+ * `--require-engines` (#957): fail before any side effect when an enabled
66
+ * process cannot run, naming what each is missing — a scheduled run would
67
+ * rather fail loudly than index and then skip everything.
93
68
  */
94
69
  function assertRequiredEnginesAvailable(plan) {
95
70
  if (plan.engineUnavailable.length === 0)
@@ -97,11 +72,7 @@ function assertRequiredEnginesAvailable(plan) {
97
72
  const lines = plan.engineUnavailable.map((item) => ` - ${item.process} (${item.configKey}): ${item.reason}`);
98
73
  throw new ConfigError(`--require-engines: ${plan.engineUnavailable.length} improve process${plan.engineUnavailable.length === 1 ? "" : "es"} cannot run because ${plan.engineUnavailable.length === 1 ? "its" : "their"} engine is unavailable:\n${lines.join("\n")}`, "LLM_NOT_CONFIGURED");
99
74
  }
100
- /**
101
- * Every distinct `kind: "llm"` connection the active strategy's plan would
102
- * actually dispatch against — the main per-process runners plus triage's own
103
- * judgment engine, which is resolved separately (#957).
104
- */
75
+ /** Every LLM connection the plan would dispatch to, triage's judgment engine included. */
105
76
  function collectRequiredEngineTargets(plan) {
106
77
  const targets = [];
107
78
  for (const [processName, process] of Object.entries(plan.processes)) {
@@ -119,24 +90,10 @@ function collectRequiredEngineTargets(plan) {
119
90
  return targets;
120
91
  }
121
92
  /**
122
- * `--require-engines` field re-test (#957): the static check above only
123
- * proves an engine is configured and credentialed — it cannot see a dead
124
- * endpoint. A field run against an unreachable engine sat silent for
125
- * minutes instead of hitting the documented exit-78 path. Exercise the real
126
- * model completion path with a tiny response and a three-second bound. The
127
- * `/models` endpoint used by the lightweight health check is deliberately
128
- * insufficient here: a gateway can list a model while its upstream completion
129
- * route is dead (#980). Deduplicate by endpoint + model, not endpoint alone,
130
- * because model backends behind one gateway can fail independently.
131
- *
132
- * R17: a probe that PASSES used to leave no trace — a slow or flapping
133
- * gateway was invisible in the improve result. On success, return one
134
- * {@link EngineProbeOutcome} per target (process, engine, endpoint,
135
- * reachable, latencyMs) so the caller can record it on the run result;
136
- * targets sharing a deduplicated probe share its measured latency.
137
- *
138
- * Exported for unit tests, which inject a fake `probeReachable` (the
139
- * "probe seam") instead of hitting a real endpoint.
93
+ * `--require-engines`, live: probe each connection's real completion path
94
+ * (a gateway can list a model whose completion route is dead, #980) with a 3s
95
+ * bound, once per endpoint + model. Returns each target's latency for the run
96
+ * result (R17); an unreachable one fails the run.
140
97
  */
141
98
  export async function assertRequiredEnginesReachable(plan, probeReachable = (connection) => probeLlmReachable(connection, 3_000)) {
142
99
  const targets = collectRequiredEngineTargets(plan);
@@ -170,14 +127,7 @@ export async function assertRequiredEnginesReachable(plan, probeReachable = (con
170
127
  latencyMs: item.latencyMs,
171
128
  }));
172
129
  }
173
- /**
174
- * `--show-prompt` (#952): render the composed reflect prompt for one asset ref
175
- * and exit, before any lock, log, index write, or engine dispatch — the field
176
- * had no cheap way to confirm the #952 prompt fix (unverified-feedback framing,
177
- * no-truncation-marker instruction) without running a full improve cycle.
178
- * Reuses `renderReflectPromptPreview` (reflect.ts), which stops before the
179
- * dispatch lease reflect would otherwise acquire, so this never calls an engine.
180
- */
130
+ /** `--show-prompt` (#952): print reflect's composed prompt for one ref — no lock, write or dispatch. */
181
131
  async function runShowPromptCli(refArg, parsedRef, taskArg, targetArg, resolvedPlan) {
182
132
  const readSource = resolveImproveReadSource(resolvedPlan.config, parsedRef, targetArg);
183
133
  const preview = await renderReflectPromptPreview({
@@ -201,30 +151,14 @@ async function runShowPromptCli(refArg, parsedRef, taskArg, targetArg, resolvedP
201
151
  prompt: preview.prompt,
202
152
  });
203
153
  }
204
- /**
205
- * `akm improve report` (#944): a scope value that dispatches to the per-run
206
- * LLM usage/routing report instead of a real improve run — "report" is not,
207
- * and will never be, a real asset type (`DEFAULT_ALLOWED_TYPES` in
208
- * improve-strategies.ts), so this already matched zero assets before this
209
- * flag existed, matching the precedent `rejectRetiredCanaryScope` set for
210
- * intercepting a special scope word ahead of any lock/log/index side effect.
211
- */
154
+ /** `akm improve report` (#944): the per-run LLM usage/routing report ("report" is no asset type). */
212
155
  function runImproveReportCli(args) {
213
156
  const runIdArg = getStringArg(args, "run");
214
157
  const sinceArg = getStringArg(args, "since");
215
158
  const result = runImproveReportQuery({ runId: runIdArg, since: sinceArg });
216
159
  output("improve-report", { ok: true, ...result });
217
160
  }
218
- /**
219
- * `--run`/`--since` only mean anything with the "report" scope, which
220
- * intercepts before this point in the `run` handler below. citty is
221
- * non-strict, so passing either with a real scope (or no scope at all) used
222
- * to be silently ignored — the flag's value was read nowhere else, and the
223
- * run proceeded as an ordinary improve run with no error, discarding the
224
- * operator's intent. Reject explicitly instead, matching the precedent
225
- * `rejectRetiredCanaryScope`/`rejectRetiredImproveTargetFlag` set for other
226
- * flag misuse on this command.
227
- */
161
+ /** `--run`/`--since` belong to `improve report`; elsewhere citty would silently ignore them. */
228
162
  function rejectReportOnlyFlags(args) {
229
163
  const flag = getStringArg(args, "run") !== undefined
230
164
  ? "--run"
@@ -240,9 +174,7 @@ export const improveCommand = defineCommand({
240
174
  name: "improve",
241
175
  description: "Analyze existing AKM assets and generate improvement proposals; also consolidates memories when the selected strategy enables consolidate.",
242
176
  },
243
- // Raw defineCommand, so the global output flags are declared here explicitly.
244
- // Without them citty treats `--format` as a boolean and its space-separated
245
- // value falls through to the `scope` positional.
177
+ // Declared explicitly: otherwise citty takes `--format`'s value as the scope.
246
178
  args: {
247
179
  ...GLOBAL_OUTPUT_ARGS,
248
180
  scope: {
@@ -311,27 +243,16 @@ export const improveCommand = defineCommand({
311
243
  },
312
244
  async run({ args }) {
313
245
  await runWithJsonErrors(async () => {
314
- // #944 — dispatch before any lock/log/index side effect, same
315
- // interception point as rejectRetiredCanaryScope below.
316
246
  if (getStringArg(args, "scope") === "report") {
317
247
  runImproveReportCli(args);
318
248
  return;
319
249
  }
320
250
  rejectReportOnlyFlags(args);
321
251
  rejectRetiredImproveTargetFlag();
322
- // D7 — `--format` used to be rejected here outright. It is a global flag on
323
- // a command that does emit an envelope through `output()` (always on
324
- // `--dry-run`, otherwise with `--json-to-stdout`), so rejecting it made
325
- // improve a fourth inconsistent format behaviour rather than a documented
326
- // exemption. It now applies to that envelope; progress output stays on
327
- // stderr regardless.
328
252
  const jsonToStdout = args["json-to-stdout"];
329
253
  const targetArg = getStringArg(args, "bundle");
330
254
  const taskArg = getStringArg(args, "task");
331
- // #947 — `--plan` is a zero-logic discoverability alias for `--dry-run`;
332
- // it must never fork the computation, only set the same flag. #952 —
333
- // `--show-prompt` implies the same read-only posture (it never reaches
334
- // akmImprove at all, but keeps writeTarget/resolvedPlan unset the same way).
255
+ // `--plan` is an alias for `--dry-run`; `--show-prompt` is read-only too.
335
256
  const dryRun = args["dry-run"] || args.plan || args["show-prompt"];
336
257
  const limitRaw = parsePositiveIntFlag(args.limit ?? undefined);
337
258
  const timeoutMs = parsePositiveIntFlag(args["timeout-ms"], "--timeout-ms");
@@ -347,16 +268,9 @@ export const improveCommand = defineCommand({
347
268
  : scopeRef
348
269
  ? resolveMutationTarget(effectiveConfig, scopeRef, targetArg).target
349
270
  : resolveWriteTarget(effectiveConfig, targetArg);
350
- // Resolve every enabled model-backed process before logging, signal
351
- // lifecycle setup, or any filesystem/database side effect.
352
- // #800/#957 round 3 — `--dry-run`/`--plan` never dispatches, so the
353
- // "no improve process can run" guard must not throw when every process
354
- // is disabled purely by an unreachable credential; a live run keeps
355
- // throwing (allowAllDisabled unset).
271
+ // Every model-backed process resolves before any side effect; a dry run
272
+ // never dispatches, so it tolerates every process being disabled.
356
273
  const resolvedPlan = resolveImprovePlan(strategyArg, effectiveConfig, { allowAllDisabled: Boolean(dryRun) });
357
- // #952 — same interception point as the `report` scope above: before any
358
- // lock, log, or index side effect. Requires a single fully-qualified
359
- // asset ref (not a type or whole-bundle scope).
360
274
  if (args["show-prompt"]) {
361
275
  if (!scopeArg || !scopeRef) {
362
276
  throw new UsageError("`--show-prompt` requires a fully-qualified asset ref as the scope (e.g. `akm improve lessons/my-lesson --show-prompt`).", "INVALID_FLAG_VALUE");
@@ -371,9 +285,7 @@ export const improveCommand = defineCommand({
371
285
  }
372
286
  const selectedStrategyName = resolvedPlan.strategy.name;
373
287
  const sensitiveValues = collectEngineCredentialValues(effectiveConfig);
374
- // Only set the keys the user actually passed (citty leaves the flag
375
- // undefined unless `--sync`/`--no-sync` / `--push`/`--no-push` appears),
376
- // so the resolved profile `sync` block wins by default.
288
+ // Only flags actually passed override the strategy's `sync` block.
377
289
  const syncFlag = args.sync;
378
290
  const pushFlag = args.push;
379
291
  const syncOverride = {};
@@ -387,16 +299,10 @@ export const improveCommand = defineCommand({
387
299
  }
388
300
  const startedAtMs = Date.now();
389
301
  const startedAtIso = new Date(startedAtMs).toISOString();
390
- // Mint the run-id up front so signal handlers can persist a partial
391
- // record if the process is killed mid-run. Pre-2026-05-26 the runId
392
- // was minted at end-of-run, so SIGTERM'd runs (cron timeout) left no
393
- // row in improve_runs and effectively disappeared from `akm health`.
302
+ // The run id is minted up front so a killed run still leaves an improve_runs row.
394
303
  const runId = buildImproveRunId();
395
304
  const primaryStashDir = writeTarget?.source.path;
396
305
  const inferredScopeMode = scopeRef ? "ref" : scopeArg ? "type" : "all";
397
- // Signal handler + exception path both flow through this helper so
398
- // every abnormal termination produces a row with ok:false and a
399
- // reason in metadata.terminated.
400
306
  let runRecorded = false;
401
307
  const persistTerminated = (reason, errorMessage) => {
402
308
  if (dryRun)
@@ -420,14 +326,7 @@ export const improveCommand = defineCommand({
420
326
  process.stderr.write(`warning: failed to persist terminated improve run ${runId}: ${err instanceof Error ? err.message : String(err)}\n`);
421
327
  }
422
328
  };
423
- // R8: the signal table / handlers / watchdog / persist-before-exit
424
- // choreography lives in `runImproveSession`. It registers the
425
- // SIGTERM/SIGINT/SIGHUP handlers (each persists the terminated-run row
426
- // BEFORE process.exit so a SIGTERM'd run — e.g. cron timeout — always
427
- // leaves a row in improve_runs), awaits the work, then removes the
428
- // handlers on the way out. `onTerminate` persists synchronously
429
- // (recordTerminatedImproveRun -> bun:sqlite writes are sync), and the
430
- // 2000ms watchdog inside the session force-exits if that ever hangs.
329
+ // The session persists the terminated-run row before exiting on a signal.
431
330
  let improveResult;
432
331
  try {
433
332
  improveResult = await runImproveSession({
@@ -446,11 +345,6 @@ export const improveCommand = defineCommand({
446
345
  ...(strategyArg !== undefined ? { strategy: strategyArg } : {}),
447
346
  ...(engineProbe !== undefined ? { engineProbe } : {}),
448
347
  ...(Object.keys(syncOverride).length > 0 ? { sync: syncOverride } : {}),
449
- consolidateOptions: {
450
- target: targetArg,
451
- dryRun,
452
- task: taskArg,
453
- },
454
348
  }),
455
349
  }, {
456
350
  signalSource: process,
@@ -462,10 +356,6 @@ export const improveCommand = defineCommand({
462
356
  });
463
357
  }
464
358
  catch (err) {
465
- // akmImprove threw — record the failure before letting runWithJsonErrors
466
- // emit the standard JSON error envelope. Without this, exceptions in
467
- // the main loop (LLM provider crash, OOM, etc.) leave no improve_runs
468
- // row, matching the SIGTERM gap.
469
359
  persistTerminated("exception", err instanceof Error ? err.message : String(err));
470
360
  throw err;
471
361
  }
@@ -473,57 +363,30 @@ export const improveCommand = defineCommand({
473
363
  clearLogFile();
474
364
  }
475
365
  if (dryRun) {
476
- // A dry-run never persists its result, so stdout is its only result
477
- // channel. F4: was `process.exit(0)`, which terminates synchronously
478
- // and skips pending cleanup (e.g. the `finally { clearLogFile(); }`
479
- // above already ran, but any citty/runWithJsonErrors-level cleanup
480
- // on the way out would not). Exit code is 0 either way — `return`
481
- // alone is sufficient since success is the default `process.exitCode`.
366
+ // A dry run persists nothing: stdout is its only result channel.
482
367
  output("improve", improveResult);
483
368
  return;
484
369
  }
485
- // Default mode (0.8.0+): persist the full result as a row in the
486
- // `improve_runs` table of state.db (migration 003) and emit NOTHING
487
- // on stdout. The verbose JSON would otherwise scroll earlier progress
488
- // logs out of the terminal buffer. The existing `[improve] ...`
489
- // progress log lines on stderr remain the canonical console UX — the
490
- // usage-report table below (#944) follows that same convention
491
- // (stderr, `[improve]`-prefixed), it is not new stdout noise.
492
- //
493
- // Pre-0.8.0 wrote `<stash>/.akm/runs/<run-id>/improve-result.json`;
494
- // those files are no longer authored. Query recent runs with:
495
- // sqlite3 "$AKM_DATA_DIR/state.db" \
496
- // "SELECT id, started_at, ok, dry_run FROM improve_runs \
497
- // ORDER BY started_at DESC LIMIT 10"
498
- // runId + primaryStashDir minted up-top so signal handlers can record
499
- // partial runs; reuse them here for the success path.
500
- runRecorded = true; // Suppress any late signal-handler write — the success path owns the row now.
370
+ // A live run's result goes to state.db's improve_runs, not stdout
371
+ // (progress stays on stderr). The success path owns the row now.
372
+ runRecorded = true;
501
373
  if (primaryStashDir) {
502
374
  try {
503
375
  recordImproveRunResult(primaryStashDir, runId, improveResult, startedAtIso, sensitiveValues);
504
376
  }
505
377
  catch (err) {
506
- // Stderr warning on the failure path is preferable to crashing
507
- // the run after all the work has completed.
508
378
  process.stderr.write(`warning: failed to record improve run ${runId}: ${err instanceof Error ? err.message : String(err)}\n`);
509
379
  }
510
380
  }
511
381
  else {
512
382
  process.stderr.write(`warning: no writable bundle directory resolved; improve result not persisted to state.db (use --json-to-stdout to capture)\n`);
513
383
  }
514
- // #944 — same table `akm improve report` renders, appended to every
515
- // real (non-dry-run) run so an operator sees the routing/cost split
516
- // without a separate command. Omitted when the run made no LLM calls
517
- // and skipped no enabled process (nothing to report).
384
+ // The `akm improve report` table, on stderr, when there is anything to report (#944).
518
385
  if (improveResult.usageReport) {
519
386
  process.stderr.write(`${formatUsageReportTable(improveResult.usageReport)}\n`);
520
387
  }
521
388
  if (jsonToStdout)
522
389
  output("improve", improveResult);
523
- // F4: was `process.exit(0)` — the run has already been fully recorded
524
- // above (recordImproveRunResult / the warning path), so nothing here
525
- // depends on an immediate synchronous exit. This is the last statement
526
- // in the handler, so a plain fall-through is equivalent.
527
390
  });
528
391
  },
529
392
  });
@@ -2,72 +2,31 @@
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
- * Helpers for persisting the `akm improve` result envelope.
6
- *
7
- * v0.8.0 behavioural default change:
8
- * - Default: the full result is recorded as a single row in the
9
- * `improve_runs` table of `state.db` (migration 003). Stdout is empty.
10
- * The existing `[improve] ...` progress log lines on stderr remain the
11
- * canonical console UX.
12
- * - `--json-to-stdout` additionally emits the persisted result as JSON.
13
- *
14
- * v0.8.0 storage change (this module): the previous on-disk artifact at
15
- * `<stash>/.akm/runs/<runId>/improve-result.json` is no longer written. The
16
- * canonical record now lives in `improve_runs` (see
17
- * `src/core/state-db.ts`). Pre-existing files from older runs are not
18
- * deleted by this change — they become historical artifacts. Zero current
19
- * code paths read them, so no consumers needed to update.
20
- *
21
- * Run-id format: ISO-8601 timestamp (colons/dots replaced by `-`) plus an
22
- * 8-char hex random suffix. There is no existing canonical run-id helper for
23
- * persistent per-command artefacts on disk — the `workflow_runs` table uses
24
- * `randomUUID()` but is database-scoped, and `consolidate-journal.json` is a
25
- * single-slot artefact. We mint a fresh timestamped id for each improve run.
5
+ * Persist an `akm improve` result as one `improve_runs` row in state.db (since
6
+ * 0.8.0 stdout stays empty unless `--json-to-stdout`). A run that did not
7
+ * complete still gets a row.
26
8
  */
27
9
  import crypto from "node:crypto";
28
10
  import { decodeImproveResult } from "../../core/improve-result.js";
29
11
  import { redactSensitiveValue } from "../../core/redaction.js";
30
12
  import { withImmediateTransaction, withStateDb } from "../../core/state-db.js";
31
13
  import { recordImproveRun } from "../../storage/repositories/improve-runs-repository.js";
32
- /**
33
- * Build a stable run-id for a single improve invocation.
34
- *
35
- * Shape: `<iso-8601-utc-with-dashes>-<8 hex chars>`, e.g.
36
- * `2026-05-19T17-30-22-123Z-a1b2c3d4`.
37
- *
38
- * The hex suffix protects against same-millisecond collisions when multiple
39
- * runs happen back-to-back in tests or scripts.
40
- */
14
+ /** `<iso-8601 with dashes>-<8 hex>`, e.g. `2026-05-19T17-30-22-123Z-a1b2c3d4` (the suffix breaks same-ms ties). */
41
15
  export function buildImproveRunId(now = new Date()) {
42
16
  const iso = now.toISOString().replace(/[:.]/g, "-");
43
17
  const rand = crypto.randomBytes(4).toString("hex");
44
18
  return `${iso}-${rand}`;
45
19
  }
46
- /**
47
- * Persist the full improve result into the `improve_runs` table of state.db.
48
- *
49
- * The state.db row carries the scope and dry-run flag from `result.scope`
50
- * and `result.dryRun`, plus the full result JSON for full fidelity. The
51
- * dry-run column is indexed so productivity audits can filter cleanly
52
- * (closes the dry-run/real-run artifact-trap recorded in MEMORY.md
53
- * `feedback_akm_dryrun_artifact_trap`).
54
- *
55
- */
20
+ /** Record a finished run (the full result, redacted; dry runs stay filterable). */
56
21
  export function recordImproveRunResult(stashDir, runId, result, startedAt, sensitiveValues = []) {
57
22
  const decoded = decodeImproveResult(result);
58
23
  const persistedResult = redactSensitiveValue(result, sensitiveValues);
59
24
  withStateDb((db) => {
60
25
  const completedAt = new Date().toISOString();
61
- // startedAt is the ISO timestamp captured at process launch (passed from the
62
- // CLI entry point). If omitted, fall back to the run-id's embedded timestamp
63
- // so started_at != completed_at even on older call sites.
26
+ // Without a launch timestamp, the one embedded in the run id.
64
27
  const resolvedStartedAt = startedAt ??
65
28
  runId.slice(0, 24).replace(/^(\d{4}-\d{2}-\d{2}T)(\d{2})-(\d{2})-(\d{2})-(\d{3})Z$/, "$1$2:$3:$4.$5Z");
66
- // #948: route through the shared BEGIN IMMEDIATE retry/reclassify helper
67
- // instead of a bare write — this INSERT used to rely solely on the
68
- // connection's 30s busy_timeout, with no retry and no friendly
69
- // reclassification on exhaustion, so a raw "database is locked" from
70
- // improve's own ledger write could reach the CLI as exit 70.
29
+ // BEGIN IMMEDIATE with retry: a bare write surfaced "database is locked" (#948).
71
30
  withImmediateTransaction(db, () => {
72
31
  recordImproveRun(db, {
73
32
  id: runId,
@@ -86,21 +45,9 @@ export function recordImproveRunResult(stashDir, runId, result, startedAt, sensi
86
45
  });
87
46
  }
88
47
  /**
89
- * Persist an improve_runs row for a run that did NOT complete normally.
90
- * 2026-05-26 incident: the cron's `timeout_ms: 1800000` SIGTERM'd an
91
- * akm-improve invocation at 30:00 with 54 actionable refs in-flight. No
92
- * `improve_runs` row was written because the writer only fired at successful
93
- * end-of-run, so the run vanished from `akm health --detail per-run` even
94
- * though it had consumed 30 min of LLM time and produced 29 ref-level
95
- * proposals. This helper closes that gap: signal handlers and the CLI
96
- * try/catch wrapper call it on the abnormal-exit paths so the row exists
97
- * with `ok: false` and `metadata.terminated.reason` set.
98
- *
99
- * The persisted result envelope is minimal — we don't try to reconstruct
100
- * the in-flight `actions[]` because that state lives inside `akmImprove`
101
- * and is gone by the time the signal handler runs. The row captures
102
- * enough to know: a run started, was scoped to X, did NOT complete, and
103
- * why.
48
+ * Record a run that did not complete (a signal, e.g. a cron timeout, or an
49
+ * exception) so it does not vanish from `akm health`: `ok: false` with the
50
+ * reason. The in-flight actions are gone by then, so the envelope is minimal.
104
51
  */
105
52
  export function recordTerminatedImproveRun(stashDir, runId, startedAt, reason, ctx) {
106
53
  const completedAt = new Date().toISOString();
@@ -123,9 +70,6 @@ export function recordTerminatedImproveRun(stashDir, runId, startedAt, reason, c
123
70
  },
124
71
  }, ctx.sensitiveValues ?? []);
125
72
  withStateDb((db) => {
126
- // #948: same rationale as recordImproveRunResult above — this is the
127
- // signal-handler/terminated-run write path, which must not itself raise
128
- // a raw "database is locked" while trying to record why the run ended.
129
73
  withImmediateTransaction(db, () => {
130
74
  recordImproveRun(db, {
131
75
  id: runId,
@@ -17,6 +17,7 @@ import { ConfigError } from "../../core/errors.js";
17
17
  import { describeLlmCredentialAvailability } from "../../integrations/agent/engine-resolution.js";
18
18
  import { applyAutonomyGate } from "./autonomy-gate.js";
19
19
  import { resolveImproveExecution, resolveImproveLlmExecution } from "./execution.js";
20
+ import { stripBundle } from "./ledger.js";
20
21
  export const DEFAULT_ALLOWED_TYPES = {
21
22
  reflect: ["agent", "command", "knowledge", "lesson", "memory", "skill", "workflow"],
22
23
  distill: ["memory"],
@@ -27,13 +28,9 @@ export function resolveProcessEnabled(processName, strategy) {
27
28
  const processes = strategy.processes;
28
29
  return processes?.[processName]?.enabled === true;
29
30
  }
30
- /** `bundle//conceptId` -> bare `conceptId`, for an `excludeRefPrefixes` entry. */
31
+ /** An `excludeRefPrefixes` entry as a bare conceptId without a trailing `/` (else `startsWith(".../raw//")` never matches). */
31
32
  function stripBundlePrefix(value) {
32
- const boundary = value.indexOf("//");
33
- const stripped = boundary >= 0 ? value.slice(boundary + 2) : value;
34
- // A trailing `/` (e.g. "knowledge/wikis/articles/raw/") would otherwise turn the
35
- // segment-boundary check below into `startsWith(".../raw//")`, which never matches.
36
- return stripped.replace(/\/+$/, "");
33
+ return stripBundle(value).replace(/\/+$/, "");
37
34
  }
38
35
  export function shouldSkipRef(ref, processName, strategy) {
39
36
  const process = strategy.processes?.[processName];
@@ -59,6 +56,14 @@ export function shouldSkipRef(ref, processName, strategy) {
59
56
  }
60
57
  return { skip: false, reason: "" };
61
58
  }
59
+ const REF_SCOPED_PROCESSES = new Set(["reflect", "distill", "consolidate"]);
60
+ /** How many refs a ref-scoped process would act on (`undefined` for any other process). */
61
+ export function eligibleRefCount(refs, process, strategy) {
62
+ if (!REF_SCOPED_PROCESSES.has(process))
63
+ return undefined;
64
+ const name = process;
65
+ return refs.filter((entry) => !shouldSkipRef(entry.ref, name, strategy).skip).length;
66
+ }
62
67
  export function isStrategyFilteredForAllPasses(ref, strategy) {
63
68
  return shouldSkipRef(ref, "reflect", strategy).skip && shouldSkipRef(ref, "distill", strategy).skip;
64
69
  }
@@ -140,7 +145,7 @@ export function projectResolvedProcessRouting(plan) {
140
145
  }
141
146
  return rows;
142
147
  }
143
- function cloneAndFreeze(value) {
148
+ export function cloneAndFreeze(value) {
144
149
  const clone = structuredClone(value);
145
150
  const freeze = (item) => {
146
151
  if (typeof item !== "object" || item === null || Object.isFrozen(item))