akm-cli 0.9.30 → 0.10.261001-beta

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 (384) hide show
  1. package/CHANGELOG.md +438 -0
  2. package/README.md +10 -10
  3. package/ROADMAP.md +112 -0
  4. package/STABILITY.md +99 -57
  5. package/dist/api-node.mjs +20 -0
  6. package/dist/api.d.ts +23 -0
  7. package/dist/api.js +45 -0
  8. package/dist/assets/hints/cli-hints-full.md +15 -29
  9. package/dist/assets/hints/cli-hints-short.md +2 -2
  10. package/dist/assets/improve-strategies/default.json +3 -3
  11. package/dist/assets/prompts/consolidate-pair.md +9 -7
  12. package/dist/assets/stash-skeleton/README.md +1 -1
  13. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
  14. package/dist/cli/shared.js +31 -48
  15. package/dist/cli/unknown-flags.js +6 -43
  16. package/dist/cli.js +8 -34
  17. package/dist/commands/agent/contribute-cli.js +1 -1
  18. package/dist/commands/completions.js +1 -2
  19. package/dist/commands/config-cli.js +5 -28
  20. package/dist/commands/env/env-cli.js +5 -5
  21. package/dist/commands/env/secret-cli.js +2 -2
  22. package/dist/commands/feedback-cli.js +75 -49
  23. package/dist/commands/health/checks.js +41 -16
  24. package/dist/commands/health/plugin-staleness.js +52 -22
  25. package/dist/commands/improve/autonomy-gate.js +5 -3
  26. package/dist/commands/improve/consolidate/chunking.js +3 -10
  27. package/dist/commands/improve/consolidate/continuity-check.js +4 -4
  28. package/dist/commands/improve/consolidate/pair-pass.js +29 -10
  29. package/dist/commands/improve/consolidate.js +10 -65
  30. package/dist/commands/improve/distill/lesson-update.js +91 -0
  31. package/dist/commands/improve/distill-guards.js +1 -43
  32. package/dist/commands/improve/distill.js +148 -21
  33. package/dist/commands/improve/eligibility.js +14 -1
  34. package/dist/commands/improve/extract-cli.js +1 -1
  35. package/dist/commands/improve/extract-prompt.js +17 -7
  36. package/dist/commands/improve/extract.js +5 -7
  37. package/dist/commands/improve/improve-cli.js +125 -118
  38. package/dist/commands/improve/improve-strategies.js +8 -12
  39. package/dist/commands/improve/improve.js +134 -52
  40. package/dist/commands/improve/loop-stages.js +18 -5
  41. package/dist/commands/improve/planner.js +0 -1
  42. package/dist/commands/improve/preparation.js +40 -99
  43. package/dist/commands/improve/proactive-maintenance.js +2 -2
  44. package/dist/commands/improve/reflect-noise.js +3 -55
  45. package/dist/commands/improve/reflect.js +29 -34
  46. package/dist/commands/improve/retrieval-queries.js +37 -0
  47. package/dist/commands/improve/retrieval-scope.js +2 -2
  48. package/dist/commands/improve/salience.js +2 -9
  49. package/dist/commands/improve/session-asset.js +4 -1
  50. package/dist/commands/improve/stage.js +23 -12
  51. package/dist/commands/lint/index.js +5 -20
  52. package/dist/commands/observability-cli.js +3 -9
  53. package/dist/commands/proposal/drain.js +43 -6
  54. package/dist/commands/proposal/proposal-cli.js +11 -31
  55. package/dist/commands/proposal/repository.js +20 -14
  56. package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -3
  57. package/dist/commands/read/knowledge.js +3 -3
  58. package/dist/commands/read/remember-cli.js +0 -13
  59. package/dist/commands/read/search-cli.js +29 -97
  60. package/dist/commands/read/search.js +0 -11
  61. package/dist/commands/read/show.js +0 -19
  62. package/dist/commands/sources/plugin-upgrade.js +164 -101
  63. package/dist/commands/sources/sources-cli.js +1 -15
  64. package/dist/commands/sources/stash-cli.js +6 -14
  65. package/dist/commands/tasks/tasks-cli.js +4 -115
  66. package/dist/commands/workflow/plan.js +11 -33
  67. package/dist/commands/workflow-cli.js +2 -7
  68. package/dist/core/adapter/adapter-ids.js +1 -2
  69. package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
  70. package/dist/core/adapter/adapters/akm-adapter.js +9 -48
  71. package/dist/core/adapter/adapters/akm-lint.js +10 -26
  72. package/dist/core/adapter/adapters/akm-metadata.js +2 -72
  73. package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
  74. package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
  75. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
  76. package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
  77. package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
  78. package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
  79. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
  80. package/dist/core/adapter/recognize-match.js +3 -4
  81. package/dist/core/common.js +18 -2
  82. package/dist/core/config/config-schema.js +12 -15
  83. package/dist/core/config/config-types.js +6 -15
  84. package/dist/core/config/config-walker.js +6 -23
  85. package/dist/core/config/config.js +17 -80
  86. package/dist/core/config/engine-semantics.js +7 -3
  87. package/dist/core/config/schema/engines.js +7 -7
  88. package/dist/core/config/schema/improve-processes.js +10 -63
  89. package/dist/core/config/schema/improve.js +2 -21
  90. package/dist/core/config/schema/index-config.js +6 -39
  91. package/dist/core/env-secret-ref.js +2 -45
  92. package/dist/core/errors.js +1 -2
  93. package/dist/core/extra-params.js +2 -86
  94. package/dist/core/file-lock.js +12 -7
  95. package/dist/core/improve-result.js +13 -1
  96. package/dist/core/mutation-target.js +2 -2
  97. package/dist/core/paths.js +4 -0
  98. package/dist/core/recognition-util.js +4 -5
  99. package/dist/core/run-lock.js +1 -1
  100. package/dist/core/subprocess.js +49 -0
  101. package/dist/core/warn.js +21 -4
  102. package/dist/core/write-source.js +3 -3
  103. package/dist/execution/target-ref.js +1 -2
  104. package/dist/indexer/indexer.js +15 -6
  105. package/dist/indexer/passes/memory-inference.js +15 -6
  106. package/dist/indexer/search/db-search.js +49 -36
  107. package/dist/indexer/search/fts-query.js +0 -19
  108. package/dist/indexer/search/ranking.js +29 -1
  109. package/dist/indexer/usage/usage-events.js +21 -16
  110. package/dist/integrations/agent/engine-resolution.js +5 -2
  111. package/dist/integrations/agent/index.js +1 -1
  112. package/dist/integrations/agent/profiles.js +41 -98
  113. package/dist/integrations/agent/request-lowering.js +3 -3
  114. package/dist/integrations/harnesses/aider/index.js +5 -0
  115. package/dist/integrations/harnesses/amazonq/index.js +1 -0
  116. package/dist/integrations/harnesses/claude/config-import.js +2 -3
  117. package/dist/integrations/harnesses/claude/index.js +5 -0
  118. package/dist/integrations/harnesses/codex/index.js +1 -0
  119. package/dist/integrations/harnesses/copilot/index.js +1 -0
  120. package/dist/integrations/harnesses/gemini/index.js +1 -0
  121. package/dist/integrations/harnesses/index.js +18 -39
  122. package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
  123. package/dist/integrations/harnesses/opencode/config-import.js +121 -52
  124. package/dist/integrations/harnesses/opencode/index.js +22 -0
  125. package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
  126. package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
  127. package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
  128. package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
  129. package/dist/integrations/harnesses/opencode/session-log.js +104 -115
  130. package/dist/integrations/harnesses/opencode/version.js +98 -0
  131. package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
  132. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
  133. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
  134. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +254 -0
  135. package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
  136. package/dist/integrations/harnesses/openhands/index.js +1 -0
  137. package/dist/integrations/harnesses/pi/index.js +1 -0
  138. package/dist/integrations/harnesses/shared.js +2 -0
  139. package/dist/integrations/harnesses/types.js +2 -0
  140. package/dist/llm/client.js +74 -14
  141. package/dist/output/cli-hints.js +1 -1
  142. package/dist/output/context.js +10 -19
  143. package/dist/output/format-exempt.js +5 -8
  144. package/dist/output/shapes/helpers.js +1 -22
  145. package/dist/output/shapes/passthrough.js +0 -1
  146. package/dist/output/shapes.js +3 -31
  147. package/dist/output/stdout.js +5 -1
  148. package/dist/output/text/command-format.js +6 -1
  149. package/dist/output/text/workflow-format.js +4 -22
  150. package/dist/scripts/akm-migrate-node.js +15769 -16731
  151. package/dist/scripts/akm-migrate.js +16339 -17297
  152. package/dist/setup/detect.js +10 -4
  153. package/dist/setup/harness-config-import.js +5 -14
  154. package/dist/setup/setup.js +1 -1
  155. package/dist/setup/steps/tasks.js +12 -13
  156. package/dist/sources/providers/git-stash.js +94 -13
  157. package/dist/storage/repositories/index-utility-repository.js +2 -32
  158. package/dist/storage/repositories/index-vec-repository.js +4 -0
  159. package/dist/storage/repositories/task-history-repository.js +0 -5
  160. package/dist/storage/repositories/workflow-runs-repository.js +5 -15
  161. package/dist/tasks/embedded.js +2 -1
  162. package/dist/tasks/run/run-command-task.js +9 -1
  163. package/dist/tasks/run/run-workflow-task.js +4 -3
  164. package/dist/tasks/run/task-history.js +0 -2
  165. package/dist/tasks/run/task-log.js +38 -1
  166. package/dist/tasks/schedule-gates.js +39 -0
  167. package/dist/tasks/scheduler-binding.js +0 -16
  168. package/dist/tasks/scheduler-sync.js +3 -97
  169. package/dist/tasks/source/bounded-document.js +1 -1
  170. package/dist/tasks/source/task-source-v4.js +2 -12
  171. package/dist/tasks/task-run-reserved-flags.js +2 -31
  172. package/dist/workflows/authoring/authoring.js +6 -6
  173. package/dist/workflows/compile.js +8 -65
  174. package/dist/workflows/exec/child-workflow.js +22 -3
  175. package/dist/workflows/exec/native-executor.js +12 -147
  176. package/dist/workflows/exec/run-workflow.js +31 -103
  177. package/dist/workflows/exec/step-work.js +9 -178
  178. package/dist/workflows/exec/unit-dispatch.js +0 -10
  179. package/dist/workflows/freeze/environment.js +3 -44
  180. package/dist/workflows/freeze/freeze.js +8 -41
  181. package/dist/workflows/freeze/targets/child-workflow.js +27 -63
  182. package/dist/workflows/freeze/targets/command.js +2 -16
  183. package/dist/workflows/freeze/targets/shell.js +0 -2
  184. package/dist/workflows/freeze/task-bindings.js +2 -28
  185. package/dist/workflows/ir/params.js +12 -2
  186. package/dist/workflows/parser.js +82 -232
  187. package/dist/workflows/plan.js +1 -1
  188. package/dist/workflows/renderer.js +10 -19
  189. package/dist/workflows/runtime/run-plan.js +11 -53
  190. package/dist/workflows/runtime/runs.js +4 -27
  191. package/dist/workflows/source-files.js +12 -16
  192. package/dist/workflows/source-semantics.js +1 -93
  193. package/docs/README.md +43 -42
  194. package/docs/agents/README.md +15 -0
  195. package/docs/agents/agent-install.md +14 -0
  196. package/docs/architecture/0.10-process-rules.md +827 -0
  197. package/docs/architecture/README.md +67 -0
  198. package/docs/architecture/adapters.md +258 -0
  199. package/docs/architecture/akm-architecture-decision-history.md +2055 -0
  200. package/docs/architecture/akm-core-principles.md +126 -0
  201. package/docs/architecture/architecture.md +578 -0
  202. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
  203. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
  204. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
  205. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
  206. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
  207. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
  208. package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
  209. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
  210. package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
  211. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
  212. package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
  213. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
  214. package/docs/architecture/decisions/README.md +62 -0
  215. package/docs/architecture/improvement.md +479 -0
  216. package/docs/architecture/internals/classification.md +154 -0
  217. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
  218. package/docs/architecture/internals/functional-contract-patterns.md +340 -0
  219. package/docs/architecture/internals/health-advisories.md +110 -0
  220. package/docs/architecture/internals/improve-workflow.md +719 -0
  221. package/docs/architecture/internals/indexing.md +498 -0
  222. package/docs/architecture/internals/registry-network-boundary.md +57 -0
  223. package/docs/architecture/internals/search.md +248 -0
  224. package/docs/architecture/internals/storage-locations.md +943 -0
  225. package/docs/architecture/persisted-data-compat.md +68 -0
  226. package/docs/architecture/reviews/env-secret-access.md +319 -0
  227. package/docs/architecture/runtime-boundary-design.md +153 -0
  228. package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
  229. package/docs/architecture/specs/0.9.0-decisions.md +532 -0
  230. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
  231. package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
  232. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
  233. package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
  234. package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
  235. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
  236. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
  237. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
  238. package/docs/architecture/specs/di-seams-plan.md +539 -0
  239. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
  240. package/docs/architecture/specs/fact-asset-type.md +142 -0
  241. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
  242. package/docs/architecture/specs/okf-support.md +148 -0
  243. package/docs/architecture/specs/ref.md +329 -0
  244. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
  245. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
  246. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
  247. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
  248. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
  249. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
  250. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
  251. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
  252. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
  253. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
  254. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
  255. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
  256. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
  257. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
  258. package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
  259. package/docs/architecture/specs/stash-organization-conventions.md +377 -0
  260. package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
  261. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
  262. package/docs/architecture/specs/workflow-format-unification.md +432 -0
  263. package/docs/architecture/testing/manual-testing-checklist.md +3411 -0
  264. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
  265. package/docs/architecture/testing/task-workflow-manual-tests.md +1720 -0
  266. package/docs/architecture/testing/testing-workflow.md +446 -0
  267. package/docs/architecture/workflow-engine.md +223 -0
  268. package/docs/guides/README.md +33 -0
  269. package/docs/guides/author-bundles.md +310 -0
  270. package/docs/guides/author-workflows.md +490 -0
  271. package/docs/guides/bundles.md +323 -0
  272. package/docs/guides/capture-knowledge.md +136 -0
  273. package/docs/guides/claude-code-vs-akm-workflows.md +29 -0
  274. package/docs/guides/concepts.md +93 -0
  275. package/docs/guides/discover-and-load.md +158 -0
  276. package/docs/guides/getting-started.md +240 -0
  277. package/docs/guides/improve-the-library.md +247 -0
  278. package/docs/guides/knowledge-management.md +18 -0
  279. package/docs/guides/recipes/headless-install.md +97 -0
  280. package/docs/guides/recipes/website-source.md +28 -0
  281. package/docs/guides/run-workflows.md +279 -0
  282. package/docs/guides/scheduling.md +131 -0
  283. package/docs/guides/use-with-any-agent.md +174 -0
  284. package/docs/guides/wikis.md +81 -0
  285. package/docs/integration/bundling-akm.md +7 -6
  286. package/docs/maintainers/README.md +19 -0
  287. package/docs/maintainers/curate-workmap.md +335 -0
  288. package/docs/maintainers/eval.md +3 -0
  289. package/docs/maintainers/local-development.md +108 -0
  290. package/docs/maintainers/release-checklist.md +135 -0
  291. package/docs/migration/README.md +4 -2
  292. package/docs/migration/release-notes/0.0.13.md +1 -1
  293. package/docs/migration/release-notes/0.1.0.md +1 -1
  294. package/docs/migration/release-notes/0.10.0.md +201 -0
  295. package/docs/migration/release-notes/0.2.0.md +1 -1
  296. package/docs/migration/release-notes/0.3.0.md +1 -1
  297. package/docs/migration/release-notes/0.5.0.md +1 -1
  298. package/docs/migration/release-notes/0.6.0.md +1 -1
  299. package/docs/migration/release-notes/0.7.0.md +1 -1
  300. package/docs/migration/release-notes/0.7.3.md +2 -2
  301. package/docs/migration/release-notes/0.7.4.md +1 -1
  302. package/docs/migration/release-notes/0.7.5.md +1 -1
  303. package/docs/migration/release-notes/0.8.0.md +1 -1
  304. package/docs/migration/release-notes/0.9.0.md +1 -1
  305. package/docs/migration/release-notes/0.9.14.md +1 -1
  306. package/docs/migration/release-notes/0.9.15.md +1 -1
  307. package/docs/migration/release-notes/0.9.16.md +1 -1
  308. package/docs/migration/release-notes/0.9.17.md +1 -1
  309. package/docs/migration/release-notes/0.9.19.md +1 -1
  310. package/docs/migration/release-notes/0.9.2.md +1 -1
  311. package/docs/migration/release-notes/README.md +28 -31
  312. package/docs/migration/v0.5-to-v0.6.md +759 -0
  313. package/docs/migration/v0.8-to-v0.9.md +1 -1
  314. package/docs/migration/v0.9-to-v0.10.md +507 -0
  315. package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
  316. package/docs/posts/README.md +30 -0
  317. package/docs/posts/akm-command-tour.md +495 -0
  318. package/docs/posts/akm-logo-sized.webp +0 -0
  319. package/docs/posts/belief-aware-memory-12.md +139 -0
  320. package/docs/posts/config-v2-lessons-16.md +191 -0
  321. package/docs/posts/improve-loop-autonomous-10.md +225 -0
  322. package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
  323. package/docs/posts/intro-part-01.md +76 -0
  324. package/docs/posts/intro-part-02.md +221 -0
  325. package/docs/posts/intro-part-03.md +190 -0
  326. package/docs/posts/intro-part-04.md +221 -0
  327. package/docs/posts/llm-mode-reflect-13.md +166 -0
  328. package/docs/posts/openpalm-built-on-akm.md +86 -0
  329. package/docs/posts/progressive-disclosure-06.md +129 -0
  330. package/docs/posts/proposal-safety-net-15.md +112 -0
  331. package/docs/posts/release-0.5.0.md +247 -0
  332. package/docs/posts/release-0.6.0.md +117 -0
  333. package/docs/posts/release-0.7.0.md +159 -0
  334. package/docs/posts/release-0.8.0.md +332 -0
  335. package/docs/posts/scattered-skills-05.md +100 -0
  336. package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
  337. package/docs/posts/team-skills-07.md +198 -0
  338. package/docs/posts/wiki-agent-knowledge-08.md +223 -0
  339. package/docs/posts/workflows-vaults-09.md +321 -0
  340. package/docs/product-surface.md +54 -0
  341. package/docs/reference/README.md +2 -2
  342. package/docs/reference/api.md +65 -0
  343. package/docs/reference/asset-types.md +166 -0
  344. package/docs/reference/bundle-types.md +2 -4
  345. package/docs/reference/cli.md +163 -192
  346. package/docs/reference/configuration.md +101 -20
  347. package/docs/reference/data-and-telemetry.md +2 -2
  348. package/docs/reference/env-and-secrets.md +126 -0
  349. package/docs/reference/memory.md +124 -0
  350. package/docs/reference/refs.md +156 -0
  351. package/docs/reference/registry.md +489 -0
  352. package/docs/reference/supported-formats.md +3 -4
  353. package/docs/reference/tasks.md +12 -19
  354. package/docs/reference/website-sources.md +141 -0
  355. package/docs/reference/workflow-schema.md +89 -317
  356. package/docs/reference/workflows.md +20 -48
  357. package/package.json +23 -16
  358. package/schemas/akm-config.json +2 -243
  359. package/schemas/akm-task.json +2 -3
  360. package/schemas/akm-workflow.json +47 -84
  361. package/dist/assets/improve-strategies/catchup.json +0 -41
  362. package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
  363. package/dist/assets/improve-strategies/quick.json +0 -17
  364. package/dist/assets/improve-strategies/reflect-distill.json +0 -29
  365. package/dist/assets/improve-strategies/thorough.json +0 -49
  366. package/dist/assets/prompts/retrieval-relevance-judge.md +0 -6
  367. package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
  368. package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
  369. package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
  370. package/dist/cli/retired-commands.js +0 -118
  371. package/dist/commands/improve/retrieval-gate.js +0 -132
  372. package/dist/commands/tasks/validate.js +0 -176
  373. package/dist/core/config/legacy-source-shape-shim.js +0 -117
  374. package/dist/indexer/feedback/utility-policy.js +0 -75
  375. package/dist/integrations/harnesses/ids.js +0 -28
  376. package/dist/tasks/source/task-source-v3-frozen.js +0 -424
  377. package/dist/tasks/source/task-to-v4.js +0 -863
  378. package/dist/workflows/exec/worktree.js +0 -372
  379. package/dist/workflows/freeze/child-output-references.js +0 -83
  380. package/dist/workflows/freeze/identity.js +0 -22
  381. package/dist/workflows/freeze/targets/script.js +0 -50
  382. package/dist/workflows/freeze/targets/task.js +0 -124
  383. package/dist/workflows/github-yaml.js +0 -506
  384. package/dist/workflows/runtime/run-outputs.js +0 -80
