akm-cli 0.9.16 → 0.9.17-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (403) hide show
  1. package/CHANGELOG.md +2101 -0
  2. package/STABILITY.md +11 -10
  3. package/dist/akm +124 -193
  4. package/dist/akm-migrate +38 -19
  5. package/dist/assets/hints/cli-hints-full.md +6 -7
  6. package/dist/assets/improve-strategies/catchup.json +0 -3
  7. package/dist/assets/improve-strategies/consolidate.json +0 -1
  8. package/dist/assets/improve-strategies/default.json +1 -2
  9. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -2
  10. package/dist/assets/improve-strategies/quick.json +1 -2
  11. package/dist/assets/improve-strategies/reflect-distill.json +1 -2
  12. package/dist/assets/improve-strategies/thorough.json +0 -3
  13. package/dist/assets/prompts/consolidate-pair.md +20 -0
  14. package/dist/assets/prompts/consolidate-system.md +4 -11
  15. package/dist/assets/prompts/retrieval-relevance-judge.md +6 -0
  16. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +20 -20
  17. package/dist/assets/stash-skeleton/facts/conventions/domains.md +2 -2
  18. package/dist/assets/templates/html/health.html +3 -5
  19. package/dist/cli/retired-commands.js +1 -1
  20. package/dist/cli/shared.js +6 -2
  21. package/dist/cli/unknown-flags.js +24 -1
  22. package/dist/cli.js +68 -10
  23. package/dist/commands/agent/agent-dispatch.js +1 -1
  24. package/dist/commands/command/command-execution.js +24 -62
  25. package/dist/commands/feedback-cli.js +0 -1
  26. package/dist/commands/health/accept-rate.js +6 -0
  27. package/dist/commands/health/archive-usage.js +92 -0
  28. package/dist/commands/health/checks.js +83 -74
  29. package/dist/commands/health/config-skew.js +38 -0
  30. package/dist/commands/health/data-dir-usage.js +25 -13
  31. package/dist/commands/health/egress.js +54 -0
  32. package/dist/commands/health/html-report.js +1 -42
  33. package/dist/commands/health/improve-metrics.js +136 -591
  34. package/dist/commands/health/md-report.js +1 -6
  35. package/dist/commands/health/plugin-staleness.js +53 -3
  36. package/dist/commands/health/renderers.js +12 -4
  37. package/dist/commands/health/report-view-model.js +14 -120
  38. package/dist/commands/health/types-improve.js +4 -19
  39. package/dist/commands/health/windows.js +64 -74
  40. package/dist/commands/health.js +145 -143
  41. package/dist/commands/improve/consolidate/chunking.js +26 -117
  42. package/dist/commands/improve/consolidate/continuity-check.js +137 -0
  43. package/dist/commands/improve/consolidate/pair-pass.js +791 -0
  44. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  45. package/dist/commands/improve/consolidate.js +589 -1127
  46. package/dist/commands/improve/content-hash.js +16 -24
  47. package/dist/commands/improve/distill/content-repair.js +18 -100
  48. package/dist/commands/improve/distill-guards.js +20 -81
  49. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  50. package/dist/commands/improve/distill.js +608 -1041
  51. package/dist/commands/improve/eligibility.js +126 -390
  52. package/dist/commands/improve/execution.js +8 -10
  53. package/dist/commands/improve/extract-prompt.js +1 -2
  54. package/dist/commands/improve/extract.js +487 -1046
  55. package/dist/commands/improve/feedback-valence.js +0 -25
  56. package/dist/commands/improve/improve-cli.js +75 -169
  57. package/dist/commands/improve/improve-result-file.js +10 -66
  58. package/dist/commands/improve/improve-strategies.js +52 -4
  59. package/dist/commands/improve/improve-usage-report.js +18 -64
  60. package/dist/commands/improve/improve.js +480 -1074
  61. package/dist/commands/improve/ledger.js +119 -0
  62. package/dist/commands/improve/locks.js +2 -8
  63. package/dist/commands/improve/loop-stages.js +415 -1073
  64. package/dist/commands/improve/memory/derived-ref.js +12 -77
  65. package/dist/commands/improve/memory/memory-belief.js +16 -118
  66. package/dist/commands/improve/memory/memory-improve.js +266 -14
  67. package/dist/commands/improve/outcome-loop.js +28 -156
  68. package/dist/commands/improve/planner.js +5 -15
  69. package/dist/commands/improve/preparation.js +779 -2319
  70. package/dist/commands/improve/proactive-maintenance.js +34 -101
  71. package/dist/commands/improve/reflect-noise.js +104 -280
  72. package/dist/commands/improve/reflect.js +642 -1353
  73. package/dist/commands/improve/retrieval-gate.js +127 -0
  74. package/dist/commands/improve/retrieval-scope.js +92 -0
  75. package/dist/commands/improve/salience.js +41 -240
  76. package/dist/commands/improve/session-asset.js +19 -100
  77. package/dist/commands/improve/stage.js +322 -0
  78. package/dist/commands/lint/base-linter.js +37 -15
  79. package/dist/commands/proposal/drain.js +261 -578
  80. package/dist/commands/proposal/proposal-cli.js +19 -20
  81. package/dist/commands/proposal/proposal-types.js +31 -24
  82. package/dist/commands/proposal/proposal.js +38 -8
  83. package/dist/commands/proposal/propose.js +134 -160
  84. package/dist/commands/proposal/repository.js +1097 -1394
  85. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  86. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  87. package/dist/commands/proposal/validators/proposals.js +22 -89
  88. package/dist/commands/read/curate.js +105 -462
  89. package/dist/commands/read/knowledge.js +3 -2
  90. package/dist/commands/read/search-cli.js +16 -33
  91. package/dist/commands/read/search.js +17 -23
  92. package/dist/commands/read/show.js +57 -108
  93. package/dist/commands/sources/bundle-cli.js +25 -2
  94. package/dist/commands/sources/bundle-config-ops.js +4 -0
  95. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  96. package/dist/commands/sources/info.js +127 -29
  97. package/dist/commands/sources/installed-stashes.js +197 -746
  98. package/dist/commands/sources/schema-repair.js +98 -129
  99. package/dist/commands/sources/source-add.js +62 -12
  100. package/dist/commands/sources/source-manage.js +9 -2
  101. package/dist/commands/sources/stash-cli.js +24 -4
  102. package/dist/commands/tasks/explain.js +10 -13
  103. package/dist/commands/tasks/tasks-cli.js +12 -13
  104. package/dist/commands/tasks/tasks.js +350 -936
  105. package/dist/commands/tasks/validate.js +26 -24
  106. package/dist/commands/workflow/plan.js +22 -29
  107. package/dist/commands/workflow-cli.js +4 -4
  108. package/dist/core/adapter/adapters/akm-adapter.js +2 -1
  109. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  110. package/dist/core/adapter/adapters/akm-metadata.js +42 -12
  111. package/dist/core/adapter/adapters/akm-task-adapter.js +29 -8
  112. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  113. package/dist/core/adapter/execution-source.js +17 -29
  114. package/dist/core/asset/asset-placement.js +4 -13
  115. package/dist/core/asset/frontmatter.js +106 -1
  116. package/dist/core/asset/resolve-ref.js +1 -1
  117. package/dist/core/bundle-id.js +42 -5
  118. package/dist/core/bundle-rename.js +285 -0
  119. package/dist/core/config/config-io.js +1 -2
  120. package/dist/core/config/config-schema.js +9 -34
  121. package/dist/core/config/config-walker.js +1 -1
  122. package/dist/core/config/config.js +184 -111
  123. package/dist/core/config/engine-semantics.js +0 -2
  124. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  125. package/dist/core/config/schema/embedding.js +20 -5
  126. package/dist/core/config/schema/engines.js +5 -0
  127. package/dist/core/config/schema/execution.js +1 -1
  128. package/dist/core/config/schema/experimental.js +1 -1
  129. package/dist/core/config/schema/improve-processes.js +54 -125
  130. package/dist/core/config/schema/improve.js +4 -42
  131. package/dist/core/config/schema/index-config.js +9 -48
  132. package/dist/core/config/schema/scheduler.js +12 -12
  133. package/dist/core/config/schema/search.js +6 -22
  134. package/dist/core/env-secret-ref.js +0 -1
  135. package/dist/core/errors.js +8 -9
  136. package/dist/core/file-change.js +13 -5
  137. package/dist/core/file-lock.js +76 -173
  138. package/dist/core/improve-result.js +35 -7
  139. package/dist/core/improve-types.js +0 -1
  140. package/dist/core/logs-db.js +2 -2
  141. package/dist/core/loopback.js +7 -12
  142. package/dist/core/non-task-input.js +20 -0
  143. package/dist/core/parse.js +13 -16
  144. package/dist/core/paths.js +0 -24
  145. package/dist/core/redaction.js +109 -2
  146. package/dist/core/run-lock.js +2 -5
  147. package/dist/core/spawn-env.js +1 -1
  148. package/dist/core/state/migrations.js +123 -61
  149. package/dist/core/state-db-scope.js +2 -4
  150. package/dist/core/state-db.js +126 -692
  151. package/dist/core/time.js +0 -20
  152. package/dist/core/type-presentation.js +1 -9
  153. package/dist/core/write-source.js +294 -1005
  154. package/dist/execution/input-contract.js +1 -1
  155. package/dist/execution/resolved-request.js +135 -689
  156. package/dist/execution/source.js +63 -257
  157. package/dist/execution/target-ref.js +1 -1
  158. package/dist/indexer/bundle-identity-guard.js +2 -2
  159. package/dist/indexer/db/llm-cache.js +2 -2
  160. package/dist/indexer/ensure-index.js +77 -73
  161. package/dist/indexer/index-rebuild-lock.js +3 -11
  162. package/dist/indexer/index-writer-lock.js +8 -17
  163. package/dist/indexer/index-written-assets.js +141 -154
  164. package/dist/indexer/indexer.js +400 -1124
  165. package/dist/indexer/links/declared-links.js +90 -0
  166. package/dist/indexer/materialize-embeddings.js +60 -397
  167. package/dist/indexer/passes/memory-inference.js +96 -90
  168. package/dist/indexer/passes/metadata.js +132 -219
  169. package/dist/indexer/read-preflight.js +0 -7
  170. package/dist/indexer/scan/doc-to-entry.js +2 -3
  171. package/dist/indexer/scan/drain-dir.js +1 -1
  172. package/dist/indexer/search/db-search.js +190 -590
  173. package/dist/indexer/search/fts-query.js +30 -41
  174. package/dist/indexer/search/ranking.js +28 -154
  175. package/dist/indexer/search/search-attribution.js +12 -32
  176. package/dist/indexer/search/search-fields.js +11 -15
  177. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  178. package/dist/indexer/search/search-source.js +1 -4
  179. package/dist/indexer/usage/usage-events.js +36 -7
  180. package/dist/indexer/walk/walker.js +3 -4
  181. package/dist/integrations/agent/engine-fallback.js +23 -40
  182. package/dist/integrations/agent/engine-resolution.js +93 -183
  183. package/dist/integrations/agent/execution.js +507 -0
  184. package/dist/integrations/agent/model-map.js +28 -156
  185. package/dist/integrations/agent/request-lowering.js +66 -141
  186. package/dist/integrations/agent/runner-dispatch.js +143 -321
  187. package/dist/integrations/agent/runner.js +54 -14
  188. package/dist/integrations/lockfile.js +53 -101
  189. package/dist/llm/client.js +18 -6
  190. package/dist/llm/embedders/deterministic.js +2 -3
  191. package/dist/llm/embedders/profile.js +71 -0
  192. package/dist/llm/embedders/remote.js +11 -17
  193. package/dist/llm/feature-gate.js +0 -8
  194. package/dist/llm/index-passes.js +3 -5
  195. package/dist/llm/memory-infer.js +1 -2
  196. package/dist/llm/structured-call.js +5 -24
  197. package/dist/output/generic-render.js +23 -11
  198. package/dist/output/html-render.js +13 -10
  199. package/dist/output/render-registry.js +3 -32
  200. package/dist/output/shapes/helpers.js +25 -38
  201. package/dist/output/shapes/passthrough.js +1 -9
  202. package/dist/{indexer/graph/graph-types.js → output/text/bundle-rename.js} +4 -1
  203. package/dist/output/text/command-format.js +69 -31
  204. package/dist/output/text/helpers.js +1 -1
  205. package/dist/output/text/migrate.js +5 -14
  206. package/dist/output/text/proposal-format.js +48 -3
  207. package/dist/output/text/show-format.js +13 -17
  208. package/dist/output/text/workflow-format.js +0 -32
  209. package/dist/output/text.js +2 -0
  210. package/dist/registry/factory.js +4 -19
  211. package/dist/registry/network.js +66 -220
  212. package/dist/registry/providers/index.js +0 -2
  213. package/dist/registry/providers/skills-sh.js +3 -14
  214. package/dist/registry/providers/static-index.js +24 -26
  215. package/dist/registry/resolve.js +55 -131
  216. package/dist/scripts/akm-migrate-node.js +42948 -92369
  217. package/dist/scripts/akm-migrate.js +42935 -92354
  218. package/dist/setup/registry-stash-loader.js +4 -13
  219. package/dist/setup/semantic-assets.js +3 -44
  220. package/dist/setup/setup.js +1 -1
  221. package/dist/setup/steps/connection.js +5 -6
  222. package/dist/setup/steps/platforms.js +2 -2
  223. package/dist/setup/steps/tasks.js +25 -15
  224. package/dist/sources/provider-factory.js +17 -18
  225. package/dist/sources/providers/filesystem.js +2 -3
  226. package/dist/sources/providers/git-install.js +7 -1
  227. package/dist/sources/providers/git-provider.js +0 -3
  228. package/dist/sources/providers/git-stash.js +83 -21
  229. package/dist/sources/providers/npm.js +2 -4
  230. package/dist/sources/providers/provider-utils.js +5 -10
  231. package/dist/sources/providers/website.js +0 -2
  232. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  233. package/dist/sources/website-url.js +2 -2
  234. package/dist/storage/database.js +9 -35
  235. package/dist/storage/repositories/improve-ledger-repository.js +209 -0
  236. package/dist/storage/repositories/index-connection.js +39 -72
  237. package/dist/storage/repositories/index-entries-repository.js +131 -129
  238. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  239. package/dist/storage/repositories/index-entry-schema.js +101 -268
  240. package/dist/storage/repositories/index-fts-repository.js +86 -256
  241. package/dist/storage/repositories/index-links-repository.js +143 -0
  242. package/dist/storage/repositories/index-llm-cache-repository.js +7 -9
  243. package/dist/storage/repositories/index-meta-repository.js +6 -4
  244. package/dist/storage/repositories/index-schema.js +257 -325
  245. package/dist/storage/repositories/index-utility-repository.js +8 -29
  246. package/dist/storage/repositories/index-vec-repository.js +133 -414
  247. package/dist/storage/repositories/outcome-repository.js +2 -1
  248. package/dist/storage/repositories/proposals-repository.js +104 -1
  249. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  250. package/dist/storage/repositories/salience-repository.js +1 -19
  251. package/dist/storage/repositories/task-history-repository.js +26 -4
  252. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  253. package/dist/storage/sqlite-migrations.js +136 -0
  254. package/dist/storage/sqlite-pragmas.js +11 -9
  255. package/dist/storage/sqlite-transaction.js +170 -0
  256. package/dist/storage/state-db-integrity.js +130 -0
  257. package/dist/tasks/activation-config.js +134 -62
  258. package/dist/tasks/backends/cron.js +191 -302
  259. package/dist/tasks/backends/exec-utils.js +2 -5
  260. package/dist/tasks/backends/launchd.js +141 -748
  261. package/dist/tasks/backends/schtasks.js +119 -623
  262. package/dist/tasks/prepare/prepare-support.js +5 -15
  263. package/dist/tasks/prepare/prepare.js +0 -2
  264. package/dist/tasks/resolve-akm-bin.js +20 -79
  265. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  266. package/dist/tasks/run/load-task.js +1 -1
  267. package/dist/tasks/scheduler-binding.js +20 -238
  268. package/dist/tasks/scheduler-invocation.js +136 -244
  269. package/dist/tasks/scheduler-lock.js +53 -0
  270. package/dist/tasks/scheduler-sync.js +368 -679
  271. package/dist/tasks/source/parse-task-source.js +55 -9
  272. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  273. package/dist/tasks/source/task-to-v4.js +464 -88
  274. package/dist/workflows/authoring/authoring.js +3 -12
  275. package/dist/workflows/compile.js +211 -0
  276. package/dist/workflows/concurrency-policy.js +13 -74
  277. package/dist/workflows/exec/child-invocation.js +3 -17
  278. package/dist/workflows/exec/child-workflow.js +32 -141
  279. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  280. package/dist/workflows/exec/environment.js +98 -0
  281. package/dist/workflows/exec/exec-unit.js +33 -140
  282. package/dist/workflows/exec/frozen-judge.js +7 -59
  283. package/dist/workflows/exec/native-executor.js +82 -341
  284. package/dist/workflows/exec/param-secrets.js +29 -47
  285. package/dist/workflows/exec/run-workflow.js +154 -387
  286. package/dist/workflows/exec/scheduler.js +9 -36
  287. package/dist/workflows/exec/step-work.js +127 -430
  288. package/dist/workflows/exec/unit-dispatch.js +11 -63
  289. package/dist/workflows/exec/unit-writer.js +8 -52
  290. package/dist/workflows/exec/worktree.js +39 -273
  291. package/dist/workflows/freeze/child-output-references.js +4 -15
  292. package/dist/workflows/freeze/environment.js +99 -92
  293. package/dist/workflows/freeze/freeze.js +172 -0
  294. package/dist/workflows/freeze/step-values.js +19 -21
  295. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  296. package/dist/workflows/freeze/targets/command.js +10 -33
  297. package/dist/workflows/freeze/targets/script.js +5 -12
  298. package/dist/workflows/freeze/targets/shell.js +3 -6
  299. package/dist/workflows/freeze/targets/task.js +25 -80
  300. package/dist/workflows/freeze/task-bindings.js +20 -67
  301. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  302. package/dist/workflows/ir/params.js +6 -51
  303. package/dist/workflows/ir/plan-hash.js +2 -34
  304. package/dist/workflows/parser.js +140 -43
  305. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  306. package/dist/workflows/renderer.js +36 -69
  307. package/dist/workflows/resource-limits.js +12 -120
  308. package/dist/workflows/runtime/agent-identity.js +8 -40
  309. package/dist/workflows/runtime/run-outputs.js +3 -6
  310. package/dist/workflows/runtime/run-plan.js +316 -0
  311. package/dist/workflows/runtime/runs.js +48 -200
  312. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  313. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  314. package/dist/workflows/validate-summary.js +2 -7
  315. package/docs/integration/bundling-akm.md +49 -42
  316. package/docs/migration/README.md +1 -0
  317. package/docs/migration/release-notes/0.9.17.md +43 -0
  318. package/docs/migration/v0.9.1-to-v0.9.2.md +23 -7
  319. package/docs/reference/cli.md +232 -135
  320. package/docs/reference/configuration.md +71 -57
  321. package/docs/reference/data-and-telemetry.md +20 -21
  322. package/docs/reference/tasks.md +105 -39
  323. package/docs/reference/workflow-schema.md +14 -18
  324. package/docs/reference/workflows.md +6 -9
  325. package/package.json +1 -1
  326. package/schemas/akm-config.json +115 -738
  327. package/schemas/akm-workflow.json +1 -0
  328. package/dist/assets/improve-strategies/graph-refresh.json +0 -15
  329. package/dist/assets/prompts/contradiction-judge.md +0 -33
  330. package/dist/assets/prompts/graph-extract-system.md +0 -1
  331. package/dist/assets/prompts/graph-extract-user-prompt.md +0 -35
  332. package/dist/assets/prompts/metadata-enhance-system.md +0 -1
  333. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +0 -4
  334. package/dist/commands/health/advisories.js +0 -150
  335. package/dist/commands/health/metrics.js +0 -329
  336. package/dist/commands/health/surfaces.js +0 -102
  337. package/dist/commands/improve/anti-collapse.js +0 -83
  338. package/dist/commands/improve/collapse-detector.js +0 -432
  339. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  340. package/dist/commands/improve/consolidate/merge.js +0 -149
  341. package/dist/commands/improve/distill/promote-memory.js +0 -291
  342. package/dist/commands/improve/distill/quality-gate.js +0 -337
  343. package/dist/commands/improve/eval-cases.js +0 -52
  344. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  345. package/dist/commands/improve/proposal-envelope.js +0 -31
  346. package/dist/commands/improve/run-context.js +0 -123
  347. package/dist/commands/improve/shared.js +0 -31
  348. package/dist/commands/improve/source-identity.js +0 -28
  349. package/dist/commands/improve/triage.js +0 -96
  350. package/dist/commands/proposal/drain-policies.js +0 -151
  351. package/dist/commands/sources/update-transaction.js +0 -220
  352. package/dist/core/action-contributors.js +0 -28
  353. package/dist/core/config/config-version-shim.js +0 -101
  354. package/dist/core/fs-txn.js +0 -405
  355. package/dist/core/lexical-score.js +0 -25
  356. package/dist/core/maintenance-barrier.js +0 -167
  357. package/dist/execution/executable-identity.js +0 -105
  358. package/dist/execution/guarded-source.js +0 -427
  359. package/dist/indexer/db/graph-db.js +0 -444
  360. package/dist/indexer/graph/graph-boost.js +0 -427
  361. package/dist/indexer/graph/graph-dedup.js +0 -95
  362. package/dist/indexer/graph/graph-extraction.js +0 -1108
  363. package/dist/indexer/search/name-match.js +0 -35
  364. package/dist/indexer/search/ranking-contributors.js +0 -515
  365. package/dist/indexer/search/ranking-types.js +0 -4
  366. package/dist/indexer/walk/project-context.js +0 -192
  367. package/dist/integrations/agent/execution-cascade.js +0 -566
  368. package/dist/integrations/agent/execution-definitions.js +0 -202
  369. package/dist/integrations/agent/execution-lowering.js +0 -841
  370. package/dist/integrations/agent/execution-preparation.js +0 -98
  371. package/dist/integrations/agent/inline-execution.js +0 -74
  372. package/dist/llm/graph-extract.js +0 -728
  373. package/dist/llm/metadata-enhance.js +0 -96
  374. package/dist/registry/create-provider-registry.js +0 -29
  375. package/dist/registry/pinned-request-helper.js +0 -247
  376. package/dist/registry/pinned-transport.js +0 -717
  377. package/dist/sources/providers/index.js +0 -14
  378. package/dist/storage/engines/sqlite-migrations.js +0 -271
  379. package/dist/storage/repositories/canaries-repository.js +0 -107
  380. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  381. package/dist/storage/repositories/registry-cache.js +0 -113
  382. package/dist/tasks/scheduler-sync-preview.js +0 -52
  383. package/dist/tasks/source/task-to-v3.js +0 -507
  384. package/dist/workflows/freeze/resolve-steps.js +0 -86
  385. package/dist/workflows/freeze/source-freeze.js +0 -64
  386. package/dist/workflows/ir/compile.js +0 -321
  387. package/dist/workflows/ir/environment-v4.js +0 -330
  388. package/dist/workflows/ir/freeze-v4.js +0 -153
  389. package/dist/workflows/ir/schema-v4.js +0 -745
  390. package/dist/workflows/ir/schema.js +0 -354
  391. package/dist/workflows/program/schema.js +0 -77
  392. package/dist/workflows/runtime/checkin.js +0 -57
  393. package/dist/workflows/runtime/plan-classifier.js +0 -196
  394. package/dist/workflows/runtime/unit-checkin.js +0 -45
  395. package/dist/workflows/runtime/unit-phases.js +0 -20
  396. package/dist/workflows/schema.js +0 -4
  397. package/dist/workflows/source-ir/compile.js +0 -200
  398. package/dist/workflows/source-ir/program.js +0 -50
  399. package/dist/workflows/source-ir/result.js +0 -26
  400. package/dist/workflows/source-ir/schema.js +0 -786
  401. package/dist/workflows/source-ir/triggers.js +0 -79
  402. package/dist/workflows/source-ir/uses.js +0 -40
  403. package/dist/workflows/validator.js +0 -60
