akm-cli 0.9.1 → 0.9.2-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/tasks/core/extract.yml +6 -5
  12. package/dist/assets/tasks/core/improve.yml +6 -5
  13. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  14. package/dist/assets/tasks/core/sync.yml +6 -5
  15. package/dist/assets/tasks/core/version-check.yml +6 -5
  16. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  17. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  21. package/dist/cli/confirm.js +2 -2
  22. package/dist/cli/parse-args.js +3 -24
  23. package/dist/cli/retired-commands.js +1 -1
  24. package/dist/cli/shared.js +2 -2
  25. package/dist/cli.js +11 -9
  26. package/dist/commands/agent/agent-dispatch.js +55 -89
  27. package/dist/commands/agent/contribute-cli.js +12 -45
  28. package/dist/commands/command/builtin-action.js +32 -0
  29. package/dist/commands/command/command-cli.js +99 -0
  30. package/dist/commands/command/command-execution.js +308 -0
  31. package/dist/commands/command/execution-source-loader.js +176 -0
  32. package/dist/commands/command/portable-template.js +60 -0
  33. package/dist/commands/config-cli.js +10 -4
  34. package/dist/commands/env/env.js +4 -2
  35. package/dist/commands/feedback-cli.js +1 -1
  36. package/dist/commands/health/checks.js +241 -29
  37. package/dist/commands/health/html-report.js +0 -14
  38. package/dist/commands/health/report-view-model.js +0 -1
  39. package/dist/commands/health/surfaces.js +6 -7
  40. package/dist/commands/health/types.js +0 -2
  41. package/dist/commands/health.js +63 -18
  42. package/dist/commands/improve/collapse-detector.js +2 -2
  43. package/dist/commands/improve/consolidate.js +251 -214
  44. package/dist/commands/improve/distill/promote-memory.js +71 -34
  45. package/dist/commands/improve/distill/quality-gate.js +17 -5
  46. package/dist/commands/improve/distill.js +232 -155
  47. package/dist/commands/improve/eligibility.js +112 -79
  48. package/dist/commands/improve/execution.js +57 -0
  49. package/dist/commands/improve/extract-cli.js +5 -5
  50. package/dist/commands/improve/extract.js +521 -342
  51. package/dist/commands/improve/improve-strategies.js +43 -14
  52. package/dist/commands/improve/improve.js +249 -29
  53. package/dist/commands/improve/loop-stages.js +11 -17
  54. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  55. package/dist/commands/improve/outcome-loop.js +22 -38
  56. package/dist/commands/improve/planner.js +134 -0
  57. package/dist/commands/improve/preparation.js +730 -409
  58. package/dist/commands/improve/reflect.js +386 -223
  59. package/dist/commands/improve/run-context.js +3 -4
  60. package/dist/commands/improve/salience.js +6 -58
  61. package/dist/commands/improve/session-asset.js +12 -12
  62. package/dist/commands/lint/index.js +101 -29
  63. package/dist/commands/migrate-cli.js +11 -69
  64. package/dist/commands/migration-tool.js +6 -9
  65. package/dist/commands/models-cli.js +27 -0
  66. package/dist/commands/proposal/drain.js +258 -186
  67. package/dist/commands/proposal/proposal-cli.js +32 -10
  68. package/dist/commands/proposal/proposal.js +2 -5
  69. package/dist/commands/proposal/propose.js +192 -172
  70. package/dist/commands/proposal/repository.js +54 -91
  71. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  72. package/dist/commands/read/curate.js +10 -0
  73. package/dist/commands/read/registry-search.js +25 -9
  74. package/dist/commands/read/remember-cli.js +14 -2
  75. package/dist/commands/read/search.js +10 -4
  76. package/dist/commands/read/show.js +139 -153
  77. package/dist/commands/registry-cli.js +16 -7
  78. package/dist/commands/remember.js +33 -18
  79. package/dist/commands/sources/add-cli.js +19 -178
  80. package/dist/commands/sources/bundle-cli.js +15 -3
  81. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  82. package/dist/commands/sources/info.js +2 -1
  83. package/dist/commands/sources/installed-stashes.js +835 -152
  84. package/dist/commands/sources/schema-repair.js +174 -95
  85. package/dist/commands/sources/self-update.js +30 -74
  86. package/dist/commands/sources/source-add.js +3 -5
  87. package/dist/commands/sources/sources-cli.js +2 -15
  88. package/dist/commands/sources/update-transaction.js +220 -0
  89. package/dist/commands/tasks/tasks-cli.js +3 -3
  90. package/dist/commands/tasks/tasks.js +736 -317
  91. package/dist/commands/workflow-cli.js +2 -2
  92. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  93. package/dist/core/adapter/adapters/akm-adapter.js +83 -34
  94. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  95. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  96. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  97. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  98. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  99. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  100. package/dist/core/adapter/adapters/index.js +0 -9
  101. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  102. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  103. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  104. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  105. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  106. package/dist/core/adapter/execution-source.js +308 -0
  107. package/dist/core/adapter/recognize-match.js +36 -13
  108. package/dist/core/adapter/registry.js +0 -9
  109. package/dist/core/asset/stash-meta.js +94 -4
  110. package/dist/core/common.js +6 -11
  111. package/dist/core/config/config-io.js +3 -3
  112. package/dist/core/config/config-schema.js +18 -40
  113. package/dist/core/config/config-sources.js +11 -21
  114. package/dist/core/config/config-walker.js +31 -13
  115. package/dist/core/config/config.js +21 -20
  116. package/dist/core/config/schema/engines.js +8 -7
  117. package/dist/core/config/schema/improve-processes.js +29 -5
  118. package/dist/core/config/schema/primitives.js +1 -23
  119. package/dist/core/config/schema/sources-bundles.js +13 -16
  120. package/dist/core/errors.js +2 -0
  121. package/dist/core/events.js +68 -32
  122. package/dist/core/extra-params.js +1 -0
  123. package/dist/core/improve-result.js +315 -0
  124. package/dist/core/lesson-lint.js +0 -6
  125. package/dist/core/maintenance-barrier.js +4 -4
  126. package/dist/core/network-policy.js +152 -0
  127. package/dist/core/paths.js +1 -1
  128. package/dist/core/recognition-util.js +4 -4
  129. package/dist/core/registry-url.js +456 -0
  130. package/dist/core/state/migrations.js +161 -47
  131. package/dist/core/state-db.js +453 -80
  132. package/dist/core/system-error.js +32 -0
  133. package/dist/core/time.js +2 -12
  134. package/dist/core/write-source.js +0 -18
  135. package/dist/execution/directory-identity.js +52 -0
  136. package/dist/execution/executable-identity.js +107 -0
  137. package/dist/execution/guarded-source.js +398 -0
  138. package/dist/execution/json.js +95 -0
  139. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  140. package/dist/execution/record.js +55 -0
  141. package/dist/execution/resolved-request.js +730 -0
  142. package/dist/execution/source.js +320 -0
  143. package/dist/indexer/bundle-identity-guard.js +5 -4
  144. package/dist/indexer/db/graph-db.js +33 -0
  145. package/dist/indexer/graph/graph-boost.js +3 -4
  146. package/dist/indexer/graph/graph-extraction.js +562 -373
  147. package/dist/indexer/index-written-assets.js +65 -34
  148. package/dist/indexer/indexer.js +443 -222
  149. package/dist/indexer/installations.js +6 -0
  150. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  151. package/dist/indexer/passes/memory-inference.js +227 -174
  152. package/dist/indexer/scan/doc-to-entry.js +7 -9
  153. package/dist/indexer/scan/drain-dir.js +51 -23
  154. package/dist/indexer/search/db-search.js +98 -22
  155. package/dist/indexer/search/search-fields.js +1 -1
  156. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  157. package/dist/indexer/search/search-source.js +7 -16
  158. package/dist/indexer/search/semantic-status.js +10 -1
  159. package/dist/indexer/usage/show-usage.js +105 -0
  160. package/dist/indexer/usage/usage-events.js +7 -2
  161. package/dist/indexer/walk/matchers.js +40 -10
  162. package/dist/indexer/walk/path-resolver.js +5 -2
  163. package/dist/indexer/walk/walker.js +20 -2
  164. package/dist/integrations/agent/builder-shared.js +3 -6
  165. package/dist/integrations/agent/conversation-fallback.js +16 -0
  166. package/dist/integrations/agent/engine-resolution.js +87 -87
  167. package/dist/integrations/agent/execution-cascade.js +566 -0
  168. package/dist/integrations/agent/execution-definitions.js +211 -0
  169. package/dist/integrations/agent/execution-lowering.js +811 -0
  170. package/dist/integrations/agent/execution-preparation.js +67 -0
  171. package/dist/integrations/agent/index.js +0 -2
  172. package/dist/integrations/agent/inline-execution.js +74 -0
  173. package/dist/integrations/agent/model-map.js +515 -0
  174. package/dist/integrations/agent/persona-fallback.js +30 -0
  175. package/dist/integrations/agent/request-lowering.js +186 -0
  176. package/dist/integrations/agent/runner-dispatch.js +230 -37
  177. package/dist/integrations/agent/runner.js +12 -83
  178. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  179. package/dist/integrations/harnesses/aider/index.js +0 -1
  180. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  181. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  182. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  183. package/dist/integrations/harnesses/claude/index.js +1 -5
  184. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  185. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  186. package/dist/integrations/harnesses/codex/index.js +0 -1
  187. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/copilot/index.js +0 -1
  189. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/gemini/index.js +0 -1
  191. package/dist/integrations/harnesses/index.js +4 -44
  192. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  193. package/dist/integrations/harnesses/opencode/index.js +0 -2
  194. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  195. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  196. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  197. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  198. package/dist/integrations/harnesses/openhands/index.js +0 -1
  199. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  200. package/dist/integrations/harnesses/pi/index.js +0 -1
  201. package/dist/integrations/harnesses/shared.js +0 -1
  202. package/dist/integrations/harnesses/types.js +1 -3
  203. package/dist/integrations/lockfile.js +82 -79
  204. package/dist/integrations/session-logs/index.js +6 -17
  205. package/dist/integrations/session-logs/provider-base.js +1 -29
  206. package/dist/llm/client.js +10 -5
  207. package/dist/llm/embedder.js +6 -7
  208. package/dist/llm/embedders/local.js +37 -88
  209. package/dist/llm/embedders/types.js +1 -1
  210. package/dist/llm/graph-extract.js +75 -50
  211. package/dist/llm/index-passes.js +43 -5
  212. package/dist/llm/memory-infer.js +8 -6
  213. package/dist/llm/metadata-enhance.js +5 -3
  214. package/dist/llm/structured-call.js +122 -25
  215. package/dist/output/format-exempt.js +1 -1
  216. package/dist/output/render-registry.js +0 -16
  217. package/dist/output/renderers.js +12 -7
  218. package/dist/output/shapes/curate.js +1 -0
  219. package/dist/output/shapes/helpers.js +10 -2
  220. package/dist/output/shapes/passthrough.js +2 -0
  221. package/dist/output/text/command-format.js +28 -32
  222. package/dist/output/text/health-format.js +1 -29
  223. package/dist/output/text/migrate.js +6 -56
  224. package/dist/output/text/proposal-format.js +16 -1
  225. package/dist/output/text/workflow-format.js +16 -0
  226. package/dist/registry/network.js +279 -0
  227. package/dist/registry/pinned-request-helper.js +247 -0
  228. package/dist/registry/pinned-transport.js +717 -0
  229. package/dist/registry/providers/skills-sh.js +18 -6
  230. package/dist/registry/providers/static-index.js +20 -7
  231. package/dist/registry/resolve.js +53 -28
  232. package/dist/scripts/akm-migrate-node.js +19122 -52218
  233. package/dist/scripts/akm-migrate.js +19064 -51567
  234. package/dist/setup/registry-stash-loader.js +64 -20
  235. package/dist/setup/semantic-assets.js +9 -34
  236. package/dist/setup/setup.js +12 -30
  237. package/dist/setup/source-identity.js +17 -0
  238. package/dist/setup/steps/sources.js +36 -15
  239. package/dist/setup/steps/tasks.js +39 -11
  240. package/dist/sources/providers/git-provider.js +3 -3
  241. package/dist/sources/providers/npm.js +2 -2
  242. package/dist/sources/providers/provider-utils.js +4 -3
  243. package/dist/sources/providers/website.js +11 -7
  244. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  245. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  246. package/dist/sources/website-url.js +73 -0
  247. package/dist/storage/engines/sqlite-migrations.js +81 -26
  248. package/dist/storage/managed-db.js +27 -24
  249. package/dist/storage/repositories/events-repository.js +3 -0
  250. package/dist/storage/repositories/index-connection.js +42 -10
  251. package/dist/storage/repositories/index-entries-repository.js +167 -207
  252. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  253. package/dist/storage/repositories/index-entry-schema.js +255 -0
  254. package/dist/storage/repositories/index-fts-repository.js +10 -10
  255. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  256. package/dist/storage/repositories/index-meta-repository.js +0 -11
  257. package/dist/storage/repositories/index-schema.js +71 -342
  258. package/dist/storage/repositories/index-utility-repository.js +12 -17
  259. package/dist/storage/repositories/index-vec-repository.js +1 -1
  260. package/dist/storage/repositories/proposals-repository.js +4 -127
  261. package/dist/storage/repositories/registry-cache.js +2 -1
  262. package/dist/storage/repositories/task-history-repository.js +20 -40
  263. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  264. package/dist/storage/sqlite-read-snapshot.js +148 -0
  265. package/dist/tasks/backends/cron.js +170 -42
  266. package/dist/tasks/backends/index.js +1 -1
  267. package/dist/tasks/backends/launchd.js +787 -202
  268. package/dist/tasks/backends/schtasks.js +282 -83
  269. package/dist/tasks/embedded.js +7 -7
  270. package/dist/tasks/frozen-script.js +50 -0
  271. package/dist/tasks/resolve-akm-bin.js +5 -1
  272. package/dist/tasks/runner.js +239 -251
  273. package/dist/tasks/runtime-v3.js +281 -0
  274. package/dist/tasks/scheduler-binding.js +272 -0
  275. package/dist/tasks/scheduler-invocation.js +57 -43
  276. package/dist/tasks/scheduler-sync.js +654 -0
  277. package/dist/tasks/source-v3.js +752 -0
  278. package/dist/tasks/standalone-script-entry.js +5 -0
  279. package/dist/tasks/task-id.js +29 -0
  280. package/dist/workflows/authoring/authoring.js +15 -32
  281. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  282. package/dist/workflows/exec/exec-unit.js +7 -28
  283. package/dist/workflows/exec/frozen-judge.js +57 -89
  284. package/dist/workflows/exec/lowering-notices.js +23 -0
  285. package/dist/workflows/exec/native-executor.js +301 -458
  286. package/dist/workflows/exec/param-secrets.js +4 -3
  287. package/dist/workflows/exec/run-workflow.js +26 -32
  288. package/dist/workflows/exec/step-work.js +105 -109
  289. package/dist/workflows/exec/unit-dispatch.js +103 -27
  290. package/dist/workflows/exec/unit-writer.js +3 -3
  291. package/dist/workflows/exec/worktree.js +2 -2
  292. package/dist/workflows/ir/compile.js +86 -72
  293. package/dist/workflows/ir/environment-v4.js +328 -0
  294. package/dist/workflows/ir/freeze-v4.js +122 -0
  295. package/dist/workflows/ir/plan-hash.js +13 -7
  296. package/dist/workflows/ir/schema-v4.js +525 -0
  297. package/dist/workflows/ir/schema.js +25 -284
  298. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  299. package/dist/workflows/parser.js +27 -24
  300. package/dist/workflows/program/schema.js +1 -2
  301. package/dist/workflows/renderer.js +42 -29
  302. package/dist/workflows/resource-limits.js +4 -5
  303. package/dist/workflows/runtime/agent-identity.js +11 -13
  304. package/dist/workflows/runtime/plan-classifier.js +8 -8
  305. package/dist/workflows/runtime/runs.js +27 -43
  306. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  307. package/dist/workflows/source-files.js +373 -0
  308. package/dist/workflows/source-ir/compile.js +196 -0
  309. package/dist/workflows/source-ir/github-yaml.js +577 -0
  310. package/dist/workflows/source-ir/ordering.js +38 -0
  311. package/dist/workflows/source-ir/program.js +50 -0
  312. package/dist/workflows/source-ir/result.js +26 -0
  313. package/dist/workflows/source-ir/schema.js +772 -0
  314. package/dist/workflows/source-ir/semantics.js +242 -0
  315. package/dist/workflows/source-ir/uses.js +14 -0
  316. package/docs/README.md +2 -0
  317. package/docs/migration/README.md +3 -1
  318. package/docs/migration/release-notes/0.9.2.md +55 -0
  319. package/docs/migration/release-notes/README.md +5 -0
  320. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  321. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  322. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  323. package/docs/reference/README.md +1 -0
  324. package/docs/reference/cli.md +230 -98
  325. package/docs/reference/configuration.md +153 -27
  326. package/docs/reference/data-and-telemetry.md +19 -1
  327. package/docs/reference/supported-formats.md +23 -3
  328. package/docs/reference/tasks.md +182 -0
  329. package/docs/reference/workflow-schema.md +91 -40
  330. package/docs/reference/workflows.md +33 -6
  331. package/package.json +10 -6
  332. package/schemas/akm-config.json +372 -216
  333. package/schemas/akm-task.json +324 -80
  334. package/schemas/akm-workflow.json +6 -9
  335. package/dist/core/migration-operation.js +0 -75
  336. package/dist/integrations/agent/model-aliases.js +0 -74
  337. package/dist/tasks/parser.js +0 -380
  338. package/dist/tasks/schema.js +0 -123
  339. package/dist/tasks/validator.js +0 -80
  340. package/dist/workflows/ir/freeze.js +0 -320
  341. package/dist/workflows/runtime/document-cache.js +0 -13