@@ -28,7 +28,7 @@ import { detectAdapterId } from "../../core/adapter/detect-adapter.js";
28
28
  import { assembleAssetFromString, serializeFrontmatter } from "../../core/asset/asset-serialize.js";
29
29
  import { parseFrontmatter } from "../../core/asset/frontmatter.js";
30
30
  import { conceptIdFromTypeName, displayRef, parseRefInput } from "../../core/asset/resolve-ref.js";
31
- import { getImproveProcessConfig, loadConfig } from "../../core/config/config.js";
31
+ import { loadConfig } from "../../core/config/config.js";
32
32
  import { parseEmbeddedJsonResponse } from "../../core/parse.js";
33
33
  import { openStateDatabase } from "../../core/state-db.js";
34
34
  import { warn, warnVerbose } from "../../core/warn.js";
@@ -43,7 +43,7 @@ import { getBodyEmbeddings, upsertBodyEmbeddings } from "../../storage/repositor
43
43
  import { closeDatabase, openExistingDatabase, openReadonlyExistingDatabase, } from "../../storage/repositories/index-connection.js";
44
44
  import { getAllEntries } from "../../storage/repositories/index-entries-repository.js";
45
45
  import { listProposals, listProposalsReadOnly, proposalContent } from "../proposal/repository.js";
