akm-cli 0.9.0-rc.8 → 0.9.0

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 (450) hide show
  1. package/CHANGELOG.md +1063 -44
  2. package/README.md +51 -25
  3. package/SECURITY.md +14 -1
  4. package/STABILITY.md +497 -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 +73 -88
  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 +141 -87
  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/md-report.js +5 -9
  57. package/dist/commands/health/metrics.js +62 -20
  58. package/dist/commands/health/renderers.js +47 -0
  59. package/dist/commands/health/report-view-model.js +4 -5
  60. package/dist/commands/health/stash-exposure.js +1 -1
  61. package/dist/commands/health/surfaces.js +3 -48
  62. package/dist/commands/health/task-runs.js +3 -67
  63. package/dist/commands/health/types-improve.js +7 -0
  64. package/dist/commands/health.js +99 -28
  65. package/dist/commands/improve/anti-collapse.js +2 -2
  66. package/dist/commands/improve/autonomy-gate.js +68 -0
  67. package/dist/commands/improve/collapse-detector.js +41 -40
  68. package/dist/commands/improve/consolidate/eligibility.js +1 -23
  69. package/dist/commands/improve/consolidate/merge.js +4 -0
  70. package/dist/commands/improve/consolidate.js +140 -1000
  71. package/dist/commands/improve/distill/promote-memory.js +12 -12
  72. package/dist/commands/improve/distill/quality-gate.js +6 -6
  73. package/dist/commands/improve/distill.js +58 -69
  74. package/dist/commands/improve/eligibility.js +105 -57
  75. package/dist/commands/improve/extract-cli.js +14 -133
  76. package/dist/commands/improve/improve-cli.js +98 -114
  77. package/dist/commands/improve/improve-result-file.js +1 -28
  78. package/dist/commands/improve/improve-strategies.js +8 -5
  79. package/dist/commands/improve/improve.js +128 -91
  80. package/dist/commands/improve/loop-stages.js +182 -20
  81. package/dist/commands/improve/memory/derived-ref.js +45 -43
  82. package/dist/commands/improve/memory/memory-belief.js +1 -1
  83. package/dist/commands/improve/memory/memory-contradiction-detect.js +4 -12
  84. package/dist/commands/improve/memory/memory-improve.js +6 -5
  85. package/dist/commands/improve/outcome-loop.js +22 -65
  86. package/dist/commands/improve/preparation.js +114 -123
  87. package/dist/commands/improve/proactive-maintenance.js +2 -5
  88. package/dist/commands/improve/reflect.js +56 -160
  89. package/dist/commands/improve/salience.js +11 -122
  90. package/dist/commands/improve/source-identity.js +10 -38
  91. package/dist/commands/lint/base-linter.js +20 -124
  92. package/dist/commands/lint/env-key-rules.js +31 -47
  93. package/dist/commands/lint/index.js +249 -43
  94. package/dist/commands/{events.js → log.js} +33 -38
  95. package/dist/commands/migrate-cli.js +92 -12
  96. package/dist/commands/migration-tool.js +46 -0
  97. package/dist/commands/observability-cli.js +70 -209
  98. package/dist/commands/proposal/drain.js +101 -29
  99. package/dist/commands/proposal/proposal-cli.js +76 -48
  100. package/dist/commands/proposal/proposal.js +54 -18
  101. package/dist/commands/proposal/propose-cli.js +88 -0
  102. package/dist/commands/proposal/propose.js +23 -15
  103. package/dist/commands/proposal/repository.js +701 -278
  104. package/dist/commands/proposal/validators/proposal-quality-validators.js +2 -8
  105. package/dist/commands/proposal/validators/proposal-validators.js +55 -7
  106. package/dist/commands/proposal/validators/proposals.js +4 -7
  107. package/dist/commands/read/curate.js +34 -53
  108. package/dist/commands/read/knowledge.js +150 -95
  109. package/dist/commands/read/registry-search.js +2 -2
  110. package/dist/commands/read/remember-cli.js +42 -15
  111. package/dist/commands/read/search-cli.js +180 -78
  112. package/dist/commands/read/search.js +58 -43
  113. package/dist/commands/read/show.js +197 -141
  114. package/dist/commands/registry-cli.js +12 -51
  115. package/dist/commands/remember.js +14 -57
  116. package/dist/commands/sources/add-cli.js +100 -31
  117. package/dist/commands/sources/bundle-cli.js +166 -0
  118. package/dist/commands/sources/bundle-config-ops.js +7 -2
  119. package/dist/commands/sources/info.js +18 -5
  120. package/dist/commands/sources/init.js +12 -12
  121. package/dist/commands/sources/installed-stashes.js +382 -98
  122. package/dist/commands/sources/schema-repair.js +3 -2
  123. package/dist/commands/sources/self-update.js +131 -38
  124. package/dist/commands/sources/source-add.js +72 -17
  125. package/dist/commands/sources/source-clone.js +129 -45
  126. package/dist/commands/sources/source-manage.js +43 -23
  127. package/dist/commands/sources/sources-cli.js +57 -208
  128. package/dist/commands/sources/stash-cli.js +46 -53
  129. package/dist/commands/tasks/tasks-cli.js +91 -97
  130. package/dist/commands/tasks/tasks.js +276 -421
  131. package/dist/commands/workflow-cli.js +175 -450
  132. package/dist/core/adapter/adapters/akm-adapter.js +47 -28
  133. package/dist/core/adapter/adapters/akm-lint.js +42 -27
  134. package/dist/core/adapter/adapters/akm-metadata.js +15 -44
  135. package/dist/core/adapter/adapters/akm-task-adapter.js +15 -13
  136. package/dist/core/adapter/adapters/akm-workflow-adapter.js +55 -71
  137. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  138. package/dist/core/adapter/adapters/generic-files-adapter.js +2 -0
  139. package/dist/core/adapter/adapters/index.js +6 -6
  140. package/dist/core/adapter/adapters/llm-wiki-adapter.js +14 -8
  141. package/dist/core/adapter/adapters/okf-adapter.js +187 -19
  142. package/dist/core/adapter/adapters/shared.js +3 -19
  143. package/dist/core/adapter/adapters/tool-dir-shared.js +8 -3
  144. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -0
  145. package/dist/core/adapter/detect-adapter.js +17 -0
  146. package/dist/core/adapter/recognize-match.js +6 -4
  147. package/dist/core/adapter/validate-context.js +214 -0
  148. package/dist/core/asset/akm-markdown.js +63 -0
  149. package/dist/core/asset/asset-placement.js +20 -6
  150. package/dist/core/asset/asset-ref.js +11 -9
  151. package/dist/core/asset/frontmatter-lint.js +30 -0
  152. package/dist/core/asset/frontmatter.js +37 -9
  153. package/dist/core/asset/markdown.js +40 -51
  154. package/dist/core/asset/resolve-ref.js +89 -18
  155. package/dist/core/asset/stash-meta.js +1 -1
  156. package/dist/core/bundle-id.js +51 -0
  157. package/dist/core/common.js +152 -38
  158. package/dist/core/config/config-io.js +12 -1
  159. package/dist/core/config/config-schema.js +35 -8
  160. package/dist/core/config/config-sources.js +55 -11
  161. package/dist/core/config/config-walker.js +25 -9
  162. package/dist/core/config/config.js +9 -48
  163. package/dist/core/config/experimental.js +21 -0
  164. package/dist/core/config/schema/embedding.js +5 -1
  165. package/dist/core/config/schema/experimental.js +30 -0
  166. package/dist/core/config/schema/improve-processes.js +0 -6
  167. package/dist/core/config/schema/improve.js +21 -3
  168. package/dist/core/config/schema/index-config.js +8 -15
  169. package/dist/core/config/schema/output.js +4 -1
  170. package/dist/core/config/schema/setup.js +9 -18
  171. package/dist/core/config/schema/sources-bundles.js +49 -33
  172. package/dist/core/config/schema/workflow.js +3 -3
  173. package/dist/core/env-secret-ref.js +76 -46
  174. package/dist/core/errors.js +18 -12
  175. package/dist/core/events.js +46 -128
  176. package/dist/core/file-change.js +6 -5
  177. package/dist/core/fs-txn.js +83 -7
  178. package/dist/core/git-message.js +2 -2
  179. package/dist/core/improve-result.js +1 -100
  180. package/dist/core/lesson-lint.js +1 -17
  181. package/dist/core/logs-db.js +2 -1
  182. package/dist/core/migration-operation.js +16 -0
  183. package/dist/core/mutation-target.js +78 -0
  184. package/dist/core/parse.js +4 -1
  185. package/dist/core/paths.js +17 -20
  186. package/dist/core/recognition-util.js +12 -14
  187. package/dist/core/redaction.js +34 -0
  188. package/dist/core/standards/resolve-standards-context.js +2 -14
  189. package/dist/core/standards/resolve-stash-standards.js +2 -2
  190. package/dist/core/standards/resolve-type-conventions.js +2 -2
  191. package/dist/core/state/migrations.js +41 -18
  192. package/dist/core/state-db.js +5 -14
  193. package/dist/core/structured.js +1 -1
  194. package/dist/core/subprocess.js +6 -4
  195. package/dist/core/text-truncation.js +9 -5
  196. package/dist/core/type-presentation.js +3 -3
  197. package/dist/core/warn.js +0 -3
  198. package/dist/core/write-source.js +771 -95
  199. package/dist/indexer/bundle-identity-guard.js +3 -2
  200. package/dist/indexer/db/graph-db.js +0 -24
  201. package/dist/indexer/ensure-index.js +1 -0
  202. package/dist/indexer/graph/graph-boost.js +9 -34
  203. package/dist/indexer/graph/graph-extraction.js +8 -5
  204. package/dist/indexer/index-writer-lock.js +53 -17
  205. package/dist/indexer/index-written-assets.js +16 -22
  206. package/dist/indexer/indexer.js +497 -239
  207. package/dist/indexer/installations.js +14 -96
  208. package/dist/indexer/passes/dir-staleness.js +16 -9
  209. package/dist/indexer/passes/memory-inference.js +11 -9
  210. package/dist/indexer/passes/metadata.js +113 -47
  211. package/dist/indexer/scan/doc-to-entry.js +38 -1
  212. package/dist/indexer/scan/drain-dir.js +13 -23
  213. package/dist/indexer/search/db-search.js +99 -54
  214. package/dist/indexer/search/fts-query.js +47 -24
  215. package/dist/indexer/search/ranking-contributors.js +42 -20
  216. package/dist/indexer/search/ranking.js +18 -99
  217. package/dist/indexer/search/search-fields.js +7 -2
  218. package/dist/indexer/search/search-source.js +82 -93
  219. package/dist/indexer/usage/usage-events.js +0 -89
  220. package/dist/indexer/walk/file-context.js +2 -1
  221. package/dist/indexer/walk/matchers.js +30 -43
  222. package/dist/indexer/walk/path-resolver.js +7 -2
  223. package/dist/indexer/walk/walker.js +38 -12
  224. package/dist/integrations/agent/builders.js +0 -6
  225. package/dist/integrations/agent/config.js +2 -2
  226. package/dist/integrations/agent/detect.js +49 -19
  227. package/dist/integrations/agent/engine-fallback.js +76 -0
  228. package/dist/integrations/agent/profiles.js +14 -0
  229. package/dist/integrations/agent/prompts.js +12 -8
  230. package/dist/integrations/agent/runner-dispatch.js +4 -2
  231. package/dist/integrations/agent/runner.js +0 -1
  232. package/dist/integrations/agent/spawn.js +5 -6
  233. package/dist/integrations/github.js +1 -1
  234. package/dist/integrations/harnesses/aider/agent-builder.js +6 -4
  235. package/dist/integrations/harnesses/amazonq/agent-builder.js +7 -4
  236. package/dist/integrations/harnesses/claude/session-log.js +0 -10
  237. package/dist/integrations/harnesses/codex/agent-builder.js +5 -2
  238. package/dist/integrations/harnesses/copilot/agent-builder.js +5 -3
  239. package/dist/integrations/harnesses/gemini/agent-builder.js +5 -3
  240. package/dist/integrations/harnesses/index.js +3 -7
  241. package/dist/integrations/harnesses/opencode/agent-builder.js +21 -2
  242. package/dist/integrations/harnesses/opencode/session-log.js +0 -15
  243. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +13 -4
  244. package/dist/integrations/harnesses/openhands/agent-builder.js +9 -6
  245. package/dist/integrations/harnesses/pi/agent-builder.js +6 -4
  246. package/dist/integrations/lockfile.js +101 -6
  247. package/dist/integrations/session-logs/index.js +3 -28
  248. package/dist/llm/client.js +136 -100
  249. package/dist/llm/embedders/remote.js +13 -5
  250. package/dist/llm/feature-gate.js +4 -12
  251. package/dist/llm/graph-extract.js +5 -11
  252. package/dist/llm/memory-infer.js +144 -1
  253. package/dist/llm/metadata-enhance.js +5 -7
  254. package/dist/llm/structured-call.js +1 -1
  255. package/dist/llm/usage-persist.js +26 -5
  256. package/dist/llm/usage-telemetry.js +25 -2
  257. package/dist/output/cli-hints.js +1 -2
  258. package/dist/output/context.js +22 -7
  259. package/dist/output/format-exempt.js +80 -0
  260. package/dist/output/generic-render.js +259 -0
  261. package/dist/output/render-registry.js +57 -0
  262. package/dist/output/renderers.js +14 -36
  263. package/dist/output/shapes/curate.js +10 -1
  264. package/dist/output/shapes/events.js +12 -7
  265. package/dist/output/shapes/helpers.js +56 -83
  266. package/dist/output/shapes/migrate.js +8 -0
  267. package/dist/output/shapes/passthrough.js +7 -41
  268. package/dist/output/shapes/proposal/producer.js +15 -7
  269. package/dist/output/shapes.js +2 -9
  270. package/dist/output/text/{init.js → bundle-create.js} +3 -1
  271. package/dist/output/text/bundle-show.js +7 -0
  272. package/dist/output/text/command-format.js +164 -96
  273. package/dist/output/text/env.js +1 -3
  274. package/dist/output/text/events.js +8 -7
  275. package/dist/output/text/health-format.js +103 -0
  276. package/dist/output/text/health.js +7 -0
  277. package/dist/output/text/helpers.js +10 -8
  278. package/dist/output/text/lint-format.js +43 -0
  279. package/dist/output/text/{save.js → lint.js} +2 -2
  280. package/dist/output/text/migrate.js +88 -0
  281. package/dist/output/text/proposal/producer.js +4 -2
  282. package/dist/output/text/proposal-format.js +44 -72
  283. package/dist/output/text/registry-commands.js +1 -2
  284. package/dist/output/text/show-directives.js +15 -7
  285. package/dist/output/text/status-list.js +32 -0
  286. package/dist/output/text/sync.js +5 -0
  287. package/dist/output/text/workflow-format.js +24 -203
  288. package/dist/output/text/workflow.js +1 -7
  289. package/dist/output/text.js +16 -17
  290. package/dist/registry/factory.js +4 -6
  291. package/dist/registry/origin-resolve.js +16 -27
  292. package/dist/registry/providers/skills-sh.js +3 -3
  293. package/dist/registry/providers/static-index.js +13 -23
  294. package/dist/registry/resolve.js +42 -7
  295. package/dist/registry/semver.js +34 -84
  296. package/dist/runtime.js +2 -23
  297. package/dist/scripts/akm-migrate-node.js +60290 -0
  298. package/dist/scripts/akm-migrate.js +59628 -0
  299. package/dist/setup/detect.js +42 -15
  300. package/dist/setup/registry-stash-loader.js +2 -2
  301. package/dist/setup/setup.js +236 -136
  302. package/dist/setup/steps/connection.js +7 -9
  303. package/dist/setup/steps/platforms.js +9 -9
  304. package/dist/setup/steps/semantic.js +15 -3
  305. package/dist/setup/steps/sources.js +12 -13
  306. package/dist/setup/steps/stashdir.js +2 -3
  307. package/dist/setup/steps/tasks.js +237 -120
  308. package/dist/sources/freshness.js +1 -1
  309. package/dist/sources/provider-factory.js +11 -17
  310. package/dist/sources/providers/filesystem.js +2 -3
  311. package/dist/sources/providers/git-install.js +278 -34
  312. package/dist/sources/providers/git-provider.js +25 -23
  313. package/dist/sources/providers/git-stash.js +395 -106
  314. package/dist/sources/providers/git.js +2 -2
  315. package/dist/sources/providers/npm.js +16 -19
  316. package/dist/sources/providers/provider-utils.js +7 -4
  317. package/dist/sources/providers/sync-from-ref.js +3 -9
  318. package/dist/sources/providers/website.js +6 -1
  319. package/dist/sources/resolve.js +6 -5
  320. package/dist/sources/snapshot-fetchers/bluesky.js +146 -0
  321. package/dist/sources/snapshot-fetchers/content-extract.js +566 -0
  322. package/dist/sources/snapshot-fetchers/fetcher-util.js +41 -0
  323. package/dist/sources/snapshot-fetchers/github.js +100 -0
  324. package/dist/sources/snapshot-fetchers/host-guard.js +291 -0
  325. package/dist/sources/snapshot-fetchers/registry.js +17 -1
  326. package/dist/sources/snapshot-fetchers/robots.js +348 -0
  327. package/dist/sources/snapshot-fetchers/rss.js +282 -0
  328. package/dist/sources/snapshot-fetchers/secret-seam.js +42 -0
  329. package/dist/sources/snapshot-fetchers/website-ingest.js +566 -268
  330. package/dist/sources/snapshot-fetchers/x.js +910 -0
  331. package/dist/storage/database.js +7 -0
  332. package/dist/storage/engines/sqlite-migrations.js +23 -111
  333. package/dist/storage/managed-db.js +2 -2
  334. package/dist/storage/repositories/canaries-repository.js +1 -1
  335. package/dist/storage/repositories/events-repository.js +27 -11
  336. package/dist/storage/repositories/improve-runs-repository.js +6 -12
  337. package/dist/storage/repositories/index-connection.js +17 -6
  338. package/dist/storage/repositories/index-entries-repository.js +151 -240
  339. package/dist/storage/repositories/index-entry-mapper.js +15 -11
  340. package/dist/storage/repositories/index-fts-repository.js +5 -2
  341. package/dist/storage/repositories/index-llm-cache-repository.js +0 -1
  342. package/dist/storage/repositories/index-meta-repository.js +2 -3
  343. package/dist/storage/repositories/index-schema.js +10 -25
  344. package/dist/storage/repositories/index-utility-repository.js +15 -28
  345. package/dist/storage/repositories/index-vec-repository.js +6 -1
  346. package/dist/storage/repositories/outcome-repository.js +119 -0
  347. package/dist/storage/repositories/proposals-repository.js +296 -59
  348. package/dist/storage/repositories/registry-cache.js +19 -0
  349. package/dist/storage/repositories/salience-repository.js +172 -0
  350. package/dist/storage/repositories/task-history-repository.js +15 -13
  351. package/dist/storage/repositories/workflow-runs-repository.js +52 -40
  352. package/dist/tasks/backends/cron.js +105 -15
  353. package/dist/tasks/backends/index.js +1 -1
  354. package/dist/tasks/backends/launchd.js +85 -38
  355. package/dist/tasks/backends/schtasks.js +135 -15
  356. package/dist/tasks/embedded.js +56 -40
  357. package/dist/tasks/parser.js +7 -157
  358. package/dist/tasks/resolve-akm-bin.js +137 -59
  359. package/dist/tasks/runner.js +79 -42
  360. package/dist/tasks/scheduler-invocation.js +220 -10
  361. package/dist/tasks/schema.js +24 -1
  362. package/dist/tasks/task-id.js +1 -3
  363. package/dist/tasks/validator.js +20 -6
  364. package/dist/workflows/authoring/authoring.js +94 -143
  365. package/dist/workflows/authoring/scope-key.js +1 -1
  366. package/dist/workflows/exec/frozen-judge.js +28 -2
  367. package/dist/workflows/exec/native-executor.js +77 -57
  368. package/dist/workflows/exec/param-secrets.js +9 -9
  369. package/dist/workflows/exec/run-workflow.js +133 -79
  370. package/dist/workflows/exec/step-work.js +219 -346
  371. package/dist/{migrate-storage-node.mjs → workflows/exec/unit-dispatch.js} +1 -5
  372. package/dist/workflows/ir/compile.js +141 -270
  373. package/dist/workflows/ir/freeze.js +40 -30
  374. package/dist/workflows/ir/params.js +135 -11
  375. package/dist/workflows/ir/plan-hash.js +1 -1
  376. package/dist/workflows/ir/schema.js +25 -26
  377. package/dist/workflows/parser.js +872 -307
  378. package/dist/workflows/program/expressions.js +20 -208
  379. package/dist/workflows/program/schema.js +7 -10
  380. package/dist/workflows/renderer.js +95 -68
  381. package/dist/workflows/resource-limits.js +2 -0
  382. package/dist/workflows/runtime/checkin.js +3 -3
  383. package/dist/workflows/runtime/plan-classifier.js +16 -75
  384. package/dist/workflows/runtime/runs.js +186 -127
  385. package/dist/workflows/runtime/unit-checkin.js +1 -1
  386. package/dist/workflows/runtime/unit-phases.js +2 -2
  387. package/dist/workflows/runtime/workflow-asset-loader.js +232 -83
  388. package/dist/workflows/schema.js +1 -11
  389. package/dist/workflows/validate-summary.js +30 -36
  390. package/dist/workflows/validator.js +21 -62
  391. package/docs/README.md +68 -0
  392. package/docs/migration/README.md +8 -0
  393. package/docs/migration/release-notes/0.7.0.md +11 -11
  394. package/docs/migration/release-notes/0.9.0.md +208 -27
  395. package/docs/migration/v0.7-to-v0.8.md +46 -47
  396. package/docs/migration/v0.8-to-v0.9.md +564 -208
  397. package/docs/migration/v0.9.0-troubleshooting.md +561 -0
  398. package/docs/reference/README.md +12 -0
  399. package/docs/reference/cli.md +2253 -0
  400. package/docs/reference/configuration.md +358 -0
  401. package/docs/reference/data-and-telemetry.md +105 -42
  402. package/docs/reference/workflows.md +647 -0
  403. package/package.json +22 -11
  404. package/schemas/akm-asset-envelope.json +93 -0
  405. package/schemas/akm-config.json +81 -128
  406. package/schemas/akm-workflow.json +74 -73
  407. package/dist/assets/tasks/core/backup.yml +0 -5
  408. package/dist/assets/tasks/graph-refresh-weekly.yml +0 -10
  409. package/dist/cli/config-migrate.js +0 -1806
  410. package/dist/cli/config-validate.js +0 -41
  411. package/dist/commands/backup-cli.js +0 -56
  412. package/dist/commands/bundle/bundle-cli.js +0 -68
  413. package/dist/commands/bundle/bundle.js +0 -219
  414. package/dist/commands/graph/graph-cli.js +0 -124
  415. package/dist/commands/graph/graph.js +0 -489
  416. package/dist/commands/improve/extract-watch.js +0 -140
  417. package/dist/commands/mv-cli.js +0 -1221
  418. package/dist/commands/sources/history.js +0 -201
  419. package/dist/commands/tasks/default-tasks.js +0 -186
  420. package/dist/core/migration-backup.js +0 -1234
  421. package/dist/indexer/usage/unmigrated-vaults-guard.js +0 -95
  422. package/dist/llm/memory-infer-impl.js +0 -138
  423. package/dist/migrate/legacy/config-source-migration.js +0 -223
  424. package/dist/migrate/legacy/content-migration.js +0 -305
  425. package/dist/migrate/legacy/legacy-layout.js +0 -779
  426. package/dist/migrate/legacy/legacy-paths.js +0 -25
  427. package/dist/migrate/legacy/legacy-stash-json.js +0 -72
  428. package/dist/migrate/legacy/proposal-fs-import.js +0 -168
  429. package/dist/migrate/legacy/task-target-ref-migration.js +0 -272
  430. package/dist/migrate/legacy/three-db-cutover.js +0 -841
  431. package/dist/migrate/legacy/workflow-migrations-bodies.js +0 -52
  432. package/dist/migrate/legacy/workflow-migrations-frozen.js +0 -21
  433. package/dist/migrate/legacy-ref-grammar.js +0 -214
  434. package/dist/output/shapes/distill.js +0 -14
  435. package/dist/output/shapes/history.js +0 -11
  436. package/dist/output/text/distill.js +0 -6
  437. package/dist/output/text/enable-disable.js +0 -8
  438. package/dist/output/text/history.js +0 -6
  439. package/dist/registry/build-index.js +0 -382
  440. package/dist/schemas/akm-config.json +0 -4704
  441. package/dist/schemas/akm-task.json +0 -87
  442. package/dist/schemas/akm-workflow.json +0 -372
  443. package/dist/scripts/migrate-storage.js +0 -3816
  444. package/dist/workflows/authoring/workflow-program-template.yaml +0 -31
  445. package/dist/workflows/cli.js +0 -53
  446. package/dist/workflows/exec/brief.js +0 -481
  447. package/dist/workflows/exec/report.js +0 -1460
  448. package/dist/workflows/exec/watch.js +0 -116
  449. package/dist/workflows/program/parser.js +0 -813
  450. package/dist/workflows/program/project.js +0 -104