@@ -1,77 +1,22 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
- /**
5
- * index.db schema, version stamps, and targeted migrations, kept in the
6
- * storage layer. This isolates the one genuinely risky area (schema
7
- * evolution) from the CRUD/FTS/vector queries.
8
- *
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.
12
- */
13
- import { bestEffort } from "../../core/best-effort.js";
14
- import { warn } from "../../core/warn.js";
4
+ import { CANONICAL_ENTRY_SCHEMA_SQL, CANONICAL_INDEX_DB_VERSION, isCanonicalIndexGeneration, } from "./index-entry-schema.js";
15
5
  import { getMeta, setMeta } from "./index-meta-repository.js";
16
6
  import { isVecAvailable, purgeEmbeddings } from "./index-vec-repository.js";
17
7
  // ── Constants ───────────────────────────────────────────────────────────────
18
- // NOTE: schema changes are additive. DB_VERSION is a forensic stamp only it
19
- // no longer gates any destructive path (the old nuclear drop-and-rebuild was
20
- // removed; index.db's idempotent CREATE IF NOT EXISTS schema converges any
21
- // older/partial DB forward without dropping data). Graph re-keying uses a
22
- // TARGETED, graph-only migration (migrateGraphFilesSchema) — the model for any
23
- // incompatible change: migrate in place, never wipe the whole index.
24
- //
25
- // v17→v18 (Chunk-5 Step 2, spec §14.4): the `entries` table gains the durable
26
- // bundle-adapter identity/provenance columns — `item_ref` (`<bundle>//<concept
27
- // -id>` canonical stored spelling), `bundle_id`/`component_id`/`concept_id`/
28
- // `adapter_id` provenance, `type` (open token), and `content_hash`/`document
29
- // _json`. They land ADDITIVELY ALONGSIDE the legacy `entry_key`/`dir_path`/
30
- // `stash_dir`/`entry_json`/`entry_type` columns (dev-time transitional shape):
31
- // the writer populates the identity/provenance columns while every reader still
32
- // keys on the legacy columns, so the battery stays green while the reader
33
- // repoint + ref-grammar flip land incrementally. The legacy columns + this
34
- // coexistence are removed once every reader is repointed onto `item_ref`
35
- // (spec §3.3 — single clean shape, no dual read-path). The index is a
36
- // regenerable derived cache, so an `akm index` rebuild repopulates the new
37
- // columns on any DB opened at an older version.
8
+ // index.db is a regenerable cache. Incompatible entry-schema changes advance
9
+ // this generation and discard only derived index tables; durable state remains
10
+ // in state.db. Current readers and writers therefore target exactly one schema
11
+ // and never carry live compatibility SQL for previous generations.
38
12
  //