@@ -1,13 +1,13 @@
1
1
  // This Source Code Form is subject to the terms of the Mozilla Public
2
2
  // License, v. 2.0. If a copy of the MPL was not distributed with this
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
+ /** The refs an improve run may consider, read from the index, and the small predicates over them. */
4
5
  import fs from "node:fs";
5
6
  import path from "node:path";
6
7
  import { parseFrontmatter } from "../../core/asset/frontmatter.js";
7
- import { conceptIdFromTypeName, parseRefInput, resolveRef } from "../../core/asset/resolve-ref.js";
8
+ import { conceptIdFromTypeName, parseRefInput, resolveRef, typeNameFromConceptId } from "../../core/asset/resolve-ref.js";
8
9
  import { loadConfig } from "../../core/config/config.js";
9
10
  import { ConfigError, NotFoundError, rethrowIfTestIsolationError, UsageError } from "../../core/errors.js";
10
- import { readEvents } from "../../core/events.js";
11
11
  import { isPathAbsent } from "../../core/path-access.js";
12
12
  import { getDbPath } from "../../core/paths.js";
13
13
  import { deriveInstallations, deriveWritableBundleIds } from "../../indexer/installations.js";
@@ -15,70 +15,71 @@ import { resolveSourceEntries } from "../../indexer/search/search-source.js";
15
15
  import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
