akm-cli 0.9.31 → 0.10.261002-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/CHANGELOG.md +420 -0
  2. package/STABILITY.md +99 -57
  3. package/dist/api-node.mjs +20 -0
  4. package/dist/api.d.ts +23 -0
  5. package/dist/api.js +45 -0
  6. package/dist/assets/hints/cli-hints-full.md +15 -29
  7. package/dist/assets/hints/cli-hints-short.md +2 -2
  8. package/dist/assets/improve-strategies/default.json +3 -3
  9. package/dist/assets/prompts/consolidate-pair.md +9 -7
  10. package/dist/assets/stash-skeleton/README.md +1 -1
  11. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
  12. package/dist/cli/shared.js +31 -48
  13. package/dist/cli/unknown-flags.js +6 -43
  14. package/dist/cli.js +8 -34
  15. package/dist/commands/agent/contribute-cli.js +1 -1
  16. package/dist/commands/completions.js +1 -2
  17. package/dist/commands/config-cli.js +5 -28
  18. package/dist/commands/env/env-cli.js +5 -5
  19. package/dist/commands/env/secret-cli.js +2 -2
  20. package/dist/commands/feedback-cli.js +75 -49
  21. package/dist/commands/health/checks.js +41 -16
  22. package/dist/commands/health/plugin-staleness.js +52 -22
  23. package/dist/commands/improve/autonomy-gate.js +5 -3
  24. package/dist/commands/improve/consolidate/chunking.js +3 -10
  25. package/dist/commands/improve/consolidate/continuity-check.js +4 -4
  26. package/dist/commands/improve/consolidate/pair-pass.js +29 -10
  27. package/dist/commands/improve/consolidate.js +10 -65
  28. package/dist/commands/improve/distill/lesson-update.js +91 -0
  29. package/dist/commands/improve/distill-guards.js +1 -43
  30. package/dist/commands/improve/distill.js +148 -21
  31. package/dist/commands/improve/eligibility.js +14 -1
  32. package/dist/commands/improve/extract-cli.js +1 -1
  33. package/dist/commands/improve/extract-prompt.js +17 -7
  34. package/dist/commands/improve/extract.js +5 -7
  35. package/dist/commands/improve/improve-cli.js +92 -106
  36. package/dist/commands/improve/improve-strategies.js +8 -12
  37. package/dist/commands/improve/improve.js +134 -52
  38. package/dist/commands/improve/loop-stages.js +18 -5
  39. package/dist/commands/improve/planner.js +0 -1
  40. package/dist/commands/improve/preparation.js +40 -99
  41. package/dist/commands/improve/proactive-maintenance.js +2 -2
  42. package/dist/commands/improve/reflect-noise.js +3 -55
  43. package/dist/commands/improve/reflect.js +29 -34
  44. package/dist/commands/improve/retrieval-queries.js +37 -0
  45. package/dist/commands/improve/retrieval-scope.js +2 -2
  46. package/dist/commands/improve/salience.js +2 -9
  47. package/dist/commands/improve/session-asset.js +4 -1
  48. package/dist/commands/improve/stage.js +23 -12
  49. package/dist/commands/lint/index.js +5 -20
  50. package/dist/commands/observability-cli.js +3 -9
  51. package/dist/commands/proposal/drain.js +43 -6
  52. package/dist/commands/proposal/proposal-cli.js +11 -31
  53. package/dist/commands/proposal/repository.js +20 -14
  54. package/dist/commands/proposal/validators/proposal-quality-validators.js +0 -3
  55. package/dist/commands/read/knowledge.js +3 -3
  56. package/dist/commands/read/remember-cli.js +0 -13
  57. package/dist/commands/read/search-cli.js +29 -97
  58. package/dist/commands/read/search.js +0 -11
  59. package/dist/commands/read/show.js +0 -19
  60. package/dist/commands/sources/plugin-upgrade.js +164 -101
  61. package/dist/commands/sources/sources-cli.js +1 -15
  62. package/dist/commands/sources/stash-cli.js +6 -14
  63. package/dist/commands/tasks/tasks-cli.js +4 -115
  64. package/dist/commands/workflow/plan.js +11 -33
  65. package/dist/commands/workflow-cli.js +2 -7
  66. package/dist/core/adapter/adapter-ids.js +1 -2
  67. package/dist/core/adapter/adapters/agent-skills-adapter.js +0 -4
  68. package/dist/core/adapter/adapters/akm-adapter.js +9 -48
  69. package/dist/core/adapter/adapters/akm-lint.js +10 -26
  70. package/dist/core/adapter/adapters/akm-metadata.js +2 -72
  71. package/dist/core/adapter/adapters/akm-task-adapter.js +0 -5
  72. package/dist/core/adapter/adapters/akm-workflow-adapter.js +9 -28
  73. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -20
  74. package/dist/core/adapter/adapters/generic-files-adapter.js +0 -10
  75. package/dist/core/adapter/adapters/llm-wiki-adapter.js +0 -4
  76. package/dist/core/adapter/adapters/tool-dir-shared.js +0 -24
  77. package/dist/core/adapter/adapters/website-snapshot-adapter.js +4 -4
  78. package/dist/core/adapter/recognize-match.js +3 -4
  79. package/dist/core/common.js +18 -2
  80. package/dist/core/config/config-schema.js +12 -15
  81. package/dist/core/config/config-types.js +6 -15
  82. package/dist/core/config/config-walker.js +6 -23
  83. package/dist/core/config/config.js +17 -80
  84. package/dist/core/config/engine-semantics.js +7 -3
  85. package/dist/core/config/schema/engines.js +7 -7
  86. package/dist/core/config/schema/improve-processes.js +10 -63
  87. package/dist/core/config/schema/improve.js +2 -21
  88. package/dist/core/config/schema/index-config.js +6 -39
  89. package/dist/core/env-secret-ref.js +2 -45
  90. package/dist/core/errors.js +1 -2
  91. package/dist/core/extra-params.js +2 -86
  92. package/dist/core/file-lock.js +2 -6
  93. package/dist/core/improve-result.js +13 -1
  94. package/dist/core/mutation-target.js +2 -2
  95. package/dist/core/paths.js +4 -0
  96. package/dist/core/recognition-util.js +4 -5
  97. package/dist/core/run-lock.js +1 -1
  98. package/dist/core/subprocess.js +49 -0
  99. package/dist/core/warn.js +21 -4
  100. package/dist/core/write-source.js +3 -3
  101. package/dist/execution/target-ref.js +1 -2
  102. package/dist/indexer/indexer.js +15 -6
  103. package/dist/indexer/passes/memory-inference.js +15 -6
  104. package/dist/indexer/search/db-search.js +49 -36
  105. package/dist/indexer/search/fts-query.js +0 -19
  106. package/dist/indexer/search/ranking.js +29 -1
  107. package/dist/indexer/usage/usage-events.js +21 -16
  108. package/dist/integrations/agent/engine-resolution.js +5 -2
  109. package/dist/integrations/agent/index.js +1 -1
  110. package/dist/integrations/agent/profiles.js +41 -98
  111. package/dist/integrations/agent/request-lowering.js +3 -3
  112. package/dist/integrations/harnesses/aider/index.js +5 -0
  113. package/dist/integrations/harnesses/amazonq/index.js +1 -0
  114. package/dist/integrations/harnesses/claude/config-import.js +2 -3
  115. package/dist/integrations/harnesses/claude/index.js +5 -0
  116. package/dist/integrations/harnesses/codex/index.js +1 -0
  117. package/dist/integrations/harnesses/copilot/index.js +1 -0
  118. package/dist/integrations/harnesses/gemini/index.js +1 -0
  119. package/dist/integrations/harnesses/index.js +18 -39
  120. package/dist/integrations/harnesses/opencode/agent-builder.js +60 -35
  121. package/dist/integrations/harnesses/opencode/config-import.js +121 -52
  122. package/dist/integrations/harnesses/opencode/index.js +22 -0
  123. package/dist/integrations/harnesses/opencode/plugin-layout.js +68 -0
  124. package/dist/integrations/harnesses/opencode/session-log-types.js +12 -0
  125. package/dist/integrations/harnesses/opencode/session-log-v1.js +72 -0
  126. package/dist/integrations/harnesses/opencode/session-log-v2.js +140 -0
  127. package/dist/integrations/harnesses/opencode/session-log.js +104 -115
  128. package/dist/integrations/harnesses/opencode/version.js +98 -0
  129. package/dist/integrations/harnesses/opencode-sdk/{harness.js → index.js} +5 -17
  130. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +105 -206
  131. package/dist/integrations/harnesses/opencode-sdk/v1-adapter.js +199 -0
  132. package/dist/integrations/harnesses/opencode-sdk/v2-adapter.js +254 -0
  133. package/dist/integrations/harnesses/opencode-sdk/wire.js +66 -0
  134. package/dist/integrations/harnesses/openhands/index.js +1 -0
  135. package/dist/integrations/harnesses/pi/index.js +1 -0
  136. package/dist/integrations/harnesses/shared.js +2 -0
  137. package/dist/integrations/harnesses/types.js +2 -0
  138. package/dist/llm/client.js +41 -11
  139. package/dist/output/cli-hints.js +1 -1
  140. package/dist/output/context.js +10 -19
  141. package/dist/output/format-exempt.js +5 -8
  142. package/dist/output/shapes/helpers.js +1 -22
  143. package/dist/output/shapes/passthrough.js +0 -1
  144. package/dist/output/shapes.js +3 -31
  145. package/dist/output/stdout.js +5 -1
  146. package/dist/output/text/command-format.js +6 -1
  147. package/dist/output/text/workflow-format.js +4 -22
  148. package/dist/scripts/akm-migrate-node.js +15745 -16711
  149. package/dist/scripts/akm-migrate.js +16318 -17280
  150. package/dist/setup/detect.js +10 -4
  151. package/dist/setup/harness-config-import.js +5 -14
  152. package/dist/setup/setup.js +1 -1
  153. package/dist/setup/steps/tasks.js +12 -13
  154. package/dist/sources/providers/git-stash.js +113 -16
  155. package/dist/storage/repositories/index-utility-repository.js +2 -32
  156. package/dist/storage/repositories/index-vec-repository.js +4 -0
  157. package/dist/storage/repositories/task-history-repository.js +0 -5
  158. package/dist/storage/repositories/workflow-runs-repository.js +5 -15
  159. package/dist/tasks/embedded.js +2 -1
  160. package/dist/tasks/run/run-command-task.js +9 -1
  161. package/dist/tasks/run/run-workflow-task.js +4 -3
  162. package/dist/tasks/run/task-history.js +0 -2
  163. package/dist/tasks/run/task-log.js +38 -1
  164. package/dist/tasks/schedule-gates.js +39 -0
  165. package/dist/tasks/scheduler-binding.js +0 -16
  166. package/dist/tasks/scheduler-sync.js +3 -97
  167. package/dist/tasks/source/bounded-document.js +1 -1
  168. package/dist/tasks/source/task-source-v4.js +2 -12
  169. package/dist/tasks/task-run-reserved-flags.js +2 -31
  170. package/dist/workflows/authoring/authoring.js +6 -6
  171. package/dist/workflows/compile.js +8 -65
  172. package/dist/workflows/exec/child-workflow.js +22 -3
  173. package/dist/workflows/exec/native-executor.js +12 -147
  174. package/dist/workflows/exec/run-workflow.js +31 -103
  175. package/dist/workflows/exec/step-work.js +9 -178
  176. package/dist/workflows/exec/unit-dispatch.js +0 -10
  177. package/dist/workflows/freeze/environment.js +3 -44
  178. package/dist/workflows/freeze/freeze.js +8 -41
  179. package/dist/workflows/freeze/targets/child-workflow.js +27 -63
  180. package/dist/workflows/freeze/targets/command.js +2 -16
  181. package/dist/workflows/freeze/targets/shell.js +0 -2
  182. package/dist/workflows/freeze/task-bindings.js +2 -28
  183. package/dist/workflows/ir/params.js +12 -2
  184. package/dist/workflows/parser.js +82 -232
  185. package/dist/workflows/plan.js +1 -1
  186. package/dist/workflows/renderer.js +10 -19
  187. package/dist/workflows/runtime/run-plan.js +11 -53
  188. package/dist/workflows/runtime/runs.js +4 -27
  189. package/dist/workflows/source-files.js +12 -16
  190. package/dist/workflows/source-semantics.js +1 -93
  191. package/docs/README.md +1 -1
  192. package/docs/agents/README.md +3 -3
  193. package/docs/agents/agent-install.md +1 -1
  194. package/docs/architecture/0.10-process-rules.md +827 -0
  195. package/docs/architecture/adapters.md +17 -31
  196. package/docs/architecture/architecture.md +4 -5
  197. package/docs/architecture/improvement.md +115 -76
  198. package/docs/architecture/internals/improve-workflow.md +22 -16
  199. package/docs/architecture/internals/indexing.md +1 -1
  200. package/docs/architecture/internals/search.md +1 -2
  201. package/docs/architecture/internals/storage-locations.md +6 -6
  202. package/docs/architecture/persisted-data-compat.md +13 -4
  203. package/docs/architecture/specs/0.9.0-decisions.md +6 -2
  204. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +2 -2
  205. package/docs/architecture/specs/workflow-format-unification.md +1 -6
  206. package/docs/architecture/testing/manual-testing-checklist.md +21 -30
  207. package/docs/architecture/testing/task-workflow-manual-tests.md +106 -203
  208. package/docs/architecture/testing/testing-workflow.md +1 -1
  209. package/docs/architecture/workflow-engine.md +5 -56
  210. package/docs/guides/author-workflows.md +39 -69
  211. package/docs/guides/bundles.md +3 -1
  212. package/docs/guides/capture-knowledge.md +1 -1
  213. package/docs/guides/claude-code-vs-akm-workflows.md +1 -2
  214. package/docs/guides/discover-and-load.md +4 -4
  215. package/docs/guides/getting-started.md +1 -1
  216. package/docs/guides/improve-the-library.md +10 -9
  217. package/docs/guides/recipes/headless-install.md +2 -2
  218. package/docs/guides/run-workflows.md +1 -1
  219. package/docs/guides/scheduling.md +9 -1
  220. package/docs/guides/use-with-any-agent.md +49 -4
  221. package/docs/integration/bundling-akm.md +7 -6
  222. package/docs/maintainers/curate-workmap.md +1 -2
  223. package/docs/maintainers/release-checklist.md +33 -1
  224. package/docs/migration/README.md +2 -0
  225. package/docs/migration/release-notes/0.10.0.md +201 -0
  226. package/docs/migration/release-notes/README.md +1 -0
  227. package/docs/migration/v0.9-to-v0.10.md +507 -0
  228. package/docs/posts/akm-command-tour.md +3 -3
  229. package/docs/product-surface.md +1 -1
  230. package/docs/reference/README.md +1 -0
  231. package/docs/reference/api.md +65 -0
  232. package/docs/reference/asset-types.md +3 -5
  233. package/docs/reference/bundle-types.md +2 -4
  234. package/docs/reference/cli.md +160 -188
  235. package/docs/reference/configuration.md +101 -20
  236. package/docs/reference/data-and-telemetry.md +2 -2
  237. package/docs/reference/env-and-secrets.md +2 -2
  238. package/docs/reference/supported-formats.md +3 -4
  239. package/docs/reference/tasks.md +12 -19
  240. package/docs/reference/website-sources.md +1 -1
  241. package/docs/reference/workflow-schema.md +89 -317
  242. package/docs/reference/workflows.md +20 -48
  243. package/package.json +12 -2
  244. package/schemas/akm-config.json +2 -243
  245. package/schemas/akm-task.json +2 -3
  246. package/schemas/akm-workflow.json +47 -84
  247. package/dist/assets/improve-strategies/catchup.json +0 -41
  248. package/dist/assets/improve-strategies/proactive-maintenance.json +0 -24
  249. package/dist/assets/improve-strategies/quick.json +0 -17
  250. package/dist/assets/improve-strategies/reflect-distill.json +0 -29
  251. package/dist/assets/improve-strategies/thorough.json +0 -49
  252. package/dist/assets/prompts/retrieval-relevance-judge.md +0 -6
  253. package/dist/assets/tasks/improve/akm-improve-catchup.yml +0 -6
  254. package/dist/assets/tasks/improve/akm-improve-frequent.yml +0 -4
  255. package/dist/assets/tasks/improve/akm-improve-nightly.yml +0 -4
  256. package/dist/cli/retired-commands.js +0 -118
  257. package/dist/commands/improve/retrieval-gate.js +0 -132
  258. package/dist/commands/tasks/validate.js +0 -176
  259. package/dist/core/config/legacy-source-shape-shim.js +0 -117
  260. package/dist/indexer/feedback/utility-policy.js +0 -75
  261. package/dist/integrations/harnesses/ids.js +0 -28
  262. package/dist/tasks/source/task-source-v3-frozen.js +0 -424
  263. package/dist/tasks/source/task-to-v4.js +0 -863
  264. package/dist/workflows/exec/worktree.js +0 -372
  265. package/dist/workflows/freeze/child-output-references.js +0 -83
  266. package/dist/workflows/freeze/identity.js +0 -22
  267. package/dist/workflows/freeze/targets/script.js +0 -50
  268. package/dist/workflows/freeze/targets/task.js +0 -124
  269. package/dist/workflows/github-yaml.js +0 -506
  270. package/dist/workflows/runtime/run-outputs.js +0 -80