39
- // v18v19 (Chunk-5 flip F4c, spec §11.4): `entries.item_ref` becomes THE durable
40
- // identity its lookup index is upgraded to UNIQUE (every indexed row now
41
- // carries item_ref; NULLs are distinct in a SQLite UNIQUE index, so write-back
42
- // stragglers coexist). The durable state keyed off refs (`usage_events.entry_ref`)
43
- // is re-keyed onto item_ref by the one-time §11.4 migration cutover
44
- // (020-three-db-cutover) with orphan quarantine in `legacy_state`. The index is
45
- // regenerable, so a rebuild is an acceptable fallback if the UNIQUE upgrade finds
46
- // a duplicate on a partially-migrated DB.
47
- //
48
- // v19→v20 (Chunk-8 WI-8.3, three-DB merge): index.db STOPS owning `usage_events`
49
- // + `legacy_state`. Both are durable, non-regenerable state and now live in
50
- // state.db (folded by state migration 020; the cutover rescues the old index.db
51
- // rows across). index.db is a regenerable cache, so dropping their DDL from the
52
- // schema path needs no migration — a rebuild simply no longer re-creates them.
53
- // STANDARD REBUILD NOTE: after this bump, any index.db opened at v19 is rebuilt
54
- // from the stash on the next `akm index`; usage_events/legacy_state are read
55
- // exclusively from state.db thereafter.
56
- //
57
- // Reader/writer repoint progress (spec §3.3 "single clean shape"): the `entries`
58
- // upsert (index-entries-repository `getUpsertStmts`) uses the UNIQUE `item_ref`
59
- // as its PRIMARY conflict target, and the graph-boost related-ref reader
60
- // (`listRelatedPathsForFile`) resolves the user-facing ref from
61
- // `concept_id`/`item_ref` instead of stripping `entry_key`. The legacy
62
- // `entry_key`/`dir_path`/`stash_dir`/`entry_type`/`entry_json` columns are NOT
63
- // yet removable: they retain live consumers outside this module — `entry_key`
64
- // (mv-cli re-key, usage-event legacy resolution, index-entry-mapper, the LLM
65
- // cache), `entry_json` (the row payload every reader decodes), `entry_type`
66
- // (FTS + workflow loader), `stash_dir`/`dir_path` (scan/delete/utility scoping).
67
- // The upsert therefore keeps `entry_key` as a NULL-item_ref-safe SECOND conflict
68
- // target (the LLM metadata-enhance re-upsert still writes existing rows with a
69
- // NULL item_ref) and degrades to it entirely when the item_ref index is the
70
- // non-unique fallback. Both are deletable once every remaining reader repoints.
71
- export const DB_VERSION = 20;
13
+ // v20v21: remove the transitional entry_key/dir_path/stash_dir/entry_json/
14
+ // entry_type columns. item_ref is the sole conflict key; document_json is the
15
+ // sole stored document projection; bundle provenance and file_path provide the
16
+ // current identity and materialized read path.
17
+ export const DB_VERSION = CANONICAL_INDEX_DB_VERSION;
72
18
  export const EMBEDDING_DIM = 384;
