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
@@ -18,20 +18,30 @@ import { getStateDbPath, withStateDb } from "../../core/state-db.js";
18
18
  import { warn } from "../../core/warn.js";
19
19
  import { getMarkdownFragmentContent, hasMarkdownFragmentContent, } from "../../indexer/passes/metadata.js";
20
20
  import { buildSearchText } from "../../indexer/search/search-fields.js";
21
+ import { sha256Hex } from "../../runtime.js";
21
22
  import { ENTRY_COLUMNS, rowToIndexedEntry } from "./index-entry-mapper.js";
22
23
  import { deleteFtsEntries, replaceFtsEntry } from "./index-fts-repository.js";
24
+ import { deleteEntryLinks, replaceEntryLinks } from "./index-links-repository.js";
23
25
  import { SQLITE_CHUNK_SIZE } from "./index-sql.js";
24
- import { deleteEntryVectors, isVecAvailable } from "./index-vec-repository.js";
26
+ import { deleteEntryVectors } from "./index-vec-repository.js";
25
27
  // ── Entry operations ────────────────────────────────────────────────────────
28
+ /**
29
+ * Hash of the text an entry's vector is embedded from (`buildSearchText`),
30
+ * stored as `entries.embed_hash`: a change deletes the entry's vector, and the
31
+ * next embedding pass re-embeds it.
32
+ */
33
+ function embedHash(entry) {
34
+ return sha256Hex(buildSearchText(entry));
35
+ }
26
36
  /**
27
37
  * Insert or update one canonical entry and all synchronously derived search
28
38
  * state. Returns the stable row id.
29
39
  *
30
- * The entries row, FTS projection, and stale-vector invalidation commit as one
31
- * SQLite transaction. Callers therefore cannot publish an entry and forget a
32
- * second FTS maintenance step.
40
+ * The entries row, FTS projection, declared links, and stale-vector
41
+ * invalidation commit as one SQLite transaction. Callers therefore cannot
42
+ * publish an entry and forget a second FTS or links maintenance step.
33
43
  */
