akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4

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 (343) hide show
  1. package/CHANGELOG.md +756 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/proposal/drain.js +251 -644
  53. package/dist/commands/proposal/proposal-cli.js +3 -18
  54. package/dist/commands/proposal/proposal-types.js +20 -41
  55. package/dist/commands/proposal/proposal.js +1 -2
  56. package/dist/commands/proposal/propose.js +134 -160
  57. package/dist/commands/proposal/repository.js +502 -1487
  58. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  59. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  60. package/dist/commands/proposal/validators/proposals.js +13 -89
  61. package/dist/commands/read/curate.js +63 -413
  62. package/dist/commands/read/search-cli.js +16 -33
  63. package/dist/commands/read/search.js +17 -23
  64. package/dist/commands/read/show.js +2 -13
  65. package/dist/commands/sources/bundle-cli.js +25 -2
  66. package/dist/commands/sources/bundle-config-ops.js +7 -0
  67. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  68. package/dist/commands/sources/info.js +2 -11
  69. package/dist/commands/sources/installed-stashes.js +197 -746
  70. package/dist/commands/sources/schema-repair.js +98 -129
  71. package/dist/commands/sources/source-add.js +62 -12
  72. package/dist/commands/sources/stash-cli.js +1 -1
  73. package/dist/commands/tasks/explain.js +10 -13
  74. package/dist/commands/tasks/tasks-cli.js +9 -8
  75. package/dist/commands/tasks/tasks.js +326 -930
  76. package/dist/commands/tasks/validate.js +42 -21
  77. package/dist/commands/workflow/plan.js +22 -29
  78. package/dist/commands/workflow-cli.js +4 -4
  79. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  80. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  81. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  82. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  83. package/dist/core/adapter/execution-source.js +17 -29
  84. package/dist/core/asset/resolve-ref.js +1 -1
  85. package/dist/core/bundle-id.js +42 -5
  86. package/dist/core/bundle-rename.js +291 -0
  87. package/dist/core/config/config-io.js +1 -2
  88. package/dist/core/config/config-schema.js +1 -33
  89. package/dist/core/config/config-walker.js +1 -1
  90. package/dist/core/config/config.js +163 -68
  91. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  92. package/dist/core/config/schema/embedding.js +20 -5
  93. package/dist/core/config/schema/engines.js +5 -0
  94. package/dist/core/config/schema/execution.js +1 -1
  95. package/dist/core/config/schema/experimental.js +1 -1
  96. package/dist/core/config/schema/improve-processes.js +21 -95
  97. package/dist/core/config/schema/improve.js +4 -42
  98. package/dist/core/config/schema/scheduler.js +12 -12
  99. package/dist/core/config/schema/search.js +6 -22
  100. package/dist/core/env-secret-ref.js +0 -1
  101. package/dist/core/errors.js +8 -9
  102. package/dist/core/file-lock.js +76 -173
  103. package/dist/core/logs-db.js +2 -2
  104. package/dist/core/paths.js +0 -27
  105. package/dist/core/redaction.js +109 -2
  106. package/dist/core/run-lock.js +2 -5
  107. package/dist/core/spawn-env.js +1 -1
  108. package/dist/core/state/migrations.js +108 -61
  109. package/dist/core/state-db-scope.js +2 -4
  110. package/dist/core/state-db.js +126 -692
  111. package/dist/core/type-presentation.js +1 -9
  112. package/dist/core/write-source.js +293 -1012
  113. package/dist/execution/input-contract.js +1 -1
  114. package/dist/execution/resolved-request.js +135 -689
  115. package/dist/execution/source.js +63 -257
  116. package/dist/execution/target-ref.js +1 -1
  117. package/dist/indexer/bundle-identity-guard.js +2 -2
  118. package/dist/indexer/db/graph-db.js +106 -46
  119. package/dist/indexer/ensure-index.js +44 -85
  120. package/dist/indexer/graph/graph-extraction.js +340 -562
  121. package/dist/indexer/graph/graph-related.js +130 -0
  122. package/dist/indexer/index-rebuild-lock.js +3 -11
  123. package/dist/indexer/index-writer-lock.js +8 -17
  124. package/dist/indexer/index-written-assets.js +139 -151
  125. package/dist/indexer/indexer.js +524 -846
  126. package/dist/indexer/materialize-embeddings.js +60 -397
  127. package/dist/indexer/passes/memory-inference.js +81 -90
  128. package/dist/indexer/passes/metadata.js +132 -200
  129. package/dist/indexer/read-preflight.js +0 -7
  130. package/dist/indexer/scan/doc-to-entry.js +1 -3
  131. package/dist/indexer/scan/drain-dir.js +1 -1
  132. package/dist/indexer/search/db-search.js +181 -590
  133. package/dist/indexer/search/fts-query.js +30 -41
  134. package/dist/indexer/search/ranking.js +28 -154
  135. package/dist/indexer/search/search-attribution.js +12 -32
  136. package/dist/indexer/search/search-fields.js +11 -15
  137. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  138. package/dist/indexer/search/search-source.js +1 -4
  139. package/dist/indexer/usage/usage-events.js +2 -7
  140. package/dist/integrations/agent/engine-fallback.js +23 -40
  141. package/dist/integrations/agent/engine-resolution.js +93 -183
  142. package/dist/integrations/agent/execution.js +507 -0
  143. package/dist/integrations/agent/model-map.js +28 -156
  144. package/dist/integrations/agent/request-lowering.js +66 -141
  145. package/dist/integrations/agent/runner-dispatch.js +143 -321
  146. package/dist/integrations/agent/runner.js +54 -14
  147. package/dist/integrations/lockfile.js +53 -101
  148. package/dist/llm/embedders/deterministic.js +2 -3
  149. package/dist/llm/embedders/profile.js +71 -0
  150. package/dist/llm/embedders/remote.js +10 -15
  151. package/dist/llm/graph-extract.js +3 -12
  152. package/dist/llm/index-passes.js +3 -5
  153. package/dist/llm/memory-infer.js +1 -2
  154. package/dist/llm/metadata-enhance.js +1 -2
  155. package/dist/llm/structured-call.js +5 -24
  156. package/dist/output/generic-render.js +23 -11
  157. package/dist/output/html-render.js +13 -10
  158. package/dist/output/render-registry.js +3 -32
  159. package/dist/output/shapes/helpers.js +2 -34
  160. package/dist/output/shapes/passthrough.js +1 -9
  161. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  162. package/dist/output/text/command-format.js +60 -23
  163. package/dist/output/text/helpers.js +1 -1
  164. package/dist/output/text/migrate.js +5 -14
  165. package/dist/output/text/proposal-format.js +1 -2
  166. package/dist/output/text/workflow-format.js +0 -32
  167. package/dist/output/text.js +2 -0
  168. package/dist/registry/factory.js +4 -19
  169. package/dist/registry/network.js +66 -220
  170. package/dist/registry/providers/index.js +0 -2
  171. package/dist/registry/providers/skills-sh.js +3 -14
  172. package/dist/registry/providers/static-index.js +24 -26
  173. package/dist/registry/resolve.js +55 -131
  174. package/dist/scripts/akm-migrate-node.js +43937 -93313
  175. package/dist/scripts/akm-migrate.js +43697 -93071
  176. package/dist/setup/registry-stash-loader.js +4 -13
  177. package/dist/setup/semantic-assets.js +3 -44
  178. package/dist/setup/setup.js +1 -1
  179. package/dist/setup/steps/tasks.js +25 -15
  180. package/dist/sources/provider-factory.js +17 -18
  181. package/dist/sources/providers/filesystem.js +2 -3
  182. package/dist/sources/providers/git-install.js +7 -1
  183. package/dist/sources/providers/git-provider.js +0 -3
  184. package/dist/sources/providers/git-stash.js +0 -17
  185. package/dist/sources/providers/npm.js +2 -4
  186. package/dist/sources/providers/provider-utils.js +5 -10
  187. package/dist/sources/providers/website.js +0 -2
  188. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  189. package/dist/sources/website-url.js +2 -2
  190. package/dist/storage/database.js +9 -35
  191. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  192. package/dist/storage/repositories/index-connection.js +34 -70
  193. package/dist/storage/repositories/index-entries-repository.js +69 -111
  194. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  195. package/dist/storage/repositories/index-entry-schema.js +83 -269
  196. package/dist/storage/repositories/index-fts-repository.js +86 -256
  197. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  198. package/dist/storage/repositories/index-meta-repository.js +6 -4
  199. package/dist/storage/repositories/index-schema.js +192 -220
  200. package/dist/storage/repositories/index-utility-repository.js +8 -29
  201. package/dist/storage/repositories/index-vec-repository.js +133 -414
  202. package/dist/storage/repositories/outcome-repository.js +2 -1
  203. package/dist/storage/repositories/proposals-repository.js +35 -0
  204. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  205. package/dist/storage/repositories/task-history-repository.js +26 -4
  206. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  207. package/dist/storage/sqlite-migrations.js +136 -0
  208. package/dist/storage/sqlite-pragmas.js +11 -9
  209. package/dist/storage/sqlite-transaction.js +170 -0
  210. package/dist/storage/state-db-integrity.js +34 -27
  211. package/dist/tasks/activation-config.js +134 -62
  212. package/dist/tasks/backends/cron.js +129 -277
  213. package/dist/tasks/backends/exec-utils.js +2 -5
  214. package/dist/tasks/backends/launchd.js +125 -745
  215. package/dist/tasks/backends/schtasks.js +101 -620
  216. package/dist/tasks/prepare/prepare-support.js +5 -15
  217. package/dist/tasks/prepare/prepare.js +0 -2
  218. package/dist/tasks/resolve-akm-bin.js +20 -79
  219. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  220. package/dist/tasks/scheduler-binding.js +18 -238
  221. package/dist/tasks/scheduler-invocation.js +52 -52
  222. package/dist/tasks/scheduler-lock.js +53 -0
  223. package/dist/tasks/scheduler-sync.js +363 -679
  224. package/dist/tasks/source/parse-task-source.js +160 -10
  225. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  226. package/dist/tasks/source/task-to-v4.js +2 -2
  227. package/dist/workflows/authoring/authoring.js +3 -12
  228. package/dist/workflows/compile.js +211 -0
  229. package/dist/workflows/concurrency-policy.js +13 -74
  230. package/dist/workflows/exec/child-invocation.js +3 -17
  231. package/dist/workflows/exec/child-workflow.js +32 -141
  232. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  233. package/dist/workflows/exec/environment.js +98 -0
  234. package/dist/workflows/exec/exec-unit.js +33 -140
  235. package/dist/workflows/exec/frozen-judge.js +7 -59
  236. package/dist/workflows/exec/native-executor.js +82 -341
  237. package/dist/workflows/exec/param-secrets.js +29 -47
  238. package/dist/workflows/exec/run-workflow.js +154 -387
  239. package/dist/workflows/exec/scheduler.js +9 -36
  240. package/dist/workflows/exec/step-work.js +127 -430
  241. package/dist/workflows/exec/unit-dispatch.js +11 -63
  242. package/dist/workflows/exec/unit-writer.js +8 -52
  243. package/dist/workflows/exec/worktree.js +39 -273
  244. package/dist/workflows/freeze/child-output-references.js +4 -15
  245. package/dist/workflows/freeze/environment.js +99 -92
  246. package/dist/workflows/freeze/freeze.js +172 -0
  247. package/dist/workflows/freeze/step-values.js +19 -21
  248. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  249. package/dist/workflows/freeze/targets/command.js +10 -33
  250. package/dist/workflows/freeze/targets/script.js +5 -12
  251. package/dist/workflows/freeze/targets/shell.js +3 -6
  252. package/dist/workflows/freeze/targets/task.js +25 -80
  253. package/dist/workflows/freeze/task-bindings.js +20 -67
  254. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  255. package/dist/workflows/ir/params.js +6 -51
  256. package/dist/workflows/ir/plan-hash.js +2 -34
  257. package/dist/workflows/parser.js +140 -43
  258. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  259. package/dist/workflows/renderer.js +36 -69
  260. package/dist/workflows/resource-limits.js +12 -120
  261. package/dist/workflows/runtime/agent-identity.js +8 -40
  262. package/dist/workflows/runtime/run-outputs.js +3 -6
  263. package/dist/workflows/runtime/run-plan.js +316 -0
  264. package/dist/workflows/runtime/runs.js +48 -200
  265. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  266. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  267. package/dist/workflows/validate-summary.js +2 -7
  268. package/docs/integration/bundling-akm.md +49 -42
  269. package/docs/migration/README.md +1 -0
  270. package/docs/migration/release-notes/0.9.17.md +41 -0
  271. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  272. package/docs/reference/cli.md +182 -125
  273. package/docs/reference/configuration.md +49 -56
  274. package/docs/reference/data-and-telemetry.md +19 -20
  275. package/docs/reference/tasks.md +86 -38
  276. package/docs/reference/workflow-schema.md +14 -18
  277. package/docs/reference/workflows.md +6 -9
  278. package/package.json +1 -1
  279. package/schemas/akm-config.json +87 -406
  280. package/dist/commands/health/advisories.js +0 -150
  281. package/dist/commands/health/metrics.js +0 -329
  282. package/dist/commands/health/surfaces.js +0 -102
  283. package/dist/commands/improve/anti-collapse.js +0 -83
  284. package/dist/commands/improve/collapse-detector.js +0 -432
  285. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  286. package/dist/commands/improve/consolidate/merge.js +0 -146
  287. package/dist/commands/improve/distill/promote-memory.js +0 -329
  288. package/dist/commands/improve/distill/quality-gate.js +0 -500
  289. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  290. package/dist/commands/improve/proposal-envelope.js +0 -31
  291. package/dist/commands/improve/run-context.js +0 -123
  292. package/dist/commands/improve/shared.js +0 -21
  293. package/dist/commands/improve/source-identity.js +0 -28
  294. package/dist/commands/improve/triage.js +0 -96
  295. package/dist/commands/proposal/drain-policies.js +0 -151
  296. package/dist/commands/sources/update-transaction.js +0 -220
  297. package/dist/core/action-contributors.js +0 -28
  298. package/dist/core/config/config-version-shim.js +0 -101
  299. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  300. package/dist/core/fs-txn.js +0 -405
  301. package/dist/core/lexical-score.js +0 -25
  302. package/dist/core/maintenance-barrier.js +0 -167
  303. package/dist/execution/executable-identity.js +0 -105
  304. package/dist/execution/guarded-source.js +0 -427
  305. package/dist/indexer/graph/graph-boost.js +0 -427
  306. package/dist/indexer/graph/graph-dedup.js +0 -95
  307. package/dist/indexer/search/name-match.js +0 -35
  308. package/dist/indexer/search/ranking-contributors.js +0 -515
  309. package/dist/indexer/walk/project-context.js +0 -192
  310. package/dist/integrations/agent/execution-cascade.js +0 -566
  311. package/dist/integrations/agent/execution-definitions.js +0 -202
  312. package/dist/integrations/agent/execution-lowering.js +0 -841
  313. package/dist/integrations/agent/execution-preparation.js +0 -98
  314. package/dist/integrations/agent/inline-execution.js +0 -74
  315. package/dist/registry/create-provider-registry.js +0 -29
  316. package/dist/registry/pinned-request-helper.js +0 -247
  317. package/dist/registry/pinned-transport.js +0 -717
  318. package/dist/sources/providers/index.js +0 -14
  319. package/dist/storage/engines/sqlite-migrations.js +0 -271
  320. package/dist/storage/repositories/canaries-repository.js +0 -107
  321. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  322. package/dist/storage/repositories/registry-cache.js +0 -113
  323. package/dist/tasks/scheduler-sync-preview.js +0 -52
  324. package/dist/workflows/freeze/resolve-steps.js +0 -86
  325. package/dist/workflows/freeze/source-freeze.js +0 -64
  326. package/dist/workflows/ir/compile.js +0 -321
  327. package/dist/workflows/ir/environment-v4.js +0 -330
  328. package/dist/workflows/ir/freeze-v4.js +0 -153
  329. package/dist/workflows/ir/schema-v4.js +0 -745
  330. package/dist/workflows/ir/schema.js +0 -354
  331. package/dist/workflows/program/schema.js +0 -78
  332. package/dist/workflows/runtime/checkin.js +0 -57
  333. package/dist/workflows/runtime/plan-classifier.js +0 -196
  334. package/dist/workflows/runtime/unit-checkin.js +0 -45
  335. package/dist/workflows/runtime/unit-phases.js +0 -20
  336. package/dist/workflows/schema.js +0 -4
  337. package/dist/workflows/source-ir/compile.js +0 -200
  338. package/dist/workflows/source-ir/program.js +0 -50
  339. package/dist/workflows/source-ir/result.js +0 -26
  340. package/dist/workflows/source-ir/schema.js +0 -786
  341. package/dist/workflows/source-ir/triggers.js +0 -79
  342. package/dist/workflows/source-ir/uses.js +0 -40
  343. package/dist/workflows/validator.js +0 -60
