akm-cli 0.9.16 → 0.9.17-alpha.10

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 (403) hide show
  1. package/CHANGELOG.md +2101 -0
  2. package/STABILITY.md +11 -10
  3. package/dist/akm +124 -193
  4. package/dist/akm-migrate +38 -19
  5. package/dist/assets/hints/cli-hints-full.md +6 -7
  6. package/dist/assets/improve-strategies/catchup.json +0 -3
  7. package/dist/assets/improve-strategies/consolidate.json +0 -1
  8. package/dist/assets/improve-strategies/default.json +1 -2
  9. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  10. package/dist/assets/improve-strategies/quick.json +1 -2
  11. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  12. package/dist/assets/improve-strategies/thorough.json +0 -3
  13. package/dist/assets/prompts/consolidate-pair.md +20 -0
  14. package/dist/assets/prompts/consolidate-system.md +4 -11
  15. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  17. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  18. package/dist/assets/templates/html/health.html +3 -5
  19. package/dist/cli/retired-commands.js +1 -1
  20. package/dist/cli/shared.js +6 -2
  21. package/dist/cli/unknown-flags.js +24 -1
  22. package/dist/cli.js +68 -10
  23. package/dist/commands/agent/agent-dispatch.js +1 -1
  24. package/dist/commands/command/command-execution.js +24 -62
  25. package/dist/commands/feedback-cli.js +0 -1
  26. package/dist/commands/health/accept-rate.js +6 -0
  27. package/dist/commands/health/archive-usage.js +92 -0
  28. package/dist/commands/health/checks.js +83 -74
  29. package/dist/commands/health/config-skew.js +38 -0
  30. package/dist/commands/health/data-dir-usage.js +25 -13
  31. package/dist/commands/health/egress.js +54 -0
  32. package/dist/commands/health/html-report.js +1 -42
  33. package/dist/commands/health/improve-metrics.js +136 -591
  34. package/dist/commands/health/md-report.js +1 -6
  35. package/dist/commands/health/plugin-staleness.js +53 -3
  36. package/dist/commands/health/renderers.js +12 -4
  37. package/dist/commands/health/report-view-model.js +14 -120
  38. package/dist/commands/health/types-improve.js +4 -19
  39. package/dist/commands/health/windows.js +64 -74
  40. package/dist/commands/health.js +145 -143
  41. package/dist/commands/improve/consolidate/chunking.js +26 -117
  42. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  43. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  44. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  45. package/dist/commands/improve/consolidate.js +589 -1127
  46. package/dist/commands/improve/content-hash.js +16 -24
  47. package/dist/commands/improve/distill/content-repair.js +18 -100
  48. package/dist/commands/improve/distill-guards.js +20 -81
  49. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  50. package/dist/commands/improve/distill.js +608 -1041
  51. package/dist/commands/improve/eligibility.js +126 -390
  52. package/dist/commands/improve/execution.js +8 -10
  53. package/dist/commands/improve/extract-prompt.js +1 -2
  54. package/dist/commands/improve/extract.js +487 -1046
  55. package/dist/commands/improve/feedback-valence.js +0 -25
  56. package/dist/commands/improve/improve-cli.js +75 -169
  57. package/dist/commands/improve/improve-result-file.js +10 -66
  58. package/dist/commands/improve/improve-strategies.js +52 -4
  59. package/dist/commands/improve/improve-usage-report.js +18 -64
  60. package/dist/commands/improve/improve.js +480 -1074
  61. package/dist/commands/improve/ledger.js +119 -0
  62. package/dist/commands/improve/locks.js +2 -8
  63. package/dist/commands/improve/loop-stages.js +415 -1073
  64. package/dist/commands/improve/memory/derived-ref.js +12 -77
  65. package/dist/commands/improve/memory/memory-belief.js +16 -118
  66. package/dist/commands/improve/memory/memory-improve.js +266 -14
  67. package/dist/commands/improve/outcome-loop.js +28 -156
  68. package/dist/commands/improve/planner.js +5 -15
  69. package/dist/commands/improve/preparation.js +779 -2319
  70. package/dist/commands/improve/proactive-maintenance.js +34 -101
  71. package/dist/commands/improve/reflect-noise.js +104 -280
  72. package/dist/commands/improve/reflect.js +642 -1353
  73. package/dist/commands/improve/retrieval-gate.js +127 -0
  74. package/dist/commands/improve/retrieval-scope.js +92 -0
  75. package/dist/commands/improve/salience.js +41 -240
  76. package/dist/commands/improve/session-asset.js +19 -100
  77. package/dist/commands/improve/stage.js +322 -0
  78. package/dist/commands/lint/base-linter.js +37 -15
  79. package/dist/commands/proposal/drain.js +261 -578
  80. package/dist/commands/proposal/proposal-cli.js +19 -20
  81. package/dist/commands/proposal/proposal-types.js +31 -24
  82. package/dist/commands/proposal/proposal.js +38 -8
  83. package/dist/commands/proposal/propose.js +134 -160
  84. package/dist/commands/proposal/repository.js +1097 -1394
  85. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  86. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  87. package/dist/commands/proposal/validators/proposals.js +22 -89
  88. package/dist/commands/read/curate.js +105 -462
  89. package/dist/commands/read/knowledge.js +3 -2
  90. package/dist/commands/read/search-cli.js +16 -33
  91. package/dist/commands/read/search.js +17 -23
  92. package/dist/commands/read/show.js +57 -108
  93. package/dist/commands/sources/bundle-cli.js +25 -2
  94. package/dist/commands/sources/bundle-config-ops.js +4 -0
  95. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  96. package/dist/commands/sources/info.js +127 -29
  97. package/dist/commands/sources/installed-stashes.js +197 -746
  98. package/dist/commands/sources/schema-repair.js +98 -129
  99. package/dist/commands/sources/source-add.js +62 -12
  100. package/dist/commands/sources/source-manage.js +9 -2
  101. package/dist/commands/sources/stash-cli.js +24 -4
  102. package/dist/commands/tasks/explain.js +10 -13
  103. package/dist/commands/tasks/tasks-cli.js +12 -13
  104. package/dist/commands/tasks/tasks.js +350 -936
  105. package/dist/commands/tasks/validate.js +26 -24
  106. package/dist/commands/workflow/plan.js +22 -29
  107. package/dist/commands/workflow-cli.js +4 -4
  108. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  109. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  110. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  111. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  112. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  113. package/dist/core/adapter/execution-source.js +17 -29
  114. package/dist/core/asset/asset-placement.js +4 -13
  115. package/dist/core/asset/frontmatter.js +106 -1
  116. package/dist/core/asset/resolve-ref.js +1 -1
  117. package/dist/core/bundle-id.js +42 -5
  118. package/dist/core/bundle-rename.js +285 -0
  119. package/dist/core/config/config-io.js +1 -2
  120. package/dist/core/config/config-schema.js +9 -34
  121. package/dist/core/config/config-walker.js +1 -1
  122. package/dist/core/config/config.js +184 -111
  123. package/dist/core/config/engine-semantics.js +0 -2
  124. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  125. package/dist/core/config/schema/embedding.js +20 -5
  126. package/dist/core/config/schema/engines.js +5 -0
  127. package/dist/core/config/schema/execution.js +1 -1
  128. package/dist/core/config/schema/experimental.js +1 -1
  129. package/dist/core/config/schema/improve-processes.js +54 -125
  130. package/dist/core/config/schema/improve.js +4 -42
  131. package/dist/core/config/schema/index-config.js +9 -48
  132. package/dist/core/config/schema/scheduler.js +12 -12
  133. package/dist/core/config/schema/search.js +6 -22
  134. package/dist/core/env-secret-ref.js +0 -1
  135. package/dist/core/errors.js +8 -9
  136. package/dist/core/file-change.js +13 -5
  137. package/dist/core/file-lock.js +76 -173
  138. package/dist/core/improve-result.js +35 -7
  139. package/dist/core/improve-types.js +0 -1
  140. package/dist/core/logs-db.js +2 -2
  141. package/dist/core/loopback.js +7 -12
  142. package/dist/core/non-task-input.js +20 -0
  143. package/dist/core/parse.js +13 -16
  144. package/dist/core/paths.js +0 -24
  145. package/dist/core/redaction.js +109 -2
  146. package/dist/core/run-lock.js +2 -5
  147. package/dist/core/spawn-env.js +1 -1
  148. package/dist/core/state/migrations.js +123 -61
  149. package/dist/core/state-db-scope.js +2 -4
  150. package/dist/core/state-db.js +126 -692
  151. package/dist/core/time.js +0 -20
  152. package/dist/core/type-presentation.js +1 -9
  153. package/dist/core/write-source.js +294 -1005
  154. package/dist/execution/input-contract.js +1 -1
  155. package/dist/execution/resolved-request.js +135 -689
  156. package/dist/execution/source.js +63 -257
  157. package/dist/execution/target-ref.js +1 -1
  158. package/dist/indexer/bundle-identity-guard.js +2 -2
  159. package/dist/indexer/db/llm-cache.js +2 -2
  160. package/dist/indexer/ensure-index.js +77 -73
  161. package/dist/indexer/index-rebuild-lock.js +3 -11
  162. package/dist/indexer/index-writer-lock.js +8 -17
  163. package/dist/indexer/index-written-assets.js +141 -154
  164. package/dist/indexer/indexer.js +400 -1124
  165. package/dist/indexer/links/declared-links.js +90 -0
  166. package/dist/indexer/materialize-embeddings.js +60 -397
  167. package/dist/indexer/passes/memory-inference.js +96 -90
  168. package/dist/indexer/passes/metadata.js +132 -219
  169. package/dist/indexer/read-preflight.js +0 -7
  170. package/dist/indexer/scan/doc-to-entry.js +2 -3
  171. package/dist/indexer/scan/drain-dir.js +1 -1
  172. package/dist/indexer/search/db-search.js +190 -590
  173. package/dist/indexer/search/fts-query.js +30 -41
  174. package/dist/indexer/search/ranking.js +28 -154
  175. package/dist/indexer/search/search-attribution.js +12 -32
  176. package/dist/indexer/search/search-fields.js +11 -15
  177. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  178. package/dist/indexer/search/search-source.js +1 -4
  179. package/dist/indexer/usage/usage-events.js +36 -7
  180. package/dist/indexer/walk/walker.js +3 -4
  181. package/dist/integrations/agent/engine-fallback.js +23 -40
  182. package/dist/integrations/agent/engine-resolution.js +93 -183
  183. package/dist/integrations/agent/execution.js +507 -0
  184. package/dist/integrations/agent/model-map.js +28 -156
  185. package/dist/integrations/agent/request-lowering.js +66 -141
  186. package/dist/integrations/agent/runner-dispatch.js +143 -321
  187. package/dist/integrations/agent/runner.js +54 -14
  188. package/dist/integrations/lockfile.js +53 -101
  189. package/dist/llm/client.js +18 -6
  190. package/dist/llm/embedders/deterministic.js +2 -3
  191. package/dist/llm/embedders/profile.js +71 -0
  192. package/dist/llm/embedders/remote.js +11 -17
  193. package/dist/llm/feature-gate.js +0 -8
  194. package/dist/llm/index-passes.js +3 -5
  195. package/dist/llm/memory-infer.js +1 -2
  196. package/dist/llm/structured-call.js +5 -24
  197. package/dist/output/generic-render.js +23 -11
  198. package/dist/output/html-render.js +13 -10
  199. package/dist/output/render-registry.js +3 -32
  200. package/dist/output/shapes/helpers.js +25 -38
  201. package/dist/output/shapes/passthrough.js +1 -9
  202. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  203. package/dist/output/text/command-format.js +69 -31
  204. package/dist/output/text/helpers.js +1 -1
  205. package/dist/output/text/migrate.js +5 -14
  206. package/dist/output/text/proposal-format.js +48 -3
  207. package/dist/output/text/show-format.js +13 -17
  208. package/dist/output/text/workflow-format.js +0 -32
  209. package/dist/output/text.js +2 -0
  210. package/dist/registry/factory.js +4 -19
  211. package/dist/registry/network.js +66 -220
  212. package/dist/registry/providers/index.js +0 -2
  213. package/dist/registry/providers/skills-sh.js +3 -14
  214. package/dist/registry/providers/static-index.js +24 -26
  215. package/dist/registry/resolve.js +55 -131
  216. package/dist/scripts/akm-migrate-node.js +42948 -92369
  217. package/dist/scripts/akm-migrate.js +42935 -92354
  218. package/dist/setup/registry-stash-loader.js +4 -13
  219. package/dist/setup/semantic-assets.js +3 -44
  220. package/dist/setup/setup.js +1 -1
  221. package/dist/setup/steps/connection.js +5 -6
  222. package/dist/setup/steps/platforms.js +2 -2
  223. package/dist/setup/steps/tasks.js +25 -15
  224. package/dist/sources/provider-factory.js +17 -18
  225. package/dist/sources/providers/filesystem.js +2 -3
  226. package/dist/sources/providers/git-install.js +7 -1
  227. package/dist/sources/providers/git-provider.js +0 -3
  228. package/dist/sources/providers/git-stash.js +83 -21
  229. package/dist/sources/providers/npm.js +2 -4
  230. package/dist/sources/providers/provider-utils.js +5 -10
  231. package/dist/sources/providers/website.js +0 -2
  232. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  233. package/dist/sources/website-url.js +2 -2
  234. package/dist/storage/database.js +9 -35
  235. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  236. package/dist/storage/repositories/index-connection.js +39 -72
  237. package/dist/storage/repositories/index-entries-repository.js +131 -129
  238. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  239. package/dist/storage/repositories/index-entry-schema.js +101 -268
  240. package/dist/storage/repositories/index-fts-repository.js +86 -256
  241. package/dist/storage/repositories/index-links-repository.js +143 -0
  242. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  243. package/dist/storage/repositories/index-meta-repository.js +6 -4
  244. package/dist/storage/repositories/index-schema.js +257 -325
  245. package/dist/storage/repositories/index-utility-repository.js +8 -29
  246. package/dist/storage/repositories/index-vec-repository.js +133 -414
  247. package/dist/storage/repositories/outcome-repository.js +2 -1
  248. package/dist/storage/repositories/proposals-repository.js +104 -1
  249. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  250. package/dist/storage/repositories/salience-repository.js +1 -19
  251. package/dist/storage/repositories/task-history-repository.js +26 -4
  252. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  253. package/dist/storage/sqlite-migrations.js +136 -0
  254. package/dist/storage/sqlite-pragmas.js +11 -9
  255. package/dist/storage/sqlite-transaction.js +170 -0
  256. package/dist/storage/state-db-integrity.js +130 -0
  257. package/dist/tasks/activation-config.js +134 -62
  258. package/dist/tasks/backends/cron.js +191 -302
  259. package/dist/tasks/backends/exec-utils.js +2 -5
  260. package/dist/tasks/backends/launchd.js +141 -748
  261. package/dist/tasks/backends/schtasks.js +119 -623
  262. package/dist/tasks/prepare/prepare-support.js +5 -15
  263. package/dist/tasks/prepare/prepare.js +0 -2
  264. package/dist/tasks/resolve-akm-bin.js +20 -79
  265. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  266. package/dist/tasks/run/load-task.js +1 -1
  267. package/dist/tasks/scheduler-binding.js +20 -238
  268. package/dist/tasks/scheduler-invocation.js +136 -244
  269. package/dist/tasks/scheduler-lock.js +53 -0
  270. package/dist/tasks/scheduler-sync.js +368 -679
  271. package/dist/tasks/source/parse-task-source.js +55 -9
  272. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  273. package/dist/tasks/source/task-to-v4.js +464 -88
  274. package/dist/workflows/authoring/authoring.js +3 -12
  275. package/dist/workflows/compile.js +211 -0
  276. package/dist/workflows/concurrency-policy.js +13 -74
  277. package/dist/workflows/exec/child-invocation.js +3 -17
  278. package/dist/workflows/exec/child-workflow.js +32 -141
  279. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  280. package/dist/workflows/exec/environment.js +98 -0
  281. package/dist/workflows/exec/exec-unit.js +33 -140
  282. package/dist/workflows/exec/frozen-judge.js +7 -59
  283. package/dist/workflows/exec/native-executor.js +82 -341
  284. package/dist/workflows/exec/param-secrets.js +29 -47
  285. package/dist/workflows/exec/run-workflow.js +154 -387
  286. package/dist/workflows/exec/scheduler.js +9 -36
  287. package/dist/workflows/exec/step-work.js +127 -430
  288. package/dist/workflows/exec/unit-dispatch.js +11 -63
  289. package/dist/workflows/exec/unit-writer.js +8 -52
  290. package/dist/workflows/exec/worktree.js +39 -273
  291. package/dist/workflows/freeze/child-output-references.js +4 -15
  292. package/dist/workflows/freeze/environment.js +99 -92
  293. package/dist/workflows/freeze/freeze.js +172 -0
  294. package/dist/workflows/freeze/step-values.js +19 -21
  295. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  296. package/dist/workflows/freeze/targets/command.js +10 -33
  297. package/dist/workflows/freeze/targets/script.js +5 -12
  298. package/dist/workflows/freeze/targets/shell.js +3 -6
  299. package/dist/workflows/freeze/targets/task.js +25 -80
  300. package/dist/workflows/freeze/task-bindings.js +20 -67
  301. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  302. package/dist/workflows/ir/params.js +6 -51
  303. package/dist/workflows/ir/plan-hash.js +2 -34
  304. package/dist/workflows/parser.js +140 -43
  305. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  306. package/dist/workflows/renderer.js +36 -69
  307. package/dist/workflows/resource-limits.js +12 -120
  308. package/dist/workflows/runtime/agent-identity.js +8 -40
  309. package/dist/workflows/runtime/run-outputs.js +3 -6
  310. package/dist/workflows/runtime/run-plan.js +316 -0
  311. package/dist/workflows/runtime/runs.js +48 -200
  312. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  313. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  314. package/dist/workflows/validate-summary.js +2 -7
  315. package/docs/integration/bundling-akm.md +49 -42
  316. package/docs/migration/README.md +1 -0
  317. package/docs/migration/release-notes/0.9.17.md +43 -0
  318. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  319. package/docs/reference/cli.md +232 -135
  320. package/docs/reference/configuration.md +71 -57
  321. package/docs/reference/data-and-telemetry.md +20 -21
  322. package/docs/reference/tasks.md +105 -39
  323. package/docs/reference/workflow-schema.md +14 -18
  324. package/docs/reference/workflows.md +6 -9
  325. package/package.json +1 -1
  326. package/schemas/akm-config.json +115 -738
  327. package/schemas/akm-workflow.json +1 -0
  328. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  329. package/dist/assets/prompts/contradiction-judge.md +0 -33
  330. package/dist/assets/prompts/graph-extract-system.md +0 -1
  331. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  332. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  333. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  334. package/dist/commands/health/advisories.js +0 -150
  335. package/dist/commands/health/metrics.js +0 -329
  336. package/dist/commands/health/surfaces.js +0 -102
  337. package/dist/commands/improve/anti-collapse.js +0 -83
  338. package/dist/commands/improve/collapse-detector.js +0 -432
  339. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  340. package/dist/commands/improve/consolidate/merge.js +0 -149
  341. package/dist/commands/improve/distill/promote-memory.js +0 -291
  342. package/dist/commands/improve/distill/quality-gate.js +0 -337
  343. package/dist/commands/improve/eval-cases.js +0 -52
  344. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  345. package/dist/commands/improve/proposal-envelope.js +0 -31
  346. package/dist/commands/improve/run-context.js +0 -123
  347. package/dist/commands/improve/shared.js +0 -31
  348. package/dist/commands/improve/source-identity.js +0 -28
  349. package/dist/commands/improve/triage.js +0 -96
  350. package/dist/commands/proposal/drain-policies.js +0 -151
  351. package/dist/commands/sources/update-transaction.js +0 -220
  352. package/dist/core/action-contributors.js +0 -28
  353. package/dist/core/config/config-version-shim.js +0 -101
  354. package/dist/core/fs-txn.js +0 -405
  355. package/dist/core/lexical-score.js +0 -25
  356. package/dist/core/maintenance-barrier.js +0 -167
  357. package/dist/execution/executable-identity.js +0 -105
  358. package/dist/execution/guarded-source.js +0 -427
  359. package/dist/indexer/db/graph-db.js +0 -444
  360. package/dist/indexer/graph/graph-boost.js +0 -427
  361. package/dist/indexer/graph/graph-dedup.js +0 -95
  362. package/dist/indexer/graph/graph-extraction.js +0 -1108
  363. package/dist/indexer/search/name-match.js +0 -35
  364. package/dist/indexer/search/ranking-contributors.js +0 -515
  365. package/dist/indexer/search/ranking-types.js +0 -4
  366. package/dist/indexer/walk/project-context.js +0 -192
  367. package/dist/integrations/agent/execution-cascade.js +0 -566
  368. package/dist/integrations/agent/execution-definitions.js +0 -202
  369. package/dist/integrations/agent/execution-lowering.js +0 -841
  370. package/dist/integrations/agent/execution-preparation.js +0 -98
  371. package/dist/integrations/agent/inline-execution.js +0 -74
  372. package/dist/llm/graph-extract.js +0 -728
  373. package/dist/llm/metadata-enhance.js +0 -96
  374. package/dist/registry/create-provider-registry.js +0 -29
  375. package/dist/registry/pinned-request-helper.js +0 -247
  376. package/dist/registry/pinned-transport.js +0 -717
  377. package/dist/sources/providers/index.js +0 -14
  378. package/dist/storage/engines/sqlite-migrations.js +0 -271
  379. package/dist/storage/repositories/canaries-repository.js +0 -107
  380. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  381. package/dist/storage/repositories/registry-cache.js +0 -113
  382. package/dist/tasks/scheduler-sync-preview.js +0 -52
  383. package/dist/tasks/source/task-to-v3.js +0 -507
  384. package/dist/workflows/freeze/resolve-steps.js +0 -86
  385. package/dist/workflows/freeze/source-freeze.js +0 -64
  386. package/dist/workflows/ir/compile.js +0 -321
  387. package/dist/workflows/ir/environment-v4.js +0 -330
  388. package/dist/workflows/ir/freeze-v4.js +0 -153
  389. package/dist/workflows/ir/schema-v4.js +0 -745
  390. package/dist/workflows/ir/schema.js +0 -354
  391. package/dist/workflows/program/schema.js +0 -77
  392. package/dist/workflows/runtime/checkin.js +0 -57
  393. package/dist/workflows/runtime/plan-classifier.js +0 -196
  394. package/dist/workflows/runtime/unit-checkin.js +0 -45
  395. package/dist/workflows/runtime/unit-phases.js +0 -20
  396. package/dist/workflows/schema.js +0 -4
  397. package/dist/workflows/source-ir/compile.js +0 -200
  398. package/dist/workflows/source-ir/program.js +0 -50
  399. package/dist/workflows/source-ir/result.js +0 -26
  400. package/dist/workflows/source-ir/schema.js +0 -786
  401. package/dist/workflows/source-ir/triggers.js +0 -79
  402. package/dist/workflows/source-ir/uses.js +0 -40
  403. package/dist/workflows/validator.js +0 -60
