akm-cli 0.9.1 → 0.9.2-alpha.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 (350) hide show
  1. package/CHANGELOG.md +103 -28
  2. package/README.md +3 -1
  3. package/SECURITY.md +1 -1
  4. package/STABILITY.md +1 -1
  5. package/dist/akm +2 -2
  6. package/dist/akm-migrate +2 -2
  7. package/dist/assets/hints/cli-hints-full.md +14 -9
  8. package/dist/assets/improve-strategies/proactive-maintenance.json +1 -1
  9. package/dist/assets/improve-strategies/reflect-distill.json +1 -1
  10. package/dist/assets/models.json +35 -0
  11. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +3 -4
  12. package/dist/assets/stash-skeleton/facts/conventions/organization.md +1 -3
  13. package/dist/assets/tasks/core/extract.yml +6 -5
  14. package/dist/assets/tasks/core/improve.yml +6 -5
  15. package/dist/assets/tasks/core/index-refresh.yml +6 -5
  16. package/dist/assets/tasks/core/sync.yml +6 -5
  17. package/dist/assets/tasks/core/version-check.yml +6 -5
  18. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +6 -5
  19. package/dist/assets/tasks/improve/akm-improve-catchup.yml +6 -5
  20. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +6 -5
  21. package/dist/assets/tasks/improve/akm-improve-frequent.yml +6 -5
  22. package/dist/assets/tasks/improve/akm-improve-nightly.yml +6 -5
  23. package/dist/cli/confirm.js +2 -2
  24. package/dist/cli/parse-args.js +3 -24
  25. package/dist/cli/retired-commands.js +1 -1
  26. package/dist/cli/shared.js +2 -2
  27. package/dist/cli.js +11 -9
  28. package/dist/commands/agent/agent-dispatch.js +55 -89
  29. package/dist/commands/agent/contribute-cli.js +12 -45
  30. package/dist/commands/command/builtin-action.js +32 -0
  31. package/dist/commands/command/command-cli.js +99 -0
  32. package/dist/commands/command/command-execution.js +308 -0
  33. package/dist/commands/command/execution-source-loader.js +176 -0
  34. package/dist/commands/command/portable-template.js +60 -0
  35. package/dist/commands/config-cli.js +10 -4
  36. package/dist/commands/env/env.js +4 -2
  37. package/dist/commands/feedback-cli.js +1 -1
  38. package/dist/commands/health/checks.js +241 -29
  39. package/dist/commands/health/html-report.js +0 -14
  40. package/dist/commands/health/report-view-model.js +0 -1
  41. package/dist/commands/health/surfaces.js +6 -7
  42. package/dist/commands/health/types.js +0 -2
  43. package/dist/commands/health.js +63 -18
  44. package/dist/commands/improve/collapse-detector.js +5 -6
  45. package/dist/commands/improve/consolidate.js +251 -214
  46. package/dist/commands/improve/distill/promote-memory.js +71 -34
  47. package/dist/commands/improve/distill/quality-gate.js +17 -5
  48. package/dist/commands/improve/distill.js +232 -155
  49. package/dist/commands/improve/eligibility.js +112 -79
  50. package/dist/commands/improve/execution.js +57 -0
  51. package/dist/commands/improve/extract-cli.js +5 -5
  52. package/dist/commands/improve/extract-prompt.js +64 -22
  53. package/dist/commands/improve/extract.js +608 -360
  54. package/dist/commands/improve/improve-strategies.js +43 -14
  55. package/dist/commands/improve/improve.js +249 -29
  56. package/dist/commands/improve/loop-stages.js +11 -17
  57. package/dist/commands/improve/memory/memory-contradiction-detect.js +90 -66
  58. package/dist/commands/improve/outcome-loop.js +22 -38
  59. package/dist/commands/improve/planner.js +134 -0
  60. package/dist/commands/improve/preparation.js +730 -409
  61. package/dist/commands/improve/reflect.js +386 -223
  62. package/dist/commands/improve/run-context.js +3 -4
  63. package/dist/commands/improve/salience.js +6 -58
  64. package/dist/commands/improve/session-asset.js +12 -12
  65. package/dist/commands/lint/index.js +101 -29
  66. package/dist/commands/migrate-cli.js +11 -69
  67. package/dist/commands/migration-tool.js +6 -9
  68. package/dist/commands/models-cli.js +27 -0
  69. package/dist/commands/proposal/drain.js +258 -186
  70. package/dist/commands/proposal/proposal-cli.js +32 -10
  71. package/dist/commands/proposal/proposal.js +2 -5
  72. package/dist/commands/proposal/propose.js +192 -172
  73. package/dist/commands/proposal/repository.js +54 -91
  74. package/dist/commands/proposal/validators/proposal-validators.js +9 -7
  75. package/dist/commands/read/curate.js +53 -22
  76. package/dist/commands/read/registry-search.js +25 -9
  77. package/dist/commands/read/remember-cli.js +14 -2
  78. package/dist/commands/read/search.js +10 -4
  79. package/dist/commands/read/show.js +139 -153
  80. package/dist/commands/registry-cli.js +16 -7
  81. package/dist/commands/remember.js +33 -18
  82. package/dist/commands/sources/add-cli.js +19 -178
  83. package/dist/commands/sources/bundle-cli.js +15 -3
  84. package/dist/commands/sources/dangerous-env-audit.js +135 -0
  85. package/dist/commands/sources/info.js +2 -1
  86. package/dist/commands/sources/installed-stashes.js +901 -177
  87. package/dist/commands/sources/schema-repair.js +174 -95
  88. package/dist/commands/sources/self-update.js +30 -74
  89. package/dist/commands/sources/source-add.js +3 -5
  90. package/dist/commands/sources/sources-cli.js +2 -15
  91. package/dist/commands/sources/update-transaction.js +220 -0
  92. package/dist/commands/tasks/tasks-cli.js +3 -3
  93. package/dist/commands/tasks/tasks.js +736 -317
  94. package/dist/commands/workflow-cli.js +2 -2
  95. package/dist/core/adapter/adapters/agent-skills-adapter.js +3 -0
  96. package/dist/core/adapter/adapters/akm-adapter.js +85 -35
  97. package/dist/core/adapter/adapters/akm-lint.js +54 -39
  98. package/dist/core/adapter/adapters/akm-metadata.js +45 -45
  99. package/dist/core/adapter/adapters/akm-task-adapter.js +32 -49
  100. package/dist/core/adapter/adapters/akm-workflow-adapter.js +38 -23
  101. package/dist/core/adapter/adapters/dotenv-adapter.js +30 -1
  102. package/dist/core/adapter/adapters/generic-files-adapter.js +11 -0
  103. package/dist/core/adapter/adapters/index.js +0 -9
  104. package/dist/core/adapter/adapters/llm-wiki-adapter.js +4 -0
  105. package/dist/core/adapter/adapters/okf-adapter.js +4 -0
  106. package/dist/core/adapter/adapters/opencode-adapter.js +5 -8
  107. package/dist/core/adapter/adapters/tool-dir-shared.js +63 -6
  108. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -0
  109. package/dist/core/adapter/execution-source.js +308 -0
  110. package/dist/core/adapter/recognize-match.js +36 -13
  111. package/dist/core/adapter/registry.js +0 -9
  112. package/dist/core/asset/stash-meta.js +94 -4
  113. package/dist/core/common.js +6 -11
  114. package/dist/core/config/config-io.js +3 -3
  115. package/dist/core/config/config-schema.js +18 -40
  116. package/dist/core/config/config-sources.js +11 -21
  117. package/dist/core/config/config-walker.js +31 -13
  118. package/dist/core/config/config.js +23 -26
  119. package/dist/core/config/schema/engines.js +8 -7
  120. package/dist/core/config/schema/improve-processes.js +29 -5
  121. package/dist/core/config/schema/index-config.js +0 -27
  122. package/dist/core/config/schema/primitives.js +1 -23
  123. package/dist/core/config/schema/sources-bundles.js +13 -16
  124. package/dist/core/errors.js +2 -0
  125. package/dist/core/events.js +68 -32
  126. package/dist/core/extra-params.js +1 -0
  127. package/dist/core/improve-result.js +315 -0
  128. package/dist/core/lesson-lint.js +0 -6
  129. package/dist/core/maintenance-barrier.js +4 -4
  130. package/dist/core/network-policy.js +152 -0
  131. package/dist/core/paths.js +1 -1
  132. package/dist/core/recognition-util.js +4 -4
  133. package/dist/core/registry-url.js +456 -0
  134. package/dist/core/state/migrations.js +161 -47
  135. package/dist/core/state-db.js +453 -80
  136. package/dist/core/system-error.js +32 -0
  137. package/dist/core/time.js +2 -12
  138. package/dist/core/write-source.js +0 -18
  139. package/dist/execution/directory-identity.js +52 -0
  140. package/dist/execution/executable-identity.js +107 -0
  141. package/dist/execution/guarded-source.js +398 -0
  142. package/dist/execution/json.js +95 -0
  143. package/dist/{commands/health/types-session-log.js → execution/limits.js} +2 -1
  144. package/dist/execution/record.js +55 -0
  145. package/dist/execution/resolved-request.js +730 -0
  146. package/dist/execution/source.js +320 -0
  147. package/dist/indexer/bundle-identity-guard.js +5 -4
  148. package/dist/indexer/db/graph-db.js +33 -0
  149. package/dist/indexer/graph/graph-boost.js +3 -4
  150. package/dist/indexer/graph/graph-extraction.js +562 -373
  151. package/dist/indexer/index-written-assets.js +78 -39
  152. package/dist/indexer/indexer.js +471 -432
  153. package/dist/indexer/installations.js +6 -0
  154. package/dist/indexer/lookup/adapter-concept-owner.js +283 -0
  155. package/dist/indexer/materialize-embeddings.js +155 -0
  156. package/dist/indexer/passes/memory-inference.js +227 -174
  157. package/dist/indexer/passes/metadata.js +263 -118
  158. package/dist/indexer/scan/doc-to-entry.js +7 -10
  159. package/dist/indexer/scan/drain-dir.js +51 -23
  160. package/dist/indexer/search/db-search.js +156 -50
  161. package/dist/indexer/search/fts-query.js +40 -40
  162. package/dist/indexer/search/ranking.js +36 -1
  163. package/dist/indexer/search/search-attribution.js +3 -1
  164. package/dist/indexer/search/search-fields.js +23 -14
  165. package/dist/indexer/search/search-hit-enrichers.js +1 -1
  166. package/dist/indexer/search/search-source.js +7 -16
  167. package/dist/indexer/search/semantic-status.js +10 -1
  168. package/dist/indexer/usage/show-usage.js +105 -0
  169. package/dist/indexer/usage/usage-events.js +7 -2
  170. package/dist/indexer/walk/matchers.js +40 -10
  171. package/dist/indexer/walk/path-resolver.js +5 -2
  172. package/dist/indexer/walk/walker.js +20 -2
  173. package/dist/integrations/agent/builder-shared.js +3 -6
  174. package/dist/integrations/agent/conversation-fallback.js +16 -0
  175. package/dist/integrations/agent/engine-resolution.js +87 -87
  176. package/dist/integrations/agent/execution-cascade.js +566 -0
  177. package/dist/integrations/agent/execution-definitions.js +211 -0
  178. package/dist/integrations/agent/execution-lowering.js +811 -0
  179. package/dist/integrations/agent/execution-preparation.js +67 -0
  180. package/dist/integrations/agent/index.js +0 -2
  181. package/dist/integrations/agent/inline-execution.js +74 -0
  182. package/dist/integrations/agent/model-map.js +515 -0
  183. package/dist/integrations/agent/persona-fallback.js +30 -0
  184. package/dist/integrations/agent/request-lowering.js +186 -0
  185. package/dist/integrations/agent/runner-dispatch.js +230 -37
  186. package/dist/integrations/agent/runner.js +12 -83
  187. package/dist/integrations/harnesses/aider/agent-builder.js +8 -0
  188. package/dist/integrations/harnesses/aider/index.js +0 -1
  189. package/dist/integrations/harnesses/amazonq/agent-builder.js +8 -0
  190. package/dist/integrations/harnesses/amazonq/index.js +0 -1
  191. package/dist/integrations/harnesses/claude/agent-builder.js +14 -1
  192. package/dist/integrations/harnesses/claude/index.js +1 -5
  193. package/dist/integrations/harnesses/claude/session-log.js +3 -33
  194. package/dist/integrations/harnesses/codex/agent-builder.js +8 -0
  195. package/dist/integrations/harnesses/codex/index.js +0 -1
  196. package/dist/integrations/harnesses/copilot/agent-builder.js +8 -0
  197. package/dist/integrations/harnesses/copilot/index.js +0 -1
  198. package/dist/integrations/harnesses/gemini/agent-builder.js +8 -0
  199. package/dist/integrations/harnesses/gemini/index.js +0 -1
  200. package/dist/integrations/harnesses/index.js +4 -44
  201. package/dist/integrations/harnesses/opencode/agent-builder.js +16 -9
  202. package/dist/integrations/harnesses/opencode/index.js +0 -2
  203. package/dist/integrations/harnesses/opencode/session-log.js +14 -204
  204. package/dist/integrations/harnesses/opencode-sdk/harness.js +12 -1
  205. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +40 -42
  206. package/dist/integrations/harnesses/openhands/agent-builder.js +8 -0
  207. package/dist/integrations/harnesses/openhands/index.js +0 -1
  208. package/dist/integrations/harnesses/pi/agent-builder.js +8 -0
  209. package/dist/integrations/harnesses/pi/index.js +0 -1
  210. package/dist/integrations/harnesses/shared.js +0 -1
  211. package/dist/integrations/harnesses/types.js +1 -3
  212. package/dist/integrations/lockfile.js +82 -79
  213. package/dist/integrations/session-logs/index.js +6 -17
  214. package/dist/integrations/session-logs/provider-base.js +1 -29
  215. package/dist/llm/client.js +10 -5
  216. package/dist/llm/embedder.js +6 -7
  217. package/dist/llm/embedders/local.js +37 -88
  218. package/dist/llm/embedders/types.js +1 -1
  219. package/dist/llm/graph-extract.js +75 -50
  220. package/dist/llm/index-passes.js +43 -5
  221. package/dist/llm/memory-infer.js +8 -6
  222. package/dist/llm/metadata-enhance.js +5 -3
  223. package/dist/llm/structured-call.js +122 -25
  224. package/dist/output/format-exempt.js +1 -1
  225. package/dist/output/render-registry.js +0 -16
  226. package/dist/output/renderers.js +12 -7
  227. package/dist/output/shapes/curate.js +1 -0
  228. package/dist/output/shapes/helpers.js +10 -2
  229. package/dist/output/shapes/passthrough.js +2 -0
  230. package/dist/output/text/command-format.js +31 -33
  231. package/dist/output/text/health-format.js +1 -29
  232. package/dist/output/text/migrate.js +6 -56
  233. package/dist/output/text/proposal-format.js +16 -1
  234. package/dist/output/text/workflow-format.js +16 -0
  235. package/dist/registry/network.js +279 -0
  236. package/dist/registry/pinned-request-helper.js +247 -0
  237. package/dist/registry/pinned-transport.js +717 -0
  238. package/dist/registry/providers/skills-sh.js +18 -6
  239. package/dist/registry/providers/static-index.js +20 -7
  240. package/dist/registry/resolve.js +53 -28
  241. package/dist/scripts/akm-migrate-node.js +19334 -52269
  242. package/dist/scripts/akm-migrate.js +19270 -51612
  243. package/dist/setup/registry-stash-loader.js +64 -20
  244. package/dist/setup/semantic-assets.js +9 -34
  245. package/dist/setup/setup.js +12 -30
  246. package/dist/setup/source-identity.js +17 -0
  247. package/dist/setup/steps/sources.js +36 -15
  248. package/dist/setup/steps/tasks.js +39 -11
  249. package/dist/sources/providers/git-provider.js +3 -3
  250. package/dist/sources/providers/npm.js +2 -2
  251. package/dist/sources/providers/provider-utils.js +4 -3
  252. package/dist/sources/providers/website.js +11 -7
  253. package/dist/sources/snapshot-fetchers/host-guard.js +9 -136
  254. package/dist/sources/snapshot-fetchers/website-ingest.js +25 -109
  255. package/dist/sources/website-url.js +73 -0
  256. package/dist/storage/engines/sqlite-migrations.js +81 -26
  257. package/dist/storage/managed-db.js +27 -24
  258. package/dist/storage/repositories/events-repository.js +3 -0
  259. package/dist/storage/repositories/index-connection.js +42 -10
  260. package/dist/storage/repositories/index-entries-repository.js +203 -229
  261. package/dist/storage/repositories/index-entry-mapper.js +8 -12
  262. package/dist/storage/repositories/index-entry-schema.js +255 -0
  263. package/dist/storage/repositories/index-fts-repository.js +64 -71
  264. package/dist/storage/repositories/index-llm-cache-repository.js +8 -13
  265. package/dist/storage/repositories/index-meta-repository.js +0 -11
  266. package/dist/storage/repositories/index-schema.js +74 -350
  267. package/dist/storage/repositories/index-utility-repository.js +12 -17
  268. package/dist/storage/repositories/index-vec-repository.js +56 -7
  269. package/dist/storage/repositories/proposals-repository.js +4 -127
  270. package/dist/storage/repositories/registry-cache.js +2 -1
  271. package/dist/storage/repositories/task-history-repository.js +20 -40
  272. package/dist/storage/repositories/workflow-runs-repository.js +228 -129
  273. package/dist/storage/sqlite-read-snapshot.js +148 -0
  274. package/dist/tasks/backends/cron.js +170 -42
  275. package/dist/tasks/backends/index.js +1 -1
  276. package/dist/tasks/backends/launchd.js +787 -202
  277. package/dist/tasks/backends/schtasks.js +282 -83
  278. package/dist/tasks/embedded.js +7 -7
  279. package/dist/tasks/frozen-script.js +50 -0
  280. package/dist/tasks/resolve-akm-bin.js +5 -1
  281. package/dist/tasks/runner.js +239 -251
  282. package/dist/tasks/runtime-v3.js +281 -0
  283. package/dist/tasks/scheduler-binding.js +272 -0
  284. package/dist/tasks/scheduler-invocation.js +57 -43
  285. package/dist/tasks/scheduler-sync.js +654 -0
  286. package/dist/tasks/source-v3.js +752 -0
  287. package/dist/tasks/standalone-script-entry.js +5 -0
  288. package/dist/tasks/task-id.js +29 -0
  289. package/dist/workflows/authoring/authoring.js +15 -32
  290. package/dist/workflows/exec/dispatch-redaction.js +14 -8
  291. package/dist/workflows/exec/exec-unit.js +7 -28
  292. package/dist/workflows/exec/frozen-judge.js +57 -89
  293. package/dist/workflows/exec/lowering-notices.js +23 -0
  294. package/dist/workflows/exec/native-executor.js +301 -458
  295. package/dist/workflows/exec/param-secrets.js +4 -3
  296. package/dist/workflows/exec/run-workflow.js +26 -32
  297. package/dist/workflows/exec/step-work.js +105 -109
  298. package/dist/workflows/exec/unit-dispatch.js +103 -27
  299. package/dist/workflows/exec/unit-writer.js +3 -3
  300. package/dist/workflows/exec/worktree.js +2 -2
  301. package/dist/workflows/ir/compile.js +86 -72
  302. package/dist/workflows/ir/environment-v4.js +328 -0
  303. package/dist/workflows/ir/freeze-v4.js +122 -0
  304. package/dist/workflows/ir/plan-hash.js +13 -7
  305. package/dist/workflows/ir/schema-v4.js +525 -0
  306. package/dist/workflows/ir/schema.js +25 -284
  307. package/dist/workflows/ir/source-freeze-v4.js +506 -0
  308. package/dist/workflows/parser.js +27 -24
  309. package/dist/workflows/program/schema.js +1 -2
  310. package/dist/workflows/renderer.js +42 -29
  311. package/dist/workflows/resource-limits.js +4 -5
  312. package/dist/workflows/runtime/agent-identity.js +11 -13
  313. package/dist/workflows/runtime/plan-classifier.js +8 -8
  314. package/dist/workflows/runtime/runs.js +27 -43
  315. package/dist/workflows/runtime/workflow-asset-loader.js +45 -205
  316. package/dist/workflows/source-files.js +373 -0
  317. package/dist/workflows/source-ir/compile.js +196 -0
  318. package/dist/workflows/source-ir/github-yaml.js +577 -0
  319. package/dist/workflows/source-ir/ordering.js +38 -0
  320. package/dist/workflows/source-ir/program.js +50 -0
  321. package/dist/workflows/source-ir/result.js +26 -0
  322. package/dist/workflows/source-ir/schema.js +772 -0
  323. package/dist/workflows/source-ir/semantics.js +242 -0
  324. package/dist/workflows/source-ir/uses.js +14 -0
  325. package/docs/README.md +2 -0
  326. package/docs/migration/README.md +3 -1
  327. package/docs/migration/release-notes/0.9.2.md +55 -0
  328. package/docs/migration/release-notes/README.md +5 -0
  329. package/docs/migration/v0.8-to-v0.9.md +76 -1077
  330. package/docs/migration/v0.9.0-troubleshooting.md +104 -516
  331. package/docs/migration/v0.9.1-to-v0.9.2.md +150 -0
  332. package/docs/reference/README.md +1 -0
  333. package/docs/reference/cli.md +230 -98
  334. package/docs/reference/configuration.md +159 -36
  335. package/docs/reference/data-and-telemetry.md +19 -1
  336. package/docs/reference/supported-formats.md +23 -3
  337. package/docs/reference/tasks.md +182 -0
  338. package/docs/reference/workflow-schema.md +91 -40
  339. package/docs/reference/workflows.md +33 -6
  340. package/package.json +10 -6
  341. package/schemas/akm-config.json +372 -224
  342. package/schemas/akm-task.json +324 -80
  343. package/schemas/akm-workflow.json +6 -9
  344. package/dist/core/migration-operation.js +0 -75
  345. package/dist/integrations/agent/model-aliases.js +0 -74
  346. package/dist/tasks/parser.js +0 -380
  347. package/dist/tasks/schema.js +0 -123
  348. package/dist/tasks/validator.js +0 -80
  349. package/dist/workflows/ir/freeze.js +0 -320
  350. package/dist/workflows/runtime/document-cache.js +0 -13