16
16
  import { closeDatabase, openExistingDatabase, openReadonlyExistingDatabase, } from "../../storage/repositories/index-connection.js";
17
17
  import { getAllEntries } from "../../storage/repositories/index-entries-repository.js";
18
+ import { hasCurrentEntriesTable } from "../../storage/repositories/index-entry-schema.js";
18
19
  import { getUtilityScoresByIds } from "../../storage/repositories/index-utility-repository.js";
19
20
  import { SqliteReadSnapshotUnavailableError } from "../../storage/sqlite-read-snapshot.js";
20
21
  import { isDistillRefusedInputType } from "./distill.js";
21
22
  import { isStrategyFilteredForAllPasses } from "./improve-strategies.js";
22
23
  import { parseMemoryRef } from "./memory/derived-ref.js";
23
- import { improveStateReadRefs } from "./source-identity.js";
24
- // Eligibility / candidate-selection predicates for improve. Free functions
25
- // (no akmImprove closure state) extracted from improve.ts to shrink the
26
- // orchestrator and make candidate selection independently testable.
24
+ /** The asset type of a short or bundle-qualified conceptId (`""` when unknown). */
25
+ export function assetTypeOf(ref) {
26
+ return typeNameFromConceptId(ref.includes("//") ? ref.slice(ref.indexOf("//") + 2) : ref)?.type ?? "";
27
+ }
27
28
  /**
28
- * Open the index for eligibility queries, or `undefined` when no index exists
29
- * yet. `openExistingDatabase` refuses to create a missing `index.db` (see
30
- * index-connection.ts) — for eligibility, "no index" simply means nothing is
31
- * eligible, exactly like the readOnly arm's `undefined`.
32
- *
33
- * `undefined` is reserved for a genuinely ABSENT index. The readOnly arm has
34
- * refused to conflate that with an unreadable one since #791; this arm used
35
- * `fs.existsSync`, so the same run reported "nothing eligible to improve" at
36
- * exit 0 depending only on which branch it took. Both arms now raise.
29
+ * Run `fn` against index.db — the live index, or a non-mutating snapshot for
30
+ * a dry run. `undefined` when there is none or the read fails (best-effort).
37
31
  */