@@ -2,61 +2,57 @@
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
- * index.db schema and version stamps, kept in the
6
- * storage layer. This isolates the one genuinely risky area (schema
7
- * evolution) from the CRUD/FTS/vector queries.
5
+ * index.db schema, kept in the storage layer so schema evolution stays apart
6
+ * from the CRUD/FTS/vector queries.
8
7
  *
9
- * The meta accessors, embedding purge, and vec-availability probe that
10
- * `ensureSchema` leans on live in the sibling `index-meta-repository` /
11
- * `index-vec-repository` modules.
8
+ * `ensureSchema` runs on every writable open and brings an older layout up
9
+ * to date in place: `CREATE ... IF NOT EXISTS`, `ALTER TABLE ... ADD COLUMN`
10
+ * for columns added after a table first shipped, drops of retired derived
11
+ * tables and columns, and one in-place rebuild of the (derived, cheap) FTS
12
+ * table when its layout is older than this release's. It never drops
13
+ * `entries`, `embeddings`, `utility_scores`, or `llm_enrichment_cache` to
14
+ * cross a version boundary; the only from-scratch rebuild is the
15
+ * SQLITE_CORRUPT path in `index-connection.ts`. A layout newer than this
16
+ * release's is refused, naming the upgrade ({@link newerIndexLayoutError}).
17
+ * The one exception is the LLM entity graph (`graph_meta`, `graph_files`,
18
+ * `graph_file_*`), retired in 0.9.17-alpha.9: those tables are dropped
19
+ * unconditionally below (index.db is a regenerable cache, and declared links
20
+ * — `asset_links` — now back `akm show`'s `links` field, which replaced the
21
+ * graph's `related` list).
12
22
  */
23
+ import { createRequire } from "node:module";
24
+ import path from "node:path";
13
25
  import { ConfigError } from "../../core/errors.js";
14
26
  import { warn } from "../../core/warn.js";
15
- import { ensureEmbeddingSalvageTable, salvageEmbeddingsBeforeDiscard } from "./embedding-salvage-repository.js";
16
- import { CANONICAL_ENTRY_SCHEMA_SQL, CANONICAL_INDEX_DB_VERSION, classifyIndexGeneration, isCanonicalIndexGeneration, } from "./index-entry-schema.js";
27
+ import { sha256Hex } from "../../runtime.js";
28
+ import { CANONICAL_ENTRY_SCHEMA_SQL, CANONICAL_INDEX_DB_VERSION, entriesFtsDdl, isContentlessFtsDdl, missingEntryColumns, readTableSql, supportsContentlessDelete, tableExists, } from "./index-entry-schema.js";
29
+ import { rebuildFts } from "./index-fts-repository.js";
30
+ import { rebuildAllEntryLinks } from "./index-links-repository.js";
17
31
  import { getMeta, setMeta } from "./index-meta-repository.js";
18
- import { isVecAvailable, purgeEmbeddings } from "./index-vec-repository.js";
19
32
  // ── Constants ───────────────────────────────────────────────────────────────
20
- // index.db is a regenerable cache. Incompatible entry-schema changes advance
21
- // this generation and discard only derived index tables; durable state remains
22
- // in state.db. Current readers and writers therefore target exactly one schema
23
- // and never carry live compatibility SQL for previous generations.
24
- //
25
- // v20→v21: remove the transitional entry_key/dir_path/stash_dir/entry_json/
26
- // entry_type columns. item_ref is the sole conflict key; document_json is the
27
- // sole stored document projection; bundle provenance and file_path provide the
28
- // current identity and materialized read path.
29
- //
30
- // v21→v22: entry mutations publish FTS synchronously and no dirty queue exists.
31
- // Discard the old derived generation so stale FTS rows and caller-managed dirty
32
- // state cannot cross the mutation-authority boundary.
33
33
  export const DB_VERSION = CANONICAL_INDEX_DB_VERSION;
34
- export const EMBEDDING_DIM = 384;
35
- // #624-P1: graph_files is keyed to (stash_root, file_path, body_hash).
36
- export const GRAPH_SCHEMA_VERSION = 4;
34
+ /** `index_meta` key set when the writable opener migrated the layout; cleared once `akm index` VACUUMs. */
35
+ export const VACUUM_PENDING_META = "vacuumPending";
36
+ /** The layout that added declared links (`asset_links`, #935). */
37
+ const DECLARED_LINKS_LAYOUT = 26;
38
+ /**
39
+ * The refusal for an index a newer akm wrote. Readers and the writable opener
40
+ * both raise it: a newer layout may lack tables or columns this release reads
41
+ * (layout 25 dropped `entries.search_text`), and writing it back at this
42
+ * layout would undo the newer release's migration.
43
+ */
44
+ export function newerIndexLayoutError(storedVersion, dbPath) {
45
+ return new ConfigError(`Index database${dbPath ? ` at ${dbPath}` : ""} was written by a newer akm (layout ${storedVersion}; this akm ` +
46
+ `understands ${DB_VERSION}). Upgrade akm to use this index.`, "INDEX_SCHEMA_INCOMPATIBLE", "Upgrade akm to a version that understands this index layout.");
47
+ }
37
48
  // ── Schema ──────────────────────────────────────────────────────────────────
38
49
  /**
39
50
  * DDL for the `registry_index_cache` table. This table lives in index.db
40
51
  * (managed by this module), so its DDL belongs here next to the `ensureSchema`
41
52
  * that applies it — not in state-db.ts.
42
53
  *
43
- * Created with CREATE TABLE IF NOT EXISTS so it is safe to call inside
44
- * `ensureSchema()`. Caches the result of resolving and fetching remote registry
45
- * stash indexes so `akm search` does not hit the network on every invocation.
46
- *
47
- * Indexed (query) columns:
48
- * registry_url TEXT PK — canonical URL of the registry; cache key.
49
- * fetched_at TEXT — ISO-8601; used to detect stale entries (TTL).
50
- * etag TEXT — HTTP ETag for conditional GET (If-None-Match).
51
- * last_modified TEXT — HTTP Last-Modified for conditional GET.
52
- *
53
- * Non-indexed payload:
54
- * index_json TEXT — JSON blob of the fetched registry index document.
55
- *
56
- * ADD COLUMN extension points (future migrations):
57
- * ALTER TABLE registry_index_cache ADD COLUMN schema_version INTEGER DEFAULT 1;
58
- * ALTER TABLE registry_index_cache ADD COLUMN kit_count INTEGER DEFAULT NULL;
59
- * ALTER TABLE registry_index_cache ADD COLUMN error_message TEXT DEFAULT NULL;
54
+ * Caches the result of resolving and fetching remote registry stash indexes so
55
+ * `akm search` does not hit the network on every invocation.
60
56
  */
61
57
  const REGISTRY_INDEX_CACHE_DDL = `
62
58
  CREATE TABLE IF NOT EXISTS registry_index_cache (
@@ -71,193 +67,207 @@ const REGISTRY_INDEX_CACHE_DDL = `
71
67
  ON registry_index_cache(fetched_at);
72
68
  `;
73
69
  /**
74
- * Create the graph-extraction tables (`graph_meta`/`graph_files`/`graph_file_entities`/
75
- * `graph_file_relations`/`graph_extraction_queue`).
70
+ * An `entries` table missing a required column cannot be read or written by
71
+ * this release (the last such change was v20→v21, which removed the
72
+ * transitional `entry_key`/`dir_path`/... columns and made `item_ref` the
73
+ * key). Recreate only the tables keyed by `entries.id` — their ids are about
74
+ * to be re-minted, so the rows would dangle anyway. The LLM enrichment cache
75
+ * (keyed by ref) is kept. The LLM entity-graph tables are unconditionally
76
+ * dropped elsewhere in this file regardless of this recreation (retired
77
+ * 0.9.17-alpha.9), not kept. The next index run re-walks every source.
76
78
  */
77
- function ensureGraphTables(db) {
78
- db.exec(`
79
- CREATE TABLE IF NOT EXISTS graph_meta (
80
- stash_root TEXT PRIMARY KEY,
81
- schema_version INTEGER NOT NULL,
82
- generated_at TEXT NOT NULL,
83
- considered_files INTEGER NOT NULL DEFAULT 0,
84
- extracted_files INTEGER NOT NULL DEFAULT 0,
85
- entity_count INTEGER NOT NULL DEFAULT 0,
86
- relation_count INTEGER NOT NULL DEFAULT 0,
87
- extraction_coverage REAL NOT NULL DEFAULT 0,
88
- density REAL NOT NULL DEFAULT 0,
89
- extractor_id TEXT,
90
- extraction_run_id TEXT,
91
- model TEXT,
92
- prompt_version TEXT,
93
- batch_size INTEGER,
94
- cache_hits INTEGER NOT NULL DEFAULT 0,
95
- cache_misses INTEGER NOT NULL DEFAULT 0,
96
- truncation_count INTEGER NOT NULL DEFAULT 0,
97
- failure_count INTEGER NOT NULL DEFAULT 0
98
- );
99
-
100
- CREATE TABLE IF NOT EXISTS graph_files (
101
- stash_root TEXT NOT NULL,
102
- file_path TEXT NOT NULL,
103
- file_order INTEGER NOT NULL,
104
- file_type TEXT NOT NULL,
105
- body_hash TEXT NOT NULL,
106
- confidence REAL,
107
- status TEXT NOT NULL DEFAULT 'extracted',
108
- reason TEXT,
109
- extraction_run_id TEXT,
110
- PRIMARY KEY (stash_root, file_path, body_hash)
111
- );
112
-
113
- CREATE UNIQUE INDEX IF NOT EXISTS idx_graph_files_path
114
- ON graph_files(stash_root, file_path);
115
-
116
- CREATE INDEX IF NOT EXISTS idx_graph_files_stash_order
117
- ON graph_files(stash_root, file_order);
118
-
119
- CREATE TABLE IF NOT EXISTS graph_file_entities (
120
- stash_root TEXT NOT NULL,
121
- file_path TEXT NOT NULL,
122
- body_hash TEXT NOT NULL,
123
- entity_order INTEGER NOT NULL,
124
- entity_norm TEXT NOT NULL,
125
- entity TEXT NOT NULL,
126
- PRIMARY KEY (stash_root, file_path, body_hash, entity_order),
127
- FOREIGN KEY (stash_root, file_path, body_hash)
128
- REFERENCES graph_files(stash_root, file_path, body_hash) ON DELETE CASCADE
129
- );
130
-
131
- CREATE INDEX IF NOT EXISTS idx_graph_file_entities_entity_norm
132
- ON graph_file_entities(stash_root, entity_norm);
133
-
134
- CREATE TABLE IF NOT EXISTS graph_file_relations (
135
- stash_root TEXT NOT NULL,
136
- file_path TEXT NOT NULL,
137
- body_hash TEXT NOT NULL,
138
- relation_order INTEGER NOT NULL,
139
- from_entity_norm TEXT NOT NULL,
140
- from_entity TEXT NOT NULL,
141
- to_entity_norm TEXT NOT NULL,
142
- to_entity TEXT NOT NULL,
143
- relation_type TEXT,
144
- confidence REAL,
145
- PRIMARY KEY (stash_root, file_path, body_hash, relation_order),
146
- FOREIGN KEY (stash_root, file_path, body_hash)
147
- REFERENCES graph_files(stash_root, file_path, body_hash) ON DELETE CASCADE
148
- );
149
-
150
- -- #624-P3: lazy graph-extraction queue. Standalone table (NO FK to
151
- -- graph_files — a queued file by definition has no graph row yet).
152
- -- Idempotent on (stash_root, file_path); drained highest-priority-first.
153
- -- CREATE TABLE IF NOT EXISTS is the forward migration (no DB_VERSION bump).
154
- CREATE TABLE IF NOT EXISTS graph_extraction_queue (
155
- stash_root TEXT NOT NULL,
156
- file_path TEXT NOT NULL,
157
- body_hash TEXT NOT NULL,
158
- queued_at TEXT NOT NULL DEFAULT (datetime('now')),
159
- priority INTEGER NOT NULL DEFAULT 0,
160
- PRIMARY KEY (stash_root, file_path)
161
- );
162
-
163
- CREATE INDEX IF NOT EXISTS idx_graph_extraction_queue_drain
164
- ON graph_extraction_queue(stash_root, priority DESC, queued_at);
165
- `);
79
+ function ensureEntriesLayout(db) {
80
+ if (!tableExists(db, "entries"))
81
+ return;
82
+ const missing = missingEntryColumns(db);
83
+ if (missing.length === 0)
84
+ return;
85
+ warn(`Index database entries table predates the ${missing.join(", ")} column${missing.length === 1 ? "" : "s"} — ` +
86
+ "recreating the entries-keyed tables (entries, full-text, embeddings, utility scores); the " +
87
+ "LLM enrichment cache is kept. The next index run re-walks every source.");
88
+ db.transaction(() => {
89
+ for (const table of [
90
+ "entries_fts",
91
+ "entry_fragments",
92
+ "asset_links",
93
+ "embeddings",
94
+ "utility_scores_scoped",
95
+ "utility_scores",
96
+ "index_dir_state",
97
+ "entries",
98
+ ]) {
99
+ db.exec(`DROP TABLE IF EXISTS ${table}`);
100
+ }
101
+ db.exec("DELETE FROM index_meta WHERE key IN ('builtAt', 'hasEmbeddings')");
102
+ })();
166
103
  }
167
104
  /**
168
- * Cross the incompatible entry-schema boundary by discarding the derived index
169
- * generation. No row conversion or dual-schema compatibility is attempted:
170
- * the next index run rebuilds entries, FTS, embeddings, utility aggregates,
171
- * graph extraction, and enrichment caches from current sources/state.
105
+ * Drop the sqlite-vec mirror of `embeddings` (`entries_vec`, layout 24 and
106
+ * earlier); vectors are searched from `embeddings` alone. Dropping a vec0
107
+ * table needs its module, so an install without sqlite-vec leaves the table
108
+ * in place, unread, and the next writable open that can load it drops it.
172
109
  */
173
- function rebuildIncompatibleIndexGeneration(db) {
174
- const version = getMeta(db, "version");
175
- const hasEntries = tableExists(db, "entries");
176
- if (!hasEntries && version === undefined)
177
- return;
178
- if (isCanonicalIndexGeneration(db))
110
+ function dropVecMirror(db) {
111
+ if (!tableExists(db, "entries_vec"))
179
112
  return;
180
- const classification = classifyIndexGeneration(db);
181
- if (classification.status === "newer") {
182
- throw new ConfigError(`Index database was built by a newer akm (stored generation ${classification.storedVersion ?? "unknown"}; ` +
183
- `this binary understands generation ${CANONICAL_INDEX_DB_VERSION}). Refusing to modify it — upgrade akm ` +
184
- "to use this index.", "INDEX_SCHEMA_INCOMPATIBLE", "Upgrade akm to a version that understands this index generation.");
185
- }
186
- warn(`Index database generation ${classification.storedVersion ?? "unknown"} is older than this akm's generation ` +
187
- `${CANONICAL_INDEX_DB_VERSION} — rebuilding the derived index (entries, FTS, embeddings, graph tables, ` +
188
- "utility scores, and the LLM enrichment cache). This re-walks and re-indexes every source on the next run.");
189
- let vecResetPending = false;
190
113
  try {
191
- db.exec("DROP TABLE IF EXISTS entries_vec");
114
+ createRequire(import.meta.url)("sqlite-vec").load(db);
115
+ db.exec("DROP TABLE entries_vec");
192
116
  }