@@ -15,29 +15,24 @@
15
15
  * via the renderer contributor's throw → metadata-pass skip-with-warning; the
16
16
  * `akm` adapter's synchronous `foldRecognizedMetadata` SWALLOWS the parse
17
17
  * error, so a broken workflow would otherwise silently index. We re-run
18
- * `parseWorkflow` / `parseWorkflowProgram` on drained workflow docs and DROP
18
+ * `parseWorkflow` on drained workflow docs and DROP
19
19
  * the entry with the same `Skipped workflow …` warning
20
20
  * ({@link buildMetadataSkipWarning}), so the workflow-skip summary counts it.
21
21
  * - **Workflow-document side-table (workflow-md only).** The valid parsed
22
22
  * `WorkflowDocument` is handed to the persist layer through the same
23
23
  * `document-cache` side channel the live renderer contributor used, keyed by
24
24
  * the reconstructed entry — so `takeWorkflowDocument(entry)` in the persist
25
- * loop writes the `workflow_documents` row exactly as before. YAML programs
26
- * are re-parsed from disk by the runtime loader, so they carry no cache row
27
- * (mirrors the live path).
25
+ * loop writes the `workflow_documents` row exactly as before.
28
26
  *
29
27
  * `doc.hash` (= sha256 of the file content) is surfaced per recognized file so