46
- import { hasHotCaptureMode, hasSupersededStatus, validateProposalFrontmatter, } from "../proposal/validators/proposal-quality-validators.js";
46
+ import { hasSupersededStatus, validateProposalFrontmatter } from "../proposal/validators/proposal-quality-validators.js";
47
47
  import { buildChunkPrompt, computeSafeChunkSize, DEFAULT_CONTEXT_LENGTH_TOKENS } from "./consolidate/chunking.js";
48
48
  import { openKnowledgeCoverage } from "./consolidate/coverage.js";
49
49
  import { runConsolidatePairPass } from "./consolidate/pair-pass.js";
@@ -75,21 +75,6 @@ export function mergePlans(chunks) {
75
75
  export function isConsolidationEligibleMemoryName(name) {
76
76
  return !name.endsWith(".derived");
77
77
  }
78
- /**
79
- * A `captureMode: hot` memory (written deliberately with `akm remember`). A
80
- * missing file is not hot; an unreadable one is treated as hot — the check is
81
- * a protection and must not fail open.
82
- */
83
- export function isHotCapturedMemory(filePath) {
84
- if (!fs.existsSync(filePath))
85
- return false;
86
- try {
87
- return hasHotCaptureMode(parseFrontmatter(fs.readFileSync(filePath, "utf8")).data);
88
- }
89
- catch {
90
- return true;
91
- }
92
- }
93
78
  /**
94
79
  * Structured-output schema for a plan. Promote-only: merge/delete/contradict
95
80
  * were removed in 0.9.17-alpha.1 (`e82eec811`) after running in production —
@@ -210,36 +195,6 @@ async function clusterMemoriesBySimilarity(memories, config, stateDb, signal) {
210
195
  }
211
196
  return { ordered, embedTelemetry: telemetry };
212
197
  }
213
- /**
214
- * Anti-collapse (default on, `antiCollapse.enabled: false` opts out): a small
215
- * deterministic sample of the pool is spread through the similarity order so
216
- * consolidation is not purely similarity-driven.
217
- */
218
- function injectRandomClusterMembers(memories, profile, warnings) {
219
- const config = getImproveProcessConfig("consolidate", profile)?.antiCollapse ?? {};
220
- if (config.enabled === false || memories.length <= 2)
221
- return memories;
222
- const fraction = config.randomClusterFraction ?? 0.05;
223
- const randomCount = Math.max(1, Math.floor(memories.length * fraction));
224
- const sample = [...memories]
225
- .sort((a, b) => contentHash(a.name).localeCompare(contentHash(b.name)))
226
- .slice(0, randomCount);
227
- const sampled = new Set(sample.map((m) => m.name));
228
- const interval = Math.max(2, Math.floor(memories.length / randomCount));
229
- const out = [];
230
- let next = 0;
231
- for (let i = 0; i < memories.length; i++) {
232
- const m = memories[i];
233
- if (m && !sampled.has(m.name))
234
- out.push(m);
235
- if (i > 0 && i % interval === 0 && next < sample.length)
236
- out.push(sample[next++]);
237
- }
238
- while (next < sample.length)
239
- out.push(sample[next++]);
240
- warnings.push(`Anti-collapse: injected ${randomCount} random (non-similarity-driven) cluster member(s) into consolidation pool (fraction=${fraction}).`);
241
- return out;
242
- }
243
198
  /** Body hashes of pending consolidate proposals, so the prompt can mark memories already queued. */
244
199
  function loadPendingConsolidateProposalHashes(stashDir, proposalsCtx) {
245
200
  const hashes = new Set();
@@ -561,9 +516,8 @@ const ABORT_MIN_CHUNKS = 4;
561
516
  const ABORT_FAILURE_RATE = 0.5;
562
517
  /**
563
518
  * The chunk loop: stop cleanly on the budget signal, abort once ≥50% of at
564
- * least 4 chunks failed (the model is likely down), skip an all-hot chunk
565
- * without a call (the only thing the model could do with it is refused), and
566
- * count every memory into exactly one accounting bucket.
519
+ * least 4 chunks failed (the model is likely down), and count every memory
520
+ * into exactly one accounting bucket.
567
521
  */
568
522
  async function judgeConsolidationChunks(args) {
569
523
  const { chunks, opts, config, warnings, acc } = args;
@@ -601,15 +555,6 @@ async function judgeConsolidationChunks(args) {
601
555
  }
602
556
  }
603
557
  const chunk = chunks[chunkIdx];
604
- if (chunk.length > 0 && chunk.every((m) => isHotCapturedMemory(m.filePath))) {
605
- for (const m of chunk) {
606
- acc.judgedNoActionRefs.add(memRef(m));
607
- acc.judgedRefs.add(memRef(m));
608
- }
609
- acc.judgedNoAction += chunk.length;
610
- warn(`[consolidate] chunk ${chunkIdx + 1}/${chunks.length}: all ${chunk.length} memories are captureMode: hot — skipping LLM (judged no-action).`);
611
- continue;
612
- }
613
558
  warn(`[consolidate] chunk ${chunkIdx + 1}/${chunks.length} (${chunk.length} memories) …`);
614
559
  processed++;
615
560
  if (!llmRunner) {
@@ -670,11 +615,12 @@ async function judgeConsolidationChunks(args) {
670
615
  }
671
616
  return planned;
672
617
  }
618
+ /** Assumed wall-clock seconds per consolidation chunk for the up-front budget cap. */
619
+ const CHUNK_SECONDS_ESTIMATE = 30;
673
620
  /**
674
621
  * The model's plan for the narrowed pool: chunk size from the context window,
675
622
  * an up-front cap when the remaining budget cannot cover every chunk (oldest
676
- * first, the rest deferred), similarity clustering, anti-collapse, then the
677
- * chunk loop.
623
+ * first, the rest deferred), similarity clustering, then the chunk loop.
678
624
  */
679
625
  async function planConsolidation(opts, config, stashDir, memories, warnings, stateDb, acc) {
680
626
  const llmRunner = opts.llmRunner ?? undefined;
@@ -685,7 +631,7 @@ async function planConsolidation(opts, config, stashDir, memories, warnings, sta
685
631
  let budgeted = memories;
686
632
  const budgetMs = opts.signal?.remainingBudgetMs;
687
633
  if (opts.signal && budgetMs !== undefined) {
688
- const safeChunks = Math.max(0, Math.floor((Math.max(0, budgetMs) / 1000 / (opts.p90ChunkSecondsDefault ?? 30)) * 0.6));
634
+ const safeChunks = Math.max(0, Math.floor((Math.max(0, budgetMs) / 1000 / CHUNK_SECONDS_ESTIMATE) * 0.6));
689
635
  if (safeChunks * chunkSize < memories.length) {
690
636
  budgeted = [...memories]
691
637
  .sort((a, b) => mtimeMsOf(a) - mtimeMsOf(b) || a.name.localeCompare(b.name))
@@ -696,11 +642,10 @@ async function planConsolidation(opts, config, stashDir, memories, warnings, sta
696
642
  }
697
643
  }
698
644
  const slice = (list) => Array.from({ length: Math.ceil(list.length / chunkSize) }, (_, i) => list.slice(i * chunkSize, (i + 1) * chunkSize));
699
- const willDispatch = slice(budgeted).some((chunk) => chunk.length > 0 && !chunk.every((memory) => isHotCapturedMemory(memory.filePath)));
700
- if (llmRunner && willDispatch)
645
+ if (llmRunner && budgeted.length > 0)
701
646
  assertRunnerCredentials(llmRunner);
702
647
  const { ordered, embedTelemetry } = await clusterMemoriesBySimilarity(budgeted, config, stateDb, opts.signal);
703
- const chunks = slice(injectRandomClusterMembers(ordered, opts.improveProfile, warnings));
648
+ const chunks = slice(ordered);
704
649
  const pendingProposalBodyHashes = loadPendingConsolidateProposalHashes(stashDir, opts.proposalsCtx);
705
650
  warn(`[consolidate] ${budgeted.length} memories / ${chunks.length} chunk(s) / chunk_size=${chunkSize}` +
706
651
  ` / pending-proposal hashes: ${pendingProposalBodyHashes.size}`);
@@ -0,0 +1,91 @@
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
+ * Distill's lesson update (#1090): when a memory's lesson repeats a lesson the library already holds, the writer
6
+ * is asked to extend that lesson with what the memory adds instead of the duplicate being thrown away. These are
7
+ * the prompt, the reply schema and the two deterministic checks; the call, the judge and the proposal are in
8
+ * `../distill`.
9
+ */
10
+ import { parseEmbeddedJsonResponse } from "../../../core/parse.js";
11
+ export const DISTILL_LESSON_UPDATE_JSON_SCHEMA = {
12
+ type: "object",
13
+ required: ["reason", "new_facts", "decision", "ref", "body"],
14
+ additionalProperties: false,
15
+ properties: {
16
+ reason: {
17
+ type: "string",
18
+ description: "One sentence: which listed lesson states the rule and what the memory adds to it, or why nothing.",
19
+ },
20
+ new_facts: {
21
+ type: "array",
22
+ items: { type: "string" },
23
+ description: "Written before the body: each cause, step, number, limit or consequence the memory states and the lesson lacks, one short phrase each. Empty when there is none.",
24
+ },
25
+ decision: {
26
+ type: "string",
27
+ enum: ["update", "none"],
28
+ description: "`none` when no listed lesson states the memory's rule, or `new_facts` is empty: leave `ref` and `body` empty. Otherwise `update`.",
29
+ },
30
+ ref: { type: "string", description: "The `Existing lesson ref:` of the lesson to extend. Empty for `none`." },
31
+ body: {
32
+ type: "string",
33
+ description: "That lesson's whole body with the new lines added, every existing line unchanged. No frontmatter. Empty for `none`.",
34
+ },
35
+ },
36
+ };
37
+ export function buildLessonUpdatePrompt(memory, feedback, candidates) {
38
+ const lines = [
39
+ "A lesson written from the memory below would repeat a lesson the library already holds. Do not write a new lesson. Extend the existing one with what the memory adds.",
40
+ "",
41
+ "Memory:",
42
+ "```",
43
+ memory.slice(0, 3000),
44
+ "```",
45
+ ];
46
+ if (feedback.length > 0)
47
+ lines.push("", "Feedback recorded about the memory:", "```", feedback.join("\n").slice(0, 1500), "```");
48
+ lines.push("", "Existing lessons, nearest first (they may be on another subject):");
49
+ for (const candidate of candidates) {
50
+ lines.push(`\nExisting lesson ref: ${candidate.ref}`, "```", candidate.body.slice(0, 3000), "```");
51
+ }
52
+ lines.push("", "Pick the one existing lesson that already states the rule the memory gives. Write its whole body again with the new lines added:", "- Copy every existing line unchanged, in its place. Do not reword, shorten, reorder or drop any of them.", "First list in new_facts each cause, step, number, limit or consequence the memory states and the lesson lacks, one short phrase each: check every sentence of the memory against the lesson. The memory's own incident retold (what happened in one test, project or day) is not a new fact unless it carries one of these. Then add one line to the body for each new fact, and say nothing the memory does not say.", "- If the memory contradicts a line of the lesson, answer none.", "- If no listed lesson states the memory's rule, or the memory adds nothing the lesson lacks, answer none.", "", 'Return ONLY valid JSON, no prose: {"reason": "...", "new_facts": ["..."], "decision": "update" | "none", "ref": "<the existing lesson ref>", "body": "<the whole body>"}');
53
+ return lines.join("\n");
54
+ }
55
+ /** The writer's chosen update, or `null` for `none` and for any reply that names no listed lesson, no new fact or no body. */
56
+ export function parseLessonUpdate(raw, candidates) {
57
+ const payload = parseEmbeddedJsonResponse(raw);
58
+ if (!payload || typeof payload !== "object" || Array.isArray(payload) || payload.decision !== "update")
59
+ return null;
60
+ const ref = typeof payload.ref === "string" ? payload.ref.trim() : "";
61
+ const body = typeof payload.body === "string" ? payload.body.trim() : "";
62
+ const candidate = candidates.find((c) => c.ref === ref);
63
+ const facts = Array.isArray(payload.new_facts)
64
+ ? payload.new_facts.filter((f) => typeof f === "string" && f.trim() !== "")
65
+ : [];
66
+ if (!candidate || !body || facts.length === 0)
67
+ return null;
68
+ return { candidate, body, reason: typeof payload.reason === "string" ? payload.reason.trim() : "" };
69
+ }
70
+ const normalizeLine = (line) => line.trim().replace(/\s+/g, " ").toLowerCase();
71
+ const bodyLines = (body) => body
72
+ .split("\n")
73
+ .map(normalizeLine)
74
+ .filter((line) => line !== "");
75
+ /**
76
+ * What an update does to a lesson's body: the existing lines it no longer holds (compared by words, in any case
77
+ * and spacing, anywhere in the new body) and the lines it adds.
78
+ */
79
+ export function diffLessonBody(existingBody, mergedBody) {
80
+ const existing = bodyLines(existingBody);
81
+ const merged = bodyLines(mergedBody);
82
+ const kept = new Set(merged);
83
+ const had = new Set(existing);
84
+ return {
85
+ dropped: existing.filter((line) => !kept.has(line)),
86
+ added: mergedBody
87
+ .split("\n")
88
+ .filter((line) => line.trim() !== "" && !had.has(normalizeLine(line)))
89
+ .map((line) => line.trim()),
90
+ };
91
+ }
@@ -3,8 +3,7 @@
3
3
  // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
4
  /**
5
5
  * Distill guards: the related lessons, knowledge notes and skills shown to the
6
- * writer so it does not repeat or overwrite them (CLS context), and a cheap check
7
- * that a proposal does not contradict the memories it came from.
6
+ * writer so it does not repeat or overwrite them (CLS context).
8
7
  */
9
8
  export const DEFAULT_CLS_ADJACENT_COUNT = 3;
10
9
  /** The CLS prompt section (each entry capped at 600 chars); empty when disabled (on unless `enabled: false`) or nothing is related. */
@@ -22,44 +21,3 @@ export function buildClsContext(adjacentItems, config) {
22
21
  lines.push(`### ${item.ref}`, item.content.trim().slice(0, 600), "");
23
22
  return lines.join("\n");
24
23
  }
25
- /**
26
- * Flag a proposal whose "always/must X" (or "never/must not X") claim meets
27
- * the opposite claim about X in a source. Deliberately conservative: a flag
28
- * only costs a human review, while a model call per proposal is expensive.
29
- */
30
- export function checkDistillFidelity(proposalBody, sourceBodies, config) {
31
- if (!config.enabled || sourceBodies.length === 0)
32
- return { contradictionDetected: false };
33
- const strongClaims = extractStrongClaims(proposalBody.toLowerCase());
34
- for (const sourceBody of sourceBodies) {
35
- const sourceLow = sourceBody.toLowerCase();
36
- for (const { polarity, term } of strongClaims) {
37
- if (hasStrongClaim(sourceLow, term, polarity === "positive" ? "negative" : "positive")) {
38
- return {
39
- contradictionDetected: true,
40
- reason: `Proposal makes a ${polarity} strong claim about "${term}" that conflicts with an opposing claim in a cited source. Route to human review.`,
41
- };
42
- }
43
- }
44
- }
45
- return { contradictionDetected: false };
46
- }
47
- const CLAIM_PATTERNS = [
48
- { polarity: "positive", re: /\b(?:always|must)\s+(\w+)/g },
49
- { polarity: "negative", re: /\b(?:never|must\s+not|should\s+not)\s+(\w+)/g },
50
- ];
51
- function extractStrongClaims(text) {
52
- const claims = [];
53
- for (const { polarity, re } of CLAIM_PATTERNS) {
54
- for (const m of text.matchAll(re)) {
55
- const term = m[1];
56
- if (term && term.length > 2)
57
- claims.push({ polarity, term });
58
- }
59
- }
60
- return claims;
61
- }
62
- function hasStrongClaim(text, term, polarity) {
63
- const marker = polarity === "positive" ? /\b(?:always|must)\s/ : /\b(?:never|must\s+not|should\s+not)\s/;
64
- return marker.test(text) && text.includes(term);
65
- }
@@ -40,15 +40,15 @@ import { getAllEntries } from "../../storage/repositories/index-entries-reposito
40
40
  import { listProposals } from "../proposal/repository.js";
41
41
  import { detectDoubleFrontmatter, isValidDescription } from "../proposal/validators/proposal-quality-validators.js";
42
42
  import { akmSearch } from "../read/search.js";
43
- import { stripFrontmatterBody } from "./content-hash.js";
44
43
  import { autoRepairLessonFrontmatter, autoSwapDescriptionWhenToUse, collectLessonQualityFindings, repairLessonDescriptionTruncation, } from "./distill/content-repair.js";
45
- import { buildClsContext, checkDistillFidelity, DEFAULT_CLS_ADJACENT_COUNT } from "./distill-guards.js";
44
+ import { buildLessonUpdatePrompt, DISTILL_LESSON_UPDATE_JSON_SCHEMA, diffLessonBody, parseLessonUpdate, } from "./distill/lesson-update.js";
45
+ import { buildClsContext, DEFAULT_CLS_ADJACENT_COUNT } from "./distill-guards.js";
46
46
  import { assessMemoryKnowledgePromotionCandidate, deriveKnowledgeRef } from "./distill-promotion-policy.js";
47
47
  import { buildRefVocabulary, scoreEncodingSalience } from "./encoding-salience.js";
48
48
  import { resolveImproveStrategy, resolveProcessEnabled } from "./improve-strategies.js";
49
49
  import { recordLedgerAttempt } from "./ledger.js";
50
50
  import { computeSalience, upsertAssetSalience } from "./salience.js";
51
- import { callStage, mintProposal, noticeSet, rejectedProposalContext, resolveQualityGateJudge, runLessonQualityJudge, stageRunner, } from "./stage.js";
51
+ import { callStage, LESSON_REJECT_MAX_SCORE, mintProposal, noticeSet, rejectedProposalContext, resolveQualityGateJudge, runLessonQualityJudge, stageRunner, } from "./stage.js";
52
52
  /**
53
53
  * Input types distill structurally refuses: a lesson is the distilled form
54
54
  * (distilling one would mint `lessons/lesson-…-lesson`), and env/secret bytes
@@ -361,7 +361,6 @@ export async function akmDistill(options) {
361
361
  eligMeta,
362
362
  asset,
363
363
  vocabulary: loadRefVocabulary(),
364
- outcomeWeightEnabled: config.improve?.salience?.outcomeWeightEnabled !== false,
365
364
  related: options.fetchRelatedFn ?? fetchRelatedAssets,
366
365
  lookup,
367
366
  };
@@ -436,6 +435,12 @@ async function distill(run, targetKind, kind, outputRef, feedbackEvents) {
436
435
  }
437
436
  const none = answeredNone(call.raw);
438
437
  if (none) {
438
+ // The writer may have answered NONE because a lesson already states the rule (the related list says to).
439
+ if (kind === "lesson") {
440
+ const update = await proposeLessonUpdate(run, feedbackLines(feedback), none.reason);
441
+ if (update)
442
+ return update;
443
+ }
439
444
  return skipDistill(run, outputRef, kind, "nothing_reusable", `The writer found no lesson in ${run.inputRef}${none.reason ? `: ${none.reason}` : "."}`);
440
445
  }
441
446
  const assembled = assembleDistilledContent(run, call.raw, kind, outputRef);
@@ -537,6 +542,13 @@ async function judgeAndQueue(run, out) {
537
542
  onNotices: run.notices.add,
538
543
  });
539
544
  if (!verdict.pass) {
545
+ if (out.kind === "lesson" &&
546
+ !out.promotion &&
547
+ (verdict.criteria?.nonRedundancy ?? 5) <= LESSON_REJECT_MAX_SCORE) {
548
+ const update = await proposeLessonUpdate(run, out.feedback ?? [], verdict.reason, related);
549
+ if (update)
550
+ return update;
551
+ }
540
552
  return rejectDistilled(run, out.ref, content, verdict.score, verdict.reason, {
541
553
  ...(verdict.reviewNeeded ? { reviewNeeded: true } : {}),
542
554
  ...(verdict.criteria ? { criteria: verdict.criteria } : {}),
@@ -553,19 +565,6 @@ async function judgeAndQueue(run, out) {
553
565
  frontmatter = data;
554
566
  }
555
567
  else {
556
- // Optional check against the cited source; a contradiction goes to a human.
557
- const fidelity = getImproveProcessConfig("distill", run.profile)?.fidelityCheck ?? {};
558
- if (fidelity.enabled && out.source) {
559
- try {
560
- const verdict = checkDistillFidelity(stripFrontmatterBody(content), [stripFrontmatterBody(out.source)], fidelity);
561
- if (verdict.contradictionDetected) {
562
- return rejectDistilled(run, out.ref, content, 2.0, verdict.reason ?? "Proposal may contradict cited source memories.", { reviewNeeded: true, fidelityContradiction: true });
563
- }
564
- }
565
- catch {
566
- // The fidelity check is supplemental.
567
- }
568
- }
569
568
  // Canonical provenance goes into the content promotion writes.
570
569
  const parsed = parseFrontmatter(content);
571
570
  const xrefs = Array.isArray(parsed.data.xrefs) ? parsed.data.xrefs.map(String) : [];
@@ -622,6 +621,129 @@ async function judgeAndQueue(run, out) {
622
621
  ...swapped,
623
622
  };
624
623
  }
624
+ /**
625
+ * A memory whose lesson repeats a lesson the library holds extends that lesson instead (#1090): the writer returns
626
+ * the lesson's body with the memory's new lines added, and the update is proposed on the lesson's own ref, for a
627
+ * person to review (never staged for the drain). `finding` is what the writer (its NONE reason) or the judge said
628
+ * about the repeat: the related lessons it names are the candidates, or the only related lesson when it names none,
629
+ * so a lesson that merely sits among several near the memory costs no call. Nothing is proposed, and `undefined`
630
+ * returned, when no lesson qualifies, the writer finds the memory adds
631
+ * nothing or contradicts the lesson, the body drops or rewords a line, the judge does not pass the extended lesson,
632
+ * or the lesson changed meanwhile. Needs the quality gate: the judge is what holds the added lines to the memory.
633
+ */
634
+ async function proposeLessonUpdate(run, feedback, finding, related) {
635
+ const memory = run.asset.content ? parseFrontmatter(run.asset.content).content.trim() : "";
636
+ const said = finding.toLowerCase();
637
+ if (!run.runner || !qualityGateEnabled(run) || !memory)
638
+ return undefined;
639
+ const lessons = [];
640
+ for (const asset of related ?? (await run.related(memory.slice(0, 500), RELATED_COUNT))) {
641
+ const parsed = parseRefInput(asset.ref);
642
+ if (parsed.type !== "lesson" || !asset.path || !isWithin(asset.path, run.stash))
643
+ continue;
644
+ try {
645
+ const content = fs.readFileSync(asset.path, "utf8");
646
+ const ref = conceptIdFromTypeName("lesson", parsed.name);
647
+ lessons.push({ ref, path: asset.path, content, body: parseFrontmatter(content).content.trim() });
648
+ }
649
+ catch {
650
+ // A lesson that cannot be read cannot be extended.
651
+ }
652
+ }
653
+ const named = lessons.filter((l) => said.includes(l.ref.toLowerCase()) || said.includes(l.ref.split("/").slice(1).join("/").toLowerCase()));
654
+ const candidates = named.length > 0 ? named : lessons.length === 1 ? lessons : [];
655
+ if (candidates.length === 0)
656
+ return undefined;
657
+ const call = await callStage({
658
+ feature: "distill",
659
+ runner: run.runner,
660
+ system: "Return only valid JSON. No prose.",
661
+ prompt: buildLessonUpdatePrompt(memory, feedback, candidates),
662
+ gate: { config: run.config, enabled: true },
663
+ request: {
664
+ ...(run.options.chat === undefined
665
+ ? { responseSchema: DISTILL_LESSON_UPDATE_JSON_SCHEMA }
666
+ : { chat: run.options.chat }),
667
+ ...(run.options.signal ? { signal: run.options.signal } : {}),
668
+ },
669
+ onNotices: run.notices.add,
670
+ });
671
+ const update = call.ok ? parseLessonUpdate(call.raw, candidates) : null;
672
+ if (!update)
673
+ return undefined;
674
+ const target = update.candidate;
675
+ const { dropped, added } = diffLessonBody(target.body, update.body);
676
+ if (dropped.length > 0) {
677
+ warnVerbose(`[akm] distill update of ${target.ref} dropped or reworded ${dropped.length} line(s); not proposed`);
678
+ return undefined;
679
+ }
680
+ if (added.length === 0)
681
+ return undefined;
682
+ // An edit is judged as the lesson it makes: the existing lesson with the lines added, as one lesson (reusable,
683
+ // grounded in the memory or the lesson), whose added lines must each say something the lesson did not (one call).
684
+ const extended = assembleAsset(parseFrontmatter(target.content).data, update.body);
685
+ const verdict = await runLessonQualityJudge(run.config, extended, memory, run.options.chat, {
686
+ extending: target.body,
687
+ ...(feedback.length > 0 ? { feedback } : {}),
688
+ ...((run.judgeRunner ?? run.runner) ? { llmRunner: run.judgeRunner ?? run.runner } : {}),
689
+ ...(run.options.signal ? { signal: run.options.signal } : {}),
690
+ onNotices: run.notices.add,
691
+ });
692
+ if (!verdict.pass) {
693
+ warnVerbose(`[akm] distill update of ${target.ref} not proposed: ${verdict.reason}`);
694
+ return undefined;
695
+ }
696
+ // The proposal's before-hash is taken at mint: a lesson edited since it was read is not what the writer extended.
697
+ let proposal;
698
+ try {
699
+ if (fs.readFileSync(target.path, "utf8") !== target.content)
700
+ return undefined;
701
+ const existing = parseFrontmatter(target.content);
702
+ const xrefs = Array.isArray(existing.data.xrefs) ? existing.data.xrefs.map(String) : [];
703
+ const frontmatter = { ...existing.data, xrefs: [...new Set([...xrefs, run.inputRef])] };
704
+ proposal = mintProposal(run.stash, run.options.ctx, {
705
+ ref: target.ref,
706
+ source: "distill",
707
+ ...(run.options.sourceRun !== undefined ? { sourceRun: run.options.sourceRun } : {}),
708
+ payload: { content: assembleAsset(frontmatter, update.body), frontmatter },
709
+ ...(verdict.score > 0 ? { confidence: verdict.score / 5 } : {}),
710
+ ...(run.options.eligibilitySource ? { eligibilitySource: run.options.eligibilitySource } : {}),
711
+ attemptedRefs: [run.ledgerRef],
712
+ }, {
713
+ review: {
714
+ reason: "distill-update",
715
+ gate: "quality-gate",
716
+ ...(verdict.criteria ? { scores: verdict.criteria } : {}),
717
+ judgeReason: verdict.reason,
718
+ },
719
+ });
720
+ }
721
+ catch (error) {
722
+ warnVerbose(`[akm] distill update of ${target.ref} not proposed: ${error instanceof Error ? error.message : error}`);
723
+ return undefined;
724
+ }
725
+ emitDistill(run, {
726
+ outcome: "queued",
727
+ proposalRef: target.ref,
728
+ proposalKind: "lesson",
729
+ proposalId: proposal.id,
730
+ updatesExisting: true,
731
+ ...(run.options.sourceRun !== undefined ? { sourceRun: run.options.sourceRun } : {}),
732
+ ...exclusionMeta(run, false),
733
+ });
734
+ return {
735
+ schemaVersion: 1,
736
+ ok: true,
737
+ outcome: "queued",
738
+ inputRef: run.inputRef,
739
+ proposalRef: target.ref,
740
+ proposalKind: "lesson",
741
+ proposalId: proposal.id,
742
+ proposal,
743
+ updatesExisting: true,
744
+ ...exclusionMeta(run, true),
745
+ };
746
+ }
625
747
  function rejectDistilled(run, proposalRef, content, score, reason, meta) {
626
748
  return writeQualityRejection({
627
749
  stash: run.stash,
@@ -667,7 +789,14 @@ export function writeQualityRejection(args) {
667
789
  payload: { content: args.content },
668
790
  attemptedRefs: [ledgerRef],
669
791
  ...(args.eligibilitySource ? { eligibilitySource: args.eligibilitySource } : {}),
670
- }, { review: { reason: "quality-review", gate: "quality-gate" } });
792
+ }, {
793
+ review: {
794
+ reason: "quality-review",
795
+ gate: "quality-gate",
796
+ judgeReason: args.reason,
797
+ ...(meta.criteria ? { scores: meta.criteria } : {}),
798
+ },
799
+ });
671
800
  }
672
801
  catch (error) {
673
802
  warn(`[akm] writeQualityRejection: failed to queue ${args.proposalRef} for review: ${error instanceof Error ? error.message : String(error)}`);
@@ -863,7 +992,6 @@ function stampInputSalience(run) {
863
992
  type,
864
993
  retrievalFreq: 0,
865
994
  encodingSalience: scored.score,
866
- outcomeWeightEnabled: run.outcomeWeightEnabled,
867
995
  })));
868
996
  }
869
997
  catch {
@@ -887,7 +1015,6 @@ function persistOutputEncodingSalience(run, ref, body) {
887
1015
  type,
888
1016
  retrievalFreq: 0,
889
1017
  encodingSalience: scored.score,
890
- outcomeWeightEnabled: run.outcomeWeightEnabled,
891
1018
  })));