193
117
  catch {
194
- // A vec0 table cannot be dropped while sqlite-vec is unavailable. It does
195
- // not reference entries, so leave a marker and drop it on the first later
196
- // open where the extension is available.
197
- vecResetPending = true;
118
+ // sqlite-vec is not loadable here.
119
+ }
120
+ }
121
+ /**
122
+ * Layout 25 keeps a hash of each entry's embedding input (`embed_hash`)
123
+ * instead of the text (`search_text`, layout 24 and earlier); the text is
124
+ * derived from `document_json` when the entry is embedded. The hash is taken
125
+ * from the stored text, so every vector stays valid until its entry's text
126
+ * changes. One transaction: a crash leaves `search_text` for the next
127
+ * writable open.
128
+ */
129
+ function replaceSearchTextWithHash(db) {
130
+ if (!tableHasColumn(db, "entries", "search_text"))
131
+ return;
132
+ db.transaction(() => {
133
+ ensureColumn(db, "entries", "embed_hash", "TEXT");
134
+ const page = db.prepare("SELECT id, search_text FROM entries WHERE id > ? ORDER BY id LIMIT 500");
135
+ const update = db.prepare("UPDATE entries SET embed_hash = ? WHERE id = ?");
136
+ let afterId = -1;
137
+ for (;;) {
138
+ const rows = page.all(afterId);
139
+ if (rows.length === 0)
140
+ break;
141
+ afterId = rows[rows.length - 1].id;
142
+ for (const row of rows)
143
+ update.run(sha256Hex(row.search_text), row.id);
144
+ }
145
+ db.exec("ALTER TABLE entries DROP COLUMN search_text");
146
+ })();
147
+ }
148
+ /**
149
+ * Bring `entries_fts` to the contentless layout, rebuilding it from `entries`
150
+ * when it is missing or still carries the content-bearing layout older
151
+ * releases wrote (the one-time v23→v24 migration). One transaction: a crash
152
+ * mid-rebuild leaves the old table in place and the next writable open
153
+ * retries. A SQLite without `contentless_delete` keeps (or gets) the
154
+ * content-bearing layout instead.
155
+ */
156
+ function ensureFtsLayout(db) {
157
+ const contentless = supportsContentlessDelete(db);
158
+ const sql = readTableSql(db, "entries_fts");
159
+ if (sql !== null && isContentlessFtsDdl(sql) === contentless)
160
+ return;
161
+ const entryCount = Number(db.prepare("SELECT COUNT(*) AS n FROM entries").get().n);
162
+ if (entryCount > 0) {
163
+ warn(`Rebuilding the full-text index for ${entryCount} entr${entryCount === 1 ? "y" : "ies"} ` +
164
+ "(embeddings, utility scores, and the LLM enrichment cache are kept).");
198
165
  }
199
166
  db.transaction(() => {
200
- // #955: copy embeddings about to be discarded wholesale into
201
- // `embedding_salvage` (keyed by content hash + the fingerprint they were
202
- // generated under) BEFORE dropping `embeddings`, in the same transaction
203
- // as the drop, so the copy and the discard commit or roll back together.
204
- // The next embedding pass hands salvaged vectors back to unchanged
205
- // content instead of re-embedding the whole corpus after this bump.
206
- salvageEmbeddingsBeforeDiscard(db);
207
- db.exec("DROP TABLE IF EXISTS graph_file_relations");
208
- db.exec("DROP TABLE IF EXISTS graph_file_entities");
209
- db.exec("DROP TABLE IF EXISTS graph_files");
210
- db.exec("DROP TABLE IF EXISTS graph_extraction_queue");
211
- db.exec("DROP TABLE IF EXISTS graph_meta");
212
- db.exec("DROP TABLE IF EXISTS entries_fts_dirty");
213
- db.exec("DROP TABLE IF EXISTS entry_fragments_fts");
214
- db.exec("DROP TABLE IF EXISTS entry_fragments");
215
167
  db.exec("DROP TABLE IF EXISTS entries_fts");
216
- db.exec("DROP TABLE IF EXISTS embeddings");
217
- db.exec("DROP TABLE IF EXISTS utility_scores_scoped");
218
- db.exec("DROP TABLE IF EXISTS utility_scores");
219
- db.exec("DROP TABLE IF EXISTS llm_enrichment_cache");
220
- db.exec("DROP TABLE IF EXISTS index_dir_state");
221
- db.exec("DROP TABLE IF EXISTS entries");
222
- db.exec("DELETE FROM index_meta");
223
- // embedding_salvage is deliberately absent from the drop list above —
224
- // it is the ONE piece of derived state a generation rebuild must not
225
- // discard.
168
+ db.exec(entriesFtsDdl(contentless));
169
+ rebuildFts(db);
226
170
  })();
227
- if (vecResetPending)
228
- setMeta(db, "vecResetPending", "1");
229
171
  }