30
28
  * the persist layer can populate the `content_hash` column (item 2). It is keyed
31
- * by the file's absolute path (stable across the legacy-sidecar merge,
32
- * which rebuilds entry objects) rather than by the entry object.
29
+ * by the file's absolute path rather than by the entry object.
33
30
  *
34
31
  * Pure of DB/global state beyond the workflow-document side channel; a new leaf
35
32
  * (nothing imports it back), so it joins no import cycle.
36
33
  */
37
34
  import { akmAdapter } from "../../core/adapter/adapters/akm-adapter.js";
38
35
  import { parseWorkflow } from "../../workflows/parser.js";
39
- import { parseWorkflowProgram } from "../../workflows/program/parser.js";
40
- import { WORKFLOW_PROGRAM_RENDERER_NAME } from "../../workflows/program/project.js";
41
36
  import { cacheWorkflowDocument } from "../../workflows/runtime/document-cache.js";
42
37
  import { buildMetadataSkipWarning } from "../passes/metadata.js";
43
38
  import { buildFileContext } from "../walk/file-context.js";
@@ -62,6 +57,10 @@ export function drainDirDocuments(adapter, component, fileContexts) {
62
57
  const doc = adapter.recognize(component, file);
63
58
  if (doc === null)
64
59
  continue;
60
+ if (!doc.conceptId) {
61
+ warnings.push(`Skipped ${file.absPath}: adapter "${adapter.id}" returned no conceptId.`);
62
+ continue;
63
+ }
65
64
  const entry = indexDocumentToStashEntry(doc);
66
65
  // Workflow docs: drop-with-warning if broken; otherwise cache the parsed
67
66
  // markdown document for the persist-time `workflow_documents` write.
@@ -72,8 +71,7 @@ export function drainDirDocuments(adapter, component, fileContexts) {
72
71
  }
73
72
  if (doc.hash !== undefined)
74
73
  hashByFile.set(file.absPath, doc.hash);
75
- if (doc.conceptId !== undefined)
76
- conceptIdByFile.set(file.absPath, doc.conceptId);
74
+ conceptIdByFile.set(file.absPath, doc.conceptId);
77
75
  entries.push(entry);
78
76
  }