892
1019
  }
893
1020
  catch {
@@ -974,7 +1101,7 @@ async function fetchRelatedAssets(query, n) {
974
1101
  catch {
975
1102
  // best-effort
976
1103
  }
977
- return { ref: h.ref, content };
1104
+ return { ref: h.ref, content, ...(h.path ? { path: h.path } : {}) };
978
1105
  });
979
1106
  }
980
1107
  catch {
@@ -4,6 +4,7 @@
4
4
  /** The refs an improve run may consider, read from the index, and the small predicates over them. */
5
5
  import fs from "node:fs";
6
6
  import path from "node:path";
7
+ import { closestMatch } from "../../cli/unknown-flags.js";
7
8
  import { parseBundleRef } from "../../core/asset/asset-ref.js";
8
9
  import { parseFrontmatter } from "../../core/asset/frontmatter.js";
9
10
  import { conceptIdFromTypeName, parseRefInput, resolveRef, typeNameFromConceptId } from "../../core/asset/resolve-ref.js";
@@ -11,6 +12,8 @@ import { loadConfig } from "../../core/config/config.js";
11
12
  import { ConfigError, NotFoundError, rethrowIfTestIsolationError, UsageError } from "../../core/errors.js";
12
13
  import { isPathAbsent } from "../../core/path-access.js";
13
14
  import { getDbPath } from "../../core/paths.js";
15
+ import { KNOWN_TYPES } from "../../core/recognition-util.js";
16
+ import { warn } from "../../core/warn.js";
14
17
  import { deriveInstallations, deriveWritableBundleIds } from "../../indexer/installations.js";
15
18
  import { resolveSourceEntries } from "../../indexer/search/search-source.js";
16
19
  import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
@@ -210,7 +213,11 @@ async function collectEligibleRefsFromIndex(scope, stashDir, improveProfile, rea
210
213
  indexSnapshot: ready,
211
214
  };
212
215
  }
