akm-cli 0.9.0-rc.0 → 0.9.0-rc.2

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 (262) hide show
  1. package/CHANGELOG.md +339 -2
  2. package/SECURITY.md +23 -24
  3. package/dist/assets/help/help-improve.md +10 -10
  4. package/dist/assets/hints/cli-hints-full.md +44 -10
  5. package/dist/assets/hints/cli-hints-short.md +6 -2
  6. package/dist/assets/{profiles → improve-strategies}/default.json +1 -0
  7. package/dist/assets/{profiles → improve-strategies}/graph-refresh.json +1 -1
  8. package/dist/assets/{profiles → improve-strategies}/proactive-maintenance.json +2 -3
  9. package/dist/assets/{profiles → improve-strategies}/reflect-distill.json +3 -4
  10. package/dist/assets/prompts/workflow-unit-preamble.md +26 -0
  11. package/dist/assets/stash-skeleton/README.md +28 -0
  12. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +6 -0
  13. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +6 -0
  14. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +12 -1
  15. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +11 -1
  16. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +6 -0
  17. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +9 -0
  18. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +7 -0
  19. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +7 -0
  20. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +6 -0
  21. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +98 -0
  22. package/dist/assets/stash-skeleton/facts/conventions/domains.md +63 -0
  23. package/dist/assets/stash-skeleton/facts/conventions/organization.md +127 -0
  24. package/dist/assets/tasks/core/backup.yml +1 -0
  25. package/dist/assets/tasks/core/extract.yml +1 -0
  26. package/dist/assets/tasks/core/improve.yml +1 -0
  27. package/dist/assets/tasks/core/index-refresh.yml +1 -0
  28. package/dist/assets/tasks/core/sync.yml +1 -0
  29. package/dist/assets/tasks/core/version-check.yml +1 -0
  30. package/dist/assets/tasks/graph-refresh-weekly.yml +4 -4
  31. package/dist/assets/templates/html/health.html +5 -1
  32. package/dist/cli/config-migrate.js +31 -138
  33. package/dist/cli/config-validate.js +10 -8
  34. package/dist/cli.js +48 -14
  35. package/dist/commands/agent/agent-dispatch.js +17 -16
  36. package/dist/commands/agent/agent-support.js +0 -24
  37. package/dist/commands/agent/contribute-cli.js +5 -15
  38. package/dist/commands/backup-cli.js +54 -0
  39. package/dist/commands/config-cli.js +45 -159
  40. package/dist/commands/env/env-binding.js +95 -0
  41. package/dist/commands/env/env-cli.js +8 -65
  42. package/dist/commands/env/secret.js +8 -5
  43. package/dist/commands/health/checks.js +130 -83
  44. package/dist/commands/health/html-report.js +4 -0
  45. package/dist/commands/health/improve-metrics.js +30 -32
  46. package/dist/commands/health/llm-usage.js +19 -19
  47. package/dist/commands/health/md-report.js +4 -0
  48. package/dist/commands/health/metrics.js +2 -1
  49. package/dist/commands/health/surfaces.js +5 -4
  50. package/dist/commands/health.js +1 -1
  51. package/dist/commands/improve/consolidate/chunking.js +2 -2
  52. package/dist/commands/improve/consolidate.js +28 -25
  53. package/dist/commands/improve/distill/promote-memory.js +5 -12
  54. package/dist/commands/improve/distill/quality-gate.js +5 -7
  55. package/dist/commands/improve/distill.js +16 -5
  56. package/dist/commands/improve/eligibility.js +22 -12
  57. package/dist/commands/improve/extract-cli.js +47 -19
  58. package/dist/commands/improve/extract.js +110 -62
  59. package/dist/commands/improve/improve-cli.js +38 -16
  60. package/dist/commands/improve/improve-result-file.js +30 -24
  61. package/dist/commands/improve/improve-strategies.js +137 -0
  62. package/dist/commands/improve/improve.js +60 -30
  63. package/dist/commands/improve/locks.js +66 -45
  64. package/dist/commands/improve/loop-stages.js +75 -33
  65. package/dist/commands/improve/memory/memory-belief.js +79 -7
  66. package/dist/commands/improve/memory/memory-contradiction-detect.js +12 -4
  67. package/dist/commands/improve/preparation.js +71 -73
  68. package/dist/commands/improve/procedural.js +3 -2
  69. package/dist/commands/improve/recombine.js +2 -1
  70. package/dist/commands/improve/reflect.js +119 -214
  71. package/dist/commands/improve/shared.js +11 -5
  72. package/dist/commands/lint/base-linter.js +152 -42
  73. package/dist/commands/mv-cli.js +809 -0
  74. package/dist/commands/proposal/proposal-cli.js +18 -8
  75. package/dist/commands/proposal/propose.js +64 -69
  76. package/dist/commands/read/knowledge.js +436 -4
  77. package/dist/commands/read/remember-cli.js +39 -2
  78. package/dist/commands/read/search-cli.js +6 -1
  79. package/dist/commands/registry-cli.js +29 -14
  80. package/dist/commands/remember.js +2 -0
  81. package/dist/commands/sources/init.js +13 -14
  82. package/dist/commands/sources/migration-help.js +7 -4
  83. package/dist/commands/sources/schema-repair.js +2 -4
  84. package/dist/commands/sources/source-add.js +62 -73
  85. package/dist/commands/sources/source-manage.js +50 -46
  86. package/dist/commands/sources/stash-cli.js +41 -4
  87. package/dist/commands/tasks/default-tasks.js +12 -12
  88. package/dist/commands/tasks/tasks-cli.js +7 -3
  89. package/dist/commands/tasks/tasks.js +113 -18
  90. package/dist/commands/wiki-cli.js +9 -10
  91. package/dist/commands/workflow-cli.js +276 -12
  92. package/dist/core/asset/asset-spec.js +58 -1
  93. package/dist/core/asset/frontmatter.js +12 -2
  94. package/dist/core/common.js +5 -3
  95. package/dist/core/config/config-io.js +28 -17
  96. package/dist/core/config/config-schema.js +379 -66
  97. package/dist/core/config/config-types.js +3 -3
  98. package/dist/core/config/config-version.js +29 -0
  99. package/dist/core/config/config-walker.js +98 -27
  100. package/dist/core/config/config.js +132 -266
  101. package/dist/core/config/deep-merge.js +41 -0
  102. package/dist/core/config/engine-semantics.js +32 -0
  103. package/dist/core/errors.js +2 -2
  104. package/dist/core/extra-params.js +61 -0
  105. package/dist/core/file-lock.js +201 -56
  106. package/dist/core/improve-result.js +178 -0
  107. package/dist/core/json-schema.js +142 -0
  108. package/dist/core/maintenance-barrier.js +119 -0
  109. package/dist/core/migration-backup.js +416 -0
  110. package/dist/core/paths.js +3 -0
  111. package/dist/core/redaction.js +358 -0
  112. package/dist/core/state/migrations.js +17 -2
  113. package/dist/core/state-db.js +44 -1
  114. package/dist/indexer/db/db.js +118 -2
  115. package/dist/indexer/graph/graph-extraction.js +28 -16
  116. package/dist/indexer/index-writer-lock.js +31 -24
  117. package/dist/indexer/index-written-assets.js +15 -6
  118. package/dist/indexer/indexer.js +47 -2
  119. package/dist/indexer/passes/memory-inference.js +10 -6
  120. package/dist/indexer/passes/metadata.js +250 -0
  121. package/dist/indexer/search/db-search.js +111 -44
  122. package/dist/indexer/search/fts-query.js +41 -0
  123. package/dist/indexer/search/ranking-contributors.js +48 -0
  124. package/dist/indexer/search/ranking.js +36 -23
  125. package/dist/indexer/search/search-fields.js +11 -1
  126. package/dist/indexer/walk/matchers.js +39 -0
  127. package/dist/integrations/agent/builder-shared.js +7 -0
  128. package/dist/integrations/agent/builders.js +5 -50
  129. package/dist/integrations/agent/config.js +3 -143
  130. package/dist/integrations/agent/detect.js +17 -2
  131. package/dist/integrations/agent/engine-resolution.js +202 -0
  132. package/dist/integrations/agent/index.js +1 -2
  133. package/dist/integrations/agent/model-aliases.js +16 -2
  134. package/dist/integrations/agent/profiles.js +36 -62
  135. package/dist/integrations/agent/runner-dispatch.js +91 -4
  136. package/dist/integrations/agent/runner.js +76 -207
  137. package/dist/integrations/agent/spawn.js +141 -20
  138. package/dist/integrations/harnesses/aider/agent-builder.js +112 -0
  139. package/dist/integrations/harnesses/aider/index.js +57 -0
  140. package/dist/integrations/harnesses/aider/result-extractor.js +53 -0
  141. package/dist/integrations/harnesses/amazonq/agent-builder.js +152 -0
  142. package/dist/integrations/harnesses/amazonq/index.js +58 -0
  143. package/dist/integrations/harnesses/amazonq/result-extractor.js +48 -0
  144. package/dist/integrations/harnesses/claude/agent-builder.js +46 -8
  145. package/dist/integrations/harnesses/claude/index.js +25 -25
  146. package/dist/integrations/harnesses/claude/result-extractor.js +52 -0
  147. package/dist/integrations/harnesses/codex/agent-builder.js +136 -0
  148. package/dist/integrations/harnesses/codex/index.js +62 -0
  149. package/dist/integrations/harnesses/codex/result-extractor.js +73 -0
  150. package/dist/integrations/harnesses/copilot/agent-builder.js +121 -0
  151. package/dist/integrations/harnesses/copilot/index.js +59 -0
  152. package/dist/integrations/harnesses/copilot/result-extractor.js +151 -0
  153. package/dist/integrations/harnesses/gemini/agent-builder.js +120 -0
  154. package/dist/integrations/harnesses/gemini/index.js +59 -0
  155. package/dist/integrations/harnesses/gemini/result-extractor.js +121 -0
  156. package/dist/integrations/harnesses/index.js +27 -28
  157. package/dist/integrations/harnesses/opencode/agent-builder.js +2 -3
  158. package/dist/integrations/harnesses/opencode/index.js +15 -22
  159. package/dist/integrations/harnesses/opencode-sdk/harness.js +60 -0
  160. package/dist/integrations/harnesses/opencode-sdk/index.js +8 -32
  161. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +681 -108
  162. package/dist/integrations/harnesses/openhands/agent-builder.js +125 -0
  163. package/dist/integrations/harnesses/openhands/index.js +57 -0
  164. package/dist/integrations/harnesses/openhands/result-extractor.js +103 -0
  165. package/dist/integrations/harnesses/pi/agent-builder.js +103 -0
  166. package/dist/integrations/harnesses/pi/index.js +57 -0
  167. package/dist/integrations/harnesses/pi/result-extractor.js +135 -0
  168. package/dist/integrations/harnesses/types.js +8 -32
  169. package/dist/integrations/lockfile.js +32 -21
  170. package/dist/integrations/session-logs/index.js +24 -11
  171. package/dist/llm/client.js +48 -14
  172. package/dist/llm/feature-gate.js +15 -47
  173. package/dist/llm/graph-extract.js +1 -1
  174. package/dist/llm/index-passes.js +8 -42
  175. package/dist/llm/memory-infer-impl.js +1 -1
  176. package/dist/llm/usage-persist.js +4 -0
  177. package/dist/llm/usage-telemetry.js +35 -5
  178. package/dist/output/renderers.js +3 -2
  179. package/dist/output/shapes/helpers.js +2 -1
  180. package/dist/output/shapes/passthrough.js +6 -0
  181. package/dist/output/text/helpers.js +215 -2
  182. package/dist/output/text/workflow.js +3 -1
  183. package/dist/schemas/akm-config.json +16638 -0
  184. package/dist/schemas/akm-task.json +87 -0
  185. package/dist/schemas/akm-workflow.json +372 -0
  186. package/dist/scripts/migrate-storage.js +10944 -8801
  187. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +9247 -350
  188. package/dist/setup/detected-engines.js +142 -0
  189. package/dist/setup/engine-config.js +89 -0
  190. package/dist/setup/setup.js +236 -132
  191. package/dist/setup/steps/connection.js +61 -32
  192. package/dist/setup/steps/platforms.js +4 -4
  193. package/dist/setup/steps.js +3 -2
  194. package/dist/storage/database.js +13 -1
  195. package/dist/storage/engines/sqlite-migrations.js +1 -0
  196. package/dist/storage/repositories/improve-runs-repository.js +5 -5
  197. package/dist/storage/repositories/task-history-repository.js +78 -0
  198. package/dist/storage/repositories/workflow-runs-repository.js +190 -1
  199. package/dist/tasks/parser.js +138 -52
  200. package/dist/tasks/runner.js +71 -75
  201. package/dist/tasks/schema.js +1 -1
  202. package/dist/tasks/validator.js +11 -6
  203. package/dist/text-import-hook.mjs +1 -1
  204. package/dist/wiki/wiki.js +9 -8
  205. package/dist/workflows/authoring/authoring.js +123 -10
  206. package/dist/workflows/authoring/workflow-program-template.yaml +31 -0
  207. package/dist/workflows/cli.js +4 -0
  208. package/dist/workflows/concurrency-policy.js +15 -0
  209. package/dist/workflows/db.js +200 -13
  210. package/dist/workflows/exec/brief.js +478 -0
  211. package/dist/workflows/exec/frozen-judge.js +47 -0
  212. package/dist/workflows/exec/native-executor.js +1034 -0
  213. package/dist/workflows/exec/param-secrets.js +115 -0
  214. package/dist/workflows/exec/report.js +1355 -0
  215. package/dist/workflows/exec/run-workflow.js +609 -0
  216. package/dist/workflows/exec/scheduler.js +71 -0
  217. package/dist/workflows/exec/step-work.js +1212 -0
  218. package/dist/workflows/exec/unit-writer.js +23 -0
  219. package/dist/workflows/exec/watch.js +116 -0
  220. package/dist/workflows/exec/worktree.js +171 -0
  221. package/dist/workflows/ir/compile.js +375 -0
  222. package/dist/workflows/ir/freeze.js +243 -0
  223. package/dist/workflows/ir/params.js +54 -0
  224. package/dist/workflows/ir/plan-hash.js +68 -0
  225. package/dist/workflows/ir/schema.js +545 -0
  226. package/dist/workflows/parser.js +10 -1
  227. package/dist/workflows/program/expressions.js +369 -0
  228. package/dist/workflows/program/parser.js +869 -0
  229. package/dist/workflows/program/project.js +104 -0
  230. package/dist/workflows/program/schema.js +54 -0
  231. package/dist/workflows/renderer.js +82 -5
  232. package/dist/workflows/resource-limits.js +20 -0
  233. package/dist/workflows/runtime/agent-identity.js +59 -14
  234. package/dist/workflows/runtime/plan-classifier.js +187 -0
  235. package/dist/workflows/runtime/runs.js +246 -69
  236. package/dist/workflows/runtime/unit-checkin.js +45 -0
  237. package/dist/workflows/runtime/workflow-asset-loader.js +42 -1
  238. package/dist/workflows/validate-summary.js +24 -3
  239. package/dist/workflows/validator.js +26 -1
  240. package/docs/data-and-telemetry.md +4 -3
  241. package/docs/migration/release-notes/0.6.0.md +1 -1
  242. package/docs/migration/release-notes/0.7.0.md +5 -4
  243. package/docs/migration/release-notes/0.9.0-beta.60.md +19 -0
  244. package/docs/migration/v0.8-to-v0.9.md +401 -0
  245. package/package.json +4 -2
  246. package/schemas/akm-config.json +16638 -0
  247. package/schemas/akm-task.json +87 -0
  248. package/schemas/akm-workflow.json +372 -0
  249. package/dist/commands/improve/improve-profiles.js +0 -168
  250. package/dist/core/config/config-migration.js +0 -602
  251. package/dist/core/deep-merge.js +0 -38
  252. package/dist/llm/call-ai.js +0 -62
  253. package/dist/setup/legacy-config.js +0 -106
  254. package/docs/README.md +0 -104
  255. /package/dist/assets/{profiles → improve-strategies}/catchup.json +0 -0
  256. /package/dist/assets/{profiles → improve-strategies}/consolidate.json +0 -0
  257. /package/dist/assets/{profiles → improve-strategies}/frequent.json +0 -0
  258. /package/dist/assets/{profiles → improve-strategies}/memory-focus.json +0 -0
  259. /package/dist/assets/{profiles → improve-strategies}/quick.json +0 -0
  260. /package/dist/assets/{profiles → improve-strategies}/recombine-only.json +0 -0
  261. /package/dist/assets/{profiles → improve-strategies}/synthesize.json +0 -0
  262. /package/dist/assets/{profiles → improve-strategies}/thorough.json +0 -0
