akm-cli 0.9.0-beta.9 → 0.9.0-rc.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 (381) hide show
  1. package/CHANGELOG.md +715 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/help/help-improve.md +9 -6
  6. package/dist/assets/hints/cli-hints-full.md +6 -5
  7. package/dist/assets/profiles/default.json +9 -4
  8. package/dist/assets/profiles/frequent.json +1 -1
  9. package/dist/assets/profiles/memory-focus.json +1 -1
  10. package/dist/assets/profiles/proactive-maintenance.json +25 -0
  11. package/dist/assets/profiles/quick.json +1 -1
  12. package/dist/assets/profiles/recombine-only.json +21 -0
  13. package/dist/assets/profiles/reflect-distill.json +30 -0
  14. package/dist/assets/profiles/synthesize.json +15 -0
  15. package/dist/assets/profiles/thorough.json +1 -1
  16. package/dist/assets/prompts/consolidate-system.md +23 -0
  17. package/dist/assets/prompts/contradiction-judge.md +33 -0
  18. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  19. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  20. package/dist/assets/prompts/extract-session.md +11 -3
  21. package/dist/assets/prompts/graph-extract-system.md +1 -0
  22. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  23. package/dist/assets/prompts/memory-infer-system.md +1 -0
  24. package/dist/assets/prompts/memory-infer-user.md +5 -0
  25. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  26. package/dist/assets/prompts/procedural-system.md +44 -0
  27. package/dist/assets/prompts/recombine-system.md +40 -0
  28. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  29. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  30. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  34. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  35. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  36. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  37. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  38. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  39. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  40. package/dist/assets/templates/html/health.html +281 -111
  41. package/dist/assets/wiki/ingest-workflow-template.md +45 -16
  42. package/dist/assets/wiki/schema-template.md +4 -4
  43. package/dist/cli/clack.js +56 -0
  44. package/dist/cli/config-migrate.js +7 -1
  45. package/dist/cli/confirm.js +1 -1
  46. package/dist/cli/parse-args.js +46 -1
  47. package/dist/cli/shared.js +28 -0
  48. package/dist/cli.js +25 -21
  49. package/dist/commands/agent/agent-dispatch.js +3 -2
  50. package/dist/commands/agent/agent-support.js +0 -7
  51. package/dist/commands/agent/contribute-cli.js +26 -7
  52. package/dist/commands/config-cli.js +26 -13
  53. package/dist/commands/env/child-env.js +47 -0
  54. package/dist/commands/env/env-binding.js +95 -0
  55. package/dist/commands/env/env-cli.js +228 -292
  56. package/dist/commands/env/env.js +14 -67
  57. package/dist/commands/env/secret-cli.js +140 -138
  58. package/dist/commands/feedback-cli.js +156 -155
  59. package/dist/commands/graph/graph-cli.js +5 -13
  60. package/dist/commands/graph/graph.js +3 -3
  61. package/dist/commands/health/advisories.js +151 -0
  62. package/dist/commands/health/checks.js +103 -16
  63. package/dist/commands/health/html-report.js +447 -81
  64. package/dist/commands/health/improve-metrics.js +771 -0
  65. package/dist/commands/health/llm-usage.js +65 -0
  66. package/dist/commands/health/md-report.js +103 -0
  67. package/dist/commands/health/metrics.js +278 -0
  68. package/dist/commands/health/stash-exposure.js +46 -0
  69. package/dist/commands/health/surfaces.js +216 -0
  70. package/dist/commands/health/task-runs.js +135 -0
  71. package/dist/commands/health/types.js +26 -0
  72. package/dist/commands/health/windows.js +195 -0
  73. package/dist/commands/health.js +91 -1091
  74. package/dist/commands/improve/anti-collapse.js +170 -0
  75. package/dist/commands/improve/calibration.js +161 -0
  76. package/dist/commands/improve/collapse-detector.js +421 -0
  77. package/dist/commands/improve/consolidate/chunking.js +141 -0
  78. package/dist/commands/improve/consolidate/eligibility.js +64 -0
  79. package/dist/commands/improve/consolidate/merge.js +145 -0
  80. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  81. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  82. package/dist/commands/improve/consolidate.js +1295 -1277
  83. package/dist/commands/improve/dedup.js +482 -0
  84. package/dist/commands/improve/distill/content-repair.js +202 -0
  85. package/dist/commands/improve/distill/promote-memory.js +229 -0
  86. package/dist/commands/improve/distill/quality-gate.js +236 -0
  87. package/dist/commands/improve/distill-guards.js +127 -0
  88. package/dist/commands/improve/distill-promotion-policy.js +826 -167
  89. package/dist/commands/improve/distill.js +228 -605
  90. package/dist/commands/improve/eligibility.js +434 -0
  91. package/dist/commands/improve/encoding-salience.js +205 -0
  92. package/dist/commands/improve/extract-cli.js +179 -59
  93. package/dist/commands/improve/extract-prompt.js +54 -3
  94. package/dist/commands/improve/extract-watch.js +140 -0
  95. package/dist/commands/improve/extract.js +409 -43
  96. package/dist/commands/improve/feedback-valence.js +54 -0
  97. package/dist/commands/improve/hot-probation.js +45 -0
  98. package/dist/commands/improve/improve-auto-accept.js +157 -10
  99. package/dist/commands/improve/improve-cli.js +115 -73
  100. package/dist/commands/improve/improve-profiles.js +28 -8
  101. package/dist/commands/improve/improve-result-file.js +15 -25
  102. package/dist/commands/improve/improve-session.js +58 -0
  103. package/dist/commands/improve/improve.js +485 -2764
  104. package/dist/commands/improve/locks.js +154 -0
  105. package/dist/commands/improve/loop-stages.js +1100 -0
  106. package/dist/commands/improve/memory/memory-belief.js +14 -15
  107. package/dist/commands/improve/memory/memory-contradiction-detect.js +83 -60
  108. package/dist/commands/improve/memory/memory-improve.js +27 -27
  109. package/dist/commands/improve/outcome-loop.js +270 -0
  110. package/dist/commands/improve/preparation.js +2002 -0
  111. package/dist/commands/improve/proactive-maintenance.js +37 -35
  112. package/dist/commands/improve/procedural.js +398 -0
  113. package/dist/commands/improve/recombine.js +818 -0
  114. package/dist/commands/improve/reflect-noise.js +0 -0
  115. package/dist/commands/improve/reflect.js +206 -45
  116. package/dist/commands/improve/salience.js +455 -0
  117. package/dist/commands/improve/schema-similarity-gate.js +168 -0
  118. package/dist/commands/improve/shared.js +51 -0
  119. package/dist/commands/improve/triage.js +93 -0
  120. package/dist/commands/lint/agent-linter.js +19 -24
  121. package/dist/commands/lint/base-linter.js +173 -60
  122. package/dist/commands/lint/command-linter.js +19 -24
  123. package/dist/commands/lint/env-key-rules.js +38 -1
  124. package/dist/commands/lint/fact-linter.js +39 -0
  125. package/dist/commands/lint/index.js +31 -13
  126. package/dist/commands/lint/memory-linter.js +1 -1
  127. package/dist/commands/lint/registry.js +7 -2
  128. package/dist/commands/lint/task-linter.js +3 -3
  129. package/dist/commands/lint/workflow-linter.js +26 -1
  130. package/dist/commands/observability-cli.js +4 -4
  131. package/dist/commands/proposal/drain-policies.js +13 -4
  132. package/dist/commands/proposal/drain.js +45 -51
  133. package/dist/commands/proposal/legacy-import.js +115 -0
  134. package/dist/commands/proposal/proposal-cli.js +24 -34
  135. package/dist/commands/proposal/proposal.js +2 -1
  136. package/dist/commands/proposal/propose.js +8 -3
  137. package/dist/commands/proposal/repository.js +829 -0
  138. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  139. package/dist/commands/proposal/validators/proposals.js +93 -895
  140. package/dist/commands/read/curate.js +410 -111
  141. package/dist/commands/read/knowledge.js +10 -3
  142. package/dist/commands/read/remember-cli.js +133 -138
  143. package/dist/commands/read/search-cli.js +15 -8
  144. package/dist/commands/read/search.js +22 -11
  145. package/dist/commands/read/show.js +106 -14
  146. package/dist/commands/registry-cli.js +76 -87
  147. package/dist/commands/remember.js +11 -12
  148. package/dist/commands/sources/add-cli.js +91 -95
  149. package/dist/commands/sources/history.js +1 -1
  150. package/dist/commands/sources/init.js +66 -18
  151. package/dist/commands/sources/installed-stashes.js +11 -3
  152. package/dist/commands/sources/migration-help.js +7 -4
  153. package/dist/commands/sources/schema-repair.js +44 -46
  154. package/dist/commands/sources/self-update.js +2 -2
  155. package/dist/commands/sources/source-add.js +7 -3
  156. package/dist/commands/sources/sources-cli.js +3 -3
  157. package/dist/commands/sources/stash-cli.js +19 -39
  158. package/dist/commands/sources/stash-skeleton.js +57 -8
  159. package/dist/commands/tasks/default-tasks.js +15 -2
  160. package/dist/commands/tasks/tasks-cli.js +20 -29
  161. package/dist/commands/tasks/tasks.js +39 -11
  162. package/dist/commands/wiki-cli.js +23 -38
  163. package/dist/commands/workflow-cli.js +291 -13
  164. package/dist/core/asset/asset-registry.js +3 -1
  165. package/dist/core/asset/asset-spec.js +79 -5
  166. package/dist/core/asset/frontmatter.js +188 -167
  167. package/dist/core/asset/markdown.js +8 -0
  168. package/dist/core/authoring-rules.js +92 -0
  169. package/dist/core/common.js +4 -23
  170. package/dist/core/concurrent.js +10 -1
  171. package/dist/core/config/config-io.js +10 -1
  172. package/dist/core/config/config-migration.js +18 -40
  173. package/dist/core/config/config-schema.js +403 -62
  174. package/dist/core/config/config-types.js +3 -3
  175. package/dist/core/config/config.js +67 -22
  176. package/dist/core/deep-merge.js +38 -0
  177. package/dist/core/errors.js +1 -0
  178. package/dist/core/eval/rank-metrics.js +113 -0
  179. package/dist/core/events.js +4 -7
  180. package/dist/core/improve-types.js +47 -8
  181. package/dist/core/json-schema.js +142 -0
  182. package/dist/core/logs-db.js +14 -75
  183. package/dist/core/parse.js +36 -16
  184. package/dist/core/paths.js +18 -18
  185. package/dist/core/standards/resolve-standards-context.js +87 -0
  186. package/dist/core/standards/resolve-stash-standards.js +99 -0
  187. package/dist/core/standards/resolve-type-conventions.js +66 -0
  188. package/dist/core/state/migrations.js +770 -0
  189. package/dist/core/state-db.js +132 -1126
  190. package/dist/core/structured.js +69 -0
  191. package/dist/core/time.js +53 -0
  192. package/dist/core/warn.js +21 -0
  193. package/dist/core/write-source.js +37 -0
  194. package/dist/indexer/db/db.js +261 -770
  195. package/dist/indexer/db/entry-mapper.js +41 -0
  196. package/dist/indexer/db/graph-db.js +129 -86
  197. package/dist/indexer/db/llm-cache.js +2 -2
  198. package/dist/indexer/db/schema.js +516 -0
  199. package/dist/indexer/ensure-index.js +36 -92
  200. package/dist/indexer/feedback/utility-policy.js +75 -0
  201. package/dist/indexer/graph/graph-boost.js +51 -41
  202. package/dist/indexer/graph/graph-extraction.js +207 -4
  203. package/dist/indexer/index-writer-lock.js +18 -11
  204. package/dist/indexer/index-written-assets.js +105 -0
  205. package/dist/indexer/indexer.js +182 -204
  206. package/dist/indexer/passes/dir-staleness.js +114 -0
  207. package/dist/indexer/passes/memory-inference.js +13 -5
  208. package/dist/indexer/passes/metadata.js +20 -0
  209. package/dist/indexer/read-preflight.js +23 -0
  210. package/dist/indexer/search/db-search.js +89 -13
  211. package/dist/indexer/search/fts-query.js +51 -0
  212. package/dist/indexer/search/ranking-contributors.js +95 -9
  213. package/dist/indexer/search/ranking.js +79 -3
  214. package/dist/indexer/search/search-fields.js +6 -0
  215. package/dist/indexer/search/search-source.js +32 -21
  216. package/dist/indexer/search/semantic-status.js +4 -0
  217. package/dist/indexer/walk/matchers.js +48 -0
  218. package/dist/indexer/walk/walker.js +21 -13
  219. package/dist/integrations/agent/builders.js +41 -13
  220. package/dist/integrations/agent/config.js +20 -59
  221. package/dist/integrations/agent/detect.js +9 -0
  222. package/dist/integrations/agent/index.js +3 -19
  223. package/dist/integrations/agent/model-aliases.js +16 -2
  224. package/dist/integrations/agent/profiles.js +79 -6
  225. package/dist/integrations/agent/prompts.js +75 -9
  226. package/dist/integrations/agent/runner-dispatch.js +83 -0
  227. package/dist/integrations/agent/runner.js +13 -9
  228. package/dist/integrations/agent/spawn.js +206 -81
  229. package/dist/integrations/harnesses/aider/agent-builder.js +113 -0
  230. package/dist/integrations/harnesses/aider/index.js +58 -0
  231. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  232. package/dist/integrations/harnesses/amazonq/agent-builder.js +153 -0
  233. package/dist/integrations/harnesses/amazonq/index.js +59 -0
  234. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  235. package/dist/integrations/harnesses/claude/agent-builder.js +46 -7
  236. package/dist/integrations/harnesses/claude/index.js +27 -23
  237. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  238. package/dist/integrations/harnesses/claude/session-log.js +10 -0
  239. package/dist/integrations/harnesses/codex/agent-builder.js +137 -0
  240. package/dist/integrations/harnesses/codex/index.js +63 -0
  241. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  242. package/dist/integrations/harnesses/copilot/agent-builder.js +122 -0
  243. package/dist/integrations/harnesses/copilot/index.js +60 -0
  244. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  245. package/dist/integrations/harnesses/gemini/agent-builder.js +121 -0
  246. package/dist/integrations/harnesses/gemini/index.js +60 -0
  247. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  248. package/dist/integrations/harnesses/index.js +28 -7
  249. package/dist/integrations/harnesses/opencode/agent-builder.js +1 -1
  250. package/dist/integrations/harnesses/opencode/index.js +17 -16
  251. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  252. package/dist/integrations/harnesses/opencode-sdk/harness.js +65 -0
  253. package/dist/integrations/harnesses/opencode-sdk/index.js +10 -34
  254. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +642 -71
  255. package/dist/integrations/harnesses/openhands/agent-builder.js +126 -0
  256. package/dist/integrations/harnesses/openhands/index.js +58 -0
  257. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  258. package/dist/integrations/harnesses/pi/agent-builder.js +104 -0
  259. package/dist/integrations/harnesses/pi/index.js +58 -0
  260. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  261. package/dist/integrations/harnesses/types.js +8 -0
  262. package/dist/integrations/session-logs/index.js +40 -11
  263. package/dist/llm/call-ai.js +2 -2
  264. package/dist/llm/client.js +34 -11
  265. package/dist/llm/embedder.js +67 -4
  266. package/dist/llm/embedders/cache.js +3 -1
  267. package/dist/llm/embedders/deterministic.js +66 -0
  268. package/dist/llm/embedders/local.js +73 -3
  269. package/dist/llm/feature-gate.js +16 -15
  270. package/dist/llm/graph-extract.js +67 -44
  271. package/dist/llm/memory-infer-impl.js +138 -0
  272. package/dist/llm/memory-infer.js +1 -127
  273. package/dist/llm/metadata-enhance.js +44 -31
  274. package/dist/llm/structured-call.js +49 -0
  275. package/dist/migrate-storage-node.mjs +8 -0
  276. package/dist/output/context.js +5 -5
  277. package/dist/output/renderers.js +87 -15
  278. package/dist/output/shapes/curate.js +14 -2
  279. package/dist/output/shapes/helpers.js +0 -3
  280. package/dist/output/shapes/passthrough.js +6 -1
  281. package/dist/output/text/helpers.js +241 -2
  282. package/dist/output/text/workflow.js +4 -1
  283. package/dist/registry/providers/skills-sh.js +21 -147
  284. package/dist/registry/providers/static-index.js +15 -157
  285. package/dist/registry/resolve.js +27 -9
  286. package/dist/runtime.js +25 -1
  287. package/dist/schemas/akm-config.json +14225 -0
  288. package/dist/schemas/akm-workflow.json +328 -0
  289. package/dist/scripts/migrate-storage.js +2743 -8390
  290. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +1652 -607
  291. package/dist/setup/detect.js +9 -0
  292. package/dist/setup/legacy-config.js +106 -0
  293. package/dist/setup/prompt.js +57 -0
  294. package/dist/setup/providers.js +14 -0
  295. package/dist/setup/registry-stash-loader.js +12 -0
  296. package/dist/setup/semantic-assets.js +124 -0
  297. package/dist/setup/setup.js +52 -1614
  298. package/dist/setup/steps/connection.js +734 -0
  299. package/dist/setup/steps/output.js +31 -0
  300. package/dist/setup/steps/platforms.js +124 -0
  301. package/dist/setup/steps/semantic.js +27 -0
  302. package/dist/setup/steps/sources.js +222 -0
  303. package/dist/setup/steps/stashdir.js +42 -0
  304. package/dist/setup/steps/tasks.js +152 -0
  305. package/dist/sources/include.js +6 -2
  306. package/dist/sources/providers/filesystem.js +0 -1
  307. package/dist/sources/providers/git-install.js +210 -0
  308. package/dist/sources/providers/git-provider.js +234 -0
  309. package/dist/sources/providers/git-stash.js +248 -0
  310. package/dist/sources/providers/git.js +10 -661
  311. package/dist/sources/providers/npm.js +2 -6
  312. package/dist/sources/providers/provider-utils.js +13 -7
  313. package/dist/sources/providers/sync-from-ref.js +9 -1
  314. package/dist/sources/providers/website.js +9 -5
  315. package/dist/sources/website-ingest.js +187 -29
  316. package/dist/sources/wiki-fetchers/registry.js +53 -0
  317. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  318. package/dist/storage/database.js +45 -10
  319. package/dist/storage/managed-db.js +82 -0
  320. package/dist/storage/repositories/canaries-repository.js +107 -0
  321. package/dist/storage/repositories/consolidation-repository.js +38 -0
  322. package/dist/storage/repositories/embeddings-repository.js +72 -0
  323. package/dist/storage/repositories/events-repository.js +187 -0
  324. package/dist/storage/repositories/extract-sessions-repository.js +96 -0
  325. package/dist/storage/repositories/improve-runs-repository.js +146 -0
  326. package/dist/storage/repositories/index-db.js +14 -8
  327. package/dist/storage/repositories/proposals-repository.js +220 -0
  328. package/dist/storage/repositories/recombine-repository.js +213 -0
  329. package/dist/storage/repositories/registry-cache.js +93 -0
  330. package/dist/storage/repositories/registry-index-cache-repository.js +46 -0
  331. package/dist/storage/repositories/task-history-repository.js +93 -0
  332. package/dist/storage/repositories/workflow-runs-repository.js +189 -1
  333. package/dist/storage/sqlite-pragmas.js +146 -0
  334. package/dist/tasks/backends/cron.js +1 -1
  335. package/dist/tasks/backends/index.js +9 -0
  336. package/dist/tasks/backends/launchd.js +1 -1
  337. package/dist/tasks/backends/schtasks.js +1 -1
  338. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  339. package/dist/tasks/runner.js +15 -13
  340. package/dist/text-import-hook.mjs +1 -1
  341. package/dist/wiki/wiki.js +52 -11
  342. package/dist/workflows/authoring/authoring.js +123 -10
  343. package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
  344. package/dist/workflows/cli.js +5 -0
  345. package/dist/workflows/db.js +138 -4
  346. package/dist/workflows/exec/brief.js +484 -0
  347. package/dist/workflows/exec/native-executor.js +975 -0
  348. package/dist/workflows/exec/param-secrets.js +115 -0
  349. package/dist/workflows/exec/report.js +1295 -0
  350. package/dist/workflows/exec/run-workflow.js +596 -0
  351. package/dist/workflows/exec/scheduler.js +100 -0
  352. package/dist/workflows/exec/step-work.js +1156 -0
  353. package/dist/workflows/exec/unit-writer.js +23 -0
  354. package/dist/workflows/exec/watch.js +116 -0
  355. package/dist/workflows/exec/worktree.js +171 -0
  356. package/dist/workflows/ir/compile.js +388 -0
  357. package/dist/workflows/ir/params.js +54 -0
  358. package/dist/workflows/ir/plan-hash.js +33 -0
  359. package/dist/workflows/ir/schema.js +4 -0
  360. package/dist/workflows/parser.js +3 -1
  361. package/dist/workflows/program/expressions.js +369 -0
  362. package/dist/workflows/program/parser.js +760 -0
  363. package/dist/workflows/program/project.js +105 -0
  364. package/dist/workflows/program/schema.js +54 -0
  365. package/dist/workflows/renderer.js +82 -5
  366. package/dist/workflows/runtime/agent-identity.js +59 -14
  367. package/dist/workflows/runtime/runs.js +248 -153
  368. package/dist/workflows/runtime/unit-checkin.js +45 -0
  369. package/dist/workflows/runtime/workflow-asset-loader.js +188 -0
  370. package/dist/workflows/validate-summary.js +26 -10
  371. package/dist/workflows/validator.js +1 -1
  372. package/docs/README.md +69 -18
  373. package/docs/data-and-telemetry.md +7 -5
  374. package/docs/migration/release-notes/0.7.0.md +1 -1
  375. package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
  376. package/docs/migration/release-notes/0.9.0.md +39 -0
  377. package/package.json +10 -10
  378. package/dist/assets/tasks/core/update-stashes.yml +0 -4
  379. package/dist/commands/db-cli.js +0 -23
  380. package/dist/indexer/db/db-backup.js +0 -376
  381. package/dist/indexer/passes/staleness-detect.js +0 -488
