akm-cli 0.9.0-rc.9 → 0.9.1-beta.1

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 (477) hide show
  1. package/CHANGELOG.md +1770 -44
  2. package/README.md +38 -47
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +499 -0
  5. package/dist/akm +148 -35
  6. package/dist/{akm-migrate-storage → akm-migrate} +6 -9
  7. package/dist/assets/hints/cli-hints-full.md +223 -95
  8. package/dist/assets/hints/cli-hints-short.md +85 -22
  9. package/dist/assets/improve-strategies/default.json +1 -1
  10. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  11. package/dist/assets/prompts/memory-infer-user.md +2 -3
  12. package/dist/assets/stash-skeleton/README.md +6 -5
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +2 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +2 -0
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +2 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +2 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +2 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +2 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +2 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +2 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +2 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -0
  24. package/dist/assets/stash-skeleton/facts/conventions/organization.md +20 -9
  25. package/dist/assets/tasks/core/extract.yml +1 -1
  26. package/dist/assets/tasks/core/version-check.yml +1 -1
  27. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +5 -0
  28. package/dist/assets/tasks/improve/akm-improve-catchup.yml +8 -0
  29. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +5 -0
  30. package/dist/assets/tasks/improve/akm-improve-frequent.yml +5 -0
  31. package/dist/assets/tasks/improve/akm-improve-nightly.yml +5 -0
  32. package/dist/assets/templates/html/health.html +1 -3
  33. package/dist/assets/workflows/workflow-template.md +32 -15
  34. package/dist/cli/invocation.js +40 -15
  35. package/dist/cli/parse-args.js +0 -22
  36. package/dist/cli/retired-commands.js +121 -0
  37. package/dist/cli/shared.js +154 -22
  38. package/dist/cli/unknown-flags.js +236 -0
  39. package/dist/cli-node.mjs +2 -1
  40. package/dist/cli.js +696 -258
  41. package/dist/commands/agent/agent-dispatch.js +14 -3
  42. package/dist/commands/agent/contribute-cli.js +74 -89
  43. package/dist/commands/completions.js +79 -22
  44. package/dist/commands/config-cli.js +17 -150
  45. package/dist/commands/env/env-cli.js +59 -143
  46. package/dist/commands/env/env.js +12 -163
  47. package/dist/commands/env/marker-path.js +6 -0
  48. package/dist/commands/env/secret-cli.js +36 -66
  49. package/dist/commands/env/secret.js +24 -57
  50. package/dist/commands/feedback-cli.js +148 -88
  51. package/dist/commands/health/accept-rate.js +58 -0
  52. package/dist/commands/health/advisories.js +3 -4
  53. package/dist/commands/health/checks.js +85 -23
  54. package/dist/commands/health/html-report.js +7 -10
  55. package/dist/commands/health/improve-metrics.js +25 -83
  56. package/dist/commands/health/llm-usage.js +2 -1
  57. package/dist/commands/health/md-report.js +5 -9
  58. package/dist/commands/health/metrics.js +62 -20
  59. package/dist/commands/health/renderers.js +47 -0
  60. package/dist/commands/health/report-view-model.js +4 -5
  61. package/dist/commands/health/stash-exposure.js +1 -1
  62. package/dist/commands/health/surfaces.js +4 -122
  63. package/dist/commands/health/task-runs.js +3 -67
  64. package/dist/commands/health/types-improve.js +7 -0
  65. package/dist/commands/health.js +160 -35
  66. package/dist/commands/improve/anti-collapse.js +2 -2
  67. package/dist/commands/improve/autonomy-gate.js +68 -0
  68. package/dist/commands/improve/collapse-detector.js +41 -40
  69. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  70. package/dist/commands/improve/consolidate/merge.js +4 -0
  71. package/dist/commands/improve/consolidate.js +140 -1000
  72. package/dist/commands/improve/distill/promote-memory.js +12 -12
  73. package/dist/commands/improve/distill/quality-gate.js +11 -6
  74. package/dist/commands/improve/distill.js +58 -69
  75. package/dist/commands/improve/eligibility.js +101 -55
  76. package/dist/commands/improve/extract-cli.js +14 -133
  77. package/dist/commands/improve/improve-cli.js +98 -114
  78. package/dist/commands/improve/improve-result-file.js +1 -28
  79. package/dist/commands/improve/improve-strategies.js +8 -5
  80. package/dist/commands/improve/improve.js +254 -101
  81. package/dist/commands/improve/locks.js +7 -0
  82. package/dist/commands/improve/loop-stages.js +182 -20
  83. package/dist/commands/improve/memory/derived-ref.js +45 -43
  84. package/dist/commands/improve/memory/memory-belief.js +1 -1
  85. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  86. package/dist/commands/improve/memory/memory-improve.js +14 -5
  87. package/dist/commands/improve/outcome-loop.js +22 -65
  88. package/dist/commands/improve/preparation.js +114 -123
  89. package/dist/commands/improve/proactive-maintenance.js +2 -5
  90. package/dist/commands/improve/reflect.js +56 -160
  91. package/dist/commands/improve/run-context.js +5 -0
  92. package/dist/commands/improve/salience.js +11 -122
  93. package/dist/commands/improve/session-asset.js +4 -0
  94. package/dist/commands/improve/source-identity.js +10 -38
  95. package/dist/commands/lint/base-linter.js +51 -131
  96. package/dist/commands/lint/env-key-rules.js +31 -47
  97. package/dist/commands/lint/index.js +444 -88
  98. package/dist/commands/lint/types.js +22 -1
  99. package/dist/commands/{events.js → log.js} +33 -38
  100. package/dist/commands/migrate-cli.js +92 -12
  101. package/dist/commands/migration-tool.js +46 -0
  102. package/dist/commands/observability-cli.js +70 -209
  103. package/dist/commands/proposal/drain.js +101 -29
  104. package/dist/commands/proposal/proposal-cli.js +76 -48
  105. package/dist/commands/proposal/proposal.js +54 -18
  106. package/dist/commands/proposal/propose-cli.js +88 -0
  107. package/dist/commands/proposal/propose.js +23 -15
  108. package/dist/commands/proposal/repository.js +718 -279
  109. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  110. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  111. package/dist/commands/proposal/validators/proposals.js +4 -7
  112. package/dist/commands/read/curate.js +34 -53
  113. package/dist/commands/read/knowledge.js +150 -95
  114. package/dist/commands/read/registry-search.js +2 -2
  115. package/dist/commands/read/remember-cli.js +42 -15
  116. package/dist/commands/read/search-cli.js +180 -78
  117. package/dist/commands/read/search.js +58 -43
  118. package/dist/commands/read/show.js +197 -141
  119. package/dist/commands/registry-cli.js +12 -51
  120. package/dist/commands/remember.js +14 -57
  121. package/dist/commands/sources/add-cli.js +100 -31
  122. package/dist/commands/sources/bundle-cli.js +166 -0
  123. package/dist/commands/sources/bundle-config-ops.js +7 -2
  124. package/dist/commands/sources/info.js +30 -7
  125. package/dist/commands/sources/init.js +12 -12
  126. package/dist/commands/sources/installed-stashes.js +387 -98
  127. package/dist/commands/sources/schema-repair.js +3 -2
  128. package/dist/commands/sources/self-update.js +131 -38
  129. package/dist/commands/sources/source-add.js +72 -17
  130. package/dist/commands/sources/source-clone.js +129 -45
  131. package/dist/commands/sources/source-manage.js +43 -23
  132. package/dist/commands/sources/sources-cli.js +57 -208
  133. package/dist/commands/sources/stash-cli.js +46 -53
  134. package/dist/commands/tasks/tasks-cli.js +91 -97
  135. package/dist/commands/tasks/tasks.js +284 -423
  136. package/dist/commands/workflow-cli.js +181 -450
  137. package/dist/core/abort-deadline.js +28 -0
  138. package/dist/core/adapter/adapters/agent-skills-adapter.js +67 -5
  139. package/dist/core/adapter/adapters/akm-adapter.js +60 -38
  140. package/dist/core/adapter/adapters/akm-lint.js +109 -42
  141. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  142. package/dist/core/adapter/adapters/akm-task-adapter.js +54 -31
  143. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  144. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  145. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  146. package/dist/core/adapter/adapters/index.js +6 -6
  147. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  148. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  149. package/dist/core/adapter/adapters/shared.js +3 -19
  150. package/dist/core/adapter/adapters/tool-dir-shared.js +13 -6
  151. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  152. package/dist/core/adapter/detect-adapter.js +17 -0
  153. package/dist/core/adapter/recognize-match.js +6 -4
  154. package/dist/core/adapter/validate-context.js +214 -0
  155. package/dist/core/asset/akm-markdown.js +63 -0
  156. package/dist/core/asset/asset-placement.js +20 -6
  157. package/dist/core/asset/asset-ref.js +11 -9
  158. package/dist/core/asset/frontmatter-lint.js +30 -0
  159. package/dist/core/asset/frontmatter.js +29 -9
  160. package/dist/core/asset/markdown.js +40 -51
  161. package/dist/core/asset/resolve-ref.js +89 -18
  162. package/dist/core/asset/stash-meta.js +1 -1
  163. package/dist/core/bundle-id.js +51 -0
  164. package/dist/core/common.js +218 -44
  165. package/dist/core/concurrent.js +32 -0
  166. package/dist/core/config/config-io.js +12 -1
  167. package/dist/core/config/config-schema.js +35 -8
  168. package/dist/core/config/config-sources.js +55 -11
  169. package/dist/core/config/config-walker.js +25 -9
  170. package/dist/core/config/config.js +9 -48
  171. package/dist/core/config/experimental.js +21 -0
  172. package/dist/core/config/schema/embedding.js +5 -1
  173. package/dist/core/config/schema/experimental.js +30 -0
  174. package/dist/core/config/schema/improve-processes.js +0 -6
  175. package/dist/core/config/schema/improve.js +21 -3
  176. package/dist/core/config/schema/index-config.js +8 -15
  177. package/dist/core/config/schema/output.js +4 -1
  178. package/dist/core/config/schema/setup.js +9 -18
  179. package/dist/core/config/schema/sources-bundles.js +49 -33
  180. package/dist/core/config/schema/workflow.js +14 -3
  181. package/dist/core/env-secret-ref.js +76 -46
  182. package/dist/core/errors.js +43 -12
  183. package/dist/core/events.js +76 -152
  184. package/dist/core/file-change.js +6 -5
  185. package/dist/core/file-lock.js +7 -1
  186. package/dist/core/fs-txn.js +83 -7
  187. package/dist/core/git-message.js +2 -2
  188. package/dist/core/improve-result.js +6 -100
  189. package/dist/core/json-schema.js +327 -9
  190. package/dist/core/lesson-lint.js +1 -17
  191. package/dist/core/logs-db.js +2 -1
  192. package/dist/core/loopback.js +89 -0
  193. package/dist/core/migration-operation.js +33 -2
  194. package/dist/core/mutation-target.js +78 -0
  195. package/dist/core/parse.js +4 -1
  196. package/dist/core/path-access.js +107 -0
  197. package/dist/core/paths.js +17 -20
  198. package/dist/core/recognition-util.js +12 -14
  199. package/dist/core/redaction.js +98 -17
  200. package/dist/core/spawn-env.js +234 -0
  201. package/dist/core/standards/resolve-standards-context.js +2 -14
  202. package/dist/core/standards/resolve-stash-standards.js +2 -2
  203. package/dist/core/standards/resolve-type-conventions.js +2 -2
  204. package/dist/core/state/migrations.js +41 -18
  205. package/dist/core/state-db-scope.js +134 -0
  206. package/dist/core/state-db.js +5 -14
  207. package/dist/core/structured.js +1 -1
  208. package/dist/core/subprocess.js +182 -36
  209. package/dist/core/text-truncation.js +9 -5
  210. package/dist/core/type-presentation.js +3 -3
  211. package/dist/core/warn.js +0 -3
  212. package/dist/core/write-provenance.js +85 -0
  213. package/dist/core/write-source.js +778 -95
  214. package/dist/indexer/bundle-identity-guard.js +3 -2
  215. package/dist/indexer/db/graph-db.js +17 -30
  216. package/dist/indexer/ensure-index.js +11 -3
  217. package/dist/indexer/graph/graph-boost.js +9 -34
  218. package/dist/indexer/graph/graph-extraction.js +8 -5
  219. package/dist/indexer/index-writer-lock.js +53 -17
  220. package/dist/indexer/index-written-assets.js +33 -24
  221. package/dist/indexer/indexer.js +551 -253
  222. package/dist/indexer/installations.js +14 -96
  223. package/dist/indexer/passes/dir-staleness.js +16 -9
  224. package/dist/indexer/passes/memory-inference.js +15 -9
  225. package/dist/indexer/passes/metadata.js +113 -47
  226. package/dist/indexer/scan/doc-to-entry.js +38 -1
  227. package/dist/indexer/scan/drain-dir.js +13 -23
  228. package/dist/indexer/search/db-search.js +107 -69
  229. package/dist/indexer/search/fts-query.js +47 -24
  230. package/dist/indexer/search/ranking-contributors.js +42 -20
  231. package/dist/indexer/search/ranking.js +18 -99
  232. package/dist/indexer/search/search-fields.js +7 -2
  233. package/dist/indexer/search/search-source.js +82 -93
  234. package/dist/indexer/usage/usage-events.js +0 -89
  235. package/dist/indexer/walk/file-context.js +2 -1
  236. package/dist/indexer/walk/matchers.js +30 -43
  237. package/dist/indexer/walk/path-resolver.js +7 -2
  238. package/dist/indexer/walk/walker.js +38 -12
  239. package/dist/integrations/agent/builders.js +0 -6
  240. package/dist/integrations/agent/config.js +2 -2
  241. package/dist/integrations/agent/detect.js +49 -19
  242. package/dist/integrations/agent/engine-fallback.js +76 -0
  243. package/dist/integrations/agent/engine-resolution.js +24 -11
  244. package/dist/integrations/agent/model-aliases.js +1 -1
  245. package/dist/integrations/agent/profiles.js +23 -1
  246. package/dist/integrations/agent/prompts.js +12 -8
  247. package/dist/integrations/agent/runner-dispatch.js +4 -2
  248. package/dist/integrations/agent/runner.js +0 -1
  249. package/dist/integrations/agent/spawn.js +20 -93
  250. package/dist/integrations/github.js +1 -1
  251. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  252. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  253. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  254. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  255. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  256. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  257. package/dist/integrations/harnesses/index.js +3 -7
  258. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  259. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  260. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  261. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  262. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  263. package/dist/integrations/lockfile.js +132 -8
  264. package/dist/integrations/session-logs/index.js +3 -28
  265. package/dist/llm/client.js +136 -100
  266. package/dist/llm/embedders/remote.js +13 -5
  267. package/dist/llm/feature-gate.js +4 -12
  268. package/dist/llm/graph-extract.js +5 -11
  269. package/dist/llm/memory-infer.js +144 -1
  270. package/dist/llm/metadata-enhance.js +5 -7
  271. package/dist/llm/structured-call.js +1 -1
  272. package/dist/llm/usage-persist.js +26 -5
  273. package/dist/llm/usage-telemetry.js +25 -2
  274. package/dist/output/cli-hints.js +1 -2
  275. package/dist/output/context.js +22 -7
  276. package/dist/output/format-exempt.js +80 -0
  277. package/dist/output/generic-render.js +259 -0
  278. package/dist/output/render-registry.js +57 -0
  279. package/dist/output/renderers.js +14 -36
  280. package/dist/output/shapes/curate.js +10 -1
  281. package/dist/output/shapes/events.js +12 -7
  282. package/dist/output/shapes/helpers.js +56 -83
  283. package/dist/output/shapes/migrate.js +8 -0
  284. package/dist/output/shapes/passthrough.js +7 -41
  285. package/dist/output/shapes/proposal/producer.js +15 -7
  286. package/dist/output/shapes.js +2 -9
  287. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  288. package/dist/output/text/bundle-show.js +7 -0
  289. package/dist/output/text/command-format.js +164 -96
  290. package/dist/output/text/env.js +1 -3
  291. package/dist/output/text/events.js +8 -7
  292. package/dist/output/text/health-format.js +103 -0
  293. package/dist/output/text/health.js +7 -0
  294. package/dist/output/text/helpers.js +10 -8
  295. package/dist/output/text/lint-format.js +56 -0
  296. package/dist/{migrate-storage-node.mjs → output/text/lint.js} +2 -5
  297. package/dist/output/text/migrate.js +88 -0
  298. package/dist/output/text/proposal/producer.js +4 -2
  299. package/dist/output/text/proposal-format.js +44 -72
  300. package/dist/output/text/registry-commands.js +1 -2
  301. package/dist/output/text/show-directives.js +15 -7
  302. package/dist/output/text/status-list.js +32 -0
  303. package/dist/output/text/{save.js → sync.js} +2 -2
  304. package/dist/output/text/workflow-format.js +24 -203
  305. package/dist/output/text/workflow.js +1 -7
  306. package/dist/output/text.js +16 -17
  307. package/dist/registry/factory.js +4 -6
  308. package/dist/registry/origin-resolve.js +16 -27
  309. package/dist/registry/providers/skills-sh.js +3 -3
  310. package/dist/registry/providers/static-index.js +13 -23
  311. package/dist/registry/resolve.js +42 -7
  312. package/dist/registry/semver.js +34 -84
  313. package/dist/runtime.js +2 -23
  314. package/dist/scripts/akm-migrate-node.js +60972 -0
  315. package/dist/scripts/akm-migrate.js +60310 -0
  316. package/dist/setup/detect.js +42 -15
  317. package/dist/setup/registry-stash-loader.js +2 -2
  318. package/dist/setup/setup.js +236 -136
  319. package/dist/setup/steps/connection.js +7 -9
  320. package/dist/setup/steps/platforms.js +9 -9
  321. package/dist/setup/steps/semantic.js +15 -3
  322. package/dist/setup/steps/sources.js +12 -13
  323. package/dist/setup/steps/stashdir.js +2 -3
  324. package/dist/setup/steps/tasks.js +237 -120
  325. package/dist/sources/freshness.js +1 -1
  326. package/dist/sources/provider-factory.js +11 -17
  327. package/dist/sources/providers/filesystem.js +2 -3
  328. package/dist/sources/providers/git-install.js +278 -34
  329. package/dist/sources/providers/git-provider.js +25 -23
  330. package/dist/sources/providers/git-stash.js +414 -106
  331. package/dist/sources/providers/git.js +2 -2
  332. package/dist/sources/providers/npm.js +16 -19
  333. package/dist/sources/providers/provider-utils.js +7 -4
  334. package/dist/sources/providers/sync-from-ref.js +3 -9
  335. package/dist/sources/providers/website.js +6 -1
  336. package/dist/sources/resolve.js +6 -5
  337. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  338. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  339. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  340. package/dist/sources/snapshot-fetchers/github.js +100 -0
  341. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  342. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  343. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  344. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  345. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  346. package/dist/sources/snapshot-fetchers/website-ingest.js +686 -282
  347. package/dist/sources/snapshot-fetchers/x.js +910 -0
  348. package/dist/storage/database.js +13 -1
  349. package/dist/storage/engines/sqlite-migrations.js +23 -111
  350. package/dist/storage/managed-db.js +21 -2
  351. package/dist/storage/repositories/canaries-repository.js +1 -1
  352. package/dist/storage/repositories/events-repository.js +27 -11
  353. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  354. package/dist/storage/repositories/index-connection.js +44 -8
  355. package/dist/storage/repositories/index-entries-repository.js +157 -241
  356. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  357. package/dist/storage/repositories/index-fts-repository.js +5 -2
  358. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  359. package/dist/storage/repositories/index-meta-repository.js +2 -3
  360. package/dist/storage/repositories/index-schema.js +10 -25
  361. package/dist/storage/repositories/index-utility-repository.js +15 -28
  362. package/dist/storage/repositories/index-vec-repository.js +6 -1
  363. package/dist/storage/repositories/outcome-repository.js +119 -0
  364. package/dist/storage/repositories/proposals-repository.js +296 -59
  365. package/dist/storage/repositories/registry-cache.js +19 -0
  366. package/dist/storage/repositories/salience-repository.js +172 -0
  367. package/dist/storage/repositories/task-history-repository.js +15 -13
  368. package/dist/storage/repositories/workflow-runs-repository.js +118 -53
  369. package/dist/tasks/backends/cron.js +105 -15
  370. package/dist/tasks/backends/index.js +1 -1
  371. package/dist/tasks/backends/launchd.js +85 -38
  372. package/dist/tasks/backends/schtasks.js +135 -15
  373. package/dist/tasks/embedded.js +56 -40
  374. package/dist/tasks/log-redaction.js +156 -0
  375. package/dist/tasks/parser.js +87 -160
  376. package/dist/tasks/resolve-akm-bin.js +137 -59
  377. package/dist/tasks/runner.js +209 -44
  378. package/dist/tasks/scheduler-invocation.js +220 -10
  379. package/dist/tasks/schema.js +89 -1
  380. package/dist/tasks/task-id.js +1 -3
  381. package/dist/tasks/validator.js +20 -6
  382. package/dist/workflows/authoring/authoring.js +94 -143
  383. package/dist/workflows/authoring/scope-key.js +1 -1
  384. package/dist/workflows/concurrency-policy.js +95 -1
  385. package/dist/workflows/exec/dispatch-redaction.js +114 -0
  386. package/dist/workflows/exec/exec-unit.js +542 -0
  387. package/dist/workflows/exec/frozen-judge.js +130 -32
  388. package/dist/workflows/exec/native-executor.js +513 -274
  389. package/dist/workflows/exec/param-secrets.js +12 -11
  390. package/dist/workflows/exec/run-workflow.js +496 -237
  391. package/dist/workflows/exec/step-work.js +616 -412
  392. package/dist/workflows/exec/unit-dispatch.js +34 -0
  393. package/dist/workflows/exec/unit-writer.js +53 -13
  394. package/dist/workflows/exec/worktree.js +454 -41
  395. package/dist/workflows/ir/compile.js +166 -271
  396. package/dist/workflows/ir/freeze.js +119 -42
  397. package/dist/workflows/ir/params.js +135 -11
  398. package/dist/workflows/ir/plan-hash.js +1 -1
  399. package/dist/workflows/ir/schema.js +129 -45
  400. package/dist/workflows/parser.js +1070 -307
  401. package/dist/workflows/program/expressions.js +20 -208
  402. package/dist/workflows/program/schema.js +31 -10
  403. package/dist/workflows/renderer.js +123 -68
  404. package/dist/workflows/resource-limits.js +184 -0
  405. package/dist/workflows/runtime/checkin.js +3 -3
  406. package/dist/workflows/runtime/plan-classifier.js +16 -75
  407. package/dist/workflows/runtime/runs.js +331 -132
  408. package/dist/workflows/runtime/unit-checkin.js +1 -1
  409. package/dist/workflows/runtime/unit-phases.js +2 -2
  410. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  411. package/dist/workflows/schema.js +1 -11
  412. package/dist/workflows/validate-summary.js +47 -38
  413. package/dist/workflows/validator.js +21 -62
  414. package/docs/README.md +110 -0
  415. package/docs/migration/README.md +8 -0
  416. package/docs/migration/release-notes/0.7.0.md +11 -11
  417. package/docs/migration/release-notes/0.9.0.md +209 -27
  418. package/docs/migration/v0.7-to-v0.8.md +48 -48
  419. package/docs/migration/v0.8-to-v0.9.md +567 -209
  420. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  421. package/docs/reference/README.md +19 -0
  422. package/docs/reference/bundle-types.md +19 -0
  423. package/docs/reference/cli.md +2342 -0
  424. package/docs/reference/configuration.md +371 -0
  425. package/docs/reference/data-and-telemetry.md +129 -46
  426. package/docs/reference/supported-formats.md +50 -0
  427. package/docs/reference/workflow-schema.md +1014 -0
  428. package/docs/reference/workflows.md +51 -0
  429. package/package.json +31 -13
  430. package/schemas/akm-asset-envelope.json +93 -0
  431. package/schemas/akm-config.json +89 -128
  432. package/schemas/akm-task.json +27 -5
  433. package/schemas/akm-workflow.json +157 -81
  434. package/dist/assets/tasks/core/backup.yml +0 -5
  435. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  436. package/dist/cli/config-migrate.js +0 -1878
  437. package/dist/cli/config-validate.js +0 -41
  438. package/dist/commands/backup-cli.js +0 -56
  439. package/dist/commands/bundle/bundle-cli.js +0 -68
  440. package/dist/commands/bundle/bundle.js +0 -219
  441. package/dist/commands/graph/graph-cli.js +0 -124
  442. package/dist/commands/graph/graph.js +0 -489
  443. package/dist/commands/improve/extract-watch.js +0 -140
  444. package/dist/commands/mv-cli.js +0 -1221
  445. package/dist/commands/sources/history.js +0 -201
  446. package/dist/commands/tasks/default-tasks.js +0 -186
  447. package/dist/core/migration-backup.js +0 -1231
  448. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  449. package/dist/llm/memory-infer-impl.js +0 -138
  450. package/dist/migrate/legacy/config-source-migration.js +0 -257
  451. package/dist/migrate/legacy/content-migration.js +0 -350
  452. package/dist/migrate/legacy/legacy-layout.js +0 -779
  453. package/dist/migrate/legacy/legacy-paths.js +0 -25
  454. package/dist/migrate/legacy/legacy-stash-json.js +0 -80
  455. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  456. package/dist/migrate/legacy/task-target-ref-migration.js +0 -278
  457. package/dist/migrate/legacy/three-db-cutover.js +0 -845
  458. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  459. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  460. package/dist/migrate/legacy-ref-grammar.js +0 -214
  461. package/dist/output/shapes/distill.js +0 -14
  462. package/dist/output/shapes/history.js +0 -11
  463. package/dist/output/text/distill.js +0 -6
  464. package/dist/output/text/enable-disable.js +0 -8
  465. package/dist/output/text/history.js +0 -6
  466. package/dist/registry/build-index.js +0 -382
  467. package/dist/schemas/akm-config.json +0 -4704
  468. package/dist/schemas/akm-task.json +0 -87
  469. package/dist/schemas/akm-workflow.json +0 -372
  470. package/dist/scripts/migrate-storage.js +0 -3816
  471. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  472. package/dist/workflows/cli.js +0 -53
  473. package/dist/workflows/exec/brief.js +0 -481
  474. package/dist/workflows/exec/report.js +0 -1460
  475. package/dist/workflows/exec/watch.js +0 -116
  476. package/dist/workflows/program/parser.js +0 -813
  477. package/dist/workflows/program/project.js +0 -104