38
- function openEligibilityDb(readOnly) {
39
- if (readOnly)
40
- return openReadonlyExistingDatabase(undefined, { isolatedSnapshot: true });
41
- return isPathAbsent(getDbPath()) ? undefined : openExistingDatabase();
42
- }
43
- function describeIndexSnapshot(readOnly, status) {
44
- if (status === "ready") {
45
- return {
46
- status,
47
- reason: readOnly ? "loaded a non-mutating point-in-time copy of the existing index" : "loaded the prepared index",
48
- };
32
+ export function withIndexDb(readOnly, fn) {
33
+ let db;
34
+ try {
35
+ db = readOnly ? openReadonlyExistingDatabase(undefined, { isolatedSnapshot: true }) : openExistingDatabase();
36
+ return db ? fn(db) : undefined;
37
+ }
38
+ catch (err) {
39
+ rethrowIfTestIsolationError(err);
40
+ return undefined;
41
+ }
42
+ finally {
43
+ if (db)
44
+ closeDatabase(db);
49
45
  }
50
- return {
51
- status,
52
- reason: readOnly
53
- ? "index.db is missing; dry-run uses an empty snapshot and does not create it"
54
- : "index.db is missing after index preparation; the selector uses an empty snapshot",
55
- };
56
- }
57
- function describeUnavailableSnapshot(error) {
58
- return {
59
- status: "incompatible",
60
- reason: `index.db cannot provide a stable non-mutating snapshot (${error.message}); dry-run uses an empty snapshot`,
61
- };
62
46
  }
63
47
  /**
64
- * A dry-run is explicitly a non-mutating planning operation, so it may report
65
- * an unusable derived index as an empty snapshot. This is deliberately a
66
- * typed boundary mapping: readers otherwise receive no incompatible handle,
67
- * and we must not turn an arbitrary SQLite error into a successful plan by
68
- * matching its text.
48
+ * The index for candidate selection, or `undefined` when it is absent. An
49
+ * index without a readable `entries` table needs a rebuild and says so.
69
50
  */
70
- function isIncompatibleIndexError(error) {
71
- return error instanceof ConfigError && error.code === "INDEX_SCHEMA_INCOMPATIBLE";
51
+ function openEligibilityDb(readOnly) {
52
+ const db = readOnly
53
+ ? openReadonlyExistingDatabase(undefined, { isolatedSnapshot: true })
54
+ : isPathAbsent(getDbPath())
55
+ ? undefined
56
+ : openExistingDatabase();
57
+ if (db && !hasCurrentEntriesTable(db)) {
58
+ closeDatabase(db);
59
+ throw new ConfigError("index.db has no entries table this akm can read, so nothing can be selected for improvement.", "INDEX_SCHEMA_INCOMPATIBLE", "Run `akm index` to rebuild the derived index from the currently materialized sources.");
60
+ }
61
+ return db;
72
62
  }
73
- function describeIncompatibleIndexSnapshot(error) {
74
- // `INDEX_SCHEMA_INCOMPATIBLE` establishes that this is the derived-index
75
- // boundary, and the error's hint preserves whether this binary should
76
- // rebuild an older/unknown generation or upgrade for a newer one.
77
- const action = error.hint() ?? error.message;
78
- return {
79
- status: "incompatible",
80
- reason: `index.db is incompatible; ${action} Dry-run uses an empty snapshot and does not migrate it.`,
81
- };
63
+ /**
64
+ * How a dry run describes an index it could not read. Only the typed
65
+ * failures map to an empty plan; any other error still throws.
66
+ */
67
+ function unreadableSnapshot(readOnly, error) {
68
+ if (!readOnly)
69
+ return undefined;
70
+ if (error instanceof SqliteReadSnapshotUnavailableError) {
71
+ return {
72
+ status: "incompatible",
73
+ reason: `index.db cannot provide a stable non-mutating snapshot (${error.message}); dry-run uses an empty snapshot`,
74
+ };
75
+ }
76
+ if (error instanceof ConfigError && error.code === "INDEX_SCHEMA_INCOMPATIBLE") {
77
+ return {
78
+ status: "incompatible",
79
+ reason: `index.db is incompatible; ${error.hint() ?? error.message} Dry-run uses an empty snapshot and does not migrate it.`,
80
+ };
81
+ }
82
+ return undefined;
82
83
  }
83
84
  export function resolveImproveScope(scope) {
84
85
  const trimmed = scope?.trim();
@@ -89,80 +90,68 @@ export function resolveImproveScope(scope) {
89
90
  return { mode: "ref", value: trimmed };
90
91
  }
91
92
  catch (err) {
92
- // A bare word with no ref separator is a `--scope <type>` filter attempt,
93
- // not a ref. Any such word is accepted, including foreign/unknown type strings,
94
- // which simply match zero entries downstream (a read-only query filter,
95
- // not a data-acceptance gate, so no deny-list is needed here: an
96
- // unrecognized type just matches nothing, since nothing is ever indexed
97
- // with it). No test pinned the old "Unknown asset type" rejection for
98
- // this case (chunk-1.5 anchors §A.5 — 0 hits).
99
- //
100
- // A colon-shaped value that still fails `parseRefInput` is a genuinely
101
- // malformed ref attempt (bad name, path traversal, or a deny-listed
102
- // deliberately-removed type like tool/vault, D1.5-6) — that must still
103
- // surface as a real error, not be silently absorbed into a type filter
104
- // that will never match anything.
105
- if (!/[/:#]/.test(trimmed)) {
93
+ // A bare word is a `--scope <type>` filter (an unknown type matches
94
+ // nothing); a ref-shaped value that fails to parse is a real error.
95
+ if (!/[/:#]/.test(trimmed))
106
96
  return { mode: "type", value: trimmed };
107
- }
108
97
  const message = err instanceof Error ? err.message : String(err);
109
98
  throw new UsageError(`Invalid --scope "${trimmed}": ${message}`, "INVALID_FLAG_VALUE");
110
99
  }
111
100
  }
112
- /**
113
- * Dedupe eligible refs by their durable item ref when available, preserving
114
- * the first candidate and its display ref. Used to
115
- * merge the eligibility sources (feedback-signal, Layer-2 proactive-maintenance,
116
- * high-salience) without admitting a ref into the loop twice.
117
- */
101
+ /** Dedupe by durable identity (item_ref, else ref), keeping the first. */
118
102
  export function dedupeRefs(refs) {
119
103
  const seen = new Set();
120
- const out = [];
121
- for (const r of refs) {
104
+ return refs.filter((r) => {
122
105
  const key = r.itemRef ?? r.ref;
123
106
  if (seen.has(key))
124
- continue;
107
+ return false;
125
108
  seen.add(key);
126
- out.push(r);
127
- }
128
- return out;
109
+ return true;
110
+ });
129
111
  }
130
112
  export async function collectEligibleRefs(scope, stashDir, improveProfile, config) {
131
113
  return collectEligibleRefsFromIndex(scope, stashDir, improveProfile, false, config);
132
114
  }
133
- /** Dry-run planner path: query an existing index without creating or mutating it. */
115
+ /** Dry-run selector: query an existing index without creating or mutating it. */
134
116
  export async function collectEligibleRefsReadOnly(scope, stashDir, improveProfile, config) {
135
117
  return collectEligibleRefsFromIndex(scope, stashDir, improveProfile, true, config);
136
118
  }
137
119
  async function collectEligibleRefsFromIndex(scope, stashDir, improveProfile, readOnly, configOverride) {
120
+ const empty = (indexSnapshot) => ({
121
+ plannedRefs: [],
122
+ memorySummary: { eligible: 0, derived: 0 },
123
+ strategyFilteredRefs: [],
124
+ ...(indexSnapshot ? { indexSnapshot } : {}),
125
+ });
138
126
  const config = configOverride ?? loadConfig();
139
127
  let sources;
140
128
  try {
141
129
  sources = resolveSourceEntries(stashDir, config);
142
130
  }
143
131
  catch {
144
- return { plannedRefs: [], memorySummary: { eligible: 0, derived: 0 }, strategyFilteredRefs: [] };
145
- }
146
- if (sources.length === 0) {
147
- return { plannedRefs: [], memorySummary: { eligible: 0, derived: 0 }, strategyFilteredRefs: [] };
132
+ return empty();
148
133
  }
134
+ if (sources.length === 0)
135
+ return empty();
149
136
  const installations = deriveInstallations(sources);
150
137
  const writableBundleIds = deriveWritableBundleIds(sources);
151
- if (scope.mode === "ref" && scope.value) {
152
- let db;
153
- try {
154
- db = openEligibilityDb(readOnly);
155
- if (!db) {
156
- return {
157
- plannedRefs: [],
158
- memorySummary: { eligible: 0, derived: 0 },
159
- strategyFilteredRefs: [],
160
- indexSnapshot: describeIndexSnapshot(readOnly, "missing"),
161
- };
162
- }
163
- const indexSnapshot = describeIndexSnapshot(readOnly, "ready");
164
- const entries = getAllEntries(db);
165
- const entriesByItemRef = new Map(entries.map((entry) => [entry.itemRef, entry]));
138
+ const ready = {
139
+ status: "ready",
140
+ reason: readOnly ? "loaded a non-mutating point-in-time copy of the existing index" : "loaded the prepared index",
141
+ };
142
+ const missing = {
143
+ status: "missing",
144
+ reason: readOnly
145
+ ? "index.db is missing; dry-run uses an empty snapshot and does not create it"
146
+ : "index.db is missing after index preparation; the selector uses an empty snapshot",
147
+ };
148
+ let db;
149
+ try {
150
+ db = openEligibilityDb(readOnly);
151
+ if (!db)
152
+ return empty(missing);
153
+ if (scope.mode === "ref" && scope.value) {
154
+ const entriesByItemRef = new Map(getAllEntries(db).map((entry) => [entry.itemRef, entry]));
166
155
  const resolved = resolveRef(scope.value, {
167
156
  defaultBundle: config.defaultBundle,
168
157
  bundles: installations.map((installation) => ({
@@ -172,77 +161,31 @@ async function collectEligibleRefsFromIndex(scope, stashDir, improveProfile, rea
172
161
  });
173
162
  const indexed = entriesByItemRef.get(`${resolved.bundle}//${resolved.conceptId}`);
174
163
  if (!indexed?.bundleId || !indexed.conceptId || !fs.existsSync(indexed.filePath)) {
175
- if (await findAssetFilePath(scope.value, stashDir)) {
176
- return {
177
- plannedRefs: [],
178
- memorySummary: { eligible: 0, derived: 0 },
179
- strategyFilteredRefs: [],
180
- indexSnapshot,
181
- };
182
- }
164
+ if (await findAssetFilePath(scope.value, stashDir))
165
+ return empty(ready);
183
166
  throw new NotFoundError(`Asset not found in the selected writable source: ${scope.value}`, "ASSET_NOT_FOUND");
184
167
  }
185
- if (!writableBundleIds.has(indexed.bundleId)) {
186
- return { plannedRefs: [], memorySummary: { eligible: 0, derived: 0 }, strategyFilteredRefs: [], indexSnapshot };
187
- }
168
+ if (!writableBundleIds.has(indexed.bundleId))
169
+ return empty(ready);
170
+ const isMemory = indexed.entry.type === "memory";
188
171
  return {
189
172
  plannedRefs: [
190
173
  { ref: indexed.conceptId, itemRef: indexed.itemRef, reason: "scope-ref", filePath: indexed.filePath },
191
174
  ],
192
175
  memorySummary: {
193
- eligible: indexed.entry.type === "memory" ? 1 : 0,
194
- derived: indexed.entry.type === "memory" && indexed.entry.name.endsWith(".derived") ? 1 : 0,
176
+ eligible: isMemory ? 1 : 0,
177
+ derived: isMemory && indexed.entry.name.endsWith(".derived") ? 1 : 0,
195
178
  },
196
179
  strategyFilteredRefs: [],
197
- indexSnapshot,
180
+ indexSnapshot: ready,
198
181
  };
199
182
  }
200
- catch (error) {
201
- if (readOnly && error instanceof SqliteReadSnapshotUnavailableError) {
202
- return {
203
- plannedRefs: [],
204
- memorySummary: { eligible: 0, derived: 0 },
205
- strategyFilteredRefs: [],
206
- indexSnapshot: describeUnavailableSnapshot(error),
207
- };
208
- }
209
- if (readOnly && isIncompatibleIndexError(error)) {
210
- return {
211
- plannedRefs: [],
212
- memorySummary: { eligible: 0, derived: 0 },
213
- strategyFilteredRefs: [],
214
- indexSnapshot: describeIncompatibleIndexSnapshot(error),
215
- };
216
- }
217
- throw error;
218
- }
219
- finally {
220
- if (db)
221
- closeDatabase(db);
222
- }
223
- }
224
- let db;
225
- try {
226
- db = openEligibilityDb(readOnly);
227
- if (!db) {
228
- return {
229
- plannedRefs: [],
230
- memorySummary: { eligible: 0, derived: 0 },
231
- strategyFilteredRefs: [],
232
- indexSnapshot: describeIndexSnapshot(readOnly, "missing"),
233
- };
234
- }
235
- const indexSnapshot = describeIndexSnapshot(readOnly, "ready");
236
183
  const entries = getAllEntries(db, scope.mode === "type" ? scope.value : undefined).filter((indexed) => writableBundleIds.has(indexed.bundleId));
237
184
  const planned = new Map();
238
185
  const strategyFiltered = new Map();
239
186
  let memoryEligible = 0;
240
187
  let memoryDerived = 0;
241
188
  for (const indexed of entries) {
242
- // Chunk-8 WI-8.5c: the candidate `ref` is the SHORT conceptId
243
- // (`<stash-subdir>/<name>`, D-R2) — it now matches the disk lookup,
244
- // xrefs, and `displayRef` output spelling. `.itemRef` below stays the
245
- // fully-qualified durable key.
246
189
  const ref = conceptIdFromTypeName(indexed.entry.type, indexed.entry.name);
247
190
  try {
248
191
  parseRefInput(ref);
@@ -252,40 +195,16 @@ async function collectEligibleRefsFromIndex(scope, stashDir, improveProfile, rea
252
195
  continue;
253
196
  throw error;
254
197
  }
255
- // Derive the durable item_ref from indexed provenance without another query.
256
- // A provenance-free row uses the short conceptId ref for improve state.
257
- const itemRef = indexed.itemRef;
258
198
  const isDerived = indexed.entry.name.endsWith(".derived");
259
- // `.derived` memories are LLM-inferred and intentionally skip reflect
260
- // (see the synthetic `derived-memory-reflect-skipped` branch in the
261
- // improve loop). Enqueueing them here just produced one synthetic skip
262
- // per derived memory per hour with no real work — pure churn observed
263
- // 2026-05-21: 11 derived refs re-planned every hour during idle periods.
264
- // The cleanup phase (analyzeMemoryCleanup) inspects derived memories
265
- // independently of `plannedRefs`, so dropping them here loses nothing.
199
+ // `.derived` memories skip reflect; cleanup inspects them on its own.
266
200
  if (!isDerived && !planned.has(ref) && !strategyFiltered.has(ref)) {
267
- // 2026-05-27: extend the .derived precedent to strategy-incompatible
268
- // refs. If every per-ref pass (reflect + distill) on the active
269
- // strategy would refuse this ref, drop it from `plannedRefs`. The
270
- // caller emits `improve_skipped { reason: strategy_filtered_all_passes }`
271
- // once `eventsCtx` is available so the audit trail is preserved in a
272
- // single event per ref instead of 2× synthetic actions per run.
273
- // Background: see /tmp/akm-health-investigations/planner-profile-metrics-deep-analysis.md
201
+ const candidate = { ref, filePath: indexed.filePath, itemRef: indexed.itemRef };
274
202
  if (improveProfile && isStrategyFilteredForAllPasses(ref, improveProfile)) {
275
- strategyFiltered.set(ref, {
276
- ref,
277
- reason: "strategy_filtered_all_passes",
278
- filePath: indexed.filePath,
279
- itemRef,
280
- });
203
+ strategyFiltered.set(ref, { ...candidate, reason: "strategy_filtered_all_passes" });
281
204
  }
282
205
  else {
283
- planned.set(ref, {
284
- ref,
285
- reason: scope.mode === "type" ? "scope-type" : indexed.entry.type === "memory" ? "memory-cleanup" : "scope-type",
286
- filePath: indexed.filePath,
287
- itemRef,
288
- });
206
+ const reason = scope.mode !== "type" && indexed.entry.type === "memory" ? "memory-cleanup" : "scope-type";
207
+ planned.set(ref, { ...candidate, reason });
289
208
  }
290
209
  }
291
210
  if (indexed.entry.type === "memory") {
@@ -298,28 +217,14 @@ async function collectEligibleRefsFromIndex(scope, stashDir, improveProfile, rea
298
217
  plannedRefs: [...planned.values()],
299
218
  memorySummary: { eligible: memoryEligible, derived: memoryDerived },
300
219
  strategyFilteredRefs: [...strategyFiltered.values()],
301
- indexSnapshot,
220
+ indexSnapshot: ready,
302
221
  };
303
222
  }
304
223
  catch (error) {
305
- // Empty-stash setup paths can open index.db before its schema exists.
306
224
  rethrowIfTestIsolationError(error);
307
- if (readOnly && error instanceof SqliteReadSnapshotUnavailableError) {
308
- return {
309
- plannedRefs: [],
310
- memorySummary: { eligible: 0, derived: 0 },
311
- strategyFilteredRefs: [],
312
- indexSnapshot: describeUnavailableSnapshot(error),
313
- };
314
- }
315
- if (readOnly && isIncompatibleIndexError(error)) {
316
- return {
317
- plannedRefs: [],
318
- memorySummary: { eligible: 0, derived: 0 },
319
- strategyFilteredRefs: [],
320
- indexSnapshot: describeIncompatibleIndexSnapshot(error),
321
- };
322
- }
225
+ const snapshot = unreadableSnapshot(readOnly, error);
226
+ if (snapshot)
227
+ return empty(snapshot);
323
228
  throw error;
324
229
  }
325
230
  finally {
@@ -327,30 +232,20 @@ async function collectEligibleRefsFromIndex(scope, stashDir, improveProfile, rea
327
232
  closeDatabase(db);
328
233
  }
329
234
  }
235
+ /** The parent memory a `--scope <memory ref>` cleanup is restricted to. */
330
236
  export function memoryCleanupParentRef(scope, stashDir) {
331
237
  if (scope.mode !== "ref" || !scope.value)
332
238
  return undefined;
333
239
  const parsed = parseRefInput(scope.value);
334
240
  if (parsed.type !== "memory")
335
241
  return undefined;
336
- // Non-derived parent scope: emit the canonical `memories/<name>` conceptId so
337
- // it matches `resolveParentRef`'s output in analyzeMemoryCleanup's parentRef
338
- // filter (Group-C item 2 — the reader and every comparison site flipped
339
- // together; emitting the raw scope value would re-open the mismatch the
340
- // chunk-8 history warns about).
341
242
  if (!parsed.name.endsWith(".derived"))
342
243
  return conceptIdFromTypeName(parsed.type, parsed.name);
343
- const sources = resolveSourceEntries(stashDir);
344
- for (const source of sources) {
244
+ for (const source of resolveSourceEntries(stashDir)) {
345
245
  const candidate = path.join(source.path, "memories", `${parsed.name}.md`);
346
246
  if (!fs.existsSync(candidate))
347
247
  continue;
348
- const raw = fs.readFileSync(candidate, "utf8");
349
- const fm = parseFrontmatter(raw).data;
350
- // The `source:` backref is normalized to the `memories/<name>` conceptId.
351
- // That is exactly what
352
- // analyzeMemoryCleanup's parentRef filter compares against — resolveParentRef
353
- // emits the same conceptId — so the two sites stay in lockstep.
248
+ const fm = parseFrontmatter(fs.readFileSync(candidate, "utf8")).data;
354
249
  const parent = parseMemoryRef(typeof fm.source === "string" ? fm.source : undefined);
355
250
  if (parent)
356
251
  return parent;
@@ -358,169 +253,29 @@ export function memoryCleanupParentRef(scope, stashDir) {
358
253
  return conceptIdFromTypeName("memory", parsed.name.slice(0, -".derived".length));
359
254
  }
360
255
  export function isLessonCandidate(ref) {
361
- // Only lesson assets need lesson-schema validation (description + when_to_use).
362
- // Memories have their own distill path via shouldDistillMemoryRef.
363
- // All other types go through reflect, not distill.
364
256
  return parseRefInput(ref).type === "lesson";
365
257
  }
366
- /**
367
- * Planner-side check: should this ref enter the distill queue?
368
- *
369
- * Distill produces lessons from non-lesson sources. Two cases are eligible:
370
- *
371
- * 1. Memory refs that pass {@link shouldDistillMemoryRef} (the existing
372
- * memory→lesson/knowledge promotion path).
373
- *
374
- * Refs whose `type` is in {@link DISTILL_REFUSED_INPUT_TYPES} (currently
375
- * `lesson:*`) are explicitly excluded — distill refuses them at runtime and
376
- * queuing them just produces a no-op `skipped` outcome per ref per hour. That
377
- * planner waste was the bug fixed in commit
378
- * fix(improve): drop distill-refused types from planner.
379
- *
380
- * Note: prior to this fix the gate used `isLessonCandidate(ref)` directly,
381
- * which was true *only* for `lesson:*` refs — exactly the set distill refuses.
382
- * The result: every hourly run re-queued the same lesson refs, the same skip
383
- * message returned, and no work was ever done. See
384
- * `tests/integration/commands/improve-distill-planner-skip-lessons.test.ts`.
385
- */
258
+ /** Should this ref enter the distill queue? Types distill refuses never do. */
386
259
  export function isDistillCandidateRef(ref, stashDir) {
387
- const parsed = parseRefInput(ref);
388
- if (isDistillRefusedInputType(parsed.type))
260
+ if (isDistillRefusedInputType(parseRefInput(ref).type))
389
261
  return false;
390
262
  return shouldDistillMemoryRef(ref, stashDir);
391
263
  }
264
+ /** A non-derived memory that is not marked `quality: proposed`. */
392
265
  export function shouldDistillMemoryRef(ref, stashDir) {
393
266
  const parsed = parseRefInput(ref);
394
267
  if (parsed.type !== "memory")
395
268
  return false;
396
- const sources = resolveSourceEntries(stashDir);
397
- for (const source of sources) {
269
+ for (const source of resolveSourceEntries(stashDir)) {
398
270
  const candidate = `${source.path}/memories/${parsed.name}.md`;
399
271
  if (!fs.existsSync(candidate))
400
272
  continue;
401
- const raw = fs.readFileSync(candidate, "utf8");
402
- const fm = parseFrontmatter(raw).data;
403
- const quality = typeof fm.quality === "string" ? fm.quality : undefined;
404
- if (quality === "proposed")
273
+ if (parseFrontmatter(fs.readFileSync(candidate, "utf8")).data.quality === "proposed")
405
274
  return false;
406
- return !parsed.name.endsWith(".derived");
275
+ break;
407
276
  }
408
277
  return !parsed.name.endsWith(".derived");
409
278
  }
410
- // ── Signal-delta eligibility helpers (0.8.0) ────────────────────────────────
411
- //
412
- // The 0.8.0 redesign replaced flat time-based cooldowns for reflect/distill
413
- // with a *signal-delta* gate: a ref is re-eligible iff new feedback has
414
- // landed since the last proposal was generated for it. These helpers build
415
- // the two timestamp maps the gate needs in bulk, so the planner avoids
416
- // N+1 queries across the full postCleanupRefs set.
417
- /**
418
- * Latest feedback event timestamp per ref in the active window. Reads all
419
- * `feedback` events newer than `sinceIso` in one query and indexes by ref,
420
- * keeping the maximum `ts` per ref.
421
- *
422
- * Only events with a meaningful payload count as "signal" — `metadata.signal`
423
- * (positive/negative) OR `metadata.note` (a free-form annotation). Empty
424
- * metadata events are ignored so a stray `akm feedback <ref>` invocation
425
- * without a flag doesn't trigger downstream re-processing.
426
- */
427
- export function buildLatestFeedbackTsMap(refs, sinceIso, itemRefByRef, eventsCtx) {
428
- const out = new Map();
429
- if (refs.length === 0)
430
- return out;
431
- // Correlate item_ref-keyed events and conceptId-keyed direct invocations.
432
- const refByDurableKey = new Map(refs.flatMap((ref) => improveStateReadRefs(ref, itemRefByRef?.get(ref)).map((key) => [key, ref])));
433
- const { events } = readEvents({ type: "feedback", since: sinceIso }, eventsCtx);
434
- for (const e of events) {
435
- const ref = e.ref ? refByDurableKey.get(e.ref) : undefined;
436
- if (!ref)
437
- continue;
438
- const meta = e.metadata;
439
- const hasSignal = meta !== undefined && (typeof meta.signal === "string" || typeof meta.note === "string");
440
- if (!hasSignal)
441
- continue;
442
- const ts = e.ts ?? "";
443
- if (ts > (out.get(ref) ?? ""))
444
- out.set(ref, ts);
445
- }
446
- return out;
447
- }
448
- /**
449
- * Latest proposal timestamp per input-ref, filtered by source ('reflect' or
450
- * 'distill'). Reads the corresponding `*_invoked` events from state.db —
451
- * these events are emitted at proposal creation time and carry the *input*
452
- * asset ref (`memories/foo`, `skills/bar`, etc.) directly. We use them rather than
453
- * `listProposals` because distill proposals are keyed by the derived
454
- * lesson/knowledge ref, not the source memory — joining back through the
455
- * payload would be fragile.
456
- */
457
- export function buildLatestProposalTsMap(refs, source, itemRefByRef, eventsCtx) {
458
- const out = new Map();
459
- if (refs.length === 0)
460
- return out;
461
- // Correlate item_ref-keyed events and conceptId-keyed direct invocations.
462
- const refByDurableKey = new Map(refs.flatMap((ref) => improveStateReadRefs(ref, itemRefByRef?.get(ref)).map((key) => [key, ref])));
463
- const eventType = source === "reflect" ? "reflect_invoked" : "distill_invoked";
464
- const { events } = readEvents({ type: eventType }, eventsCtx);
465
- for (const e of events) {
466
- const ref = e.ref ? refByDurableKey.get(e.ref) : undefined;
467
- if (!ref)
468
- continue;
469
- // For distill_invoked we only count attempts that produced (or attempted
470
- // to produce) a real proposal — config_disabled / parse-error outcomes
471
- // should not move the signal-delta cursor forward.
472
- if (eventType === "distill_invoked") {
473
- const outcome = e.metadata?.outcome;
474
- if (outcome !== "queued" && outcome !== "skipped" && outcome !== "validation_failed")
475
- continue;
476
- }
477
- const ts = e.ts ?? "";
478
- if (ts > (out.get(ref) ?? ""))
479
- out.set(ref, ts);
480
- }
481
- return out;
482
- }
483
- /**
484
- * Signal-delta eligibility predicate.
485
- *
486
- * True iff `latestFeedback[ref]` is defined AND either no prior proposal
487
- * exists for this (ref, source) OR `latestFeedback[ref] > lastProposal[ref]`.
488
- *
489
- * Refs with no feedback signal at all are ineligible by definition — the
490
- * proactive-maintenance and high-salience fallback lanes (see
491
- * `noFeedbackCandidates` later in the planner) handle never-rated assets
492
- * separately.
493
- */
494
- export function isSignalDeltaEligible(ref, latestFeedback, lastProposal) {
495
- const fb = latestFeedback.get(ref);
496
- if (!fb)
497
- return false;
498
- const lp = lastProposal.get(ref);
499
- if (!lp)
500
- return true;
501
- return fb > lp;
502
- }
503
- /**
504
- * H7 (#566): cooperative budget watchdog with a captured, RAII-cleared hard-kill.
505
- *
506
- * When the wall-clock budget expires, `onExhausted` (normally an
507
- * `AbortController.abort`) signals cooperative cancellation so the run can drain
508
- * its in-flight log/`state.db` flush and unwind naturally. A second hard-kill
509
- * timer is then armed as a watchdog: it only `exit(0)`s if the drain itself
510
- * overruns `hardKillGraceMs`, preventing the process from outliving the task
511
- * timeout window (lock-cascade fix).
512
- *
513
- * Both timers are captured; the returned dispose() clears whichever is still
514
- * pending. Callers invoke it from a `finally`, so a *clean* drain reaches the
515
- * `finally` and cancels the pending hard-kill before it can fire — the previous
516
- * detached `setTimeout(() => process.exit(0), 5000)` always fired, truncating a
517
- * clean flush. The hard-kill timer is `unref()`-ed so it never keeps the event
518
- * loop alive on its own: once the run drains it exits with its own code, not the
519
- * forced 0.
520
- *
521
- * Dependencies are injectable purely so the concurrency-sensitive timing
522
- * contract can be exercised deterministically in unit tests.
523
- */
524
279
  export function shouldAnalyzeMemoryCleanup(scope, eligibleMemories, primaryStashDir) {
525
280
  if (!primaryStashDir || eligibleMemories === 0)
526
281
  return false;
@@ -528,47 +283,28 @@ export function shouldAnalyzeMemoryCleanup(scope, eligibleMemories, primaryStash
528
283
  return true;
529
284
  if (scope.mode === "type")
530
285
  return scope.value === "memory";
531
- if (!scope.value)
532
- return false;
533
- return parseRefInput(scope.value).type === "memory";
286
+ return scope.value ? parseRefInput(scope.value).type === "memory" : false;
534
287
  }
535
288
  export function buildUtilityMap(refs, readOnly = false) {
536
289
  const map = new Map();
537
290
  if (refs.length === 0)
538
291
  return map;
539
292
  const refSet = new Set(refs.map((r) => r.ref));
540
- let db;
541
- try {
542
- db = readOnly ? openReadonlyExistingDatabase(undefined, { isolatedSnapshot: true }) : openExistingDatabase();
543
- if (!db)
544
- return map;
545
- const allDbEntries = getAllEntries(db);
293
+ withIndexDb(readOnly, (db) => {
546
294
  const idToRef = new Map();
547
- for (const indexed of allDbEntries) {
548
- // Chunk-8 WI-8.5c: correlate on the SHORT conceptId to match the
549
- // `ImproveEligibleRef.ref` set built in collectEligibleRefsFromIndex.
295
+ for (const indexed of getAllEntries(db)) {
550
296
  const ref = conceptIdFromTypeName(indexed.entry.type, indexed.entry.name);
551
297
  if (refSet.has(ref))
552
298
  idToRef.set(indexed.id, ref);
553
299
  }
554
- const ids = [...idToRef.keys()];
555
- if (ids.length > 0) {
556
- const { global: scores } = getUtilityScoresByIds(db, ids);
557
- for (const [id, score] of scores) {
558
- const ref = idToRef.get(id);
559
- if (ref)
560
- map.set(ref, score.utility);
561
- }
300
+ if (idToRef.size === 0)
301
+ return;
302
+ for (const [id, score] of getUtilityScoresByIds(db, [...idToRef.keys()])) {
303
+ const ref = idToRef.get(id);
304
+ if (ref)
305
+ map.set(ref, score.utility);
562
306
  }
563
- }
564
- catch (err) {
565
- rethrowIfTestIsolationError(err);
566
- // best-effort: if DB unavailable, all utilities default to 0
567
- }
568
- finally {
569
- if (db)
570
- closeDatabase(db);
571
- }
307
+ });
572
308
  return map;
573
309
  }
574
310
  export async function findAssetFilePath(ref, stashDir, writableBundleIds) {