213
- const entries = getAllEntries(db, scope.mode === "type" ? scope.value : undefined).filter((indexed) => isCandidateBundle(indexed.bundleId));
216
+ const scoped = getAllEntries(db, scope.mode === "type" ? scope.value : undefined);
217
+ if (scope.mode === "type" && scope.value && scope.value !== "any" && scoped.length === 0) {
218
+ warnUnmatchedTypeScope(scope.value, getAllEntries(db));
219
+ }
220
+ const entries = scoped.filter((indexed) => isCandidateBundle(indexed.bundleId));
214
221
  const planned = new Map();
215
222
  const strategyFiltered = new Map();
216
223
  let memoryEligible = 0;
@@ -262,6 +269,12 @@ async function collectEligibleRefsFromIndex(scope, stashDir, improveProfile, rea
262
269
  closeDatabase(db);
263
270
  }
264
271
  }
272
+ /** A type scope that matches no indexed asset: warn on stderr, naming the nearest real type. */
273
+ function warnUnmatchedTypeScope(value, indexed) {
274
+ const types = [...new Set([...KNOWN_TYPES, ...indexed.map((entry) => entry.entry.type)])];
275
+ const suggestion = closestMatch(value, types, Math.max(1, Math.floor(value.length / 3)));
276
+ warn(`Scope "${value}" matches no asset in the stash.${suggestion ? ` Did you mean \`${suggestion}\`?` : ""}`);
277
+ }
265
278
  /** The parent memory a `--scope <memory ref>` cleanup is restricted to. */
266
279
  export function memoryCleanupParentRef(scope, stashDir) {
267
280
  if (scope.mode !== "ref" || !scope.value)
@@ -26,7 +26,7 @@ import { akmExtract, resolveStandaloneExtractPlan } from "./extract.js";
26
26
  export const extractCommand = defineJsonCommand({
27
27
  meta: {
28
28
  name: "extract",
29
- description: "Experimental: extract durable insights from native session files (claude, codex, opencode) and queue them as proposals.",
29
+ description: "Extract durable insights from native session files (claude, codex, opencode) and queue them as proposals.",
30
30
  },
31
31
  args: {
32
32
  type: {
@@ -121,26 +121,36 @@ function formatAlreadyPreserved(inlineRefs) {
121
121
  export const TRANSCRIPT_FENCE_BEGIN = "=== BEGIN UNTRUSTED SESSION TRANSCRIPT ===";
122
122
  export const TRANSCRIPT_FENCE_END = "=== END UNTRUSTED SESSION TRANSCRIPT ===";
123
123
  /**
124
- * Format pre-filtered events as a transcript snippet. Each event becomes:
125
- * [<role> @ <iso>] <text>
126
- * Events are already truncated/cleaned by the pre-filter; this is purely
127
- * a render step.
124
+ * Wrap transcript text in the untrusted fence.
128
125
  *
129
126
  * Anti-spoof: any occurrence of the fence markers inside the transcript text is
130
127
  * neutralised so a crafted session cannot forge the boundary and "escape" the
131
128
  * fence to inject trusted-looking instructions.
132
129
  */
130
+ export function fenceTranscript(text) {
131
+ const body = text
132
+ .split(TRANSCRIPT_FENCE_BEGIN)
133
+ .join("=== (fence) ===")
134
+ .split(TRANSCRIPT_FENCE_END)
135
+ .join("=== (fence) ===");
136
+ return `${TRANSCRIPT_FENCE_BEGIN}\n${body}\n${TRANSCRIPT_FENCE_END}`;
137
+ }
138
+ /**
139
+ * Format pre-filtered events as a transcript snippet. Each event becomes:
140
+ * [<role> @ <iso>] <text>
141
+ * Events are already truncated/cleaned by the pre-filter; this is purely
142
+ * a render step.
143
+ */
133
144
  function formatTranscript(events) {
134
145
  if (events.length === 0)
135
146
  return "(empty — pre-filter removed all events as noise)";
136
- const body = events
147
+ return events
137
148
  .map((e) => {
138
149
  const tsLabel = e.ts ? new Date(e.ts).toISOString() : "unknown-ts";
139
150
  const roleLabel = e.role ?? "unknown";
140
151
  return `[${roleLabel} @ ${tsLabel}] ${e.text}`;
141
152
  })
142
153
  .join("\n\n");
143
- return body.split(TRANSCRIPT_FENCE_BEGIN).join("=== (fence) ===").split(TRANSCRIPT_FENCE_END).join("=== (fence) ===");
144
154
  }
145
155
  /**
146
156
  * Build the user-prompt body for the extract LLM call by interpolating
@@ -164,7 +174,7 @@ export function buildExtractPrompt(input) {
164
174
  .replace("{{PROJECT_HINT}}", ref.projectHint ?? "(no project hint)")
165
175
  .replace("{{ALREADY_PRESERVED}}", formatAlreadyPreserved(input.inlineRefs))
166
176
  .replace("{{STANDARDS}}", standards)
167
- .replace("{{TRANSCRIPT}}", `${TRANSCRIPT_FENCE_BEGIN}\n${formatTranscript(input.events)}\n${TRANSCRIPT_FENCE_END}`);
177
+ .replace("{{TRANSCRIPT}}", fenceTranscript(formatTranscript(input.events)));
168
178
  }
169
179
  function failedExtractPayload(code, message) {
170
180
  return { candidates: [], rationale_if_empty: message, parseFailure: { code, message } };