73
- // #624-P1: graph_files re-keyed to (stash_root, file_path, body_hash). Bumped 3→4
74
- // as a marker; the actual migration is the targeted drop in migrateGraphFilesSchema.
19
+ // #624-P1: graph_files is keyed to (stash_root, file_path, body_hash).
75
20
  export const GRAPH_SCHEMA_VERSION = 4;
76
21
  // ── Schema ──────────────────────────────────────────────────────────────────
77
22
  /**
@@ -111,9 +56,7 @@ const REGISTRY_INDEX_CACHE_DDL = `
111
56
  `;
112
57
  /**
113
58
  * Create the graph-extraction tables (`graph_meta`/`graph_files`/`graph_file_entities`/
114
- * `graph_file_relations`/`graph_extraction_queue`). Extracted verbatim from
115
- * {@link ensureSchema} (called at the same point, between `migrateGraphFilesSchema`
116
- * and `migrateGraphDataFromLegacy`) — a pure, behavior-identical decomposition.
59
+ * `graph_file_relations`/`graph_extraction_queue`).
117
60
  */
118
61
  function ensureGraphTables(db) {
119
62
  db.exec(`
@@ -205,6 +148,48 @@ function ensureGraphTables(db) {
205
148
  ON graph_extraction_queue(stash_root, priority DESC, queued_at);
206
149
  `);
207
150
  }