@@ -6,13 +6,14 @@ import path from "node:path";
6
6
  import { parseAssetRef } from "../../core/asset/asset-ref.js";
7
7
  import { parseFrontmatter } from "../../core/asset/frontmatter.js";
8
8
  import { daysToMs } from "../../core/common.js";
9
- import { getDefaultLlmConfig, loadConfig } from "../../core/config/config.js";
10
- import { rethrowIfTestIsolationError } from "../../core/errors.js";
9
+ import { loadConfig } from "../../core/config/config.js";
10
+ import { ConfigError, rethrowIfTestIsolationError } from "../../core/errors.js";
11
11
  import { appendEvent, readEvents } from "../../core/events.js";
12
12
  import { openStateDatabase, withStateDb } from "../../core/state-db.js";
13
13
  import { info, warn } from "../../core/warn.js";
14
14
  import { closeDatabase, getRetrievalCounts, getZeroResultSearches, openExistingDatabase } from "../../indexer/db/db.js";
15
15
  import { countUsageEventsByType } from "../../indexer/usage/usage-events.js";
16
+ import { materializeLlmRunnerConnection } from "../../integrations/agent/runner.js";
16
17
  import { getAvailableHarnesses } from "../../integrations/session-logs/index.js";
17
18
  import { withLlmStage } from "../../llm/usage-telemetry.js";
