akm-cli 0.9.17-alpha.2 → 0.9.17-alpha.4

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 (343) hide show
  1. package/CHANGELOG.md +756 -0
  2. package/dist/akm +94 -196
  3. package/dist/cli/shared.js +6 -2
  4. package/dist/cli.js +22 -9
  5. package/dist/commands/agent/agent-dispatch.js +1 -1
  6. package/dist/commands/command/command-execution.js +24 -62
  7. package/dist/commands/feedback-cli.js +0 -1
  8. package/dist/commands/health/accept-rate.js +2 -2
  9. package/dist/commands/health/checks.js +30 -75
  10. package/dist/commands/health/config-skew.js +38 -0
  11. package/dist/commands/health/egress.js +54 -0
  12. package/dist/commands/health/html-report.js +0 -38
  13. package/dist/commands/health/improve-metrics.js +123 -562
  14. package/dist/commands/health/plugin-staleness.js +53 -3
  15. package/dist/commands/health/renderers.js +12 -4
  16. package/dist/commands/health/report-view-model.js +11 -106
  17. package/dist/commands/health/types-improve.js +4 -19
  18. package/dist/commands/health/windows.js +64 -73
  19. package/dist/commands/health.js +122 -143
  20. package/dist/commands/improve/consolidate/chunking.js +25 -100
  21. package/dist/commands/improve/consolidate/sanitize.js +54 -149
  22. package/dist/commands/improve/consolidate.js +538 -1075
  23. package/dist/commands/improve/content-hash.js +16 -24
  24. package/dist/commands/improve/distill/content-repair.js +18 -100
  25. package/dist/commands/improve/distill-guards.js +20 -81
  26. package/dist/commands/improve/distill-promotion-policy.js +23 -243
  27. package/dist/commands/improve/distill.js +608 -1075
  28. package/dist/commands/improve/eligibility.js +126 -400
  29. package/dist/commands/improve/execution.js +3 -5
  30. package/dist/commands/improve/extract.js +487 -1046
  31. package/dist/commands/improve/feedback-valence.js +0 -25
  32. package/dist/commands/improve/improve-cli.js +29 -166
  33. package/dist/commands/improve/improve-result-file.js +10 -66
  34. package/dist/commands/improve/improve-strategies.js +12 -7
  35. package/dist/commands/improve/improve-usage-report.js +18 -64
  36. package/dist/commands/improve/improve.js +443 -1063
  37. package/dist/commands/improve/ledger.js +114 -0
  38. package/dist/commands/improve/locks.js +2 -8
  39. package/dist/commands/improve/loop-stages.js +459 -1172
  40. package/dist/commands/improve/memory/derived-ref.js +12 -77
  41. package/dist/commands/improve/memory/memory-belief.js +14 -118
  42. package/dist/commands/improve/memory/memory-improve.js +4 -3
  43. package/dist/commands/improve/outcome-loop.js +28 -156
  44. package/dist/commands/improve/planner.js +5 -10
  45. package/dist/commands/improve/preparation.js +851 -2339
  46. package/dist/commands/improve/proactive-maintenance.js +34 -101
  47. package/dist/commands/improve/reflect-noise.js +104 -280
  48. package/dist/commands/improve/reflect.js +621 -1367
  49. package/dist/commands/improve/salience.js +46 -232
  50. package/dist/commands/improve/session-asset.js +19 -100
  51. package/dist/commands/improve/stage.js +323 -0
  52. package/dist/commands/proposal/drain.js +251 -644
  53. package/dist/commands/proposal/proposal-cli.js +3 -18
  54. package/dist/commands/proposal/proposal-types.js +20 -41
  55. package/dist/commands/proposal/proposal.js +1 -2
  56. package/dist/commands/proposal/propose.js +134 -160
  57. package/dist/commands/proposal/repository.js +502 -1487
  58. package/dist/commands/proposal/validators/proposal-quality-validators.js +71 -174
  59. package/dist/commands/proposal/validators/proposal-validators.js +1 -1
  60. package/dist/commands/proposal/validators/proposals.js +13 -89
  61. package/dist/commands/read/curate.js +63 -413
  62. package/dist/commands/read/search-cli.js +16 -33
  63. package/dist/commands/read/search.js +17 -23
  64. package/dist/commands/read/show.js +2 -13
  65. package/dist/commands/sources/bundle-cli.js +25 -2
  66. package/dist/commands/sources/bundle-config-ops.js +7 -0
  67. package/dist/commands/sources/dangerous-env-audit.js +1 -2
  68. package/dist/commands/sources/info.js +2 -11
  69. package/dist/commands/sources/installed-stashes.js +197 -746
  70. package/dist/commands/sources/schema-repair.js +98 -129
  71. package/dist/commands/sources/source-add.js +62 -12
  72. package/dist/commands/sources/stash-cli.js +1 -1
  73. package/dist/commands/tasks/explain.js +10 -13
  74. package/dist/commands/tasks/tasks-cli.js +9 -8
  75. package/dist/commands/tasks/tasks.js +326 -930
  76. package/dist/commands/tasks/validate.js +42 -21
  77. package/dist/commands/workflow/plan.js +22 -29
  78. package/dist/commands/workflow-cli.js +4 -4
  79. package/dist/core/adapter/adapters/akm-adapter.js +0 -1
  80. package/dist/core/adapter/adapters/akm-lint.js +2 -3
  81. package/dist/core/adapter/adapters/akm-metadata.js +11 -12
  82. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  83. package/dist/core/adapter/execution-source.js +17 -29
  84. package/dist/core/asset/resolve-ref.js +1 -1
  85. package/dist/core/bundle-id.js +42 -5
  86. package/dist/core/bundle-rename.js +291 -0
  87. package/dist/core/config/config-io.js +1 -2
  88. package/dist/core/config/config-schema.js +1 -33
  89. package/dist/core/config/config-walker.js +1 -1
  90. package/dist/core/config/config.js +163 -68
  91. package/dist/core/config/legacy-source-shape-shim.js +38 -9
  92. package/dist/core/config/schema/embedding.js +20 -5
  93. package/dist/core/config/schema/engines.js +5 -0
  94. package/dist/core/config/schema/execution.js +1 -1
  95. package/dist/core/config/schema/experimental.js +1 -1
  96. package/dist/core/config/schema/improve-processes.js +21 -95
  97. package/dist/core/config/schema/improve.js +4 -42
  98. package/dist/core/config/schema/scheduler.js +12 -12
  99. package/dist/core/config/schema/search.js +6 -22
  100. package/dist/core/env-secret-ref.js +0 -1
  101. package/dist/core/errors.js +8 -9
  102. package/dist/core/file-lock.js +76 -173
  103. package/dist/core/logs-db.js +2 -2
  104. package/dist/core/paths.js +0 -27
  105. package/dist/core/redaction.js +109 -2
  106. package/dist/core/run-lock.js +2 -5
  107. package/dist/core/spawn-env.js +1 -1
  108. package/dist/core/state/migrations.js +108 -61
  109. package/dist/core/state-db-scope.js +2 -4
  110. package/dist/core/state-db.js +126 -692
  111. package/dist/core/type-presentation.js +1 -9
  112. package/dist/core/write-source.js +293 -1012
  113. package/dist/execution/input-contract.js +1 -1
  114. package/dist/execution/resolved-request.js +135 -689
  115. package/dist/execution/source.js +63 -257
  116. package/dist/execution/target-ref.js +1 -1
  117. package/dist/indexer/bundle-identity-guard.js +2 -2
  118. package/dist/indexer/db/graph-db.js +106 -46
  119. package/dist/indexer/ensure-index.js +44 -85
  120. package/dist/indexer/graph/graph-extraction.js +340 -562
  121. package/dist/indexer/graph/graph-related.js +130 -0
  122. package/dist/indexer/index-rebuild-lock.js +3 -11
  123. package/dist/indexer/index-writer-lock.js +8 -17
  124. package/dist/indexer/index-written-assets.js +139 -151
  125. package/dist/indexer/indexer.js +524 -846
  126. package/dist/indexer/materialize-embeddings.js +60 -397
  127. package/dist/indexer/passes/memory-inference.js +81 -90
  128. package/dist/indexer/passes/metadata.js +132 -200
  129. package/dist/indexer/read-preflight.js +0 -7
  130. package/dist/indexer/scan/doc-to-entry.js +1 -3
  131. package/dist/indexer/scan/drain-dir.js +1 -1
  132. package/dist/indexer/search/db-search.js +181 -590
  133. package/dist/indexer/search/fts-query.js +30 -41
  134. package/dist/indexer/search/ranking.js +28 -154
  135. package/dist/indexer/search/search-attribution.js +12 -32
  136. package/dist/indexer/search/search-fields.js +11 -15
  137. package/dist/indexer/search/search-hit-enrichers.js +54 -85
  138. package/dist/indexer/search/search-source.js +1 -4
  139. package/dist/indexer/usage/usage-events.js +2 -7
  140. package/dist/integrations/agent/engine-fallback.js +23 -40
  141. package/dist/integrations/agent/engine-resolution.js +93 -183
  142. package/dist/integrations/agent/execution.js +507 -0
  143. package/dist/integrations/agent/model-map.js +28 -156
  144. package/dist/integrations/agent/request-lowering.js +66 -141
  145. package/dist/integrations/agent/runner-dispatch.js +143 -321
  146. package/dist/integrations/agent/runner.js +54 -14
  147. package/dist/integrations/lockfile.js +53 -101
  148. package/dist/llm/embedders/deterministic.js +2 -3
  149. package/dist/llm/embedders/profile.js +71 -0
  150. package/dist/llm/embedders/remote.js +10 -15
  151. package/dist/llm/graph-extract.js +3 -12
  152. package/dist/llm/index-passes.js +3 -5
  153. package/dist/llm/memory-infer.js +1 -2
  154. package/dist/llm/metadata-enhance.js +1 -2
  155. package/dist/llm/structured-call.js +5 -24
  156. package/dist/output/generic-render.js +23 -11
  157. package/dist/output/html-render.js +13 -10
  158. package/dist/output/render-registry.js +3 -32
  159. package/dist/output/shapes/helpers.js +2 -34
  160. package/dist/output/shapes/passthrough.js +1 -9
  161. package/dist/{indexer/search/ranking-types.js → output/text/bundle-rename.js} +4 -1
  162. package/dist/output/text/command-format.js +60 -23
  163. package/dist/output/text/helpers.js +1 -1
  164. package/dist/output/text/migrate.js +5 -14
  165. package/dist/output/text/proposal-format.js +1 -2
  166. package/dist/output/text/workflow-format.js +0 -32
  167. package/dist/output/text.js +2 -0
  168. package/dist/registry/factory.js +4 -19
  169. package/dist/registry/network.js +66 -220
  170. package/dist/registry/providers/index.js +0 -2
  171. package/dist/registry/providers/skills-sh.js +3 -14
  172. package/dist/registry/providers/static-index.js +24 -26
  173. package/dist/registry/resolve.js +55 -131
  174. package/dist/scripts/akm-migrate-node.js +43937 -93313
  175. package/dist/scripts/akm-migrate.js +43697 -93071
  176. package/dist/setup/registry-stash-loader.js +4 -13
  177. package/dist/setup/semantic-assets.js +3 -44
  178. package/dist/setup/setup.js +1 -1
  179. package/dist/setup/steps/tasks.js +25 -15
  180. package/dist/sources/provider-factory.js +17 -18
  181. package/dist/sources/providers/filesystem.js +2 -3
  182. package/dist/sources/providers/git-install.js +7 -1
  183. package/dist/sources/providers/git-provider.js +0 -3
  184. package/dist/sources/providers/git-stash.js +0 -17
  185. package/dist/sources/providers/npm.js +2 -4
  186. package/dist/sources/providers/provider-utils.js +5 -10
  187. package/dist/sources/providers/website.js +0 -2
  188. package/dist/sources/snapshot-fetchers/website-ingest.js +1 -1
  189. package/dist/sources/website-url.js +2 -2
  190. package/dist/storage/database.js +9 -35
  191. package/dist/storage/repositories/improve-ledger-repository.js +168 -0
  192. package/dist/storage/repositories/index-connection.js +34 -70
  193. package/dist/storage/repositories/index-entries-repository.js +69 -111
  194. package/dist/storage/repositories/index-entry-mapper.js +1 -2
  195. package/dist/storage/repositories/index-entry-schema.js +83 -269
  196. package/dist/storage/repositories/index-fts-repository.js +86 -256
  197. package/dist/storage/repositories/index-llm-cache-repository.js +17 -0
  198. package/dist/storage/repositories/index-meta-repository.js +6 -4
  199. package/dist/storage/repositories/index-schema.js +192 -220
  200. package/dist/storage/repositories/index-utility-repository.js +8 -29
  201. package/dist/storage/repositories/index-vec-repository.js +133 -414
  202. package/dist/storage/repositories/outcome-repository.js +2 -1
  203. package/dist/storage/repositories/proposals-repository.js +35 -0
  204. package/dist/storage/repositories/registry-index-cache-repository.js +100 -0
  205. package/dist/storage/repositories/task-history-repository.js +26 -4
  206. package/dist/storage/repositories/workflow-runs-repository.js +53 -244
  207. package/dist/storage/sqlite-migrations.js +136 -0
  208. package/dist/storage/sqlite-pragmas.js +11 -9
  209. package/dist/storage/sqlite-transaction.js +170 -0
  210. package/dist/storage/state-db-integrity.js +34 -27
  211. package/dist/tasks/activation-config.js +134 -62
  212. package/dist/tasks/backends/cron.js +129 -277
  213. package/dist/tasks/backends/exec-utils.js +2 -5
  214. package/dist/tasks/backends/launchd.js +125 -745
  215. package/dist/tasks/backends/schtasks.js +101 -620
  216. package/dist/tasks/prepare/prepare-support.js +5 -15
  217. package/dist/tasks/prepare/prepare.js +0 -2
  218. package/dist/tasks/resolve-akm-bin.js +20 -79
  219. package/dist/tasks/run/attempt-lifecycle.js +0 -1
  220. package/dist/tasks/scheduler-binding.js +18 -238
  221. package/dist/tasks/scheduler-invocation.js +52 -52
  222. package/dist/tasks/scheduler-lock.js +53 -0
  223. package/dist/tasks/scheduler-sync.js +363 -679
  224. package/dist/tasks/source/parse-task-source.js +160 -10
  225. package/dist/tasks/source/task-source-v3-frozen.js +3 -4
  226. package/dist/tasks/source/task-to-v4.js +2 -2
  227. package/dist/workflows/authoring/authoring.js +3 -12
  228. package/dist/workflows/compile.js +211 -0
  229. package/dist/workflows/concurrency-policy.js +13 -74
  230. package/dist/workflows/exec/child-invocation.js +3 -17
  231. package/dist/workflows/exec/child-workflow.js +32 -141
  232. package/dist/workflows/exec/dispatch-redaction.js +13 -53
  233. package/dist/workflows/exec/environment.js +98 -0
  234. package/dist/workflows/exec/exec-unit.js +33 -140
  235. package/dist/workflows/exec/frozen-judge.js +7 -59
  236. package/dist/workflows/exec/native-executor.js +82 -341
  237. package/dist/workflows/exec/param-secrets.js +29 -47
  238. package/dist/workflows/exec/run-workflow.js +154 -387
  239. package/dist/workflows/exec/scheduler.js +9 -36
  240. package/dist/workflows/exec/step-work.js +127 -430
  241. package/dist/workflows/exec/unit-dispatch.js +11 -63
  242. package/dist/workflows/exec/unit-writer.js +8 -52
  243. package/dist/workflows/exec/worktree.js +39 -273
  244. package/dist/workflows/freeze/child-output-references.js +4 -15
  245. package/dist/workflows/freeze/environment.js +99 -92
  246. package/dist/workflows/freeze/freeze.js +172 -0
  247. package/dist/workflows/freeze/step-values.js +19 -21
  248. package/dist/workflows/freeze/targets/child-workflow.js +23 -92
  249. package/dist/workflows/freeze/targets/command.js +10 -33
  250. package/dist/workflows/freeze/targets/script.js +5 -12
  251. package/dist/workflows/freeze/targets/shell.js +3 -6
  252. package/dist/workflows/freeze/targets/task.js +25 -80
  253. package/dist/workflows/freeze/task-bindings.js +20 -67
  254. package/dist/workflows/{source-ir/github-yaml.js → github-yaml.js} +88 -206
  255. package/dist/workflows/ir/params.js +6 -51
  256. package/dist/workflows/ir/plan-hash.js +2 -34
  257. package/dist/workflows/parser.js +140 -43
  258. package/dist/{commands/improve/consolidate/types.js → workflows/plan.js} +2 -1
  259. package/dist/workflows/renderer.js +36 -69
  260. package/dist/workflows/resource-limits.js +12 -120
  261. package/dist/workflows/runtime/agent-identity.js +8 -40
  262. package/dist/workflows/runtime/run-outputs.js +3 -6
  263. package/dist/workflows/runtime/run-plan.js +316 -0
  264. package/dist/workflows/runtime/runs.js +48 -200
  265. package/dist/workflows/runtime/workflow-asset-loader.js +24 -57
  266. package/dist/workflows/{source-ir/semantics.js → source-semantics.js} +16 -20
  267. package/dist/workflows/validate-summary.js +2 -7
  268. package/docs/integration/bundling-akm.md +49 -42
  269. package/docs/migration/README.md +1 -0
  270. package/docs/migration/release-notes/0.9.17.md +41 -0
  271. package/docs/migration/v0.9.1-to-v0.9.2.md +19 -7
  272. package/docs/reference/cli.md +182 -125
  273. package/docs/reference/configuration.md +49 -56
  274. package/docs/reference/data-and-telemetry.md +19 -20
  275. package/docs/reference/tasks.md +86 -38
  276. package/docs/reference/workflow-schema.md +14 -18
  277. package/docs/reference/workflows.md +6 -9
  278. package/package.json +1 -1
  279. package/schemas/akm-config.json +87 -406
  280. package/dist/commands/health/advisories.js +0 -150
  281. package/dist/commands/health/metrics.js +0 -329
  282. package/dist/commands/health/surfaces.js +0 -102
  283. package/dist/commands/improve/anti-collapse.js +0 -83
  284. package/dist/commands/improve/collapse-detector.js +0 -432
  285. package/dist/commands/improve/consolidate/eligibility.js +0 -48
  286. package/dist/commands/improve/consolidate/merge.js +0 -146
  287. package/dist/commands/improve/distill/promote-memory.js +0 -329
  288. package/dist/commands/improve/distill/quality-gate.js +0 -500
  289. package/dist/commands/improve/memory/memory-contradiction-detect.js +0 -291
  290. package/dist/commands/improve/proposal-envelope.js +0 -31
  291. package/dist/commands/improve/run-context.js +0 -123
  292. package/dist/commands/improve/shared.js +0 -21
  293. package/dist/commands/improve/source-identity.js +0 -28
  294. package/dist/commands/improve/triage.js +0 -96
  295. package/dist/commands/proposal/drain-policies.js +0 -151
  296. package/dist/commands/sources/update-transaction.js +0 -220
  297. package/dist/core/action-contributors.js +0 -28
  298. package/dist/core/config/config-version-shim.js +0 -101
  299. package/dist/core/config/retired-experimental-keys-shim.js +0 -62
  300. package/dist/core/fs-txn.js +0 -405
  301. package/dist/core/lexical-score.js +0 -25
  302. package/dist/core/maintenance-barrier.js +0 -167
  303. package/dist/execution/executable-identity.js +0 -105
  304. package/dist/execution/guarded-source.js +0 -427
  305. package/dist/indexer/graph/graph-boost.js +0 -427
  306. package/dist/indexer/graph/graph-dedup.js +0 -95
  307. package/dist/indexer/search/name-match.js +0 -35
  308. package/dist/indexer/search/ranking-contributors.js +0 -515
  309. package/dist/indexer/walk/project-context.js +0 -192
  310. package/dist/integrations/agent/execution-cascade.js +0 -566
  311. package/dist/integrations/agent/execution-definitions.js +0 -202
  312. package/dist/integrations/agent/execution-lowering.js +0 -841
  313. package/dist/integrations/agent/execution-preparation.js +0 -98
  314. package/dist/integrations/agent/inline-execution.js +0 -74
  315. package/dist/registry/create-provider-registry.js +0 -29
  316. package/dist/registry/pinned-request-helper.js +0 -247
  317. package/dist/registry/pinned-transport.js +0 -717
  318. package/dist/sources/providers/index.js +0 -14
  319. package/dist/storage/engines/sqlite-migrations.js +0 -271
  320. package/dist/storage/repositories/canaries-repository.js +0 -107
  321. package/dist/storage/repositories/embedding-salvage-repository.js +0 -184
  322. package/dist/storage/repositories/registry-cache.js +0 -113
  323. package/dist/tasks/scheduler-sync-preview.js +0 -52
  324. package/dist/workflows/freeze/resolve-steps.js +0 -86
  325. package/dist/workflows/freeze/source-freeze.js +0 -64
  326. package/dist/workflows/ir/compile.js +0 -321
  327. package/dist/workflows/ir/environment-v4.js +0 -330
  328. package/dist/workflows/ir/freeze-v4.js +0 -153
  329. package/dist/workflows/ir/schema-v4.js +0 -745
  330. package/dist/workflows/ir/schema.js +0 -354
  331. package/dist/workflows/program/schema.js +0 -78
  332. package/dist/workflows/runtime/checkin.js +0 -57
  333. package/dist/workflows/runtime/plan-classifier.js +0 -196
  334. package/dist/workflows/runtime/unit-checkin.js +0 -45
  335. package/dist/workflows/runtime/unit-phases.js +0 -20
  336. package/dist/workflows/schema.js +0 -4
  337. package/dist/workflows/source-ir/compile.js +0 -200
  338. package/dist/workflows/source-ir/program.js +0 -50
  339. package/dist/workflows/source-ir/result.js +0 -26
  340. package/dist/workflows/source-ir/schema.js +0 -786
  341. package/dist/workflows/source-ir/triggers.js +0 -79
  342. package/dist/workflows/source-ir/uses.js +0 -40
  343. package/dist/workflows/validator.js +0 -60