151
+ /**
152
+ * Cross the incompatible entry-schema boundary by discarding the derived index
153
+ * generation. No row conversion or dual-schema compatibility is attempted:
154
+ * the next index run rebuilds entries, FTS, embeddings, utility aggregates,
155
+ * graph extraction, and enrichment caches from current sources/state.
156
+ */
157
+ function rebuildIncompatibleIndexGeneration(db) {
158
+ const version = getMeta(db, "version");
159
+ const hasEntries = tableExists(db, "entries");
160
+ if (!hasEntries && version === undefined)
161
+ return;
162
+ if (isCanonicalIndexGeneration(db))
163
+ return;
164
+ let vecResetPending = false;
165
+ try {
166
+ db.exec("DROP TABLE IF EXISTS entries_vec");
167
+ }
168
+ catch {
169
+ // A vec0 table cannot be dropped while sqlite-vec is unavailable. It does
170
+ // not reference entries, so leave a marker and drop it on the first later
171
+ // open where the extension is available.
172
+ vecResetPending = true;
173
+ }
174
+ db.transaction(() => {
175
+ db.exec("DROP TABLE IF EXISTS graph_file_relations");
176
+ db.exec("DROP TABLE IF EXISTS graph_file_entities");
177
+ db.exec("DROP TABLE IF EXISTS graph_files");
178
+ db.exec("DROP TABLE IF EXISTS graph_extraction_queue");
179
+ db.exec("DROP TABLE IF EXISTS graph_meta");
180
+ db.exec("DROP TABLE IF EXISTS entries_fts_dirty");
181
+ db.exec("DROP TABLE IF EXISTS entries_fts");
182
+ db.exec("DROP TABLE IF EXISTS embeddings");
183
+ db.exec("DROP TABLE IF EXISTS utility_scores_scoped");
184
+ db.exec("DROP TABLE IF EXISTS utility_scores");
185
+ db.exec("DROP TABLE IF EXISTS llm_enrichment_cache");
186
+ db.exec("DROP TABLE IF EXISTS index_dir_state");
187
+ db.exec("DROP TABLE IF EXISTS entries");
188
+ db.exec("DELETE FROM index_meta");
189
+ })();
190
+ if (vecResetPending)
191
+ setMeta(db, "vecResetPending", "1");
192
+ }
208
193
  export function ensureSchema(db, embeddingDim) {
209
194
  // Create meta table first so we can check version
210
195
  db.exec(`
@@ -213,87 +198,14 @@ export function ensureSchema(db, embeddingDim) {
213
198
  value TEXT NOT NULL
214
199
  );
215
200
  `);
216
- // index.db is a fully regenerable derived cache, so its schema is built
217
- // idempotently below: every table is CREATE … IF NOT EXISTS and column
218
- // additions go through guarded ALTERs (ensureDerivedFromColumn) and targeted
219
- // migrations (migrateGraphFilesSchema / migrateGraphDataFromLegacy). Opening a
220
- // database with an older or partial schema converges it forward WITHOUT ever
221
- // dropping data — there is intentionally no "nuclear drop the whole index on a
222
- // DB_VERSION mismatch" path (a destructive design the regenerable index never
223
- // needed, and whose pre-drop data-dir backup it required). A genuinely
224
- // incompatible change is handled by an additive/targeted migration; the few
225
- // derived tables that ever must be rebuilt are regenerated by `akm index`.
226
- db.exec(`
227
- CREATE TABLE IF NOT EXISTS entries (
228
- id INTEGER PRIMARY KEY AUTOINCREMENT,
229
- entry_key TEXT NOT NULL UNIQUE,
230
- dir_path TEXT NOT NULL,
231
- file_path TEXT NOT NULL,
232
- stash_dir TEXT NOT NULL,
233
- entry_json TEXT NOT NULL,
234
- search_text TEXT NOT NULL,
235
- entry_type TEXT NOT NULL,
236
- derived_from TEXT,
237
- -- Chunk-5 Step 2 / DB v18 (spec 14.4): bundle-adapter identity + provenance,
238
- -- ADDITIVE alongside the legacy columns above. item_ref is the durable
239
- -- <bundle>//<concept-id> spelling; nullable during the transition so a
240
- -- pre-repoint reader path never trips a NOT NULL on a partially-migrated row.
241
- item_ref TEXT,
242
- bundle_id TEXT,
243
- component_id TEXT,
244
- concept_id TEXT,
245
- adapter_id TEXT,
246
- type TEXT,
247
- content_hash TEXT,
248
- document_json TEXT
249
- );
250
-
251
- CREATE INDEX IF NOT EXISTS idx_entries_dir ON entries(dir_path);
252
- CREATE INDEX IF NOT EXISTS idx_entries_type ON entries(entry_type);
253
- CREATE INDEX IF NOT EXISTS idx_entries_file_path ON entries(file_path);
254
- `);
255
- // v18: backfill the bundle-adapter identity/provenance columns on databases
256
- // created against a pre-v18 binary (partial schema) — same PRAGMA-then-ALTER
257
- // guard pattern as `ensureDerivedFromColumn`. Runs BEFORE the item_ref index
258
- // so the CREATE INDEX below never references a not-yet-added column.
259
- ensureBundleRefColumns(db);
260
- // v19 (F4c, spec §11.4): item_ref is THE durable identity — its index is
261
- // UNIQUE. Every indexed row now carries item_ref; SQLite treats NULLs as
262
- // distinct in a UNIQUE index, so NULL-item_ref write-back stragglers coexist.
263
- // A pre-v19 DB carries a NON-unique `idx_entries_item_ref`, so drop-then-create.
264
- ensureUniqueItemRefIndex(db);
265
- // Phase 5A / DB v17: backfill `derived_from` column + index on databases
266
- // that were created at v17 fresh OR carry a partial v17 schema (a DB whose
267
- // `index_meta.version` was bumped to 17 but whose `entries` table still
268
- // lacks the column — this happens when a previous v17 binary opened a
269
- // pre-v17 DB without taking the upgrade path because no version mismatch
270
- // was seen at boot). The PRAGMA-then-ALTER guard runs unconditionally so
271
- // both fresh and partial schemas converge. The CREATE INDEX for
272
- // `derived_from` MUST run after this helper so we never reference a
273
- // column that has not yet been added on partial schemas.
274
- ensureDerivedFromColumn(db);
275
- // Validated WorkflowDocument JSON, one row per indexed workflow entry.
276
- // Pure index data — fully rebuilt on each `akm index`. ON DELETE CASCADE
277
- // means clearing entries (full rebuild or per-dir delete) drops these too.
278
- db.exec(`
279
- CREATE TABLE IF NOT EXISTS workflow_documents (
280
- entry_id INTEGER PRIMARY KEY REFERENCES entries(id) ON DELETE CASCADE,
281
- schema_version INTEGER NOT NULL,
282
- document_json TEXT NOT NULL,
283
- source_path TEXT NOT NULL,
284
- source_hash TEXT NOT NULL,
285
- updated_at TEXT NOT NULL
286
- );
287
-
288
- CREATE INDEX IF NOT EXISTS idx_workflow_documents_source_path
289
- ON workflow_documents(source_path);
290
- `);
291
- // Set version immediately after table creation so a crash before the end of
292
- // ensureSchema() does not leave the database in a versionless state on next open.
293
- const versionAfterCreate = getMeta(db, "version");
294
- if (!versionAfterCreate) {
295
- setMeta(db, "version", String(DB_VERSION));
296
- }
201
+ rebuildIncompatibleIndexGeneration(db);
202
+ db.exec(CANONICAL_ENTRY_SCHEMA_SQL);
203
+ // Workflow source is compiled directly into source IR at each command
204
+ // boundary. The former workflow_documents cache duplicated that IR in a
205
+ // second persisted representation and was never used by current execution.
206
+ // index.db is derived state, so remove the obsolete table on every open.
207
+ db.exec("DROP TABLE IF EXISTS workflow_documents");
208
+ setMeta(db, "version", String(DB_VERSION));
297
209
  // BLOB-based embedding storage (always available, no sqlite-vec needed)
298
210
  db.exec(`
299
211
  CREATE TABLE IF NOT EXISTS embeddings (
@@ -317,9 +229,8 @@ export function ensureSchema(db, embeddingDim) {
317
229
  );
318
230
  `);
319
231
  }