230
- export function ensureSchema(db, embeddingDim) {
231
- // Create meta table first so we can check version
172
+ /**
173
+ * Layout 26 stores declared links (#935). Every relation an older layout
174
+ * indexed already sits in `document_json`, so the links are derived from there
175
+ * in place, with no file read. The exception is a workflow's step targets and
176
+ * a task's target, which no earlier layout stored: the directories holding
177
+ * workflows and tasks lose their incremental cursor, so the next `akm index`
178
+ * re-reads those and nothing else. One transaction.
179
+ */
180
+ function migrateToDeclaredLinks(db) {
181
+ db.transaction(() => {
182
+ rebuildAllEntryLinks(db);
183
+ const rows = db
184
+ .prepare("SELECT DISTINCT file_path FROM entries WHERE type IN ('workflow', 'task')")
185
+ .all();
186
+ const forget = db.prepare("DELETE FROM index_dir_state WHERE dir_path = ?");
187
+ for (const dir of new Set(rows.map((row) => path.dirname(row.file_path))))
188
+ forget.run(dir);
189
+ })();
190
+ }
191
+ function tableHasColumn(db, table, column) {
192
+ const columns = db.prepare(`PRAGMA table_info(${table})`).all();
193
+ return columns.some((existing) => existing.name === column);
194
+ }
195
+ /** `ALTER TABLE ... ADD COLUMN` for a column added after the table first shipped. Idempotent. */
196
+ function ensureColumn(db, table, column, type) {
197
+ if (tableHasColumn(db, table, column))
198
+ return false;
199
+ db.exec(`ALTER TABLE ${table} ADD COLUMN ${column} ${type}`);
200
+ return true;
201
+ }
202
+ export function ensureSchema(db) {
232
203
  db.exec(`
233
204
  CREATE TABLE IF NOT EXISTS index_meta (
234
205
  key TEXT PRIMARY KEY,
235
206
  value TEXT NOT NULL
236
207
  );
237
208
  `);
238
- // #955: created before the generation-rebuild check below so a discard
239
- // has somewhere to copy vectors to. Additive-only — it carries no bearing
240
- // on the `entries` generation fingerprint (`hasCanonicalEntrySchema`), so
241
- // adding it does not require a `CANONICAL_INDEX_DB_VERSION` bump.
242
- ensureEmbeddingSalvageTable(db);
243
- rebuildIncompatibleIndexGeneration(db);
209
+ const storedVersion = Number(getMeta(db, "version") ?? 0);
210
+ if (storedVersion > DB_VERSION)
211
+ throw newerIndexLayoutError(storedVersion);
212
+ ensureEntriesLayout(db);
213
+ const hadFragmentSource = tableExists(db, "entry_fragments");
244
214
  db.exec(CANONICAL_ENTRY_SCHEMA_SQL);
245
- // Workflow source is compiled directly into source IR at each command
246
- // boundary. The former workflow_documents cache duplicated that IR in a
247
- // second persisted representation and was never used by current execution.
248
- // index.db is derived state, so remove the obsolete table on every open.
215
+ replaceSearchTextWithHash(db);
216
+ // Retired derived tables: the workflow IR cache, the pre-v22 FTS dirty
217
+ // queue, the #955 embedding salvage staging table (embeddings now carry
218
+ // their model per row, so nothing is copied aside and reused), the
219
+ // fragment FTS table search stopped reading (layout 24 and earlier), the
220
+ // per-project scoped utility table (IR-7a: it shipped, but no code ever read
221
+ // or wrote a row), and the lazy graph-extraction queue (extraction runs only
222
+ // in improve).
249
223
  db.exec("DROP TABLE IF EXISTS workflow_documents");
250
- // BLOB-based embedding storage (always available, no sqlite-vec needed)
224
+ db.exec("DROP TABLE IF EXISTS entries_fts_dirty");
225
+ db.exec("DROP TABLE IF EXISTS embedding_salvage");
226
+ db.exec("DROP TABLE IF EXISTS entry_fragments_fts");
227
+ db.exec("DROP TABLE IF EXISTS utility_scores_scoped");
228
+ db.exec("DROP TABLE IF EXISTS graph_extraction_queue");
229
+ // The LLM entity graph, retired in 0.9.17-alpha.9: declared links
230
+ // (`asset_links`) now back `akm show`'s `links` field (which replaced the
231
+ // graph's `related` list) and curate's support refs (#935), and the
232
+ // navigation eval measured vector kNN beating the graph's `related` list
233
+ // by 0.157 P@5. `graph_files` stands in for the whole set — all four
234
+ // tables are only ever created and dropped together. Gated on it (rather
235
+ // than the unconditional `DROP TABLE IF EXISTS` pattern used above) so
236
+ // this reclaim runs once: after the first writable open drops these
237
+ // tables, every later open finds `graph_files` already gone and skips the
238
+ // no-op DROPs and the repeat VACUUM flag below. An older release's
239
+ // `CREATE TABLE IF NOT EXISTS` still recreates them (empty) if it ever
240
+ // opens this index again — a later open here would then drop them again.
241
+ const hadGraphTables = tableExists(db, "graph_files");
242
+ if (hadGraphTables) {
243
+ db.exec("DROP TABLE IF EXISTS graph_meta");
244
+ db.exec("DROP TABLE IF EXISTS graph_files");
245
+ db.exec("DROP TABLE IF EXISTS graph_file_entities");
246
+ db.exec("DROP TABLE IF EXISTS graph_file_relations");
247
+ }
248
+ // One float32 BLOB per entry, searched by an exact scan
249
+ // (index-vec-repository.ts). `model` is the provider fingerprint the vector was generated under
250
+ // (`deriveSemanticProviderFingerprint`); the embedding pass re-embeds only
251
+ // rows whose model differs from the configured one. NULL means the row
252
+ // predates model tracking and is trusted as the current model.
251
253
  db.exec(`
252
254
  CREATE TABLE IF NOT EXISTS embeddings (
253
255
  id INTEGER PRIMARY KEY,
254
256
  embedding BLOB NOT NULL,
257
+ model TEXT,
255
258
  FOREIGN KEY (id) REFERENCES entries(id)
256
259
  );
257
260
  `);
258
- // usage_events lives in state.db. utility_scores remains a regenerable
259
- // index.db cache.
260
- // Utility scores table (aggregated per-entry utility metrics)
261
+ if (ensureColumn(db, "embeddings", "model", "TEXT")) {
262
+ // Rows written before model tracking were generated under the fingerprint
263
+ // the last pass recorded; label them so a later model change re-embeds
264
+ // them instead of trusting them forever.
265
+ const fingerprint = getMeta(db, "embeddingFingerprint");
266
+ if (fingerprint)
267
+ db.prepare("UPDATE embeddings SET model = ? WHERE model IS NULL").run(fingerprint);
268
+ }
269
+ // Utility scores (aggregated per-entry utility metrics) — a regenerable
270
+ // cache recomputed from state.db's usage_events on every index run.
261
271
  db.exec(`
262
272
  CREATE TABLE IF NOT EXISTS utility_scores (
263
273
  entry_id INTEGER PRIMARY KEY,
@@ -269,20 +279,6 @@ export function ensureSchema(db, embeddingDim) {
269
279
  updated_at TEXT NOT NULL DEFAULT (datetime('now')),
270
280
  FOREIGN KEY (entry_id) REFERENCES entries(id) ON DELETE CASCADE
271
281
  );
272
- `);
273
- // Per-project scoped utility scores — tracks usage per (entry, cwd-anchor)
274
- // so assets useful in project A don't pollute rankings in project B.
275
- // The global utility_scores table is preserved as a fallback / cold-start aid.
276
- db.exec(`
277
- CREATE TABLE IF NOT EXISTS utility_scores_scoped (
278
- entry_id INTEGER NOT NULL,
279
- scope_key TEXT NOT NULL,
280
- utility REAL NOT NULL DEFAULT 0,
281
- last_used_at INTEGER NOT NULL,
282
- PRIMARY KEY (entry_id, scope_key)
283
- );
284
- CREATE INDEX IF NOT EXISTS idx_utility_scores_scoped_entry_id
285
- ON utility_scores_scoped(entry_id);
286
282
  `);
287
283
  db.exec(`
288
284
  CREATE TABLE IF NOT EXISTS index_dir_state (
@@ -291,16 +287,18 @@ export function ensureSchema(db, embeddingDim) {
291
287
  file_mtime_max_ms REAL NOT NULL,
292
288
  reason TEXT NOT NULL,
293
289
  updated_at TEXT NOT NULL,
294
- row_count INTEGER
290
+ row_count INTEGER,
291
+ index_variant TEXT
295
292
  );
296
293
  `);
297
- ensureIndexDirStateRowCountColumn(db);
298
- // LLM enrichment result cache. Stores a SHA-256 body hash and the JSON
299
- // result for each asset so that subsequent `akm index --enrich` runs can
300
- // skip the LLM call when the body hasn't changed. The cache is keyed by
301
- // a stable asset_ref string (e.g. the absolute file path for graph/memory
302
- // passes, or `itemRef:passId` for the metadata-enhance pass).
303
- // Entries are cleaned up when assets are removed or --re-enrich is used.
294
+ // #900 (`row_count`) and the adapter variant were added after the table's
295
+ // first release. Pre-existing rows keep NULL until their directory is next
296
+ // drained.
297
+ ensureColumn(db, "index_dir_state", "row_count", "INTEGER");
298
+ ensureColumn(db, "index_dir_state", "index_variant", "TEXT");
299
+ // LLM enrichment result cache, keyed by a stable asset_ref string (the
300
+ // absolute file path of the memory-inference pass) plus the body hash the
301
+ // result was produced for.
304
302
  db.exec(`
305
303
  CREATE TABLE IF NOT EXISTS llm_enrichment_cache (
306
304
  asset_ref TEXT NOT NULL,
@@ -314,110 +312,44 @@ export function ensureSchema(db, embeddingDim) {
314
312
  CREATE INDEX IF NOT EXISTS idx_llm_cache_updated
315
313
  ON llm_enrichment_cache(updated_at);
316
314
  `);
317
- // Graph extraction tables — schema v4 ((stash_root, file_path, body_hash) PK).
318
- //
319
- // graph_files is self-keyed on (stash_root, file_path, body_hash) and is NO
320
- // LONGER tied to entries.id. This is the #624-P1 win: deleting and
321
- // re-inserting an entries row during a reindex no longer cascade-wipes the
322
- // extracted graph — as long as the file's body_hash is unchanged, the graph
323
- // data survives. body_hash is part of the PK so a content change yields a
324
- // distinct key; a UNIQUE index on (stash_root, file_path) still enforces
325
- // exactly one graph_files row per path (delete-then-insert on a hash change).
326
- //
327
- // graph_file_entities and graph_file_relations carry (stash_root, file_path,
328
- // body_hash) and declare a composite FK -> graph_files ON DELETE CASCADE so
329
- // child rows are removed when a graph_files row is replaced.
330
- //
331
- ensureGraphTables(db);
332
- // If a generation rebuild could not drop a vec0 table while the extension
333
- // was unavailable, finish that reset as soon as vec0 can be loaded again.
334
- if (isVecAvailable(db) && getMeta(db, "vecResetPending") === "1") {
335
- db.exec("DROP TABLE IF EXISTS entries_vec");
336
- setMeta(db, "vecResetPending", "0");
337
- }
338
- // sqlite-vec table
339
- //
340
- // Dimension contract:
341
- // - When `embeddingDim` is `undefined`, the caller did NOT request a
342
- // specific dim. Do not touch `index_meta.embeddingDim` and do not run
343
- // the dim-change wipe — fall back to the stored dim (or the static
344
- // default) only when we have to materialise the vec table for the
345
- // first time. Without this guard, registry-side and other dim-unaware
346
- // `openDatabase()` callers would silently overwrite the dim-aware
347
- // improve/index value and oscillate the stored dim.
348
- // - When `embeddingDim` is a number, the caller explicitly asked for
349
- // that dim and owns the dim-change/backup/wipe semantics.
350
- const dimExplicit = embeddingDim !== undefined;
351
- const requestedDim = embeddingDim ?? (Number(getMeta(db, "embeddingDim")) || EMBEDDING_DIM);
352
- const effectiveDim = Number.isInteger(requestedDim) && requestedDim > 0 ? requestedDim : EMBEDDING_DIM;
353
- if (effectiveDim !== requestedDim) {
354
- warn(`Invalid embedding dimension ${requestedDim} — falling back to the default (${EMBEDDING_DIM}).`);
315
+ // Metadata-enhance retired (RS-D, 0.9.17-alpha.9): its rows were the only
316
+ // ones keyed by the default empty cache_variant (memory inference writes
317
+ // `memory-inference-v2`), so this is safe to run unconditionally on every
318
+ // writable open. The table
319
+ // itself stays — memory inference still reads it.
320
+ db.exec("DELETE FROM llm_enrichment_cache WHERE cache_variant = ''");
321
+ // The graph-extraction cache variant is retired along with the tables
322
+ // above; its rows would otherwise sit unread forever. Gated the same way,
323
+ // on the same one-time flag, so a rerun does not re-scan the cache table
324
+ // for rows that are already gone.
325
+ if (hadGraphTables) {
326
+ db.exec("DELETE FROM llm_enrichment_cache WHERE cache_variant LIKE 'graph-extraction:%'");
327
+ // The drops and delete above freed real space (measured ~68MB on a
328
+ // representative index): flag it the same way a version-gated layout
329
+ // migration does, since this reclaim is unconditional-on-version but
330
+ // still one-time-per-index (guarded by hadGraphTables above).
331
+ setMeta(db, VACUUM_PENDING_META, "1");
355
332
  }
356
- if (isVecAvailable(db)) {
357
- // Check if stored embedding dimension differs from configured one
358
- if (dimExplicit) {
359
- const storedDim = getMeta(db, "embeddingDim");
360
- if (storedDim && storedDim !== String(effectiveDim)) {
361
- // Stored vectors are incompatible with the new dimension. Drop the vec
362
- // table so the block below recreates it at the new width; the BLOB rows
363
- // go too. Regenerable from markdown — re-embedded by the next index.
364
- purgeEmbeddings(db, { dropVecTable: true });
365
- }
366
- }
367
- const vecExists = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='entries_vec'").get();
368
- if (!vecExists) {
369
- db.exec(`
370
- CREATE VIRTUAL TABLE entries_vec USING vec0(
371
- id INTEGER PRIMARY KEY,
372
- embedding FLOAT[${effectiveDim}]
373
- );
374
- `);
375
- }
376
- if (dimExplicit) {
377
- setMeta(db, "embeddingDim", String(effectiveDim));
378
- }
333
+ dropVecMirror(db);
334
+ // Meta keys only the sqlite-vec mirror read.
335
+ db.exec("DELETE FROM index_meta WHERE key IN ('embeddingDim', 'vecFastPathReady')");
336
+ db.exec(REGISTRY_INDEX_CACHE_DDL);
337
+ ensureFtsLayout(db);
338
+ // An index that had no fragment source table (v22 and earlier) has no safe
339
+ // Markdown for `akm show` to resolve fragment selectors from until each
340
+ // directory is drained again. Clearing the per-directory cursor makes the
341
+ // next run re-read every source; entry ids, embeddings and utility rows
342
+ // stay put.
343
+ if (!hadFragmentSource && tableExists(db, "entries")) {
344
+ db.exec("DELETE FROM index_dir_state");
379
345
  }
380
- else {
381
- // Also purge BLOB embeddings on dimension change (JS fallback path).
382
- // When sqlite-vec is unavailable, entries_vec doesn't exist but the BLOB
383
- // embeddings table still stores vectors. If the configured dimension
384
- // changes, those stored BLOBs become silently incompatible.
385
- if (dimExplicit) {
386
- const storedDim = getMeta(db, "embeddingDim");
387
- if (storedDim && storedDim !== String(effectiveDim)) {
388
- // JS-fallback path: no vec table, just clear the stale BLOB vectors.
389
- purgeEmbeddings(db);
390
- }
391
- setMeta(db, "embeddingDim", String(effectiveDim));
392
- }
346
+ if (storedVersion > 0 && storedVersion < DECLARED_LINKS_LAYOUT && tableExists(db, "entries")) {
347
+ migrateToDeclaredLinks(db);
393
348
  }
394
- // Usage telemetry (usage_events) lives in state.db since Chunk-8 WI-8.3 —
395
- // no longer created here.
396
- // Registry index cache table — caches remote registry index documents so
397
- // `akm search` does not hit the network on every invocation.
398
- db.exec(REGISTRY_INDEX_CACHE_DDL);
399
- // Write the generation stamp only after every required DDL surface exists.
400
- // A crash before this point leaves an unversioned generation that the next
401
- // writable open safely rebuilds instead of admitting a partial v23 index.
349
+ // Migrating an existing layout drops tables and columns; the next `akm index`
350
+ // VACUUMs the pages they leave free (`vacuumIndexDb`, indexer.ts), since a
351
+ // writable open may run inside a caller's transaction, where VACUUM cannot.
352
+ if (storedVersion > 0 && storedVersion < DB_VERSION)
353
+ setMeta(db, VACUUM_PENDING_META, "1");
402
354
  setMeta(db, "version", String(DB_VERSION));
403
355
  }
404
- /**
405
- * Returns true when a table exists in the current database.
406
- */
407
- function tableExists(db, name) {
408
- const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1").get(name);
409
- return row !== undefined && row !== null;
410
- }
411
- /**
412
- * #900: `row_count` was added after the table's first release, so a database
413
- * created before it needs an `ALTER TABLE` (`CREATE TABLE IF NOT EXISTS` only
414
- * shapes a fresh table). Idempotent. Pre-existing rows keep NULL until their
415
- * directory is next drained; index.db is a regenerable cache, so nothing is
416
- * backfilled.
417
- */
418
- function ensureIndexDirStateRowCountColumn(db) {
419
- const columns = db.prepare("PRAGMA table_info(index_dir_state)").all();
420
- if (!columns.some((column) => column.name === "row_count")) {
421
- db.exec("ALTER TABLE index_dir_state ADD COLUMN row_count INTEGER");
422
- }
423
- }