@@ -0,0 +1,168 @@
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
+ export const IMPROVE_LEDGER_OUTCOMES = [
5
+ "proposed",
6
+ "accepted",
7
+ "rejected",
8
+ "quality_rejected",
9
+ "review_needed",
10
+ "expired",
11
+ "unchanged",
12
+ "failed",
13
+ "judged_no_action",
14
+ ];
15
+ const MS_PER_DAY = 86_400_000;
16
+ const DETAIL_MAX_CHARS = 500;
17
+ /**
18
+ * Post-rejection windows by source: reflect 14 d, distill 30 d, every other
19
+ * source 7 d — the constants the rejection backoff always used.
20
+ */
21
+ export const LEDGER_REJECTION_WINDOW_DAYS = Object.freeze({
22
+ reflect: 14,
23
+ distill: 30,
24
+ });
25
+ export const LEDGER_DEFAULT_REJECTION_WINDOW_DAYS = 7;
26
+ /** A proposal nobody reviewed inside the retention window: short grace, not a rejection backoff. */
27
+ export const LEDGER_EXPIRED_GRACE_DAYS = 1;
28
+ /** Revisit cadence for a ref the stage looked at and had nothing to do (or is still pending). */
29
+ export const LEDGER_REVISIT_CADENCE_DAYS = 7;
30
+ /**
31
+ * Outcomes whose window a fresh signal on the asset (new feedback, a content
32
+ * change) cannot lift. Every other window is a revisit cadence that a signal
33
+ * newer than `last_attempt_at` lifts.
34
+ */
35
+ export const LEDGER_HARD_OUTCOMES = new Set([
36
+ "rejected",
37
+ "quality_rejected",
38
+ "expired",
39
+ ]);
40
+ function windowDays(source, outcome) {
41
+ switch (outcome) {
42
+ case "rejected":
43
+ case "quality_rejected":
44
+ return LEDGER_REJECTION_WINDOW_DAYS[source] ?? LEDGER_DEFAULT_REJECTION_WINDOW_DAYS;
45
+ case "expired":
46
+ return LEDGER_EXPIRED_GRACE_DAYS;
47
+ case "unchanged":
48
+ case "judged_no_action":
49
+ case "proposed":
50
+ case "review_needed":
51
+ return LEDGER_REVISIT_CADENCE_DAYS;
52
+ case "accepted":
53
+ case "failed":
54
+ return null;
55
+ }
56
+ }
57
+ /**
58
+ * The single cadence function: when a `(source, outcome)` recorded at
59
+ * `fromIso` becomes eligible again, or `null` for "immediately".
60
+ */
61
+ export function nextEligibleAt(source, outcome, fromIso) {
62
+ const from = Date.parse(fromIso);
63
+ if (!Number.isFinite(from))
64
+ return null;
65
+ const days = windowDays(source, outcome);
66
+ return days === null ? null : new Date(from + days * MS_PER_DAY).toISOString();
67
+ }
68
+ /**
69
+ * Whether the ledger blocks another attempt on this row at `nowIso`.
70
+ * `signalSinceIso` is the newest signal on the asset (feedback, content
71
+ * change); a signal newer than the last attempt lifts a soft window but never
72
+ * a hard one ({@link LEDGER_HARD_OUTCOMES}).
73
+ */
74
+ export function isLedgerBlocked(row, nowIso, signalSinceIso) {
75
+ if (!row?.nextEligibleAt)
76
+ return false;
77
+ if (row.nextEligibleAt <= nowIso)
78
+ return false;
79
+ if (LEDGER_HARD_OUTCOMES.has(row.outcome))
80
+ return true;
81
+ return !(signalSinceIso !== undefined && signalSinceIso > row.lastAttemptAt);
82
+ }
83
+ function toRow(row) {
84
+ return {
85
+ stashDir: row.stash_dir,
86
+ ref: row.ref,
87
+ source: row.source,
88
+ lastAttemptAt: row.last_attempt_at,
89
+ // Tolerate an outcome a newer release may add: it still carries a window.
90
+ outcome: row.outcome,
91
+ nextEligibleAt: row.next_eligible_at,
92
+ proposalId: row.proposal_id,
93
+ detail: row.detail,
94
+ };
95
+ }
96
+ function trimDetail(detail) {
97
+ if (detail === undefined)
98
+ return null;
99
+ const trimmed = detail.trim();
100
+ if (trimmed.length === 0)
101
+ return null;
102
+ return trimmed.length > DETAIL_MAX_CHARS ? `${trimmed.slice(0, DETAIL_MAX_CHARS - 1)}…` : trimmed;
103
+ }
104
+ /** Record an attempt on `(stashDir, ref, source)`: upsert the row and its cadence. */
105
+ export function recordImproveLedger(db, input) {
106
+ const row = {
107
+ stashDir: input.stashDir,
108
+ ref: input.ref,
109
+ source: input.source,
110
+ lastAttemptAt: input.at,
111
+ outcome: input.outcome,
112
+ nextEligibleAt: nextEligibleAt(input.source, input.outcome, input.at),
113
+ proposalId: input.proposalId ?? null,
114
+ detail: trimDetail(input.detail),
115
+ };
116
+ db.prepare(`INSERT INTO improve_ledger
117
+ (stash_dir, ref, source, last_attempt_at, outcome, next_eligible_at, proposal_id, detail)
118
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
119
+ ON CONFLICT(stash_dir, ref, source) DO UPDATE SET
120
+ last_attempt_at = excluded.last_attempt_at,
121
+ outcome = excluded.outcome,
122
+ next_eligible_at = excluded.next_eligible_at,
123
+ proposal_id = excluded.proposal_id,
124
+ detail = excluded.detail`).run(row.stashDir, row.ref, row.source, row.lastAttemptAt, row.outcome, row.nextEligibleAt, row.proposalId, row.detail);
125
+ return row;
126
+ }
127
+ /**
128
+ * Record a decision (accept / reject / expire / revert) on the proposal a row
129
+ * was minted for. The row is found by `proposal_id` — the proposal's ref may
130
+ * differ from the ledger key (a distill proposal for `lessons/x` is keyed by
131
+ * its input `memories/x`) — and `last_attempt_at` is kept: the window starts
132
+ * at the decision, the attempt happened when it happened. A proposal no row
133
+ * knows (minted before the ledger existed) gets a row keyed by its own ref.
134
+ */
135
+ export function recordImproveLedgerDecision(db, input) {
136
+ const changes = db
137
+ .prepare(`UPDATE improve_ledger
138
+ SET outcome = ?, next_eligible_at = ?, detail = ?
139
+ WHERE stash_dir = ? AND proposal_id = ?`)
140
+ .run(input.outcome, nextEligibleAt(input.source, input.outcome, input.at), trimDetail(input.detail), input.stashDir, input.proposalId).changes;
141
+ if (Number(changes) > 0)
142
+ return;
143
+ recordImproveLedger(db, {
144
+ stashDir: input.stashDir,
145
+ ref: input.ref,
146
+ source: input.source,
147
+ outcome: input.outcome,
148
+ at: input.at,
149
+ proposalId: input.proposalId,
150
+ ...(input.detail !== undefined ? { detail: input.detail } : {}),
151
+ });
152
+ }
153
+ export function getImproveLedgerRow(db, stashDir, ref, source) {
154
+ const row = db
155
+ .prepare(`SELECT stash_dir, ref, source, last_attempt_at, outcome, next_eligible_at, proposal_id, detail
156
+ FROM improve_ledger WHERE stash_dir = ? AND ref = ? AND source = ?`)
157
+ .get(stashDir, ref, source);
158
+ return row ? toRow(row) : undefined;
159
+ }
160
+ /** Every row for one stash, optionally narrowed to `sources`. */
161
+ export function listImproveLedgerRows(db, stashDir, sources) {
162
+ const sourceFilter = sources && sources.length > 0 ? ` AND source IN (${sources.map(() => "?").join(", ")})` : "";
163
+ const rows = db
164
+ .prepare(`SELECT stash_dir, ref, source, last_attempt_at, outcome, next_eligible_at, proposal_id, detail
165
+ FROM improve_ledger WHERE stash_dir = ?${sourceFilter} ORDER BY ref ASC, source ASC`)
166
+ .all(stashDir, ...(sources && sources.length > 0 ? sources : []));
167
+ return rows.map(toRow);
168
+ }
@@ -4,14 +4,13 @@
4
4
  /**
5
5
  * `index.db` connection lifecycle for the storage layer.
6
6
  *
7
- * Opens/closes the index database, arming the sqlite-vec extension and (for the
8
- * managed open path) running `ensureSchema`. This module lives BELOW the
9
- * indexer, so the storage loan helpers (`index-db.ts`, `registry-cache.ts`)
7
+ * Opens/closes the index database, running `ensureSchema` on the managed
8
+ * (writable) open path. This module lives BELOW the
9
+ * indexer, so the storage loan helpers (`index-db.ts`, `registry-index-cache-repository.ts`)
10
10
  * import their opener from a sibling here instead of reaching up into the
11
11
  * indexer — inverting the old storage→indexer arrow.
12
12
  */