320
- // usage_events + legacy_state moved to state.db (Chunk-8 WI-8.3, DB_VERSION
321
- // v20). index.db no longer creates them; usage_events writers/readers open
322
- // state.db. utility_scores (a regenerable index.db cache) stays here.
232
+ // usage_events lives in state.db. utility_scores remains a regenerable
233
+ // index.db cache.
323
234
  // Utility scores table (aggregated per-entry utility metrics)
324
235
  db.exec(`
325
236
  CREATE TABLE IF NOT EXISTS utility_scores (
@@ -360,7 +271,7 @@ export function ensureSchema(db, embeddingDim) {
360
271
  // result for each asset so that subsequent `akm index --enrich` runs can
361
272
  // skip the LLM call when the body hasn't changed. The cache is keyed by
362
273
  // a stable asset_ref string (e.g. the absolute file path for graph/memory
363
- // passes, or `entryKey:passId` for the metadata-enhance pass).
274
+ // passes, or `itemRef:passId` for the metadata-enhance pass).
364
275
  // Entries are cleaned up when assets are removed or --re-enrich is used.
365
276
  db.exec(`
366
277
  CREATE TABLE IF NOT EXISTS llm_enrichment_cache (
@@ -389,18 +300,7 @@ export function ensureSchema(db, embeddingDim) {
389
300
  // body_hash) and declare a composite FK -> graph_files ON DELETE CASCADE so
390
301
  // child rows are removed when a graph_files row is replaced.
391
302
  //
392
- // #624-P1 targeted migration: an existing DB may still hold the OLD graph_files
393
- // (entry_id PK). SQLite can't ALTER a primary key, so we RENAME the 3 graph
394
- // tables aside (→ *_legacy) here — ONLY the graph tables, never the index/
395
- // embeddings — then the CREATE block below builds the new shape, then
396
- // migrateGraphDataFromLegacy() copies the data across so the graph is PRESERVED
397
- // (not re-extracted).
398
- migrateGraphFilesSchema(db);
399
303
  ensureGraphTables(db);
400
- // #624-P1 migration step 2: copy any renamed-aside legacy graph data into the
401
- // new-shape tables (just created above), then drop the legacy tables. No-op
402
- // unless migrateGraphFilesSchema renamed a legacy graph_files this open.
403
- migrateGraphDataFromLegacy(db);
404
304
  // FTS-dirty queue. Created here (not lazily on first upsert) so the
405
305
  // per-entry write path doesn't issue a CREATE TABLE IF NOT EXISTS on
406
306
  // every call — that DDL would fire thousands of times during a full
@@ -410,6 +310,12 @@ export function ensureSchema(db, embeddingDim) {
410
310
  entry_id INTEGER PRIMARY KEY
411
311
  );
412
312
  `);
313
+ // If a generation rebuild could not drop a vec0 table while the extension
314
+ // was unavailable, finish that reset as soon as vec0 can be loaded again.
315
+ if (isVecAvailable(db) && getMeta(db, "vecResetPending") === "1") {
316
+ db.exec("DROP TABLE IF EXISTS entries_vec");
317
+ setMeta(db, "vecResetPending", "0");
318
+ }
413
319
  // sqlite-vec table
414
320
  //
415
321
  // Dimension contract:
@@ -471,86 +377,6 @@ export function ensureSchema(db, embeddingDim) {
471
377
  // `akm search` does not hit the network on every invocation.
472
378
  db.exec(REGISTRY_INDEX_CACHE_DDL);
473
379
  }
474
- /**
475
- * Phase 5A / DB v17 schema guard.
476
- *
477
- * Ensures the `entries.derived_from` column + index exist on the open
478
- * connection. Called from `ensureSchema()` after the entries CREATE so that
479
- * legacy databases (created against a pre-v17 binary) still gain the new column
480
- * without data loss. Idempotent: a `PRAGMA table_info` lookup gates the ALTER.
481
- */
482
- function ensureDerivedFromColumn(db) {
483
- bestEffort(() => {
484
- const cols = db.prepare("PRAGMA table_info(entries)").all();
485
- const hasColumn = cols.some((c) => c.name === "derived_from");
486
- if (!hasColumn) {
487
- db.exec("ALTER TABLE entries ADD COLUMN derived_from TEXT");
488
- }
489
- // Index creation is idempotent on its own; safe to call unconditionally.
490
- db.exec("CREATE INDEX IF NOT EXISTS idx_entries_derived_from ON entries(derived_from)");
491
- }, "entries table may not exist on a brand-new DB before CREATE — caller is responsible");
492
- }
493
- /**
494
- * Chunk-5 Step 2 / DB v18 schema guard.
495
- *
496
- * Ensures the bundle-adapter identity/provenance columns exist on the open
497
- * `entries` table. Called from `ensureSchema()` after the entries CREATE so a
498
- * legacy database (created against a pre-v18 binary) gains the new columns
499
- * without a rebuild. All columns are nullable and added ADDITIVELY — the
500
- * writer populates `item_ref`/`bundle_id`/`component_id`/`concept_id`/
501
- * `adapter_id`/`type` while readers still key on the legacy columns. Idempotent:
502
- * a `PRAGMA table_info` lookup gates each ALTER.
503
- */
504
- function ensureBundleRefColumns(db) {
505
- bestEffort(() => {
506
- const cols = db.prepare("PRAGMA table_info(entries)").all();
507
- const have = new Set(cols.map((c) => c.name));
508
- const additive = [
509
- ["item_ref", "TEXT"],
510
- ["bundle_id", "TEXT"],
511
- ["component_id", "TEXT"],
512
- ["concept_id", "TEXT"],
513
- ["adapter_id", "TEXT"],
514
- ["type", "TEXT"],
515
- ["content_hash", "TEXT"],
516
- ["document_json", "TEXT"],
517
- ];
518
- for (const [name, sqlType] of additive) {
519
- if (!have.has(name))
520
- db.exec(`ALTER TABLE entries ADD COLUMN ${name} ${sqlType}`);
521
- }
522
- }, "entries table may not exist on a brand-new DB before CREATE — caller is responsible");
523
- }
524
- /**
525
- * Chunk-5 flip F4c / DB v19 schema guard.
526
- *
527
- * Upgrade `entries.item_ref`'s lookup index to UNIQUE (item_ref is now THE
528
- * durable identity — spec §11.4). A pre-v19 DB has a NON-unique
529
- * `idx_entries_item_ref`, so we DROP-then-CREATE (a `CREATE UNIQUE INDEX IF NOT
530
- * EXISTS` under the same name would no-op against the existing non-unique index).
531
- * SQLite treats NULLs as distinct in a UNIQUE index. Duplicate durable identities
532
- * are an invalid index and fail the schema open rather than enabling a dual-key
533
- * fallback.
534
- */
535
- function ensureUniqueItemRefIndex(db) {
536
- // Probe before mutating. This ran unconditionally on EVERY open as two
537
- // separate autocommit statements, so there was always a window in which the
538
- // index did not exist — a concurrent open (registry-cache search, indexer,
539
- // improve) could DROP between the other's DROP and CREATE and then fail with
540
- // "index idx_entries_item_ref already exists", or serve a query with no index
541
- // at all. A DB whose index is already UNIQUE needs no work.
542
- const existing = db
543
- .prepare("SELECT sql FROM sqlite_master WHERE type = 'index' AND name = 'idx_entries_item_ref'")
544
- .get();
545
- if (existing?.sql && /\bUNIQUE\b/i.test(existing.sql))
546
- return;
547
- // Pre-v19 (non-unique index) or absent: convert atomically so a racing open
548
- // sees either the old index or the new one, never neither.
549
- db.transaction(() => {
550
- db.exec("DROP INDEX IF EXISTS idx_entries_item_ref");
551
- db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_entries_item_ref ON entries(item_ref)");
552
- })();
553
- }
554
380
  /**
555
381
  * Returns true when a table exists in the current database.
556
382
  */
@@ -558,100 +384,3 @@ function tableExists(db, name) {
558
384
  const row = db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=? LIMIT 1").get(name);
559
385
  return row !== undefined && row !== null;
560
386
  }
561
- /**
562
- * #624-P1 targeted graph-schema migration — STEP 1 of 2 (rename).
563
- *
564
- * graph_files was re-keyed from `entry_id INTEGER PRIMARY KEY REFERENCES
565
- * entries(id)` to a self-contained `(stash_root, file_path, body_hash)` PK.
566
- * SQLite cannot ALTER a primary key, so an existing DB carrying the OLD shape
567
- * has its 3 graph tables RENAMED to `*_legacy` here; ensureSchema's CREATE block
568
- * then builds the new-shape tables, and {@link migrateGraphDataFromLegacy} COPIES
569
- * the data across before dropping the legacy tables. The graph is preserved —
570
- * NOT re-extracted (re-extraction is ~19s/file of LLM work).
571
- *
572
- * Crucially this is GRAPH-SCOPED: it touches ONLY the graph tables, never the
573
- * index / embeddings / enrichment cache. So users keep their (expensive)
574
- * embeddings instead of being forced into a full re-embed by a DB_VERSION bump.
575
- *
576
- * Detection: the old schema has an `entry_id` column on graph_files. Fresh DBs
577
- * (no graph_files yet) and already-migrated DBs (no entry_id column) are no-ops.
578
- * Idempotent.
579
- */
580
- function migrateGraphFilesSchema(db) {
581
- bestEffort(() => {
582
- const cols = db.prepare("PRAGMA table_info(graph_files)").all();
583
- const isLegacyShape = cols.some((c) => c.name === "entry_id");
584
- if (!isLegacyShape)
585
- return;
586
- // A previous interrupted migration may have left *_legacy behind — drop those
587
- // husks first so the rename below doesn't collide.
588
- db.exec("DROP TABLE IF EXISTS graph_file_relations_legacy");
589
- db.exec("DROP TABLE IF EXISTS graph_file_entities_legacy");
590
- db.exec("DROP TABLE IF EXISTS graph_files_legacy");
591
- // Rename the 3 entry_id-keyed tables aside. graph_meta is unchanged (stash_root
592
- // key) so it is left in place. ALTER … RENAME auto-updates child FK refs in
593
- // SQLite ≥3.25, which is fine — the legacy children are dropped after the copy.
594
- db.exec("ALTER TABLE graph_files RENAME TO graph_files_legacy");
595
- if (tableExists(db, "graph_file_entities")) {
596
- db.exec("ALTER TABLE graph_file_entities RENAME TO graph_file_entities_legacy");
597
- }
598
- if (tableExists(db, "graph_file_relations")) {
599
- db.exec("ALTER TABLE graph_file_relations RENAME TO graph_file_relations_legacy");
600
- }
601
- }, "graph_files may not exist on a brand-new DB before CREATE — caller is responsible");
602
- }
603
- /**
604
- * #624-P1 targeted graph-schema migration — STEP 2 of 2 (copy + drop legacy).
605
- *
606
- * Runs AFTER the graph CREATE TABLE block, so the new-shape tables exist. Copies
607
- * every legacy row into the re-keyed tables — the old tables already carry
608
- * (stash_root, file_path, body_hash) next to entry_id, so the projection is a
609
- * straight column copy (children JOIN back to graph_files_legacy to resolve the
610
- * composite key from their entry_id). Then drops the `*_legacy` tables.
611
- *
612
- * Best-effort: a copy failure (e.g. a pre-body_hash legacy schema) is tolerated,
613
- * and the legacy tables are dropped regardless so they never linger. Rows whose
614
- * body_hash is null/empty can't form the new PK and are skipped (they re-extract).
615
- */
616
- function migrateGraphDataFromLegacy(db) {
617
- if (!tableExists(db, "graph_files_legacy"))
618
- return;
619
- let migratedFiles = 0;
620
- bestEffort(() => {
621
- db.transaction(() => {
622
- const res = db
623
- .prepare(`INSERT OR IGNORE INTO graph_files
624
- (stash_root, file_path, body_hash, file_order, file_type, confidence, status, reason, extraction_run_id)
625
- SELECT stash_root, file_path, body_hash, file_order, file_type, confidence, status, reason, extraction_run_id
626
- FROM graph_files_legacy
627
- WHERE body_hash IS NOT NULL AND body_hash != ''`)
628
- .run();
629
- migratedFiles = Number(res.changes);
630
- if (tableExists(db, "graph_file_entities_legacy")) {
631
- db.exec(`INSERT OR IGNORE INTO graph_file_entities
632
- (stash_root, file_path, body_hash, entity_order, entity_norm, entity)
633
- SELECT gf.stash_root, gf.file_path, gf.body_hash, e.entity_order, e.entity_norm, e.entity
634
- FROM graph_file_entities_legacy e
635
- JOIN graph_files_legacy gf ON gf.entry_id = e.entry_id
636
- WHERE gf.body_hash IS NOT NULL AND gf.body_hash != ''`);
637
- }
638
- if (tableExists(db, "graph_file_relations_legacy")) {
639
- db.exec(`INSERT OR IGNORE INTO graph_file_relations
640
- (stash_root, file_path, body_hash, relation_order, from_entity_norm, from_entity, to_entity_norm, to_entity, relation_type, confidence)
641
- SELECT gf.stash_root, gf.file_path, gf.body_hash, r.relation_order, r.from_entity_norm, r.from_entity, r.to_entity_norm, r.to_entity, r.relation_type, r.confidence
642
- FROM graph_file_relations_legacy r
643
- JOIN graph_files_legacy gf ON gf.entry_id = r.entry_id
644
- WHERE gf.body_hash IS NOT NULL AND gf.body_hash != ''`);
645
- }
646
- })();
647
- }, "graph data migration is best-effort; legacy tables are dropped regardless below");
648
- // Always drop the legacy tables (children first), migrated or not.
649
- bestEffort(() => {
650
- db.exec("DROP TABLE IF EXISTS graph_file_relations_legacy");
651
- db.exec("DROP TABLE IF EXISTS graph_file_entities_legacy");
652
- db.exec("DROP TABLE IF EXISTS graph_files_legacy");
653
- }, "drop legacy graph tables after migration");
654
- if (migratedFiles > 0) {
655
- warn(`[akm] graph index re-keyed (#624): migrated ${migratedFiles} extracted file(s) to the new schema — no re-extraction needed. Index + embeddings untouched.`);
656
- }
657
- }
@@ -8,7 +8,6 @@
8
8
  * retrieval-frequency counting over `usage_events`. The bounded-step EMA policy
9
9
  * itself lives in `indexer/feedback/utility-policy`; this repo only reads/writes.
10
10
  */
11
- import path from "node:path";
12
11
  import { conceptIdFromTypeName, parseRefInput } from "../../core/asset/resolve-ref.js";
13
12
  import { computeNextUtility } from "../../indexer/feedback/utility-policy.js";
14
13
  import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
@@ -145,7 +144,7 @@ function bareRefCandidates(ref) {
145
144
  export function getRetrievalCounts(indexDb, stateDb, refs, options = {}) {
146
145
  if (refs.length === 0)
147
146
  return new Map();
148
- if (options.sourceName || options.stashDir) {
147
+ if (options.sourceName) {
149
148
  return getSourceScopedRetrievalCounts(indexDb, stateDb, refs, options);
150
149
  }
151
150
  // Map each conceptId back to the input refs that produced it so DB results
@@ -218,20 +217,18 @@ function getSourceScopedRetrievalCounts(indexDb, stateDb, refs, options) {
218
217
  bareToInputs.set(bare, [ref]);
219
218
  }
220
219
  }
221
- // Cross-DB (Chunk-8 WI-8.3): usage_events rows come from state.db; the
222
- // per-row `stash_dir` (formerly a LEFT JOIN on entries) is resolved from
223
- // index.db by entry_id. Read the usage rows first, then batch-look-up the
224
- // stash_dir for their entry_ids and join in JS.
225
- const entryStashDir = indexDb.prepare("SELECT stash_dir FROM entries WHERE id = ?");
226
- const stashDirFor = (entryId) => {
220
+ // Cross-DB: usage rows come from state.db; resolve the linked entry's
221
+ // canonical bundle identity from index.db by entry_id.
222
+ const entryBundle = indexDb.prepare("SELECT bundle_id FROM entries WHERE id = ?");
223
+ const bundleFor = (entryId) => {
227
224
  if (entryId === null)
228
225
  return null;
229
- const row = entryStashDir.get(entryId);
230
- return row?.stash_dir ?? null;
226
+ const row = entryBundle.get(entryId);
227
+ return row?.bundle_id ?? null;
231
228
  };
232
229
  const countsByBare = new Map();
233
230
  const bareForms = [...bareToInputs.keys()];
234
- const selectedRoot = options.stashDir ? path.resolve(options.stashDir) : undefined;
231
+ const selectedBundle = options.sourceName;
235
232
  for (let i = 0; i < bareForms.length; i += SQLITE_CHUNK_SIZE) {
236
233
  const chunk = bareForms.slice(i, i + SQLITE_CHUNK_SIZE);
237
234
  const placeholders = chunk.map(() => "?").join(", ");
@@ -248,15 +245,13 @@ function getSourceScopedRetrievalCounts(indexDb, stateDb, refs, options) {
248
245
  ELSE ue.entry_ref
249
246
  END IN (${placeholders})`)
250
247
  .all(...chunk);
251
- const rows = rawRows.map((r) => ({ ...r, stash_dir: stashDirFor(r.entry_id) }));
248
+ const rows = rawRows.map((r) => ({ ...r, bundle_id: bundleFor(r.entry_id) }));
252
249
  for (const row of rows) {
253
250
  const bare = bareRef(row.entry_ref);
254
- const linkedToSelectedRoot = row.entry_id !== null && selectedRoot !== undefined && row.stash_dir !== null
255
- ? path.resolve(row.stash_dir) === selectedRoot
256
- : false;
257
- const detached = row.entry_id === null || selectedRoot === undefined;
251
+ const linkedToSelectedBundle = row.entry_id !== null && row.bundle_id === selectedBundle;
252
+ const detached = row.entry_id === null;
258
253
  const qualifiedForSource = detached && options.sourceName !== undefined && row.entry_ref === `${options.sourceName}//${bare}`;
259
- if (!linkedToSelectedRoot && !qualifiedForSource)
254
+ if (!linkedToSelectedBundle && !qualifiedForSource)
260
255
  continue;
261
256
  countsByBare.set(bare, (countsByBare.get(bare) ?? 0) + 1);
262
257
  }
@@ -277,7 +277,7 @@ function searchBlobVec(db, queryEmbedding, k) {
277
277
  export function getAllEntriesForEmbedding(db) {
278
278
  return db
279
279
  .prepare(`
280
- SELECT e.id, e.search_text AS searchText, e.entry_key AS entryKey, e.file_path AS filePath FROM entries e
280
+ SELECT e.id, e.search_text AS searchText, e.item_ref AS itemRef, e.file_path AS filePath FROM entries e
281
281
  WHERE NOT EXISTS (SELECT 1 FROM embeddings b WHERE b.id = e.id)
282
282
  `)
283
283
  .all();