@@ -0,0 +1,770 @@
1
+ // This Source Code Form is subject to the terms of the Mozilla Public
2
+ // License, v. 2.0. If a copy of the MPL was not distributed with this
3
+ // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ import { runMigrations as runSqliteMigrations } from "../../storage/engines/sqlite-migrations.js";
5
+ const MIGRATIONS = [
6
+ // ── Migration 001 — initial schema ──────────────────────────────────────────
7
+ {
8
+ id: "001-initial-schema",
9
+ up: `
10
+ -- ── events ──────────────────────────────────────────────────────────────
11
+ --
12
+ -- Replaces events.jsonl. Indexed (query) columns:
13
+ -- id INTEGER PK — monotonic rowid; replaces byte-offset cursor.
14
+ -- Callers store this as "sinceId" for resume.
15
+ -- event_type TEXT — indexed; replaces the type filter in readEvents().
16
+ -- ts TEXT — ISO-8601 UTC ms; indexed for range queries.
17
+ -- ref TEXT — nullable asset ref; indexed for ref-scoped queries.
18
+ --
19
+ -- Extensible (metadata_json) columns:
20
+ -- metadata_json TEXT — JSON object storing all non-indexed payload
21
+ -- fields (tags, any future structured fields).
22
+ -- Maps directly to EventEnvelope.metadata.
23
+ --
24
+ -- schema_version mirrors EventEnvelope.schemaVersion — always 1 for v1
25
+ -- rows. Stored as a column (not in the JSON blob) so future schema
26
+ -- changes can be detected and migrated row-by-row if ever needed.
27
+ --
28
+ -- TTL: rows where ts < NOW() - 90 days can be deleted by a maintenance job.
29
+ -- No automatic deletion occurs here — callers call purgeOldEvents().
30
+ --
31
+ -- ADD COLUMN extension points (future migrations):
32
+ -- ALTER TABLE events ADD COLUMN stash_dir TEXT DEFAULT NULL;
33
+ -- ALTER TABLE events ADD COLUMN correlation_id TEXT DEFAULT NULL;
34
+ -- ALTER TABLE events ADD COLUMN schema_version INTEGER NOT NULL DEFAULT 1;
35
+ --
36
+ CREATE TABLE IF NOT EXISTS events (
37
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
38
+ event_type TEXT NOT NULL,
39
+ ts TEXT NOT NULL,
40
+ ref TEXT,
41
+ metadata_json TEXT NOT NULL DEFAULT '{}'
42
+ );
43
+
44
+ -- Query patterns supported by these indexes:
45
+ -- SELECT … WHERE event_type = ? → idx_events_type
46
+ -- SELECT … WHERE ref = ? → idx_events_ref
47
+ -- SELECT … WHERE ts >= ? AND ts <= ? → idx_events_ts
48
+ -- SELECT … WHERE event_type = ? AND ref = ? → idx_events_type (prefix scan) + filter
49
+ -- SELECT … WHERE id > ? → PK (rowid) — no extra index needed
50
+ CREATE INDEX IF NOT EXISTS idx_events_type ON events(event_type);
51
+ CREATE INDEX IF NOT EXISTS idx_events_ref ON events(ref);
52
+ CREATE INDEX IF NOT EXISTS idx_events_ts ON events(ts);
53
+
54
+ -- ── proposals ────────────────────────────────────────────────────────────
55
+ --
56
+ -- Replaces per-uuid JSON directories under <stashDir>/.akm/proposals/.
57
+ --
58
+ -- Indexed (query) columns:
59
+ -- id TEXT PK — UUID (crypto.randomUUID()); stable directory name.
60
+ -- stash_dir TEXT — absolute stash root; multi-stash installs need
61
+ -- this to partition proposal lists per stash.
62
+ -- ref TEXT — target asset ref (e.g. "lesson:alpha");
63
+ -- indexed for ref-scoped queue views.
64
+ -- status TEXT — "pending" | "accepted" | "rejected"; indexed
65
+ -- so pending-queue queries are fast.
66
+ -- source TEXT — human-readable origin tag (e.g. "reflect").
67
+ -- created_at TEXT — ISO-8601; used for ORDER BY created_at ASC.
68
+ -- updated_at TEXT — ISO-8601; updated on accept/reject.
69
+ --
70
+ -- Large payload columns (NOT indexed):
71
+ -- content TEXT — full markdown text; the proposal payload body.
72
+ -- frontmatter_json TEXT — JSON of parsed frontmatter (may be NULL when
73
+ -- the content has no frontmatter block).
74
+ --
75
+ -- Extensible (metadata_json) columns:
76
+ -- metadata_json TEXT — JSON object for future proposal fields.
77
+ -- Current fields stored here: sourceRun,
78
+ -- review, confidence, gateDecision (#577),
79
+ -- backupContent, eligibilitySource.
80
+ --
81
+ -- ADD COLUMN extension points (future migrations):
82
+ -- ALTER TABLE proposals ADD COLUMN source_run TEXT DEFAULT NULL;
83
+ -- ALTER TABLE proposals ADD COLUMN review_outcome TEXT DEFAULT NULL;
84
+ -- ALTER TABLE proposals ADD COLUMN review_reason TEXT DEFAULT NULL;
85
+ -- ALTER TABLE proposals ADD COLUMN review_decided_at TEXT DEFAULT NULL;
86
+ -- ALTER TABLE proposals ADD COLUMN archived INTEGER NOT NULL DEFAULT 0;
87
+ --
88
+ CREATE TABLE IF NOT EXISTS proposals (
89
+ id TEXT PRIMARY KEY,
90
+ stash_dir TEXT NOT NULL,
91
+ ref TEXT NOT NULL,
92
+ status TEXT NOT NULL DEFAULT 'pending',
93
+ source TEXT NOT NULL,
94
+ created_at TEXT NOT NULL,
95
+ updated_at TEXT NOT NULL,
96
+ content TEXT NOT NULL DEFAULT '',
97
+ frontmatter_json TEXT,
98
+ metadata_json TEXT NOT NULL DEFAULT '{}'
99
+ );
100
+
101
+ -- Query patterns:
102
+ -- SELECT … WHERE stash_dir = ? AND status = ? → idx_proposals_stash_status
103
+ -- SELECT … WHERE ref = ? AND status = ? → idx_proposals_ref_status
104
+ -- SELECT … WHERE id = ? → PK
105
+ CREATE INDEX IF NOT EXISTS idx_proposals_stash_status
106
+ ON proposals(stash_dir, status);
107
+ CREATE INDEX IF NOT EXISTS idx_proposals_ref_status
108
+ ON proposals(ref, status);
109
+
110
+ -- ── task_history ─────────────────────────────────────────────────────────
111
+ --
112
+ -- Replaces per-task JSONL files under <cacheDir>/tasks/history/.
113
+ --
114
+ -- Indexed (query) columns:
115
+ -- task_id TEXT PK — stable task identifier string.
116
+ -- status TEXT — terminal status (e.g. "completed", "failed",
117
+ -- "cancelled"); indexed for status-scoped queries.
118
+ -- started_at TEXT — ISO-8601; indexed for time-range queries.
119
+ -- target_kind TEXT — kind of the target entity (e.g. "issue",
120
+ -- "workflow", "agent"); indexed for kind-scoped queries.
121
+ -- target_ref TEXT — stable ref of the target entity; indexed for
122
+ -- per-target history lookups.
123
+ --
124
+ -- Non-indexed time columns:
125
+ -- completed_at TEXT — ISO-8601 or NULL if still running.
126
+ -- failed_at TEXT — ISO-8601 or NULL.
127
+ --
128
+ -- Non-indexed diagnostic columns:
129
+ -- log_path TEXT — absolute path to the task log file, if any.
130
+ --
131
+ -- Extensible (metadata_json) columns:
132
+ -- metadata_json TEXT — JSON object for future task fields (exit_code,
133
+ -- runner, priority, parent_task_id, …).
134
+ --
135
+ -- ADD COLUMN extension points (future migrations):
136
+ -- ALTER TABLE task_history ADD COLUMN exit_code INTEGER DEFAULT NULL;
137
+ -- ALTER TABLE task_history ADD COLUMN runner TEXT DEFAULT NULL;
138
+ -- ALTER TABLE task_history ADD COLUMN parent_task_id TEXT DEFAULT NULL;
139
+ -- ALTER TABLE task_history ADD COLUMN priority INTEGER NOT NULL DEFAULT 0;
140
+ --
141
+ CREATE TABLE IF NOT EXISTS task_history (
142
+ task_id TEXT PRIMARY KEY,
143
+ status TEXT NOT NULL,
144
+ started_at TEXT NOT NULL,
145
+ completed_at TEXT,
146
+ failed_at TEXT,
147
+ log_path TEXT,
148
+ target_kind TEXT,
149
+ target_ref TEXT,
150
+ metadata_json TEXT NOT NULL DEFAULT '{}'
151
+ );
152
+
153
+ -- Query patterns:
154
+ -- SELECT … WHERE task_id = ? → PK
155
+ -- SELECT … WHERE started_at >= ? AND started_at <= ? → idx_task_history_started
156
+ -- SELECT … WHERE target_kind = ? AND target_ref = ? → idx_task_history_target
157
+ -- SELECT … WHERE status = ? → idx_task_history_status
158
+ CREATE INDEX IF NOT EXISTS idx_task_history_started
159
+ ON task_history(started_at);
160
+ CREATE INDEX IF NOT EXISTS idx_task_history_target
161
+ ON task_history(target_kind, target_ref);
162
+ CREATE INDEX IF NOT EXISTS idx_task_history_status
163
+ ON task_history(status);
164
+ `,
165
+ },
166
+ // Migration 002 — fix task_history to be a true per-run log.
167
+ //
168
+ // Migration 001 used task_id as PRIMARY KEY, meaning each task had exactly
169
+ // one row and every new run overwrote the previous one. This silently
170
+ // discarded all historical runs — the opposite of a history table.
171
+ //
172
+ // This migration recreates the table with an AUTOINCREMENT id so each run
173
+ // appends a new row. The old single-row table is renamed to _old, the new
174
+ // table is created, data is copied, and the old table is dropped.
175
+ {
176
+ id: "002-task-history-per-run",
177
+ up: `
178
+ ALTER TABLE task_history RENAME TO task_history_v1;
179
+
180
+ CREATE TABLE task_history (
181
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
182
+ task_id TEXT NOT NULL,
183
+ status TEXT NOT NULL,
184
+ started_at TEXT NOT NULL,
185
+ completed_at TEXT,
186
+ failed_at TEXT,
187
+ log_path TEXT,
188
+ target_kind TEXT,
189
+ target_ref TEXT,
190
+ metadata_json TEXT NOT NULL DEFAULT '{}'
191
+ );
192
+
193
+ INSERT INTO task_history
194
+ (task_id, status, started_at, completed_at, failed_at,
195
+ log_path, target_kind, target_ref, metadata_json)
196
+ SELECT task_id, status, started_at, completed_at, failed_at,
197
+ log_path, target_kind, target_ref, metadata_json
198
+ FROM task_history_v1;
199
+
200
+ DROP TABLE task_history_v1;
201
+
202
+ -- Unique constraint: same task cannot have two runs with the same start time.
203
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_task_history_run
204
+ ON task_history(task_id, started_at);
205
+ CREATE INDEX IF NOT EXISTS idx_task_history_task_id
206
+ ON task_history(task_id);
207
+ CREATE INDEX IF NOT EXISTS idx_task_history_started
208
+ ON task_history(started_at);
209
+ CREATE INDEX IF NOT EXISTS idx_task_history_target
210
+ ON task_history(target_kind, target_ref);
211
+ CREATE INDEX IF NOT EXISTS idx_task_history_status
212
+ ON task_history(status);
213
+ `,
214
+ },
215
+ // ── Migration 003 — improve_runs ────────────────────────────────────────────
216
+ //
217
+ // Records every `akm improve` invocation as a durable row, replacing the
218
+ // legacy `<stash>/.akm/runs/<runId>/improve-result.json` artifact files.
219
+ //
220
+ // The `dry_run` column is FIRST-CLASS and indexed so productivity audits can
221
+ // cleanly filter dry-run probes out of real-run analyses without parsing
222
+ // `result_json`. The dry-run/real-run artifact-trap (recorded in
223
+ // feedback_akm_dryrun_artifact_trap) was the specific motivating bug.
224
+ //
225
+ // Indexed (query) columns:
226
+ // id TEXT PK — runId (`buildImproveRunId()` output).
227
+ // started_at TEXT — ISO-8601; indexed for time-range queries.
228
+ // stash_dir TEXT — absolute stash root; multi-stash scoping.
229
+ // dry_run INTEGER — 0/1; indexed for productivity audits.
230
+ // scope_mode TEXT — "all" | "type" | "ref"; indexed via composite
231
+ // with stash_dir for stash-scoped scope queries.
232
+ //
233
+ // Non-indexed payload:
234
+ // completed_at TEXT — ISO-8601 or NULL if interrupted.
235
+ // profile TEXT — improve profile name (nullable).
236
+ // scope_value TEXT — type name or asset ref (nullable).
237
+ // guidance TEXT — user-provided guidance text, if any.
238
+ // ok INTEGER — 0/1; whether the run produced ok=true.
239
+ // result_json TEXT — full AkmImproveResult JSON.
240
+ // metrics_json TEXT — aggregate counts extracted from result, cheap
241
+ // to query without parsing result_json.
242
+ //
243
+ // Extensible (metadata_json) columns:
244
+ // metadata_json TEXT — JSON object for future improve-run fields.
245
+ //
246
+ // ADD COLUMN extension points (future migrations):
247
+ // ALTER TABLE improve_runs ADD COLUMN duration_ms INTEGER DEFAULT NULL;
248
+ // ALTER TABLE improve_runs ADD COLUMN host TEXT DEFAULT NULL;
249
+ //
250
+ // TTL: rows where started_at < NOW() - 90 days can be deleted by
251
+ // `purgeOldImproveRuns()`. No automatic deletion occurs here.
252
+ {
253
+ id: "003-improve-runs",
254
+ up: `
255
+ CREATE TABLE IF NOT EXISTS improve_runs (
256
+ id TEXT PRIMARY KEY,
257
+ started_at TEXT NOT NULL,
258
+ completed_at TEXT,
259
+ stash_dir TEXT NOT NULL,
260
+ dry_run INTEGER NOT NULL DEFAULT 0,
261
+ profile TEXT,
262
+ scope_mode TEXT NOT NULL,
263
+ scope_value TEXT,
264
+ guidance TEXT,
265
+ ok INTEGER NOT NULL,
266
+ result_json TEXT NOT NULL,
267
+ metrics_json TEXT,
268
+ metadata_json TEXT NOT NULL DEFAULT '{}'
269
+ );
270
+
271
+ -- Query patterns supported:
272
+ -- SELECT … WHERE started_at >= ? AND started_at <= ?
273
+ -- → idx_improve_runs_started
274
+ -- SELECT … WHERE dry_run = 0
275
+ -- → idx_improve_runs_dry_run (productivity audits filter trap)
276
+ -- SELECT … WHERE stash_dir = ? AND scope_mode = ?
277
+ -- → idx_improve_runs_stash_scope
278
+ CREATE INDEX IF NOT EXISTS idx_improve_runs_started
279
+ ON improve_runs(started_at);
280
+ CREATE INDEX IF NOT EXISTS idx_improve_runs_dry_run
281
+ ON improve_runs(dry_run);
282
+ CREATE INDEX IF NOT EXISTS idx_improve_runs_stash_scope
283
+ ON improve_runs(stash_dir, scope_mode);
284
+ `,
285
+ },
286
+ // ── Migration 004 — extract_sessions_seen ───────────────────────────────────
287
+ //
288
+ // Tracks which platform sessions the extractor has processed, so the discovery
289
+ // pass in `akm extract --since <window>` skips sessions whose content hasn't
290
+ // changed since the last successful run. Replaces the akm-plugin
291
+ // session-checkpoint hook's implicit "write-once" memory of what's been
292
+ // captured — but persistent and queryable.
293
+ //
294
+ // Indexed (query) columns:
295
+ // harness TEXT — harness name (claude-code, opencode, ...).
296
+ // session_id TEXT — platform-native session identifier.
297
+ // processed_at TEXT — ISO-8601 UTC; when extract last ran on this session.
298
+ // session_ended_at TEXT — session.endedAt at processing time. When a
299
+ // later listSessions reports a *newer* endedAt
300
+ // for the same session_id, the extractor
301
+ // re-processes the appended events.
302
+ // outcome TEXT — "candidates_queued" | "no_candidates" |
303
+ // "skipped" | "failed".
304
+ //
305
+ // Non-indexed columns:
306
+ // candidate_count INTEGER — number of candidates the LLM produced.
307
+ // proposal_count INTEGER — number of proposals actually queued
308
+ // (candidates may fail downstream validation).
309
+ // rationale TEXT — for "no_candidates", the LLM's explanation.
310
+ // source_run TEXT — sourceRun id for PROV-DM traceability.
311
+ // metadata_json TEXT — future-proofing (pre-filter stats, LLM
312
+ // model+version, prompt token count, etc.).
313
+ //
314
+ // PK: (harness, session_id) — one row per session per harness. A re-extract
315
+ // updates the row in place via INSERT OR REPLACE.
316
+ //
317
+ // TTL: no automatic deletion. Sessions stay tracked as long as the source
318
+ // session files exist on disk. Operator can `DELETE FROM extract_sessions_seen
319
+ // WHERE processed_at < ?` for cleanup if desired.
320
+ {
321
+ id: "004-extract-sessions-seen",
322
+ up: `
323
+ CREATE TABLE IF NOT EXISTS extract_sessions_seen (
324
+ harness TEXT NOT NULL,
325
+ session_id TEXT NOT NULL,
326
+ processed_at TEXT NOT NULL,
327
+ session_ended_at TEXT,
328
+ outcome TEXT NOT NULL,
329
+ candidate_count INTEGER NOT NULL DEFAULT 0,
330
+ proposal_count INTEGER NOT NULL DEFAULT 0,
331
+ rationale TEXT,
332
+ source_run TEXT,
333
+ metadata_json TEXT NOT NULL DEFAULT '{}',
334
+ PRIMARY KEY (harness, session_id)
335
+ );
336
+
337
+ -- Query patterns:
338
+ -- SELECT … WHERE harness = ? → idx_extract_sessions_harness
339
+ -- SELECT … WHERE processed_at >= ? → idx_extract_sessions_processed
340
+ -- SELECT … WHERE harness = ? AND session_id = ? → PK
341
+ CREATE INDEX IF NOT EXISTS idx_extract_sessions_harness
342
+ ON extract_sessions_seen(harness);
343
+ CREATE INDEX IF NOT EXISTS idx_extract_sessions_processed
344
+ ON extract_sessions_seen(processed_at);
345
+ `,
346
+ },
347
+ // ── Migration 005 — proposal_fs_imports ─────────────────────────────────────
348
+ //
349
+ // One-shot ledger for the legacy filesystem→SQLite proposal import (#578).
350
+ //
351
+ // Before 0.9.0 the proposal queue lived as per-uuid JSON directories under
352
+ // `<stashDir>/.akm/proposals/` and the `proposals` table (created in 001) was
353
+ // dead weight. 0.9.0 makes the table canonical; the first proposal operation
354
+ // against a stash imports any legacy `proposal.json` files it finds (INSERT
355
+ // OR IGNORE, so re-runs never duplicate) and records the stash here so later
356
+ // invocations skip the directory walk entirely.
357
+ //
358
+ // Indexed (query) columns:
359
+ // stash_dir TEXT PK — absolute stash root the import ran against.
360
+ //
361
+ // Non-indexed columns:
362
+ // imported_at TEXT — ISO-8601 UTC; when the import completed.
363
+ // imported_count INTEGER — rows actually inserted by the import.
364
+ {
365
+ id: "005-proposal-fs-imports",
366
+ up: `
367
+ CREATE TABLE IF NOT EXISTS proposal_fs_imports (
368
+ stash_dir TEXT PRIMARY KEY,
369
+ imported_at TEXT NOT NULL,
370
+ imported_count INTEGER NOT NULL DEFAULT 0
371
+ );
372
+ `,
373
+ },
374
+ // ── Migration 006 — pending proposal lookup index ──────────────────────────
375
+ //
376
+ // Supports the transaction-scoped dedup / queue-mutation hardening added in
377
+ // 0.9.x. The queue now acquires an IMMEDIATE write transaction before it
378
+ // reads pending proposals, so the hot path is a stash-scoped `status='pending'
379
+ // AND ref=?` probe followed by an update/insert. This composite index keeps
380
+ // that lookup index-covered under contention.
381
+ {
382
+ id: "006-proposals-pending-ref-source",
383
+ up: `
384
+ CREATE INDEX IF NOT EXISTS idx_proposals_stash_status_ref_source
385
+ ON proposals(stash_dir, status, ref, source);
386
+ `,
387
+ },
388
+ // ── Migration 007 — consolidation_judged ────────────────────────────────────
389
+ //
390
+ // Judged-state cache for nightly consolidation (#581). Lets one consolidation
391
+ // run cover the FULL memory corpus cheaply by SKIPPING memories already judged
392
+ // with unchanged content, instead of narrowing to a recent time-window slice
393
+ // (which leaves a near-duplicate backlog the corpus can never clear).
394
+ //
395
+ // The consolidate LLM judging loop UPSERTs a row for every memory it saw in a
396
+ // successfully-judged chunk; the next run hashes each candidate's current
397
+ // content and skips it when the hash equals the cached `content_hash`
398
+ // (judged-unchanged → no re-judge). A memory whose content changed produces a
399
+ // new hash and is re-judged. This converts coverage from O(window) to
400
+ // O(changed/new). DEFAULT OFF — gated behind
401
+ // `processes.consolidate.judgedCache.enabled`; when the feature is off this
402
+ // table is never read or written and behaviour is byte-identical to today.
403
+ //
404
+ // Indexed (query) columns:
405
+ // entry_key TEXT PK — `memory:<name>` ref; one row per judged memory.
406
+ //
407
+ // Non-indexed columns:
408
+ // content_hash TEXT — sha256 of the frontmatter-stripped, trimmed body.
409
+ // judged_at TEXT — ISO-8601 UTC; when the memory was last judged.
410
+ // outcome TEXT — coarse outcome of the last judge ("actioned" |
411
+ // "no_action"); observability only, never gates.
412
+ //
413
+ // TTL: no automatic deletion. Rows for memories deleted on disk become
414
+ // harmless dead entries (their entry_key never recurs); operators can prune
415
+ // with `DELETE FROM consolidation_judged WHERE judged_at < ?` if desired.
416
+ {
417
+ id: "007-consolidation-judged",
418
+ up: `
419
+ CREATE TABLE IF NOT EXISTS consolidation_judged (
420
+ entry_key TEXT PRIMARY KEY,
421
+ content_hash TEXT NOT NULL,
422
+ judged_at TEXT NOT NULL,
423
+ outcome TEXT NOT NULL
424
+ );
425
+ `,
426
+ },
427
+ // ── Migration 008 — body_embeddings ─────────────────────────────────────────
428
+ //
429
+ // cacheHash-keyed body-embedding cache (WS-3a). Stores the embedding of the
430
+ // case-preserving stripped body so the dedup pre-pass and the consolidation
431
+ // clustering step share one computed vector per unique body, eliminating
432
+ // redundant embedding calls across runs.
433
+ //
434
+ // Design:
435
+ // - PK is the `cacheHash` (sha256 of the stripped, case-preserving body).
436
+ // - `embedding` is a raw BLOB storing a Float32 array (384 floats × 4 B =
437
+ // 1 536 B per entry for the default bge-small-en-v1.5 model; ~20 MB at
438
+ // 13 k memories). This matches the native wire format and avoids JSON
439
+ // round-trip overhead.
440
+ // - `model_id` is MANDATORY. On mismatch (model changed) the entire table
441
+ // is dropped and rebuilt — stale vectors from the wrong metric space would
442
+ // produce silent cosine errors.
443
+ // - `created_at` is an INTEGER Unix ms timestamp for lazy orphan purges.
444
+ //
445
+ // Writes: one bulk `WHERE content_hash IN (…)` lookup → embed only misses →
446
+ // upsert all results in one transaction per run.
447
+ //
448
+ // TTL: no automatic row deletion. Orphaned rows for bodies no longer in the
449
+ // stash stay until an operator prunes them. The table is ~1.5 KB per row
450
+ // (~20 MB at 13 k memories — acceptable).
451
+ {
452
+ id: "008-body-embeddings",
453
+ up: `
454
+ CREATE TABLE IF NOT EXISTS body_embeddings (
455
+ content_hash TEXT PRIMARY KEY,
456
+ embedding BLOB NOT NULL,
457
+ model_id TEXT NOT NULL,
458
+ created_at INTEGER NOT NULL
459
+ );
460
+ `,
461
+ },
462
+ // ── Migration 009 — asset_salience (WS-1 salience vector) ───────────────────
463
+ //
464
+ // Per-asset salience vector persisted in state.db (canonical store).
465
+ //
466
+ // Three independently-stored, independently-decayable sub-scores:
467
+ // encoding_salience — intrinsic importance (Gap 1; v1 = type-weight stub).
468
+ // outcome_salience — differential usefulness (WS-2; 0 until that lands).
469
+ // retrieval_salience — frequency × recency (the decayable term).
470
+ //
471
+ // Plus the scalar projection for ranking:
472
+ // rank_score = (w_e·encoding + w_o·outcome + w_r·retrieval) × sizePenalty,
473
+ // normalized [0,1]. Every selector reads rank_score; individual sub-scores
474
+ // are available for telemetry and per-dimension thresholding.
475
+ //
476
+ // Plasticity column:
477
+ // consecutive_no_ops INTEGER — number of consecutive improve cycles where
478
+ // this asset produced a no-op (reflect/distill produced no change).
479
+ // Dampens CONSOLIDATION-SELECTION only — intentionally NOT applied to
480
+ // rank_score (stable assets stay retrievable but skip LLM merge passes).
481
+ //
482
+ // updated_at is an INTEGER Unix-ms timestamp for recency queries.
483
+ //
484
+ // The canonical store is state.db, not frontmatter. An optional frontmatter
485
+ // mirror of the stable encodingSalience is allowed for portability (#608).
486
+ //
487
+ // TTL: rows are overwritten on every run; orphaned rows for deleted assets
488
+ // accumulate harmlessly until an operator prunes them.
489
+ {
490
+ id: "009-asset-salience",
491
+ up: `
492
+ CREATE TABLE IF NOT EXISTS asset_salience (
493
+ asset_ref TEXT PRIMARY KEY,
494
+ encoding_salience REAL NOT NULL DEFAULT 0.5,
495
+ outcome_salience REAL NOT NULL DEFAULT 0.0,
496
+ retrieval_salience REAL NOT NULL DEFAULT 0.0,
497
+ rank_score REAL NOT NULL DEFAULT 0.0,
498
+ consecutive_no_ops INTEGER NOT NULL DEFAULT 0,
499
+ updated_at INTEGER NOT NULL DEFAULT 0
500
+ );
501
+
502
+ -- Hot path: sort / filter by rank_score for selector queries.
503
+ CREATE INDEX IF NOT EXISTS idx_asset_salience_rank
504
+ ON asset_salience(rank_score DESC);
505
+ `,
506
+ },
507
+ // ── Migration 010 — asset_outcome (WS-2 outcome loop) ───────────────────────
508
+ //
509
+ // Per-asset outcome loop persisted in state.db (S2 seam, WS-2).
510
+ //
511
+ // Stores the differential "was this retrieval useful" signal so the salience
512
+ // vector's `outcomeSalience` sub-score (WS-1 `W_OUTCOME` term) is non-zero.
513
+ //
514
+ // Columns:
515
+ // asset_ref TEXT PK — `type:name` asset ref (FK to asset_salience).
516
+ // last_retrieved_at INTEGER — Unix-ms of the most recent retrieval.
517
+ // retrieval_count INTEGER — total retrieval count from the index DB.
518
+ // expected_retrieval_rate REAL — EMA-smoothed expected count per cycle.
519
+ // negative_feedback_count INTEGER — cumulative negative-feedback events.
520
+ // accepted_change_count INTEGER — cumulative accepted proposals.
521
+ // review_pressure INTEGER — #613 pressure counter: repeated low-satisfaction
522
+ // retrievals increment it; feeds outcomeSalience.
523
+ // outcome_score REAL — differential outcome signal (can be negative).
524
+ // updated_at INTEGER — Unix-ms timestamp of last update.
525
+ //
526
+ // Design:
527
+ // - outcome_score is differential (prediction-error shaped), NOT a raw count,
528
+ // so it rewards assets that are retrieved MORE than their rolling mean AND
529
+ // accepted for change when retrieved. See outcome-loop.ts for the formula.
530
+ // - review_pressure (#613): repeated negative-feedback retrievals raise it,
531
+ // non-negative cycles decay it. Never mutates asset content directly.
532
+ // - warm_start: seeded from utility EMA at row creation, clipped to [0, 0.3]
533
+ // so the first negative delta does not cause a spurious rank inversion.
534
+ // - Orphaned rows (deleted assets) accumulate harmlessly; operators can prune
535
+ // with `DELETE FROM asset_outcome WHERE updated_at < ?` if desired.
536
+ {
537
+ id: "010-asset-outcome",
538
+ up: `
539
+ CREATE TABLE IF NOT EXISTS asset_outcome (
540
+ asset_ref TEXT PRIMARY KEY,
541
+ last_retrieved_at INTEGER NOT NULL DEFAULT 0,
542
+ retrieval_count INTEGER NOT NULL DEFAULT 0,
543
+ expected_retrieval_rate REAL NOT NULL DEFAULT 0.0,
544
+ negative_feedback_count INTEGER NOT NULL DEFAULT 0,
545
+ accepted_change_count INTEGER NOT NULL DEFAULT 0,
546
+ review_pressure INTEGER NOT NULL DEFAULT 0,
547
+ outcome_score REAL NOT NULL DEFAULT 0.0,
548
+ updated_at INTEGER NOT NULL DEFAULT 0
549
+ );
550
+
551
+ -- Hot path: sort assets by review_pressure DESC for #613 admission.
552
+ CREATE INDEX IF NOT EXISTS idx_asset_outcome_review_pressure
553
+ ON asset_outcome(review_pressure DESC);
554
+
555
+ -- Secondary: sort by outcome_score DESC for outcomeSalience reads.
556
+ CREATE INDEX IF NOT EXISTS idx_asset_outcome_score
557
+ ON asset_outcome(outcome_score DESC);
558
+ `,
559
+ },
560
+ // ── Migration 011 — asset_salience: homeostatic_demoted_at column ─────────────
561
+ //
562
+ // WS-3b step 0a (homeostatic demotion). Records the last time `retrievalSalience`
563
+ // was demoted for this asset so:
564
+ // (a) Each run can identify assets that have been demoted but not yet
565
+ // re-retrieved (they stay in the demoted state until a retrieval
566
+ // re-promotes them via `upsertAssetSalience`).
567
+ // (b) The homeostatic pass can log "N assets demoted this run".
568
+ //
569
+ // NULL = never demoted (or was re-promoted after last demotion, since a fresh
570
+ // `upsertAssetSalience` call clears the flag by updating retrieval_salience
571
+ // from live data rather than the demoted value — the column is informational,
572
+ // not the canonical source of the salience value).
573
+ {
574
+ id: "011-asset-salience-homeostatic-demoted-at",
575
+ up: `
576
+ ALTER TABLE asset_salience ADD COLUMN homeostatic_demoted_at INTEGER DEFAULT NULL;
577
+ `,
578
+ },
579
+ // ── Migration 012 — improve_gate_thresholds (WS-4 per-phase threshold store) ─
580
+ //
581
+ // Persists the auto-tuned accept-gate threshold PER PHASE so that each phase
582
+ // (reflect, distill, extract, consolidate) maintains its own calibrated
583
+ // threshold rather than sharing a single global `options.autoAccept`.
584
+ //
585
+ // Schema:
586
+ // phase TEXT PK — phase label, e.g. "reflect", "distill", "extract",
587
+ // "consolidate".
588
+ // threshold INTEGER — tuned threshold (0-100), matches the integer
589
+ // scale used everywhere else in the gate pipeline.
590
+ // updated_at INTEGER — Unix milliseconds of the last update.
591
+ //
592
+ // `makeGateConfig` reads the stored threshold for its phase (falling back to
593
+ // the caller-supplied `globalThreshold` when no row exists yet). WS-4's
594
+ // `persistPhaseThreshold` writes it after each auto-tune step.
595
+ {
596
+ id: "012-improve-gate-thresholds",
597
+ up: `
598
+ CREATE TABLE IF NOT EXISTS improve_gate_thresholds (
599
+ phase TEXT NOT NULL PRIMARY KEY,
600
+ threshold INTEGER NOT NULL,
601
+ updated_at INTEGER NOT NULL
602
+ );
603
+ `,
604
+ },
605
+ // ── Migration 013 — extract_sessions_seen: content_hash column (#602) ────────
606
+ //
607
+ // Replaces the brittle timestamp-based incrementality (`session_ended_at`,
608
+ // compared against the live session metadata) with a content hash. The old
609
+ // clock/timestamp logic caused the Jun 11-12 double-extract + over-throttle
610
+ // incident (clock skew / out-of-order endedAt both double-processed AND
611
+ // over-suppressed sessions). The hash makes the skip decision byte-exact and
612
+ // clock-independent.
613
+ //
614
+ // Additive ADD COLUMN (migration-safe; mirrors migration 011's style). All
615
+ // pre-existing rows read back `content_hash = NULL`, which the skip logic
616
+ // treats as "seen before content-hash tracking existed → process once to
617
+ // backfill", after which the row gets a real hash and becomes hash-stable.
618
+ // Never mutate migration 004 (the original table) — this column is appended.
619
+ {
620
+ id: "013-extract-sessions-content-hash",
621
+ up: `
622
+ ALTER TABLE extract_sessions_seen ADD COLUMN content_hash TEXT DEFAULT NULL;
623
+ `,
624
+ },
625
+ // ── Migration 014 — recombine_hypotheses (#625 confirmation count) ───────────
626
+ //
627
+ // Second-pass promotion ledger for the recombine pass. The first pass (#609)
628
+ // only ever emits `type: hypothesis` proposals; this table tracks how many
629
+ // CONSECUTIVE runs re-induced the SAME generalization (keyed by the
630
+ // deterministic `deriveRecombineLessonRef` value — a hash of the sorted
631
+ // member entryKeys). Once `consecutive_count >= confirmThreshold`, the run
632
+ // promotes the generalization to a `type: lesson` proposal (through the same
633
+ // proposal queue + quality gate). A hypothesis NOT re-induced in a run has its
634
+ // consecutive streak reset (decay-to-zero), so confirmation is per exact
635
+ // member-set and conservative.
636
+ //
637
+ // Indexed columns:
638
+ // hypothesis_ref TEXT PK — the `lesson:recombined/<slug>-<hash>` ref; one
639
+ // row per re-inducible generalization. The ref is
640
+ // the promotion TARGET (a lesson in both the
641
+ // hypothesis and promoted states), so the ref never
642
+ // encodes the proposal type.
643
+ // last_seen_at TEXT (idx) — for forensic / pruning queries.
644
+ //
645
+ // Non-indexed columns:
646
+ // signature TEXT — the cluster's shared relatedness signal (tag /
647
+ // entity) at induction time; forensics only.
648
+ // member_key TEXT — sorted member entryKeys joined; the membership
649
+ // fingerprint behind the ref hash. Stored so a
650
+ // membership change (which yields a DIFFERENT ref)
651
+ // is auditable.
652
+ // consecutive_count INTEGER — current confirmation streak (reset on decay
653
+ // and on promotion).
654
+ // first_seen_at TEXT — ISO-8601 UTC of the first induction.
655
+ // last_run TEXT — sourceRun token of the last induction; the
656
+ // same-run idempotency guard.
657
+ // promoted_at TEXT — non-null once promoted; guards against
658
+ // double-promoting on every subsequent run.
659
+ // metadata_json TEXT — reserved for future forensics; defaults to '{}'.
660
+ {
661
+ id: "014-recombine-hypotheses",
662
+ up: `
663
+ CREATE TABLE IF NOT EXISTS recombine_hypotheses (
664
+ hypothesis_ref TEXT PRIMARY KEY,
665
+ signature TEXT NOT NULL,
666
+ member_key TEXT NOT NULL,
667
+ consecutive_count INTEGER NOT NULL DEFAULT 0,
668
+ first_seen_at TEXT NOT NULL,
669
+ last_seen_at TEXT NOT NULL,
670
+ last_run TEXT,
671
+ promoted_at TEXT,
672
+ metadata_json TEXT NOT NULL DEFAULT '{}'
673
+ );
674
+ CREATE INDEX IF NOT EXISTS idx_recombine_hypotheses_last_seen
675
+ ON recombine_hypotheses(last_seen_at);
676
+ `,
677
+ },
678
+ // ── Migration 015 — asset_salience: encoding_source provenance column (#644) ──
679
+ //
680
+ // Records HOW the stored `encoding_salience` was derived so an improve run's
681
+ // type-weight fallback can no longer clobber a real content-derived score:
682
+ //
683
+ // "content" — written by the distill path from `scoreEncodingSalience`
684
+ // (novelty·0.40 + magnitude·0.35 + predictionError·0.25).
685
+ // "type-stub" — written by `computeSalience`'s `DEFAULT_TYPE_ENCODING_WEIGHTS`
686
+ // fallback (no content-based score available for this ref yet).
687
+ // NULL — legacy row written before this migration; provenance unknown.
688
+ // Treated as a stub by readers UNLESS its stored value differs
689
+ // from the pure type-weight (best-effort heuristic for old data).
690
+ //
691
+ // Why a column rather than inference: before #644, every improve run overwrote
692
+ // the distill-written content score with the type-weight stub, so the stored
693
+ // value alone cannot distinguish a real score from a stub. The provenance flag
694
+ // is the single source of truth going forward; `upsertAssetSalience` refuses to
695
+ // lower a "content" row to a "type-stub" so the high-salience gate (#608) keys
696
+ // on genuine novelty/magnitude/prediction-error, not the asset type.
697
+ {
698
+ id: "015-asset-salience-encoding-source",
699
+ up: `
700
+ ALTER TABLE asset_salience ADD COLUMN encoding_source TEXT DEFAULT NULL;
701
+ `,
702
+ },
703
+ // ── Migration 016 — collapse/churn detector (R5) ─────────────────────────────
704
+ //
705
+ // Longitudinal store-health history for the improve pipeline
706
+ // (docs/design/improve-collapse-churn-detector-design.md).
707
+ //
708
+ // canary_queries — the fixed canary set, minted deterministically from the
709
+ // live stash on first detector run and NEVER auto-refreshed (silent
710
+ // re-baselining is how a slow collapse hides). `canary_set_id` groups one
711
+ // mint; deactivated sets keep their rows (active = 0) so historical cycle
712
+ // rows stay interpretable. Tens of rows; never purged.
713
+ //
714
+ // improve_cycle_metrics — one row per qualifying improve cycle (a run where
715
+ // consolidate processed ≥1 op or recombine evaluated ≥1 cluster). Every
716
+ // column is a scalar or a size-capped JSON blob (< 2 KB/row by
717
+ // construction — the result_json lesson applied). Retention: 365 days via
718
+ // purgeOldCycleMetrics. Trend queries drive the collapse/churn alert
719
+ // evaluation and the health advisory; `canary_set_id` scoping prevents
720
+ // comparing across canary re-mints.
721
+ {
722
+ id: "016-collapse-churn-detector",
723
+ up: `
724
+ CREATE TABLE IF NOT EXISTS canary_queries (
725
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
726
+ canary_set_id TEXT NOT NULL,
727
+ anchor_ref TEXT NOT NULL,
728
+ query TEXT NOT NULL,
729
+ source TEXT NOT NULL DEFAULT 'auto',
730
+ active INTEGER NOT NULL DEFAULT 1,
731
+ created_at TEXT NOT NULL
732
+ );
733
+ CREATE INDEX IF NOT EXISTS idx_canary_queries_active
734
+ ON canary_queries(active, canary_set_id);
735
+
736
+ CREATE TABLE IF NOT EXISTS improve_cycle_metrics (
737
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
738
+ run_id TEXT NOT NULL,
739
+ ts TEXT NOT NULL,
740
+ pass TEXT NOT NULL,
741
+ canary_set_id TEXT NOT NULL,
742
+ mean_recall REAL NOT NULL,
743
+ mean_ndcg REAL NOT NULL,
744
+ mean_mrr REAL NOT NULL,
745
+ canary_ranks_json TEXT NOT NULL,
746
+ store_total INTEGER NOT NULL,
747
+ store_by_type_json TEXT NOT NULL,
748
+ distinct_content_ratio REAL NOT NULL,
749
+ mean_bigram_diversity REAL NOT NULL,
750
+ over_generation_count INTEGER NOT NULL,
751
+ accepted_actions INTEGER NOT NULL,
752
+ merge_floor_violations INTEGER NOT NULL DEFAULT 0,
753
+ alerts_json TEXT NOT NULL DEFAULT '[]'
754
+ );
755
+ CREATE INDEX IF NOT EXISTS idx_improve_cycle_metrics_ts
756
+ ON improve_cycle_metrics(ts);
757
+ `,
758
+ },
759
+ ];
760
+ /**
761
+ * Apply every pending migration in a single transaction per migration.
762
+ *
763
+ * Delegates to the shared SQLite migration engine; state.db has no
764
+ * pre-versioning bootstrap step, so no `bootstrap` hook is passed.
765
+ *
766
+ * Called automatically by `openStateDatabase()`.
767
+ */
768
+ export function runMigrations(db) {
769
+ runSqliteMigrations(db, MIGRATIONS);
770
+ }