13
13
  import fs from "node:fs";
14
- import { createRequire } from "node:module";
15
14
  import { ConfigError } from "../../core/errors.js";
16
15
  import { classifyPathAccess, describeInaccessiblePath } from "../../core/path-access.js";
17
16
  import { getDbPath } from "../../core/paths.js";
@@ -20,9 +19,8 @@ import { openDatabase } from "../database.js";
20
19
  import { openManagedDatabase } from "../managed-db.js";
21
20
  import { SQLITE_BUSY_TIMEOUT_MS } from "../sqlite-pragmas.js";
22
21
  import { openSqliteReadSnapshot, SqliteReadSnapshotUnavailableError } from "../sqlite-read-snapshot.js";
23
- import { CANONICAL_INDEX_DB_VERSION, classifyIndexGeneration, isCanonicalIndexGeneration } from "./index-entry-schema.js";
24
- import { ensureSchema } from "./index-schema.js";
25
- import { loadVecExtension, warnIfVecMissing } from "./index-vec-repository.js";
22
+ import { CANONICAL_INDEX_DB_VERSION } from "./index-entry-schema.js";
23
+ import { ensureSchema, newerIndexLayoutError } from "./index-schema.js";
26
24
  /**
27
25
  * Whether `error` is SQLite reporting on-disk corruption (`SQLITE_CORRUPT`,
28
26
  * "database disk image is malformed") rather than a permission, lock, or
@@ -41,21 +39,11 @@ export function openIndexDatabase(dbPath, options) {
41
39
  const spec = {
42
40
  path: resolvedPath,
43
41
  init: (db) => {
44
- // Try to load sqlite-vec extension
45
- loadVecExtension(db);
46
42
  // Source update uses this narrow lifecycle seam to ATTACH state.db and
47
43
  // open its coordinator-owned outer transaction before ensureSchema or
48
44
  // any indexer write can mutate the live generation.
49
45
  options?.beforeSchema?.(db);
50
- // Dim resolution: explicit option wins; otherwise consult the on-disk
51
- // config so unparameterised opens (registry providers, graph helpers,
52
- // ad-hoc CLI subcommands) honour the operator-declared dimension. Only if
53
- // both are absent do we fall through to the no-clobber path, which keeps
54
- // ensureSchema from touching `index_meta.embeddingDim` at all.
55
- const resolvedDim = options?.embeddingDim ?? resolveConfiguredEmbeddingDim();
56
- ensureSchema(db, resolvedDim);
57
- // Warn once at init if using JS fallback with many entries
58
- warnIfVecMissing(db, { once: true });
46
+ ensureSchema(db);
59
47
  },
60
48
  };
61
49
  try {
@@ -66,10 +54,9 @@ export function openIndexDatabase(dbPath, options) {
66
54
  // (see src/core/state-db.ts's "Why a separate database from index.db"
67
55
  // note) — so real on-disk corruption is recovered by deleting the file
68
56
  // and rebuilding, not by surfacing a raw SQLITE_CORRUPT to the caller or
69
- // quietly falling through to an unreadable index. This mirrors the
70
- // existing stale-version-marker rebuild below, one layer further down
71
- // (that path opens fine and rewrites tables in place; corruption prevents
72
- // even opening, so the file itself has to go first).
57
+ // quietly falling through to an unreadable index (#865). This is the ONE
58
+ // from-scratch rebuild: an older layout is migrated in place by
59
+ // ensureSchema, never dropped.
73
60
  if (!isCorruptionError(error))
74
61
  throw error;
75
62
  warn(`Index database is corrupt at ${resolvedPath} — rebuilding.`);
@@ -85,34 +72,9 @@ export function openIndexDatabase(dbPath, options) {
85
72
  return openManagedDatabase(spec);
86
73
  }
87
74
  }
88
- /**
89
- * Read the operator-configured embedding dimension from the on-disk config.
90
- * Returns `undefined` when no config file is present, when the config has
91
- * no `embedding.dimension` set, or when reading the config throws (e.g.
92
- * inside isolated test fixtures with no XDG home). Failure is silent on
93
- * purpose — every openDatabase() call would otherwise have to handle a
94
- * config-not-found error path, and the fallback (no-clobber semantics) is
95
- * already correct.
96
- */
97
- function resolveConfiguredEmbeddingDim() {
98
- try {
99
- const esmRequire = createRequire(import.meta.url);
100
- const { loadConfig } = esmRequire("../../core/config/config");
101
- const dim = loadConfig().embedding?.dimension;
102
- if (typeof dim === "number" && Number.isInteger(dim) && dim > 0 && dim <= 4096) {
103
- return dim;
104
- }
105
- return undefined;
106
- }
107
- catch {
108
- return undefined;
109
- }
110
- }
111
75
  export function openExistingDatabase(dbPath) {
112
- // Existing-DB callers do not mutate schema or embedding metadata on open.
113
- // They do validate the exact current derived generation before returning a
114
- // handle, so no current reader can accidentally serve a populated legacy
115
- // table and fail later on its first canonical-column query.
76
+ // Existing-DB callers do not mutate schema or embedding metadata on open;
77
+ // they serve an older layout as-is and refuse a newer one (see checkIndexLayout).
116
78
  //
117
79
  // "Existing" is load-bearing: a missing file throws instead of being
118
80
  // created. Create-on-open used to leave a schema-less index.db behind (a
@@ -126,15 +88,9 @@ export function openExistingDatabase(dbPath) {
126
88
  if (classifyPathAccess(resolvedPath).access === "absent") {
127
89
  throw new Error(`Index database not found at ${resolvedPath}. Run 'akm index' to build it.`);
128
90
  }
129
- const db = openManagedDatabase({
130
- path: resolvedPath,
131
- init: (db) => {
132
- loadVecExtension(db);
133
- },
134
- create: false,
135
- });
91
+ const db = openManagedDatabase({ path: resolvedPath, create: false });
136
92
  try {
137
- assertCanonicalIndexGeneration(db, resolvedPath);
93
+ checkIndexLayout(db, resolvedPath);
138
94
  return db;
139
95
  }
140
96
  catch (error) {
@@ -143,21 +99,29 @@ export function openExistingDatabase(dbPath) {
143
99
  }
144
100
  }
145
101
  /**
146
- * Read callers must never receive a known-incompatible derived index. The
147
- * writable opener owns rebuilding an older generation; a reader can only
148
- * report the one action that is safe for the direction of the mismatch.
102
+ * A reader serves an older layout as-is (the FTS readers understand both
103
+ * layouts, and a missing table degrades at the caller — keyword-only search,
104
+ * an inline rebuild, or a "run akm index" notice) and names it once per
105
+ * process; the next writable open migrates it in place. A newer layout is
106
+ * refused, naming the upgrade ({@link newerIndexLayoutError}).
149
107
  */
150
- function assertCanonicalIndexGeneration(db, resolvedPath) {
151
- if (isCanonicalIndexGeneration(db))
108
+ function checkIndexLayout(db, resolvedPath) {
109
+ let stored;
110
+ try {
111
+ const row = db.prepare("SELECT value FROM index_meta WHERE key = 'version'").get();
112
+ if (!row)
113
+ return;
114
+ stored = Number(row.value);
115
+ }
116
+ catch {
152
117
  return;
153
- const classification = classifyIndexGeneration(db);
154
- const stored = classification.storedVersion ?? "unknown";
155
- if (classification.status === "newer") {
156
- throw new ConfigError(`Index database at ${resolvedPath} was built by a newer akm (stored generation ${stored}; ` +
157
- `this binary understands ${CANONICAL_INDEX_DB_VERSION}). Upgrade akm to use this index.`, "INDEX_SCHEMA_INCOMPATIBLE", "Upgrade akm to a version that understands this index generation.");
158
118
  }
159
- throw new ConfigError(`Index database at ${resolvedPath} is not usable with this akm's derived schema (stored generation ${stored}; ` +
160
- `this binary understands ${CANONICAL_INDEX_DB_VERSION}). Run 'akm index' to rebuild it.`, "INDEX_SCHEMA_INCOMPATIBLE", "Run `akm index` to rebuild the derived index from the currently materialized sources.");
119
+ if (!Number.isFinite(stored) || stored === CANONICAL_INDEX_DB_VERSION)
120
+ return;
121
+ if (stored > CANONICAL_INDEX_DB_VERSION)
122
+ throw newerIndexLayoutError(stored, resolvedPath);
123
+ warnOnce(`index-db-layout:${resolvedPath}`, `Index database at ${resolvedPath} uses an older layout (${stored}; this akm writes ${CANONICAL_INDEX_DB_VERSION}). ` +
124
+ "Serving it as-is; the next 'akm index' migrates it in place.");
161
125
  }
162
126
  /**
163
127
  * Refuse to treat an UNREADABLE index as a missing one (#791).
@@ -219,7 +183,7 @@ export function openReadonlyExistingDatabase(dbPath, options) {
219
183
  // connection, so apply just that one.
220
184
  try {
221
185
  db.exec(`PRAGMA busy_timeout = ${SQLITE_BUSY_TIMEOUT_MS}`);
222
- assertCanonicalIndexGeneration(db, resolvedPath);
186
+ checkIndexLayout(db, resolvedPath);
223
187
  return db;
224
188
  }
225
189
  catch (error) {