79
77
  return { entries, warnings, hashByFile, conceptIdByFile };
@@ -106,20 +104,12 @@ export function recognizeStashEntries(stashRoot, files) {
106
104
  * `null` immediately.
107
105
  */
108
106
  function handleWorkflowDoc(doc, entry, file) {
109
- const renderer = docRenderer(doc);
110
- if (renderer === WORKFLOW_MD_RENDERER) {
111
- const result = parseWorkflow(file.content(), { path: file.relPath });
112
- if (!result.ok)
113
- return workflowDropWarning(file, result.errors);
114
- cacheWorkflowDocument(entry, result.document);
107
+ if (docRenderer(doc) !== WORKFLOW_MD_RENDERER)
115
108
  return null;
116
- }
117
- if (renderer === WORKFLOW_PROGRAM_RENDERER_NAME) {
118
- const result = parseWorkflowProgram(file.content(), { path: file.relPath });
119
- if (!result.ok)
120
- return workflowDropWarning(file, result.errors);
121
- return null;
122
- }
109
+ const result = parseWorkflow(file.content(), { path: file.relPath });
110
+ if (!result.ok)
111
+ return workflowDropWarning(file, result.errors);
112
+ cacheWorkflowDocument(entry, result.document);
123
113
  return null;
124
114
  }
125
115
  /** The winning renderer name the `akm` adapter carries on `documentJson.renderer`, or `undefined`. */
@@ -14,8 +14,9 @@
14
14
  * implementation, not a "local vs. remote" distinction.
15
15
  */
16
16
  import fs from "node:fs";
17
+ import path from "node:path";
17
18
  import { buildActionFromContributors, defaultActionContributors } from "../../core/action-contributors.js";
18
- import { placementTypes } from "../../core/asset/asset-placement.js";
19
+ import { stashDirFor } from "../../core/asset/asset-placement.js";
19
20
  import { displayRef } from "../../core/asset/resolve-ref.js";
20
21
  import { getDbPath } from "../../core/paths.js";
21
22
  import { defaultRendererRegistry } from "../../core/type-presentation.js";
@@ -30,7 +31,7 @@ import { ensureIndex } from "../ensure-index.js";
30
31
  import { collectGraphRelatedHit, loadGraphBoostContext } from "../graph/graph-boost.js";
31
32
  import { isProposedQuality } from "../passes/metadata.js";
32
33
  import { resolveProjectContext } from "../walk/project-context.js";
33
- import { parseRefPrefixQuery, sanitizeFtsQuery } from "./fts-query.js";
34
+ import { parseRefPrefixQuery, parseRetiredTypePrefixQuery, sanitizeFtsQuery } from "./fts-query.js";
34
35
  import { applyRankingRules, combineSearchScores, normalizeFtsScores } from "./ranking.js";
35
36
  import { attachSearchHitAttribution, copySearchHitAttribution, getSearchHitAttribution } from "./search-attribution.js";
36
37
  import { enrichSearchHit } from "./search-hit-enrichers.js";
@@ -44,6 +45,9 @@ import { deriveSemanticProviderFingerprint, getEffectiveSemanticStatus, isSemant
44
45
  * actionable without re-introducing read-triggered reindexing.
45
46
  */
46
47
  const STALE_INDEX_HINT_MS = 7 * 24 * 60 * 60 * 1000;
48
+ function hasIndexedProvenance(entry) {
49
+ return Boolean(entry.itemRef && entry.bundleId && entry.conceptId);
50
+ }
47
51
  function buildStaleIndexHint(db) {
48
52
  try {
49
53
  const builtAt = getMeta(db, "builtAt");
@@ -59,21 +63,19 @@ function buildStaleIndexHint(db) {
59
63
  return undefined;
60
64
  }
61
65
  }
66
+ function indexedProvenance(entry) {
67
+ return { itemRef: entry.itemRef, bundleId: entry.bundleId, conceptId: entry.conceptId };
68
+ }
62
69
  export function buildLocalAction(type, ref, registry = defaultRendererRegistry) {
63
70
  return buildActionFromContributors({ type, ref }, defaultActionContributors(registry)) ?? `akm show ${ref}`;
64
71
  }
65
- function resolveSearchHitRef(entry, refName, source) {
66
- // F4b output-spelling flip: emit the 0.9.0 conceptId grammar for the hit's
67
- // user-facing ref (short conceptId in the primary bundle, `bundle//conceptId`
68
- // for a slug-clean non-default source). `displayRef` prefers the row's stored
69
- // conceptId and derives `stashDir/name` (== the old `stashDir/name` body) when it
70
- // is absent, so this is a pure ref-spelling change over the old output.
72
+ function resolveSearchHitRef(entry, provenance, defaultBundleId) {
71
73
  return displayRef({
72
74
  type: entry.type,
73
- name: refName,
74
- conceptId: entry.conceptId,
75
- bundleId: source?.registryId ?? undefined,
76
- });
75
+ name: entry.name,
76
+ conceptId: provenance.conceptId,
77
+ bundleId: provenance.bundleId,
78
+ }, defaultBundleId);
77
79
  }
78
80
  function resolveSearchHitOrigin(source) {
79
81
  return source?.registryId ?? null;
@@ -130,7 +132,26 @@ export async function searchLocal(input) {
130
132
  }
131
133
  }
132
134
  if (config.semanticSearchMode === "auto" && semanticStatus === "blocked") {
133
- warnings.push("Semantic search is currently blocked. Using keyword search until the semantic backend is healthy again.");
135
+ if (!config.embedding?.endpoint || !config.embedding?.model) {
136
+ // F7/A2: same predicate as the `pending` branch above (#480) — a
137
+ // `blocked` status can outlive the provider config that produced it
138
+ // (e.g. the embedding config was later unset). This is not a fault;
139
+ // there is simply nothing configured to use.
140
+ warnings.push("Semantic search is enabled (semanticSearchMode='auto') but no embedding provider is configured. " +
141
+ 'Either: (a) `akm config set embedding \'{"endpoint":"...","model":"..."}\'`, or ' +
142
+ "(b) `akm config set semanticSearchMode off` to use keyword-only search.");
143
+ }
144
+ else {
145
+ // F7/A2: surface the ACTUAL diagnostic instead of one fixed generic
146
+ // string. `rawStatus.reason`/`rawStatus.message` record why the
147
+ // semantic backend failed (auth, network, a stuck local-model
148
+ // download, …) — read at `readSemanticStatus()` above and, before
149
+ // this fix, discarded here in favor of a message that never varied.
150
+ const detail = rawStatus?.message ?? (rawStatus?.reason ? `reason: ${rawStatus.reason}` : undefined);
151
+ warnings.push(`Semantic search is blocked${detail ? ` (${detail})` : ""}. Using keyword search until the semantic ` +
152
+ "backend is healthy again. Run 'akm index --full' to retry, or " +
153
+ "`akm config set semanticSearchMode off` to silence this warning.");
154
+ }
134
155
  }
135
156
  // Bootstrap-only: builds the index inline when it cannot serve this stash.
136
157
  // Content freshness is the writers' job (indexWrittenAssets + full runs);
@@ -162,9 +183,7 @@ export async function searchLocal(input) {
162
183
  const { hits, embedMs, rankMs, usedSemantic } = await searchDatabase(db, query, searchType, limit, stashDir, allSourceDirs, config, sources, rendererRegistry, filters, includeProposed, beliefFilter, restrictToSources, includeExcludedTypes, disableProjectContext, disableScopedUtility);
163
184
  return {
164
185
  hits,
165
- tip: hits.length === 0
166
- ? "No matching stash assets were found. Try a different query or run 'akm index' to rebuild."
167
- : undefined,
186
+ tip: hits.length === 0 ? emptyResultTip(query) : undefined,
168
187
  warnings: warnings.length > 0 ? warnings : undefined,
169
188
  embedMs,
170
189
  rankMs,
@@ -185,14 +204,15 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
185
204
  // `includeExcludedTypes`. When the config key is ABSENT a built-in default of
186
205
  // ['session'] is applied; an explicit empty list disables exclusion.
187
206
  const defaultExcludes = searchType === "any" && !includeExcludedTypes ? (config.search?.defaultExcludeTypes ?? ["session"]) : [];
188
- // SPEC-4 — ref-prefix queries (`<type>:` / `<type>:<prefix>/`) translate to
189
- // a typed enumeration narrowed by name prefix, instead of degenerating into
190
- // the AND-token FTS query their sanitized form would produce ("memory
191
- // projecta" — noise). The branch fires only on the untyped path: an explicit
192
- // `--type` flag expresses stronger intent and wins. The PARSED type is
193
- // itself explicit intent, so `defaultExcludeTypes` does not apply — a bare
194
- // `session:` enumerates sessions exactly like `--type session` does.
195
- const refPrefix = searchType === "any" ? parseRefPrefixQuery(query, placementTypes()) : null;
207
+ // D4 — conceptId-prefix queries (`memories/projecta/`, `bundle//`,
208
+ // `bundle//skills/`) translate to a deterministic enumeration narrowed by
209
+ // conceptId, instead of degenerating into the AND-token FTS query their
210
+ // sanitized form would produce ("memories projecta" — noise). The branch
211
+ // fires only on the untyped path: an explicit `--type` flag expresses
212
+ // stronger intent and wins. The PREFIX is itself explicit intent, so
213
+ // `defaultExcludeTypes` does not apply — `sessions/` enumerates sessions
214
+ // exactly like `--type session` does, and `bundle//` means the whole bundle.
215
+ const refPrefix = searchType === "any" ? parseRefPrefixQuery(query) : null;
196
216
  // Shared args for the two browse paths below; browse never runs semantic
197
217
  // ranking, so both return usedSemantic: false.
198
218
  const browseArgs = {
@@ -210,13 +230,13 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
210
230
  restrictToSources,
211
231
  };
212
232
  if (refPrefix) {
213
- // Browse path (ref-prefix enumeration).
233
+ // Browse path (conceptId-prefix enumeration).
214
234
  return {
215
235
  ...(await enumerateEntries({
216
236
  ...browseArgs,
217
- typeFilter: refPrefix.type,
218
237
  excludeTypes: [],
219
- namePrefix: refPrefix.namePrefix,
238
+ conceptIdPrefix: refPrefix.conceptIdPrefix,
239
+ ...(refPrefix.bundle !== undefined ? { bundle: refPrefix.bundle } : {}),
220
240
  })),
221
241
  usedSemantic: false,
222
242
  };
@@ -272,7 +292,7 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
272
292
  // not leak into default ('any') results. defaultExcludes is already []
273
293
  // unless this is the untyped path without includeExcludedTypes.
274
294
  excludeTypes: defaultExcludes,
275
- });
295
+ }).filter(hasIndexedProvenance);
276
296
  // ── Scoring Phase ──────────────────────────────────────────────────────
277
297
  // Apply boosts as multiplicative factors (all boosts in a single phase
278
298
  // so that sort order and displayed scores are always consistent).
@@ -298,7 +318,7 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
298
318
  })();