18
19
  import { persistPhaseThreshold } from "../../storage/repositories/improve-runs-repository.js";
@@ -27,7 +28,6 @@ import { buildLatestFeedbackTsMap, buildLatestProposalTsMap, buildUtilityMap, de
27
28
  import { akmExtract, countNewExtractCandidates } from "./extract.js";
28
29
  import { computeValenceScore, FEEDBACK_WEIGHT, UTILITY_WEIGHT } from "./feedback-valence.js";
29
30
  import { makeGateConfig, resolveExtractConfidence, runAutoAcceptGate } from "./improve-auto-accept.js";
30
- import { resolveProcessEnabled } from "./improve-profiles.js";
31
31
  import { applyMemoryCleanup } from "./memory/memory-improve.js";
32
32
  import { computeProxyAdequacy, getAllAssetOutcomes, getOutcomeScoresByRef, OUTCOME_SCORE_MAX, outcomeScoreToSalience, updateAssetOutcome, } from "./outcome-loop.js";
33
33
  import { DEFAULT_DUE_DAYS, DEFAULT_MAX_PER_RUN, selectProactiveMaintenanceRefs } from "./proactive-maintenance.js";
@@ -151,36 +151,12 @@ export function maybeAutoTuneThreshold(currentThreshold, config, stateDbPath, ct
151
151
  * need, so the fix is non-invasive and provably correct.
152
152
  */
153
153
  export async function runConsolidationPass(args) {
154
- const { options, primaryStashDir, memorySummary, improveProfile, eventsCtx, budgetSignal, runBudgetMs } = args;
154
+ const { options, primaryStashDir, memorySummary, improveProfile, resolvedPlan, eventsCtx, budgetSignal, runBudgetMs, } = args;
155
155
  const baseConfig = options.config ?? loadConfig();
156
156
  const MEMORY_VOLUME_THRESHOLD = options.memoryVolumeConsolidationThreshold ?? 100;
157
- const hasLlm = !!(baseConfig.defaults?.llm || baseConfig.defaults?.agent);
157
+ const hasLlm = resolvedPlan.processes.consolidate.runner !== null;
158
158
  const volumeTriggered = typeof memorySummary.eligible === "number" && memorySummary.eligible > MEMORY_VOLUME_THRESHOLD && hasLlm;
159
- // When volume triggers a consolidation pass, force-enable the consolidate
160
- // process on the default improve profile so the gate accepts the run even
161
- // if the user's config disabled it. We synthesise a new profile override
162
- // rather than mutating connection settings.
163
- const consolidationConfig = volumeTriggered
164
- ? {
165
- ...baseConfig,
166
- profiles: {
167
- ...(baseConfig.profiles ?? {}),
168
- improve: {
169
- ...(baseConfig.profiles?.improve ?? {}),
170
- default: {
171
- ...(baseConfig.profiles?.improve?.default ?? {}),
172
- processes: {
173
- ...(baseConfig.profiles?.improve?.default?.processes ?? {}),
174
- consolidate: {
175
- ...(baseConfig.profiles?.improve?.default?.processes?.consolidate ?? {}),
176
- enabled: true,
177
- },
178
- },
179
- },
180
- },
181
- },
182
- }
183
- : baseConfig;
159
+ const consolidationConfig = baseConfig;
184
160
  // 0.8.0 pool-delta gate for consolidate: re-eligible iff at least one
185
161
  // memory file has been updated since the most recent successful
186
162
  // consolidate_completed event. Time-based cooldowns produced the same
@@ -323,6 +299,9 @@ export async function runConsolidationPass(args) {
323
299
  // Active profile for this improve run — lets consolidate's secondary
324
300
  // process-config reads honor `--profile <name>` instead of `default`.
325
301
  improveProfile,
302
+ llmConfig: resolvedPlan.processes.consolidate.runner
303
+ ? materializeLlmRunnerConnection(resolvedPlan.processes.consolidate.runner)
304
+ : null,
326
305
  autoTriggered: volumeTriggered,
327
306
  // Tie consolidate proposals back to this improve invocation so
328
307
  // accept-rate-per-run aggregation works. Mirrors reflect/propose/extract.
@@ -357,7 +336,7 @@ export async function runConsolidationPass(args) {
357
336
  // WS-5: pass total run budget so perfTelemetry.estimatedBudgetFractionUsed
358
337
  // can flag when consolidation alone exceeded the budget.
359
338
  runBudgetMs,
360
- }));
339
+ }), { engine: resolvedPlan.processes.consolidate.runner?.engine, process: "consolidate" });
361
340
  {
362
341
  const consolidateGr = await runAutoAcceptGate(consolidation.promoted.map((proposalId) => {
363
342
  try {
@@ -422,7 +401,7 @@ export async function runConsolidationPass(args) {
422
401
  * consolidation pass and accumulated here.
423
402
  */
424
403
  async function runSessionExtractPass(args) {
425
- const { options, primaryStashDir, improveProfile, eventsCtx, seedGateAccepted, seedGateFailed } = args;
404
+ const { options, primaryStashDir, improveProfile, resolvedPlan, eventsCtx, seedGateAccepted, seedGateFailed } = args;
426
405
  const warnings = [];
427
406
  // Phase 0.4 — session-extract pass.
428
407
  //
@@ -476,11 +455,22 @@ async function runSessionExtractPass(args) {
476
455
  // #593/#594: the ACTIVE resolved improve profile is the single source of
477
456
  // truth for whether extract runs. (Previously this also ANDed in the legacy
478
457
  // `session_extraction` feature flag, which only reads
479
- // `profiles.improve.default.processes.extract.enabled`; that made the default
480
- // profile a global kill switch, so a non-default profile enabling extract was
481
- // silently overridden. The default profile is now just another profile.)
458
+ // a retired global feature path; the selected strategy is authoritative.)
482
459
  // `akmExtract` re-checks the same active profile internally via `improveProfile`.
483
- if (resolveProcessEnabled("extract", improveProfile)) {
460
+ if (resolvedPlan.processes.extract.enabled) {
461
+ const extractRunner = resolvedPlan.processes.extract.runner;
462
+ if (!extractRunner?.engine) {
463
+ throw new ConfigError("Resolved improve plan has no runner for enabled extract process.", "LLM_NOT_CONFIGURED");
464
+ }
465
+ const extractPlan = Object.freeze({
466
+ strategy: resolvedPlan.strategy.name,
467
+ engine: extractRunner.engine,
468
+ enabled: true,
469
+ process: resolvedPlan.processes.extract.config,
470
+ runner: extractRunner,
471
+ timeoutMs: extractRunner.timeoutMs === undefined ? 600_000 : extractRunner.timeoutMs,
472
+ embeddingConfig: Object.freeze(structuredClone(extractConfig.embedding)),
473
+ });
484
474
  const availableHarnesses = options.extractHarnesses ?? getAvailableHarnesses();
485
475
  // The guard engages only when minNewSessions > 0; 0 disables it entirely.
486
476
  let belowMinNewSessions = false;
@@ -521,14 +511,12 @@ async function runSessionExtractPass(args) {
521
511
  type: h.name,
522
512
  ...(primaryStashDir !== undefined ? { stashDir: primaryStashDir } : {}),
523
513
  config: extractConfig,
524
- // Thread the ACTIVE profile so extract's internal gate + per-process
525
- // config read the running profile, not always `default`.
526
- improveProfile,
514
+ resolvedPlan: extractPlan,
527
515
  dryRun: options.dryRun ?? false,
528
516
  ...(options.extractHarnesses ? { harnesses: options.extractHarnesses } : {}),
529
517
  // C2: pin extract's skip-tracking state.db open to the boundary path.
530
518
  ...(eventsCtx?.dbPath ? { stateDbPath: eventsCtx.dbPath } : {}),
531
- }));
519
+ }), { engine: resolvedPlan.processes.extract.runner?.engine, process: "extract" });
532
520
  extractResults.push(result);
533
521
  {
534
522
  const gr = await runAutoAcceptGate(primaryStashDir
@@ -577,49 +565,47 @@ async function runSessionExtractPass(args) {
577
565
  * with structural problems (missing file, missing lesson description), attempts
578
566
  * LLM schema repair, and returns the still-failing ref set + the repair records.
579
567
  */
580
- async function runValidationAndRepairPass(args) {
581
- const { postCleanupRefs, options, startMs, budgetMs, primaryStashDir } = args;
582
- const validationFailures = [];
583
- for (const candidate of postCleanupRefs) {
568
+ export async function runValidationAndRepairPass(args) {
569
+ const { postCleanupRefs, options, startMs, budgetMs, primaryStashDir, resolvedPlan, repairValidationFailures, schemaRepairFn = runSchemaRepairPass, } = args;
570
+ const validateCandidate = async (candidate) => {
584
571
  try {
585
- // #591: use the path pre-resolved at planning time when it is still on
586
- // disk — a serial async DB lookup per ref cost ~500 s on a 9 000-ref
587
- // stash. Fall back to findAssetFilePath only for refs that bypassed
588
- // collectEligibleRefs' index scan or whose file moved since planning.
589
572
  const filePath = candidate.filePath && fs.existsSync(candidate.filePath)
590
573
  ? candidate.filePath
591
574
  : await findAssetFilePath(candidate.ref, options.stashDir);
592
- if (!filePath) {
593
- validationFailures.push({ ref: candidate.ref, reason: "file not found on disk" });
594
- continue;
595
- }
596
- if (path.extname(filePath).toLowerCase() !== ".md") {
597
- continue;
598
- }
575
+ if (!filePath)
576
+ return "file not found on disk";
577
+ if (path.extname(filePath).toLowerCase() !== ".md")
578
+ return undefined;
599
579
  if (isLessonCandidate(candidate.ref)) {
600
- const raw = fs.readFileSync(filePath, "utf8");
601
- const fm = parseFrontmatter(raw).data;
580
+ const fm = parseFrontmatter(fs.readFileSync(filePath, "utf8")).data;
602
581
  if (!fm.description)
603
- validationFailures.push({ ref: candidate.ref, reason: "missing description" });
582
+ return "missing description";
604
583
  }
584
+ return undefined;
605
585
  }
606
- catch (e) {
607
- validationFailures.push({ ref: candidate.ref, reason: String(e) });
586
+ catch (error) {
587
+ return String(error);
608
588
  }
589
+ };
590
+ const validationFailures = [];
591
+ for (const candidate of postCleanupRefs) {
592
+ const reason = await validateCandidate(candidate);
593
+ if (reason)
594
+ validationFailures.push({ ref: candidate.ref, reason });
609
595
  }
610
596
  if (validationFailures.length > 0) {
611
- info(`[improve] ${validationFailures.length} assets have validation issues (will attempt schema repair):`);
597
+ info(`[improve] ${validationFailures.length} assets have validation issues${repairValidationFailures ? " (will attempt schema repair)" : ""}:`);
612
598
  for (const f of validationFailures)
613
599
  info(` ${f.ref}: ${f.reason}`);
614
600
  }
615
601
  let schemaRepairs = [];
616
- let repairedRefs = new Set();
602
+ const repairedRefs = new Set();
617
603
  // Schema repair pass: attempt to fix validation failures via LLM before skipping.
618
- if (validationFailures.length > 0 && options.repairValidationFailures !== false) {
619
- const baseConfigForRepair = options.config ?? loadConfig();
620
- const llmCfg = getDefaultLlmConfig(baseConfigForRepair);
604
+ if (validationFailures.length > 0) {
605
+ const validationRunner = resolvedPlan.processes.validation.runner;
606
+ const llmCfg = validationRunner ? materializeLlmRunnerConnection(validationRunner) : undefined;
621
607
  if (llmCfg) {
622
- const result = await runSchemaRepairPass(validationFailures, {
608
+ const result = await withLlmStage("validation", () => schemaRepairFn(validationFailures, {
623
609
  startMs,
624
610
  budgetMs,
625
611
  llmConfig: llmCfg,
@@ -633,9 +619,17 @@ async function runValidationAndRepairPass(args) {
633
619
  stashDir: primaryStashDir,
634
620
  findFilePath: findAssetFilePath,
635
621
  isLessonCandidateFn: isLessonCandidate,
636
- });
622
+ }), { engine: resolvedPlan.processes.validation.runner?.engine, process: "validation" });
637
623
  schemaRepairs = result.repairs;
638
- repairedRefs = result.repairedRefs;
624
+ // A repair result is advisory. Only a fresh structural read of the live
625
+ // asset can remove it from the failure set; queued content is not live.
626
+ const failedRefs = new Set(validationFailures.map((failure) => failure.ref));
627
+ const candidatesByRef = new Map(postCleanupRefs.map((candidate) => [candidate.ref, candidate]));
628
+ for (const ref of failedRefs) {
629
+ const candidate = candidatesByRef.get(ref);
630
+ if (candidate && !(await validateCandidate(candidate)))
631
+ repairedRefs.add(ref);
632
+ }
639
633
  }
640
634
  }
641
635
  const validationFailureRefs = new Set(validationFailures.filter((f) => !repairedRefs.has(f.ref)).map((f) => f.ref));
@@ -645,7 +639,7 @@ async function runValidationAndRepairPass(args) {
645
639
  return { validationFailures, validationFailureRefs, schemaRepairs };
646
640
  }
647
641
  export async function runImprovePreparationStage(args) {
648
- const { scope, options, plannedRefs, memoryCleanupPlan, primaryStashDir, memorySummary, reindexFn, startMs, budgetMs, eventsCtx, initialCleanupWarnings, improveProfile, budgetSignal, } = args;
642
+ const { scope, options, plannedRefs, memoryCleanupPlan, primaryStashDir, memorySummary, reindexFn, startMs, budgetMs, eventsCtx, initialCleanupWarnings, improveProfile, resolvedPlan, strategyName, budgetSignal, } = args;
649
643
  const actions = [];
650
644
  const cleanupWarnings = initialCleanupWarnings ? [...initialCleanupWarnings] : [];
651
645
  // Phase 0 — MEMORY.md budget check (200-line cap; warn at 180)
@@ -681,6 +675,7 @@ export async function runImprovePreparationStage(args) {
681
675
  primaryStashDir,
682
676
  memorySummary,
683
677
  improveProfile,
678
+ resolvedPlan,
684
679
  eventsCtx,
685
680
  budgetSignal,
686
681
  runBudgetMs: budgetMs,
@@ -690,6 +685,7 @@ export async function runImprovePreparationStage(args) {
690
685
  options,
691
686
  primaryStashDir,
692
687
  improveProfile,
688
+ resolvedPlan,
693
689
  eventsCtx,
694
690
  seedGateAccepted: consolidationPass.gateAutoAcceptedCount,
695
691
  seedGateFailed: consolidationPass.gateAutoAcceptFailedCount,
@@ -704,7 +700,7 @@ export async function runImprovePreparationStage(args) {
704
700
  appendEvent({
705
701
  eventType: "improve_invoked",
706
702
  ref: scope.mode === "ref" ? scope.value : `improve:${scope.mode}:${scope.value ?? "all"}`,
707
- metadata: { scope, dryRun: options.dryRun ?? false, eligibleCount: plannedRefs.length },
703
+ metadata: { strategy: strategyName, scope, dryRun: options.dryRun ?? false, eligibleCount: plannedRefs.length },
708
704
  }, eventsCtx);
709
705
  // ensureIndex now runs in akmImprove() BEFORE collectEligibleRefs so the
710
706
  // eligible-ref query sees a populated `entries` table on the very first
@@ -751,6 +747,8 @@ export async function runImprovePreparationStage(args) {
751
747
  startMs,
752
748
  budgetMs,
753
749
  primaryStashDir,
750
+ resolvedPlan,
751
+ repairValidationFailures: resolvedPlan.processes.validation.enabled && options.repairValidationFailures !== false,
754
752
  });
755
753
  // Phase 0.5 — structural hygiene pass
756
754
  let lintSummary;
@@ -892,7 +890,7 @@ export async function runImprovePreparationStage(args) {
892
890
  // Has feedback on record but no signal delta since the last proposal —
893
891
  // genuinely fully skipped. Counted here; a single aggregated
894
892
  // improve_skipped event is emitted after the loop (mirrors
895
- // profile_filtered_all_passes) instead of one event per ref.
893
+ // strategy_filtered_all_passes) instead of one event per ref.
896
894
  fullySkippedCount++;
897
895
  actions.push({
898
896
  ref: r.ref,
@@ -903,7 +901,7 @@ export async function runImprovePreparationStage(args) {
903
901
  }
904
902
  // Emit ONE aggregated skip event for the fully-skipped bucket rather than one
905
903
  // improve_skipped event per ref (#592 pattern, mirrors
906
- // profile_filtered_all_passes above). The per-ref loop previously produced
904
+ // strategy_filtered_all_passes above). The per-ref loop previously produced
907
905
  // ~11K state.db writes per run on a large stash, the dominant contributor to
908
906
  // 900 s timeouts. The in-memory `actions` log keeps the per-ref detail for the
909
907
  // run summary; no downstream consumer needs a per-ref DB audit trail (health's
@@ -1063,7 +1061,7 @@ export async function runImprovePreparationStage(args) {
1063
1061
  // runs rotate through the due pool rather than re-selecting the same heads.
1064
1062
  let proactiveRefs = [];
1065
1063
  let proactiveMaintenanceSummary;
1066
- const proactiveEnabled = scope.mode !== "ref" && resolveProcessEnabled("proactiveMaintenance", improveProfile);
1064
+ const proactiveEnabled = scope.mode !== "ref" && resolvedPlan.processes.proactiveMaintenance.enabled;
1067
1065
  if (proactiveEnabled) {
1068
1066
  const pmCfg = improveProfile.processes?.proactiveMaintenance;
1069
1067
  const dueDays = pmCfg?.dueDays ?? DEFAULT_DUE_DAYS;
@@ -4,8 +4,8 @@
4
4
  /**
5
5
  * #615 — procedural-compilation pass.
6
6
  *
7
- * An OPT-IN post-loop improve stage (default disabled via
8
- * `IMPROVE_PROCESS_DEFAULTS.procedural`). It reads assets that carry an
7
+ * An OPT-IN post-loop improve stage (the built-in default strategy sets
8
+ * `processes.procedural.enabled: false`). It reads assets that carry an
9
9
  * `orderedActions` frontmatter list (captured by #619), detects RECURRING
10
10
  * successful action sequences across sessions (the SAME normalized ordered step
11
11
  * list appearing >= `minRecurrence` times with a non-failure `outcomeData`), and
@@ -293,6 +293,7 @@ export async function akmProcedural(opts) {
293
293
  tag: "[procedural]",
294
294
  signal: opts.signal,
295
295
  activeProfile: opts.improveProfile,
296
+ llmConfig: opts.llmConfig,
296
297
  });
297
298
  if (!llmFn) {
298
299
  warnings.push("procedural: no LLM configured — skipping");
@@ -5,7 +5,7 @@
5
5
  * #609 — recombine / synthesize pass.
6
6
  *
7
7
  * A whole-corpus synthesis stage that runs AFTER consolidation and is OPT-IN
8
- * (default disabled via `IMPROVE_PROCESS_DEFAULTS.recombine`). It clusters
8
+ * (the built-in default strategy sets `processes.recombine.enabled: false`). It clusters
9
9
  * memories by RELATEDNESS (shared tags / graph entities — NEVER embedding
10
10
  * similarity), issues ONE bounded LLM call per cluster to induce a single
11
11
  * cross-episodic generalization, and emits the result as a NORMAL pending
@@ -554,6 +554,7 @@ export async function akmRecombine(opts) {
554
554
  tag: "[recombine]",
555
555
  signal: opts.signal,
556
556
  activeProfile: opts.improveProfile,
557
+ llmConfig: opts.llmConfig,
557
558
  });
558
559
  if (!llmFn) {
559
560
  warnings.push("recombine: no LLM configured — skipping");