package/CHANGELOG.md CHANGED
@@ -4,12 +4,721 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
- ## [0.9.0] - 2026-07-20
7
+ ## [Unreleased]
8
+
9
+ ## [0.9.1-beta.1] - 2026-08-13
10
+
11
+ ### Breaking changes & migration
12
+
13
+ The 0.9.x series carries breaking changes as it works toward the 0.10.x
14
+ stabilization line. Every item here is detailed further down; this section is
15
+ what an upgrader reads first.
16
+
17
+ - **A data directory akm cannot READ is now an error, not an empty result.**
18
+ Commands that previously returned `hits: []` / `entryCount: 0` / "nothing
19
+ eligible" at exit 0 for an index, lockfile or database they lacked permission
20
+ on now raise `DATA_DIR_UNREADABLE` (exit 78) naming the path, errno, mode,
21
+ owner and running uid. *Affected:* anyone whose data dir is partly unreadable
22
+ — most often a `$XDG_DATA_HOME` shared across uids. *Remedy:* fix the
23
+ ownership or mode the error names, or point `AKM_DATA_DIR` somewhere this
24
+ user owns. The old behaviour was a false success, so a script that treated
25
+ exit 0 as "no results" was already being lied to.
26
+
27
+ - **Lockfile writes refuse to run against an unreadable `akm.lock`.**
28
+ `akm bundle add` / `remove` / `update` now fail closed instead of reading the
29
+ lock as empty and writing the single incoming entry over the whole record.
30
+ *Remedy:* as above. This one prevented real data loss — see Fixed.
31
+
32
+ - **`akm workflow run` exits 1 when a run ends `blocked`.** Previously 0.
33
+ *Affected:* CI steps and scheduled wrappers that branched only on `failed`.
34
+ *Remedy:* treat nonzero as "not verified"; resume with
35
+ `akm workflow resume <id>`.
36
+
37
+ - **`akm index --clean` no longer deletes entries whose file it cannot read.**
38
+ It keeps and names them. *Affected:* anyone relying on `--clean` to prune
39
+ aggressively; it is now conservative where it cannot see.
40
+
41
+ - **Workflow documents are bounds-checked at authoring time.** `engine:` name
42
+ grammar, `retry.max` 0–100, `gate.max_loops` 1–100, `map.concurrency` and
43
+ `engines.<name>.concurrency` 1–64, and any `timeout:` ≤ 2 147 483 647 ms are
44
+ now enforced by the parser. *Affected:* documents that parsed at 0.9.0 but
45
+ could never actually run — the frozen-plan decoder already refused them.
46
+ *Remedy:* edit the offending field; the error is now line-anchored.
47
+
48
+ - **`akm health` no longer emits `secret-file-perms`, and no longer exits 4 for
49
+ it.** The check is gone. *Affected:* anything parsing health output for that
50
+ check name.
51
+
52
+ - **Command-target task logs are now redacted.** Output that previously
53
+ persisted verbatim may now contain `[REDACTED]`. *Affected:* anything
54
+ grepping task logs for values that are now recognised as secrets.
55
+
56
+ - **Leftover `isolation: worktree` trees are garbage-collected after 7 days.**
57
+ *Remedy:* copy anything you want to keep out of a retained worktree within a
58
+ week.
59
+
60
+ ### Added
61
+
62
+ - **`exec` workflow units — run a shell command as a workflow step.** A step
63
+ whose `unit:` block declares `exec:` runs a command directly instead of
64
+ dispatching to an LLM or an agent, so deterministic work (test suites,
65
+ builds, lint, scripts) no longer costs a model dispatch, its latency, its
66
+ tokens, or its nondeterminism.
67
+
68
+ ```yaml
69
+ - id: test
70
+ unit:
71
+ exec:
72
+ command: ["bun", "run", "test:unit"]
73
+ pass_env: [CARGO_HOME] # optional: widen the default env allowlist
74
+ timeout: "10m"
75
+ retry: { max: 1, on: [timeout] }
76
+ ```
77
+
78
+ - **`command:` is an argv array; there is no shell-string spelling.** The
79
+ child is spawned directly, so `;`, `|`, `&&`, `$(…)` and `*` inside an
80
+ argument are inert literal bytes — the quoting/injection class is
81
+ structurally absent, not defended against. Write `["bash", "-lc", "…"]`
82
+ when a pipeline is genuinely wanted, and own that choice in the diff.
83
+ - **An exec unit names no engine.** It rejects `engine`/`model`/`llm`, spends
84
+ no tokens, and a workflow made only of exec steps runs on an install with
85
+ no engine configured at all.
86
+ - **Everything else about a unit still applies:** `timeout`, `retry`,
87
+ `on_error`, `output`, `env`, `isolation: worktree`, `map` fan-out and its
88
+ concurrency limits, the unit journal, budget accounting, and replay/reuse
89
+ (a completed exec unit is never re-run on resume).
90
+ - **Output rule:** stdout is the promoted artifact with trailing newlines
91
+ stripped (like shell `$(…)`); with an `output:` schema on the unit, stdout
92
+ must be exactly one JSON value, strictly parsed and validated. stderr is a
93
+ diagnostic channel only. A schema miss is *not* re-prompted — a fixed argv
94
+ cannot answer feedback, but re-running it could deploy twice.
95
+ - **Exit codes:** non-zero → `non_zero_exit`, wall-clock expiry → `timeout`,
96
+ cancellation → `aborted`, failure to start → `spawn_failed`. Those are
97
+ pre-existing `retry.on` reasons. With the default `on_error: fail`, a
98
+ non-zero exit fails the step and the run, which is what makes a `test` step
99
+ a gate.
100
+ - **A partial capture is never promoted as the artifact.** Exiting 0 does not
101
+ prove stdout was read to the end: a pipe can error, and a background
102
+ descendant holding the stdout handle open after the command leader exits
103
+ keeps the pipe alive past the drain deadline. Both leave a *prefix* of the
104
+ real output, so the unit fails — with its own reason,
105
+ `exec_capture_incomplete`, which is deliberately **not** a `retry.on` value.
106
+ The command already ran; re-dispatching identical argv to fix a capture
107
+ problem would run its side effects a second time.
108
+ - **Everything the command can spend is bounded — without inventing failures.**
109
+ Alongside the wall-clock timeout, akm bounds the memory it spends on the
110
+ command's behalf and the environment it can hand the command. Both bounds are
111
+ built so that they only ever *explain* a failure that was going to happen
112
+ anyway; neither fails a run that would otherwise have succeeded.
113
+
114
+ **Retained output: 8 MiB per stream, drain-and-discard.** akm keeps at most
115
+ 8 MiB of stdout and 8 MiB of stderr. Past the cap it keeps *reading* the pipe
116
+ and throws the extra bytes away, so the child never blocks on backpressure:
117
+ the command runs to completion and its real exit code decides the unit. A
118
+ verbose-but-passing test suite is not failed over its log volume. What
119
+ overflow costs is completeness of the artifact, and that is never hidden —
120
+ a step with **no** `output:` schema succeeds and its artifact is the retained
121
+ head with a `__akm_exec_output_truncated__` block appended (naming bytes
122
+ written vs bytes retained), so truncated data can never be mistaken for
123
+ complete data by `steps.<id>.output`, a gate judge, or a human. A step **with**
124
+ an `output:` schema still fails `exec_output_limit`: stdout must parse as
125
+ exactly one JSON value, a truncated prefix cannot, and promoting it would
126
+ corrupt every downstream reference to the typed artifact.
127
+
128
+ **Context environment: this platform's ceiling, not the smallest one.** The
129
+ engine-authored `AKM_*` context is capped at **96 KiB per variable / 128 KiB
130
+ total** on Linux, macOS and BSD, and at **32 767 bytes per variable / 64 000
131
+ bytes total** on Windows. The numbers cite their sources: Linux's
132
+ `MAX_ARG_STRLEN` (`32 * PAGE_SIZE` = 131 072 bytes per `argv`/`environ`
133
+ string), macOS's 256 KiB `ARG_MAX` over argv + environ combined, and Win32
134
+ `SetEnvironmentVariable`'s 32 767-character per-variable limit. Crossing the
135
+ bound fails `exec_context_too_large` *before* the spawn, with an error naming
136
+ the variable, its size, this platform's limit and where that limit comes from
137
+ — replacing a bare `E2BIG` from the spawn syscall that named neither the
138
+ variable nor the data behind it. Converting that inevitable failure into an
139
+ actionable one is the check's *only* job, so it uses the ceiling of the
140
+ platform the run is on: previously it applied Windows' limit everywhere and
141
+ refused spawns Linux and macOS would have accepted. Workflows that must also
142
+ run on Windows should stay under the smaller bound — that is documented
143
+ guidance now, not something a Linux host enforces.
144
+
145
+ `exec_output_limit` and `exec_context_too_large` keep their meanings and
146
+ their place outside the `retry.on` vocabulary, alongside `exec_cwd_escape`:
147
+ each is deterministic, so re-dispatching could only spend the budget again.
148
+ `PROGRAM_RETRY_REASONS` is unchanged.
149
+ - **A failing command's stderr survives to a durable surface.** The unit
150
+ journal now keeps each failed unit's redacted diagnostic (clipped to 2000
151
+ characters), and the step summary carries the first failure's. For an exec
152
+ unit that is the difference between `akm workflow status --units` saying
153
+ `non_zero_exit` and it saying *why* — a command that explains itself only on
154
+ stderr with empty stdout previously left no diagnostic anywhere durable.
155
+ This is an output surface only: the unit input hash is computed from
156
+ plan-frozen inputs, so no completed unit re-dispatches because of it.
157
+ - **The child's environment is an ALLOWLIST, not an inheritance.** The
158
+ command starts from an empty environment and receives `PATH`, `HOME`, the
159
+ identity/locale/temp/terminal variables, the Windows process-creation
160
+ essentials (`SystemRoot`, `SystemDrive`, `WINDIR`, `COMSPEC`, `PATHEXT`)
161
+ and the Windows home/config roots, plus `AKM_EVENT_SOURCE` — then the
162
+ unit's `env:` bindings, then the `AKM_*` context. `exec.pass_env: [NAME…]`
163
+ adds a few more names (for a per-machine toolchain variable like
164
+ `CARGO_HOME`, which a committed `env:` asset cannot express);
165
+ `exec.inherit_env: true` opts all the way back into akm's whole
166
+ environment. Both keys live inside `exec:` because the unit-level `env:`
167
+ key already means "env asset binding refs", and both are dispatch-
168
+ significant, so both are in the input hash.
169
+
170
+ This is not a claim to stop a determined attacker — a command that runs at
171
+ all can read the same credentials off disk. It bounds **accidental**
172
+ exposure (the invoking shell or CI job routinely exports tokens for
173
+ unrelated services), makes the environment surface **explicit and
174
+ reviewable**, and **matches the convention akm already applies** to
175
+ agent-harness children (`profile.envPassthrough`), which now share one
176
+ mechanism with exec units instead of two.
177
+ - **Security:** commands run inside the existing workflow trust model.
178
+ Secrets come from `env:` bindings by NAME — the frozen plan and the replay
179
+ hash carry only ref names, and resolved values are scrubbed from stdout,
180
+ stderr, and failure diagnostics by the same redaction contract every other
181
+ dispatch uses, before anything is journaled. `cwd:` is relative and
182
+ `..`-free, re-checked against the resolved base (symlinks included) before
183
+ spawning.
184
+ - **Cancellation is real:** the child is spawned in its own process group and
185
+ gets a SIGTERM→SIGKILL ladder on timeout or abort, so `--timeout` / Ctrl-C
186
+ stop a running command without orphaning its children.
187
+ - **No replay churn:** the exec spec was added to the unit input-hash preimage
188
+ as a key present only on exec units, so `hashVersion` stays 4 and every
189
+ previously-frozen llm/agent/sdk unit hashes byte-identically — runs already
190
+ in flight neither re-dispatch nor diverge. The env-scope keys are inside
191
+ that same spec and are frozen only in their non-default form (`inherit_env`
192
+ only when `true`, `pass_env` only when non-empty), so an exec unit that says
193
+ nothing about its environment hashes byte-identically too.
194
+
195
+ See [Workflow Schema: Exec (shell) units](docs/reference/workflow-schema.md#exec-shell-units)
196
+ and the worked example in
197
+ [Author's Guide](https://github.com/itlackey/akm/blob/main/docs/guides/author-workflows.md#deterministic-steps-run-a-command-gate-on-it).
198
+
199
+ ### Changed
200
+
201
+ - **`akm workflow run` now exits non-zero when the run ends `blocked`.** A
202
+ verification judge that throws, cannot be resolved, or returns a malformed
203
+ verdict stops the run `blocked` — unverified, and resumable with `akm
204
+ workflow resume <id>`. That previously exited 0, so a CI step or scheduled
205
+ wrapper read an unverified run as a passing one. It now exits 1, matching
206
+ `failed` and gate rejections, and matching how the scheduled-task path
207
+ already reported it.
208
+
209
+ - **Workflow dispatch bounds are enforced at authoring time, not only by the
210
+ frozen-plan decoder.** `engine:` names must match the decoder's own grammar
211
+ (lowercase dash-separated letters/digits, starting with a letter, ≤63
212
+ chars); `retry.max` is 0–100; `gate.max_loops` is 1–100; `map.concurrency`
213
+ and `engines.<name>.concurrency` are 1–64; any `timeout:` must resolve to at
214
+ most 2 147 483 647 ms (~24.8 days, `setTimeout`'s 32-bit ceiling). Every one
215
+ of these was already refused by the frozen-plan decoder, so such a document
216
+ could never actually run — but it *parsed*, so `akm lint`, `akm workflow
217
+ show` and `akm workflow create` all reported it clean and the failure arrived
218
+ at `workflow run` as an unlocated "Invalid frozen workflow plan". The error is
219
+ now line-anchored at parse time. Nothing changes for a document already
220
+ inside the bounds.
221
+
222
+ - **`akm lint` gained an advisory channel.** The result envelope carries
223
+ `warnings: LintIssue[]` alongside `fixed`/`flagged`, `summary` gains a
224
+ `warnings` count, and text output prints a `warnings` section. Advisories
225
+ never route into `flagged`, so `--fail-on-flagged` cannot fail a run over
226
+ one. Workflow compile advisories (`workflow-warning`) are surfaced for the
227
+ first time — a step with no `output:` schema, a `params.<name>` reference to
228
+ an undeclared param, a `gate.max_loops` above 1 on an `exec` step — so a
229
+ bundle that linted clean at 0.9.0 may now report warnings without becoming a
230
+ failure. Findings that know a location carry `line` in `--format json` and
231
+ render as `file:line` in text. A new `lint-failed` code reports a file the
232
+ sweep reached but could not finish.
233
+
234
+ - **Leftover `isolation: worktree` trees are now garbage-collected.** A run
235
+ that crashed, or one whose worktree was retained after a dirty unit, used to
236
+ leave its tree under the worktrees root forever. akm now opportunistically
237
+ removes such trees once they are 7 days old, confined to the worktrees root,
238
+ symlinks skipped, containment re-checked. A worktree still in use is never
239
+ collected: every live tree carries a liveness marker (pid, host, resolved
240
+ path) in git's administrative directory for it, and the sweep skips a
241
+ candidate whose holder is still running here.
242
+
243
+ - **The workflow JSON Schema subset now enforces `allOf`/`anyOf`/`oneOf`/`not`.**
244
+ A step `output:` or `params:` schema may use the combinators, and the runtime
245
+ now evaluates them. Previously it ignored them: a schema using one was
246
+ accepted and simply constrained less than it appeared to. Evaluation stays
247
+ bounded — nesting is capped at 64 levels and one validation at 100 000 checks,
248
+ and exhausting either is reported as an error rather than a truncated pass.
249
+
250
+ **This one reaches runs already in flight.** The combinators live in the
251
+ frozen plan, which the decoder still accepts unchanged, so a run frozen before
252
+ the upgrade is resumed against the *new* evaluation: an artifact that passed
253
+ when the combinators were ignored can fail validation now. There is no
254
+ `irVersion` bump to gate it, because the plan bytes did not change — only what
255
+ they mean. Runs whose schemas use no combinators are unaffected, as is every
256
+ step already completed.
257
+
258
+ `pattern` is **not** part of the subset. It is a recognized-but-unsupported
259
+ keyword like `format` or `const`: using one is a loud, line-anchored authoring
260
+ error naming the keyword, so no schema silently fails to constrain what it
261
+ looks like it constrains. Enforcing it would mean screening every author
262
+ regex for catastrophic backtracking before the match — and any such screen
263
+ also refuses regexes authors legitimately write (the usual hand-rolled email
264
+ pattern among them), which is authoring friction with no workflow asking for
265
+ it. Where a string's shape matters, `enum` lists the allowed values,
266
+ `minLength`/`maxLength` bound the size, and a step's `### gate` rubric can
267
+ check a shape and explain a mismatch. The `format` hint now points at `enum`
268
+ rather than at `pattern`.
269
+
270
+ **Existing workflows that use one of these keywords must be edited before
271
+ they load again.** They previously parsed — the keyword was silently
272
+ non-constraining — so a workflow carrying `format: date-time` or `pattern:`
273
+ ran fine and now fails to parse for every caller: `workflow run`, `workflow
274
+ show`, `workflow create`, and `akm lint`. The quietest surface is `akm index`,
275
+ which skips an asset it cannot parse with a scan warning, so the workflow
276
+ simply stops appearing in the stash index. A run already frozen from such an
277
+ asset still resumes — the frozen-plan decoder does not re-screen keywords —
278
+ so resuming works while re-creating the same workflow errors until it is
279
+ edited.
280
+
281
+ - **A document-level `defaults.llm` is now rejected at freeze when any step
282
+ resolves onto an agent engine**, naming the step and the engine. The guard
283
+ existed before but was unreachable: overrides were computed only for `llm`
284
+ engines, so `defaults.llm` on a document with an agent step was silently
285
+ DROPPED for that step — the run proceeded with the author's sampling settings
286
+ quietly discarded. Failing loudly is the point, but it means a document that
287
+ mixes `defaults.llm` with any agent-engine step no longer freezes.
288
+
289
+ There is no per-step opt-out: `llm: {}` is a no-op, `llm: null` is a parse
290
+ error, and the layer merge is additive. Move the `llm:` block from
291
+ `defaults:` onto the `unit:` of each LLM step that wants it.
292
+
293
+ - **A scheduled workflow task now gets a 6-hour whole-run timeout by default.**
294
+ This applies to task files that declare no `timeoutMs:` — which is every task
295
+ file written before this release, since the key was previously rejected on
296
+ workflow targets. An unattended run that legitimately takes longer will be
297
+ aborted and the attempt reported failed on every firing until the task is
298
+ edited. `timeoutMs: null` opts out entirely, and any number overrides the
299
+ default. The abort itself is graceful: it lands at a step boundary, the
300
+ journal and lease are kept, and the run stays resumable with
301
+ `akm workflow resume <id>` — which the failure message names.
302
+
303
+ - **Workflow `map` steps now fan out in parallel by default.** A `map` step
304
+ that declares no `concurrency:` freezes a width of **4** instead of 1, and an
305
+ LLM engine that declares no `engines.<name>.concurrency` freezes **4** for a
306
+ remote endpoint (loopback endpoints stay at **1** — a local model server holds
307
+ one loaded model and returns HTTP 500 under concurrent inference). Both
308
+ defaults previously froze 1, which made every fan-out serial unless the author
309
+ opted in at two independent layers, and left `workflow.maxConcurrency` and the
310
+ host CPU cap binding on nothing.
311
+
312
+ This is a behavior change on a patch release, so every escape hatch is
313
+ explicit:
314
+ - `map.concurrency: 1` on a step is honored exactly as before — an authored
315
+ `1` is kept distinct from an unset field and always wins.
316
+ - New config key **`workflow.defaultMapConcurrency`** sets the default for
317
+ every workflow on the machine. `akm config set workflow.defaultMapConcurrency 1`
318
+ restores the pre-0.9.1 serial default wholesale.
319
+ - `engines.<name>.concurrency` pins any engine's own limit (and is now clamped
320
+ to `1..64` at freeze time instead of freezing a plan the decoder would then
321
+ refuse to load).
322
+ - **Runs already in flight are unaffected.** Both values are frozen into
323
+ `plan_json` when a run starts and the frozen-plan decoder requires them, so
324
+ a resumed run keeps the widths it began with. The new defaults apply only to
325
+ runs started after the upgrade.
326
+
327
+ The effective width remains the minimum of the step's `concurrency`, the run's
328
+ frozen `workflow.maxConcurrency`, the selected engine's concurrency, and the
329
+ current host's CPU cap.
330
+
331
+ - **`--max-steps` now counts steps, not engine-loop iterations.** The budget is
332
+ spent by the DISTINCT spine steps that finished — completed, failed, or
333
+ gate-rejected with the loop budget spent. It was previously spent by entries
334
+ in the `executed` report,
335
+ which gains one per gate-loop iteration and one per route-skip, so
336
+ `--max-steps 3` against a step with `gate.max_loops: 3` could stop after a
337
+ single step had finished, and an unselected branch target consumed budget for
338
+ work that was never dispatched. Three steps now means three steps, which is
339
+ what the flag has always said (`Stop after executing this many steps`). A step
340
+ the invocation left unfinished — an abort, a judge outage — still consumes
341
+ nothing, because the work is still owed. The same accounting is what a
342
+ `--max-retries` reopen subtracts, so loops and skips no longer shrink a
343
+ retry's remaining budget either, and `maxSteps:` in a workflow task file is
344
+ the same knob and moves with it. The count is now reported: `akm workflow run`
345
+ carries a `stepsProcessed` field alongside `executed`, so the number the
346
+ budget is spent on is visible rather than inferred from a list that counts
347
+ something else.
348
+
349
+ **This loosens the dispatch exposure of one invocation, and the loosening is
350
+ cumulative across steps.** A step's whole bounded gate loop now costs one step
351
+ instead of one per iteration, so the rounds a single `akm workflow run` can
352
+ dispatch go from roughly `N + max_loops` to `N × max_loops`.
353
+
354
+ What did **not** change is what the flag bounds within one step. `--max-steps`
355
+ was never a cap on total dispatch rounds on either version: the budget is
356
+ tested only BETWEEN steps, so a single step's gate loop could always run out
357
+ its full `gate.max_loops` no matter how little budget was left. The per-step
358
+ ceiling is `gate.max_loops` (1–100); the whole-run ceilings are
359
+ `budget.max_units` and `budget.max_tokens`, which are seeded from the unit
360
+ journal and hold across resumes.
361
+
362
+ ### Fixed
363
+
364
+ - **akm's Node fallback no longer aborts at teardown on Node 24.** On Node
365
+ 24.19.0 and later, any command that opened a database could intermittently
366
+ die with `node::RemoveEnvironmentCleanupHook … Assertion (env) != nullptr`
367
+ and exit 134 — after its work was done, so the failure looked random and
368
+ depended on garbage-collection timing.
369
+
370
+ The cause was upstream and nothing to do with akm's own code.
371
+ `better-sqlite3` ships one prebuilt binary per Node ABI and falls back to
372
+ `node-gyp rebuild` when none matches, and the 11.x line publishes no prebuild
373
+ for Node 24 — so installing it there silently compiled the driver from source.
374
+ Node 24.19.0 had just changed the public `node_object_wrap.h` so that
375
+ `ObjectWrap`'s constructor and destructor register and unregister an
376
+ environment cleanup hook; a binding compiled against those headers
377
+ unregisters the hook after the environment is already gone, and aborts from
378
+ V8's teardown path. Only the Node 24 line was affected, and only from that
379
+ release on.
380
+
381
+ akm now pins `better-sqlite3` to `12.11.1`, which publishes prebuilt binaries
382
+ for Node 22, 24, 25 and 26 — so no Node version akm supports compiles the
383
+ driver at all. This affected real installs, not just CI: an npm user on Node
384
+ 24 LTS was getting the same crash-prone from-source build.
385
+
386
+ The Node-fallback CI job now installs the exact spec `package.json` declares
387
+ instead of carrying a range of its own, and both that job and the smoke
388
+ script fail loudly on a native crash banner — previously an abort was
389
+ reported only as missing output, and the one step that tolerates a non-zero
390
+ exit would not have failed at all.
391
+
392
+ - **A website source interrupted mid-refresh no longer loses the snapshot it
393
+ already had.** A refresh deleted the whole mirror and then rebuilt it page by
394
+ page, so a process killed inside that loop left an empty or partial directory
395
+ with the old content already gone — and the freshness marker still looked
396
+ recent, so the next `sync()` served the wreckage instead of rebuilding. The
397
+ new snapshot is built in a dot-prefixed sibling directory and swapped in with
398
+ renames: an interrupted refresh leaves the PREVIOUS complete snapshot
399
+ untouched. Abandoned staging directories are dot-prefixed so the indexer's
400
+ walk skips them, and are swept by the next refresh once an hour old.
401
+
402
+ - **A resumed workflow run no longer re-dispatches work that already ran.** The
403
+ single-driver guard was checked at the run level, so a run whose lease had
404
+ been stolen left its still-owned unit row `running` and discarded the real
405
+ outcome — the resume then re-dispatched a unit that had already executed its
406
+ side effects and already spent its tokens. The guard now lives on the row, so
407
+ a stale driver's finish matches nothing and a live outcome is never dropped.
408
+
409
+ - **Lowering `retry.max` no longer re-runs finished work.** The completed-attempt
410
+ scan matched only attempts the *current* retry policy could have produced, so
411
+ reducing `retry.max` between invocations hid a journaled `~rN` row and the
412
+ unit was dispatched again. It now matches any journaled attempt of the unit.
413
+
414
+ - **A scheduled `command` task no longer writes your secrets into its log.**
415
+ Task logs were scrubbed for credential *shapes* — `Bearer …`, `sk-…`, webhook
416
+ URLs — but only prompt- and workflow-target runs also scrubbed exact secret
417
+ *values*. A command that echoed a configured secret shaped like nothing in
418
+ particular persisted it verbatim into both the run `.log` and `logs.db`, for
419
+ the whole retention window. Exact-value redaction now runs in the one sink all
420
+ three target kinds share, so every task kind is covered.
421
+
422
+ akm treats a value as secret when your config declares it
423
+ (`engines.<name>.apiKey`, `embedding.apiKey`, and the
424
+ `AKM_ENGINE_<NAME>_API_KEY` / `AKM_LLM_API_KEY` / `AKM_EMBED_API_KEY`
425
+ recipes), and infers others from the variable name (`*_TOKEN`, `*_SECRET`,
426
+ `*_API_KEY`, `*_PASSWORD`, …) when the value is at least 8 characters. The
427
+ floor applies only to the *guesses*: a declared secret is redacted at any
428
+ length. Redaction replaces substrings, so an over-eager rule does real damage
429
+ — treating every non-allowlisted variable in the inherited environment as a
430
+ secret classified 127 of 132 variables as credentials, 25 of them one
431
+ character long, and turned `3 tests passed, 0 failed` into `[REDACTED] tests
432
+ passed, [REDACTED] failed`.
433
+
434
+ For a secret exported under a name none of those rules recognise, any task may
435
+ name it:
436
+
437
+ ```yaml
438
+ command: ./deploy.sh
439
+ redact: [ACME_DEPLOY_TOKEN] # NAMES, never values — max 32
440
+ ```
441
+
442
+ Names only, and a name that is unset at run time contributes nothing. A
443
+ literal secret in a task file would leak far more widely than the redaction
444
+ closes: task files are indexed into the search database, can be sent to an
445
+ embedding provider, are printed verbatim by `akm show`, and ship inside
446
+ bundles over git and npm — the same rule exec units' `pass_env:` follows.
447
+
448
+ - **Redacting a log can no longer explode it.** Exact-value redaction took a
449
+ fast path that rewrote the text once per secret, over an accumulator it had
450
+ already rewritten — so a secret containing any of the letters in `[REDACTED]`
451
+ matched the tokens it had just inserted, and the output grew geometrically.
452
+ Fifty characters against six single-letter values produced 32,450 characters,
453
+ a 649x blowup reachable from ordinary command output. Matches are now found
454
+ against the original text and the result emitted once. Overlapping matches
455
+ merge into a single `[REDACTED]`, and the two redaction paths no longer
456
+ disagree about output shape depending on whether the text happened to contain
457
+ a `%`.
458
+
459
+ - **Redacting a structured value no longer drops fields.** When two distinct
460
+ object keys redacted to the same string, the rebuilt object silently kept only
461
+ the last — `{a, b, ab}` came back with two entries, one of them simply gone
462
+ rather than redacted. Colliding keys are now suffixed, so the value survives
463
+ with its key still hidden. This affected persisted improve results and
464
+ journaled workflow outcomes.
465
+
466
+ - **`akm improve` auto-sync now commits exactly the files the run wrote.**
467
+ Every akm write path records the file it mutated into a run-scoped
468
+ write-provenance journal, and the end-of-run (and crash-path) commit stages
469
+ precisely those paths. A managed-directory file someone else edits while a
470
+ long run is in flight is left dirty for its author instead of being swept into
471
+ akm's commit, and a file that was already dirty when the run started and was
472
+ then rewritten by the run is now committed instead of being silently skipped.
473
+ Deletions are journaled like writes, so a path written and then reverted or
474
+ purged stages its final on-disk state — or produces no commit at all. The run
475
+ reports its journal as `writtenPaths` on the improve result, and the
476
+ `stash_synced` event gains `attributed` / `unattributed` counts. `akm sync` /
477
+ `akm push`, which supply no explicit path list, keep the managed-pathspec
478
+ fallback unchanged. (#652)
479
+
480
+ - **`akm lint` no longer reports a clean scan for a task file that cannot run.**
481
+ A `tasks/*.yml` whose YAML does not parse (bad indentation, an unterminated
482
+ quote, tab characters) produced `flagged: 0`: every task reader collapsed a
483
+ parse failure onto an empty mapping, and every task rule short-circuits on
484
+ one — so a CI gate on `--fail-on-flagged` passed a task that would die at
485
+ schedule time. The parse failure is now its own `invalid-task-yaml` finding.
486
+ A `tasks/*.yaml` file — a spelling akm never indexes and never schedules —
487
+ used to be skipped by the directory walk entirely; it is now collected and
488
+ flagged for the extension, with the rename in the message. Fixed on all three
489
+ task-lint surfaces (the CLI sweep, the `akm` adapter's `validate`, and the
490
+ `akm-task` format adapter) from one shared parse, so they cannot disagree.
491
+
492
+ - **`akm lint --fix` refuses a bundle configured `writable: false`.** Every
493
+ other mutating command checks the flag before touching disk; `--fix` wrote
494
+ directly and never consulted it, so it rewrote frontmatter in a bundle
495
+ explicitly marked read-only. It is now a usage error raised before any file
496
+ is modified.
497
+
498
+ - **A `--fix` write failure no longer aborts the run and hides the fixes that
499
+ already landed.** One unwritable file (read-only file, full disk) threw
500
+ straight out of `akm lint`, so the caller got an exception instead of a
501
+ result — with no way to tell which earlier files in the same sweep had
502
+ already been rewritten. A failed fix is now reported in-band on its own file
503
+ as `fixed: "failed"`, and the sweep continues through the rest of the bundle.
504
+
505
+ - **`akm lint --type` says so when it does nothing.** For a non-akm bundle the
506
+ adapter validates the whole bundle regardless of `--type`, so scoping a run
507
+ silently had no effect. It now warns, naming the flag and the adapter.
508
+ Findings are unchanged (full-bundle validation was already a superset), and
509
+ it is deliberately a warning, not an error, so scripts passing one `--type`
510
+ across mixed-adapter bundle sets keep working.
511
+
512
+ - **`missing-skill-md` fires again for an `agent-skills` package with no
513
+ manifest.** The check iterated pending CHANGES, and a change is always a
514
+ file — so a package directory holding resources but no `SKILL.md`
515
+ contributed nothing it could see, and a skills pack with a broken package
516
+ linted clean. It is now a real directory pass over the bundle root. Related:
517
+ under opencode's supported singular `skill/` alias the same package went
518
+ unflagged while an identical one under `skills/` was caught; both spellings
519
+ are now checked.
520
+
521
+ - **An index akm cannot read no longer reports as an index that does not
522
+ exist.** `fs.existsSync()` answers `false` for a permission error exactly as
523
+ it does for a missing file, and the read path used it as its "is there an
524
+ index?" gate — so `akm search` and `akm curate` returned no hits at **exit 0**
525
+ with the tip *"No search index available. Run 'akm index' to build one."* for
526
+ a populated index sitting right there on disk, and `akm info` reported
527
+ `entryCount: 0, vecAvailable: false` for the same index. Nothing said
528
+ "permission". A consuming agent had no way to tell that from a genuine empty
529
+ result, so it relayed the false answer to its user with an explanation it had
530
+ invented.
531
+
532
+ Absent and inaccessible are now distinct everywhere it matters:
533
+
534
+ - `search` / `curate` / the index openers raise a `ConfigError`
535
+ (`DATA_DIR_UNREADABLE`, exit 78) naming the path, the errno, the mode and
536
+ owner, and the uid actually running — instead of an empty success.
537
+ - `akm info` reports an `indexStats.unreadable` diagnostic rather than zeros
538
+ that look healthy. The field is absent on every healthy run.
539
+ - `akm health` now *diagnoses* an unreadable `state.db` as a failing
540
+ `state-db-readable` check instead of dying on the open before it could
541
+ report anything — it is the command you reach for when this happens.
542
+ - `probeLock` returns a distinct `inaccessible` state instead of classifying a
543
+ permission error as a stale lock. "I cannot read this lock" and "the holder
544
+ is dead" are opposite facts, and `akm improve` now stops rather than
545
+ reclaiming a lease that may be genuinely held.
546
+
547
+ The same conflation existed on the write paths, where the consequence was
548
+ worse than a wrong answer:
549
+
550
+ - **An unreadable `akm.lock` could destroy every bundle record in it.** The
551
+ lockfile read that exists specifically so a write path never sees `[]`
552
+ returned `[]` for *any* read failure, permission errors included — and
553
+ every lockfile write is read-modify-write, so the next atomic write
554
+ replaced the operator's whole lock record with the single entry being
555
+ added. Verified by probe: the symlink was replaced by a regular file
556
+ holding one entry. Lockfile writes now refuse to run against a lock they
557
+ cannot read.
558
+ - **The migration recovery gate failed open.** "I cannot tell whether a
559
+ recovery is pending" cleared the gate exactly as "no recovery is pending"
560
+ did, so akm would open the canonical databases on top of a half-applied
561
+ migration. It now fails closed.
562
+ - **`akm index --clean` deleted rows for files it merely could not look at**,
563
+ and reported the deletions as a clean success. Unreadable entries are now
564
+ kept and named.
565
+ - `indexWrittenAssets` returned `true` — "the index is as you expect" — for
566
+ an index it could not open, on the strength of which `acceptProposal`
567
+ advanced its journal to `index-finalized`.
568
+ - `akm improve` eligibility, `akm feedback`, `akm bundle list` and the graph
569
+ loaders each turned a permission fault into an empty result, a zero count,
570
+ or the advice to "Run `akm index` first".
571
+
572
+ - **akm no longer manages permissions on your data directory, its databases, or
573
+ your task logs — and no longer reports on them either.** Those take your
574
+ process umask; their mode is yours to set, and `chmod`/`umask` are your
575
+ levers.
576
+
577
+ This is scoped, not blanket: akm still creates a handful of files at
578
+ restrictive modes *at creation time*, as it always has — `env` and `secret`
579
+ assets and config backups at `0600`, their directories at `0700`, and the
580
+ scheduler invocation files it writes for cron/launchd/schtasks. Those are
581
+ files akm authors itself and whose contents are credentials; setting their
582
+ mode when creating them is not the same as re-permissioning a directory you
583
+ already owned.
584
+
585
+ Two 0.9.1 pre-release changes are gone. The first chmodded akm's databases
586
+ and task logs to `0600`/`0700` on every open — reverted because
587
+ re-permissioning a directory akm did not create silently broke installs that
588
+ share `$XDG_DATA_HOME` between two uids (agent sandboxes, containers, service
589
+ accounts). If a pre-release tightened your data directory, `chmod` it back.
590
+ The second was an `akm health` advisory (`secret-file-perms`) that reported
591
+ group/other-readable `env`, `secrets` and `config-backups` paths — removed
592
+ too: it is meaningless on Windows, and nagging about modes akm does not set
593
+ is not health reporting. `akm health` no longer emits this check, and no
594
+ longer exits `4` on account of it.
595
+
596
+ - **`timeout: none` on an exec unit is genuinely unbounded again.** The
597
+ stream-drain safety net — a one-hour bound on a pipe still being read after
598
+ the child is gone — was armed when capture STARTED, so a command that ran
599
+ past an hour had its output reader cancelled mid-run and was then failed for
600
+ an incomplete capture even though it exited 0. It is now armed from the
601
+ child's exit, which is the only window it was ever meant to bound.
602
+
603
+ - **A bounded exec unit no longer waits out its whole `timeout` after the
604
+ command has already exited.** The drain deadline for a unit WITH a wall budget
605
+ ran from the moment capture started — budget plus a 2 s grace — so a command
606
+ that exited in milliseconds while a background descendant held a pipe open
607
+ kept the unit, and with it a fan-out slot, occupied for the entire declared
608
+ timeout before reporting. It now runs from the moment nothing living owns the
609
+ pipe: the child's exit, or (for a child that outlived its own kill ladder) the
610
+ budget's expiry, plus the same 2 s grace. A command that really does spend its
611
+ whole budget sees the identical ceiling it saw before; only the case that used
612
+ to stall stopped stalling.
613
+
614
+ - **A stderr drain that never finished no longer fails an exec unit whose
615
+ command succeeded.** `exec_capture_incomplete` was raised when EITHER pipe
616
+ failed to drain, so a command that exited 0 with its stdout captured whole was
617
+ failed — and a valid artifact thrown away — because a background descendant
618
+ was still holding STDERR open. stderr is a diagnostic channel that never
619
+ contributes to the artifact, so only an incomplete STDOUT capture fails the
620
+ unit now; an incomplete stderr drain is reported on the warn stream instead,
621
+ naming the unit and warning that any stderr shown for it may be missing its
622
+ tail.
623
+
624
+ - **A step artifact larger than 1 MiB no longer breaks the next step of the run
625
+ that produced it.** Step evidence is clipped to bound one SQLite row, and the
626
+ engine rebuilt each downstream `steps.<id>.output` scope by re-reading those
627
+ rows — so a large artifact (an exec unit's stdout retains up to 8 MiB) reached
628
+ the very next step as a truncation marker: a path reference failed with a
629
+ missing-property error that never mentioned truncation, and a whole-value
630
+ reference silently handed the marker to the unit as its input. The run now
631
+ carries its own complete values forward; the row stays clipped for resume,
632
+ where a reference into a clipped artifact fails by name.
633
+
634
+ - **A workflow run that completed is no longer reported as timed out.** The
635
+ deadline is observed between steps, so one landing during a run's final
636
+ bookkeeping set the timed-out flag on a run that then finished. On a scheduled
637
+ workflow task that recorded the attempt as failed, with a hint to resume a run
638
+ that had nothing left to resume; under `akm workflow run --timeout` it
639
+ rendered a `timedOut` marker on a `completed` run and exited nonzero. Both
640
+ surfaces now drop the marker once the run reached `completed` — a deadline
641
+ that lands with nothing left to abort has nothing to report.
642
+
643
+ - **A rejected gate on an `exec` step no longer re-runs the command.** A gate
644
+ loop earns its re-dispatch by handing the judge's feedback to a unit that can
645
+ answer it. An exec unit cannot: its argv is frozen and never interpolated, and
646
+ the exec context environment carries no feedback variable — so the loop could
647
+ only re-run the byte-identical command, performing a deploy, a publish, or a
648
+ migration a second time for a verdict that could not change. The gate still
649
+ EVALUATES on an exec step and can still fail it: a rejection is final on the
650
+ first evaluation, carrying the judge's missing criteria and feedback exactly
651
+ as in the one-shot case. What an author sees is
652
+ that `gate.max_loops` is capped at 1 on a step whose unit is `exec:` — not an
653
+ authoring error, and no change at all to an engine step, where a declared
654
+ `max_loops` is still honored in full. This is the same reasoning that already
655
+ makes an exec unit's `output:` schema miss fail without a corrective
656
+ re-dispatch.
657
+
658
+ - **The stale-worktree sweep no longer collects a worktree that is still in
659
+ use.** The opportunistic age-based GC of leftover `isolation: worktree` trees
660
+ judged staleness from the worktree root's mtime, which a unit writing only
661
+ inside subdirectories never touches — so another akm process minting a
662
+ worktree could delete the tree a long-running unit was working in. Every live
663
+ worktree now carries a liveness marker (pid, host, resolved path) in git's own
664
+ administrative directory for it, and the sweep skips a candidate whose holder
665
+ is still running here. A marker from a dead pid, from another host, or for a
666
+ different path is not liveness: crashed runs and retained dirty trees stay
667
+ collectible, which is what the sweep exists for.
668
+
669
+ - On Windows, an agent CLI, gate judge, or prompt task was spawned into an
670
+ environment the loader cannot start from: the shared passthrough allowlist
671
+ named no `SystemRoot`/`SystemDrive`/`WINDIR`, and without `PATHEXT` a
672
+ `bin: "bun"` profile was unresolvable — while an exec unit on the same host
673
+ worked, because its own allowlist names them. Those variables are now added
674
+ when any allowlisted child environment is built.
675
+
676
+ - Scheduler PATH repair skipped itself in the environments it exists for. It
677
+ decided a PATH was "interactive" by testing whether any entry began with the
678
+ user's home directory as a *string*, so a home of `/` — system crontab,
679
+ launchd, service accounts — matched every absolute entry, and a sibling home
680
+ (`/home/alice/bin` against `/home/al`) matched too.
681
+
682
+ - A directory whose name merely begins with two dots (`..data`) was treated as
683
+ a path escape. For a workflow exec `cwd` that meant the parser and the frozen
684
+ plan accepted a spelling the executor then failed as tampering, with a reason
685
+ no retry can clear.
686
+
687
+ - `appendEvent` resolved the state.db path outside its own error handling, and
688
+ did so even when the caller supplied an open connection — so a caller holding
689
+ a perfectly good handle could take a configuration error from a function whose
690
+ contract is that it never propagates one.
691
+
692
+ - Workflow freeze attributed per-step `engine`/`model`/`timeout`/`llm` overrides
693
+ by matching the compiled draft step list against the source document
694
+ **positionally**. That was correct only because compilation happens to be 1:1
695
+ and order-preserving; a compile pass that filtered or reordered steps would
696
+ have silently applied one step's overrides to another. Attribution is now
697
+ keyed by `stepId`.
698
+
699
+ ### Security
700
+
701
+ - **A gate judge's response is now scrubbed before it is journaled.** The judge
702
+ verdict is written into the gate row's `result_json`, and a judge failure's
703
+ message becomes the blocked step's notes — but the judge dispatch bypassed
704
+ the redaction contract every unit dispatch goes through, so a judge that
705
+ echoed a credential out of the promoted artifact persisted it unredacted into
706
+ the workflow journal. Both judge paths (agent and llm) now wrap their
707
+ dispatch in the same scrub, with the sensitive-value set collected per
708
+ dispatch rather than at build time, so a credential rotated between the two
709
+ reads is still caught. The dispatch also carries the real run/step/gate ids
710
+ instead of a synthetic `"gate"` placeholder, so a gate row and its telemetry
711
+ describe the same thing.
712
+
713
+ - **Command-target task logs are scrubbed of exact secret values**, closing the
714
+ last redaction lane — see the `### Fixed` entry above for the full account.
715
+
716
+ ## [0.9.0] - 2026-08-06
8
717
 
9
718
  0.9.0 is the format-neutral **bundle / adapter** refactor: it replaces the flat
10
719
  asset-type registry with per-format adapters, adopts one canonical ref grammar,
11
- and consolidates the durable databases and config. This section supersedes the
12
- earlier `0.9.0-rc.1` / `0.9.0-beta.*` development entries below.
720
+ and consolidates the durable databases and config. This section consolidates and
721
+ supersedes the `0.9.0-rc.*` / `0.9.0-beta.*` development entries below.
13
722
 
14
723
  ### Breaking changes & migration
15
724
 
@@ -28,17 +737,18 @@ earlier `0.9.0-rc.1` / `0.9.0-beta.*` development entries below.
28
737
  fully-qualified `bundle//conceptId`; the short bundle-omitted form is accepted
29
738
  input only (resolved against `defaultBundle`, then installation-priority
30
739
  order). The pre-0.9.0 `[origin//]type:name` grammar is removed — there is no
31
- compatibility parser; the frozen migrator in `src/migrate/legacy/` is the only
32
- place it survives.
33
- - **Explicit, journaled, crash-resumable cutover (`akm migrate apply`).** The
740
+ compatibility parser; the frozen migrator in `scripts/akm-migrate/migrate/`
741
+ is the only place it survives.
742
+ - **Explicit, crash-resumable cutover (`akm migrate apply`).** The
34
743
  migrator re-keys all durable state to the new spelling, folds the former
35
744
  `workflow.db` into `state.db` (four databases down to three: `state.db` /
36
745
  `index.db` / a separate `logs.db`), and migrates config from the flat
37
746
  `stashDir` / `sources` / `installed` / `wikiName` keys to `bundles` /
38
- `defaultBundle`. A verified, installation-scoped **backup manifest v3**
39
- (covering the pre-rescue `index.db`) is taken before any ledger is sealed;
40
- expected orphans are quarantined, integrity failures fail closed, and the
41
- whole cutover resumes idempotently after a crash. Normal commands refuse an
747
+ `defaultBundle`. A semantically verified, installation-scoped **backup manifest v4**
748
+ (covering the pre-rescue `index.db`) is taken before mutation. One phase-free
749
+ incomplete sentinel retains that backup and target; expected orphans are
750
+ quarantined, integrity failures fail closed, and the whole cutover reruns
751
+ idempotently after a crash. Normal commands refuse an
42
752
  un-migrated or divergent durable schema rather than migrating as a side effect.
43
753
  The retired `stashDir` / `sources` / `installed` keys are **hard-rejected** by
44
754
  the 0.9.0 config schema whenever present (the error names `akm migrate apply`);
@@ -52,7 +762,7 @@ earlier `0.9.0-rc.1` / `0.9.0-beta.*` development entries below.
52
762
  index (and renamed by the content migration when they hold a real concept).
53
763
  - **`vault` asset type removed.** Use `env` (a whole `.env` group; key names
54
764
  surfaced, values never) and `secret` (a single sensitive value), addressed as
55
- `env/<name>` and `secrets/<name>`. `akm-migrate-storage` still performs the
765
+ `env/<name>` and `secrets/<name>`. `akm-migrate storage` performs the
56
766
  non-destructive `vaults/` → `env/` copy for older stashes.
57
767
  - **0.8-era CLI aliases removed.** The flat proposal verbs (`akm proposals`,
58
768
  `akm accept`, `akm reject`, `akm diff`, `akm revert`, `akm show proposal`),
@@ -64,8 +774,95 @@ earlier `0.9.0-rc.1` / `0.9.0-beta.*` development entries below.
64
774
  See `docs/migration/v0.8-to-v0.9.md` and
65
775
  `docs/migration/release-notes/0.9.0.md` for the full upgrade procedure.
66
776
 
777
+ ### Removed
778
+
779
+ - **The experimental `akm workflow brief` / `akm workflow report`
780
+ external-driver protocol is removed**, along with the
781
+ `experimental.workflowEngine` config key that gated it, its
782
+ `WORKFLOW_ENGINE_NOT_ENABLED` error code, and the `workflowEngine` block in
783
+ `akm task doctor`. `akm workflow run` is now the single execution surface.
784
+
785
+ The protocol let a calling agent session execute a run's units itself
786
+ instead of akm dispatching them. Its stated justification was harness
787
+ neutrality, which measurement did not support: native dispatch already
788
+ covers **ten** harnesses (opencode, claude, opencode-sdk, codex, copilot,
789
+ pi, gemini, aider, amazonq, openhands) in 2,214 LOC total, while the
790
+ protocol cost 2,690 LOC on its own — more than supporting every harness
791
+ natively — and an eleventh harness is ~220 lines, not a protocol. Removing
792
+ it also drops the second consumer of `workflow_run_units` and the
793
+ cross-surface parity obligation on `step-work.ts`, both of which
794
+ constrained every future engine change. The analysis is recorded in
795
+ `docs/architecture/specs/driver-protocol-keep-or-cut.md`.
796
+
797
+ Legacy configs setting `experimental.workflowEngine` remain valid — the
798
+ config schema is `.passthrough()`, so the key is accepted and ignored.
799
+
67
800
  ### Added
68
801
 
802
+ - **`akm workflow run` and prompt tasks fall back to `opencode-sdk` instead of
803
+ refusing when no engine is configured.** A clean install that never ran
804
+ `akm setup` — a bare container, a CI image, an agent-operated session — used
805
+ to fail closed with `INVALID_CONFIG_FILE` (exit 78). When the `opencode`
806
+ binary is on PATH, akm now synthesizes a **config-free** `opencode-sdk`
807
+ engine: it carries no model, endpoint, or credential, so provider, model,
808
+ and auth all resolve from opencode's own configuration and akm never mirrors
809
+ or validates it. With `opencode` absent the failure is unchanged, and its
810
+ remedy now names both routes. An operator-configured `opencode-sdk` engine
811
+ always wins over the synthesized one.
812
+
813
+ The requirement is the **binary**, not the npm package: `@opencode-ai/sdk`
814
+ is an HTTP client that declares no dependencies and whose own
815
+ `createOpencodeServer` spawns `opencode serve`, so a host with the package
816
+ and no binary has no server to reach. Install it with `npm i -g opencode-ai`
817
+ or opencode's own installer.
818
+
819
+ The fallback is **announced, never silent** on every surface that applies
820
+ it: a workflow run surfaces it once at run creation in the result's
821
+ `warnings`, a prompt task writes it to the task run log, `akm agent`
822
+ carries it in its result `warnings` and on stderr, and `propose` and
823
+ `improve` reflect warn on stderr. The frozen plan records the engine
824
+ actually used, so a resume never re-announces a decision it did not make.
825
+
826
+ - **RSS, Bluesky, and X sources.** `akm bundle add` now recognizes three new
827
+ kinds of URL and snapshots them as knowledge assets instead of crawling
828
+ them as ordinary web pages:
829
+
830
+ ```sh
831
+ akm bundle add https://blog.example/feed # RSS 2.0 / Atom / RDF
832
+ akm bundle add https://bsky.app/profile/<handle> # public, no auth
833
+ akm bundle add https://x.com/<user> # see token note below
834
+ ```
835
+
836
+ Any of these falling through — a `/feed` URL that actually serves HTML, an
837
+ unresolvable Bluesky handle — degrades to the normal website crawl rather
838
+ than failing the command.
839
+
840
+ X needs credentials: set `X_BEARER_TOKEN` for the X API v2, or
841
+ `X_RSS_TEMPLATE` to an RSS bridge URL containing `{username}`. To keep the
842
+ token out of your shell history, store it as an akm secret and inject it
843
+ per-invocation:
844
+
845
+ ```sh
846
+ akm secret set x-bearer-token
847
+ akm secret run secrets/x-bearer-token X_BEARER_TOKEN -- akm bundle add https://x.com/<user>
848
+ ```
849
+
850
+ With neither set, the X fetcher emits one warning and falls through.
851
+
852
+ - **`akm-migrate` derives the 0.9 config from your 0.8 keys instead of
853
+ demanding one.** Upgrading used to require hand-authoring a complete 0.9
854
+ config before `migrate apply` would act. The first `apply` with no
855
+ `--config` now writes a validated starter config — `bundles`/`defaultBundle`
856
+ derived from the 0.8 `stashDir` / `sources` / `installed` keys — to a
857
+ predictable path under the backup root and stops, with config and durable
858
+ state byte-for-byte untouched; a second `apply` picks it up and performs the
859
+ cutover. Engine settings are never guessed: `profiles.*` and
860
+ `defaults.llm|agent|improve` are stripped and reported individually in
861
+ `droppedKeys` by their exact 0.8 dotted path. `status` and `apply --dry-run`
862
+ preview the same plan, and an explicit `--config` always wins and is never
863
+ overwritten. `akm migrate --format` now renders text/md/html/yaml through
864
+ the normal output pipeline instead of warning and printing JSON anyway.
865
+
69
866
  - **Local downstream value attribution for memory inference and graph
70
867
  extraction.** Private search-hit sidecars now write versioned, source-qualified
71
868
  per-entry `usage_events.metadata` for emitted MI direct/surface value and the
@@ -79,33 +876,25 @@ See `docs/migration/v0.8-to-v0.9.md` and
79
876
  migration, dashboard, or health schema was added.
80
877
  - **Explicit, crash-resumable 0.9 migration coordination.** `akm migrate
81
878
  status` classifies config, `state.db`, and `workflow.db` independently;
82
- `akm migrate apply [--config <prepared>]` creates a verified,
83
- installation-scoped backup before sealing ledgers or applying pending
84
- migrations. Apply and restore use authenticated phase journals, exact
85
- artifact fingerprints, bounded streaming I/O, SQLite integrity checks,
86
- active-writer barriers, WAL/SHM-safe publication, and idempotent recovery.
87
- Routine reads and current database opens no longer depend on a historical
88
- cutover bundle. See `docs/migration/release-notes/0.9.0.md`.
879
+ `akm migrate apply [--config <prepared>]` creates a semantically verified,
880
+ installation-scoped config/database backup before applying pending migrations.
881
+ Apply and restore use one phase-free incomplete sentinel, bounded control-file
882
+ reads, SQLite integrity and ordered-ledger checks, active-writer barriers,
883
+ WAL/SHM-safe publication, and idempotent replay. Legacy checksum columns are
884
+ inert. Routine reads and current database opens no longer depend on a
885
+ historical cutover bundle. See `docs/migration/release-notes/0.9.0.md`.
89
886
  - **Workflow orchestration engine (experimental).** akm can now execute
90
- multi-step workflows as deterministic **YAML programs**, driven either by a
91
- native engine or by any agent session. This is a new, self-contained
92
- surface; classic linear **markdown workflows and the stable workflow CLI
93
- contract (`start`/`next`/`complete`/`status`/`list`) are unchanged**. What
94
- ships:
95
- - **Authoring.** Orchestrated workflows are YAML programs
96
- (`workflows/*.yaml`, `version: 2`) validated against a published JSON
97
- Schema (`schemas/akm-workflow.json`) by `akm workflow validate`; scaffold
98
- one with `akm workflow template --yaml` or `akm workflow create
99
- <name>.yaml`. A closed `${{ }}` expression language (exactly
100
- `params.<name>`, `steps.<id>.output.<path>`, `item`, `item_index`, parsed
101
- once into an AST) wires steps together. `validate` also surfaces non-fatal
102
- **warnings** (a step with no typed `output:` schema; a `${{ params.<name> }}`
103
- reference to a param the declared `params:` block omits) that never change
104
- the frozen plan or its hash. Creating a workflow whose canonical name
105
- collides with an existing asset of a **different** extension (`foo.yaml`
106
- while `foo.md` exists, or vice-versa) is refused, since the two would
107
- silently shadow each other.
108
- - **Compilation + frozen plans.** `akm workflow start` compiles the program
887
+ multi-step workflows through a native engine or any agent session. Workflow
888
+ assets use the unified markdown format described above; the stable manual
889
+ CLI contract (`start`/`next`/`complete`/`status`/`list`) and the experimental
890
+ engine consume the same asset. What ships:
891
+ - **Authoring.** A workflow is a markdown asset whose frontmatter graph is
892
+ validated against `schemas/akm-workflow.json` and whose `## <step-id>` body
893
+ sections carry instructions and gate rubrics. `akm workflow create`
894
+ scaffolds that format; `akm lint --type workflows` parses and compiles it.
895
+ Bare references (`params.<name>` and `steps.<id>.output.<path>`) wire
896
+ `map.over`, `route.input`, and `inputs`; prose is never interpolated.
897
+ - **Compilation + frozen plans.** `akm workflow start` compiles the workflow
109
898
  into a backend-agnostic Workflow Plan Graph IR (`src/workflows/ir/`) and
110
899
  freezes it on the run row (`plan_json` + `plan_hash`); a run executes the
111
900
  plan compiled at start, and edits to the source file require a new run.
@@ -114,8 +903,7 @@ See `docs/migration/v0.8-to-v0.9.md` and
114
903
  reducer), a typed `output` JSON Schema (validated via a `runStructured`
115
904
  retry-with-feedback loop), `env` bindings (resolved through the existing
116
905
  `akm env run` machinery — secret tokens, dangerous-key policy, keys-only
117
- audit events), classify-and-dispatch `route` steps, and `depends_on`
118
- ordering.
906
+ audit events), and classify-and-dispatch `route` steps.
119
907
  - **Determinism + replay.** Journaled unit identity is content-derived
120
908
  (`<step>:<sha256(item)[:12]>`, `:solo` for a single unit), so cached
121
909
  results survive item-list reordering; a completed unit whose recorded
@@ -137,9 +925,9 @@ See `docs/migration/v0.8-to-v0.9.md` and
137
925
  criteria-bearing gate judges that **artifact** (canonical JSON, clipped)
138
926
  rather than machine prose, and each engine-driven evaluation is journaled
139
927
  as a gate unit row. `gate.max_loops` bounds an evaluator-optimizer retry
140
- loop (feedback threaded into re-dispatched unit prompts); `gate.required`
141
- (or the run-wide `--require-gates`) makes a gate with no available judge
142
- **block** for a human instead of failing open.
928
+ loop (feedback threaded into re-dispatched unit prompts). Gates are
929
+ optional validation: omitted/empty rubrics and unavailable or malformed
930
+ judges skip validation.
143
931
  - **Failure policy.** Per-unit `on_error: fail | continue` (fail-fast
144
932
  default) plus bounded `retry: { max, on: [<failure_reason>…] }` keyed on
145
933
  the persisted failure taxonomy.
@@ -370,6 +1158,62 @@ See `docs/migration/v0.8-to-v0.9.md` and
370
1158
 
371
1159
  ### Changed
372
1160
 
1161
+ - **X source tokens now resolve from the secret store during bundle update.**
1162
+ The `secrets/x-bearer-token` akm secret is honored on the provider
1163
+ `sync()` / bundle-update path, not just when adding or importing a URL —
1164
+ closing a gap where a refresh saw only the `X_BEARER_TOKEN` environment
1165
+ variable. Implemented as a `SecretResolver` capability injected from above
1166
+ the source-provider import cycle; internals are documented in
1167
+ `docs/architecture/reviews/env-secret-access.md`.
1168
+
1169
+ - **`website` crawls now have a hard time limit.** `crawlTimeoutMs` (default
1170
+ 600000 — 10 minutes) bounds the entire crawl, and unlike the previous
1171
+ between-page check it aborts work already in flight: a `Retry-After` sleep
1172
+ could previously park `akm bundle add` for as long as a rate-limiting server
1173
+ asked, well past the advertised cap. Raise it for a large site, or set
1174
+ `"crawlTimeoutMs": 0` to disable the cap. Relatedly, `fetchWithRetry` now
1175
+ honors its caller's `AbortSignal` during retry backoff, so any operation that
1176
+ passes a signal can actually interrupt a long wait.
1177
+
1178
+ - **Website snapshots now extract the page's main content.** Conversion moved
1179
+ from a hand-rolled regex converter to a DOM parse plus Turndown, scoped to
1180
+ the page's content region (`<main>`, `<article>`, `[role=main]`, then common
1181
+ content ids/classes, falling back to `<body>` minus nav/header/footer/aside).
1182
+ Navigation, ads, and boilerplate no longer land in snapshots, and tables,
1183
+ nested lists, and fenced code blocks with language hints now survive
1184
+ conversion. **Existing website snapshots will change on their next refresh**
1185
+ — expect them to get shorter and cleaner. Link discovery still scans the
1186
+ whole page, so crawl coverage is unchanged.
1187
+
1188
+
1189
+ - **`website` sources now respect `robots.txt` by default.** Before crawling
1190
+ an origin, akm fetches and parses that origin's `/robots.txt` and skips
1191
+ paths disallowed for the `akm`/`akm-cli` product tokens (or `*`), honoring
1192
+ `Crawl-delay` (clamped to 10s) between page fetches. This is a deliberate
1193
+ behavior change: **existing website sources may return fewer pages, or
1194
+ fail with an error if the start URL itself is disallowed, after
1195
+ upgrading.** Re-running `akm bundle update` on a website source is what
1196
+ surfaces it. Opt out with `"respectRobots": false` on the website
1197
+ descriptor to restore the exact pre-upgrade behavior (no `/robots.txt`
1198
+ request at all):
1199
+
1200
+ ```json
1201
+ { "bundles": { "docs": { "website": { "url": "https://docs.example.com", "respectRobots": false } } } }
1202
+ ```
1203
+
1204
+ - **`akm lint` now routes through each bundle adapter's own `validate()`.**
1205
+ `validate()` was a required member of the adapter interface that nothing
1206
+ called: `akm lint` branched on adapter id and re-implemented OKF's checks
1207
+ inline (with drifted semantics for `missing-type`), OKF's `missing-ref`
1208
+ never ran at all (a bundle with a dangling link reported nothing), and
1209
+ llm-wiki's `uncited-raw` / `broken-xref` / `broken-source` /
1210
+ `missing-description` checks were unreachable dead code. **Existing OKF and
1211
+ llm-wiki bundles may surface new lint findings after upgrading.** akm-bundle
1212
+ lint output is byte-identical. Proposal promotion also runs the adapter
1213
+ check immediately before the write — advisory-only: it warns and never
1214
+ rejects, because the adapter resolver and the legacy promotion gate still
1215
+ disagree on foreign-typed cross-bundle refs.
1216
+
373
1217
  - **Improve-stage extraction and proactive maintenance now ship opt-in.** The
374
1218
  built-in `default` and `frequent` strategies resolve extract off, while
375
1219
  `default` and `reflect-distill` resolve `proactiveMaintenance` off. The
@@ -440,6 +1284,61 @@ See `docs/migration/v0.8-to-v0.9.md` and
440
1284
 
441
1285
  ### Fixed
442
1286
 
1287
+ - **Fresh 0.8 installs can actually upgrade.** A config that 0.8.x wrote
1288
+ itself carries no `configVersion` key at all (0.8 stamped it only when a
1289
+ 0.7-era migration did substantive work), and the migrator read the absent
1290
+ key as `inconsistent` — an unconditional blocker. `migrate status` reported
1291
+ `blocked` and `migrate apply` refused with exit 78 for every fresh 0.8
1292
+ install; reproduced end to end against the published `akm-cli@0.8.14`. An
1293
+ absent `configVersion` on a positively pre-cutover-shaped config now
1294
+ classifies as `old`; a present-but-unparseable version still fails closed.
1295
+ Relatedly, `migrate` reports `not-applicable` (exit 0) instead of `blocked`
1296
+ when there is no akm installation to migrate at all, and `apply` warns when
1297
+ an active workflow run targets an asset that fails 0.9 structural
1298
+ validation, naming the asset and `akm workflow abandon <run-id>`.
1299
+
1300
+ - **`akm lint` fails closed on mistyped invocations.** A nonexistent `--dir`,
1301
+ or an unknown `--type` on an akm bundle (the classic singular/plural typo,
1302
+ `--type workflow`), used to scan nothing and report a clean
1303
+ `ok:true, flagged:0` — silently passing scripted `--fail-on-flagged`
1304
+ gates. Both are now usage errors (exit 2), the `--type` error listing the
1305
+ valid values.
1306
+ - **Registry search survives a briefly unreachable registry.** Once the
1307
+ cached registry index aged past its refresh TTL, a failed fetch
1308
+ hard-failed the command even though a serviceable index sat in the cache.
1309
+ A failed fetch now serves the last cached index — past its TTL — with a
1310
+ warning naming the fetch error.
1311
+ - **`akm upgrade` verifies the package manager actually delivered the new
1312
+ version.** A lagging `@latest` dist-tag (partial publish, registry mirror
1313
+ lag) exits 0 while leaving the old version on PATH; upgrade used to report
1314
+ success anyway — and then run `migrate apply` against the old binary. It
1315
+ now re-reads `akm --version` after the install: a confirmed mismatch
1316
+ reports `upgraded: false` with an exact-version pin command, and a
1317
+ verified match is named in the success message.
1318
+ - **`akm info` no longer overstates semantic-search health.** After a run
1319
+ with partial sqlite-vec fast-path insert failures, the verification
1320
+ reported `ready-vec` ("sqlite-vec active") even though search had already
1321
+ routed to the slower JS-cosine fallback. The status now reflects the path
1322
+ search actually takes, with an `akm index --full` hint when the fast path
1323
+ is degraded. Relatedly, `embedding.dimension` is now bounded to the
1324
+ vec table's own 1–4096 limit at config validation, so an out-of-range
1325
+ value fails at `akm config set` with a clear message instead of crashing
1326
+ `akm index` mid-run.
1327
+ - **Standalone `akm remember --enrich` actually enriches.** With no other
1328
+ metadata flag, `--enrich` fell through to the zero-flag raw-write hot path
1329
+ and never attempted the LLM call — an unenriched memory with no warning.
1330
+ `--enrich` now routes to the enrichment dispatch exactly like `--auto`;
1331
+ the fail-soft contract is unchanged (no configured LLM still warns and
1332
+ writes without enrichment).
1333
+ - **Read paths no longer plant a broken `index.db` on a fresh install.**
1334
+ The fire-and-forget usage telemetry behind `search` / `show` / `curate`
1335
+ opened `index.db` with create-on-open: with no index built yet, the open
1336
+ itself left an empty, schema-less `index.db` behind, and every later
1337
+ command then saw an existing-but-broken index ("no such table: entries") —
1338
+ hard-failing proposal acceptance among others. `openExistingDatabase` now
1339
+ refuses to create the file (a missing index throws, naming `akm index` as
1340
+ the remedy) and the telemetry paths skip cleanly instead.
1341
+
443
1342
  - **Improve RC stabilization.** Restored one ownership-safe whole-run lock from
444
1343
  triage through final sync; `--skip-if-locked` is a true no-op; the run deadline
445
1344
  now starts before indexing and reaches index waits, generation, reindexing, and
@@ -465,6 +1364,833 @@ See `docs/migration/v0.8-to-v0.9.md` and
465
1364
  keys (`name`, `updated` were missing); removed the documented-but-nonexistent
466
1365
  `akm workflow step` alias from `docs/features/workflows.md`.
467
1366
 
1367
+ ## [0.9.0-rc.13] - 2026-07-31
1368
+
1369
+ ### Security
1370
+
1371
+ - **`akm update` no longer deletes a previous install directory without
1372
+ confirmation.** When a managed source's resolved content location moves,
1373
+ `update` removed the old directory outright, while `akm remove` had always
1374
+ required `--yes` in non-interactive mode. Only that destructive branch is
1375
+ gated — a normal refresh, where the location does not move, still needs no
1376
+ prompt and no flag, so existing CI invocations are unaffected. Pass
1377
+ `-y`/`--yes` to allow the deletion non-interactively. A cleanup that fails
1378
+ now warns instead of failing silently.
1379
+
1380
+ - **The dangerous-env-key install gate now scans `env/` recursively.** It
1381
+ previously read only the top level, so a stash carrying `LD_PRELOAD` in
1382
+ `env/nested/inner.env` installed cleanly with no warning. Files without a
1383
+ `.env` suffix are still not scanned — no akm code path loads them as
1384
+ environment variables.
1385
+
1386
+ ### Added
1387
+
1388
+ - **The `okf` adapter reads OKF v0.2's trust/provenance and lifecycle
1389
+ frontmatter families.** `generated: {by, at}` (with `generated.at` taking
1390
+ precedence over the legacy `timestamp` field, which remains a valid
1391
+ fallback), `verified` (a list, or v0.2's permitted single-mapping
1392
+ shorthand), `sources` (an object list — `resource` required; `id`/`title`/
1393
+ `author`/`usage_count`/`last_modified` optional), `status`
1394
+ (`draft`/`stable`/`deprecated`), and `stale_after` are now parsed leniently
1395
+ from any OKF concept's frontmatter and surfaced on new, namespaced
1396
+ `IndexDocument` fields (`provenance`, `lifecycleStatus`, `staleAfter`,
1397
+ `okfVersion`) that never overload the pre-existing AKM-native `sources`
1398
+ (wiki citation strings), `generation` (consolidation depth), or `quality`
1399
+ fields. As with every other optional OKF field, a missing or malformed
1400
+ value never rejects the document. The `okf` adapter remains consumer-only.
1401
+
1402
+ - **Accepting a proposal now stamps OKF v0.2 provenance onto the written
1403
+ asset's frontmatter**, for AKM-native writes only (never through the `okf`
1404
+ adapter, which stays consumer-only and unaffected by this). `promoteProposal`
1405
+ projects the proposal system's own `source`/`sourceRun`/`gateDecision`/
1406
+ `review` bookkeeping — already tracked in `state.db` but previously never
1407
+ written to disk. `generated: {by, at}` and `verified: [{by, at}]` are written
1408
+ **bare at the top level**, exactly as OKF v0.2 spells them, so a third-party
1409
+ OKF v0.2 reader pointed at an AKM stash sees conformant trust metadata;
1410
+ `sources` alone is namespaced as `provenance: {sources}`, because a bare
1411
+ `sources:` collides with the pre-existing wiki citation-string convention.
1412
+ `generated.by` records whether the content came from an automated pipeline
1413
+ (`akm/<version>`) or a human-initiated source (`human:<id>`); `verified`
1414
+ records whether the promotion itself was an automated gate decision or a
1415
+ direct human accept, and accumulates rather than overwriting across
1416
+ re-promotions; `evidenceSources`, when present, projects as
1417
+ `provenance.sources`. AKM's own adapter rereads what it wrote, so `akm show`
1418
+ surfaces it. Every AKM-native markdown type is stamped, `workflow` included.
1419
+
1420
+ Two consequences worth knowing: promotion re-serializes the whole frontmatter
1421
+ block, so YAML **comments** in a hand-written proposal's frontmatter are not
1422
+ preserved (values and body bytes are); and for a human-attributed promotion
1423
+ with no configured actor id, `by` falls back to `human:<OS username>`, which
1424
+ puts that username into content you may later commit and share.
1425
+
1426
+ - **Internal: a `capturedAtHead` integrity guard**
1427
+ (`scripts/lint-golden-captured-at-head.ts`, wired into `bun run lint`) now
1428
+ checks every golden fixture's recorded `capturedAtHead` commit SHA — it must
1429
+ exist in the local object database and be reachable from at least one known
1430
+ branch. Post-hoc review of this PR found all four new OKF format-family
1431
+ goldens pointed at a commit that existed locally but was unreachable from
1432
+ any ref (a pre-amend duplicate left behind by an interrupted git operation),
1433
+ which would have 404'd on GitHub and vanished under a local `git gc`; a
1434
+ human fixed that one by hand because nothing caught it. This guard is that
1435
+ catch, going forward. In CI's shallow (`fetch-depth: 1`) checkout, a merely
1436
+ *absent* commit object is inconclusive (indistinguishable from "just not
1437
+ fetched") and only warns; a commit that *exists but is unreachable from any
1438
+ branch* — the actual bug class above — still fails there too, since a
1439
+ shallow clone can tell presence apart from absence just fine.
1440
+
1441
+ - **`akm log list --limit <n>`** returns the most recent N events. The flag was
1442
+ documented but silently ignored, and there was no limiting mechanism at all
1443
+ in the read path — the command returned the entire events table regardless of
1444
+ history size. The default remains unlimited.
1445
+
1446
+ - **`--track-usage` (default on) on `akm search`, `akm curate`, and `akm show`.**
1447
+ Pass `--no-track-usage` for a read-only lookup that does not feed usage
1448
+ telemetry or the utility-score ranking signal. Previously a bare `akm search`
1449
+ silently wrote a `utility_scores` row that influenced future ranking, with no
1450
+ disclosure and no way to opt out.
1451
+
1452
+ - **`akm show` returns the canonical `ref` in every shape.** It was present only
1453
+ under `--shape agent`, so a `--shape summary` consumer had to make a second
1454
+ call at a different shape just to learn which asset it was looking at.
1455
+
1456
+ - **`akm info` gained `stashDir`, `defaultBundle`, and `indexStats.byType`.**
1457
+ Answering "which stash is primary" previously required a separate
1458
+ `akm sources list`.
1459
+
1460
+ - **`instruction` is a stash-resident asset type.** It was already in
1461
+ `KNOWN_TYPES` and had a presentation entry, but had no placement spec — so
1462
+ there was nowhere to put one and the indexer never recognized one. `akm bundle create`
1463
+ now creates an `instructions/` directory, `.md` files under it index as
1464
+ `instruction`, and `--type instruction` is accepted and tab-completable
1465
+ everywhere `--type` is. A compile-time assertion now pins
1466
+ `placementTypes() ⊆ KnownType`, so the half-registered state this fixes
1467
+ cannot recur silently.
1468
+
1469
+ - **Schedule tasks from any configured bundle via `--bundle <bundle>`** (#711).
1470
+ `akm task add`, `run`, `sync`, and `history` accept `--bundle` to
1471
+ operate on a non-default bundle instead of only the primary stash. `add`
1472
+ resolves through the normal writable-target rules; `run --bundle X` resolves
1473
+ the task file and relative asset refs from bundle X. A non-default bundle is
1474
+ recorded in the scheduler entry as `--bundle <bundle>`, so scheduled
1475
+ `akm task run` resolves the right bundle. Scheduler ids stay bare and a
1476
+ collision with another bundle is a hard error rather than a silent clobber.
1477
+
1478
+ - **Orphan-GC pass for unresolvable `asset_salience` / `asset_outcome` state
1479
+ rows** (#733). A new improve maintenance pass (`runOrphanStateGcPass`, run
1480
+ next to the existing orphan-proposal purge) stamps `missing_since` on any
1481
+ state row whose ref no longer resolves against `entries.item_ref`, clears
1482
+ the stamp the moment the ref resolves again, and — only when
1483
+ `improve.stateGc.collect` is set to `true` (**default `false`**) — deletes
1484
+ rows whose stamp is older than a fixed 7-day grace window
1485
+ (`STATE_GC_GRACE_MS`). The pass always runs and always reports counts via
1486
+ the new `asset_state_gc` event (`{pending, collected, byTable}`), emitted
1487
+ only when there is something to report, so live data can prove the report
1488
+ clean before `collect` is ever turned on. Additive migration
1489
+ `021-asset-state-missing-since` adds the `missing_since` column to both
1490
+ tables. Deliberately lean by design (Workstream C): no quarantine archive,
1491
+ no circuit breaker, no health-advisory plumbing, no new tables — "ref not
1492
+ present in `entries.item_ref`" is trusted as the authoritative-deletion
1493
+ predicate because the indexer already preserves a source's last-known-good
1494
+ rows when its scan is incomplete, so a temporarily unreachable source never
1495
+ contributes false candidates. `usage_events` is out of scope (already
1496
+ covered by cascade-on-delete plus its own 90-day retention purge).
1497
+
1498
+ ### Changed
1499
+
1500
+ - **Workflow execution is consolidated on stable `akm workflow run`.** The
1501
+ public `workflow start`, `next`, and `complete` commands are removed with
1502
+ explicit `UNKNOWN_COMMAND` migration hints; `run <ref|run-id>` now owns
1503
+ creation, active-run continuation, native dispatch, completion, and durable
1504
+ replay. It is no longer gated by `experimental.workflowEngine`; only the
1505
+ experimental `brief`/`report` external-driver protocol retains that opt-in.
1506
+ Workflow parameters move from the opaque `--params '<json>'` bag to exact
1507
+ declared flags (`--version 1.2.3`, repeated array flags, JSON object/array
1508
+ values) coerced through the frozen parameter schemas. New invocation controls
1509
+ add bounded failed-step retries (`--max-retries`) and a whole-run timeout
1510
+ (`--timeout N|Nms|Ns|Nm`); failures, gate rejection, timeout, and signals now
1511
+ produce non-zero process statuses while leaving interrupted work resumable.
1512
+
1513
+ Criteria-bearing gates now require `workflow.judgeEngine`, which may name a
1514
+ configured LLM or agent engine and is frozen into the run. Verification is
1515
+ fail-closed: a missing/failing verifier or malformed verdict rejects instead
1516
+ of silently advancing. Scheduled workflow tasks now execute through the same
1517
+ native orchestrator rather than stopping after run creation. Migration:
1518
+ replace `workflow start/next/complete` loops with `workflow run`, replace
1519
+ `--params` with exact declared flags, and configure `workflow.judgeEngine`
1520
+ before running a workflow with a non-empty `### gate` rubric.
1521
+
1522
+ - **The two workflow authoring formats — markdown documents and YAML
1523
+ orchestration programs — are unified into one format**, per
1524
+ `docs/architecture/specs/workflow-format-unification.md`. A workflow is
1525
+ now always a single markdown asset: the standard AKM frontmatter envelope
1526
+ carries the whole orchestration graph (`params`, `steps` with
1527
+ `unit`/`map`/`route`, `inputs`, `output`, `gate`, `defaults`, `budget`),
1528
+ and the body carries each step's instructions under a bare `## <step-id>`
1529
+ heading, joined to the frontmatter by step id. `.yaml`/`.yml` workflow
1530
+ files, the `# Workflow:` / `## Step:` / `Step ID:` markdown headings, and
1531
+ `akm workflow create <name>.yaml` are all gone; `akm workflow create`
1532
+ always writes the one unified template
1533
+ (`src/assets/workflows/workflow-template.md`).
1534
+
1535
+ **Prose is never interpolated.** The YAML program's `${{ … }}` template
1536
+ language, and the markdown format's decorative — and never
1537
+ substituted — `{{ … }}` moustaches, are both removed. Data reaches a
1538
+ dispatched unit as *attached context* instead: the run's params, its
1539
+ item and index for a map unit, and the artifacts its step's new
1540
+ `inputs:` key declares. Instructions refer to that context in plain
1541
+ language ("clone the repository named by the `repo` parameter") rather
1542
+ than splicing a value into the instruction string. Bare reference
1543
+ strings (two roots, `params.<name>` and `steps.<id>.output…`) now appear
1544
+ only in three frontmatter positions: `map.over`, `route.input`, and
1545
+ `inputs:`.
1546
+
1547
+ **Gate rubrics move to the body.** A step's completion criteria are no
1548
+ longer a frontmatter `gate.criteria` list or a `### Completion Criteria`
1549
+ bullet section — they live under a step's `### gate` sub-heading, the
1550
+ format's one reserved marker, as full prose a judge receives byte-exact.
1551
+ Frontmatter `gate:` now carries only optional `max_loops` configuration.
1552
+ Omitted or empty rubric text skips validation; a non-empty rubric requires
1553
+ the frozen `workflow.judgeEngine`, and unavailable or malformed judges reject
1554
+ the gate.
1555
+
1556
+ This is a **pre-1.0 format change**. The ten example workflows under
1557
+ `scripts/akm-eval/example-stash/workflows/` are rewritten to the unified
1558
+ format in this change; existing user-authored workflow assets must be updated
1559
+ manually before execution.
1560
+
1561
+ - **akm is described as a knowledge toolkit, not a package manager** (R-048).
1562
+ The npm one-liner, the README lede, and the `concepts.md` opener all led with
1563
+ "a package manager for AI agent capabilities", which misstates the product to
1564
+ its distribution channel and sets package-manager expectations for verbs
1565
+ (`update` / `upgrade` / `sync`) that don't mean what a package manager's do.
1566
+
1567
+ - **BREAKING: a command group invoked with no subcommand is now always a usage
1568
+ error, exit 2** (owner ruling 12). The eleven `akm <group>` groups did three
1569
+ different things when invoked bare: `graph`, `config`, `env`, `secret`,
1570
+ `task`, `workflow`, and `proposal` ran an implicit default action and exited
1571
+ 0 (bare `akm graph` silently rendered `graph summary`); `registry`, `log`, and
1572
+ `lessons` printed citty's human usage banner to stdout; only `migrate` raised
1573
+ a structured error. All eleven now emit the same
1574
+ `MISSING_REQUIRED_ARGUMENT` envelope on stderr, naming the available
1575
+ subcommands, and exit 2 — matching STABILITY.md's exit-code table (2 =
1576
+ usage) and the exit code already used for unknown commands. Matching exit
1577
+ codes alone was not enough: a script could not parse the failure uniformly
1578
+ while three groups answered on stdout in prose.
1579
+
1580
+ Migration: name the subcommand. `akm graph` → `akm graph summary`,
1581
+ `akm config` → `akm config list`, `akm env` → `akm env list`, `akm secret` →
1582
+ `akm secret list`, `akm task` → `akm task doctor`, `akm workflow` →
1583
+ `akm workflow list --active`, `akm proposal` → `akm proposal list` (which
1584
+ takes the same `--status`/`--queue`/`--ref`/`--type` flags the bare form did).
1585
+
1586
+ - **BREAKING: `akm sync` persists `eventType: "sync"`, not the legacy
1587
+ `"save"`.** The event name now matches the command name. Historical
1588
+ `state.db` rows are left as-is — `akm log` and `akm log tail` treat `"save"`
1589
+ and `"sync"` as synonyms on **read**, so `akm log --type save` keeps
1590
+ returning both old and new rows. Only newly written events use `"sync"`.
1591
+
1592
+ Migration: none for `akm log --type save`. A script matching raw event rows
1593
+ by `eventType === "save"` — reading state.db directly, bypassing `akm log` —
1594
+ should also match `"sync"` to see new syncs.
1595
+
1596
+ - **BREAKING: dropped the dead `installedKitCount` field from the `add`,
1597
+ `remove`, and `update` JSON envelopes.** It was a raw lockfile-entry count
1598
+ that nothing — internal code or test — ever read.
1599
+
1600
+ Migration: a script parsing `.config.installedKitCount` should stop; the
1601
+ field is gone, not renamed. `config.sourceCount` remains and is unaffected.
1602
+
1603
+ - **BREAKING: dropped the dead `graphPath` field from every `akm graph *` JSON
1604
+ envelope** (`summary`, `entities`, `relations`, `export`, `related`, `entity`,
1605
+ `orphans`). It always resolved to the shared state.db path, never a
1606
+ per-graph artifact, and carried nothing `stashPath` did not already provide.
1607
+
1608
+ Migration: a script reading `.graphPath` from any `akm graph` subcommand
1609
+ should stop; `stashPath` remains.
1610
+
1611
+ - **BREAKING: `semanticSearchMode` now defaults to `"off"`.** A bare or
1612
+ headless install (`akm init`, `akm setup --yes`, `akm setup --config`) was
1613
+ silently downloading the ~130 MB local embedding model on its first `akm
1614
+ index`, because the fallback used when the key is absent was `"auto"`. The
1615
+ interactive `akm setup` wizard still pre-selects semantic search **on** — a
1616
+ human is present to decide — and now shows the asset/download warning
1617
+ *before* the prompt rather than after, so the pre-checked box is an informed
1618
+ choice. When a remote `embedding.endpoint` is configured, enabling semantic
1619
+ search downloads nothing.
1620
+
1621
+ Migration: existing saved configs are unaffected — the flip only changes the
1622
+ fallback used when the key is absent. To keep semantic search on for a
1623
+ headless or CI install, set `semanticSearchMode: "auto"` explicitly, or point
1624
+ `embedding.endpoint` at a remote embedder.
1625
+
1626
+ - **BREAKING: `akm workflow run|brief|report` refuse to run until
1627
+ `experimental.workflowEngine` is set** (0.9.0 decision Q-05). The native
1628
+ workflow executor — including fan-out scheduling and worktree isolation —
1629
+ is experimental, and shipping it enabled by default would have made an
1630
+ unreviewed execution engine reachable from a plain `akm workflow run`. The
1631
+ gated surfaces now exit `78` with a `ConfigError` naming the exact key, and
1632
+ `akm task doctor` reports the gate's state. Authoring and linting the unified
1633
+ markdown format, along with every other `akm workflow` subcommand, remain
1634
+ ungated.
1635
+
1636
+ Migration: `akm config set experimental.workflowEngine true`.
1637
+
1638
+ - **BREAKING: the `env:<name>` / `secret:<name>` colon ref spelling is
1639
+ rejected** (0.9.0 decision Q-08). Refs are slash conceptIds only — `env/foo`,
1640
+ `secrets/deploy-key`. The colon form previously resolved as an undocumented
1641
+ alias in some places and fell through as a literal filename in others. It now
1642
+ fails with a usage error naming the slash replacement, rather than silently
1643
+ doing the wrong thing.
1644
+
1645
+ Migration: rewrite `env:<name>` as `env/<name>` and `secret:<name>` as
1646
+ `secrets/<name>`. The error message prints the exact replacement.
1647
+
1648
+ - **`akm improve` is review-first by default; autonomy is opt-in** (0.9.0
1649
+ decision D8). The command stays ON — schedules, reflect/distill proposals, and
1650
+ graph extraction are unchanged — but the lanes that mutate assets *without*
1651
+ review now require `akm config set experimental.improveAutonomy true`:
1652
+ memory-inference writes, the memory-cleanup pass, and triage
1653
+ `applyMode: "promote"` (which downgrades to `queue` rather than disabling
1654
+ triage). Consolidation remains review-oriented and is not gated.
1655
+
1656
+ A gated lane is never a silent no-op: it warns on stderr naming the lane and
1657
+ the key, appends an `improve_skipped` event with `reason: "autonomy_gated"`,
1658
+ and is counted in `akm health`'s improve skip-reason summary.
1659
+
1660
+ Migration: set `experimental.improveAutonomy: true` to restore the previous
1661
+ behavior. `sync.push` is **not** affected — it keeps its `true` default and its
1662
+ own `sync.push: false` / `--no-push` controls. Two other direct writes stay
1663
+ ungated by design: `extract`'s additive session indexing and distill's
1664
+ encoding-salience frontmatter stamp. Because the gate is applied before the LLM
1665
+ preflight, a review-first workspace may now need fewer engines configured than
1666
+ before.
1667
+
1668
+ Also: `akm improve` no longer rejects the global `--format`. It emits an
1669
+ envelope through `output()` (always under `--dry-run`, otherwise under
1670
+ `--json-to-stdout`), so `--format` applies to that envelope; progress output
1671
+ stays on stderr. Previously it exited 2 with `INVALID_FLAG_VALUE`, which made
1672
+ it the one command that rejected a valid global flag.
1673
+
1674
+ - **`akm health --report` replaces the html-only full report** (D7
1675
+ follow-through). The full health report — per-run rows, trend deltas vs the
1676
+ prior window, and the pending proposal queue — is now a **data** flag, not a
1677
+ side effect of asking for html: `akm health --report --format html` renders
1678
+ the rich report, and the identical dataset comes back under `--format json`
1679
+ (previously that data was reachable only as html). The registered md/html
1680
+ renderers fire on the shape of the result, and `akm health` no longer reads
1681
+ `--format` at all.
1682
+
1683
+ Migration: `akm health --format html` → `akm health --report --format html`
1684
+ (the bare form now renders the plain check generically); the html-only
1685
+ `--compare` flag is removed — use `--window-compare`, which with `--report`
1686
+ defaults to the `--since` window so trend deltas stay like-for-like.
1687
+
1688
+ - **Global output flags parse correctly next to positionals.** citty parses
1689
+ each command level against only its own declared args, so a root-declared
1690
+ global flag was unknown at the leaf and its space-separated value fell
1691
+ through as a positional — `akm sync --format json` synced a bundle named
1692
+ "json", and `akm env unset env:x KEY --format json` tried to unset a key
1693
+ named "json". The global output flags (`--format`, `--detail`, `--shape`,
1694
+ `--output`) are now declared on every leaf command so their values are
1695
+ consumed by the parser; the two bespoke argv-inspection workarounds this
1696
+ replaces are deleted. Three more non-exempt commands (`akm health`, `akm
1697
+ index`, `akm lint`) now declare these flags too, purely for `--help`
1698
+ visibility — all three already parsed `--format`/`--detail`/`--shape`/
1699
+ `--output` correctly, since none of them has a positional a stray value
1700
+ could fall into.
1701
+
1702
+ - **BREAKING: unknown commands and missing required arguments now exit `2`
1703
+ (usage), not `1`.** citty's own command-dispatch wrapper unconditionally
1704
+ called `process.exit(1)` for any error it raised before a command's own
1705
+ body ever ran — `akm totally-bogus` (unknown command), bare `akm log` /
1706
+ `akm lessons` (a subcommand group invoked with no subcommand), and a
1707
+ command missing a required positional (e.g. bare `akm import`) all exited
1708
+ `1`, contradicting the documented exit-code table (`1` = general error /
1709
+ not found, `2` = usage / bad input). The CLI now drives command dispatch
1710
+ directly instead of going through that wrapper, so it can reclassify this
1711
+ one error family as `2` while leaving `--help`, `--version`, and every
1712
+ other exit code unchanged.
1713
+
1714
+ Migration: a script that treated exit `1` as "something went wrong" for a
1715
+ mistyped command or missing argument should check for `2` instead (or
1716
+ keep treating any non-zero exit as failure, which was already correct).
1717
+
1718
+ - **BREAKING: `akm completions --shell <unsupported>` now exits `2` with the
1719
+ standard JSON error envelope, not `1` with a raw stack trace.**
1720
+ `completions` stays format-exempt (its own output is shell-script source,
1721
+ not a result envelope — see STABILITY.md), but its body is now wrapped in
1722
+ the same error-classification path every other command uses.
1723
+
1724
+ Migration: a script parsing this failure should now expect
1725
+ `{"ok":false,"error":"...","code":"INVALID_FLAG_VALUE","hint":...}` on
1726
+ stderr and exit code `2` in place of a stack trace and exit code `1`.
1727
+
1728
+ - **BREAKING: `akm index --dry-run` without `--clean` now exits `2` instead
1729
+ of running a real index.** The flag only ever gated the `--clean`
1730
+ stale-entry removal pass — every other phase (walk, LLM enrichment,
1731
+ embeddings, FTS, the adapter-detection config write) ran for real
1732
+ regardless, so `akm index --dry-run` alone silently performed a full index
1733
+ despite its name. The combination is now rejected with the standard usage
1734
+ envelope instead of quietly doing something other than what "dry run"
1735
+ promised.
1736
+
1737
+ Migration: a script or cron invoking bare `akm index --dry-run` was
1738
+ already getting a real index, so nothing there needs to change in effect —
1739
+ but it will now fail loudly instead. Pass `akm index --clean --dry-run` to
1740
+ preview the stale-entry removal pass, or `akm index --clean` to apply it;
1741
+ drop `--dry-run` entirely to keep running a plain real index.
1742
+
1743
+ - **BREAKING: a corrupt or unparseable `akm.lock` now makes lockfile WRITES
1744
+ throw, instead of silently destroying every entry.** The previous lenient
1745
+ reader returned `[]` on unparseable JSON; a write path that upserted a
1746
+ single entry onto that `[]` then overwrote the file, permanently deleting
1747
+ every other tracked bundle's lock entry. Install/update/remove write paths
1748
+ now use a strict reader that throws on the same corruption instead of
1749
+ reaching the destructive overwrite.
1750
+
1751
+ Migration: if a write now fails with a lockfile-parse error, `akm.lock` is
1752
+ genuinely corrupt — inspect and repair it by hand, or restore it from a
1753
+ backup (e.g. git history), before retrying the write. Reads elsewhere are
1754
+ unaffected; the lenient read contract is unchanged.
1755
+
1756
+ - **BREAKING: `AKM_NPM_REGISTRY` now redirects npm package METADATA lookups,
1757
+ not just the trusted-tarball allowlist.** Previously the override only
1758
+ widened which tarball hosts were trusted for download while metadata
1759
+ queries stayed hardcoded to `registry.npmjs.org`, so a configured private
1760
+ mirror was never actually consulted for package info — the error hint that
1761
+ points users at this variable was false. The override now also replaces
1762
+ the metadata registry base, matching how a private npm registry is meant
1763
+ to work (like npm's own `--registry` flag: wholesale replacement, not a
1764
+ merge with the public registry).
1765
+
1766
+ Migration: an operator who set `AKM_NPM_REGISTRY` expecting only tarball
1767
+ downloads to be redirected, with metadata still served from the public
1768
+ registry, should confirm the mirror actually serves equivalent package
1769
+ metadata — `akm add`/`akm update` for npm-sourced bundles now resolve
1770
+ entirely against the configured mirror when it is set.
1771
+
1772
+ - **`akm remember --show-similar` and `akm migrate apply --dry-run` are the
1773
+ documented, canonical spellings** (previously `--showSimilar` /
1774
+ `--dryRun`), matching every other multi-word flag in the CLI. Not a
1775
+ breaking change: citty registers both the camelCase and kebab-case
1776
+ spelling of any declared flag name automatically, so `--showSimilar` /
1777
+ `--dryRun` keep working — they're now explicit, documented aliases instead
1778
+ of an undocumented accident.
1779
+
1780
+ - **`--detail` and `--shape` help text is scoped honestly.** The per-command
1781
+ `--detail` description now names `info`, `list`, and `remember` as the
1782
+ commands where it has no effect (verified byte-identical output at every
1783
+ level — `akm show` is not one of these; it has three distinct
1784
+ brief/normal/full payloads). `--shape`'s per-command help now repeats the
1785
+ "`summary` is only valid on `akm show`" caveat the root help already
1786
+ documented.
1787
+
1788
+ - **All six `--format` values work on every command** (0.9.0 decision D7).
1789
+ `json|jsonl|yaml|text|md|html` are now universal. Previously there were three
1790
+ inconsistent behaviours: `md` silently emitted the JSON envelope everywhere
1791
+ except `akm health`, `html` was rejected with exit 2 everywhere except
1792
+ `akm health`, and `akm health` reached neither because it intercepted the
1793
+ format itself. Rendering is now registry-driven — a command may register a
1794
+ renderer for a document format, and anything unregistered falls back to a real
1795
+ rendering of its own envelope (headings, tables for arrays of uniform objects,
1796
+ lists otherwise). `akm health` keeps its per-run/window-compare tables and its
1797
+ full HTML report by registering them; the output is unchanged.
1798
+
1799
+ Migration: none required for `json|jsonl|yaml|text`. `--format md` on a
1800
+ non-health command previously returned JSON and now returns Markdown; a script
1801
+ that parsed that JSON should ask for `--format json` explicitly. `--format
1802
+ html` previously exited 2 on non-health commands and now succeeds.
1803
+
1804
+ Also: `akm graph export --format` is **removed** — it declared `--format`
1805
+ locally as well as globally (one token, two parsers). The artifact payload
1806
+ now follows the `--out` extension (`--out g.jsonl` writes JSONL, anything
1807
+ else JSON); the global flag only renders the command's own envelope. A dead
1808
+ local `--format` declaration on `akm history` was removed too (it was never
1809
+ read). Commands
1810
+ whose output is not an envelope (`completions`, `setup`, `env run`,
1811
+ `secret run`, `agent`, `workflow template`, `help migrate`) are declared
1812
+ format-exempt in `src/output/format-exempt.ts` and now warn when given
1813
+ `--format` instead of ignoring it silently. `output.format` in config accepts
1814
+ all six values.
1815
+
1816
+ - **Subtree browse is a conceptId prefix, not `<type>:`** (0.9.0 decision D4).
1817
+ `akm search` enumerates on `memories/`, `memories/projecta/`, `bundle//`, and
1818
+ `bundle//skills/`; a trailing `/` is still required. The prefix now matches the
1819
+ **conceptId** rather than the item name, so a ref copied out of search output
1820
+ can be truncated to a prefix and pasted straight back in — previously that
1821
+ round-trip degraded silently into a keyword search. Enumeration no longer
1822
+ validates against the `akm` adapter's placement types, so items from every
1823
+ adapter browse the same way, and `bundle//` lists a whole bundle (the
1824
+ replacement for the removed `akm bundle items`).
1825
+
1826
+ Migration: `akm search "memory:"` → `akm search "memories/"`;
1827
+ `akm search "memory:projectA/"` → `akm search "memories/projectA/"`;
1828
+ `akm search "session:"` → `akm search "sessions/"`. The retired spelling is
1829
+ now an ordinary keyword search; when it returns nothing, the tip names the
1830
+ conceptId spelling that replaces it. `scripts/lint-shipped-assets.ts` no
1831
+ longer exempts the old spelling, so it is an offense in agent-facing assets.
1832
+
1833
+ - **`akm task sync [--bundle <bundle>]` reconciles a single bundle.** Sync now
1834
+ attributes each installed scheduler entry to its bundle (parsed from the
1835
+ `--bundle` token; absent ⇒ primary) and reconciles only the entries for the
1836
+ bundle being synced. A plain (primary) sync never installs from, updates, or
1837
+ removes another bundle's entries, and sync never scans all bundles — task
1838
+ activation stays explicit (`add --bundle` or `sync --bundle`), so registering a bundle
1839
+ still never activates code. When the target is the default bundle (or omitted),
1840
+ installed scheduler entries are byte-identical to before, so upgrading shows no
1841
+ spurious drift.
1842
+
1843
+ - **The R2 salience ranking boost no longer applies to default `search`/`curate`
1844
+ ranking** (#692). `asset_salience.rank_score` (an encoding + outcome +
1845
+ retrieval projection, recomputed every `improve` run) previously composed
1846
+ into every default search as a bounded multiplicative boost
1847
+ (`salience-ranking`, ×[1.0–1.2]), loaded best-effort from `state.db` on the
1848
+ hot path. On live data it measured as noise (max observed multiplier
1849
+ ×1.071, mean ×1.016): the boost was retrieval-dominated with no source
1850
+ filter — double-counting the same `usage_events` the utility-score
1851
+ contributor already reinforces — warm-started non-zero with no outcome
1852
+ evidence, and had zero pack coverage, so it could only ever favor
1853
+ self-generated personal assets over an equally-relevant pack asset.
1854
+ Removing the default `state.db` load also fixes a confirmed hot-path
1855
+ defect: whenever `state.db` already existed, every default search
1856
+ synchronously waited on the maintenance-activity barrier before ranking
1857
+ could even start — up to a 5-second stall on a blocking wait loop, plus a
1858
+ lock-file create, before the load's own 250ms SQLite `busy_timeout` ever
1859
+ applied. No config gate was added: a key for a term being removed would be
1860
+ dead surface for the upcoming 1.0 contract freeze to carry forever.
1861
+ `rank_score` itself, and everything `improve` computes and does with it
1862
+ internally, are unchanged — only its promotion into user-facing ranking is
1863
+ removed. The contributor stays in the codebase (unwired) for a future
1864
+ gated, outcome-backed experiment.
1865
+
1866
+ - **Internal: `asset_salience` / `asset_outcome` state.db access moved behind
1867
+ `src/storage/repositories/{salience,outcome}-repository.ts`** (#672 part 2).
1868
+ Mirrors the existing state.db repository precedents
1869
+ (`proposals-repository.ts`, `improve-runs-repository.ts`,
1870
+ `events-repository.ts`): the raw SQL, row-mapping, and the #644
1871
+ encoding-provenance CASE guards are extracted verbatim, only relocated —
1872
+ `commands/improve/salience.ts` and `outcome-loop.ts` re-export the moved
1873
+ functions, so no importer or test churns. A new `state-table-sql` rule in
1874
+ `scripts/lint-repository-sql.ts` now fails the build if raw
1875
+ `asset_salience`/`asset_outcome` SQL reappears outside the repository
1876
+ directory (or `core/state/migrations.ts`). Not a user-visible behavior
1877
+ change: `rank_score`, `outcome_score`, and everything `improve`/`health`
1878
+ compute from them are identical.
1879
+
1880
+ ### Fixed
1881
+
1882
+ - **The compiled standalone binary can run `akm migrate`.** Release binaries
1883
+ compiled only `src/cli.ts`, and the migrator was resolved as a sibling file
1884
+ and spawned — neither candidate exists inside a compiled executable, so the
1885
+ documented `./akm-0.9 migrate status/apply` upgrade path always failed with
1886
+ `FILE_NOT_FOUND`. Standalone builds now compile `scripts/akm-standalone.ts`,
1887
+ a wrapper that embeds both the CLI and the migrator (src never imports
1888
+ scripts/ — the dist build's tsc forbids it); `akm migrate` re-execs the
1889
+ binary with an `AKM_MIGRATE_ENTRY` marker the wrapper dispatches on. The
1890
+ repo and npm layouts keep the subprocess path.
1891
+
1892
+ - **Quarantined migration rows are retained in full, not reduced to a count.**
1893
+ When the 0.8→0.9 cutover met a durable ref it could not map, it recorded
1894
+ surface/ref/count in `legacy_state` and then deleted the rows — destroying
1895
+ proposal payloads, event and task history, fingerprints, and canary anchors,
1896
+ contrary to the migration guide's "quarantined, not dropped". Complete rows
1897
+ are now preserved as JSON in `legacy_state_rows` before leaving the live
1898
+ tables.
1899
+
1900
+ - **A failed content migration fails the apply instead of reporting success.**
1901
+ Root discovery, sidecar folding, or the legacy-proposal import throwing was
1902
+ swallowed and logged; the apply then advanced and cleared its journal, and —
1903
+ because 0.9 removed the live `.stash.json` and filesystem-proposal readers —
1904
+ the affected metadata and pending proposals became permanently inaccessible
1905
+ behind an apparently successful upgrade. The step now fails the apply with
1906
+ the journal intact; the committed cutover is untouched and the next apply
1907
+ retries.
1908
+
1909
+ - **Sidecar provenance survives the fold.** Folding a `.stash.json` into
1910
+ frontmatter dropped `xrefs` and `sources` entirely and mapped legacy
1911
+ `sourceRefs` to a `source_refs` key that could never fire (the validator
1912
+ stopped copying the field) and that 0.9 never reads — then deleted the only
1913
+ copy. `xrefs`/`sources` now fold through, and legacy `sourceRefs` merge into
1914
+ `xrefs`.
1915
+
1916
+ - **A reserved-filename rename re-keys durable state.** The D-R6 rename of a
1917
+ mis-named `index.md`/`log.md` concept ran after the cutover had keyed usage,
1918
+ salience, and proposal rows to the old conceptId, stranding that learned
1919
+ state. The rename now feeds the same re-key engine the cutover uses, with
1920
+ the pairs persisted before re-keying so a crash between the two stays
1921
+ retryable.
1922
+
1923
+ - **v1 tasks in a read-only bundle are surfaced with a remedy instead of being
1924
+ silently skipped.** The 0.9 runtime removed the v1 task parser, so silently
1925
+ skipping a `writable: false` bundle left tasks that would start failing after
1926
+ an upgrade that reported current. The preflight now warns per bundle and
1927
+ lists the stranded files in the plan (`readOnlyLegacyTasks`). It does not
1928
+ block the apply: the migration deliberately never rewrites a read-only
1929
+ bundle, and the fix for a lock-materialized git/npm bundle belongs upstream.
1930
+
1931
+ - **Lock resolution metadata survives migration.** Merging the migrator's
1932
+ sparse lock entries replaced whole rows by id, discarding
1933
+ `resolvedVersion`/`resolvedRevision`/`integrity`/`installedAt` recorded by a
1934
+ real install. Merge now preserves existing fields the incoming entry does
1935
+ not define.
1936
+
1937
+ - **Migrating a pre-0.9 config no longer silently changes source policy.**
1938
+ Three settings were dropped by the config-shape migration: an explicit
1939
+ `writable: false` (an omitted filesystem `writable` reads as `true` in the
1940
+ new shape, so a source the user deliberately protected became writable), an
1941
+ explicit `enabled: false` (resuming refreshes and indexing for content the
1942
+ operator had turned off), and a website source's `maxDepth` (silently
1943
+ resetting crawl depth). All three now round-trip to the runtime source entry;
1944
+ `bundles.<id>.enabled` is a supported key.
1945
+
1946
+ - **`akm mv` refuses a bundle marked `writable: false`.** It renamed the file
1947
+ and rewrote citers anyway, because its preflight checked adapter
1948
+ compatibility rather than writability — every other write command already
1949
+ refused.
1950
+
1951
+ - **Memory belief edges written by `--supersedes` are no longer ignored.**
1952
+ `writeSupersededEdge` persists a fully-qualified conceptId, but the belief
1953
+ analyzer accepted only the internal `memory:<name>` spelling, so every edge
1954
+ from `akm remember --supersedes` / `akm import --supersedes` was dropped and
1955
+ a superseded memory read back as active.
1956
+
1957
+ - **`akm env run <ref> -- <cmd> --help` runs the command.** The builtin
1958
+ help-flag scan read the child tail after `--` and printed akm's own usage
1959
+ instead.
1960
+
1961
+ - **`akm mv` works under an `AKM_STASH_DIR` override again.** A valid override
1962
+ not owned by a configured bundle failed with `No configured bundle owns move
1963
+ source`.
1964
+
1965
+ - **An unexpected internal error exits 70 with the JSON failure envelope.** The
1966
+ residual dispatch boundary exited 1 with an unstructured message, so
1967
+ automation could not tell an internal defect from an ordinary failure.
1968
+
1969
+ - **Concurrent `akm config set` processes no longer give up prematurely.** The
1970
+ contended-lock wait budget was 500ms total, so several concurrent writers on
1971
+ a loaded machine could exhaust it and fail with "Timed out waiting for config
1972
+ lock" against a healthy but busy lock. Abandoned locks are still reclaimed by
1973
+ the stale probe, which this budget does not gate.
1974
+
1975
+ - **Config keys named in indexer output and comments now exist.** Four sites
1976
+ pointed at a top-level `llm.*` namespace that the config schema has no such
1977
+ key for — including the user-facing "Increase llm.timeoutMs" warning on an
1978
+ exceeded enrichment budget. The enrichment budget lives at
1979
+ `index.enrichment.timeoutMs` (or `index.defaults.timeoutMs`). Indexing
1980
+ concurrency is auto-derived (2 remote / 1 local) and currently has no config
1981
+ override on that path: `engines.<name>.concurrency` is a valid schema field
1982
+ but the engine resolver does not forward it (documented in
1983
+ `docs/architecture/internals/indexing.md`).
1984
+
1985
+ - **The bundle-identity-drift warning stops naming a command that doesn't
1986
+ exist.** It told users to "rekey it atomically via the bundle-rename
1987
+ command"; 0.9.0 ships no such command. It now gives the two remedies that
1988
+ work: restore the previous bundle id in `config.json`, or keep the new id and
1989
+ `akm index --full` to re-mint, accepting the loss of learned state keyed to
1990
+ the old id.
1991
+
1992
+ - **The scaffolded `organization.md` convention no longer contradicts `akm
1993
+ mv`.** It told authoring agents "there is no command that preserves an
1994
+ asset's identity or learned state" across a rename and showed a raw `mv`.
1995
+ `akm mv` does exactly that — it rewrites inbound refs and re-keys the index
1996
+ row, usage history, and state.db salience/outcome rows. The convention now
1997
+ points at it, flagged Experimental.
1998
+
1999
+ - **`setup.taskSchedules` is no longer documented.** The key was removed from
2000
+ the schema in 0.9.0 (nothing ever read or wrote it), but
2001
+ `docs/reference/configuration.md` still described its two sub-keys.
2002
+
2003
+ - **A freshly scaffolded stash passes its own `akm lint`.** All 12 shipped
2004
+ `facts/conventions/**` convention templates carry frontmatter but none
2005
+ carried an `updated` field, so the first `akm lint` after `akm init` flagged
2006
+ 12 `missing-updated` issues on files the user never wrote. The templates now
2007
+ ship the field, and a regression test lints a freshly scaffolded stash and
2008
+ requires nothing flagged.
2009
+
2010
+ - **`akm show akm//meta` is the documented spelling for the primary stash.**
2011
+ `docs/reference/cli.md` and `docs/guides/concepts.md` showed
2012
+ `akm show local//meta`, which errors with `ASSET_NOT_FOUND` — `local//` is no
2013
+ longer a scoping prefix, so it reads as a bundle named `local`.
2014
+
2015
+ - **`akm sync` emits `shape: "sync"`.** The envelope kept the `"save"` shape
2016
+ from the command's pre-rename name even after the persisted `eventType` was
2017
+ renamed. Unlike the event log, the shape is per-invocation and never
2018
+ persisted, so it needs no read-side synonym.
2019
+
2020
+ - **`akm add <pkg> --provider npm` adds an npm source instead of a broken
2021
+ filesystem bundle.** `--provider` was only read inside the remote-URL branch,
2022
+ so any non-URL target fell through to the filesystem path with the flag
2023
+ ignored, producing a bundle pointed at `<cwd>/<pkg>`. A URL target with
2024
+ `--provider npm` is now rejected at add time rather than storing the URL as a
2025
+ package spec and failing much later at first sync.
2026
+
2027
+ - **`akm add --provider` no longer prints `Installed undefined`.** Two
2028
+ incompatible result shapes reached one text formatter; each is now rendered
2029
+ honestly, including whether a follow-up `akm update` or `akm index` is needed.
2030
+
2031
+ - **`akm update --all` accounts for every configured source.** It previously
2032
+ considered only registry-managed installs and reported `nothing to update`
2033
+ for a stash full of plain sources — nothing was updated because nothing was
2034
+ looked at. Plain git and npm sources are now synced (npm is promoted to a
2035
+ lock-backed install on first sync) and website/filesystem sources are
2036
+ reported through a new `skipped` field with the reason. A successful update of
2037
+ a plain source no longer renders as `nothing to update` either.
2038
+
2039
+ - **`akm search` with no query browses**, as `--help` has always documented,
2040
+ instead of exiting 2.
2041
+
2042
+ - **`akm curate --type <t>` curates within the type instead of bypassing
2043
+ curation.** The filter skipped ranking, intent nudges, the score floor, and
2044
+ family collapse entirely — and could return a hit of the *wrong* type while
2045
+ dropping a higher-scoring correct one.
2046
+
2047
+ - **`akm curate` respects `--limit` for registry hits**, which were capped at a
2048
+ hard-coded 2 regardless.
2049
+
2050
+ - **`akm search --no-project-context` works.** citty strips a leading `--no-`
2051
+ before consulting declared args, so a flag *declared* as `no-project-context`
2052
+ could never be set — the ranking boost was identical with and without it. The
2053
+ flag users type is unchanged.
2054
+
2055
+ - **`akm env run`, `akm secret run`, `akm migrate`, `akm agent`, `akm proposal new`,
2056
+ `akm task run`, and `akm improve` no longer skip cleanup on exit.** They
2057
+ called `process.exit()` directly — in two cases even on success — bypassing
2058
+ teardown of spawned subprocesses. Exit codes, including forwarded non-zero
2059
+ child codes, are unchanged.
2060
+
2061
+ - **The `blocked` semantic-search warning names the cause.** It emitted one
2062
+ fixed string for every failure and discarded the status ledger's reason, so
2063
+ "no embedding provider configured" and "the configured endpoint is failing"
2064
+ read identically.
2065
+
2066
+ - **Shell completion for `--source` no longer suggests `stash|registry|both`
2067
+ on commands where that enum doesn't apply.** `--source` means a closed
2068
+ `stash|registry|both` enum on `akm search`/`akm curate`, but a free-form
2069
+ stash name/path on every `akm graph` subcommand and a free-form URL/ref/
2070
+ path on `akm remember`. The generated completion script keyed its value
2071
+ list by flag name only, so the search/curate enum leaked onto `akm graph
2072
+ --source <TAB>` and `akm remember --source <TAB>`. Value completion is now
2073
+ scoped per command path; commands without a fixed value set get no
2074
+ suggestion instead of the wrong one.
2075
+
2076
+ - **`akm setup --config <file>` / `--from <file>` no longer silently drops
2077
+ six valid config keys** (`index`, `search`, `feedback`,
2078
+ `archiveRetentionDays`, `workflow`, `experimental`). The allowlist was a
2079
+ hand-copied set that had drifted out of sync with the config schema; a
2080
+ user handing setup a config containing any of these keys got a different,
2081
+ silently truncated config written back, with only a warning and exit `0`.
2082
+ The allowlist is now derived from the schema's own key list so it cannot
2083
+ drift again. Keys that remain genuinely retired (`profiles`, `llm`,
2084
+ `agent`, `features`, `stashes`, `bindings`, `writable`) still warn-and-drop
2085
+ as before.
2086
+
2087
+ Note: a config that previously relied on one of these six keys being
2088
+ ignored (because the drop was silent) will now have it applied — re-check
2089
+ `--config`/`--from` inputs if you were unknowingly depending on that gap.
2090
+
2091
+ - **`akm index` no longer persists adapter auto-detection to `config.json`
2092
+ with zero disclosure.** Detecting and writing a bundle component's adapter
2093
+ (`bundles.<id>.components.<component>.adapter`) previously happened
2094
+ silently on every index run. It is now reported in the result envelope as
2095
+ an additive `configUpdated.detectedAdapters` map and on stderr, and only
2096
+ when a write actually happened.
2097
+
2098
+ - **`akm add owner/repo` now resolves as GitHub shorthand instead of failing
2099
+ with "Local path not found".** Any ref containing a `/` was treated as an
2100
+ explicit local path, so the local-ref resolver threw before the
2101
+ GitHub-shorthand fallback ever ran, making the advertised `owner/repo` form
2102
+ unreachable. A bare two-segment `owner/repo` (or `owner/repo#ref`) now
2103
+ falls through to the registry resolver when no such directory exists on
2104
+ disk; `./`, `../`, absolute, and three-or-more-segment paths still resolve
2105
+ as explicit local paths exactly as before.
2106
+
2107
+ - **Internal output-shape command keys renamed `events-list`/`events-tail` →
2108
+ `log-list`/`log-tail`**, matching the `akm log` command they back (the
2109
+ command group used to be `akm events`, removed in 0.9.0). Internal-only:
2110
+ the shape name is a registry lookup key that never reaches the wire (no
2111
+ output field, no schema change), so this is not a user-visible behavior
2112
+ change and carries no `schemaVersion` bump. The documented `[events-tail]`
2113
+ stderr trailer text is deliberately left as-is pending a separate ruling.
2114
+
2115
+ ### Removed
2116
+
2117
+ - **BREAKING: `akm upgrade --skip-checksum` is removed.** STABILITY.md has
2118
+ always said checksum verification is not optional and that the recovery hatch
2119
+ is an environment variable — but the flag shipped anyway, tab-completable,
2120
+ while the documented variable existed nowhere in the source. The code now
2121
+ matches the spec: set `AKM_UPGRADE_SKIP_CHECKSUM=1` if you must bypass a
2122
+ genuinely broken `checksums.txt`. It is deliberately undiscoverable.
2123
+
2124
+ - **BREAKING: `akm config enable|disable` is removed.** It was a hard-coded
2125
+ toggle for one target, the skills.sh registry, and the bare `akm enable` /
2126
+ `akm disable` aliases were already removed in 0.9.0. Use
2127
+ `akm registry add|remove`.
2128
+
2129
+ - **BREAKING: `akm mv` is removed.** No alias, no stub — `akm mv …` fails with
2130
+ the standard unknown-command error. It claimed to preserve identity across a
2131
+ rename, but its inbound-ref rewrite matched bare conceptIds rather than the
2132
+ anchored `bundle//conceptId` prose form, so it could rewrite ordinary prose
2133
+ while leaving real refs dangling. Renames are delete + create per
2134
+ `STABILITY.md`: move the file, `akm index`, `akm lint`. The one capability
2135
+ nothing else covered — carrying an asset's earned signal across the rename —
2136
+ moves to `bun scripts/rekey-asset-ref.ts <old-ref> <new-ref>` (maintainer
2137
+ tooling, `--dry-run` supported, idempotent), which re-keys the index
2138
+ `entries` row in place plus the `asset_salience` / `asset_outcome` /
2139
+ `usage_events` rows. The `mv` event type and output shape are gone; the
2140
+ script emits a `rekey` event instead. A leftover `kind:"mv"` transaction
2141
+ journal from an rc build is now swept by the recovery scanner rather than
2142
+ failing it — an unregistered journal kind no longer bricks index refresh or
2143
+ proposal accept/reject.
2144
+
2145
+ - **The CHURN alert class is removed from the collapse detector.** Its input was
2146
+ a hard-coded `0` from the 0.9.0 confidence-gate deletion onward, so the alert
2147
+ could never fire. The other three alert classes are unaffected. The
2148
+ `improve_cycle_metrics.accepted_actions` column stays and is written as `0`
2149
+ because deployed 0.8 `state.db` files already contain it.
2150
+
2151
+ - **`IndexResponse.graphQuality` is removed** from the `akm index` envelope — it
2152
+ was declared but never assigned in any code path, so it was always absent.
2153
+
2154
+ - **`akm secret path` and `akm secret remove` are removed.** The two resolved a
2155
+ secret ref through *different* stash-selection logic — `path` through the
2156
+ read-side, all-sources resolver and `remove` through the write-target
2157
+ resolver — so for a ref present in more than one stash they could name
2158
+ different files: you could inspect one secret and delete another. Rather than
2159
+ reconcile the resolvers, both subcommands are gone; `akm secret` now exposes
2160
+ only `list`, `run`, and `set`. Both spellings exit 2 with `Unknown command`.
2161
+
2162
+ Migration: a ref's file lives at `<stash>/secrets/<name>` (run `akm sources
2163
+ list` for stash roots) — locate or delete it directly, or use `akm secret run
2164
+ <ref> <VAR> -- <command>` to consume the value without it touching disk. `akm
2165
+ env path` and `akm env remove` are unaffected.
2166
+
2167
+ - Removed the dead `"backup"` output-shape registration left over from the
2168
+ removed `akm backup` command (superseded by `akm-migrate backup`). Already
2169
+ unreachable; no user-visible effect.
2170
+
2171
+ - **`akm task list`, `akm task show`, and `akm task remove` are removed** as
2172
+ redundant with the generic asset commands. List and inspect tasks with `akm
2173
+ search` / `akm show <bundle//tasks/id>` (both already cross-bundle); to remove a
2174
+ scheduled task, delete its file in the owning bundle and run `akm task sync`
2175
+ (sync uninstalls the orphaned scheduler entry). Run `akm task doctor` for
2176
+ scheduler diagnostics — bare `akm task` is a usage error, see the canonical
2177
+ bare-group change above.
2178
+
2179
+ - **The `akm show <ref> toc|section|lines|frontmatter|full` view-mode grammar is
2180
+ removed** (0.9.0 decision D2). `#fragment` is now the only section selector,
2181
+ and a positional after the ref is a usage error that names it. Migration:
2182
+
2183
+ | Old | New |
2184
+ | --- | --- |
2185
+ | `akm show knowledge/guide section "Auth"` | `akm show knowledge/guide#auth` |
2186
+ | `akm show knowledge/guide full` | `akm show knowledge/guide` |
2187
+ | `akm show knowledge/guide toc` | `akm show knowledge/guide#<unmatched>` — the error lists the available fragment slugs |
2188
+ | `akm show knowledge/guide lines 10 30` | no replacement — every response carries `path`, so slice the file yourself |
2189
+ | `akm show knowledge/guide frontmatter` | no replacement — if a raw-YAML projection proves necessary it returns as a `--shape` value |
2190
+
2191
+ The undocumented `--akmView` / `--akmHeading` / `--akmStart` / `--akmEnd`
2192
+ flags the grammar injected into argv are gone with it.
2193
+
468
2194
  ## [0.9.0-rc.1] - 2026-06-30
469
2195
 
470
2196
  ### Fixed
@@ -1948,7 +3674,7 @@ behind an interactive prompt (or `-y` / `--yes` in non-interactive use).
1948
3674
 
1949
3675
  ### Changed
1950
3676
 
1951
- - **Rebrand**: the full name "Agent Kit Manager" is now **Agent Knowledge Management** — `akm` stands for Agent Knowledge Management going forward. The binary name, npm package (`akm-cli`), and all APIs remain unchanged.
3677
+ - **Rebrand**: the full name "Agent Kit Manager" is now **Agent Knowledge Manager** — `akm` stands for Agent Knowledge Manager going forward. The binary name, npm package (`akm-cli`), and all APIs remain unchanged.
1952
3678
 
1953
3679
  - **Config layer rewrite** — single-source-of-truth Zod schema in
1954
3680
  `src/core/config-schema.ts` replaces the per-field parse switch AND
@@ -2136,7 +3862,7 @@ See `docs/migration/v0.7-to-v0.8.md` for the user-facing migration guide.
2136
3862
 
2137
3863
  ### Migration
2138
3864
 
2139
- - See [`docs/migration/release-notes/0.7.0.md`](docs/migration/release-notes/0.7.0.md) for the operator summary and the [archived pre-1.0 plan](docs/archive/pre-1.0-migration.md) for the historical per-surface delta from any 0.6.x baseline.
3865
+ - See [`docs/migration/release-notes/0.7.0.md`](docs/migration/release-notes/0.7.0.md) for the operator summary and the [archived pre-1.0 plan](https://github.com/itlackey/akm/blob/be3a6a632b0cbe7a63ce71b7d093d8ac266e857c/docs/archive/pre-1.0-migration.md) for the historical per-surface delta from any 0.6.x baseline.
2140
3866
 
2141
3867
  ## [0.6.0] - 2026-04-23
2142
3868