299
319
  // Resolve project-context tokens from the current working directory once
300
320
  // per search invocation. Returns null when running from home dir / /tmp,
301
- // or when the caller has set AKM_DISABLE_PROJECT_CONTEXT=1.
321
+ // or when the caller passed `--no-project-context` (disableProjectContext).
302
322
  const projectContext = disableProjectContext ? null : resolveProjectContext(process.cwd());
303
323
  // Phase 2A / Rec 5: resolve forgetting-curve config and skip the feedback
304
324
  // count query when the boost cannot make a difference (default ≤ 1.0 means
@@ -316,7 +336,8 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
316
336
  ? getPositiveFeedbackCountsByIds(scored.map((item) => item.id))
317
337
  : undefined;
318
338
  // Resolve per-project scope key for scoped utility scoring.
319
- // AKM_DISABLE_SCOPED_UTILITY=1 opts out (e.g. for registry searches or tests).
339
+ // `disableScopedUtility` (wired from `akm search --no-project-context`)
340
+ // opts out (e.g. for registry searches or tests).
320
341
  let scopeKey;
321
342
  try {
322
343
  scopeKey = disableScopedUtility ? undefined : getCurrentWorkflowScopeKey();
@@ -365,9 +386,6 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
365
386
  const displayScore = (s) => Math.round(Math.min(1, Math.max(0, s)) * 10000) / 10000;
366
387
  preFilter.sort((a, b) => displayScore(b.score) - displayScore(a.score) || a.entry.name.localeCompare(b.entry.name));
367
388
  // Deduplicate by file path — keep only the highest-scored entry per file.
368
- // Multiple legacy-sidecar entries can map to the same file (e.g. entries without
369
- // a filename field all collapse to files[0]). Showing the same path/ref
370
- // multiple times clutters results.
371
389
  const deduped = deduplicateByPath(preFilter);
372
390
  // Source → scope → proposed-quality → derived-twin belief inheritance →
373
391
  // belief: the post-candidate filter chain shared with enumerateEntries (see
@@ -396,7 +414,7 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
396
414
  return buildDbHit({
397
415
  entry,
398
416
  path: filePath,
399
- // Round to 4 decimal places
417
+ ...indexedProvenance(ranked),
400
418
  score: Math.round(finalScore * 10000) / 10000,
401
419
  query,
402
420
  rankingMode,
@@ -413,18 +431,33 @@ async function searchDatabase(db, query, searchType, limit, stashDir, allSourceD
413
431
  }));
414
432
  return { embedMs, rankMs, hits, usedSemantic };
415
433
  }