34
- export function upsertEntry(db, filePath, entry, searchText, provenance, contentHash) {
44
+ export function upsertEntry(db, filePath, entry, provenance, contentHash) {
35
45
  // Hot path during indexing — cache prepared statements per database
36
46
  // connection so we don't pay the SQL parse/compile cost on every call.
37
47
  const stmts = getUpsertStmts(db);
@@ -39,16 +49,18 @@ export function upsertEntry(db, filePath, entry, searchText, provenance, content
39
49
  // dedicated `derived_from` column so retrieval-time lookup (parent→child)
40
50
  // does not have to scan + JSON-decode every memory row.
41
51
  const derivedFrom = typeof entry.derivedFrom === "string" && entry.derivedFrom.trim() ? entry.derivedFrom.trim() : null;
42
- // `content_hash` is optional on the LLM-enrichment re-upsert; a missing hash
43
- // preserves the scan writer's current value.
52
+ const hash = embedHash(entry);
53
+ // `content_hash` is optional; an upsert that omits it preserves the scan
54
+ // writer's current value.
44
55
  const apply = () => {
45
56
  const previous = stmts.findByItemRef.get(provenance.itemRef);
46
- const result = stmts.upsert.get(provenance.itemRef, provenance.bundleId, provenance.componentId, provenance.conceptId, provenance.adapterId, entry.type, filePath, contentHash ?? null, JSON.stringify(entry), searchText, derivedFrom);
57
+ const result = stmts.upsert.get(provenance.itemRef, provenance.bundleId, provenance.componentId, provenance.conceptId, provenance.adapterId, entry.type, filePath, contentHash ?? null, JSON.stringify(entry), derivedFrom, hash);
47
58
  if (!result)
48
59
  throw new Error("upsertEntry: item_ref not found after upsert");
49
- if (previous?.id === result.id && previous.search_text !== searchText)
60
+ if (previous?.id === result.id && previous.embed_hash !== hash)
50
61
  deleteEntryVectors(db, result.id);
51
62
  replaceFtsEntry(db, result.id, entry, hasMarkdownFragmentContent(entry) ? (getMarkdownFragmentContent(entry) ?? null) : undefined);
63
+ replaceEntryLinks(db, result.id, entry, provenance);
52
64
  return result.id;
53
65
  };
54
66
  // Always enter the driver's transaction wrapper. Both supported SQLite
@@ -58,8 +70,8 @@ export function upsertEntry(db, filePath, entry, searchText, provenance, content
58
70
  return db.transaction(apply)();
59
71
  }
60
72
  const upsertStmtsByDb = new WeakMap();
61
- // item_ref is the sole durable conflict target. `content_hash` COALESCEs so a
62
- // metadata-only enrichment pass cannot wipe a scan hash.
73
+ // item_ref is the sole durable conflict target. `content_hash` COALESCEs so
74
+ // an upsert that omits it (`contentHash` is optional) cannot wipe a scan hash.
63
75
  const UPSERT_SET_CLAUSE = `SET
64
76
  bundle_id = excluded.bundle_id,
65
77
  component_id = excluded.component_id,
@@ -68,8 +80,8 @@ const UPSERT_SET_CLAUSE = `SET
68
80
  type = excluded.type,
69
81
  file_path = excluded.file_path,
70
82
  document_json = excluded.document_json,
71
- search_text = excluded.search_text,
72
83
  derived_from = excluded.derived_from,
84
+ embed_hash = excluded.embed_hash,
73
85
  content_hash = COALESCE(excluded.content_hash, content_hash)`;
74
86
  function getUpsertStmts(db) {
75
87
  const existing = upsertStmtsByDb.get(db);
@@ -82,13 +94,13 @@ function getUpsertStmts(db) {
82
94
  upsert: db.prepare(`
83
95
  INSERT INTO entries (
84
96
  item_ref, bundle_id, component_id, concept_id, adapter_id, type,
85
- file_path, content_hash, document_json, search_text, derived_from
97
+ file_path, content_hash, document_json, derived_from, embed_hash
86
98
  )
87
99
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
88
100
  ON CONFLICT(item_ref) DO UPDATE ${UPSERT_SET_CLAUSE}
89
101
  RETURNING id
90
102
  `),
91
- findByItemRef: db.prepare("SELECT id, search_text FROM entries WHERE item_ref = ?"),
103
+ findByItemRef: db.prepare("SELECT id, embed_hash FROM entries WHERE item_ref = ?"),
92
104
  };
93
105
  upsertStmtsByDb.set(db, stmts);
94
106
  return stmts;
@@ -164,14 +176,13 @@ export function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
164
176
  /**
165
177
  * Re-key an entries row in place for the opt-in source-maintenance script.
166
178
  *
167
- * The row id is preserved on purpose — `utility_scores`,
168
- * `utility_scores_scoped`, and `embeddings` are keyed by `entry_id`, so an
169
- * UPDATE (rather than a delete + insert under the new `item_ref`) is what
170
- * keeps the asset's accumulated usage-ranking history attached across a
171
- * rename. (`asset_salience` / `asset_outcome` live in state.db keyed by
172
- * `asset_ref` TEXT and are re-keyed separately by `akm mv` — see
179
+ * The row id is preserved on purpose — `utility_scores` and `embeddings` are
180
+ * keyed by `entry_id`, so an UPDATE (rather than a delete + insert under the
181
+ * new `item_ref`) is what keeps the asset's accumulated usage-ranking history
182
+ * attached across a rename. (`asset_salience` / `asset_outcome` live in
183
+ * state.db keyed by `asset_ref` TEXT and are re-keyed separately by `akm mv` — see
173
184
  * the state rekey helper.) `document_json.name` (and `filename`, when
174
- * present) is patched and `search_text` rebuilt so search reflects the new
185
+ * present) is patched and `embed_hash` recomputed so search reflects the new
175
186
  * name. Its FTS projection and stale vector are updated in the same
176
187
  * transaction as the canonical identity.
177
188
  *
@@ -187,7 +198,7 @@ export function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
187
198
  * A stale row already occupying the new item ref (the caller has verified no
188
199
  * FILE exists at the target, so such a row can only be a leftover for a
189
200
  * deleted file) is evicted first — through {@link deleteRelatedRows}, so its
190
- * child rows (embeddings, entries_vec, utility scores, usage events) go with
201
+ * child rows (embeddings, utility scores, usage events) go with
191
202
  * it. A bare `DELETE FROM entries` would trip the non-CASCADE `embeddings`
192
203
  * FK under `PRAGMA foreign_keys = ON` and roll back the whole re-key.
193
204
  * The moved row keeps its id.
@@ -199,7 +210,7 @@ export function getBaseBeliefStatesForDerivedTwins(db, twinIds) {
199
210
  export function rekeyEntryInPlace(db, opts) {
200
211
  const oldItemRef = `${opts.sourceName}//${opts.oldRef}`;
201
212
  const row = db
202
- .prepare("SELECT id, file_path, document_json, search_text, type FROM entries WHERE item_ref = ?")
213
+ .prepare("SELECT id, file_path, document_json, embed_hash, type FROM entries WHERE item_ref = ?")
203
214
  .get(oldItemRef);
204
215
  if (!row)
205
216
  return null;
@@ -211,7 +222,7 @@ export function rekeyEntryInPlace(db, opts) {
211
222
  // Patch the JSON payload. On corrupt document_json still re-key identity/path so
212
223
  // the utility history survives; the next full index heals the JSON.
213
224
  let documentJson = row.document_json;
214
- let searchText = row.search_text;
225
+ let hash = row.embed_hash;
215
226
  let document;
216
227
  try {
217
228
  const entry = JSON.parse(row.document_json);
@@ -221,7 +232,7 @@ export function rekeyEntryInPlace(db, opts) {
221
232
  if (opts.newDerivedFrom !== undefined)
222
233
  entry.derivedFrom = opts.newDerivedFrom;
223
234
  documentJson = JSON.stringify(entry);
224
- searchText = buildSearchText(entry);
235
+ hash = embedHash(entry);
225
236
  document = entry;
226
237
  }
227
238
  catch {
@@ -235,7 +246,7 @@ export function rekeyEntryInPlace(db, opts) {
235
246
  db.transaction(() => {
236
247
  const stale = db.prepare("SELECT id FROM entries WHERE item_ref = ?").get(newItemRef);
237
248
  if (stale && stale.id !== row.id) {
238
- // Full child-row cleanup (embeddings, entries_vec, utility scores,
249
+ // Full child-row cleanup (embeddings, utility scores,
239
250
  // usage events, FTS + dirty marks) BEFORE the entries delete: the
240
251
  // `embeddings` FK is non-CASCADE and `foreign_keys = ON`, so a bare
241
252
  // entries delete would throw and roll back the entire re-key; and
@@ -243,14 +254,16 @@ export function rekeyEntryInPlace(db, opts) {
243
254
  deleteRelatedRows(db, [{ id: stale.id }]);
244
255
  db.prepare("DELETE FROM entries WHERE id = ?").run(stale.id);
245
256
  }
246
- db.prepare("UPDATE entries SET file_path = ?, document_json = ?, search_text = ?, item_ref = ?, concept_id = ? WHERE id = ?").run(opts.newFilePath, documentJson, searchText, newItemRef, opts.newRef, row.id);
257
+ db.prepare("UPDATE entries SET file_path = ?, document_json = ?, embed_hash = ?, item_ref = ?, concept_id = ? WHERE id = ?").run(opts.newFilePath, documentJson, hash, newItemRef, opts.newRef, row.id);
247
258
  if (opts.newDerivedFrom !== undefined) {
248
259
  db.prepare("UPDATE entries SET derived_from = ? WHERE id = ?").run(opts.newDerivedFrom, row.id);
249
260
  }
250
- if (row.search_text !== searchText)
261
+ if (row.embed_hash !== hash)
251
262
  deleteEntryVectors(db, row.id);
252
- if (document)
263
+ if (document) {
253
264
  replaceFtsEntry(db, row.id, document, hasMarkdownFragmentContent(document) ? (getMarkdownFragmentContent(document) ?? null) : undefined);
265
+ replaceEntryLinks(db, row.id, document, { bundleId: opts.sourceName, conceptId: opts.newRef });
266
+ }
254
267
  else
255
268
  deleteFtsEntries(db, [row.id]);
256
269
  })();
@@ -293,50 +306,6 @@ function rewriteUsageEventRefForMove(opts) {
293
306
  throw new Error(`Failed to rewrite usage events for move: ${message}`, { cause: error });
294
307
  }
295
308
  }
296
- /**
297
- * Phase 2A / Rec 5: bulk-load positive feedback event counts for the given
298
- * entry ids. Used by the utility-decay forgetting curve to stabilize
299
- * (extend the half-life of) memories that have repeatedly proven useful.
300
- *
301
- * Returns a `Map<entryId, count>` containing only entries with at least one
302
- * positive feedback event — missing ids implicitly map to `0`. Chunks at
303
- * `SQLITE_CHUNK_SIZE` (500) to respect `SQLITE_MAX_VARIABLE_NUMBER`.
304
- *
305
- * Cheap when called with zero ids, and silently empty when state.db (or its
306
- * `usage_events` table) is absent.
307
- *
308
- * Chunk-8 WI-8.3: usage_events lives in state.db — this reads it there (no
309
- * entries join needed; the ids are supplied by the caller). Gated by the caller
310
- * (`shouldQueryPositiveFeedbackCounts`) so the state.db open is not on the
311
- * default search hot path.
312
- */
313
- export function getPositiveFeedbackCountsByIds(ids) {
314
- const result = new Map();
315
- if (ids.length === 0 || !fs.existsSync(getStateDbPath()))
316
- return result;
317
- bestEffort(() => {
318
- withStateDb((stateDb) => {
319
- for (let i = 0; i < ids.length; i += SQLITE_CHUNK_SIZE) {
320
- const chunk = ids.slice(i, i + SQLITE_CHUNK_SIZE);
321
- const placeholders = chunk.map(() => "?").join(",");
322
- const rows = stateDb
323
- .prepare(`SELECT entry_id, COUNT(*) AS cnt
324
- FROM usage_events
325
- WHERE event_type = 'feedback'
326
- AND signal = 'positive'
327
- AND entry_id IN (${placeholders})
328
- GROUP BY entry_id`)
329
- .all(...chunk);
330
- for (const row of rows) {
331
- if (row.entry_id !== null && row.cnt > 0) {
332
- result.set(row.entry_id, row.cnt);
333
- }
334
- }
335
- }
336
- });
337
- }, "positive feedback counts are best-effort");
338
- return result;
339
- }
340
309
  /**
341
310
  * Rows whose `file_path` sits directly in `dirPath`. A half-open byte range
342
311
  * over `idx_entries_file_path` (`[dir + sep, dir + sep + 1)`) turns the lookup
@@ -382,14 +351,30 @@ export function deleteEntriesByBundle(db, bundleId) {
382
351
  })();
383
352
  }
384
353
  /**
385
- * Delete the complete regenerable entry generation through the same child-row
386
- * authority used by targeted deletes. The caller may retain cross-database
387
- * usage events so the finalize pass can relink them to the new row ids.
354
+ * The `file_path` of every entry indexed under one bundle — used by
355
+ * `akm bundle rename` (D6) to find bundle CONTENT that still spells the old
356
+ * `<bundle>//` prefix (xrefs, `supersededBy`, task `uses:`), which the rename
357
+ * reports rather than rewrites.
358
+ */
359
+ export function getFilePathsByBundle(db, bundleId) {
360
+ const rows = db.prepare("SELECT DISTINCT file_path FROM entries WHERE bundle_id = ?").all(bundleId);
361
+ return rows.map((row) => row.file_path);
362
+ }
363
+ /**
364
+ * Re-key every entry row's `bundle_id`/`item_ref` from `oldBundleId` to
365
+ * `newBundleId` in place (`akm bundle rename`, D6). Unlike
366
+ * {@link rekeyEntryInPlace} (one asset, `akm mv`), this is a bulk identity
367
+ * change with no content move: `concept_id`/`file_path`/`document_json` are
368
+ * untouched, so no FTS/vector rebuild is needed (FTS and `embeddings` key on
369
+ * the entry's row `id`, which this preserves, not on `item_ref`). Returns the
370
+ * number of rows renamed.
388
371
  */
389
- export function deleteAllEntries(db, options = {}) {
372
+ export function renameEntriesBundleId(db, oldBundleId, newBundleId) {
390
373
  return db.transaction(() => {
391
- const rows = db.prepare("SELECT id FROM entries").all();
392
- return deleteEntryRows(db, rows, options);
374
+ const result = db
375
+ .prepare("UPDATE entries SET bundle_id = ?, item_ref = ? || '//' || concept_id WHERE bundle_id = ?")
376
+ .run(newBundleId, newBundleId, oldBundleId);
377
+ return Number(result.changes);
393
378
  })();
394
379
  }
395
380
  /**
@@ -415,56 +400,24 @@ function deleteRelatedRows(db, ids, options = {}) {
415
400
  if (ids.length === 0)
416
401
  return;
417
402
  const numericIds = ids.map((r) => r.id);
418
- const vecAvail = isVecAvailable(db);
419
- // FTS is part of the canonical mutation boundary, not a caller-maintained
420
- // dirty queue. Delete it before the parent row inside this transaction.
403
+ // FTS and declared links are part of the canonical mutation boundary, not a
404
+ // caller-maintained dirty queue. Delete them before the parent row inside
405
+ // this transaction.
421
406
  deleteFtsEntries(db, numericIds);
407
+ deleteEntryLinks(db, numericIds);
422
408
  // Process in chunks to stay within SQLITE_MAX_VARIABLE_NUMBER
423
409
  for (let i = 0; i < numericIds.length; i += SQLITE_CHUNK_SIZE) {
424
410
  const chunk = numericIds.slice(i, i + SQLITE_CHUNK_SIZE);
425
411
  const placeholders = chunk.map(() => "?").join(",");
426
412
  bestEffort(() => db.prepare(`DELETE FROM embeddings WHERE id IN (${placeholders})`).run(...chunk), "delete embeddings for entries");
427
- if (vecAvail) {
428
- bestEffort(() => db.prepare(`DELETE FROM entries_vec WHERE id IN (${placeholders})`).run(...chunk), "delete entries_vec for entries");
429
- }
430
413
  // Clean up utility scores before deleting entries
431
414
  bestEffort(() => db.prepare(`DELETE FROM utility_scores WHERE entry_id IN (${placeholders})`).run(...chunk), "delete utility_scores for entries");
432
- bestEffort(() => db.prepare(`DELETE FROM utility_scores_scoped WHERE entry_id IN (${placeholders})`).run(...chunk), "delete utility_scores_scoped for entries");
433
- }
434
- // Graph rows are independently keyed and intentionally survive entry
435
- // deletion. Resolve their owning roots through the canonical physical path
436
- // before entries disappear, then refresh the derived summary counts.
437
- const affectedGraphRoots = new Set();
438
- for (let i = 0; i < numericIds.length; i += SQLITE_CHUNK_SIZE) {
439
- const chunk = numericIds.slice(i, i + SQLITE_CHUNK_SIZE);
440
- const placeholders = chunk.map(() => "?").join(",");
441
- bestEffort(() => {
442
- const rows = db
443
- .prepare(`SELECT DISTINCT gf.stash_root
444
- FROM graph_files gf
445
- JOIN entries e ON e.file_path = gf.file_path
446
- WHERE e.id IN (${placeholders})`)
447
- .all(...chunk);
448
- for (const row of rows)
449
- affectedGraphRoots.add(row.stash_root);
450
- }, "resolve graph roots for graph_meta recompute");
451
- }
452
- for (const stashRoot of affectedGraphRoots) {
453
- bestEffort(() => db
454
- .prepare(`UPDATE graph_meta
455
- SET extracted_files = (SELECT COUNT(*) FROM graph_files WHERE stash_root = ?),
456
- entity_count = (SELECT COUNT(*) FROM graph_file_entities WHERE stash_root = ?),
457
- relation_count = (SELECT COUNT(*) FROM graph_file_relations WHERE stash_root = ?)
458
- WHERE stash_root = ?`)
459
- .run(stashRoot, stashRoot, stashRoot, stashRoot), "sync graph_meta counts after entries delete");
460
415
  }
461
416
  // usage_events lives in state.db, outside this transaction. Index persistence
462
417
  // disables this cleanup and runs it only after its index.db transaction
463
418
  // commits; standalone delete callers retain the immediate behavior.
464
419
  if (options.cleanupUsageEvents !== false)
465
420
  deleteUsageEventsByEntryIds(numericIds);
466
- // graph_files is keyed by its own stash_root/file_path/body_hash identity,
467
- // so deleting an entry row intentionally leaves extracted graph data intact.
468
421
  }
469
422
  export function deleteUsageEventsByEntryIds(entryIds) {
470
423
  if (entryIds.length === 0 || !fs.existsSync(getStateDbPath()))
@@ -481,7 +434,7 @@ export function deleteUsageEventsByEntryIds(entryIds) {
481
434
  }
482
435
  /**
483
436
  * Delete entries by their primary key IDs, along with all related rows
484
- * (embeddings, entries_vec, entries_fts, utility scores, usage_events).
437
+ * (embeddings, entries_fts, utility scores, usage_events).
485
438
  *
486
439
  * Used by explicit `--clean` reconciliation before embeddings and final
487
440
  * verification to remove stale entries whose source files no longer exist.
@@ -645,6 +598,20 @@ export function getEntryById(db, id) {
645
598
  adapterId: row.adapter_id,
646
599
  };
647
600
  }
601
+ /** The `item_ref` and `type` of every id that has an entry row. */
602
+ export function getEntryRefsAndTypes(db, ids) {
603
+ const out = new Map();
604
+ for (let i = 0; i < ids.length; i += SQLITE_CHUNK_SIZE) {
605
+ const chunk = ids.slice(i, i + SQLITE_CHUNK_SIZE);
606
+ const placeholders = chunk.map(() => "?").join(",");
607
+ const rows = db
608
+ .prepare(`SELECT id, item_ref, type FROM entries WHERE id IN (${placeholders})`)
609
+ .all(...chunk);
610
+ for (const row of rows)
611
+ out.set(row.id, { itemRef: row.item_ref, type: row.type });
612
+ }
613
+ return out;
614
+ }
648
615
  export function getEntriesByDir(db, dirPath) {
649
616
  return parseEntryRows(selectRowsInDirectory(db, dirPath, ENTRY_COLUMNS), "getEntriesByDir");
650
617
  }
@@ -684,6 +651,22 @@ export function getIndexedFilePaths(db) {
684
651
  .all();
685
652
  return new Set(rows.map((r) => r.file_path));
686
653
  }
654
+ /**
655
+ * `entries.file_path -> content_hash` for every currently-indexed file that
656
+ * carries a stored hash. Used by per-file staleness detection
657
+ * (`hasNewerIndexableFiles`, R6) to tell "edited after the last build" apart
658
+ * from "already re-indexed with this exact content after the last build" —
659
+ * `indexWrittenAssets` upserts a fresh row (and its `content_hash`) without
660
+ * bumping `builtAt`, so mtime alone cannot distinguish the two cases.
661
+ */
662
+ export function getIndexedFileHashes(db) {
663
+ const rows = db
664
+ .prepare(`SELECT file_path, content_hash FROM entries
665
+ WHERE file_path IS NOT NULL AND file_path <> ''
666
+ AND content_hash IS NOT NULL`)
667
+ .all();
668
+ return new Map(rows.map((r) => [r.file_path, r.content_hash]));
669
+ }
687
670
  /**
688
671
  * Resolve a single `entries.file_path` by primary key, or `undefined` if no
689
672
  * row matches.
@@ -732,6 +715,33 @@ export function getEntryByRef(db, ref) {
732
715
  const id = findEntryIdByRef(db, ref);
733
716
  return id === undefined ? null : { id };
734
717
  }
718
+ /** Build a {@link LiveRefSnapshot} from the current `entries` table. */
719
+ export function getLiveRefSnapshot(db) {
720
+ const rows = db.prepare("SELECT item_ref FROM entries").all();
721
+ const itemRefs = new Set();
722
+ const conceptIds = new Set();
723
+ for (const { item_ref } of rows) {
724
+ itemRefs.add(item_ref);
725
+ const boundary = item_ref.indexOf("//");
726
+ if (boundary >= 0)
727
+ conceptIds.add(item_ref.slice(boundary + 2));
728
+ }
729
+ return { itemRefs, conceptIds };
730
+ }
731
+ /**
732
+ * `getEntryByRef`'s liveness check (bundle-qualified exact match, or bare
733
+ * `//conceptId` suffix match across bundles, each tried with the `.md`
734
+ * toggle {@link withMdVariants} applies) against a prebuilt
735
+ * {@link LiveRefSnapshot} instead of the database — no query per call.
736
+ */
737
+ export function isRefLiveInSnapshot(snapshot, ref) {
738
+ const parsed = parseBundleRef(ref);
739
+ const conceptVariants = withMdVariants(parsed.conceptId);
740
+ if (parsed.bundle !== undefined) {
741
+ return conceptVariants.some((conceptId) => snapshot.itemRefs.has(`${parsed.bundle}//${conceptId}`));
742
+ }
743
+ return conceptVariants.some((conceptId) => snapshot.conceptIds.has(conceptId));
744
+ }
735
745
  /**
736
746
  * The fully-qualified `item_ref` (`<bundle>//<conceptId>`, the durable stored
737
747
  * spelling — spec §11.1 D-R3) for an entry `id`, or `null` when the row is gone
@@ -766,15 +776,7 @@ function resolveUsageEventEntryId(db, ref) {
766
776
  * distinct linked entry_ids in usage_events is small — and the re-resolution
767
777
  * reads `entries` from `indexDb`.
768
778
  */
769
- function qualifiedUsageEventsTable(stateSchema) {
770
- if (stateSchema === undefined)
771
- return "usage_events";
772
- if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(stateSchema))
773
- throw new Error("Invalid attached state schema name.");
774
- return `"${stateSchema}".usage_events`;
775
- }
776
- export function relinkUsageEvents(indexDb, stateDb, options = {}) {
777
- const usageEvents = qualifiedUsageEventsTable(options.stateSchema);
779
+ export function relinkUsageEvents(indexDb, stateDb) {
778
780
  bestEffort(() => {
779
781
  // Step 1: null out stale entry_ids (entry was deleted, re-keyed, etc).
780
782
  // Leaving them in place would let `recomputeUtilityScores` aggregate by an
@@ -783,7 +785,7 @@ export function relinkUsageEvents(indexDb, stateDb, options = {}) {
783
785
  // transaction. Nulled rows can be re-resolved by step 2 below; events whose
784
786
  // entry is permanently gone simply stay null and age out via retention.
785
787
  const linkedRows = stateDb
786
- .prepare(`SELECT DISTINCT entry_id AS id, entry_ref AS ref FROM ${usageEvents} WHERE entry_id IS NOT NULL`)
788
+ .prepare("SELECT DISTINCT entry_id AS id, entry_ref AS ref FROM usage_events WHERE entry_id IS NOT NULL")
787
789
  .all();
788
790
  const entryIdentity = indexDb.prepare("SELECT item_ref AS itemRef FROM entries WHERE id = ?");
789
791
  const staleLinks = linkedRows.filter(({ id, ref }) => {
@@ -791,7 +793,7 @@ export function relinkUsageEvents(indexDb, stateDb, options = {}) {
791
793
  return live == null || (ref !== null && live.itemRef !== ref);
792
794
  });
793
795
  if (staleLinks.length > 0) {
794
- const nullOut = stateDb.prepare(`UPDATE ${usageEvents} SET entry_id = NULL WHERE entry_id = ? AND entry_ref IS ?`);
796
+ const nullOut = stateDb.prepare("UPDATE usage_events SET entry_id = NULL WHERE entry_id = ? AND entry_ref IS ?");
795
797
  const nullTx = stateDb.transaction(() => {
796
798
  for (const { id, ref } of staleLinks)
797
799
  nullOut.run(id, ref);
@@ -801,9 +803,9 @@ export function relinkUsageEvents(indexDb, stateDb, options = {}) {
801
803
  // Step 2: re-resolve each fully-qualified ref. Bare rows are not current
802
804
  // durable identities and remain detached.
803
805
  const refs = stateDb
804
- .prepare(`SELECT DISTINCT entry_ref AS ref FROM ${usageEvents} WHERE entry_id IS NULL AND entry_ref IS NOT NULL`)
806
+ .prepare("SELECT DISTINCT entry_ref AS ref FROM usage_events WHERE entry_id IS NULL AND entry_ref IS NOT NULL")
805
807
  .all();
806
- const update = stateDb.prepare(`UPDATE ${usageEvents} SET entry_id = ? WHERE entry_ref = ? AND entry_id IS NULL`);
808
+ const update = stateDb.prepare("UPDATE usage_events SET entry_id = ? WHERE entry_ref = ? AND entry_id IS NULL");
807
809
  const relinkTx = stateDb.transaction(() => {
808
810
  for (const { ref } of refs) {
809
811
  let id;
@@ -23,7 +23,7 @@ import { warn } from "../../core/warn.js";
23
23
  * Durable identity columns are surfaced here so every mapped entry carries
24
24
  * current indexed provenance.
25
25
  */
26
- export const ENTRY_COLUMNS = "id, item_ref, bundle_id, component_id, concept_id, adapter_id, type, file_path, content_hash, document_json, search_text";
26
+ export const ENTRY_COLUMNS = "id, item_ref, bundle_id, component_id, concept_id, adapter_id, type, file_path, content_hash, document_json";
27
27
  /**
28
28
  * Map one raw `entries` row to a {@link DbIndexedEntry}, parsing `document_json`.
29
29
  * Returns `null` (and warns, tagged with `context`) when the JSON is corrupt so
@@ -42,7 +42,6 @@ export function rowToIndexedEntry(row, context) {
42
42
  id: row.id,
43
43
  filePath: row.file_path,
44
44
  entry,
45
- searchText: row.search_text,
46
45
  itemRef: row.item_ref,
47
46
  conceptId: row.concept_id,
48
47
  bundleId: row.bundle_id,