@@ -101,23 +101,21 @@ interface BundleAdapter {
101
101
  // reading authored bytes or walking the bundle.
102
102
  readCandidates?(c: BundleComponent, conceptId: string): Array<{ path: string; conceptId: string }>;
103
103
 
104
- // OPTIONAL — placement / discovery.
105
- placeNew?(c: BundleComponent, conceptId: string): string; // where a new item would live
104
+ // OPTIONAL — discovery.
106
105
  directoryList?(c: BundleComponent): string[]; // owned dirs; feeds git exact-path staging
107
106
  looksLikeRoot?(root: string): boolean; // install-time probe
108
107
  }
109
108
  ```
110
109
 
111
110
  `recognize` and `validate` are required on every adapter; `index`,
112
- `affectedItems`, `readCandidates`, `placeNew`, `directoryList`, and
111
+ `affectedItems`, `readCandidates`, `directoryList`, and
113
112
  `looksLikeRoot` are optional capability methods. An adapter overriding `index()` must keep
114
113
  `recognize()` coherent with it (conformance: `index()` == fold of
115
114
  `recognize()` over the core walk) or declare component-level
116
115
  incrementality.
117
116
 
118
- `readCandidates` is intentionally distinct from both `extensions` and
119
- `placeNew`: extensions are non-exhaustive walk hints, while placement is a
120
- write-normalization policy. Read candidates preserve canonical directory
117
+ `readCandidates` is intentionally distinct from `extensions`: extensions are
118
+ non-exhaustive walk hints. Read candidates preserve canonical directory
121
119
  manifests (for example `skills/<name>/SKILL.md`) so lookup, index-backed show, and workflow runtime share one
122
120
  physical-owner decision. Each candidate carries its canonical concept identity,
123
121
  so a byte-denying ownership probe never has to infer identity from the query.
@@ -145,30 +143,21 @@ concern.
145
143
 
146
144
  ---
147
145
 
148
- ## `placeNew()` wiring status
146
+ ## Placement
149
147
 
150
- The interface declares `placeNew()` as an optional capability method, and 9
151
- of the 11 built-in adapters already implement it — all but `okf` and
152
- `website-snapshot` (`claude` and `opencode` inherit theirs from the shared
153
- tool-dir factory). **Nothing in the write path calls it yet**: AKM-native
154
- writes still resolve through AKM's own flat type→directory placement table
155
- (`src/core/asset/asset-placement.ts`'s `PLACEMENT_SPECS`), not through the
156
- owning adapter's `placeNew()`.
157
-
158
- Placement for every existing bundle is already correct today — this is a
159
- deliberately sequenced routing change, not unfinished behavior. It's
160
- scoped out of 0.9.0 by
161
- [D12](../architecture/specs/0.9.0-decisions.md#d12--bundleadapterplacenew-stays-unwired-until-010)
162
- and deferred to 0.10; nothing user-visible changes in 0.10 for this alone.
163
- See [STABILITY.md](../../STABILITY.md) ("On the horizon") for the
164
- up-to-date status if this changes.
148
+ Adapters do not place new items. AKM-native writes resolve through AKM's own
149
+ flat type→directory placement table (`src/core/asset/asset-placement.ts`'s
150
+ `PLACEMENT_SPECS`), which is correct for akm-layout bundles — the only bundles
151
+ the write commands touch in practice. A per-adapter `placeNew()` existed
152
+ through 0.9 but nothing called it, and it was removed in 0.10. Supporting
153
+ writable bundles in another layout would need it back: see
154
+ [#1163](https://github.com/itlackey/akm/issues/1163).
165
155
 
166
156
  ---
167
157
 
168
158
  ## Write allowlists
169
159
 
170
- `placeNew()` wiring is a separate question from **which bundles AKM-native
171
- write commands are allowed to touch at all** — that's decided today by a
160
+ Which bundles AKM-native write commands are allowed to touch at all is decided by a
172
161
  small allowlist check, `assertAkmAssetWrite` (`src/core/write-source.ts`),
173
162
  run before any write command touches the filesystem. It compares the target
174
163
  bundle's detected (or configured) adapter id against an allowlist and throws
@@ -181,12 +170,10 @@ The default allowlist is `["akm"]`. Two command families widen it explicitly:
181
170
  | `akm env create` / `env remove` / `secret set` | `dotenv` |
182
171
  | `akm workflow create` | `akm-workflow` |
183
172
 
184
- Every other adapter — including every adapter that already implements
185
- `placeNew()` (`agent-skills`, `claude`, `opencode`, `akm-task`,
186
- `generic-files`) — is rejected by every AKM-native write command in 0.9.0.
173
+ Every other adapter (`agent-skills`, `claude`, `opencode`, `akm-task`,
174
+ `generic-files`, and so on) is rejected by every AKM-native write command.
187
175
  `akm task add` in particular uses the *default* allowlist (`akm` only), so a
188
- standalone `akm-task` bundle is read-only through AKM-native write commands
189
- even though `akm-task`'s own `placeNew()` is implemented.
176
+ standalone `akm-task` bundle is read-only through AKM-native write commands.
190
177
 
191
178
  Reading, searching, and `akm lint` are unaffected by this allowlist — only
192
179
  creating, editing, or deleting through AKM's own commands is restricted.
@@ -197,8 +184,7 @@ This allowlist boundary is also where AKM's [execution
197
184
  boundary](../guides/concepts.md) principle shows up on the write side: AKM
198
185
  retrieves and validates every supported format, but it only lets its own
199
186
  write commands mutate a narrow, explicitly-declared set of targets — it
200
- does not widen write access to a format just because that format's adapter
201
- happens to expose a `placeNew()` implementation.
187
+ does not widen write access to a format just because it can read it.
202
188
 
203
189
  ---
204
190
 
@@ -260,8 +260,7 @@ schema (`src/core/config/schema/sources-bundles.ts`).
260
260
  refresh.
261
261
 
262
262
  Write-target resolution (`resolveWriteTarget`) follows: an explicit
263
- destination flag (`--bundle` on `remember`/`clone`/`improve`, `--target` on
264
- `import`/env/secret mutations) -> `config.defaultWriteTarget` -> working
263
+ destination flag (`--bundle`) -> `config.defaultWriteTarget` -> working
265
264
  bundle (`defaultBundle`) -> `ConfigError`. The resolved target keeps the
266
265
  optional configured selector separate from the stable `source.name`: APIs
267
266
  that must re-resolve a destination use the selector, while durable refs and
@@ -299,8 +298,8 @@ whose proposal queue is read or adjudicated; it does not override the proposal's
299
298
  write destination. Qualified proposals and unqualified proposals created in a
300
299
  configured secondary queue record the destination source name and materialized
301
300
  root. Diff, accept, and revert use that binding by default and reject an explicit
302
- `--target` that resolves elsewhere. An unbound short proposal requires either
303
- an explicit `--target` or an authenticated `--queue` context; it does not
301
+ `--bundle` that resolves elsewhere. An unbound short proposal requires either
302
+ an explicit `--bundle` or an authenticated `--queue` context; it does not
304
303
  inherit a default write target.
305
304
 
306
305
  ---
@@ -442,7 +441,7 @@ An explicit missing or incompatible engine is an error and never falls
442
441
  through to another configured engine.
443
442
 
444
443
  Task-v3 execution and durable workflow-v4 dispatch use this runtime boundary.
445
- Markdown and GitHub-shaped YAML compile through source IR v1; new starts freeze
444
+ Markdown workflows compile through source IR v1; new starts freeze
446
445
  v4-family `irVersion: 5`. A stored plan that decodes runs whatever release
447
446
  froze it; one that does not is marked abandoned and `akm workflow run <ref>`
448
447
  starts afresh. Only a plan a newer akm froze is refused, naming the upgrade.
@@ -32,15 +32,16 @@ everything else routes through `akm proposal accept`.
32
32
 
33
33
  ## Components
34
34
 
35
- - **Utility policy** (`src/indexer/feedback/utility-policy.ts`) — pure
36
- domain math that turns accumulated feedback counts into a new utility
37
- score. No database access; unit-testable in isolation.
35
+ - **Utility recompute** (`recomputeUtilityScores`, `src/indexer/indexer.ts`) —
36
+ the one place utility is computed from `usage_events`; `akm index` runs it
37
+ for every entry and `akm feedback` for the entry it touched.
38
38
  - **Strategies** (`src/commands/improve/improve-strategies.ts`,
39
39
  `src/assets/improve-strategies/*.json`) — named presets that decide which
40
40
  improve processes run and with what engine/model/limits.
41
41
  - **Autonomy gate** (`src/commands/improve/autonomy-gate.ts`) — downgrades
42
- the handful of processes that would otherwise mutate assets without review,
43
- unless `experimental.improveAutonomy` is explicitly set.
42
+ the handful of processes that would otherwise mutate assets without review
43
+ (memory inference, memory cleanup, and a judged triage promote), unless
44
+ `experimental.improveAutonomy` is explicitly set.
44
45
  - **Reflect / distill / consolidate subprocesses** — the improve pipeline's
45
46
  proposal generators, invoked per asset (reflect, distill) or across the
46
47
  whole memory corpus (consolidate). See
@@ -49,7 +50,7 @@ everything else routes through `akm proposal accept`.
49
50
  - **Proposal queue** (`state.db`) — written by `createProposal` (reflect,
50
51
  distill, consolidate's promote operations, and the other proposal producers)
51
52
  and by `createRetireProposal` (the pair pass's retire proposals).
52
- - **Auto-sync** — the end-of-run commit/push step for git-backed bundles.
53
+ - **Auto-sync** — the pre-run sync and the end-of-run commit/push step for git-backed bundles.
53
54
  - **Session extraction** (`akm proposal extract`) — a separate entry point
54
55
  that mines coding-agent session transcripts for durable insights and queues
55
56
  them the same way.
@@ -62,13 +63,16 @@ everything else routes through `akm proposal accept`.
62
63
  `--negative --reason "<what is wrong and what should change>"` flags the
63
64
  asset: the next improve run may repair its frontmatter from the reason.
64
65
  `--replace`/`--with`/`--source` attach an exact fix of its text once the
65
- agent has verified the correct fact, which akm checks at once and queues as
66
- a `feedback` proposal.
66
+ agent has verified the correct fact, which akm checks and applies at once
67
+ through a `feedback` proposal it accepts on the spot (no review).
67
68
  `--positive` records that the asset helped (it raises its ranking) and does
68
69
  not trigger a rewrite.
69
- 2. The feedback event is appended to `state.db`, and the asset's utility
70
- score is updated immediately via the bounded-step formula (below) — no
71
- reindex required.
70
+ 2. The feedback event is appended to `state.db`. `akm search` and `akm curate`
71
+ count a user's recorded positives and negatives per asset and scale the
72
+ fused score by them (logarithmic in each count, the same for every asset),
73
+ so ranking moves at once with no reindex. The asset's utility score, which
74
+ improve and metrics read, is recomputed immediately by the same formula
75
+ `akm index` uses (below).
72
76
  3. `akm improve` selects assets from the one bundle it writes to. A rewrite
73
77
  (reflect) is planned only for an asset with negative feedback in the last
74
78
  30 days that is newer than the stage's last attempt, or for an explicit ref;
@@ -80,10 +84,11 @@ everything else routes through `akm proposal accept`.
80
84
  reason or note, which gives the writer nothing to distil (10 of the 11
81
85
  lessons made from such memories were rejected). Unless
82
86
  `--require-feedback-signal` is set, the
83
- fallback lanes (high salience, and proactive maintenance where the strategy
84
- enables it) pick what the retrieval scope below admits, for scoring only:
85
- they plan nothing, so improve does not rewrite assets on a proactive
86
- cadence. It ranks the selected assets by salience, applies the limit, then
87
+ proactive-maintenance lane (where the strategy enables it, as `default`
88
+ does) adds the stable assets due for a revisit that the retrieval scope below
89
+ admits, and plans them with the feedback-bearing ones. At most `maxPerRun`
90
+ (15) assets are picked per run, an asset is due only if not reflected or
91
+ distilled for `dueDays` (30), and reflect's `limit` (25) bounds the run. It ranks the selected assets by salience, applies the limit, then
87
92
  runs whichever processes the selected strategy enables against each one (see
88
93
  [Improve Workflow](internals/improve-workflow.md#ledger-pre-filter-signal-delta)).
89
94
  4. Reflect and distill each emit at most one proposal per asset per run;
@@ -119,36 +124,39 @@ everything else routes through `akm proposal accept`.
119
124
  retires, go in the same commit. A commit that fails warns and the accept
120
125
  stands.
121
126
  8. For a git-backed bundle, `akm improve`'s end-of-run auto-sync commits the
122
- rest of the run's changes as a single batch and pushes (see below).
127
+ rest of the run's changes as a single batch and pushes (see below). The run
128
+ began with the same sync, so it started from what other hosts had pushed.
129
+
123
130
 
124
131
  ## Current decisions
125
132
 
126
133
  ### Utility scoring
127
134
 
128
- Utility moves by the MemRL bounded-step EMA formula (arXiv:2601.03192),
129
- implemented in `computeNextUtility` (`src/indexer/feedback/utility-policy.ts`):
135
+ Utility has one formula and one implementation, `recomputeUtilityScores`
136
+ (`src/indexer/indexer.ts`), counting only user-attributed (`source='user'`)
137
+ events:
130
138
 
131
139
  ```text
132
- reward = weighted average of positive (1.0) and negative (0.0) feedback in the batch
133
- nextUtil = clamp(currentUtil + FEEDBACK_LR * (reward - currentUtil), 0, 1)
140
+ rate = max(selectRate, max(0, positives - negatives) / (positives + negatives))
141
+ nextUtil = prevUtil * 0.7^days + rate * (1 - 0.7^days)
134
142
  ```
135
143
 
136
- `FEEDBACK_LR` is `0.1`, so a single feedback batch moves utility by at most
137
- 0.1 in either direction regardless of how lopsided the batch is — `reward` is
138
- a proportion of the counts, not their magnitude. When a previously
139
- high-utility asset (utility ≥ 0.5) crosses back below 0.5, the update is
140
- flagged as a review-threshold crossing so callers can escalate it. Decay is
141
- time-proportional rather than tied to index frequency, and usage history
142
- (and the utility it drives) is preserved across schema resets and full index
143
- rebuilds — see [Architecture: Utility Scoring](architecture.md#utility-scoring)
144
+ `akm index` runs it for every entry. `akm feedback` runs the same function for
145
+ the one entry it touched, so the value it stores is the one the next index
146
+ would compute at that moment (a one-entry call skips the usage-event purge
147
+ and leaves the recompute watermark alone). `days` is the time since the last
148
+ full recompute, at least 1, so decay is time-proportional rather than tied to
149
+ index frequency. When a previously high-utility asset (utility >= 0.5) falls
150
+ below 0.5 after feedback, an `improve_review_needed` event is emitted. Usage
151
+ history (and the utility it drives) is preserved across schema resets and full
152
+ index rebuilds - see [Architecture: Utility Scoring](architecture.md#utility-scoring)
144
153
  for the storage-level summary.
145
154
 
146
155
  ### Strategy inheritance
147
156
 
148
157
  Improve presets live under `improve.strategies` (config) and the built-in
149
- set: `default`, `quick`, `thorough`, `consolidate`, `catchup`,
150
- `reflect-distill`, and `proactive-maintenance`
151
- (`src/assets/improve-strategies/*.json`). Selection
158
+ set: `default` and `consolidate` (`src/assets/improve-strategies/*.json`). The removed `quick`, `thorough`,
159
+ `catchup`, `reflect-distill` and `proactive-maintenance` still work as user-defined strategies. Selection
152
160
  order is `--strategy`, then `defaults.improveStrategy`, then `default`.
153
161
 
154
162
  Resolution is a two-step deep merge (`resolveImproveStrategy`): a named
@@ -157,16 +165,16 @@ any user-defined override for that same name (under `improve.strategies` in
157
165
  config) is merged on top. So a strategy — built-in or user-defined — that
158
166
  omits a field, or an entire process block, inherits it from `default`; an
159
167
  explicit `enabled: true`/`false` in the more specific layer always wins. This
160
- is why, for example, `proactiveMaintenance` stays off in `default` and
161
- `reflect-distill`, but a preset that doesn't mention it at all still inherits
162
- that "off" rather than defaulting to on.
168
+ is why, for example, `proactiveMaintenance` is on in `default` and off in
169
+ `consolidate`, and a preset that doesn't mention it at all inherits the
170
+ `default` value. A user-defined strategy named `proactive-maintenance` (the name
171
+ of the retired built-in) resolves the same way, merged onto `default`.
163
172
 
164
173
  ### Retrieval scope
165
174
 
166
175
  Improve reworks only what gets read (#986). Fresh feedback and an explicit ref
167
176
  scope (`akm improve skills/x`) are usage evidence of their own. Every other
168
- pick — the proactive-maintenance and high-salience lanes (which only score what
169
- they pick), and the memories consolidation judges — must be in the retrieval
177
+ pick — the proactive-maintenance lane, and the memories consolidation judges — must be in the retrieval
170
178
  scope (`src/commands/improve/retrieval-scope.ts`):
171
179
 
172
180
  - **Retrieved:** a user-attributed `search`, `curate` or `show` returned the
@@ -207,7 +215,13 @@ Content passes only when **every** criterion scores 4 or more (a lesson's
207
215
  grounding is judged apart, see
208
216
  [distill](internals/improve-workflow.md#distill-akmdistill)); a mean of 3.5
209
217
  passed it before. A verdict that does not pass is a review when its mean is 2.5
210
- or more and a rejection below that. The rubric and the rule were chosen on 90
218
+ or more and a rejection below that. Reflect narrows that band: a revision whose
219
+ mean is 3.5 or more is queued for a person (reason `judge-uncertain`, with the
220
+ `scores` and `judgeReason`) and records no rejection window, and a lower mean is
221
+ refused. On 113 reviewed frontmatter-only proposals (lab 27B Q2, the shipped
222
+ judge) the non-passing ones at 3.5 or more were 17 good and 9 bad, the rest 1
223
+ good and 2 bad; on 396 labelled body rewrites a mean below 3.5 was good 6 times
224
+ in 291 (the constant and its sources are in `src/commands/improve/reflect.ts`). The rubric and the rule were chosen on 90
211
225
  labelled real rewrites, where judging on the mean let rewrites that only
212
226
  reworded a correct asset through.
213
227
 
@@ -217,7 +231,10 @@ both stay on the proposal when the drain accepts it, so a later audit can read
217
231
  why it passed (`akm proposal show --format json`). A distill pass is not staged:
218
232
  the lesson or promotion is minted `deferred` for a person (reason
219
233
  `distill-review`, same gate) with the same `scores` and `judgeReason`, and the
220
- triage drain and its judgment tier leave it alone. On 2026-10-05 the gate had
234
+ triage drain and its judgment tier leave it alone. A lesson that repeats an existing
235
+ lesson may instead be proposed as an update of that lesson (reason
236
+ `distill-update`, same gate), with only what the memory adds; the drain leaves
237
+ it for a person too. On 2026-10-05 the gate had
221
238
  staged 12 lessons and 10 were bad (they restated the memory, claimed what it
222
239
  does not say, or filed a dated status as a lesson), and no score separated them
223
240
  from the two good ones. Reflect revises only an asset's
@@ -225,34 +242,34 @@ from the two good ones. Reflect revises only an asset's
225
242
  those that fixed a frontmatter defect and left the body alone were good 24
226
243
  times in 26, and those that also rewrote the body were bad 175 times in 224.
227
244
 
228
- ### Retrieval regression gate
229
-
230
- Reflect refuses a revision of an existing asset that grades lower on the
231
- asset's own retrieval queries (#722, `src/commands/improve/retrieval-gate.ts`).
232
- After the quality judge passes, up to five distinct user `search`/`curate`
233
- queries that returned the asset (envelopes, the stash README line and inputs
234
- over 2,000 characters dropped) are each graded 0–3 against the old and the new
235
- content with the retrieval eval's prompt, `retrieval-relevance-judge.md`, and
236
- its document shape: type, ref, name, description and the first 1,500 body
237
- characters. A lower mean for the new content is a `quality_rejected` refusal
238
- with the reflect rejection window. A grade that cannot be obtained refuses the
239
- rewrite, as the quality judge fails closed. An asset with no such queries is
240
- not graded.
241
-
242
- The gate exists because it was measured first. Of 60 accepted reflect rewrites
243
- (since 2026-07-01, stratified by lane) judged this way, 14 graded lower (23%,
244
- 95% CI 14–35%) and 12 higher. Grading the same content twice flipped 11 of 174
245
- query grades, which puts 3 of 60 rewrites in the "lower" bucket by noise
246
- alone. The threshold for building it was fixed before judging: a lower bound of
247
- at least 10%.
245
+ ### Judgment tier: promotions only
246
+
247
+ The triage drain's judgment tier (`processes.triage.judgment`, off in every
248
+ built-in strategy) may accept only a consolidate promotion, a memory proposed as
249
+ a new knowledge note. A retirement or a reflect revision it would
250
+ accept is left for a person (`judgment-not-promotion`); it may still reject.
251
+ Measured on the nightly eval with `experimental.improveAutonomy` (#1132, #1094),
252
+ judging every kind solved 0.49 (gpt-5.6-terra) and 0.53 (qwen3.8-27b) of the
253
+ items, harmed 14 and 11, and changed 18 and 21 notes outside the planted set.
254
+ Judging promotions alone: precision 0.87, recall 0.78 and 4.8% of bad promotions
255
+ accepted on qwen3.8-27b; precision 1.0, recall 0.17 on gpt-5.6-terra.
256
+
257
+ ### Proactive-lane proposals wait for a person
258
+
259
+ A proposal planned by the proactive-maintenance lane (`eligibilitySource:
260
+ "proactive"`, an asset with no feedback behind it) is never accepted by the
261
+ drain, whether a deterministic gate (a staged quality-judge pass), the judgment
262
+ tier or an accept staged by an earlier drain would take it. It is left for a
263
+ person with reason `proactive-needs-review`, and the judgment tier does not see
264
+ it. An empty diff is still rejected. Proposals driven by feedback (`signal-delta`,
265
+ `scope`) follow the one rule unchanged (#1147).
248
266
 
249
267
  ### Retirement continuity
250
268
 
251
269
  Before the consolidate pair pass mints a `retire` proposal (rule R3,
252
270
  `src/commands/improve/consolidate/continuity-check.ts`), it replays up to five
253
271
  of the retired asset's own past `search`/`curate` queries
254
- (`loadRetrievalQueries`, the same cleaned set `../retrieval-gate.ts` replays
255
- for the retrieval regression gate — boilerplate, harness/tool envelopes,
272
+ (`loadRetrievalQueries`, `../retrieval-queries.ts` — boilerplate, harness/tool envelopes,
256
273
  pastes and near-duplicates dropped before replay, S3a) through akm's own
257
274
  search, in-process — the ranking a user actually gets, no LLM. For every
258
275
  query where the retired asset ranked in the top 10, the successor must too
@@ -297,8 +314,7 @@ but nothing assigns or emits it any more.
297
314
  ### Dry-run planning boundary
298
315
 
299
316
  Dry and live improve runs call the same selectors for signal-delta eligibility,
300
- the fallback lanes (proactive maintenance and high salience, which score and
301
- plan nothing), the retrieval scope, salience ranking, disk presence, and the
317
+ the proactive-maintenance lane, the retrieval scope, salience ranking, disk presence, and the
302
318
  final cap, and resolve the bundle they plan the same way: `--bundle`, else
303
319
  `defaultWriteTarget`, else the working bundle (`AKM_BUNDLE_DIR`, else
304
320
  `defaultBundle`). Each invocation reports a best-effort observation assembled
@@ -337,13 +353,20 @@ equals `effective`. When the run is unbounded, `totalCeiling` is omitted.
337
353
  candidates, validation, and proactive-maintenance selection are proposal-only
338
354
  and never write assets directly regardless of this gate.
339
355
  Three specific lanes *would* mutate assets without review and are downgraded
340
- unless `experimental.improveAutonomy` is explicitly set to `true`:
356
+ unless `experimental.improveAutonomy` is explicitly set to `true` (the triage
357
+ lane only when its judgment tier is on; see below):
341
358
 
342
359
  | Lane | What it does when enabled | With autonomy off |
343
360
  | --- | --- | --- |
344
361
  | `memoryInference` | Writes `.derived.md` children and rewrites parent frontmatter | disabled |
345
362
  | memory cleanup | Belief-state frontmatter rewrites, archive moves | analyzed but not applied |
346
- | `triage` `applyMode: "promote"` | Auto-accepts queued proposals into the bundle | downgraded to `queue` — triage still runs, it just does not auto-accept |
363
+ | `triage` `applyMode: "promote"` **with judgment on** | The judgment tier auto-accepts consolidate promotions into the bundle | downgraded to `queue` — triage still runs, it just does not auto-accept |
364
+
365
+ A deterministic-only `promote` (triage with judgment off) is not gated (#1143):
366
+ the `default` strategy drains the queue through the deterministic gates, up to
367
+ `maxAcceptsPerRun` (200) per run, with no opt-in. The nightly eval's baseline is
368
+ exactly this path (`proposal drain --promote` without judgment): 0.91 (terra) and
369
+ 0.89 (qwen) of items solved, 0 to 1 harmed.
347
370
 
348
371
  Every downgrade is reported, not silent: it warns on stderr, appends an
349
372
  `improve_skipped` event with `reason: "autonomy_gated"`, and is counted in
@@ -358,18 +381,34 @@ explicit promote surface independent of this gate.
358
381
 
359
382
  ### Auto-sync
360
383
 
361
- For git-backed bundles (detected by a `.git` directory), `akm improve`
362
- automatically commits its changes as a single batch at the end of the run —
363
- the same operation as `akm sync` — and pushes if the bundle is writable, per
364
- the active strategy's `sync` setting. The `reflect-distill` and
365
- `proactive-maintenance` strategies skip sync entirely, so an interrupted run
366
- does not leave an uncommitted backlog. `--no-sync` disables sync for a single
367
- run; `--no-push` commits without pushing. Strategy sync behavior is
368
- configured via the `sync` block under `improve.strategies.<name>`.
369
-
370
- The sync always commits the paths it is given. A branch it cannot push (no
371
- upstream branch, or behind or diverged from its upstream) only skips the push,
372
- and the result says why: `sync.reason` reads `not pushed: ...`. A branch that is
384
+ For git-backed bundles (detected by a `.git` directory), `akm improve` syncs
385
+ twice: once **before** the run and once at the end. Both are the same operation
386
+ as `akm sync` — fetch and merge the upstream, commit, and push if the bundle is
387
+ writable, per the active strategy's `sync` setting. A user-defined strategy can
388
+ turn sync off, so an interrupted run does not leave an uncommitted backlog.
389
+ `--no-sync` disables both for a single run; `--no-push` commits without pushing.
390
+ Strategy sync behavior is configured via the `sync` block under
391
+ `improve.strategies.<name>`.
392
+
393
+ The pre-run sync runs right after the run takes its lock and before the index
394
+ and plan stages, so they see what another host pushed. It stages what `akm sync`
395
+ stages (the managed paths, or the content subdirectory of a repo whose content
396
+ lives in one), which means pending hand edits there are committed (and pushed)
397
+ before the run starts, not mistaken for the run's own writes. It never aborts
398
+ the run: a merge that conflicts (aborted, local commit kept), a failed fetch, or
399
+ any other error is a warning, and the run continues on the local state. The
400
+ outcome is `preRunSync` on the result (same shape as `sync`) and a `stash_synced`
401
+ event with `phase: "pre-run"`. A dry run does not sync. Scheduled `core/improve`
402
+ and `akm-improve-consolidate` runs go through the same `akm improve` entry, so
403
+ they get it too.
404
+
405
+ The sync always commits the paths it is given. It then fetches the remote and
406
+ brings the upstream in (a fast-forward when it can, otherwise git's default
407
+ merge) before it pushes, so a second host that pushed first does not stop the
408
+ push. A branch it cannot push (no upstream branch, a failed fetch, or a merge
409
+ that conflicts, which is aborted with the local commit kept) only skips the push,
410
+ and the result says why: `sync.reason` reads `not pushed: ...`. A push the remote
411
+ refuses after the commit landed still reports `sync.committed: true`. A branch that is
373
412
  ahead of its upstream, as it is after the accept commits above, is pushed with
374
413
  the sync commit. A clean tree that is ahead is not pushed: the sync pushes what
375
414
  it commits.
@@ -13,7 +13,7 @@
13
13
  | `--limit` | `number` | Cap the number of assets processed, taken from the salience ranking, highest first (refs routed to distill only come last). |
14
14
  | `--timeout-ms` | `number` | Wall-clock budget for the entire run. Default: 7 200 000 ms (2 hours). |
15
15
  | `--skip-if-locked` | `boolean` | If another improve owns the whole-run lock, return an exit-0 no-op result before triage, indexing, events, or sync. Without the flag, contention is a transient error (`IMPROVE_LOCK_HELD`, exit 75). |
16
- | `--require-feedback-signal` | `boolean` | Turn the fallback lanes (high salience, proactive maintenance) off for the run: they only select and score assets, and a rewrite needs negative feedback. |
16
+ | `--require-feedback-signal` | `boolean` | Turn the proactive-maintenance lane off for the run, so only assets with recent feedback are planned. |
17
17
 
18
18
  Injected function seams (`reflectFn`, `distillFn`, `ensureIndexFn`, `reindexFn`) replace production defaults in tests.
19
19
 
@@ -49,7 +49,7 @@ flowchart TD
49
49
  P -- yes --> P1[Log failures; refs that still fail\nare excluded from selection]
50
50
  P -- no --> L[Signal delta\nreflect: negative feedback, distill: any signal,\nnewer than the last ledger attempt\nand no hard ledger window]
51
51
  P1 --> L
52
- L --> L2[Fallback lanes\nproactive maintenance, high salience:\nonly retrieved or new material,\nscored and never planned]
52
+ L --> L2[Proactive maintenance lane:\nonly retrieved or new material,\nmaxPerRun picks, due after dueDays]
53
53
  L2 --> M[scoreSalience\nsalience vector per ref: encoding, outcome, retrieval\nutility scores from SQLite seed the outcome term]
54
54
  M --> N[Sort by salience rank DESC, no-op dampened\ndrop refs missing on disk\napply --limit if set]
55
55
  N --> Q
@@ -100,7 +100,7 @@ flowchart TD
100
100
  DISTILL_G -- no --> DISTILL_J{quality gate judge:\nreusable, non-redundancy, grounding}
101
101
  DISTILL_J -- pass --> DISTILL_H[createProposal lessons/slug-lesson\nor knowledge/slug\nsource: distill, gate decision deferred\nappendEvent: outcome=queued]
102
102
  DISTILL_J -- mean in the review band --> DISTILL_REVIEW[createProposal, gate decision deferred\nappendEvent: outcome=review_needed]
103
- DISTILL_J -- any criterion 2 or below, or the mean too low --> DISTILL_REJECT[improve_ledger row, no proposal\nappendEvent: outcome=quality_rejected]
103
+ DISTILL_J -- any criterion 2 or below, or the mean too low --> DISTILL_REJECT[improve_ledger row, no proposal\nappendEvent: outcome=quality_rejected\nunless a repeated lesson gets an update proposal, step 10]
104
104
  DISTILL_PROMOTE --> DISTILL_RETURN
105
105
  DISTILL_SKIP --> DISTILL_RETURN
106
106
  DISTILL_EXISTS --> DISTILL_RETURN
@@ -150,7 +150,7 @@ flowchart TD
150
150
  PAIR_INIT --> PAIR_CAND[Candidates: k=5 nearest by stored vector,\nsame bundle + memory tier, cosine >= T_pair]
151
151
  PAIR_CAND --> PAIR_JUDGE[One LLM call per pair:\nconsolidate-pair.md, 6-label schema]
152
152
  PAIR_JUDGE --> PAIR_OUT{judge label}
153
- PAIR_OUT -- duplicate / subsumed / supersedes --> PAIR_GUARDS[Guards: hot capture, derived parent,\nrejected-pair match, same-run chain]
153
+ PAIR_OUT -- duplicate / subsumed / supersedes --> PAIR_GUARDS[Guards: derived parent,\nrejected-pair match, same-run chain]
154
154
  PAIR_GUARDS --> PAIR_CONTINUITY[Continuity check R3:\nreplay retired asset's own queries,\nflag continuityRisk, never block]
155
155
  PAIR_CONTINUITY --> PAIR_PROPOSE[emitProposal: retire,\nsource: consolidate-pair]
156
156
  PAIR_OUT -- overlap / unrelated / contradicts --> PAIR_NOACTION[judged_no_action\nno 7-day timer — content change only]
@@ -209,9 +209,8 @@ Reflect changes only an asset's `description`, `when_to_use` and title. The engi
209
209
  contract instead.
210
210
  6. Validate the reply with `parseSchemaReflectOutput` (`parseFramedReflectOutput` for the framed contract), which strips `<think>` blocks and code fences, then requires exactly the contract's fields: `confidence` and a `frontmatterPatch` of `description`, `when_to_use` and `title` (each a non-empty single-line string, or `null` for no change), plus `ref` when no target was given. A reply that fails gets one repair turn, shared across self-refine passes: the conversation so far, and a request to reformat the reply in the contract (`reflect-output-repair.md`). A reply still invalid fails with `parse_error` and queues nothing. A failed agent or SDK dispatch is reported as it ran, with its exit code and stderr.
211
211
  7. Apply the patch (`applyReflectPatch`) to the source asset: its frontmatter with the non-null `description` and `when_to_use` set, and its body unchanged. A non-null `title` becomes a `# <title>` heading and one blank line at the top of the body, only when the body has no level-1 heading; otherwise it is ignored. A source that requires a `description` and has none gets one derived from its own text when the patch gave none (#636). A patch that changes nothing (every field `null`, or equal to the source's, and no `description` to derive) is the `no_change` outcome (`reflect_skipped_noop`), as is a source with no content to patch; a result that differs only cosmetically is `reflect_skipped_cosmetic`. Neither creates a proposal.
212
- 8. Quality gate (`processes.reflect.qualityGate`, on unless disabled): one judge call scores the revision against the source on **need**, **preservation** and **quality** ([Quality judge](../improvement.md#quality-judge)), and passes it only when every criterion scores 4 or more. A score that does not pass is refused (`quality_rejected`, no proposal). A judge that times out, errors or replies unparseably gives no verdict, and the proposal is deferred for review (`judge-error`, gate `quality-gate`). Before the judge, with the gate on or off, reflect refuses (`quality_rejected`, no proposal, no judge call; the rule is the event's `reflectDefect`) a revision that adds placeholder text (`placeholder_added`: "please confirm", "to be confirmed"; not `TODO`, `TBD` or `FIXME`), talks about its own edit (`meta_commentary_added`: "the feedback says", "this revision") or copies frontmatter into its body (`frontmatter_copied_into_body`: `sources:` or `updated:` lines outside code, a `sources` value). Each rule counts only what the revision adds to its source (`findReflectDefect`), and its wording is a list that `processes.reflect.defectFilter` replaces or, when empty, turns off ([Configuration](../../reference/configuration.md#strategies)).
213
- 9. Retrieval regression gate, after a pass on an existing asset: a revision that grades lower on the asset's own retrieval queries is refused ([Retrieval regression gate](../improvement.md#retrieval-regression-gate)).
214
- 10. Write the proposal (`mintProposal`). A pass the judge passed is stamped `staged`, and the triage drain accepts it; with the gate off the proposal is minted unstamped for the drain to decide. One made with no judge configured (`no-judge-configured`) is deferred for review (gate `reflect`), and so is one whose judge gave no verdict (`judge-error`). The drain leaves every deferral for a person.
212
+ 8. Quality gate (`processes.reflect.qualityGate`, on unless disabled): one judge call scores the revision against the source on **need**, **preservation** and **quality** ([Quality judge](../improvement.md#quality-judge)), and passes it only when every criterion scores 4 or more. A score that does not pass is refused (`quality_rejected`, no proposal) unless its mean is 3.5 or more: that is uncertain, not bad, and the proposal is deferred for a person (`judge-uncertain`, gate `quality-gate`, with the criterion `scores` and the judge's reason) and records no rejection window. A judge that times out, errors or replies unparseably gives no verdict, and the proposal is deferred for review too (`judge-error`, gate `quality-gate`). Before the judge, with the gate on or off, reflect refuses (`quality_rejected`, no proposal, no judge call; the rule is the event's `reflectDefect`) a revision that adds placeholder text (`placeholder_added`: "please confirm", "to be confirmed"; not `TODO`, `TBD` or `FIXME`), talks about its own edit (`meta_commentary_added`: "the feedback says", "this revision") or copies frontmatter into its body (`frontmatter_copied_into_body`: `sources:` or `updated:` lines outside code, a `sources` value). Each rule counts only what the revision adds to its source (`findReflectDefect`), and its wording is a list that `processes.reflect.defectFilter` replaces or, when empty, turns off ([Configuration](../../reference/configuration.md#strategies)).
213
+ 9. Write the proposal (`mintProposal`). A pass the judge passed is stamped `staged`, and the triage drain accepts it; with the gate off the proposal is minted unstamped for the drain to decide. One made with no judge configured (`no-judge-configured`) is deferred for review (gate `reflect`), and so is one whose judge gave no verdict (`judge-error`) and one in the judge's uncertain band (`judge-uncertain`). `akm proposal list` shows the reason; the drain leaves every deferral for a person.
215
214
 
216
215
  **What it writes:** one durable proposal row in `state.db`. It never writes asset files directly.
217
216
 
@@ -245,10 +244,17 @@ Reflect changes only an asset's `description`, `when_to_use` and title. The engi
245
244
  - **Grounding** asks whether every cause, step, number, rule and limit in the lesson is stated by the source or its feedback; 4–5 when each is, 3 when one stretches what the source says, 1–2 when any is in neither, when the lesson drops a limit the source states, or when it is about another subject. It is not part of the mean.
246
245
  - A criterion at 2 or below is `quality_rejected` whatever the mean is (the reason names the criterion: `grounding 2/5: …`). Otherwise the lesson passes when reusable and non-redundancy both score 4 or more, and a mean of those two of 2.5 or more is `review_needed`. A pass is also queued for a person (step 9), so the rejection is the only thing that keeps a lesson from a reviewer: on 2026-10-05 the old rubric (novelty, non-redundancy, and a grounding that scored 3 for a lesson that "goes beyond the source") passed 10 of the 17 bad lessons, because a lesson that adds to its memory scored as novel. The old novelty question was also the reason the judge rejected a faithful lesson of a lesson-worthy memory. A distill with no source to read (an unindexed ref distilled "from feedback signal alone") gives the judge an empty source, so its lesson is expected to be rejected.
247
246
  - `quality_rejected` writes an `improve_ledger` row for the input (30-day distill rejection window) and a `distill_invoked` event carrying `score`, the per-criterion `criteria` and the `reason`. It mints no proposal.
248
- - `review_needed` mints a pending proposal stamped `deferred` / `quality-gate` for a human; the triage drain leaves it alone. It is the outcome for a non-passing mean of 2.5 or more when no criterion is at 2 or below, a judge that times out or returns something unparseable or incomplete, the optional fidelity check's contradiction (`processes.distill.fidelityCheck.enabled`, off by default; a lesson that contradicts its source reaches a human this way, not through grounding), and a heuristic lesson-quality finding (for example an invalid `description`), which is found before the judge is called.
247
+ - `review_needed` mints a pending proposal stamped `deferred` / `quality-gate` for a human; the triage drain leaves it alone. It is the outcome for a non-passing mean of 2.5 or more when no criterion is at 2 or below, a judge that times out, returns something unparseable or incomplete, or is missing when the lesson or promotion is judged, and a heuristic lesson-quality finding (for example an invalid `description`), which is found before the judge is called.
249
248
  9. Create proposal: `createProposal(stash, { ref: lessonRef, source: "distill", payload })` through `mintProposal`. A pass is minted `deferred` (gate `quality-gate`, reason `distill-review`) with the judge's per-criterion `scores` and `judgeReason`, for a person: the triage drain and its judgment tier leave it alone, and the improve ledger records `review_needed`. It is never `staged`. On 2026-10-05 the gate had staged 12 lessons and 10 were bad (they restated the memory, claimed what it does not say, or filed a dated status as a lesson); no judge score separated them from the two good ones. A promotion to knowledge that passes is deferred the same way. With `processes.distill.qualityGate` off nothing was judged, so the proposal is minted unstamped for the drain to decide.
250
249
  10. Emit `distill_invoked` event with `outcome: "queued"`.
251
250
 
251
+ **Updating a lesson the memory repeats (#1090):** when the lesson repeats one the library holds, distill extends that lesson instead of only dropping the duplicate. It happens in two places, both with the quality gate on: the judge scored the writer's lesson 2 or below on non-redundancy (step 8), or the writer answered `NONE` (step 5). Either way the update is tried for the related lessons (among the top 3 nearby assets, as for the judge, in the bundle the proposal is filed in) that the writer's `reason`, or the judge's, names by ref or name, or for the only related lesson when it names none. One lesson among several that merely sit near the memory costs no call. A second call shows the writer the memory, its feedback and the candidate lessons' bodies, and asks it to pick the lesson that already states the memory's rule, list in `new_facts` each cause, step, number, limit or consequence the memory states and the lesson lacks (the memory's own incident retold is not one), and return that lesson's whole body with a line added for each; it answers `decision: "none"` when no listed lesson states the rule, when `new_facts` is empty (an update with none is not proposed), or when the memory contradicts a line. Then:
252
+
253
+ - Every existing body line must still be there (compared by words, in any case and spacing). A body that drops or rewords one is not proposed.
254
+ - The extended lesson (existing frontmatter and the whole new body) is judged as one lesson, in one call, by step 8's judge with an edit variant of its rubric: **reusable** and **grounding** read the whole lesson (a line is grounded when the memory, its feedback or the lesson being extended states it), and **non-redundancy** is asked of each added line against the lesson being extended: 1–2 when it only says again what the lesson says (its rule or cause in other words, or the same rule on an example from the memory), 4–5 when it states a fact the lesson lacks, however specific. The lesson being extended is shown to the judge as such, not as an asset the new lesson might repeat. Judging the added lines alone as a new lesson failed `reusable` for a lone fact, and no update passed. A body that adds no line is not proposed; anything short of a judge pass is not proposed.
255
+ - The update is a pending proposal on the existing lesson's ref (`source: distill`), the lesson's frontmatter unchanged except that the memory is added to `xrefs`. Its `beforeHash` is the lesson's as read, so accepting it after the lesson changed is refused as stale. It is minted `deferred` (gate `quality-gate`, reason `distill-update`) with the judge's `scores` and `judgeReason`, so the triage drain and its judgment tier leave it for a person. The result and the `distill_invoked` event are `queued` with `proposalRef` set to the lesson and `updatesExisting: true`.
256
+ - When nothing is proposed, the outcome is what it was before: `quality_rejected` with the judge's reason after a duplicate lesson, `skipped` / `nothing_reusable` after `NONE`.
257
+
252
258
  **Lesson-ref derivation rule:** `lessons/<type>-<name>-lesson` where `<type>-<name>` is derived from the input ref with origin stripped and non-alphanumeric characters replaced by `-`. Example: `skills/deploy` → `lessons/skill-deploy-lesson`. A lesson already at that ref is never overwritten (see step 4).
253
259
 
254
260
  **What it writes:** one durable proposal row in `state.db`. Never writes asset files directly.
@@ -378,8 +384,8 @@ duplicate, subsumed and superseding retirement, review-gated.
378
384
  body characters.
379
385
  4. **Outcome:** `duplicate`, `subsumed` and `supersedes` mint one `retire`
380
386
  proposal (source `consolidate-pair`, its own generator — see below) for
381
- the side that does not survive — never a `captureMode: hot` memory, never
382
- a `.derived` memory whose parent still exists, never across bundles,
387
+ the side that does not survive — never a
388
+ `.derived` memory whose parent still exists, never across bundles,
383
389
  never when either side already has a pending retire proposal (as the
384
390
  retired ref or its successor), and never retiring or reusing as a
385
391
  successor an asset already spent earlier in the SAME run (the durable
@@ -478,8 +484,8 @@ proposal.
478
484
 
479
485
  Before the pair pass mints a `retire` proposal, it replays up to five of the
480
486
  retired asset's own past `search`/`curate` queries
481
- (`loadRetrievalQueries`, `src/commands/improve/retrieval-gate.ts` — the same
482
- cleaned set the retrieval regression gate replays: `usableRetrievalQueries`
487
+ (`loadRetrievalQueries`, `src/commands/improve/retrieval-queries.ts`:
488
+ `usableRetrievalQueries`
483
489
  drops stash-README boilerplate and harness/tool envelopes
484
490
  (`nonTaskInput`), pastes over 2,000 characters, and queries that are
485
491
  duplicates once whitespace is collapsed, S3a) through akm's own search,
@@ -610,7 +616,7 @@ Two proposals can share the same `ref`; their UUID primary keys prevent collisio
610
616
 
611
617
  ## Scope restrictions
612
618
 
613
- `akm improve` reads and writes one bundle, its write target: `--bundle`, else `defaultWriteTarget`, else the working bundle. The candidate set is that bundle's assets and nothing else, because every proposal is filed in the write target. An asset that another writable bundle owns is never planned: reflect would read it from its own bundle and the proposal would land in this one, either forking the asset as a `create` or overwriting this bundle's copy with content taken from the other's (#1000). A bare ref scope (`akm improve skills/x`) resolves inside the write target only, and distill's memory-to-knowledge promotion merges only with a doc that already exists there. To improve another writable bundle, select it with `--bundle team` or a bundle-qualified scope such as `team//skills/code-review`; a scheduled run therefore covers only its write target, and each other bundle needs its own scheduled `akm improve --bundle <name>` run. A dry run (`--dry-run`, `--plan`) resolves the bundle the way a live run does, with the working bundle starting from `AKM_BUNDLE_DIR` before `defaultBundle`, so it previews the bundle a live run improves. As a second line of defence, `createProposal` refuses a proposal whose `itemRef` names an asset owned by a different configured bundle than the queue's.
619
+ `akm improve` reads and writes one bundle, its write target: `--bundle`, else `defaultWriteTarget`, else the working bundle. The candidate set is that bundle's assets and nothing else, because every proposal is filed in the write target. An asset that another writable bundle owns is never planned: reflect would read it from its own bundle and the proposal would land in this one, either forking the asset as a `create` or overwriting this bundle's copy with content taken from the other's (#1000). A bare ref scope (`akm improve skills/x`) resolves inside the write target only, and distill's memory-to-knowledge promotion merges only with a doc that already exists there. To improve another writable bundle, select it with `--bundle team` or a bundle-qualified scope such as `team//skills/code-review`; a scheduled run therefore covers only its write target, and each other bundle needs its own scheduled `akm improve --bundle <name>` run. A dry run (`--dry-run`) resolves the bundle the way a live run does, with the working bundle starting from `AKM_BUNDLE_DIR` before `defaultBundle`, so it previews the bundle a live run improves. As a second line of defence, `createProposal` refuses a proposal whose `itemRef` names an asset owned by a different configured bundle than the queue's.
614
620
 
615
621
  `akm improve` and `akm lint` only operate on writable bundle sources (sources with `writable: true`). Read-only sources (git, npm, website) are excluded from the candidate set before any other filtering; a read-only write target plans nothing.
616
622
 
@@ -622,12 +628,12 @@ Every stage reads the improve ledger (`improve_ledger` in `state.db`, one row pe
622
628
 
623
629
  - A ref that passes reflect is planned for the loop. If it does not pass distill, distill is skipped for it (a `distill-skipped` action and an `improve_skipped` event with reason `distill_no_new_signal`).
624
630
  - A ref that passes only distill, and is a distill candidate, is planned distill-only.
625
- - A ref with no in-window feedback and no reflect window is left to the fallback lanes (proactive maintenance and high salience), which pick only what retrieval returned or new material (see [Retrieval scope](../improvement.md#retrieval-scope)). The lanes only select and score: a ref they pick is not planned for reflect or distill, so improve does not rewrite assets on a proactive cadence.
626
- - Every ref the loop does not take is counted in the plan's `signal` gate (or its `retrieval` gate, when the fallback lanes could not pick it for lack of usage evidence) and reported once, in aggregate, as an `improve_skipped` event (`no_new_signal`, `not_retrieved`).
631
+ - A ref with no in-window feedback and no reflect window is left to the proactive-maintenance lane, which picks only what retrieval returned or new material (see [Retrieval scope](../improvement.md#retrieval-scope)) and plans its picks with the feedback-bearing refs. The cap against a proposal flood: `maxPerRun` (15) picks per run, an asset due only after `dueDays` (30) without a reflect or distill, and reflect's `limit` (25).
632
+ - Every ref the loop does not take is counted in the plan's `signal` gate (or its `retrieval` gate, when the proactive lane could not pick it for lack of usage evidence) and reported once, in aggregate, as an `improve_skipped` event (`no_new_signal`, `not_retrieved`).
627
633
  - A memory flagged wrong is not distilled: when a negative feedback in the 30-day window recorded the hash of the body it judged (`contentHash`, the `body` hash of `content-hash.ts`) and the file's body still has it, the loop skips distill for it (a `distill-skipped` action and an `improve_skipped` event with reason `distill_flagged_wrong`) and records the attempt in the ledger as `unchanged`, so the ref waits for newer feedback. Reflect still plans it. A write that leaves the body alone (an inference stamp, a frontmatter repair) does not lift the flag; changing the body does. Feedback recorded without a hash keeps the earlier test: it flags the memory while it is newer than the file's last write, whose modification time is the edit signal, as for the retrieval scope's new material.
628
634
  - A memory whose only feedback in the 30-day window is positive without a reason or note (`hasOnlyBarePositiveFeedback` in `preparation.ts`) is not distilled either: the loop skips distill for it (a `distill-skipped` action with the reason "only positive feedback, without a reason" and an `improve_skipped` event with reason `distill_positive_without_reason`) and records the attempt in the ledger as `unchanged`, so it waits for newer feedback. A bare `--positive` only records that a note helped, which gives the writer nothing to distil: 10 of the 11 lessons made from such a memory were rejected on 2026-10-05, and the 11th was good. A reason, a note or a negative signal among the feedback, or an explicit ref scope, lets it through.
629
635
  - A memory whose frontmatter says `beliefState: deprecated` or `superseded` (`isDeprecatedOrSuperseded` in `preparation.ts`) is not distilled either: the loop skips distill for it (a `distill-skipped` action with the reason "marked deprecated or superseded" and an `improve_skipped` event with reason `distill_deprecated_or_superseded`) and records the attempt in the ledger as `unchanged`. An explicit `--scope` ref still runs. `contradicted` is not skipped: it marks an open conflict, not a retired note.
630
- - The loop's refs are ranked by salience (`scoreSalience` in `preparation.ts`, which also scores the fallback lanes' picks and computes each vector with `computeSalience` from `salience.ts`): encoding, outcome, and retrieval frequency and recency, discounted for file size, with a ref that was repeatedly skipped as a no-op ranked lower. Refs missing on disk are dropped, and `--limit` cuts the list: reflect-path refs first, then distill-only refs.
636
+ - The loop's refs are ranked by salience (`scoreSalience` in `preparation.ts`, which also scores the lane's picks and computes each vector with `computeSalience` from `salience.ts`): encoding, outcome, and retrieval frequency and recency, discounted for file size, with a ref that was repeatedly skipped as a no-op ranked lower. Refs missing on disk are dropped, and `--limit` cuts the list: reflect-path refs first, then distill-only refs.
631
637
 
632
638
  An explicit ref scope bypasses every gate. Consolidation, extract and schema repair read their own ledger sources in their own stages.
633
639
 
@@ -429,7 +429,7 @@ so a layout change is applied in place rather than by discarding the index.
429
429
  Durable workflow, task, proposal, event, and usage state in `state.db` is
430
430
  never touched by these paths.
431
431
 
432
- Workflow `.md` and `.yml` adapters compile directly to source IR version 1.
432
+ The workflow `.md` adapter compiles directly to source IR version 1.
433
433
  The index stores only the ordinary normalized `entries` row and searchable
434
434
  metadata derived from that IR. It does not cache a second workflow AST or an
435
435
  executable plan. Starting a run recompiles the authored source once and freezes