434
+ /**
435
+ * The no-hits tip. A query in the retired `<type>:` / `<type>:<prefix>/` browse
436
+ * grammar gets the conceptId spelling that replaces it: without this it comes
437
+ * back empty and silent, which is the failure D4 removed the grammar to avoid.
438
+ */
439
+ function emptyResultTip(query) {
440
+ const generic = "No matching stash assets were found. Try a different query or run 'akm index' to rebuild.";
441
+ const retired = parseRetiredTypePrefixQuery(query);
442
+ if (!retired)
443
+ return generic;
444
+ const root = stashDirFor(retired.type);
445
+ if (!root)
446
+ return generic;
447
+ return `No matching stash assets were found. The '<type>:' browse grammar was removed in 0.9.0 — use the conceptId spelling: 'akm search "${root}/${retired.rest}"'.`;
448
+ }
416
449
  // ── Enumeration (browse) path ────────────────────────────────────────────────
417
450
  /**
418
451
  * Enumerate index entries without FTS scoring — the browse path shared by
419
- * empty/unsearchable queries and SPEC-4 ref-prefix queries (`<type>:` /
420
- * `<type>:<prefix>/`). Applies the same post-ranking filters as the scored
452
+ * empty/unsearchable queries and D4 conceptId-prefix queries (`memories/`,
453
+ * `bundle//`, `bundle//skills/`). Applies the same post-ranking filters as the scored
421
454
  * path (source narrowing, scope, proposed-quality, belief) before the limit
422
455
  * slice. Hits carry the fixed browse score 1 in type-then-name order — this is
423
456
  * a deterministic listing, not a relevance ranking.
424
457
  */