@@ -12,11 +12,12 @@ import quick from "../../assets/improve-strategies/quick.json" with { type: "jso
12
12
  import reflectDistill from "../../assets/improve-strategies/reflect-distill.json" with { type: "json" };
13
13
  import thorough from "../../assets/improve-strategies/thorough.json" with { type: "json" };
14
14
  import { parseRefInput } from "../../core/asset/resolve-ref.js";
15
+ import { ImproveProfileConfigSchema } from "../../core/config/config-schema.js";
15
16
  import { deepMergeConfig } from "../../core/config/deep-merge.js";
16
17
  import { BUILTIN_IMPROVE_STRATEGY_NAMES, IMPROVE_PROCESS_ENGINE_CAPABILITIES, } from "../../core/config/engine-semantics.js";
17
18
  import { ConfigError } from "../../core/errors.js";
18
- import { resolveImproveProcessRunner, resolveTriageJudgmentRunner, } from "../../integrations/agent/runner.js";
19
19
  import { applyAutonomyGate } from "./autonomy-gate.js";
20
+ import { resolveImproveExecution, resolveImproveLlmExecution } from "./execution.js";
20
21
  export const DEFAULT_ALLOWED_TYPES = {
21
22
  reflect: ["agent", "command", "knowledge", "lesson", "memory", "skill", "workflow"],
22
23
  distill: ["memory"],
@@ -42,13 +43,13 @@ export function isStrategyFilteredForAllPasses(ref, strategy) {
42
43
  }
43
44
  const BUILTIN_STRATEGIES = {
44
45
  default: defaultStrategy,
45
- quick: quick,
46
- thorough: thorough,
46
+ quick,
47
+ thorough,
47
48
  "memory-focus": memoryFocus,
48
49
  "graph-refresh": graphRefresh,
49
- frequent: frequent,
50
- consolidate: consolidate,
51
- catchup: catchup,
50
+ frequent,
51
+ consolidate,
52
+ catchup,
52
53
  "reflect-distill": reflectDistill,
53
54
  "proactive-maintenance": proactiveMaintenance,
54
55
  };
@@ -63,9 +64,9 @@ export function resolveImproveStrategy(name, config) {
63
64
  throw new ConfigError(`Improve strategy "${selectedName}" not found. Valid strategies: ${valid.join(", ")}.`, "UNKNOWN_IMPROVE_STRATEGY");
64
65
  }
65
66
  const selectedStrategy = BUILTIN_STRATEGIES[selectedName] ?? {};
66
- const baseStrategy = deepMergeConfig(BUILTIN_STRATEGIES.default, selectedStrategy);
67
+ const baseStrategy = deepMergeConfig(BUILTIN_STRATEGIES.default ?? {}, selectedStrategy);
67
68
  const resolved = deepMergeConfig(baseStrategy, (userStrategies[selectedName] ?? {}));
68
- return { name: selectedName, config: resolved };
69
+ return { name: selectedName, config: ImproveProfileConfigSchema.parse(resolved) };
69
70
  }
70
71
  function cloneAndFreeze(value) {
71
72
  const clone = structuredClone(value);
@@ -95,6 +96,7 @@ function buildImprovePlan(strategy, config, options) {
95
96
  const processConfig = cloneAndFreeze(strategy.config.processes?.[processName] ?? {});
96
97
  const enabled = processConfig.enabled === true;
97
98
  let runner = null;
99
+ let notices = [];
98
100
  if (IMPROVE_PROCESS_ENGINE_CAPABILITIES[processName] !== "llm" || !enabled) {
99
101
  processes[processName] = Object.freeze({ enabled, config: processConfig, runner });
100
102
  continue;
@@ -102,21 +104,44 @@ function buildImprovePlan(strategy, config, options) {
102
104
  // Validation itself is structural and always runs. Only its optional repair
103
105
  // step needs a model, so disabling repair must not create an LLM preflight.
104
106
  if (processName !== "validation" || options.repairValidationFailures !== false) {
105
- runner = resolveImproveProcessRunner(strategy.config, processName, config);
107
+ const resolved = resolveImproveLlmExecution({
108
+ config,
109
+ profile: strategy.config,
110
+ process: processConfig,
111
+ processName,
112
+ });
113
+ runner = resolved?.runner ?? null;
114
+ notices = resolved?.notices ?? [];
106
115
  }
107
116
  if (!runner && !(processName === "validation" && options.repairValidationFailures === false)) {
108
117
  throw new ConfigError(`Enabled improve process "${processName}" requires an LLM engine. Set defaults.llmEngine or improve.strategies.${strategy.name}.processes.${processName}.engine.`, "LLM_NOT_CONFIGURED");
109
118
  }
110
119
  if (runner)
111
120
  runner = cloneAndFreeze(runner);
112
- processes[processName] = Object.freeze({ enabled, config: processConfig, runner });
121
+ processes[processName] = Object.freeze({
122
+ enabled,
123
+ config: processConfig,
124
+ runner,
125
+ ...(notices.length > 0 ? { notices: cloneAndFreeze(notices) } : {}),
126
+ });
113
127
  }
114
128
  const triage = strategy.config.processes?.triage;
115
- const judgmentOptedIn = triage !== undefined && Object.hasOwn(triage, "judgment");
116
- const triageJudgment = processes.triage.enabled && judgmentOptedIn
117
- ? resolveTriageJudgmentRunner(triage.judgment, config, triage, strategy.config)
129
+ const judgmentEnabled = triage?.judgment?.enabled === true;
130
+ const triageJudgmentResolution = processes.triage.enabled && judgmentEnabled
131
+ ? resolveImproveExecution({
132
+ config,
133
+ profile: strategy.config,
134
+ process: triage,
135
+ current: triage.judgment,
136
+ processName: "triage-judgment",
137
+ })
118
138
  : null;
119
- if (processes.triage.enabled && judgmentOptedIn && !triageJudgment) {
139
+ const triageJudgment = triageJudgmentResolution?.runner ?? null;
140
+ const effectiveJudgmentLlm = triage?.judgment?.llm ?? triage?.llm ?? strategy.config.llm;
141
+ if (triageJudgment && triageJudgment.kind !== "llm" && effectiveJudgmentLlm) {
142
+ throw new ConfigError(`Triage judgment engine "${triageJudgment.engine ?? "unknown"}" is an agent engine and cannot receive llm overrides.`, "INVALID_CONFIG_FILE");
143
+ }
144
+ if (processes.triage.enabled && judgmentEnabled && !triageJudgment) {
120
145
  throw new ConfigError(`Enabled improve triage judgment requires an engine. Set defaults.llmEngine or improve.strategies.${strategy.name}.processes.triage.judgment.engine.`, "LLM_NOT_CONFIGURED");
121
146
  }
122
147
  const frozenProcesses = Object.freeze(processes);
@@ -128,8 +153,12 @@ function buildImprovePlan(strategy, config, options) {
128
153
  }),
129
154
  });
130
155
  return Object.freeze({
156
+ config: cloneAndFreeze(config),
131
157
  strategy: frozenStrategy,
132
158
  processes: frozenProcesses,
133
159
  triageJudgment: triageJudgment ? cloneAndFreeze(triageJudgment) : null,
160
+ ...(triageJudgmentResolution?.notices.length
161
+ ? { triageJudgmentNotices: cloneAndFreeze(triageJudgmentResolution.notices) }
162
+ : {}),
134
163
  });
135
164
  }
@@ -19,13 +19,14 @@ import { beginWriteProvenance, relativeWrittenPath } from "../../core/write-prov
19
19
  import { resolveWritable, resolveWriteTarget } from "../../core/write-source.js";
20
20
  import { ensureIndex } from "../../indexer/ensure-index.js";
21
21
  import { akmIndex } from "../../indexer/indexer.js";
22
+ import { collectPendingMemories } from "../../indexer/passes/memory-inference.js";
22
23
  import { resolveEntryContentDir, resolveSourceEntries } from "../../indexer/search/search-source.js";
23
24
  import { collectEngineCredentialValues } from "../../integrations/agent/engine-resolution.js";
24
- import { materializeLlmRunnerConnection } from "../../integrations/agent/runner.js";
25
25
  import { installLlmUsagePersistence } from "../../llm/usage-persist.js";
26
26
  import { isGitBackedStash, listGitChangedPaths, resolveWritableOverride, saveGitStash, } from "../../sources/providers/git.js";
27
27
  import { closeDatabase, openExistingDatabase } from "../../storage/repositories/index-connection.js";
28
28
  import { getEntryCount } from "../../storage/repositories/index-entries-repository.js";
29
+ import { openSqliteReadSnapshot, SqliteReadSnapshotUnavailableError } from "../../storage/sqlite-read-snapshot.js";
29
30
  import { drainProposals } from "../proposal/drain.js";
30
31
  import { resolveDrainPolicy } from "../proposal/drain-policies.js";
31
32
  import { describeGatedLanes, isAutonomyLaneAllowed } from "./autonomy-gate.js";
@@ -33,11 +34,12 @@ import { akmDistill } from "./distill.js";
33
34
  // Eligibility / candidate-selection predicates live in ./eligibility.
34
35
  import { buildLatestProposalTsMap, collectEligibleRefs, collectEligibleRefsReadOnly, memoryCleanupParentRef, resolveImproveScope, shouldAnalyzeMemoryCleanup, } from "./eligibility.js";
35
36
  import { countEvalCases } from "./eval-cases.js";
36
- import { resolveImprovePlan } from "./improve-strategies.js";
37
+ import { resolveImprovePlan, resolveImproveStrategy } from "./improve-strategies.js";
37
38
  import { improveLockPath, MIN_IMPROVE_LOCK_STALE_MS, releaseImproveLock, tryAcquireImproveLock } from "./locks.js";
38
39
  // The cycle loop / post-loop / maintenance stages live in ./loop-stages.
39
40
  import { runImproveLoopStage, runImprovePostLoopStage } from "./loop-stages.js";
40
41
  import { analyzeMemoryCleanup } from "./memory/memory-improve.js";
42
+ import { buildImproveExecutionPlan } from "./planner.js";
41
43
  // The pre-loop preparation pipeline lives in ./preparation.
42
44
  import { runImprovePreparationStage } from "./preparation.js";
43
45
  import { DEFAULT_DUE_DAYS, filterProactiveDue } from "./proactive-maintenance.js";
@@ -118,12 +120,10 @@ export async function akmImprove(options = {}) {
118
120
  let initialGitPaths = new Set();
119
121
  const runJournal = createRunWriteJournal();
120
122
  const preEnsureCleanupWarnings = [];
121
- // Assigned by runIndexAndCollect() (closure) so TS cannot prove definite
122
- // assignment — seed with empty values; the runIndexAndCollect() call below
123
- // always overwrites them before any read.
124
123
  let plannedRefs = [];
125
124
  let memorySummary = { eligible: 0, derived: 0 };
126
125
  let strategyFilteredRefs = [];
126
+ let indexSnapshot;
127
127
  let memoryCleanupPlan;
128
128
  let autonomyGatedDirectLanes = [];
129
129
  let guidance;
@@ -191,12 +191,20 @@ export async function akmImprove(options = {}) {
191
191
  plannedRefs = collected.plannedRefs;
192
192
  memorySummary = collected.memorySummary;
193
193
  strategyFilteredRefs = collected.strategyFilteredRefs;
194
+ indexSnapshot = collected.indexSnapshot;
194
195
  memoryCleanupPlan = collected.memoryCleanupPlan;
195
196
  autonomyGatedDirectLanes = collected.autonomyGatedDirectLanes;
196
197
  guidance = collected.guidance;
197
198
  preEnsureCleanupWarnings.push(...collected.warnings);
198
199
  if (options.dryRun) {
199
- const result = buildDryRunResult(setup, collected);
200
+ const result = await runDryPlanningStage({
201
+ run: setup,
202
+ collected,
203
+ resolvedStateDbPath,
204
+ budgetMs,
205
+ initialCleanupWarnings: preEnsureCleanupWarnings,
206
+ signal: budgetAbortController.signal,
207
+ });
200
208
  clearBudgetTimer();
201
209
  return result;
202
210
  }
@@ -215,16 +223,9 @@ export async function akmImprove(options = {}) {
215
223
  throw err;
216
224
  }
217
225
  try {
218
- try {
219
- eventsDb = openStateDatabase(resolvedStateDbPath);
220
- eventsCtx = { db: eventsDb };
221
- }
222
- catch (err) {
223
- rethrowIfTestIsolationError(err);
224
- // If we cannot open state.db up-front, fall back to per-call opens — but
225
- // still pinned to the boundary-resolved path, never a live env re-read.
226
- eventsCtx = { dbPath: resolvedStateDbPath };
227
- }
226
+ const openedEvents = openImproveEventsContext(resolvedStateDbPath);
227
+ eventsDb = openedEvents.db;
228
+ eventsCtx = openedEvents.ctx;
228
229
  // WI-9.10: construct the run's RunContext here — the first point after
229
230
  // run-setup where config/stashDir/eventsCtx/proposalsCtx/sourceRun/dryRun
230
231
  // are all in hand. See buildImproveRunContext for exactly which
@@ -248,6 +249,8 @@ export async function akmImprove(options = {}) {
248
249
  memorySummary,
249
250
  memoryCleanupPlan,
250
251
  strategyFilteredRefs,
252
+ rawPlannedRefs: plannedRefs,
253
+ indexSnapshot,
251
254
  triageDrain,
252
255
  eventsCtx,
253
256
  });
@@ -313,6 +316,17 @@ export async function akmImprove(options = {}) {
313
316
  }
314
317
  }
315
318
  }
319
+ /** Open the run-owned state handle while preserving the boundary-pinned fallback. */
320
+ function openImproveEventsContext(dbPath) {
321
+ try {
322
+ const db = openStateDatabase(dbPath);
323
+ return { db, ctx: { db } };
324
+ }
325
+ catch (err) {
326
+ rethrowIfTestIsolationError(err);
327
+ return { ctx: { dbPath } };
328
+ }
329
+ }
316
330
  /**
317
331
  * Shape the run's journaled absolute paths for `result.writtenPaths` (#652):
318
332
  * POSIX-relative to the run's primary stash dir (the repo root for a git-backed
@@ -386,6 +400,7 @@ function resolveImproveRunSetup(options) {
386
400
  // Resolve the improve profile for this run. Profile drives type filtering,
387
401
  // process gating, and the default limit value.
388
402
  const _earlyConfig = options.config ?? loadConfig();
403
+ const configuredImproveProfile = resolveImproveStrategy(options.strategy, _earlyConfig).config;
389
404
  const resolvedPlan = options.resolvedPlan ??
390
405
  resolveImprovePlan(options.strategy, _earlyConfig, {
391
406
  repairValidationFailures: options.repairValidationFailures,
@@ -393,6 +408,14 @@ function resolveImproveRunSetup(options) {
393
408
  const selectedStrategy = resolvedPlan.strategy;
394
409
  const improveSensitiveValues = collectEngineCredentialValues(_earlyConfig);
395
410
  const improveProfile = selectedStrategy.config;
411
+ const configuredLimits = {
412
+ ...(options.limit !== undefined ? { cli: options.limit } : {}),
413
+ ...(configuredImproveProfile.limit !== undefined ? { profile: configuredImproveProfile.limit } : {}),
414
+ ...(configuredImproveProfile.processes?.reflect?.limit !== undefined
415
+ ? { reflect: configuredImproveProfile.processes.reflect.limit }
416
+ : {}),
417
+ };
418
+ const effectiveLimit = options.limit ?? improveProfile?.processes?.reflect?.limit ?? improveProfile.limit;
396
419
  const scopedRef = scope.mode === "ref" && scope.value ? parseRefInput(scope.value) : undefined;
397
420
  const readSource = options.dryRun
398
421
  ? options.writeTarget
@@ -438,7 +461,7 @@ function resolveImproveRunSetup(options) {
438
461
  },
439
462
  // Profile-level limit, then process-level reflect.limit as fallback.
440
463
  // CLI --limit takes precedence over both.
441
- limit: options.limit ?? improveProfile?.processes?.reflect?.limit ?? improveProfile.limit,
464
+ limit: effectiveLimit,
442
465
  };
443
466
  let primaryStashDir;
444
467
  try {
@@ -482,6 +505,9 @@ function resolveImproveRunSetup(options) {
482
505
  selectedStrategy,
483
506
  improveSensitiveValues,
484
507
  improveProfile,
508
+ configuredImproveProfile,
509
+ configuredLimits,
510
+ effectiveLimit,
485
511
  writeTarget,
486
512
  options,
487
513
  primaryStashDir,
@@ -515,14 +541,9 @@ function buildImproveRunContext(run, eventsCtx) {
515
541
  // Not yet wired into any proposal call site this stage — verb-level
516
542
  // RunContext adoption (reflect/distill/extract/consolidate) is later.
517
543
  proposalsCtx: { dbPath: run.resolvedStateDbPath },
518
- // Representative connection for this run: reflect is the loop's primary
519
- // LLM-driving process. Mirrors the existing
520
- // `runner ? materializeLlmRunnerConnection(runner) : null` pattern
521
- // already used for the consolidate process elsewhere (contradiction pass).
522
- getLlmConfig: () => {
523
- const runner = run.resolvedPlan.processes.reflect.runner;
524
- return runner ? materializeLlmRunnerConnection(runner) : null;
525
- },
544
+ // Representative symbolic runner for this run: reflect is the loop's
545
+ // primary model-driving process. Credentials remain unresolved here.
546
+ getLlmRunner: () => run.resolvedPlan.processes.reflect.runner,
526
547
  sourceRun: run.options.runId ?? `improve-${run.startMs}`,
527
548
  // Always false here: callers only reach this point past the dry-run
528
549
  // early return in akmImprove.
@@ -559,6 +580,7 @@ async function indexAndCollect(args) {
559
580
  let plannedRefs = [];
560
581
  let memorySummary = { eligible: 0, derived: 0 };
561
582
  let strategyFilteredRefs = [];
583
+ let indexSnapshot;
562
584
  // #339 fix: ensureIndex MUST run BEFORE collectEligibleRefs. The eligible-ref
563
585
  // query reads the `entries` table; if a DB version upgrade just dropped that
564
586
  // table (or the index is otherwise empty), the prior run order silently
@@ -622,6 +644,7 @@ async function indexAndCollect(args) {
622
644
  plannedRefs,
623
645
  memorySummary,
624
646
  strategyFilteredRefs = [],
647
+ indexSnapshot,
625
648
  } = await collectEligibleRefsImpl(scope, options.stashDir, improveProfile, _earlyConfig));
626
649
  const cleanupParentRef = memoryCleanupParentRef(scope, options.stashDir);
627
650
  // D8 — the two direct lanes share one eligibility predicate, which reads scope
@@ -646,6 +669,7 @@ async function indexAndCollect(args) {
646
669
  plannedRefs,
647
670
  memorySummary,
648
671
  strategyFilteredRefs: strategyFilteredRefs ?? [],
672
+ indexSnapshot,
649
673
  memoryCleanupPlan,
650
674
  autonomyGatedDirectLanes,
651
675
  guidance,
@@ -667,23 +691,172 @@ export function buildLockSkippedResult(strategyName, scope, runId) {
667
691
  ...(runId !== undefined ? { runId } : {}),
668
692
  };
669
693
  }
694
+ /** Evaluate the real selectors on read-only state, stopping before every writer boundary. */
695
+ async function runDryPlanningStage(args) {
696
+ const { run, collected, resolvedStateDbPath, budgetMs, initialCleanupWarnings, signal } = args;
697
+ let stateDb;
698
+ let stateSnapshotUnavailable = false;
699
+ try {
700
+ try {
701
+ stateDb = openSqliteReadSnapshot(resolvedStateDbPath);
702
+ stateSnapshotUnavailable = !stateDb;
703
+ }
704
+ catch (error) {
705
+ if (!(error instanceof SqliteReadSnapshotUnavailableError))
706
+ throw error;
707
+ stateSnapshotUnavailable = true;
708
+ }
709
+ const eventsCtx = {
710
+ ...(stateDb ? { db: stateDb } : { dbPath: resolvedStateDbPath }),
711
+ readOnly: true,
712
+ ...(stateSnapshotUnavailable ? { readOnlySnapshotUnavailable: true } : {}),
713
+ };
714
+ const preparation = await run.runImprovePreparationStageImpl({
715
+ scope: run.scope,
716
+ options: run.options,
717
+ plannedRefs: collected.plannedRefs,
718
+ memoryCleanupPlan: collected.memoryCleanupPlan,
719
+ primaryStashDir: run.primaryStashDir,
720
+ memorySummary: collected.memorySummary,
721
+ reindexFn: run.reindexFn,
722
+ startMs: run.startMs,
723
+ budgetMs,
724
+ eventsCtx,
725
+ initialCleanupWarnings,
726
+ improveProfile: run.improveProfile,
727
+ resolvedPlan: run.resolvedPlan,
728
+ strategyName: run.selectedStrategy.name,
729
+ budgetSignal: signal,
730
+ planOnly: true,
731
+ });
732
+ return buildDryRunResult(run, collected, preparation);
733
+ }
734
+ finally {
735
+ stateDb?.close();
736
+ }
737
+ }
670
738
  /** The P3 dry-run envelope (plan-only early return). Exported for unit tests. */
671
- export function buildDryRunResult(run, collected) {
739
+ export function buildDryRunResult(run, collected, preparation) {
672
740
  const { selectedStrategy, scope } = run;
673
741
  const { guidance, memorySummary, memoryCleanupPlan, plannedRefs, strategyFilteredRefs } = collected;
742
+ const effectiveRefs = preparation?.loopRefs ?? plannedRefs;
743
+ const notices = collectImproveNotices({ resolvedPlan: run.resolvedPlan });
674
744
  return {
675
745
  schemaVersion: 2,
676
746
  ok: true,
677
747
  strategy: selectedStrategy.name,
678
748
  scope,
679
749
  dryRun: true,
750
+ ...(notices.length > 0 ? { notices } : {}),
680
751
  ...(guidance ? { guidance } : {}),
681
752
  memorySummary,
682
753
  ...(memoryCleanupPlan ? { memoryCleanup: shapeMemoryCleanup(memoryCleanupPlan) } : {}),
683
- plannedRefs,
754
+ plannedRefs: effectiveRefs,
755
+ ...(preparation?.planning
756
+ ? {
757
+ plan: buildResultExecutionPlan(run, preparation, plannedRefs, strategyFilteredRefs, collected.indexSnapshot, true),
758
+ }
759
+ : {}),
684
760
  ...(strategyFilteredRefs.length > 0 ? { strategyFilteredRefs } : {}),
761
+ ...(preparation?.proactiveMaintenance ? { proactiveMaintenance: preparation.proactiveMaintenance } : {}),
685
762
  };
686
763
  }
764
+ /** One public plan projection for the dry and live result assemblers. */
765
+ function buildResultExecutionPlan(run, preparation, rawProfileEligibleRefs, strategyFilteredRefs, indexSnapshot, dryRun) {
766
+ const { improveProfile, configuredImproveProfile, resolvedPlan, configuredLimits, effectiveLimit, scope } = run;
767
+ const triageConfig = improveProfile.processes?.triage;
768
+ const configuredTriage = configuredImproveProfile.processes?.triage;
769
+ const configuredProactive = configuredImproveProfile.processes?.proactiveMaintenance;
770
+ const configuredConsolidation = configuredImproveProfile.processes?.consolidate;
771
+ const triageEnabled = scope.mode !== "ref" && resolvedPlan.processes.triage.enabled;
772
+ const distillOnlySet = new Set(preparation.distillOnlyRefs.map((entry) => entry.ref));
773
+ const inferenceMinPending = improveProfile.processes?.memoryInference?.minPendingCount;
774
+ const pendingMemories = run.primaryStashDir && inferenceMinPending !== undefined && inferenceMinPending > 0
775
+ ? collectPendingMemories(run.primaryStashDir).length
776
+ : undefined;
777
+ const memoryInferenceEnabled = resolvedPlan.processes.memoryInference.enabled &&
778
+ !(pendingMemories !== undefined && inferenceMinPending !== undefined && pendingMemories < inferenceMinPending);
779
+ const graphExtractionEnabled = resolvedPlan.processes.graphExtraction.enabled && run.primaryStashDir !== undefined;
780
+ const profileGate = {
781
+ name: "profile",
782
+ removed: strategyFilteredRefs.length,
783
+ reason: "all enabled per-ref processes refuse the asset type",
784
+ };
785
+ const proactive = preparation.planning.proactive
786
+ ? {
787
+ ...preparation.planning.proactive,
788
+ configured: {
789
+ ...(configuredProactive?.dueDays !== undefined ? { dueDays: configuredProactive.dueDays } : {}),
790
+ ...(configuredProactive?.maxPerRun !== undefined ? { maxPerRun: configuredProactive.maxPerRun } : {}),
791
+ ...(configuredProactive?.limit !== undefined ? { limit: configuredProactive.limit } : {}),
792
+ },
793
+ }
794
+ : undefined;
795
+ const consolidation = {
796
+ ...preparation.planning.consolidation,
797
+ configured: {
798
+ ...(configuredConsolidation?.enabled !== undefined ? { enabled: configuredConsolidation.enabled } : {}),
799
+ ...(configuredConsolidation?.minPoolSize !== undefined
800
+ ? { minPoolSize: configuredConsolidation.minPoolSize }
801
+ : {}),
802
+ ...(configuredConsolidation?.limit !== undefined ? { limit: configuredConsolidation.limit } : {}),
803
+ ...(configuredConsolidation?.maxChunkSize !== undefined
804
+ ? { maxChunkSize: configuredConsolidation.maxChunkSize }
805
+ : {}),
806
+ ...(configuredConsolidation?.incrementalSince !== undefined
807
+ ? { incrementalSince: configuredConsolidation.incrementalSince }
808
+ : {}),
809
+ },
810
+ };
811
+ return buildImproveExecutionPlan({
812
+ dryRun,
813
+ snapshot: indexSnapshot ?? {
814
+ status: "unknown",
815
+ reason: "the injected selector did not report an index snapshot status",
816
+ },
817
+ rawInScope: rawProfileEligibleRefs.length + strategyFilteredRefs.length,
818
+ selectedRefs: preparation.actionableRefs,
819
+ effectiveRefs: preparation.loopRefs,
820
+ distillOnlyRefs: distillOnlySet,
821
+ configuredLimits,
822
+ effectiveLimit,
823
+ replayBudget: preparation.planning.replayBudget,
824
+ gates: [profileGate, ...preparation.planning.gates],
825
+ ...(proactive ? { proactive } : {}),
826
+ consolidation,
827
+ stageConfig: {
828
+ extract: {
829
+ enabled: preparation.planning.extract.wouldRun,
830
+ reason: preparation.planning.extract.reason,
831
+ },
832
+ graphExtraction: {
833
+ enabled: graphExtractionEnabled,
834
+ reason: !resolvedPlan.processes.graphExtraction.enabled
835
+ ? "disabled"
836
+ : graphExtractionEnabled
837
+ ? improveProfile.processes?.graphExtraction?.fullScan === true
838
+ ? "enabled for a full-corpus scan"
839
+ : "enabled for refs touched by this run"
840
+ : "enabled but no primary source is available",
841
+ },
842
+ memoryInference: {
843
+ enabled: memoryInferenceEnabled,
844
+ reason: !resolvedPlan.processes.memoryInference.enabled
845
+ ? "disabled"
846
+ : !memoryInferenceEnabled
847
+ ? `${pendingMemories ?? 0} pending memories is below minPendingCount ${inferenceMinPending}`
848
+ : "enabled; the pass discovers pending memories independently of selected refs",
849
+ },
850
+ },
851
+ triage: {
852
+ enabled: triageEnabled,
853
+ configuredMode: configuredTriage?.applyMode ?? "queue",
854
+ mode: triageConfig?.applyMode ?? "queue",
855
+ maxAcceptsPerRun: configuredTriage?.maxAcceptsPerRun ?? 25,
856
+ ...(configuredTriage?.maxDiffLines !== undefined ? { maxDiffLines: configuredTriage.maxDiffLines } : {}),
857
+ },
858
+ });
859
+ }
687
860
  /** The triage drain pre-pass (non-fatal; single-ref scope never drains). */
688
861
  async function runTriagePrePass(run) {
689
862
  const { primaryStashDir, resolvedPlan, scope, options, improveProfile, drainProposalsFn } = run;
@@ -1088,7 +1261,7 @@ async function runImproveStageSequence(args) {
1088
1261
  * side effects run between assembly and emit).
1089
1262
  */
1090
1263
  function finalizeImproveResult(args) {
1091
- const { guidance, memorySummary, memoryCleanupPlan, strategyFilteredRefs, triageDrain, eventsCtx } = args;
1264
+ const { guidance, memorySummary, memoryCleanupPlan, strategyFilteredRefs, rawPlannedRefs, indexSnapshot, triageDrain, eventsCtx, } = args;
1092
1265
  const { selectedStrategy, scope, options, primaryStashDir, startMs } = args.run;
1093
1266
  const { preparation, consolidation, memoryInference, graphExtraction, cycleMetrics, reflectsWithErrorContext, memoryInferenceDurationMs, graphExtractionDurationMs, orphansPurged, proposalsExpired, allWarnings, deadUrls, finalActions, } = args.seq;
1094
1267
  // C1 (13-bus-factor): fold the per-ref `distill-skipped` rows (~13k/run,
@@ -1097,12 +1270,23 @@ function finalizeImproveResult(args) {
1097
1270
  // the unbounded row list never reaches result_json. Reflect skip counters
1098
1271
  // below still read `finalActions` (reflect skips are not folded).
1099
1272
  const { actions: persistedActions, aggregate: distillSkippedAggregate } = foldDistillSkipped(finalActions);
1273
+ const notices = collectImproveNotices({
1274
+ resolvedPlan: args.run.resolvedPlan,
1275
+ actions: finalActions,
1276
+ schemaRepairs: preparation.schemaRepairs,
1277
+ consolidation,
1278
+ extract: preparation.extract,
1279
+ memoryInference,
1280
+ graphExtraction,
1281
+ triageDrain,
1282
+ });
1100
1283
  const result = {
1101
1284
  schemaVersion: 2,
1102
1285
  ok: true,
1103
1286
  strategy: selectedStrategy.name,
1104
1287
  scope,
1105
1288
  dryRun: false,
1289
+ ...(notices.length > 0 ? { notices } : {}),
1106
1290
  ...(guidance ? { guidance } : {}),
1107
1291
  memorySummary,
1108
1292
  ...(memoryCleanupPlan
@@ -1126,7 +1310,12 @@ function finalizeImproveResult(args) {
1126
1310
  },
1127
1311
  }
1128
1312
  : {}),
1129
- plannedRefs: preparation.actionableRefs,
1313
+ plannedRefs: preparation.loopRefs,
1314
+ ...(preparation.planning
1315
+ ? {
1316
+ plan: buildResultExecutionPlan(args.run, preparation, rawPlannedRefs, strategyFilteredRefs, indexSnapshot, false),
1317
+ }
1318
+ : {}),
1130
1319
  ...(strategyFilteredRefs.length > 0 ? { strategyFilteredRefs } : {}),
1131
1320
  actions: persistedActions,
1132
1321
  ...(distillSkippedAggregate ? { distillSkipped: distillSkippedAggregate } : {}),
@@ -1185,6 +1374,37 @@ function finalizeImproveResult(args) {
1185
1374
  }, eventsCtx);
1186
1375
  return result;
1187
1376
  }
1377
+ function collectImproveNotices(args) {
1378
+ const byKey = new Map();
1379
+ const collect = (carrier) => {
1380
+ if (typeof carrier !== "object" || carrier === null)
1381
+ return;
1382
+ const notices = carrier.notices;
1383
+ if (!Array.isArray(notices))
1384
+ return;
1385
+ for (const notice of notices)
1386
+ byKey.set(JSON.stringify(notice), notice);
1387
+ };
1388
+ for (const process of Object.values(args.resolvedPlan.processes))
1389
+ collect(process);
1390
+ for (const notice of args.resolvedPlan.triageJudgmentNotices ?? []) {
1391
+ byKey.set(JSON.stringify(notice), notice);
1392
+ }
1393
+ for (const action of args.actions ?? [])
1394
+ collect(action.result);
1395
+ for (const repair of args.schemaRepairs ?? [])
1396
+ collect(repair);
1397
+ collect(args.consolidation);
1398
+ for (const extract of args.extract ?? []) {
1399
+ collect(extract);
1400
+ for (const session of extract.sessions ?? [])
1401
+ collect(session);
1402
+ }
1403
+ collect(args.memoryInference);
1404
+ collect(args.graphExtraction);
1405
+ collect(args.triageDrain);
1406
+ return Object.freeze([...byKey.values()]);
1407
+ }
1188
1408
  function emitImproveCompletedEvent(result, durations, eventsCtx) {
1189
1409
  const actionCounts = {
1190
1410
  reflect: 0,
@@ -14,9 +14,9 @@ import { withStateDb } from "../../core/state-db.js";
14
14
  import { info } from "../../core/warn.js";
15
15
  import { DEFAULT_GRAPH_EXTRACTION_INCLUDE_TYPES, runGraphExtractionPass, } from "../../indexer/graph/graph-extraction.js";
16
16
  import { withIndexWriterLease } from "../../indexer/index-writer-lock.js";
17
+ import { deriveWritableBundleIds } from "../../indexer/installations.js";
17
18
  import { collectPendingMemories, runMemoryInferencePass, } from "../../indexer/passes/memory-inference.js";
18
- import { getWritableStashDirs, resolveSourceEntries } from "../../indexer/search/search-source.js";
19
- import { materializeLlmRunnerConnection } from "../../integrations/agent/runner.js";
19
+ import { resolveSourceEntries } from "../../indexer/search/search-source.js";
20
20
  import { isProcessEnabled } from "../../llm/feature-gate.js";
21
21
  import { withLlmStage } from "../../llm/usage-telemetry.js";
22
22
  import { purgeOldCycleMetrics } from "../../storage/repositories/canaries-repository.js";
@@ -179,8 +179,9 @@ async function runLoopReflectPass(planned, isDistillOnly, env, tally) {
179
179
  // O-1 (#364): pass remaining budget as timeoutMs so the agent spawn is
180
180
  // bounded by the wall-clock deadline rather than the default per-profile timeout.
181
181
  const reflectBudgetMs = env.remainingBudgetMs();
182
- // Use the runner frozen in the invocation plan; no leaf re-resolution.
183
- const reflectProfileRunner = resolvedPlan.processes.reflect.runner;
182
+ // Re-enter canonical named-engine lowering with the config snapshot and
183
+ // process profile frozen into the invocation plan. The loop never injects
184
+ // a RunnerSpec seam or observes later caller-config mutations.
184
185
  const reflectCallArgs = {
185
186
  ref: planned.ref,
186
187
  // Carry the resolved item_ref so reflect uses the same durable key for
@@ -189,7 +190,7 @@ async function runLoopReflectPass(planned, isDistillOnly, env, tally) {
189
190
  task: options.task,
190
191
  // Active strategy supplies non-engine process tuning.
191
192
  ...(improveProfile ? { improveProfile } : {}),
192
- config: options.config,
193
+ config: resolvedPlan.config,
193
194
  ...(primaryStashDir ? { stashDir: primaryStashDir } : {}),
194
195
  ...(options.sourceName && primaryStashDir
195
196
  ? { target: { source: options.sourceName, root: primaryStashDir } }
@@ -201,7 +202,6 @@ async function runLoopReflectPass(planned, isDistillOnly, env, tally) {
201
202
  lowValueFilter: improveProfile.processes?.reflect?.lowValueFilter?.enabled === true,
202
203
  ...(reflectBudgetMs > 0 ? { timeoutMs: reflectBudgetMs } : {}),
203
204
  signal: budgetSignal,
204
- runner: reflectProfileRunner ?? null,
205
205
  // R25: reflect's event emits reuse the run's long-lived state.db handle.
206
206
  eventsCtx: env.eventsCtx,
207
207
  // Attribution: carry the eligibility lane so reflect stamps it on
@@ -437,9 +437,7 @@ async function invokeDistillAndRecord(planned, parsedPlannedRef, env, tally) {
437
437
  // Active profile so distill's per-process reads honor `--profile`.
438
438
  ...(improveProfile ? { improveProfile } : {}),
439
439
  config: options.config,
440
- llmConfig: resolvedPlan.processes.distill.runner
441
- ? materializeLlmRunnerConnection(resolvedPlan.processes.distill.runner)
442
- : null,
440
+ llmRunner: resolvedPlan.processes.distill.runner,
443
441
  signal: budgetSignal,
444
442
  // R25: distill's event emits reuse the run's long-lived state.db handle.
445
443
  eventsCtx,
@@ -814,9 +812,7 @@ export async function runMemoryInferenceMaintenancePass(ctx, dbCell, memoryRefsF
814
812
  config,
815
813
  ...(resolvedPlan
816
814
  ? {
817
- llmConfig: resolvedPlan.processes.memoryInference.runner
818
- ? materializeLlmRunnerConnection(resolvedPlan.processes.memoryInference.runner)
819
- : null,
815
+ llmRunner: resolvedPlan.processes.memoryInference.runner,
820
816
  }
821
817
  : {}),
822
818
  sources,
@@ -907,8 +903,8 @@ export async function runGraphExtractionMaintenancePass(ctx, dbCell, args) {
907
903
  if (!graphExtractionFullScan) {
908
904
  candidatePaths = new Set();
909
905
  if (primaryStashDir && touchedRefs.size > 0) {
910
- const writableDirSet = new Set(getWritableStashDirs(primaryStashDir).map((d) => path.resolve(d)));
911
- const resolved = await Promise.all([...touchedRefs].map((ref) => findAssetFilePath(ref, primaryStashDir, writableDirSet).catch(() => null)));
906
+ const writableBundleIds = deriveWritableBundleIds(resolveSourceEntries(primaryStashDir));
907
+ const resolved = await Promise.all([...touchedRefs].map((ref) => findAssetFilePath(ref, primaryStashDir, writableBundleIds).catch(() => null)));
912
908
  for (const p of resolved) {
913
909
  if (typeof p === "string" && p.length > 0)
914
910
  candidatePaths.add(p);
@@ -924,9 +920,7 @@ export async function runGraphExtractionMaintenancePass(ctx, dbCell, args) {
924
920
  config,
925
921
  ...(resolvedPlan
926
922
  ? {
927
- llmConfig: resolvedPlan.processes.graphExtraction.runner
928
- ? materializeLlmRunnerConnection(resolvedPlan.processes.graphExtraction.runner)
929
- : null,
923
+ llmRunner: resolvedPlan.processes.graphExtraction.runner,
930
924
  }
931
925
  : {}),
932
926
  sources,