@@ -6,7 +6,9 @@
6
6
  *
7
7
  * Attempts to patch missing frontmatter fields (`description`, `when_to_use`)
8
8
  * on assets that failed schema validation, using a single bounded in-tree LLM
9
- * call per asset. Results are recorded as `schema_repair_invoked` events.
9
+ * call per asset. Results are recorded as `schema_repair_invoked` events; a
10
+ * queued repair's `improve_ledger` row (written with the proposal) keeps the
11
+ * same asset from being re-queued inside the ledger's revisit window.
10
12
  *
11
13
  * This module is extracted from `improve.ts` to make the repair logic
12
14
  * independently testable and to use the shared structured execution seam.
@@ -18,52 +20,30 @@ import { parseFrontmatter } from "../../core/asset/frontmatter.js";
18
20
  import { parseRefInput } from "../../core/asset/resolve-ref.js";
19
21
  import { authoringRulesForType } from "../../core/authoring-rules.js";
20
22
  import { ConfigError } from "../../core/errors.js";
21
- import { appendEvent, readEvents } from "../../core/events.js";
23
+ import { appendEvent } from "../../core/events.js";
22
24
  import { parseEmbeddedJsonResponse } from "../../core/parse.js";
23
25
  import { resolveStandardsContext } from "../../core/standards/resolve-standards-context.js";