425
458
  async function enumerateEntries(opts) {
426
459
  const { db, query, sources, config, rendererRegistry, filters, beliefFilter } = opts;
427
- const allEntries = getAllEntries(db, opts.typeFilter, opts.excludeTypes);
460
+ const allEntries = getAllEntries(db, opts.typeFilter, opts.excludeTypes).filter(hasIndexedProvenance);
428
461
  // Explicit listing order: type, then name, then filePath. The underlying
429
462
  // SELECT carries no ORDER BY, so its row order tracks the query plan and the
430
463
  // index-insertion (file-walk) order — both machine-dependent. A browse
@@ -432,11 +465,17 @@ async function enumerateEntries(opts) {
432
465
  allEntries.sort((a, b) => a.entry.type.localeCompare(b.entry.type) ||
433
466
  a.entry.name.localeCompare(b.entry.name) ||
434
467
  a.filePath.localeCompare(b.filePath));
435
- // SPEC-4: narrow to the requested subtree. `startsWith` on the full
436
- // slash-retaining prefix is exact — "projecta/" cannot match a sibling
437
- // "projectalpha/…" scope.
438
- const namePrefix = opts.namePrefix?.toLowerCase() ?? "";
439
- const prefixFiltered = namePrefix.length > 0 ? allEntries.filter((ie) => ie.entry.name.toLowerCase().startsWith(namePrefix)) : allEntries;
468
+ // D4: narrow to the requested bundle and subtree. Matching is against the
469
+ // conceptId — the spelling every emitted `ref` carries — so a ref copied out
470
+ // of search output round-trips back in. `startsWith` on the full
471
+ // slash-retaining prefix is exact: "memories/projecta/" cannot match a
472
+ // sibling "memories/projectalpha/…" scope.
473
+ const bundle = opts.bundle?.toLowerCase();
474
+ const bundleFiltered = bundle === undefined ? allEntries : allEntries.filter((ie) => ie.bundleId.toLowerCase() === bundle);
475
+ const conceptIdPrefix = opts.conceptIdPrefix?.toLowerCase() ?? "";
476
+ const prefixFiltered = conceptIdPrefix.length > 0
477
+ ? bundleFiltered.filter((ie) => ie.conceptId.toLowerCase().startsWith(conceptIdPrefix))
478
+ : bundleFiltered;
440
479
  // Deduplicate by file path — multiple entries can share the same file
441
480
  const seenFilePaths = new Set();
442
481
  const uniqueEntries = prefixFiltered.filter((ie) => {
@@ -464,6 +503,9 @@ async function enumerateEntries(opts) {
464
503
  const hits = await Promise.all(selected.map((ie) => buildDbHit({
465
504
  entry: ie.entry,
466
505
  path: ie.filePath,
506
+ itemRef: ie.itemRef,
507
+ bundleId: ie.bundleId,
508
+ conceptId: ie.conceptId,
467
509
  score: 1,
468
510
  query,
469
511
  rankingMode: "fts",
@@ -502,10 +544,10 @@ async function enumerateEntries(opts) {
502
544
  */
503
545
  function applyEntryFilters(items, opts) {
504
546
  const { filters } = opts;
505
- // Source filter: when the caller narrowed `sources` via `--source <name>`,
547
+ // Source filter: when the caller narrowed `sources` via `--from <name>`,
506
548
  // drop entries whose filePath does not live under any requested source. The
507
549
  // FTS/enumerate index spans every configured source, so without this filter a
508
- // narrowed --source request would still leak results from other sources.
550
+ // narrowed --from request would still leak results from other sources.
509
551
  const sourceFiltered = opts.restrictToSources
510
552
  ? items.filter((item) => findSourceForPath(item.filePath, opts.sources) !== undefined)
511
553
  : items;
@@ -598,7 +640,8 @@ async function tryVecScores(db, query, k, config) {
598
640
  // ── Hit building ────────────────────────────────────────────────────────────
599
641
  export async function buildDbHit(input) {
600
642
  const rendererRegistry = input.rendererRegistry ?? defaultRendererRegistry;
601
- const entryStashDir = findSourceForPath(input.path, input.sources)?.path ?? input.defaultStashDir;
643
+ const absolutePath = path.resolve(input.path);
644
+ const entryStashDir = findSourceForPath(absolutePath, input.sources)?.path ?? input.defaultStashDir;
602
645
  // Quality and confidence boosts are now applied in the main scoring
603
646
  // phase (searchDatabase). buildDbHit receives the already-final score and
604
647
  // passes it through without further multiplication. We still compute the
@@ -611,21 +654,23 @@ export async function buildDbHit(input) {
611
654
  const score = Math.round(input.score * 10000) / 10000;
612
655
  const graphBoost = getSearchHitAttribution(input.attributionSource ?? {})?.graphExtraction?.boost ?? 0;
613
656
  const whyMatched = buildWhyMatched(input.entry, input.query, input.rankingMode, qualityBoost, confidenceBoost, input.utilityBoosted, graphBoost);
614
- const graphHit = input.graphContext ? collectGraphRelatedHit(input.graphContext, input.path) : null;
615
- const source = findSourceForPath(input.path, input.sources);
616
- const ref = resolveSearchHitRef(input.entry, input.entry.name, source);
617
- const editable = isEditable(input.path, input.config);
657
+ const graphHit = input.graphContext ? collectGraphRelatedHit(input.graphContext, absolutePath) : null;
658
+ const source = findSourceForPath(absolutePath, input.sources);
659
+ const defaultBundleId = input.config?.defaultBundle ??
660
+ (source && path.resolve(source.path) === path.resolve(input.defaultStashDir)
661
+ ? (input.bundleId ?? undefined)
662
+ : undefined);
663
+ const ref = resolveSearchHitRef(input.entry, input, defaultBundleId);
664
+ const editable = isEditable(absolutePath, input.config, input.sources);
618
665
  const estimatedTokens = typeof input.entry.fileSize === "number" ? Math.round(input.entry.fileSize / 4) : undefined;
619
666
  const hit = {
620
667
  type: input.entry.type,
621
668
  name: input.entry.name,
622
- path: input.path,
669
+ path: absolutePath,
623
670
  ref,
624
671
  origin: resolveSearchHitOrigin(source),
625
672
  editable,
626
- ...(!editable
627
- ? { editHint: buildEditHint(input.path, input.entry.type, input.entry.name, source?.registryId) }
628
- : {}),
673
+ ...(!editable ? { editHint: buildEditHint(ref) } : {}),
629
674
  description: input.entry.description,
630
675
  tags: input.entry.tags,
631
676
  size: deriveSize(input.entry.fileSize),
@@ -744,7 +789,7 @@ function deduplicateByPath(items) {
744
789
  });
745
790
  }
746
791
  /**
747
- * Exact-match scope filter check. Legacy entries without a `scope` object only
792
+ * Exact-match scope filter check. Entries without a `scope` object only
748
793
  * match when no filter is supplied — which is what the caller guards on
749
794
  * before invoking this helper.
750
795
  */
@@ -52,41 +52,64 @@ export function buildPrefixQuery(ftsQuery) {
52
52
  return prefixTokens.join(" ");
53
53
  }
54
54
  /**
55
- * SPEC-4 — parse a ref-prefix query (`akm search "<type>:<prefix>/"`).
55
+ * D4 — parse a conceptId-prefix browse query.
56
56
  *
57
- * Decides whether a raw query is a typed subtree-enumeration request rather
58
- * than an ordinary keyword search. Matching is deliberately conservative: the
59
- * trimmed query must be EXACTLY
57
+ * Decides whether a raw query is a subtree-enumeration request rather than an
58
+ * ordinary keyword search. Matching is deliberately conservative: the trimmed
59
+ * query must be EXACTLY
60
60
  *
61
- * - `<known-type>:` → enumerate the whole type (namePrefix `""`), or
62
- * - `<known-type>:<prefix>/` → enumerate names under `<prefix>/`.
61
+ * - `<conceptId prefix>/` → that subtree in any bundle,
62
+ * - `<bundle>//` → one bundle entirely,
63
+ * - `<bundle>//<conceptId prefix>/` → that subtree of that bundle.
63
64
  *
64
- * The trailing slash is REQUIRED for a non-empty prefix — and is RETAINED in
65
- * the returned `namePrefix` — so that a plain `entry.name.startsWith(namePrefix)`
66
- * check gives exact `/`-boundary subtree semantics (`"projecta/"` cannot match
67
- * a sibling `projectalpha/…` scope). Bare refs like `memory:a/b` therefore
68
- * stay ordinary searches (resolving one ref is `akm show` territory), and any
69
- * interior whitespace disqualifies (prose mentioning a ref is still prose).
65
+ * The trailing slash is REQUIRED — and is RETAINED in `conceptIdPrefix` — so a
66
+ * plain `conceptId.startsWith(conceptIdPrefix)` check gives exact `/`-boundary
67
+ * subtree semantics (`"projecta/"` cannot match a sibling `projectalpha/…`
68
+ * scope). Bare refs like `memories/a/b` therefore stay ordinary searches
69
+ * (resolving one ref is `akm show` territory), and any interior whitespace
70
+ * disqualifies (prose mentioning a ref is still prose).
70
71
  *
71
- * `knownTypes` is passed in by the caller (e.g. `placementTypes()`) to keep
72
- * this module dependency-free.
72
+ * The prefix matches the conceptId — the same string every emitted `ref`
73
+ * carries — so a ref copied out of search output round-trips back in as a
74
+ * prefix. Nothing here consults a type list: enumeration covers every
75
+ * adapter's items uniformly, which the retired `<type>:` grammar could not do.
73
76
  *
74
- * Returns `null` when the query is not a ref-prefix request.
77
+ * Returns `null` when the query is not a browse request.
75
78
  */
76
- export function parseRefPrefixQuery(query, knownTypes) {
79
+ export function parseRefPrefixQuery(query) {
77
80
  const trimmed = query.trim();
78
81
  if (trimmed.length === 0 || /\s/.test(trimmed))
79
82
  return null;
83
+ const separator = trimmed.indexOf("//");
84
+ if (separator < 0) {
85
+ return trimmed.endsWith("/") ? { conceptIdPrefix: trimmed } : null;
86
+ }
87
+ const bundle = trimmed.slice(0, separator);
88
+ if (bundle.length === 0)
89
+ return null;
90
+ const rest = trimmed.slice(separator + 2);
91
+ if (rest === "")
92
+ return { bundle, conceptIdPrefix: "" };
93
+ if (rest.endsWith("/") && !rest.includes("//"))
94
+ return { bundle, conceptIdPrefix: rest };
95
+ return null;
96
+ }
97
+ /**
98
+ * Recognize the retired `<type>:` / `<type>:<prefix>/` browse grammar so the
99
+ * caller can name the replacement spelling rather than letting the query
100
+ * degrade silently into a keyword search — the exact silent failure D4 removes.
101
+ * Shape recognition only; mapping the type to its conceptId root belongs to the
102
+ * caller, which keeps this module dependency-free.
103
+ */
104
+ export function parseRetiredTypePrefixQuery(query) {
105
+ const trimmed = query.trim();
106
+ if (trimmed.length === 0 || /\s/.test(trimmed) || trimmed.includes("//"))
107
+ return null;
80
108
  const colon = trimmed.indexOf(":");
81
109
  if (colon <= 0)
82
110
  return null;
83
- const type = trimmed.slice(0, colon);
84
- if (!knownTypes.includes(type))
85
- return null;
86
111
  const rest = trimmed.slice(colon + 1);
87
- if (rest === "")
88
- return { type, namePrefix: "" };
89
- if (rest.endsWith("/"))
90
- return { type, namePrefix: rest };
91
- return null;
112
+ if (rest !== "" && !rest.endsWith("/"))
113
+ return null;
114
+ return { type: trimmed.slice(0, colon), rest };
92
115
  }
@@ -51,13 +51,19 @@ const MAX_BOOST_SUM = 3.0;
51
51
  const UTILITY_WEIGHT = 0.5;
52
52
  const UTILITY_MAX_BOOST = 1.5;
53
53
  /**
54
- * R2 — weight of the improve
55
- * loop's `asset_salience.rank_score` in user-facing ranking. Bounded well
56
- * below the utility boost so the composed signal refines, never dominates,
57
- * lexical/semantic relevance. rank_score ∈ [0,1] → boost ∈ [1, 1.2].
54
+ * R2 / #692 — weight of the improve loop's `asset_salience.rank_score`.
55
+ * rank_score ∈ [0,1] → boost ∈ [1, 1.2]. Bounded well below the utility boost
56
+ * so the composed signal would refine, never dominate, lexical/semantic
57
+ * relevance.
58
+ *
59
+ * As of #692 this is NOT wired into default user-facing ranking — see
60
+ * {@link salienceRankingContributor}'s doc comment. Kept exported (with the
61
+ * contributor) for a future gated experiment; do not reintroduce it into
62
+ * {@link defaultUtilityRankingContributors} without a measured
63
+ * curate-golden-bench delta and an explicit config gate.
58
64
  */
59
- const SALIENCE_WEIGHT = 0.2;
60
- const SALIENCE_MAX_BOOST = 1.2;
65
+ export const SALIENCE_WEIGHT = 0.2;
66
+ export const SALIENCE_MAX_BOOST = 1.2;
61
67
  /**
62
68
  * Phase 2A / Rec 5: default recency half-life (days) used when no
63
69
  * `utilityDecayConfig` is supplied to the ranking pipeline. Matches the
@@ -113,7 +119,7 @@ function beliefStateBoost(item) {
113
119
  * a ceilinged one; demoted entries still list (belief FILTERING stays a
114
120
  * separate opt-in axis, `--belief`), and scores already below a ceiling keep
115
121
  * their relative ordering. Ceiling order mirrors the additive-penalty
116
- * severity order pinned in tests/belief-state-phase1a.test.ts:
122
+ * severity order pinned in tests/integration/belief-state-phase1a.test.ts:
117
123
  * deprecated (mildest) > superseded > contradicted > archived.
118
124
  */
119
125
  const BELIEF_STATE_SCORE_CEILINGS = {
@@ -290,7 +296,7 @@ const graphRankingContributor = {
290
296
  *
291
297
  * Memories captured via the hot path (`akm remember`) get a modest additive
292
298
  * boost so they outrank otherwise-equal background-derived memories. Memories
293
- * without `captureMode` (legacy) return 0 and rank exactly as before.
299
+ * without `captureMode` return 0.
294
300
  */
295
301
  const captureModeRankingContributor = {
296
302
  name: "capture-mode-ranking",
@@ -404,7 +410,7 @@ const utilityRankingContributor = {
404
410
  *
405
411
  * Skipped entirely when `projectContext` is absent or has no tokens (e.g.
406
412
  * when running from home dir, /tmp, or when disabled via
407
- * `--no-project-context` / `AKM_DISABLE_PROJECT_CONTEXT=1`).
413
+ * `--no-project-context`).
408
414
  */
409
415
  const projectContextRankingContributor = {
410
416
  name: "project-context-ranking",
@@ -444,15 +450,31 @@ export const defaultRankingContributors = [
444
450
  projectContextRankingContributor,
445
451
  ];
446
452
  /**
447
- * R2 — compose the improve loop's salience core into user-facing ranking.
453
+ * R2 — the improve loop's salience core, as a bounded multiplicative ranking
454
+ * boost. `asset_salience.rank_score` (encoding + outcome + retrieval
455
+ * projection, maintained every improve run) otherwise drives only improve's
456
+ * INTERNAL maintenance selection.
457
+ *
458
+ * **#692 — NOT in {@link defaultUtilityRankingContributors}.** Removed from
459
+ * default user-facing ranking (search/curate): the boost was
460
+ * retrieval-dominated (`w_r = 0.60` in salience.ts, with no source filter, so
461
+ * it double-counted the same `usage_events` the utility EMA contributor
462
+ * already reinforces), warm-started non-zero with no outcome evidence, had
463
+ * zero pack coverage (favoring only self-generated personal assets), and
464
+ * measured as noise on live data (max observed multiplier ×1.071). Removing
465
+ * its default state.db load also deleted a confirmed hot-path defect — see
466
+ * the now-deleted `loadSalienceRankScores` in `ranking.ts` git history.
448
467
  *
449
- * `asset_salience.rank_score` (encoding + outcome + retrieval projection,
450
- * maintained every improve run) previously drove only improve's INTERNAL
451
- * maintenance selection — the "better assets surface more" loop ran solely
452
- * through the utility EMA. This bounded multiplicative boost closes the outer
453
- * loop: usage/outcome-reinforced assets rank higher in `search`/`curate`.
468
+ * `rank_score` itself is UNCHANGED as improve's internal selection signal.
469
+ * This contributor, and its weight/cap constants, stay exported so a FUTURE
470
+ * gated experiment (config-gated, outcome-gated) can wire it back in
471
+ * explicitly — see `RankEntriesOptions.salienceRankScores`'s doc comment in
472
+ * `ranking.ts`. Reachable today only via explicit contributor-list injection
473
+ * (tests / eval harnesses calling `applyUtilityContributors` directly with a
474
+ * list that includes it) — never through `applyRankingRules` / `akm search` /
475
+ * `akm curate`.
454
476
  */
455
- const salienceRankingContributor = {
477
+ export const salienceRankingContributor = {
456
478
  name: "salience-ranking",
457
479
  appliesTo(item, ctx) {
458
480
  const rank = ctx.salienceRankScores?.get(item.id);
@@ -464,10 +486,10 @@ const salienceRankingContributor = {
464
486
  item.score *= Math.min(rawBoost, SALIENCE_MAX_BOOST);
465
487
  },
466
488
  };
467
- export const defaultUtilityRankingContributors = [
468
- utilityRankingContributor,
469
- salienceRankingContributor,
470
- ];
489
+ // #692 — salienceRankingContributor deliberately excluded; see its doc
490
+ // comment. Do not add it back here without a config gate + a measured
491
+ // curate-golden-bench delta justifying it.
492
+ export const defaultUtilityRankingContributors = [utilityRankingContributor];
471
493
  /**
472
494
  * EVAL/DEBUG ONLY — remove named ranking contributors from a list.
473
495
  *