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,102 +0,0 @@
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
- /**
5
- * The remaining `surfaces` advisory group for `akm health` (meta-review 08).
6
- * `stash-git-exposure` (08-F1) shipped first in ./stash-exposure.ts; this
7
- * module adds the other two read-only checks the adjudication approved:
8
- *
9
- * - `binary-config-skew` — config.json written by a NEWER akm than this binary (F3)
10
- * - `egress-endpoints` — the remote-destination list, for eyeball diff (surfaces 3/9)
11
- *
12
- * Every collector is a pure projection over injected paths/config (no
13
- * process.env reads) and is silent when there is nothing to report, matching
14
- * the stash-exposure pattern. `egress-endpoints` is the one informational
15
- * (pass-status) entry: it emits whenever any remote endpoint is configured.
16
- */
17
- import { readTextFile } from "../../core/common.js";
18
- import { CURRENT_CONFIG_VERSION } from "../../core/config/config-schema.js";
19
- import { compareConfigVersion } from "../../core/config/config-version.js";
20
- import { formatRegistryUrl } from "../../core/registry-url.js";
21
- /**
22
- * `binary-config-skew` (08-F3): warn when config.json carries a configVersion
23
- * NEWER than (or unorderable against) this binary's CURRENT_CONFIG_VERSION —
24
- * i.e. a newer/foreign akm wrote the shared config and this install is stale.
25
- * That is the proven multi-install incident class. Silent for current or older
26
- * versions and unreadable configs (config loading surfaces its own errors).
27
- */
28
- export function collectConfigSkewAdvisory(configPath) {
29
- let raw;
30
- try {
31
- raw = JSON.parse(readTextFile(configPath, "Config file"));
32
- }
33
- catch {
34
- return undefined;
35
- }
36
- const onDisk = raw.configVersion;
37
- const order = compareConfigVersion(onDisk, CURRENT_CONFIG_VERSION);
38
- const skewed = order === 1 || (onDisk !== undefined && order === undefined);
39
- if (!skewed)
40
- return undefined;
41
- return {
42
- name: "binary-config-skew",
43
- kind: "deterministic",
44
- status: "warn",
45
- confidence: "high",
46
- message: `config.json has configVersion ${JSON.stringify(onDisk)} but this binary knows ${CURRENT_CONFIG_VERSION} — ` +
47
- "a newer akm wrote the shared config, so this install is stale. Upgrade this install; do not keep a stale " +
48
- "binary against the shared config/DBs.",
49
- evidence: { onDiskConfigVersion: onDisk, binaryConfigVersion: CURRENT_CONFIG_VERSION },
50
- };
51
- }
52
- /**
53
- * `egress-endpoints` (08 surfaces 3/9): the full list of remote destinations
54
- * akm can talk to under the effective config — registries, remote sources,
55
- * LLM endpoints, embedding endpoint — as one pass-status informational entry
56
- * for eyeball diff against expectations. Silent only when nothing remote is
57
- * configured at all.
58
- */
59
- export function collectEgressAdvisory(config) {
60
- if (!config)
61
- return undefined;
62
- const endpoints = [];
63
- for (const reg of config.registries ?? []) {
64
- if (reg.enabled === false || !reg.url)
65
- continue;
66
- endpoints.push(`registry ${reg.name ?? "(unnamed)"}: ${formatRegistryUrl(reg.url)}`);
67
- }
68
- for (const [key, bundle] of Object.entries(config.bundles ?? {})) {
69
- if (!bundle)
70
- continue;
71
- const url = bundle.git ?? bundle.website?.url;
72
- if (!url)
73
- continue;
74
- endpoints.push(`source ${key} (${bundle.git ? "git" : "website"}): ${url}`);
75
- }
76
- for (const [name, engine] of Object.entries(config.engines ?? {})) {
77
- if (engine?.kind !== "llm" || !engine.endpoint)
78
- continue;
79
- endpoints.push(`llm ${name}: ${engine.endpoint}`);
80
- }
81
- if (config.embedding?.endpoint)
82
- endpoints.push(`embedding: ${config.embedding.endpoint}`);
83
- if (endpoints.length === 0)
84
- return undefined;
85
- return {
86
- name: "egress-endpoints",
87
- kind: "deterministic",
88
- status: "pass",
89
- confidence: "high",
90
- message: `${endpoints.length} remote endpoint(s) in the effective config (registries/sources/LLM/embedding) — ` +
91
- "review the evidence list for unexpected destinations.",
92
- evidence: { endpoints },
93
- };
94
- }
95
- /**
96
- * Aggregate the two collectors into the advisories array shape `akmHealth`
97
- * consumes. Order is fixed: skew → egress.
98
- */
99
- export function collectSurfacesAdvisories(input) {
100
- const results = [collectConfigSkewAdvisory(input.configPath), collectEgressAdvisory(input.config)];
101
- return results.filter((r) => r !== undefined);
102
- }
@@ -1,83 +0,0 @@
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
- /**
5
- * WS-3b Step 8 — Anti-collapse merge guards.
6
- *
7
- * (a) Generation counter: merged.generation = max(sources)+1; merges cite
8
- * sources. `over_generation_count` (collapse-detector.ts) tracks assets
9
- * above the generation threshold as an advisory metric only — there is
10
- * no merge-refusal path wired in.
11
- * (b) Lexical-diversity check: low n-gram diversity ⇒ raise merge threshold.
12
- * (d) Occasional random non-similar cluster in the pool.
13
- *
14
- * @module anti-collapse
15
- */
16
- /** Default max generation depth before merge is refused. */
17
- export const DEFAULT_MAX_GENERATION = 2;
18
- /** Default fraction of pool to fill with random (non-similar) clusters. */
19
- export const DEFAULT_RANDOM_CLUSTER_FRACTION = 0.05;
20
- /**
21
- * Read the `generation` field from an asset's frontmatter.
22
- * Returns 0 when absent (no generation metadata = original asset).
23
- */
24
- export function readAssetGeneration(frontmatterData) {
25
- const gen = frontmatterData.generation;
26
- if (typeof gen === "number" && Number.isFinite(gen) && gen >= 0) {
27
- return Math.floor(gen);
28
- }
29
- return 0;
30
- }
31
- /**
32
- * Compute the new generation for a merged asset.
33
- * Rule: `merged.generation = max(source generations) + 1`.
34
- */
35
- export function computeMergedGeneration(sourceGenerations) {
36
- if (sourceGenerations.length === 0)
37
- return 1;
38
- return Math.max(...sourceGenerations) + 1;
39
- }
40
- /**
41
- * Compute the bigram n-gram diversity of a text string.
42
- * Returns a value in [0, 1] where 0 = all identical bigrams, 1 = all unique.
43
- * Used by the lexical-diversity check to detect correlated-extraction artifacts.
44
- */
45
- export function computeBigramDiversity(text) {
46
- const words = text
47
- .toLowerCase()
48
- .split(/\s+/)
49
- .filter((w) => w.length > 0);
50
- if (words.length < 2)
51
- return 1; // too short to have bigrams; treat as diverse
52
- const total = words.length - 1;
53
- const unique = new Set();
54
- for (let i = 0; i < total; i++) {
55
- unique.add(`${words[i]}\t${words[i + 1]}`);
56
- }
57
- return unique.size / total;
58
- }
59
- /**
60
- * Check whether a cluster of memories exhibits suspiciously low lexical diversity.
61
- * When true, the cluster is likely a correlated-extraction artifact; the merge
62
- * threshold should be raised.
63
- *
64
- * @param bodies - The stripped body texts of the cluster members.
65
- * @param config - Anti-collapse config.
66
- * @returns `{ lowDiversity: true, diversity }` when the cluster diversity is
67
- * below the 0.3 threshold; `{ lowDiversity: false }` otherwise.
68
- */
69
- export function checkLexicalDiversity(bodies, config) {
70
- // R5: default ON — only an explicit opt-out disables the check.
71
- if (config.enabled === false || config.lexicalDiversityCheck === false) {
72
- return { lowDiversity: false };
73
- }
74
- if (bodies.length === 0)
75
- return { lowDiversity: false };
76
- // Average bigram diversity across all bodies in the cluster.
77
- const avg = bodies.reduce((sum, b) => sum + computeBigramDiversity(b), 0) / bodies.length;
78
- const DIVERSITY_FLOOR = 0.3;
79
- if (avg < DIVERSITY_FLOOR) {
80
- return { lowDiversity: true, diversity: avg };
81
- }
82
- return { lowDiversity: false };
83
- }
@@ -1,432 +0,0 @@
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
- /**
5
- * R5 — Longitudinal collapse detector
6
- * (docs/architecture/specs/improve-collapse-churn-detector-design.md).
7
- *
8
- * Detects the measured collapse failure mode of LLM-consolidated memory
9
- * stores: repeated merges destroy information — canary retrieval recall
10
- * downtrends, distinct-content entropy downtrends, or the store shrinks
11
- * while generation counts rise.
12
- *
13
- * The CHURN alert class described in the design doc was removed: production
14
- * never threaded a real accepted-change-volume signal in (the value was a
15
- * hardcoded 0 from the 0.9.0 confidence-gate deletion onward), so the alert
16
- * could never fire. See the design doc's status note for the removal
17
- * rationale. `improve_cycle_metrics.accepted_actions` stays in the schema and
18
- * is written as 0 because deployed `state.db` files already contain it and
19
- * removing it would be a destructive schema change.
20
- *
21
- * Hard invariants: deterministic only (FTS BM25 + hashing — never an LLM,
22
- * never an embedding model); bounded storage (< 2 KB per qualifying cycle,
23
- * 365-day retention); fail-open (an error warns and skips, never breaks an
24
- * improve run); runs only on cycles where consolidate did work.
25
- *
26
- * Observe-only in v1: alerts land in `improve_cycle_metrics.alerts_json`, the
27
- * events log (`collapse_detector_alert`), and the `akm health` advisory —
28
- * nothing is ever blocked.
29
- *
30
- * @module collapse-detector
31
- */
32
- import { randomBytes } from "node:crypto";
33
- import { parseBundleRef } from "../../core/asset/asset-ref.js";
34
- import { conceptIdFromTypeName } from "../../core/asset/resolve-ref.js";
35
- import { getImproveProcessConfig } from "../../core/config/config.js";
36
- import { appendEvent } from "../../core/events.js";
37
- import { withStateDb } from "../../core/state-db.js";
38
- import { warn } from "../../core/warn.js";
39
- import { deactivateCanarySet, getActiveCanaries, getCanariesBySetId, insertCanaries, insertCycleMetrics, listActiveCanarySetIds, queryRecentCycleMetrics, } from "../../storage/repositories/canaries-repository.js";
40
- import { closeDatabase, openExistingDatabase } from "../../storage/repositories/index-connection.js";
41
- import { getAllEntries } from "../../storage/repositories/index-entries-repository.js";
42
- import { searchFts } from "../../storage/repositories/index-fts-repository.js";
43
- import { computeBigramDiversity, DEFAULT_MAX_GENERATION } from "./anti-collapse.js";
44
- import { getAllRankScores } from "./salience.js";
45
- // ── Defaults (mirrored in config-schema.ts ImproveCollapseDetectorSchema) ────
46
- export const DEFAULT_CANARY_COUNT = 40; // owner-approved 30–50 range
47
- export const DEFAULT_CANARY_K = 10;
48
- export const DEFAULT_WINDOW_CYCLES = 5;
49
- export const DEFAULT_RECALL_DROP_THRESHOLD = 0.15;
50
- export const DEFAULT_ENTROPY_DROP_THRESHOLD = 0.05;
51
- export const DEFAULT_RETENTION_DAYS = 365;
52
- /** Deterministic bigram-diversity sample cap (cost bound at 10k assets). */
53
- const DIVERSITY_SAMPLE_CAP = 2000;
54
- /**
55
- * Minimum merge-floor violations in one cycle before the advisory alert fires.
56
- * The specificity floor is deliberately strict (Phase-1 tuning pending), so a
57
- * couple of borderline merges per cycle must not flip `akm health` to warn —
58
- * that alert fatigue would drown the real collapse signals.
59
- */
60
- const MERGE_FLOOR_ALERT_MIN = 3;
61
- /** The learning-store types the detector measures. */
62
- const LEARNING_TYPES = new Set(["memory", "lesson", "knowledge"]);
63
- // ── Canary set ────────────────────────────────────────────────────────────────
64
- /** Deterministic query string for one anchor entry: name tokens + top tags + description head. */
65
- function buildCanaryQuery(entry) {
66
- const nameTokens = entry.entry.name.split(/[-_/.]+/).filter((t) => t.length > 1);
67
- const tags = (entry.entry.tags ?? []).slice(0, 3);
68
- const descriptionHead = (entry.entry.description ?? "").split(/\s+/).slice(0, 6);
69
- const parts = [...nameTokens, ...tags, ...descriptionHead].filter((t) => t.length > 0);
70
- return [...new Set(parts)].join(" ");
71
- }
72
- /** Build the mint candidate list (deterministic given index + salience tables). */
73
- function buildMintList(stateDb, entries, cfg) {
74
- const canaryCount = cfg.canaryCount ?? DEFAULT_CANARY_COUNT;
75
- const rankScores = getAllRankScores(stateDb);
76
- // Chunk-8 WI-8.5c: the canary anchor + salience correlation both key on the
77
- // SHORT conceptId (`<stash-subdir>/<name>`, D-R2) — the same spelling the
78
- // improve candidate refs and content xrefs now carry (post WI-8.5a).
79
- const candidates = entries
80
- .filter((e) => LEARNING_TYPES.has(e.entry.type))
81
- .map((e) => {
82
- const ref = conceptIdFromTypeName(e.entry.type, e.entry.name);
83
- return { e, ref, score: rankScores.get(ref) ?? 0 };
84
- })
85
- .sort((a, b) => b.score - a.score || (a.ref < b.ref ? -1 : 1));
86
- // Type-stratified top slice: ⅓ per learning type, backfill from global order.
87
- const perType = Math.ceil(canaryCount / 3);
88
- const picked = new Map();
89
- for (const type of LEARNING_TYPES) {
90
- let taken = 0;
91
- for (const c of candidates) {
92
- if (taken >= perType || picked.size >= canaryCount)
93
- break;
94
- if (c.e.entry.type === type && !picked.has(c.ref)) {
95
- picked.set(c.ref, c);
96
- taken++;
97
- }
98
- }
99
- }
100
- for (const c of candidates) {
101
- if (picked.size >= canaryCount)
102
- break;
103
- if (!picked.has(c.ref))
104
- picked.set(c.ref, c);
105
- }
106
- return [...picked.values()]
107
- .map((c) => ({ anchorRef: c.ref, query: buildCanaryQuery(c.e) }))
108
- .filter((c) => c.query.length > 0);
109
- }
110
- /** Collision-safe mint token (same-millisecond mints happen in tests + concurrent runs). */
111
- function newCanarySetId() {
112
- return `canary-${Date.now().toString(36)}-${randomBytes(2).toString("hex")}`;
113
- }
114
- /**
115
- * Mint (or return) the active canary set. Deterministic given the index +
116
- * salience tables: rank the three learning types by `asset_salience.rank_score`
117
- * (fallback 0, tie-broken by ref), take a type-stratified top slice
118
- * (⅓ per type, backfilled from the global ranking when a type is short).
119
- *
120
- * Returns `null` when the index has no mintable learning entries — a cycle
121
- * with no canary set is NOT recorded (a fresh unused set id every cycle would
122
- * mean the trend window never fills and recall reads as a fake 0).
123
- *
124
- * NEVER auto-refreshes: once minted the set is frozen until an explicit
125
- * `bun scripts/refresh-canary-set.ts --refresh` — silent re-baselining is how
126
- * a slow collapse hides. Rows are read back BY OUR OWN set id (never "newest
127
- * active") so a concurrent mint in another process cannot relabel this run's
128
- * metrics.
129
- */
130
- export function ensureCanarySet(stateDb, indexDb, cfg, preloadedEntries) {
131
- const existing = getActiveCanaries(stateDb);
132
- if (existing.length > 0) {
133
- return { canarySetId: existing[0].canary_set_id, canaries: existing };
134
- }
135
- const minted = buildMintList(stateDb, preloadedEntries ?? getAllEntries(indexDb), cfg);
136
- if (minted.length === 0)
137
- return null;
138
- const canarySetId = newCanarySetId();
139
- insertCanaries(stateDb, canarySetId, minted);
140
- return { canarySetId, canaries: getCanariesBySetId(stateDb, canarySetId) };
141
- }
142
- /**
143
- * Explicit canary re-mint (the ONLY refresh path —
144
- * `bun scripts/refresh-canary-set.ts --refresh`). Mint-first, deactivate-after:
145
- * when the index is empty or unreadable the current baseline is left untouched
146
- * instead of destroyed. Deactivates ALL other active sets (not just the
147
- * newest) so stragglers from an interrupted refresh can never resurrect.
148
- */
149
- export function refreshCanarySet(stateDb, indexDb, cfg) {
150
- const minted = buildMintList(stateDb, getAllEntries(indexDb), cfg);
151
- if (minted.length === 0)
152
- return null; // nothing mintable — keep the old baseline
153
- const canarySetId = newCanarySetId();
154
- insertCanaries(stateDb, canarySetId, minted);
155
- for (const oldSetId of listActiveCanarySetIds(stateDb)) {
156
- if (oldSetId !== canarySetId)
157
- deactivateCanarySet(stateDb, oldSetId);
158
- }
159
- return { canarySetId, canaries: getCanariesBySetId(stateDb, canarySetId) };
160
- }
161
- // ── Cycle metrics ─────────────────────────────────────────────────────────────
162
- /**
163
- * Name-free content fingerprint text for entropy metrics. The indexed
164
- * search_text EMBEDS the (unique) entry name, which would pin the
165
- * distinct-content ratio at 1.0 forever; convergence shows up in the
166
- * description/tags/heading fields, so those are what get hashed. Bounded body
167
- * content is searchable but deliberately excluded from this fingerprint: the
168
- * detector measures the structured surface generic merged assets converge on.
169
- */
170
- function contentFingerprint(entry) {
171
- const parts = [entry.description ?? "", (entry.tags ?? []).join(" "), (entry.toc ?? []).map((h) => h.text).join(" ")];
172
- return parts.filter((t) => t.length > 0).join(" ");
173
- }
174
- /** FNV-1a 64-bit over lowercased whitespace-collapsed text (distinct-content hashing). */
175
- export function normHash(text) {
176
- const normalized = text.toLowerCase().replace(/\s+/g, " ").trim();
177
- let hash = 0xcbf29ce484222325n;
178
- const prime = 0x100000001b3n;
179
- for (let i = 0; i < normalized.length; i++) {
180
- hash ^= BigInt(normalized.charCodeAt(i));
181
- hash = (hash * prime) & 0xffffffffffffffffn;
182
- }
183
- return hash.toString(16);
184
- }
185
- /**
186
- * Score one canary against the live index, merge-following via canonical
187
- * `xrefs`. A hit is the anchor ref itself or any returned entry whose
188
- * provenance contains the anchor.
189
- * Returns the 0-based rank of the first hit, or -1.
190
- */
191
- function scoreCanary(indexDb, canary, k) {
192
- const results = searchFts(indexDb, canary.query, k);
193
- const anchorConceptId = canaryConceptId(canary.anchor_ref);
194
- for (let i = 0; i < Math.min(results.length, k); i++) {
195
- const r = results[i];
196
- // Persisted canaries may be fully qualified after a state migration while
197
- // index refs and xrefs remain short conceptIds. Compare identity, not display
198
- // spelling, so migration alone cannot collapse recall to zero.
199
- const ref = conceptIdFromTypeName(r.entry.type, r.entry.name);
200
- if (ref === anchorConceptId)
201
- return i;
202
- const provenance = r.entry.xrefs ?? [];
203
- if (provenance.some((sourceRef) => canaryConceptId(sourceRef) === anchorConceptId))
204
- return i;
205
- }
206
- return -1;
207
- }
208
- function canaryConceptId(ref) {
209
- try {
210
- return parseBundleRef(ref).conceptId;
211
- }
212
- catch {
213
- return ref;
214
- }
215
- }
216
- /**
217
- * Compute one qualifying cycle's store-health snapshot. One `entries` scan +
218
- * `canaryCount` FTS queries; no LLM, no embedding model, no filesystem reads.
219
- * Returns `null` when no canary set exists AND none is mintable (empty index)
220
- * — such a cycle is not measurable and must not be recorded.
221
- */
222
- export function computeCycleMetrics(stateDb, indexDb, args) {
223
- const k = args.cfg.k ?? DEFAULT_CANARY_K;
224
- const maxGeneration = args.maxGeneration ?? DEFAULT_MAX_GENERATION;
225
- // Single entries scan — shared by the canary mint (if one is needed) and
226
- // the store-shape metrics below.
227
- const all = getAllEntries(indexDb);
228
- const canarySet = ensureCanarySet(stateDb, indexDb, args.cfg, all);
229
- if (canarySet === null)
230
- return null;
231
- const { canarySetId, canaries } = canarySet;
232
- // ── Canary retrieval metrics ───────────────────────────────────────────────
233
- const ranks = [];
234
- let recallSum = 0;
235
- let ndcgSum = 0;
236
- let mrrSum = 0;
237
- for (const canary of canaries) {
238
- const rank = scoreCanary(indexDb, canary, k);
239
- ranks.push([canary.id, rank]);
240
- if (rank >= 0) {
241
- recallSum += 1;
242
- mrrSum += 1 / (rank + 1);
243
- // Single-relevant nDCG@k closed form: ideal DCG is 1, so the score is
244
- // just the discount at the hit rank.
245
- ndcgSum += 1 / Math.log2(rank + 2);
246
- }
247
- }
248
- const n = Math.max(1, canaries.length);
249
- // ── Store-shape metrics (same single entries scan) ────────────────────────
250
- const byType = new Map();
251
- const contentHashes = new Set();
252
- let learningTotal = 0;
253
- let overGeneration = 0;
254
- const learningTexts = [];
255
- for (const e of all) {
256
- byType.set(e.entry.type, (byType.get(e.entry.type) ?? 0) + 1);
257
- if (!LEARNING_TYPES.has(e.entry.type))
258
- continue;
259
- learningTotal++;
260
- const fingerprint = contentFingerprint(e.entry);
261
- contentHashes.add(normHash(fingerprint));
262
- if ((e.entry.generation ?? 0) > maxGeneration)
263
- overGeneration++;
264
- learningTexts.push({ key: e.itemRef, text: fingerprint });
265
- }
266
- // Deterministic diversity sample: sort by item ref, take every ⌈N/cap⌉-th row.
267
- learningTexts.sort((a, b) => (a.key < b.key ? -1 : 1));
268
- const step = Math.max(1, Math.ceil(learningTexts.length / DIVERSITY_SAMPLE_CAP));
269
- let diversitySum = 0;
270
- let diversityCount = 0;
271
- for (let i = 0; i < learningTexts.length; i += step) {
272
- diversitySum += computeBigramDiversity(learningTexts[i].text);
273
- diversityCount++;
274
- }
275
- return {
276
- run_id: args.runId,
277
- ts: (args.now ?? new Date()).toISOString(),
278
- pass: args.pass,
279
- canary_set_id: canarySetId,
280
- mean_recall: recallSum / n,
281
- mean_ndcg: ndcgSum / n,
282
- mean_mrr: mrrSum / n,
283
- canary_ranks_json: JSON.stringify(ranks),
284
- store_total: learningTotal,
285
- store_by_type_json: JSON.stringify(Object.fromEntries([...byType.entries()].sort())),
286
- distinct_content_ratio: learningTotal === 0 ? 1 : contentHashes.size / learningTotal,
287
- mean_bigram_diversity: diversityCount === 0 ? 1 : diversitySum / diversityCount,
288
- over_generation_count: overGeneration,
289
- // Always 0 — the CHURN alert this fed was removed. The column is NOT NULL
290
- // in a released migration body, so it is written rather than dropped.
291
- accepted_actions: 0,
292
- merge_floor_violations: args.mergeFloorViolations,
293
- alerts_json: "[]",
294
- };
295
- }
296
- // ── Alert evaluation (pure) ───────────────────────────────────────────────────
297
- function median(values) {
298
- if (values.length === 0)
299
- return Number.NaN;
300
- const sorted = [...values].sort((a, b) => a - b);
301
- const mid = Math.floor(sorted.length / 2);
302
- return sorted.length % 2 === 0 ? (sorted[mid - 1] + sorted[mid]) / 2 : sorted[mid];
303
- }
304
- /**
305
- * Evaluate the §1 alert definitions. PURE — history rows (oldest-first, NOT
306
- * including `current`) plus the current row in, alerts out. A window shorter
307
- * than `windowCycles` never fires (no baseline yet); the merge-floor advisory
308
- * is per-cycle and fires regardless of window depth.
309
- */
310
- export function evaluateCollapseAlerts(history, current, cfg) {
311
- const alerts = [];
312
- // MERGE-FLOOR advisory: per-cycle, window-independent. Gated on a minimum
313
- // count — the specificity floor is deliberately strict pre-tuning, and one
314
- // or two borderline merges per cycle must not generate alert fatigue.
315
- if (current.merge_floor_violations >= MERGE_FLOOR_ALERT_MIN) {
316
- alerts.push({
317
- kind: "merge-floor",
318
- detail: `${current.merge_floor_violations} merge(s) failed the information floor this cycle (provenance shrank or specificity below threshold)`,
319
- metrics: { mergeFloorViolations: current.merge_floor_violations },
320
- });
321
- }
322
- const W = cfg.windowCycles ?? DEFAULT_WINDOW_CYCLES;
323
- const hist = history.slice(-W);
324
- if (hist.length < W)
325
- return alerts; // no baseline yet
326
- // Window is non-empty here: `windowCycles` is schema-bounded to ≥2 (default 5),
327
- // so `hist.length >= W >= 2`. `windowStart` is the oldest row in the window.
328
- const windowStart = hist[0];
329
- const recallDrop = cfg.recallDropThreshold ?? DEFAULT_RECALL_DROP_THRESHOLD;
330
- const entropyDrop = cfg.entropyDropThreshold ?? DEFAULT_ENTROPY_DROP_THRESHOLD;
331
- // COLLAPSE 1 — canary recall drop vs window median (median, not previous
332
- // cycle, so one noisy cycle can neither fire nor mask the alert).
333
- const medianRecall = median(hist.map((h) => h.mean_recall));
334
- if (current.mean_recall <= medianRecall - recallDrop) {
335
- alerts.push({
336
- kind: "collapse-recall",
337
- detail: `mean canary recall ${current.mean_recall.toFixed(3)} dropped ≥${recallDrop} below the ${W}-cycle median ${medianRecall.toFixed(3)}`,
338
- metrics: { currentRecall: current.mean_recall, medianRecall, threshold: recallDrop },
339
- });
340
- }
341
- // COLLAPSE 2 — monotonic distinct-content-ratio decline over the window.
342
- const series = [...hist.map((h) => h.distinct_content_ratio), current.distinct_content_ratio];
343
- const monotonicNonIncreasing = series.every((v, i) => i === 0 || v <= series[i - 1]);
344
- const totalDecline = windowStart.distinct_content_ratio - current.distinct_content_ratio;
345
- if (monotonicNonIncreasing && totalDecline >= entropyDrop) {
346
- alerts.push({
347
- kind: "collapse-entropy",
348
- detail: `distinct-content ratio declined monotonically by ${totalDecline.toFixed(3)} (≥${entropyDrop}) over ${W} cycles — store content is converging`,
349
- metrics: {
350
- windowStart: windowStart.distinct_content_ratio,
351
- current: current.distinct_content_ratio,
352
- decline: totalDecline,
353
- },
354
- });
355
- }
356
- // COLLAPSE 3 — store shrinking BECAUSE of re-merging (not deletion hygiene).
357
- const maxStore = Math.max(...hist.map((h) => h.store_total));
358
- if (current.store_total < 0.8 * maxStore && current.over_generation_count > windowStart.over_generation_count) {
359
- alerts.push({
360
- kind: "collapse-shrink",
361
- detail: `store shrank >20% (${current.store_total} vs window max ${maxStore}) while over-generation count rose (${windowStart.over_generation_count} → ${current.over_generation_count})`,
362
- metrics: {
363
- storeTotal: current.store_total,
364
- windowMax: maxStore,
365
- overGeneration: current.over_generation_count,
366
- },
367
- });
368
- }
369
- return alerts;
370
- }
371
- // ── Orchestrator ─────────────────────────────────────────────────────────────
372
- /**
373
- * Run the detector for one qualifying cycle: ensure canaries → compute →
374
- * evaluate against stored history → persist the row → append one
375
- * `collapse_detector_alert` event per fired alert. FAIL-OPEN: any error warns
376
- * and returns undefined — an improve run is never broken by its own
377
- * instrumentation.
378
- */
379
- export function runCollapseDetector(args) {
380
- const cfg = args.config.improve?.collapseDetector ?? {};
381
- if (cfg.enabled === false)
382
- return undefined;
383
- try {
384
- let indexDb;
385
- try {
386
- indexDb = openExistingDatabase(args.indexDbPath);
387
- const db = indexDb;
388
- // Over-generation threshold mirrors the guard actually in effect —
389
- // reading the same config key keeps the two aligned when tuned.
390
- const antiCollapse = getImproveProcessConfig("consolidate", args.improveProfile)?.antiCollapse;
391
- const maxGeneration = antiCollapse?.maxGeneration ?? DEFAULT_MAX_GENERATION;
392
- return withStateDb((stateDb) => {
393
- const row = computeCycleMetrics(stateDb, db, {
394
- runId: args.runId,
395
- pass: args.pass,
396
- mergeFloorViolations: args.mergeFloorViolations,
397
- cfg,
398
- maxGeneration,
399
- });
400
- if (row === null)
401
- return undefined; // empty index — nothing to measure
402
- const windowCycles = cfg.windowCycles ?? DEFAULT_WINDOW_CYCLES;
403
- const history = queryRecentCycleMetrics(stateDb, row.canary_set_id, windowCycles);
404
- const alerts = evaluateCollapseAlerts(history, row, cfg);
405
- row.alerts_json = JSON.stringify(alerts.map((a) => a.kind));
406
- insertCycleMetrics(stateDb, row);
407
- for (const alert of alerts) {
408
- appendEvent({
409
- eventType: "collapse_detector_alert",
410
- ref: undefined,
411
- metadata: {
412
- kind: alert.kind,
413
- detail: alert.detail,
414
- metrics: alert.metrics,
415
- canarySetId: row.canary_set_id,
416
- runId: args.runId,
417
- },
418
- }, args.eventsCtx);
419
- }
420
- return row;
421
- }, { path: args.eventsCtx?.dbPath, borrowed: args.eventsCtx?.db });
422
- }
423
- finally {
424
- if (indexDb)
425
- closeDatabase(indexDb);
426
- }
427
- }
428
- catch (err) {
429
- warn(`[collapse-detector] skipped (fail-open): ${err instanceof Error ? err.message : String(err)}`);
430
- return undefined;
431
- }
432
- }
@@ -1,48 +0,0 @@
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
- // Eligibility / safety predicates for consolidate: "may we touch this memory?"
5
- // One reason to change — the policy for what consolidate is allowed to act on.
6
- import fs from "node:fs";
7
- import { parseFrontmatter } from "../../../core/asset/frontmatter.js";
8
- import { hasHotCaptureMode } from "../../proposal/validators/proposal-quality-validators.js";
9
- export function isConsolidationEligibleMemoryName(name) {
10
- return !name.endsWith(".derived");
11
- }
12
- /**
13
- * Returns true when the memory file has `captureMode: hot` in its frontmatter.
14
- *
15
- * Hot memories are USER-EXPLICIT (written via `akm remember` on the hot path).
16
- * The consolidate LLM is forbidden from deleting or auto-merging them — the
17
- * user wrote them on purpose and only the user can decide to retire them.
18
- *
19
- * Reads the file once per check; consolidate runs against ~10 memories per
20
- * chunk so the IO cost is trivial. Returns false on any read/parse error
21
- * (fail-safe: an unreadable or unparseable file is treated as HOT — protected
22
- * — because a deletion shield must not fail open; a missing file is not-hot).
23
- *
24
- * Defends against four observed defect classes (see
25
- * `memories/akm-improve-critical-review-2026-05-20`):
26
- * - LLM marks a memory contradicted then deletes (dangling contradictedBy)
27
- * - LLM merges two unrelated memories sharing a topic keyword
28
- * - LLM judges a recent durable design memo as "redundant"
29
- * - Cascade deletes (LLM uses ref:X as `contradictedBy` for ref:Y then deletes both)
30
- */
31
- export function isHotCapturedMemory(filePath) {
32
- if (!fs.existsSync(filePath))
33
- return false;
34
- try {
35
- const content = fs.readFileSync(filePath, "utf8");
36
- const parsed = parseFrontmatter(content);
37
- return hasHotCaptureMode(parsed.data);
38
- }
39
- catch {
40
- // Fail CLOSED. This predicate is a deletion shield: "hot" memories are
41
- // protected from consolidate's merge/delete. Returning false on a read or
42
- // parse failure marked exactly the memories we could not inspect as fair
43
- // game — the one direction a protection check must never fail. A missing
44
- // file stays false (nothing to protect); an unreadable one is protected
45
- // until someone can actually read it.
46
- return true;
47
- }
48
- }