24
26
  import { info } from "../../core/warn.js";
25
27
  import { resolveAssetPath } from "../../indexer/walk/path-resolver.js";
26
- import { disposeLoweredExecutionDispatchLease } from "../../integrations/agent/execution-lowering.js";
27
- import { callStructured, preflightStructuredLlmRunner } from "../../llm/structured-call.js";
28
- import { createProposal, isProposalSkipped } from "../proposal/repository.js";
29
- // ── Constants ────────────────────────────────────────────────────────────────
30
- /** Minimum gap between schema-repair attempts on the same asset. */
31
- const SCHEMA_REPAIR_COOLDOWN_MS = 7 * 24 * 60 * 60 * 1000; // 7 days
32
- /**
33
- * Per-ref attempt cap (O-6 / #379): maximum number of schema-repair attempts
34
- * allowed within SCHEMA_REPAIR_WINDOW_MS. Prevents indefinite nightly re-repair
35
- * of assets whose source content is genuinely ambiguous or inconsistently
36
- * structured. After cap, the asset is skipped until the window rolls over.
37
- * Self-Refine arXiv:2303.17651 — iteration must be bounded.
38
- */
39
- const SCHEMA_REPAIR_MAX_ATTEMPTS = 3;
40
- const SCHEMA_REPAIR_WINDOW_MS = 30 * 24 * 60 * 60 * 1000; // 30 days
28
+ import { assertRunnerCredentials } from "../../integrations/agent/runner-dispatch.js";
29
+ import { callStructured } from "../../llm/structured-call.js";
30
+ import { isLedgerBlocked, ledgerKey, loadLedgerSnapshot } from "../improve/ledger.js";
31
+ import { createProposal } from "../proposal/repository.js";
32
+ /** Proposal source and improve-ledger source for this pass. */
33
+ const SCHEMA_REPAIR_SOURCE = "schema-repair";
41
34
  async function classifySchemaRepairs(args) {
42
35
  const { failures, startMs, budgetMs, stashDir, findFilePath, isLessonCandidateFn, repairs } = args;
43
36
  const eligibleRepairs = [];
44
37
  const pendingErrors = [];
38
+ const ledger = loadLedgerSnapshot(undefined, stashDir, [SCHEMA_REPAIR_SOURCE]);
39
+ const nowIso = new Date().toISOString();
45
40
  for (const failure of failures) {
46
41
  if (Date.now() - startMs >= budgetMs)
47
42
  break;
48
- const recentRepairs = readEvents({ type: "schema_repair_invoked", ref: failure.ref });
49
- const lastRepair = recentRepairs.events
50
- .filter((event) => event.metadata?.outcome === "queued")
51
- .sort((a, b) => new Date(b.ts ?? 0).getTime() - new Date(a.ts ?? 0).getTime())[0];
52
- if (lastRepair?.ts && Date.now() - new Date(lastRepair.ts).getTime() < SCHEMA_REPAIR_COOLDOWN_MS) {
43
+ if (isLedgerBlocked(ledger.get(ledgerKey(SCHEMA_REPAIR_SOURCE, failure.ref)), nowIso)) {
53
44
  repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped" });
54
45
  continue;
55
46
  }
56
- const windowStart = Date.now() - SCHEMA_REPAIR_WINDOW_MS;
57
- const attemptsInWindow = recentRepairs.events.filter((event) => event.ts !== undefined && new Date(event.ts).getTime() >= windowStart).length;
58
- if (attemptsInWindow >= SCHEMA_REPAIR_MAX_ATTEMPTS) {
59
- repairs.push({
60
- ref: failure.ref,
61
- reason: failure.reason,
62
- outcome: "skipped",
63
- error: `schema-repair attempt cap reached (${attemptsInWindow}/${SCHEMA_REPAIR_MAX_ATTEMPTS} in 30d window)`,
64
- });
65
- continue;
66
- }
67
47
  const filePath = await findFilePath(failure.ref, stashDir);
68
48
  if (!filePath || path.extname(filePath).toLowerCase() !== ".md") {
69
49
  repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped" });
@@ -152,112 +132,101 @@ export async function runSchemaRepairPass(failures, options) {
152
132
  }
153
133
  return { repairs, repairedRefs };
154
134
  }
155
- const dispatchLease = await preflightStructuredLlmRunner(llmRunner);
156
- try {
157
- for (const repair of pendingErrors) {
158
- appendEvent({
159
- eventType: "schema_repair_invoked",
160
- ref: repair.ref,
161
- metadata: { outcome: "error", reason: repair.reason, error: repair.error },
135
+ assertRunnerCredentials(llmRunner);
136
+ for (const repair of pendingErrors) {
137
+ appendEvent({
138
+ eventType: "schema_repair_invoked",
139
+ ref: repair.ref,
140
+ metadata: { outcome: "error", reason: repair.reason, error: repair.error },
141
+ });
142
+ repairs.push(repair);
143
+ }
144
+ for (const { failure, frontmatter: fm, fieldList, messages } of eligibleRepairs) {
145
+ let loweringNotices = [];
146
+ const noticeFields = () => loweringNotices.length > 0 ? { notices: loweringNotices } : {};
147
+ try {
148
+ info(`[improve] schema-repair ${failure.ref} (${fieldList})`);
149
+ const llmResponse = await callStructured({
150
+ feature: "schema_repair",
151
+ runner: llmRunner,
152
+ messages,
153
+ ...(chatFn ? { request: { chat: chatFn } } : {}),
154
+ onNotices: (value) => {
155
+ loweringNotices = value;
156
+ },
157
+ parse: (rawResponse) => rawResponse ?? "",
158
+ onError: () => "",
159
+ fallback: "",
162
160
  });
163
- repairs.push(repair);
164
- }
165
- for (const { failure, frontmatter: fm, fieldList, messages } of eligibleRepairs) {
166
- let loweringNotices = [];
167
- const noticeFields = () => loweringNotices.length > 0 ? { notices: loweringNotices } : {};
168
- try {
169
- info(`[improve] schema-repair ${failure.ref} (${fieldList})`);
170
- const llmResponse = await callStructured({
171
- feature: "schema_repair",
172
- runner: llmRunner,
173
- lease: dispatchLease,
174
- messages,
175
- ...(chatFn ? { request: { chat: chatFn } } : {}),
176
- onNotices: (value) => {
177
- loweringNotices = value;
178
- },
179
- parse: (rawResponse) => rawResponse ?? "",
180
- onError: () => "",
181
- fallback: "",
182
- });
183
- const parsed = parseEmbeddedJsonResponse(llmResponse.trim());
184
- if (!parsed) {
185
- repairs.push({
186
- ref: failure.ref,
187
- reason: failure.reason,
188
- outcome: "error",
189
- error: "LLM returned unparseable JSON for schema repair",
190
- ...noticeFields(),
191
- });
192
- continue;
193
- }
194
- const newFm = { ...fm.data };
195
- if (parsed.description)
196
- newFm.description = parsed.description;
197
- if (parsed.when_to_use)
198
- newFm.when_to_use = parsed.when_to_use;
199
- const newContent = assembleAsset(newFm, fm.content);
200
- // M-3 / #387: Route through proposal queue instead of writing directly to
201
- // disk. This restores akm's safety invariant — the proposal queue is the
202
- // only path to a committed asset write. LLM-generated `description` /
203
- // `when_to_use` fields can be incorrect; routing through the queue makes
204
- // them human-reviewable before they affect search ranking and curate hints.
205
- // mem0 open gaps (arXiv:2504.19413) — any LLM write to a memory field
206
- // should be human-reviewable.
207
- const proposalResult = createProposal(stashDir, {
208
- ref: failure.ref,
209
- source: "schema-repair",
210
- // §23.6 fingerprint model-id term (WI-6.4).
211
- modelId: llmRunner.connection.model,
212
- payload: {
213
- content: newContent,
214
- ...(Object.keys(newFm).length > 0 ? { frontmatter: newFm } : {}),
215
- },
216
- });
217
- if (isProposalSkipped(proposalResult)) {
218
- info(`[improve] schema-repair proposal skipped for ${failure.ref}: ${proposalResult.message}`);
219
- repairs.push({ ref: failure.ref, reason: failure.reason, outcome: "skipped", ...noticeFields() });
220
- continue;
221
- }
222
- info(`[improve] schema-repair queued: ${failure.ref} (proposal id: ${proposalResult.id})`);
223
- appendEvent({
224
- eventType: "schema_repair_invoked",
225
- ref: failure.ref,
226
- metadata: {
227
- outcome: "queued",
228
- reason: failure.reason,
229
- proposalId: proposalResult.id,
230
- ...noticeFields(),
231
- },
232
- });
161
+ const parsed = parseEmbeddedJsonResponse(llmResponse.trim());
162
+ if (!parsed) {
233
163
  repairs.push({
234
164
  ref: failure.ref,
235
165
  reason: failure.reason,
236
- outcome: "queued",
237
- proposalId: proposalResult.id,
166
+ outcome: "error",
167
+ error: "LLM returned unparseable JSON for schema repair",
238
168
  ...noticeFields(),
239
169
  });
170
+ continue;
240
171
  }
241
- catch (e) {
242
- if (e instanceof ConfigError)
243
- throw e;
244
- appendEvent({
245
- eventType: "schema_repair_invoked",
246
- ref: failure.ref,
247
- metadata: { outcome: "error", reason: failure.reason, error: String(e), ...noticeFields() },
248
- });
249
- repairs.push({
250
- ref: failure.ref,
172
+ const newFm = { ...fm.data };
173
+ if (parsed.description)
174
+ newFm.description = parsed.description;
175
+ if (parsed.when_to_use)
176
+ newFm.when_to_use = parsed.when_to_use;
177
+ const newContent = assembleAsset(newFm, fm.content);
178
+ // M-3 / #387: Route through proposal queue instead of writing directly to
179
+ // disk. This restores akm's safety invariant — the proposal queue is the
180
+ // only path to a committed asset write. LLM-generated `description` /
181
+ // `when_to_use` fields can be incorrect; routing through the queue makes
182
+ // them human-reviewable before they affect search ranking and curate hints.
183
+ // mem0 open gaps (arXiv:2504.19413) — any LLM write to a memory field
184
+ // should be human-reviewable.
185
+ const proposalResult = createProposal(stashDir, {
186
+ ref: failure.ref,
187
+ source: SCHEMA_REPAIR_SOURCE,
188
+ // Key the ledger row by the ref this pass reads it back under.
189
+ attemptedRefs: [failure.ref],
190
+ payload: {
191
+ content: newContent,
192
+ ...(Object.keys(newFm).length > 0 ? { frontmatter: newFm } : {}),
193
+ },
194
+ });
195
+ info(`[improve] schema-repair queued: ${failure.ref} (proposal id: ${proposalResult.id})`);
196
+ appendEvent({
197
+ eventType: "schema_repair_invoked",
198
+ ref: failure.ref,
199
+ metadata: {
200
+ outcome: "queued",
251
201
  reason: failure.reason,
252
- outcome: "error",
253
- error: String(e),
202
+ proposalId: proposalResult.id,
254
203
  ...noticeFields(),
255
- });
256
- }
204
+ },
205
+ });
206
+ repairs.push({
207
+ ref: failure.ref,
208
+ reason: failure.reason,
209
+ outcome: "queued",
210
+ proposalId: proposalResult.id,
211
+ ...noticeFields(),
212
+ });
213
+ }
214
+ catch (e) {
215
+ if (e instanceof ConfigError)
216
+ throw e;
217
+ appendEvent({
218
+ eventType: "schema_repair_invoked",
219
+ ref: failure.ref,
220
+ metadata: { outcome: "error", reason: failure.reason, error: String(e), ...noticeFields() },
221
+ });
222
+ repairs.push({
223
+ ref: failure.ref,
224
+ reason: failure.reason,
225
+ outcome: "error",
226
+ error: String(e),
227
+ ...noticeFields(),
228
+ });
257
229
  }
258
- }
259
- finally {
260
- disposeLoweredExecutionDispatchLease(dispatchLease);
261
230
  }
262
231
  return { repairs, repairedRefs };
263
232
  }
@@ -5,6 +5,7 @@ import fs from "node:fs";
5
5
  import path from "node:path";
6
6
  import { detectAdapterId } from "../../core/adapter/detect-adapter.js";
7
7
  import { isBundleSlug } from "../../core/asset/asset-ref.js";
8
+ import { slugForRegistryId, validateExplicitBundleName } from "../../core/bundle-id.js";
8
9
  import { isHttpUrl, resolveStashDir } from "../../core/common.js";
9
10
  import { bundleEntryToSourceEntry, getSources, installedSourceDescriptor, loadConfig, mutateConfig, resolveSecret, } from "../../core/config/config.js";
10
11
  import { ConfigError, UsageError } from "../../core/errors.js";
@@ -38,7 +39,7 @@ export async function akmAdd(input) {
38
39
  catch {
39
40
  // Not a local ref — fall through to registry install
40
41
  }
41
- return addRegistryStash(ref, stashDir, input.writable, input, input.credential);
42
+ return addRegistryStash(ref, stashDir, input.name, input.writable, input, input.credential);
42
43
  }
43
44
  /** Add a local directory as a filesystem bundle. */
44
45
  async function addLocalSource(ref, sourcePath, stashDir, explicitName, explicitAdapter, position = {}) {
@@ -49,6 +50,9 @@ async function addLocalSource(ref, sourcePath, stashDir, explicitName, explicitA
49
50
  mutateConfig((config) => {
50
51
  const existing = bundleKeyForPath(config, resolvedPath);
51
52
  if (existing) {
53
+ if (explicitName !== undefined) {
54
+ validateExplicitBundleName(config.bundles ?? {}, explicitName, existing);
55
+ }
52
56
  bundleKey = existing;
53
57
  const current = config.bundles?.[existing];
54
58
  if (current?.components && explicitAdapter === undefined)
@@ -62,6 +66,11 @@ async function addLocalSource(ref, sourcePath, stashDir, explicitName, explicitA
62
66
  return { ...config, bundles };
63
67
  }
64
68
  const bundles = { ...(config.bundles ?? {}) };
69
+ // D6: an explicit `--name` is a contract on this (local) add path — validated
70
+ // strictly before nextBundleKey derives a key, since that shared helper is also
71
+ // used by the out-of-scope `akm source add` (`addStash`) and stays forgiving.
72
+ if (explicitName !== undefined)
73
+ validateExplicitBundleName(bundles, explicitName);
65
74
  bundleKey = nextBundleKey(bundles, explicitName, resolvedPath);
66
75
  const entry = {
67
76
  path: resolvedPath,
@@ -75,6 +84,7 @@ async function addLocalSource(ref, sourcePath, stashDir, explicitName, explicitA
75
84
  schemaVersion: 1,
76
85
  bundleDir: stashDir,
77
86
  ref,
87
+ bundleId: bundleKey,
78
88
  sourceAdded: {
79
89
  type: "filesystem",
80
90
  path: resolvedPath,
@@ -100,10 +110,29 @@ async function addWebsiteSource(ref, stashDir, name, options, position = {}) {
100
110
  const maxPages = numberOption(options?.maxPages);
101
111
  const maxDepth = numberOption(options?.maxDepth);
102
112
  let entry;
113
+ let bundleId = "";
103
114
  mutateConfig((config) => {
104
115
  const bundles = { ...(config.bundles ?? {}) };
105
116
  const existingKey = bundleKeyForUrl(config, normalizedUrl);
106
- const key = existingKey ?? nextBundleKey(bundles, name ?? toWebsiteName(normalizedUrl), normalizedUrl);
117
+ if (existingKey && name !== undefined) {
118
+ validateExplicitBundleName(bundles, name, existingKey);
119
+ }
120
+ // An explicit `--name` is a contract (D6) — validated strictly and used
121
+ // as-is, never silently substituted. A DERIVED default (no --name) keeps
122
+ // the forgiving `deriveBundleId` fallback, so a dotted hostname or a
123
+ // URL collision still mints a usable id instead of erroring.
124
+ let key;
125
+ if (existingKey) {
126
+ key = existingKey;
127
+ }
128
+ else if (name !== undefined) {
129
+ validateExplicitBundleName(bundles, name);
130
+ key = name;
131
+ }
132
+ else {
133
+ key = deriveBundleId(toWebsiteName(normalizedUrl), normalizedUrl, new Set(Object.keys(bundles)));
134
+ }
135
+ bundleId = key;
107
136
  // Merge onto the existing descriptor rather than replacing it: re-running
108
137
  // `bundle add` for a URL that already has a bundle would otherwise drop
109
138
  // respectRobots / refresh, silently restoring default robots enforcement
@@ -140,6 +169,7 @@ async function addWebsiteSource(ref, stashDir, name, options, position = {}) {
140
169
  schemaVersion: 1,
141
170
  bundleDir: stashDir,
142
171
  ref,
172
+ bundleId,
143
173
  sourceAdded: {
144
174
  type: "website",
145
175
  url: normalizedUrl,
@@ -162,7 +192,7 @@ async function addWebsiteSource(ref, stashDir, name, options, position = {}) {
162
192
  * Install a stash from a registry (npm, github, git) by dispatching to the
163
193
  * matching syncable provider and persisting the lock entry.
164
194
  */
165
- async function addRegistryStash(ref, stashDir, writable, position = {}, credentialRef) {
195
+ async function addRegistryStash(ref, stashDir, explicitName, writable, position = {}, credentialRef) {
166
196
  const parsedRef = parseRegistryRef(ref);
167
197
  if (writable === true && parsedRef.source !== "git" && parsedRef.source !== "github") {
168
198
  throw new ConfigError("writable: true is only supported on filesystem and git sources", "INVALID_CONFIG_FILE");
@@ -172,6 +202,14 @@ async function addRegistryStash(ref, stashDir, writable, position = {}, credenti
172
202
  }
173
203
  const currentConfig = loadConfig();
174
204
  const existingBundleKey = findInstalledBundleKey(currentConfig.bundles ?? {}, parsedRef.id);
205
+ // D6: validate an explicit `--name` before any network sync or write — an
206
+ // illegal name, a name already taken by a different bundle, or re-adding
207
+ // this same install under a different name than it already carries must
208
+ // fail loudly here, not mint a `-<hash>` fallback deep inside the config
209
+ // mutation below.
210
+ if (explicitName !== undefined) {
211
+ validateExplicitBundleName(currentConfig.bundles ?? {}, explicitName, existingBundleKey);
212
+ }
175
213
  const existingBundle = existingBundleKey ? currentConfig.bundles?.[existingBundleKey] : undefined;
176
214
  const priorLock = existingBundleKey ? readLockfile().find((entry) => entry.id === existingBundleKey) : undefined;
177
215
  const existingWritable = Object.values(existingBundle?.components ?? {})[0]?.writable ?? existingBundle?.writable === true;
@@ -200,7 +238,7 @@ async function addRegistryStash(ref, stashDir, writable, position = {}, credenti
200
238
  cacheDir: synced.cacheDir,
201
239
  installedAt: synced.syncedAt,
202
240
  writable: synced.writable,
203
- }, position, credentialRef);
241
+ }, position, credentialRef, explicitName);
204
242
  // The prior materialized root (if this is a re-install) — read BEFORE the lock
205
243
  // upsert overwrites it, so a moved cache root can be cleaned afterwards.
206
244
  const priorLocalRoot = priorLock?.localRoot;
@@ -229,6 +267,8 @@ async function addRegistryStash(ref, stashDir, writable, position = {}, credenti
229
267
  schemaVersion: 1,
230
268
  bundleDir: stashDir,
231
269
  ref,
270
+ bundleId,
271
+ registryId: synced.id,
232
272
  installed: {
233
273
  id: synced.id,
234
274
  source: synced.source,
@@ -258,14 +298,15 @@ async function addRegistryStash(ref, stashDir, writable, position = {}, credenti
258
298
  * (spec §10.1 / §10.2 desired/resolved split). The bundle carries ONLY the
259
299
  * desired descriptor (git/npm locator + preserved `registryId` + `writable`);
260
300
  * the resolved cache root belongs exclusively in the lock (written by callers
261
- * via {@link upsertLockEntry} with the returned `bundleId`). Returns the config
301
+ * via {@link upsertLockEntry} with the returned `bundleId`). A new install is
302
+ * keyed by `explicitName` (the CLI's `--name`) when given. Returns the config
262
303
  * plus the derived bundle id so the caller keys its lock entry identically.
263
304
  */
264
- export function upsertInstalledRegistryEntry(entry, position = {}, credential) {
305
+ export function upsertInstalledRegistryEntry(entry, position = {}, credential, explicitName) {
265
306
  let bundleId = entry.id;
266
307
  const config = mutateConfig((current) => {
267
308
  const bundles = { ...(current.bundles ?? {}) };
268
- bundleId = resolveInstalledBundleKey(bundles, entry.id, entry.stashRoot);
309
+ bundleId = resolveInstalledBundleKey(bundles, entry.id, entry.stashRoot, explicitName);
269
310
  const existingComponents = bundles[bundleId]?.components;
270
311
  const components = existingComponents
271
312
  ? Object.fromEntries(Object.entries(existingComponents).map(([id, component]) => [
@@ -334,15 +375,24 @@ function findInstalledBundleKey(bundles, installId) {
334
375
  }
335
376
  /**
336
377
  * The stable bundle key for a registry install: reuse the existing bundle for
337
- * this install id (so re-installs keep the same key), otherwise derive a
338
- * batch-unique key via the shared {@link deriveBundleId} (D-R5), unique against
339
- * the currently-configured bundle keys.
378
+ * this install id (so re-installs keep the same key — a caller-visible name
379
+ * mismatch was already rejected upstream, by {@link validateExplicitBundleName}
380
+ * in `addRegistryStash`, before any write). Otherwise an explicit `--name`
381
+ * (D6) is a contract — validated strictly and used as-is, never silently
382
+ * substituted. Without one, a batch-unique key is derived via the shared
383
+ * {@link deriveBundleId} (D-R5) from the package/repo name the install id
384
+ * names rather than the basename of the materialized cache directory
385
+ * (`extracted`) — unique against the currently-configured bundle keys.
340
386
  */
341
- function resolveInstalledBundleKey(bundles, installId, stashRoot) {
387
+ function resolveInstalledBundleKey(bundles, installId, stashRoot, explicitName) {
342
388
  const existing = findInstalledBundleKey(bundles, installId);
343
389
  if (existing)
344
390
  return existing;
345
- return deriveBundleId(installId, path.resolve(stashRoot), new Set(Object.keys(bundles)));
391
+ if (explicitName !== undefined) {
392
+ validateExplicitBundleName(bundles, explicitName);
393
+ return explicitName;
394
+ }
395
+ return deriveBundleId(slugForRegistryId(installId), path.resolve(stashRoot), new Set(Object.keys(bundles)));
346
396
  }
347
397
  function toReadableId(resolvedPath) {
348
398
  const home = process.env.HOME ?? process.env.USERPROFILE ?? "";
@@ -71,7 +71,7 @@ export const indexCommand = defineCommand({
71
71
  },
72
72
  reembed: {
73
73
  type: "boolean",
74
- description: "Force re-embedding of every entry, bypassing the embedding-model-rename compatibility check.",
74
+ description: "Discard every stored vector and re-embed all entries under the configured model.",
75
75
  default: false,
76
76
  },
77
77
  "skip-if-locked": {
@@ -34,11 +34,10 @@
34
34
  * `with.content`, an inline `akm/command` prompt body, or a stored
35
35
  * command's own rendered content is never read, let alone printed;
36
36
  * - effective execution settings (engine/model/timeout) reuse
37
- * `prepareResolvedExecution` (`src/integrations/agent/execution-preparation.ts`)
38
- * — the SAME cascade-composition entry point `prepareCommandInvocation`
39
- * itself calls once a command/persona source is already rendered — fed a
40
- * BLANK inline placeholder command (`createInlineResolvedCommand`) and an
41
- * EMPTY command-layer values map, instead of the task's real referenced
37
+ * `resolveExecution` (`src/integrations/agent/execution.ts`) — the SAME
38
+ * resolution entry point every command/persona invocation goes through —
39
+ * fed a BLANK inline placeholder command (`createInlineResolvedCommand`)
40
+ * and an EMPTY command-layer values map, instead of the task's real referenced
42
41
  * command source. This is a deliberate, documented trade-off: a
43
42
  * referenced command's OWN frontmatter overrides (its own `engine:`/
44
43
  * `model:`, if any) do not contribute a layer here, in exchange for never
@@ -70,9 +69,8 @@
70
69
  * `suppliedInputs`). Do not paste this command's output into an untrusted
71
70
  * place on the assumption that it can never contain a credential.
72
71
  *
73
- * Field-level execution provenance is READ from `planExecutionCascade`'s own
74
- * `ResolvedExecutionPlanV1.provenance` (via `prepareResolvedExecution`) —
75
- * this module is a CONSUMER of the one common cascade resolver, never a
72
+ * Field-level execution provenance is READ from `resolveExecution`'s own
73
+ * `provenance` — this module is a CONSUMER of the one common resolver, never a
76
74
  * second resolver: it never re-derives engine/model precedence itself.
77
75
  */
78
76
  import fs from "node:fs";
@@ -83,7 +81,7 @@ import { NotFoundError } from "../../core/errors.js";
83
81
  import { applyInputDefaults, materializeInputFlags, } from "../../execution/input-contract.js";
84
82
  import { createInlineResolvedCommand } from "../../execution/resolved-request.js";
85
83
  import { resolveAdapterConceptOwner } from "../../indexer/lookup/adapter-concept-owner.js";
86
- import { prepareResolvedExecution } from "../../integrations/agent/execution-preparation.js";
84
+ import { resolveExecution } from "../../integrations/agent/execution.js";
87
85
  import { isSchedulerRefEnabled } from "../../tasks/activation-config.js";
88
86
  import { parseTaskSource } from "../../tasks/source/parse-task-source.js";
89
87
  import { projectTaskSourceV4 } from "../../tasks/source/project-v4.js";
@@ -209,10 +207,9 @@ function resolveExecutionSettings(document) {
209
207
  if (!isCommandTarget(document))
210
208
  return { provenance: {} };
211
209
  const current = currentExecutionValues(document);
212
- const prepared = prepareResolvedExecution({
210
+ const prepared = resolveExecution({
213
211
  command: createInlineResolvedCommand({ template: "", content: "" }),
214
212
  config: loadConfig(),
215
- invocationKind: "task",
216
213
  commandLayer: { id: "task-explain", values: {} },
217
214
  ...(Object.keys(current).length > 0 ? { current } : {}),
218
215
  });
@@ -225,7 +222,7 @@ function resolveExecutionSettings(document) {
225
222
  },
226
223
  ...(prepared.request.model !== undefined ? { model: prepared.request.model } : {}),
227
224
  ...(prepared.request.runtime.timeoutMs !== undefined ? { timeoutMs: prepared.request.runtime.timeoutMs } : {}),
228
- provenance: prepared.plan.provenance,
225
+ provenance: prepared.provenance,
229
226
  };
230
227
  }
231
228
  /**
@@ -272,7 +269,7 @@ export async function akmTaskExplain(ref, options = {}) {
272
269
  const materializedInputs = materializeInputFlags(inputContract, options.inputFlags ?? [], TASK_INPUT_DIAGNOSTICS);
273
270
  const defaultedInputs = applyInputDefaults(inputContract, materializedInputs);
274
271
  const document = projectTaskSourceV4(parsed.v4);
275
- const enabled = isSchedulerRefEnabled(loadConfig(), "task", makeBundleRef(bundle.source.name, taskConceptId));
272
+ const enabled = isSchedulerRefEnabled(loadConfig(), makeBundleRef(bundle.source.name, taskConceptId));
276
273
  const schedule = parsed.v4.schedule.map((entry) => ({
277
274
  ordinal: entry.ordinal,
278
275
  cron: entry.cron,
@@ -226,7 +226,7 @@ const tasksAddCommand = defineJsonCommand({
226
226
  force: { type: "boolean", description: "Overwrite an existing task with the same id", default: false },
227
227
  rebind: {
228
228
  type: "boolean",
229
- description: "Explicitly permit scheduler creation from this ineligible local invocation",
229
+ description: "Also point the bundle's installed scheduler rows at this akm invocation (as `task sync --rebind`)",
230
230
  default: false,
231
231
  },
232
232
  },
@@ -358,13 +358,13 @@ export function taskSyncDryRunExitCode(preview) {
358
358
  const tasksSyncCommand = defineJsonCommand({
359
359
  meta: {
360
360
  name: "sync",
361
- description: "Atomically preflight and reconcile a bundle's task/workflow schedules with the OS scheduler",
361
+ description: "Reconcile enabled task/workflow schedules with the OS scheduler, one row at a time",
362
362
  },
363
363
  args: {
364
364
  ...bundleArg,
365
365
  rebind: {
366
366
  type: "boolean",
367
- description: "Replace installed bindings with the current invocation",
367
+ description: "Point installed rows at this akm invocation instead of keeping their launcher",
368
368
  default: false,
369
369
  },
370
370
  "dry-run": {
@@ -435,16 +435,17 @@ const tasksDoctorCommand = defineJsonCommand({
435
435
  },
436
436
  });
437
437
  /**
438
- * #907: `akm task validate`'s exit-code contract — only current-schema
439
- * `valid` is successful (exit 0); `blocked`/`invalid`/`not-a-task` are
440
- * diagnosed defects the caller must act on (exit 1, mirroring `task sync`'s
441
- * own `failures.length > 0 -> EXIT_CODES.GENERAL`). A missing path or an
438
+ * #907: `akm task validate`'s exit-code contract — `valid` and `converts`
439
+ * (a v2/v3 document the in-memory shim converted successfully) are both
440
+ * successful (exit 0); `blocked`/`invalid`/`not-a-task` are diagnosed
441
+ * defects the caller must act on (exit 1, mirroring `task sync`'s own
442
+ * `failures.length > 0 -> EXIT_CODES.GENERAL`). A missing path or an
442
443
  * unreadable file never reaches this function at all — `akmTaskValidate`
443
444
  * throws a `UsageError` for those, which `defineJsonCommand`'s wrapping
444
445
  * already maps to exit 2.
445
446
  */
446
447
  export function taskValidateExitCode(result) {
447
- return result.outcome === "valid" ? undefined : EXIT_CODES.GENERAL;
448
+ return result.outcome === "valid" || result.outcome === "converts" ? undefined : EXIT_CODES.GENERAL;
448
449
  }
449
450
  const tasksValidateCommand = defineJsonCommand({
450
451
  meta: {