akm-cli 0.9.0-beta.5 → 0.9.0-beta.51

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 (221) hide show
  1. package/CHANGELOG.md +711 -0
  2. package/README.md +12 -4
  3. package/dist/akm +38 -0
  4. package/dist/akm-migrate-storage +38 -0
  5. package/dist/assets/profiles/default.json +9 -4
  6. package/dist/assets/profiles/frequent.json +1 -1
  7. package/dist/assets/profiles/memory-focus.json +1 -1
  8. package/dist/assets/profiles/quick.json +1 -1
  9. package/dist/assets/profiles/synthesize.json +15 -0
  10. package/dist/assets/profiles/thorough.json +1 -1
  11. package/dist/assets/prompts/consolidate-system.md +23 -0
  12. package/dist/assets/prompts/contradiction-judge.md +33 -0
  13. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  14. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  15. package/dist/assets/prompts/extract-session.md +6 -2
  16. package/dist/assets/prompts/graph-extract-system.md +1 -0
  17. package/dist/assets/prompts/graph-extract-user-prompt.md +1 -1
  18. package/dist/assets/prompts/memory-infer-system.md +1 -0
  19. package/dist/assets/prompts/memory-infer-user.md +5 -0
  20. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  21. package/dist/assets/prompts/procedural-system.md +44 -0
  22. package/dist/assets/prompts/recombine-system.md +40 -0
  23. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  24. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  25. package/dist/assets/stash-skeleton/facts/conventions/assets/agent.md +38 -0
  26. package/dist/assets/stash-skeleton/facts/conventions/assets/command.md +38 -0
  27. package/dist/assets/stash-skeleton/facts/conventions/assets/fact.md +39 -0
  28. package/dist/assets/stash-skeleton/facts/conventions/assets/knowledge.md +40 -0
  29. package/dist/assets/stash-skeleton/facts/conventions/assets/lesson.md +43 -0
  30. package/dist/assets/stash-skeleton/facts/conventions/assets/memory.md +38 -0
  31. package/dist/assets/stash-skeleton/facts/conventions/assets/script.md +43 -0
  32. package/dist/assets/stash-skeleton/facts/conventions/assets/skill.md +40 -0
  33. package/dist/assets/stash-skeleton/facts/conventions/assets/workflow.md +43 -0
  34. package/dist/assets/templates/html/health.html +281 -111
  35. package/dist/assets/wiki/ingest-workflow-template.md +38 -10
  36. package/dist/cli/parse-args.js +46 -1
  37. package/dist/cli/shared.js +28 -0
  38. package/dist/cli.js +27 -11
  39. package/dist/commands/agent/agent-dispatch.js +2 -2
  40. package/dist/commands/agent/agent-support.js +0 -7
  41. package/dist/commands/agent/contribute-cli.js +17 -4
  42. package/dist/commands/config-cli.js +18 -2
  43. package/dist/commands/env/child-env.js +47 -0
  44. package/dist/commands/env/env-cli.js +33 -26
  45. package/dist/commands/env/secret-cli.js +36 -22
  46. package/dist/commands/feedback-cli.js +15 -6
  47. package/dist/commands/graph/graph-cli.js +5 -13
  48. package/dist/commands/graph/graph.js +76 -72
  49. package/dist/commands/health/checks.js +49 -1
  50. package/dist/commands/health/html-report.js +422 -80
  51. package/dist/commands/health.js +386 -9
  52. package/dist/commands/improve/calibration.js +161 -0
  53. package/dist/commands/improve/consolidate/chunking.js +141 -0
  54. package/dist/commands/improve/consolidate/eligibility.js +81 -0
  55. package/dist/commands/improve/consolidate/merge.js +145 -0
  56. package/dist/commands/improve/consolidate/sanitize.js +231 -0
  57. package/dist/commands/{lint.js → improve/consolidate/types.js} +1 -1
  58. package/dist/commands/improve/consolidate.js +635 -660
  59. package/dist/commands/improve/dedup.js +482 -0
  60. package/dist/commands/improve/distill.js +159 -69
  61. package/dist/commands/improve/eligibility.js +434 -0
  62. package/dist/commands/improve/encoding-salience.js +205 -0
  63. package/dist/commands/improve/extract-cli.js +124 -2
  64. package/dist/commands/improve/extract-prompt.js +39 -2
  65. package/dist/commands/improve/extract-watch.js +140 -0
  66. package/dist/commands/improve/extract.js +389 -40
  67. package/dist/commands/improve/feedback-valence.js +54 -0
  68. package/dist/commands/improve/homeostatic.js +467 -0
  69. package/dist/commands/improve/improve-auto-accept.js +138 -7
  70. package/dist/commands/improve/improve-cli.js +36 -61
  71. package/dist/commands/improve/improve-profiles.js +14 -0
  72. package/dist/commands/improve/improve-result-file.js +14 -25
  73. package/dist/commands/improve/improve-session.js +58 -0
  74. package/dist/commands/improve/improve.js +485 -2498
  75. package/dist/commands/improve/locks.js +154 -0
  76. package/dist/commands/improve/loop-stages.js +1083 -0
  77. package/dist/commands/improve/memory/memory-contradiction-detect.js +23 -28
  78. package/dist/commands/improve/outcome-loop.js +256 -0
  79. package/dist/commands/improve/preparation.js +1966 -0
  80. package/dist/commands/improve/proactive-maintenance.js +115 -0
  81. package/dist/commands/improve/procedural.js +418 -0
  82. package/dist/commands/improve/recombine.js +850 -0
  83. package/dist/commands/improve/reflect-noise.js +0 -0
  84. package/dist/commands/improve/reflect.js +183 -40
  85. package/dist/commands/improve/salience.js +438 -0
  86. package/dist/commands/improve/triage.js +93 -0
  87. package/dist/commands/lint/agent-linter.js +19 -24
  88. package/dist/commands/lint/base-linter.js +173 -60
  89. package/dist/commands/lint/command-linter.js +19 -24
  90. package/dist/commands/lint/env-key-rules.js +38 -1
  91. package/dist/commands/lint/fact-linter.js +39 -0
  92. package/dist/commands/lint/index.js +31 -13
  93. package/dist/commands/lint/memory-linter.js +1 -1
  94. package/dist/commands/lint/registry.js +7 -2
  95. package/dist/commands/lint/task-linter.js +3 -3
  96. package/dist/commands/lint/workflow-linter.js +26 -1
  97. package/dist/commands/proposal/drain-policies.js +5 -0
  98. package/dist/commands/proposal/drain.js +43 -50
  99. package/dist/commands/proposal/proposal-cli.js +21 -31
  100. package/dist/commands/proposal/proposal.js +5 -0
  101. package/dist/commands/proposal/propose.js +7 -2
  102. package/dist/commands/proposal/validators/proposal-quality-validators.js +9 -8
  103. package/dist/commands/proposal/validators/proposals.js +189 -63
  104. package/dist/commands/read/curate.js +414 -94
  105. package/dist/commands/read/knowledge.js +6 -3
  106. package/dist/commands/read/search-cli.js +9 -4
  107. package/dist/commands/read/search.js +10 -6
  108. package/dist/commands/read/show.js +86 -7
  109. package/dist/commands/sources/init.js +49 -17
  110. package/dist/commands/sources/installed-stashes.js +11 -3
  111. package/dist/commands/sources/schema-repair.js +43 -45
  112. package/dist/commands/sources/self-update.js +2 -2
  113. package/dist/commands/sources/source-add.js +7 -3
  114. package/dist/commands/sources/stash-cli.js +28 -40
  115. package/dist/commands/sources/stash-skeleton.js +23 -8
  116. package/dist/commands/tasks/tasks-cli.js +19 -27
  117. package/dist/commands/tasks/tasks.js +39 -11
  118. package/dist/commands/wiki-cli.js +21 -35
  119. package/dist/core/asset/asset-registry.js +3 -1
  120. package/dist/core/asset/asset-spec.js +18 -2
  121. package/dist/core/asset/frontmatter.js +166 -167
  122. package/dist/core/asset/markdown.js +8 -0
  123. package/dist/core/authoring-rules.js +92 -0
  124. package/dist/core/common.js +0 -5
  125. package/dist/core/config/config-migration.js +12 -11
  126. package/dist/core/config/config-schema.js +340 -56
  127. package/dist/core/config/config-types.js +3 -3
  128. package/dist/core/config/config.js +28 -7
  129. package/dist/core/events.js +3 -7
  130. package/dist/core/improve-types.js +11 -8
  131. package/dist/core/logs-db.js +10 -66
  132. package/dist/core/parse.js +36 -16
  133. package/dist/core/paths.js +3 -0
  134. package/dist/core/standards/resolve-standards-context.js +87 -0
  135. package/dist/core/standards/resolve-stash-standards.js +99 -0
  136. package/dist/core/standards/resolve-type-conventions.js +66 -0
  137. package/dist/core/state/migrations.js +714 -0
  138. package/dist/core/state-db.js +525 -474
  139. package/dist/indexer/db/db.js +439 -247
  140. package/dist/indexer/db/graph-db.js +129 -86
  141. package/dist/indexer/ensure-index.js +152 -17
  142. package/dist/indexer/graph/graph-boost.js +51 -41
  143. package/dist/indexer/graph/graph-extraction.js +218 -4
  144. package/dist/indexer/index-writer-lock.js +99 -0
  145. package/dist/indexer/indexer.js +123 -221
  146. package/dist/indexer/passes/dir-staleness.js +114 -0
  147. package/dist/indexer/passes/memory-inference.js +13 -5
  148. package/dist/indexer/passes/staleness-detect.js +2 -5
  149. package/dist/indexer/search/db-search.js +19 -6
  150. package/dist/indexer/search/ranking-contributors.js +22 -0
  151. package/dist/indexer/search/ranking.js +4 -0
  152. package/dist/indexer/search/search-source.js +17 -18
  153. package/dist/indexer/search/semantic-status.js +4 -0
  154. package/dist/indexer/walk/matchers.js +9 -0
  155. package/dist/integrations/agent/config.js +6 -53
  156. package/dist/integrations/agent/index.js +2 -18
  157. package/dist/integrations/agent/prompts.js +75 -9
  158. package/dist/integrations/agent/runner-dispatch.js +59 -0
  159. package/dist/integrations/harnesses/claude/session-log.js +11 -1
  160. package/dist/integrations/harnesses/index.js +2 -3
  161. package/dist/integrations/harnesses/opencode/session-log.js +173 -3
  162. package/dist/integrations/harnesses/opencode-sdk/index.js +2 -2
  163. package/dist/integrations/harnesses/opencode-sdk/sdk-runner.js +0 -2
  164. package/dist/integrations/session-logs/index.js +16 -0
  165. package/dist/llm/client.js +45 -15
  166. package/dist/llm/embedder.js +42 -3
  167. package/dist/llm/embedders/deterministic.js +66 -0
  168. package/dist/llm/embedders/local.js +66 -2
  169. package/dist/llm/feature-gate.js +8 -4
  170. package/dist/llm/graph-extract.js +67 -44
  171. package/dist/llm/memory-infer-impl.js +138 -0
  172. package/dist/llm/memory-infer.js +1 -127
  173. package/dist/llm/metadata-enhance.js +44 -31
  174. package/dist/llm/structured-call.js +49 -0
  175. package/dist/migrate-storage-node.mjs +8 -0
  176. package/dist/output/context.js +5 -5
  177. package/dist/output/renderers.js +74 -2
  178. package/dist/output/shapes/curate.js +14 -2
  179. package/dist/output/shapes/passthrough.js +0 -1
  180. package/dist/output/text/helpers.js +16 -1
  181. package/dist/registry/providers/skills-sh.js +21 -147
  182. package/dist/registry/providers/static-index.js +15 -157
  183. package/dist/registry/resolve.js +22 -9
  184. package/dist/runtime.js +25 -1
  185. package/dist/scripts/migrate-storage.js +2617 -1961
  186. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +759 -510
  187. package/dist/setup/setup.js +29 -8
  188. package/dist/sources/include.js +6 -2
  189. package/dist/sources/providers/filesystem.js +0 -1
  190. package/dist/sources/providers/git-install.js +210 -0
  191. package/dist/sources/providers/git-provider.js +234 -0
  192. package/dist/sources/providers/git-stash.js +248 -0
  193. package/dist/sources/providers/git.js +10 -661
  194. package/dist/sources/providers/npm.js +2 -6
  195. package/dist/sources/providers/provider-utils.js +13 -7
  196. package/dist/sources/providers/sync-from-ref.js +9 -1
  197. package/dist/sources/providers/tar-utils.js +16 -8
  198. package/dist/sources/providers/website.js +9 -5
  199. package/dist/sources/website-ingest.js +187 -29
  200. package/dist/sources/wiki-fetchers/registry.js +53 -0
  201. package/dist/sources/wiki-fetchers/youtube.js +239 -0
  202. package/dist/storage/database.js +45 -10
  203. package/dist/storage/managed-db.js +82 -0
  204. package/dist/storage/repositories/registry-cache.js +92 -0
  205. package/dist/storage/sqlite-pragmas.js +146 -0
  206. package/dist/tasks/backends/cron.js +1 -1
  207. package/dist/tasks/backends/launchd.js +1 -1
  208. package/dist/tasks/backends/schtasks.js +1 -1
  209. package/dist/tasks/{resolveAkmBin.js → resolve-akm-bin.js} +2 -2
  210. package/dist/tasks/runner.js +5 -13
  211. package/dist/text-import-hook.mjs +0 -0
  212. package/dist/wiki/wiki.js +37 -0
  213. package/dist/workflows/db.js +3 -4
  214. package/dist/workflows/runtime/runs.js +1 -117
  215. package/dist/workflows/runtime/workflow-asset-loader.js +125 -0
  216. package/dist/workflows/validate-summary.js +2 -7
  217. package/docs/data-and-telemetry.md +3 -2
  218. package/docs/migration/release-notes/0.9.0.md +39 -0
  219. package/package.json +13 -11
  220. package/dist/commands/db-cli.js +0 -23
  221. package/dist/indexer/db/db-backup.js +0 -376
package/CHANGELOG.md CHANGED
@@ -6,6 +6,717 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.9.0] — 2026-06-30
10
+
11
+ ### Fixed
12
+
13
+ - **improve/recombine: cap-aware decay — the `maxClustersPerRun` cap no longer
14
+ traps recurring hypotheses below `confirmThreshold` (#658).** Recombine is a
15
+ two-pass design: a cluster must be re-induced on `confirmThreshold` (=2) runs
16
+ before its `type:hypothesis` proposal promotes to an auto-accepted
17
+ `type:lesson`. But only the top-`maxClustersPerRun` (=5) clusters are
18
+ processed per run, and `decayUnseenRecombineHypotheses` hard-reset the
19
+ confirmation streak of every hypothesis not processed that run. A cluster that
20
+ genuinely re-forms every run but is displaced out of the top-5 (slots are tied
21
+ on member-count and broken by an arbitrary alphabetical tiebreak) had its
22
+ streak zeroed — it could never win two consecutive slots, so its proposal sat
23
+ pending forever (6 such proposals were stuck in one production stash).
24
+ `decayUnseenRecombineHypotheses` is now **cap-aware**: `recombine.ts` passes
25
+ the FULL pre-cap cluster set, and a hypothesis is spared from reset when its
26
+ cluster still Jaccard-matches a present cluster (same signature, overlap ≥ 0.7
27
+ — the same rule used for re-induction). Only hypotheses with no matching
28
+ current cluster (the corpus stopped supporting them) decay. This does **not**
29
+ lower the recurrence bar: the confirmation count is still advanced only by
30
+ genuine re-induction in the processed slice (`recordRecombineInduction`);
31
+ sparing merely avoids an artificial reset, so a genuinely non-recurring
32
+ hypothesis still decays to 0 and never confirms (no new bland-hypothesis churn,
33
+ cf. #632/#633). No schema change. The cap now lives in a new `capClusters`
34
+ helper split out of `buildRelatednessClusters` so the full ranked set stays
35
+ available for the decay sweep.
36
+ - **`improve` reflect no longer emits proposals doomed to fail the
37
+ `invalid-description` gate when the source asset has no frontmatter
38
+ `description` (#636).** Reflect echoed the source frontmatter, so for assets
39
+ that carry other keys but no `description` (notably scraped docs:
40
+ `source`/`title`/`scraped`) the proposal inherited the missing/empty
41
+ description and the promote-time validator (`isValidDescription`, 20–400
42
+ chars) rejected it — observed as ~14/16 rejects in one triage pass, blocking
43
+ the whole scraped-doc/knowledge cluster from reflect improvement. The fix is
44
+ **generation-time only**: (1) `buildReflectPrompt` now injects an explicit
45
+ "synthesize a `description`" instruction whenever the source lacks a non-empty
46
+ `description` and the asset type requires one (per `authoring-rules.ts`
47
+ `DESCRIPTION_TYPES`), telling the model it MUST author a valid 20–400-char
48
+ plain-prose description from the asset's `title:`/first `# Heading`/opening
49
+ body; and (2) a deterministic reflect-side belt-and-suspenders in
50
+ `sanitizeReflectPayload` — if a source that already had frontmatter still ends
51
+ up with a missing/empty description after generation, reflect derives one
52
+ deterministically from `title:`/first heading (validated against
53
+ `isValidDescription`, never free-form invention) **before** the proposal is
54
+ created. The validator, `authoring-rules.ts` bounds, `repairProposalContent`,
55
+ and the drain are unchanged — nothing in the validator/promote path fabricates
56
+ content to pass itself.
57
+ - **The high-salience improve admission lane (#608) now requires a
58
+ content-derived encoding score, not the per-type weight stub (#655,
59
+ #608/#644 follow-up).** The lane previously admitted any zero-feedback ref
60
+ whose `asset_salience.encoding_salience >= salienceThreshold` (default 0.75).
61
+ But for assets distill has not content-scored, `encoding_salience` is just the
62
+ per-type WEIGHT STUB (skill/agent 0.9, command/workflow 0.8, lesson 0.75), so
63
+ "high-salience" degenerated into "is a skill/agent/command/lesson" — which
64
+ selected the type-stub `lore-writer` agent on every run (prod: 1 content-scored
65
+ / 37 type-stub / 1826 NULL-legacy rows). The gate now also requires
66
+ `isContentEncodingRow(row, parseAssetRef(ref).type)` (the #644 provenance
67
+ helper), so only genuinely content-scored assets qualify. This preserves
68
+ #608's intent — distilled assets, the lane's real targets, keep their real
69
+ content score and still qualify — while cutting the type-stub waste; type-stub
70
+ rows must earn retrieval/feedback signal via the other lanes. NULL-legacy rows
71
+ follow `isContentEncodingRow`'s differs-from-stub heuristic. An aggregated log
72
+ line now reports how many refs the lane admitted so lane composition is
73
+ observable. The threshold, type-weight table, 10% cap, and `isContentEncodingRow`
74
+ are unchanged.
75
+
76
+ - **Auto-sync no longer refuses to commit akm's own changes when unrelated
77
+ non-akm files are present in the stash working tree.** When a stash root is
78
+ shared with a project repo, stray files written into the stash root (e.g. a
79
+ `tasks.bak-…` backup dir or report artifacts like `data.js`,
80
+ `akm-health-report.html`, `reports/`) previously tripped the #476 safety
81
+ guard, which threw `refusing to push: … has uncommitted non-akm changes` on
82
+ **every** `akm improve` end-of-run auto-sync, `akm sync`, and `akm push`. In
83
+ one production incident this silently blocked all commits for ~1.5 days while
84
+ akm kept accepting proposals it never persisted. `saveGitStash` now **scopes
85
+ what it stages** instead of refusing: (1) an explicit modified-file list when
86
+ the caller passes `opts.paths`, else (2) the akm-managed pathspecs
87
+ (`TYPE_DIRS` values + `.akm`) that exist on disk — which by construction never
88
+ stages non-akm WIP, preserving the #476 protection without an all-or-nothing
89
+ refusal — and only as a last resort (3) `git add -A` when no managed pathspec
90
+ can be resolved. If nothing akm-managed is staged the run returns
91
+ `nothing to commit` (no empty commit, no throw). Unrelated non-akm files are
92
+ left untouched and uncommitted.
93
+
94
+ ### Changed
95
+
96
+ - **BEHAVIOR CHANGE — `akm init --dir <path>` no longer silently repoints your
97
+ default stash.** Previously, `akm init --dir X` unconditionally wrote
98
+ `stashDir: X` to `config.json` whenever `X` differed from the configured
99
+ default — so initializing a throwaway or secondary stash (e.g.
100
+ `akm init --dir /tmp/scratch`) would hijack the user's real default stash
101
+ pointer (the footgun documented in `memory:akm-init-persists-stashdir-warning`).
102
+ Now `init` persists `stashDir` to config **only** when one of the following
103
+ holds: (a) **no `--dir`** was provided (the default `~/akm` setup flow —
104
+ unchanged), (b) `--dir` was provided and **no `stashDir` exists in config yet**
105
+ (first-time bootstrap), or (c) `--dir` was provided **with the new
106
+ `--set-default` flag** (explicit opt-in). Otherwise `init` still scaffolds and
107
+ backfills the target dir exactly as before, but **leaves your default stash
108
+ pointer untouched** and prints:
109
+ `Your default stash is unchanged (<existing>). Re-run with --set-default to make <dir> the default.`
110
+ The `InitResponse` JSON gains `defaultStashUpdated: boolean` and an optional
111
+ `previousStashDir`. To make a `--dir` target your default, pass
112
+ `akm init --dir <path> --set-default`. (`akm setup` is unaffected — it remains
113
+ the explicit configuration flow and always sets the default.)
114
+
115
+ ### Added
116
+
117
+ - **Per-type SOFT authoring conventions are now user-editable stash facts.** A
118
+ third authoring-guidance layer joins the hard rules (#645) and general stash
119
+ standards (#642): a stash owner can author
120
+ `facts/conventions/assets/<type>.md` (e.g. `…/skill.md`, `…/command.md`) to
121
+ capture soft, type-specific guidance — voice, structure, length *preference*,
122
+ naming style. When an agent authors a `skill:x`, the body of
123
+ `fact:conventions/assets/skill` is injected (type-scoped — authoring
124
+ `command:y` pulls the `command` convention, never the `skill` one), labeled
125
+ as soft guidance and kept separate from the validator-enforced hard rules.
126
+ The basename must be a `getAssetTypes()`-validated asset type; facts are read
127
+ straight from disk (no index rebuild) and degrade to empty safely. When no
128
+ per-type fact exists, the built-in `TYPE_HINTS` fallback is unchanged (no
129
+ regression). These facts carry soft conventions only and can never weaken the
130
+ authoring contract the gate enforces (`authoringRulesForType` remains the sole
131
+ source of validator-rejecting rules). The general convention/meta resolver now
132
+ excludes `facts/conventions/assets/*` so per-type guidance never leaks
133
+ un-type-scoped into other authoring flows. (#646)
134
+ - **`akm init` now seeds default per-type SOFT convention templates.** Starter
135
+ `facts/conventions/assets/<type>.md` templates ship in the stash skeleton for
136
+ the authored types (`lesson, skill, command, agent, knowledge, memory,
137
+ workflow, script, fact`; `wiki`/`env`/`secret` excluded) so a stash owner has
138
+ an editable starting point. Each expands the matching built-in `TYPE_HINTS`
139
+ one-liner into soft starter guidance, carries `category: convention`
140
+ frontmatter, and states in-body that it is advice, not enforced — it carries
141
+ **no** validator-rejecting rules, so editing or deleting one cannot weaken the
142
+ gate (#645). The stash-skeleton copy is now recursive (preserving nested
143
+ subpaths), and `akm init` seeds **unconditionally** rather than only on first
144
+ create: re-running it on an existing stash backfills any missing skeleton,
145
+ convention, or `.meta/index.md` files. Seeding stays absent-only and never
146
+ overwrites a user-edited file. (#646)
147
+
148
+ ## [0.9.0-beta.36] — 2026-06-22
149
+
150
+ ### Added
151
+
152
+ - **Stash standards + wiki schemas are surfaced to authoring agents at write
153
+ time.** When an agent edits a page under `wikis/<name>/`, that wiki's
154
+ `schema.md` body is injected into the prompt; when it creates/edits a non-wiki
155
+ asset, the bodies of `category: convention`/`meta` `fact` assets are injected.
156
+ Two mutually-exclusive features selected by target type, sharing one
157
+ `standardsContext` prompt seam. Wired into reflect, propose, and every
158
+ improve authoring pass (distill, consolidate, recombine, procedural, extract,
159
+ schema-repair). (#642)
160
+ - **Unified, validator-sourced authoring-rules seam.** A new
161
+ `authoringRulesForType(type)` injects the hard authoring rules (no
162
+ pseudo-frontmatter in body, exactly two `---` fences, description/`when_to_use`
163
+ length + shape) into every authoring prompt. The numeric bounds live in one
164
+ module that the validators import, so the prompt can no longer drift from what
165
+ the gate enforces. (#645)
166
+
167
+ ### Fixed
168
+
169
+ - **High-salience reflect lane now reflects each asset at most once.** The
170
+ `#608` admission gate lacked the cooldown its sibling high-retrieval gate has,
171
+ so zero-feedback assets were re-selected on every run (auto-accept emits
172
+ `promoted`, not `feedback`), burning LLM calls and churning assets. (#643)
173
+ - **Stuck validation-failing proposals no longer dead-end.** The triage drain no
174
+ longer overwrites an `auto-rejected` gate stamp with a misleading
175
+ `auto-accepted` (the failure stays truthful and visible). A bounded,
176
+ content-preserving auto-repair (strip pseudo-frontmatter / stray `---`, repair
177
+ truncated descriptions) runs at the promote boundary and re-validates — fixable
178
+ proposals promote; genuinely unrepairable ones stay `pending` for manual
179
+ review, with nothing fabricated and validation never bypassed. (#645)
180
+ - Corrected a prompt/validator drift where the distill system prompt asked for an
181
+ 80–200 char description while the gate enforced 20–400. (#645)
182
+
183
+ ## [0.9.0-beta.35] — 2026-06-21
184
+
185
+ ### Fixed
186
+
187
+ - **Default extract discovery window is now "since the last run" (floored at 48h),
188
+ not a fixed 24h.** An intermittently-online host that was off for longer than
189
+ the old 24h window could permanently miss sessions that ended during the gap.
190
+ Discovery now looks back to the last recorded extract run for the harness, never
191
+ less than 48h. Widening is free of redundant LLM cost — the content-hash ledger
192
+ skips unchanged sessions with zero LLM calls. An explicit `--since`/`defaultSince`
193
+ still wins.
194
+ - **Per-session lock prevents concurrent double-extraction.** A session-end hook
195
+ firing `extract --session-id` while the periodic `akm improve` extract pass runs
196
+ discovery could both LLM-process the SAME session (duplicate spend + near-dup
197
+ proposals). A per-(harness, session) advisory lock (co-located with state.db,
198
+ PID + age staleness recovery) now makes the second run skip without any LLM call.
199
+ - **`minNewSessions` is read from the ACTIVE improve profile, not always `default`.**
200
+ A non-default profile (e.g. `frequent`) setting `minNewSessions` was silently
201
+ ignored because the gate (and its candidate-count discovery window) read
202
+ `profiles.improve.default`. They now read the resolved active profile, matching
203
+ how `extract.enabled` already resolves.
204
+
205
+ ### Docs
206
+
207
+ - Documented that `processes.extract.indexSessions` (default on) makes a second
208
+ LLM call per processed session (the session summary); set it to `false` to halve
209
+ per-session extract cost. Unchanged/skipped sessions still cost zero.
210
+
211
+ ## [0.9.0-beta.34] — 2026-06-21
212
+
213
+ ### Fixed
214
+
215
+ - **`akm extract --type opencode` reads opencode's SQLite session store.** opencode
216
+ migrated session storage from per-file JSON (`storage/session/<projectId>/<id>.json`
217
+ + `storage/message/<id>/*.json`) to a single Drizzle-managed database at
218
+ `<base>/opencode.db` (tables `session`/`message`/`part`; message text lives in
219
+ `part` rows with `data` JSON `type:"text"`). The legacy JSON layout went stale
220
+ ~2026-02, so extract discovered 0 sessions on current opencode and the
221
+ `session.idle` extract hook had nothing to read. `OpenCodeProvider` now prefers
222
+ `opencode.db` when present (read-only, via the cross-driver `openDatabase` seam)
223
+ and falls back to the JSON layout. Verified end-to-end through the plugin's
224
+ `session.idle` hook.
225
+
226
+ ## [0.9.0-beta.33] — 2026-06-21
227
+
228
+ ### Fixed
229
+
230
+ - **`akm extract` decoupled from the improve-stage toggle.** `processes.extract.enabled`
231
+ now gates extract only as a STAGE of `akm improve` (the active improve profile, per
232
+ #593/#594); an explicit `akm extract` command always runs. Previously dropping extract
233
+ from the daily improve profile silently disabled the standalone command (and its LLM
234
+ calls, via the shared `session_extraction` feature gate).
235
+ - **`extract --session-id` now respects the content-hash ledger; `--force` overrides.**
236
+ Explicit single-session extraction previously bypassed the #602 already-extracted skip
237
+ unconditionally — re-paying the LLM on every call and risking double-extraction against
238
+ the cron. Now a targeted `extract --session-id <id>` is idempotent (skips an unchanged,
239
+ already-extracted session with zero LLM calls) and only `--force` re-extracts. This
240
+ makes a session-end hook firing `extract --session-id <id>` precise AND idempotent.
241
+
242
+ ## [0.9.0-beta.32] — 2026-06-21
243
+
244
+ ### Added
245
+
246
+ - **Recombine acceptance path — confirmed lessons now auto-accept.** Recombine
247
+ hypotheses that reach the confirmation threshold (promoted to `type: lesson`,
248
+ #625/#633) now flow to ACCEPTED by reusing the existing drain mechanism instead
249
+ of piling up pending forever: the `personal-stash` drain policy gains a
250
+ `{ generator: "recombine", requireType: "lesson", maxDiffLines: 200 }` rule, via
251
+ a new optional `requireType` frontmatter filter on `DrainAcceptRule`. Only
252
+ confirmed `type: lesson` proposals auto-accept; unconfirmed `type: hypothesis`
253
+ proposals stay pending; the existing proposal quality gate still applies.
254
+ - **`processes.reflect.lowValueFilter` (opt-in, default OFF)** — deterministic
255
+ semantic value-floor that defers trivial reflect rewrites (#639A).
256
+ - **`processes.extract.triage.proceduralAwareFloor` (opt-in, default OFF)** —
257
+ triage floor requiring markers/edits so real lessons always pass (#641).
258
+
259
+ ### Fixed
260
+
261
+ - **Select-time proactive cooldown leak.** `selectProactiveMaintenanceRefs` plans
262
+ the due set BEFORE acquiring `reflect-distill.lock`, so overlapping/back-to-back
263
+ improve runs reused stale due-state and re-reflected the same asset repeatedly
264
+ (observed up to ~16× in a day). The orchestrator now re-applies the dueDays gate
265
+ with freshly-read timestamp maps INSIDE the lock (`filterProactiveDue`), dropping
266
+ refs a concurrent run already reflected.
267
+
268
+ ## [0.9.0-beta.31] — 2026-06-20
269
+
270
+ ### Changed
271
+
272
+ - **#632 — recombine now filters junk tags structurally.** Frontmatter tags that
273
+ are pure numbers, dates (`20260529`), short hex hashes (`002c624c`), version
274
+ strings (`0.8.0`, `v2`), single chars, or common English stopwords (`is`, `the`,
275
+ `for`, `when`, …) carry no topical signal and never form a recombine cluster.
276
+ Unlike `excludeTags` (a fixed project list), this catches the OPEN-ENDED junk —
277
+ every new date or commit hash — with no config upkeep. Exposed as `isJunkTag`.
278
+ On the live stash this turns the recombine cluster set from generic 66–171-member
279
+ buckets into tight topical clusters (`auth`, `architecture`, `patterns`, …).
280
+
281
+ ## [0.9.0-beta.30] — 2026-06-20
282
+
283
+ ### Changed / Fixed
284
+
285
+ - **#632 — recombine cluster tuning (opt-in, default-preserving).** Recombine
286
+ clustered memories by frontmatter tag and preferred the LARGEST buckets, so it
287
+ always picked the coarsest whole-stash tags (`session`/`claude`/`akm`, 63–171
288
+ members) and produced bland generalizations. Two new `processes.recombine` knobs:
289
+ `maxClusterSize` (skip clusters larger than N, so over-broad buckets no longer
290
+ reach/starve the largest-first slice) and `excludeTags` (tags that may never form
291
+ a tag cluster). Both UNSET = byte-identical to prior behavior.
292
+ - **#633 — recombine confirmation loop fixed.** The hypothesis confirmation streak
293
+ was keyed on a hash of the EXACT member set, so a growing stash drifted the key
294
+ every run → a fresh row at count 1 → `confirmThreshold` never reached → no
295
+ hypothesis ever promoted to a lesson (a dead two-pass loop). A freshly-induced
296
+ cluster now matches an existing pending row by signature + Jaccard
297
+ membership-overlap (≥ 0.7) and reuses its stable ref, so the streak accumulates
298
+ through membership drift. First/non-overlapping induction is unchanged.
299
+
300
+ ## [0.9.0-beta.29] — 2026-06-20
301
+
302
+ ### Reverted
303
+
304
+ - **#630 — `fact` asset type phase 2 reverted (#631).** The pinned-core assembly +
305
+ `akm fact` CLI shipped in beta.28 was reverted pending rework. Phase 1 (#629, the
306
+ `fact` asset type itself) remains in place.
307
+
308
+ ## [0.9.0-beta.27] — 2026-06-20
309
+
310
+ All new behavior is **opt-in / default-preserving** — default runs are byte-identical.
311
+
312
+ ### Added
313
+
314
+ - **#624 P2 — priority-ranked graph extraction.** `processes.graphExtraction.topN`:
315
+ when set, the graph-extraction pass ranks eligible files by asset utility
316
+ (`utility_scores`, read-only join) and processes only the top-N per run, so
317
+ high-value assets get graphed first instead of a ~55h full-corpus sweep. Unset
318
+ (default) = no ranking, byte-identical.
319
+ - **#624 P3 — lazy on-demand graph extraction.** New `graph_extraction_queue` table
320
+ + `enqueueGraphExtraction`/`drainExtractionQueue`/`extractGraphForSingleFile`.
321
+ `akm curate` enqueues an ungraphed hit (non-blocking); `akm show` can extract a
322
+ missing graph inline — gated on `index.graph.lazyGraphExtraction: true`
323
+ (**default off**: `show` makes no LLM call by default), model-guarded, and bounded
324
+ by a 30s timeout so it never hangs. The pass drains the queue before the ranked
325
+ sweep. This **closes #624** (all three layers shipped).
326
+ - **#616 — bounded multi-cycle phasing.** `profiles.improve.<name>.maxCycles`
327
+ (default 1): when > 1, the improve passes run in an N-cycle loop so gate-accepted
328
+ output of cycle N feeds cycle N+1 within the same run (re-running ensureIndex +
329
+ ref selection each cycle), stopping at a fixed point and respecting the run budget.
330
+ `maxCycles: 1` = byte-identical to today.
331
+
332
+ ### Fixed
333
+
334
+ - **Release CI unblocked.** `runCliCapture` (test harness) restored `process.exitCode`
335
+ to a captured `undefined`, which under `bun test` does not clear a previously-set
336
+ non-zero exit code — so the unit suite exited 1 with 0 failures at `TEST_PARALLEL=1`
337
+ (exactly how `release.yml` runs), silently blocking every npm publish since beta.11.
338
+ Fixed to restore to `0`. (This is why beta.26 was the first successful workflow publish.)
339
+
340
+ ### Changed
341
+
342
+ - **CI/release tests sharded across runner jobs (~15 min → ~2 min).** Bun 1.3.x
343
+ in-process test parallelism (`--parallel=N`, N>1) hits an intermittent
344
+ `epoll_ctl EEXIST` race / busy-spin hang on the `--isolate` workers, which had
345
+ forced fully-sequential (`TEST_PARALLEL=1`) runs. Tests now shard across separate
346
+ runner jobs (each a separate process tree, so no cross-shard fd/epoll collisions)
347
+ with `--parallel=1` within each shard; the matrix runs shards concurrently. The
348
+ release gate runs the identical set of tests. Local `bun run check` defaults to
349
+ sequential too (the only safe mode on this Bun version). Coverage unchanged.
350
+ Each shard runs through `scripts/run-test-shard.sh`, which retries **only on a
351
+ hang/timeout** (the busy-spin can rarely fire even at `--parallel=1`) and never
352
+ on a real test failure, so genuine red tests still fail fast and are never masked.
353
+
354
+ ## [0.9.0-beta.26] — 2026-06-20
355
+
356
+ ### Added
357
+
358
+ - **#628 — configurable SQLite journal mode (`AKM_SQLITE_JOURNAL_MODE`) for network
359
+ filesystems.** AKM previously opened every database with `PRAGMA journal_mode = WAL`
360
+ unconditionally, which cannot run on a network filesystem (NFS/SMB/Azure Files) —
361
+ WAL's `-shm` shared-memory wal-index can't be `mmap`'d over a network mount. You can
362
+ now set `AKM_SQLITE_JOURNAL_MODE` to `WAL` (default), `DELETE`, or `TRUNCATE`, applied
363
+ at **all five** db openers (`state.db`, `index.db` ×2 paths, `workflow.db`, `logs.db`).
364
+ At the `WAL` default AKM auto-detects a network mount for the data dir and transparently
365
+ falls back to `DELETE` (rollback journal + `synchronous = FULL`) with a one-line warning;
366
+ invalid values warn once and fall back to `WAL`. **Default behavior is byte-identical.**
367
+ This lets the AKM database subtree live on a shared volume (e.g. Azure Files under
368
+ Azure Container Apps). New docs section "Hosting AKM databases on a network share
369
+ (NFS/SMB)" in `docs/configuration.md`.
370
+
371
+ ## [0.9.0-beta.25] — 2026-06-19
372
+
373
+ Completes the recombine / extract-efficiency / graph thread. All new improve
374
+ passes are **opt-in (default off)**, so default behavior is unchanged.
375
+
376
+ ### Added
377
+
378
+ - **#606 — event-driven extract (`akm extract --watch`).** Opt-in watch mode: an
379
+ injectable, debounced watcher triggers extraction shortly after a session file
380
+ appears, with a clean `stop()` handle. The `8,28,48` cron remains the fallback;
381
+ no daemon is auto-launched.
382
+ - **#625 — recombine second pass (hypothesis → lesson).** The opt-in `recombine`
383
+ process (#609) now consumes `confirmThreshold` (default 2): a generalization
384
+ re-induced that many consecutive runs is promoted from a `type: hypothesis`
385
+ proposal to a `type: lesson` proposal through the normal queue + quality gate
386
+ (never a direct stash write). Hypotheses that stop recurring decay. Backed by a
387
+ new `recombine_hypotheses` table in `state.db`.
388
+
389
+ ### Changed
390
+
391
+ - **#624 (P1) — graph storage decoupled from `entries.id`.** `graph_files` is
392
+ re-keyed on `(stash_root, file_path, body_hash)`, so extracted graph data now
393
+ **survives a reindex** of unchanged files instead of being cascade-wiped. The
394
+ upgrade is migrated in a **targeted, graph-only path** that preserves existing
395
+ graph data and leaves the entry index, embeddings, FTS, and LLM-enrichment cache
396
+ untouched — **no full index rebuild and no re-embed** on upgrade. (P2 priority-
397
+ ranked extraction and P3 lazy/on-demand extraction remain deferred.)
398
+
399
+ ### Fixed
400
+
401
+ - Graph re-key migration no longer triggers a destructive full-index rebuild: it
402
+ is a graph-scoped table migration (no `DB_VERSION` bump), and it **copies** the
403
+ existing graph rows into the new schema rather than dropping them.
404
+ - Test-suite `/tmp` hygiene: sandbox teardown now fires on `SIGINT`/`SIGTERM`/
405
+ `SIGHUP` (not just clean exit), and a `sweep:tmp` step reclaims stale `akm-*`
406
+ sandbox dirs left by force-killed workers — eliminating the tmpfs accumulation
407
+ that caused intermittent `EEXIST: epoll_ctl` test flakes.
408
+
409
+ ## [0.9.0-beta.20] — 2026-06-18
410
+
411
+ ### Fixed
412
+
413
+ - **`akm update --all` no longer fails for writable `github:` entries stored as `source:"git"`**. `updateRegistryEntry` was using `synced.source` (re-derived from the ref scheme as `"github"`) instead of the existing `entry.source`, causing the config validator to reject `writable:true` on every update cycle.
414
+
415
+ ## [0.9.0-beta.19] — 2026-06-17
416
+
417
+ ### Fixed
418
+
419
+ - **`akm feedback` now completes in ~0.3s** (was 3+ minutes). Root cause: the command was calling `ensureIndex` with `mode: "blocking"` inside `withIndexWriterLease`, triggering a full reindex on every feedback call. Fix: removed the `ensureIndex` call entirely (feedback only needs the index to exist, not be current — a stale index is fine for ref lookup); removed the application-level writer lock (SQLite WAL + `busy_timeout=30s` handles concurrent access with `akm improve`); added a fast DB-exists guard with a clear error for first-time users.
420
+ - **`akm health --format html` now completes in ~11s** (was ~18s). Root cause: `akmHealth()` was called twice — once for the main result and once to get `deltas`. Fix: merged into a single call passing both `groupBy: "run"` and `windowCompare` together.
421
+
422
+ ## [0.9.0-beta.18] — 2026-06-17
423
+
424
+ ### Changed
425
+
426
+ - **Health report: Recent Runs table now shows all filtered runs in descending order** (newest first) instead of capping at the last 10.
427
+ - **Health report: Removed "Command Set Used" section.**
428
+ - **Health report: All timestamps now display in the viewer's local timezone** (chart axis labels, runs table, freshness line, executive summary, footer). Server-rendered ISO strings are wrapped in `<time data-iso>` elements and converted to local time by client-side JS on page load.
429
+
430
+ ### Changed (migration required)
431
+
432
+ - **WS-2 outcome loop (#613) — default-off weight change (state.db migration 010).**
433
+ Every `akm improve` run now writes an `asset_outcome` row per processed asset
434
+ (state.db migration `010`) and computes a differential usefulness signal
435
+ (`outcome_score`) per ref. The outcome signal is persisted and visible in the
436
+ health report, but the **weight change is gated behind a config flag** (see
437
+ below). Ranking is unchanged from WS-1 by default.
438
+
439
+ **Opt-in weight change.** The WS-2 projection weights (`w_e=0.25, w_o=0.15,
440
+ w_r=0.60`) affect ranking only when you explicitly set
441
+ `improve.salience.outcomeWeightEnabled: true` in your `akm.yaml`. The default
442
+ (`false`) keeps WS-1 parity weights (`w_e=0.30, w_r=0.70`, `w_o=0`), so
443
+ existing users see no ranking change on upgrade.
444
+
445
+ **Part-V measurement gate.** Before enabling the weight change, run the Part-V
446
+ T0 baseline (`scripts/akm-eval` + `akm health`; confirm proactive accept
447
+ ≥ 0.9× reactive; reversion ≤ 0.15; retrieval-delta ≥ 0; coverage not
448
+ regressed). That gate requires a running production stash and cannot be
449
+ exercised in CI. Once confirmed, set
450
+ `improve.salience.outcomeWeightEnabled: true` to activate the three-way split.
451
+
452
+ **Outcome loop mechanics.** `outcome_score` is a differential prediction-error
453
+ signal: `(retrieval_delta − expected_delta) − PENALTY × retrieval_delta × (1 −
454
+ accepted_change_rate) + valence`, tracked via an EMA (α=0.3). New rows are
455
+ warm-started from the utility EMA score (clipped to 0.3) so the signal is
456
+ non-zero from launch. A stash-wide diversity floor (10% of the max score) prevents
457
+ rare-but-correct assets from being permanently outcompeted. An inverted-proxy
458
+ tripwire (`corr(outcome_score, accepted_change_rate) < −0.3`) emits an
459
+ `outcome_proxy_inverted` health event when the signal degrades.
460
+
461
+ `review_pressure` is computed and persisted per asset but is **not yet wired into
462
+ the admission policy** — that is deferred to a later work stream per plan §Part-VI
463
+ #613. The column is present and populated; routing it into the consolidation-
464
+ selection filter is the next step.
465
+
466
+ - **WS-1 salience vector (#618) — default-on ranking change.** The eligibility sort
467
+ for all `akm improve` runs (whole-stash, type, and ref scope) has changed from
468
+ `combinedEligibilityScore = utility·0.7 + negativeOnlyRatio·0.3` to
469
+ `rankScore = (0.3·encodingSalience + 0.7·retrievalSalience) × sizePenalty`
470
+ (feedback valence and utility EMA dropped from ordering until WS-2 re-introduces
471
+ outcome salience). Assets are now ranked by retrieval frequency × recency × type
472
+ importance rather than by feedback magnitude. Because the old
473
+ `combinedEligibilityScore` ordering was never persisted, a forgetting comparison is
474
+ not possible on the first run; instead a one-time `improve_salience_first_run` marker
475
+ event is emitted to record the transition. On every subsequent run a stash-wide
476
+ `improve_salience_rank_change` drift report (including `stashSize`) is emitted so
477
+ rank movement under the new scoring can be tracked over time.
478
+ The Part-V measurement protocol (T0 baseline via `scripts/akm-eval` + health report,
479
+ throughput/quality gate) is deferred to the WS-2 milestone, when outcome salience
480
+ re-joins the projection and re-tuning is triggered.
481
+
482
+ ## [0.9.0-beta.12] - 2026-06-15
483
+
484
+ Improve-tuning work streams (all **default-off / parity-preserving** — no behavior
485
+ change until explicitly enabled).
486
+
487
+ ### Added
488
+
489
+ - **#617 — deterministic near-duplicate memory dedup** (`processes.consolidate.dedup`,
490
+ default off). A cheap no-LLM pre-pass in front of consolidation collapses obvious
491
+ duplicates — `.derived`+origin pairs and content twins (normalized content-hash
492
+ equality, or embedding cosine ≥ `cosineThreshold`, default 0.97). Each dropped
493
+ variant is archived + backed up before deletion; hot memories are never
494
+ collapsed; distinct-but-related memories fall through to the LLM.
495
+ - **#581 — judged-state cache for consolidation** (`processes.consolidate.judgedCache`,
496
+ default off). New state.db table (`consolidation_judged`) records each memory's
497
+ content hash + outcome when the LLM judges it; subsequent runs skip
498
+ judged-unchanged memories, converting coverage from O(time-window) to
499
+ O(changed/new) so a run can sweep the full corpus. Fails open; failed chunks
500
+ and dry-runs never poison the cache. (state.db migration `007`.)
501
+ - **#612 — auto-accept gate calibration** (`improve.calibration`, auto-tune default
502
+ off). Joins predicted gate confidence to realized accept/reject outcomes into a
503
+ reliability table + calibration gap, surfaced in `akm health` (+ summary rows in
504
+ the HTML report). Opt-in bounded threshold auto-tune nudges the accept threshold
505
+ within a configured band toward a target accept rate, logged via a
506
+ `calibration_autotune` event. (Replay-prioritization from prediction error is
507
+ deferred — it depends on the #610 replay budget, a 0.10 item.)
508
+
509
+ ### Fixed
510
+
511
+ - **#614 — symmetric valence weighting** (`profiles.improve.*.symmetricValence`,
512
+ default off). The eligibility sort weighted feedback negative-only; when enabled
513
+ it uses a symmetric `|valence|` magnitude so strong positive and strong negative
514
+ feedback both drive attention (utility stays the dominant factor), routing
515
+ high-negative → fix and high-positive → reinforce lanes.
516
+
517
+ ## [0.9.0-beta.11] - 2026-06-15
518
+
519
+ ### Added
520
+
521
+ - **`extract.maxSessionsPerRun`** (default 25) — caps the NEW sessions the
522
+ extract pass LLM-processes in a single run so a backlog (e.g. after downtime)
523
+ can't push one run past its scheduled-task timeout. Overflow sessions stay
524
+ unseen and are picked up by later runs, so coverage is preserved. `0` disables.
525
+
526
+ ### Fixed
527
+
528
+ - **Auto-accept validation failures are no longer a blind leak.** When a
529
+ confidence-passing proposal fails promotion validation, the gate now captures
530
+ the reason (the `validateProposal` finding kind, e.g. `validation:description-quality`),
531
+ records it on the proposal (`akm proposal show` explains the rejection), logs
532
+ it, and exposes `failedByReason` on the gate result — so the ~5% leak is
533
+ diagnosable instead of silently warned-and-dropped.
534
+ - **Inflated skip-reason aggregates in `akm health`.** `no_new_signal` /
535
+ `profile_filtered_all_passes` are per-run snapshots of a stable set; the
536
+ window aggregator summed their per-run counts (≈2.7M / 3M). It now uses the
537
+ most recent run's count for these aggregated-snapshot reasons while still
538
+ summing genuine per-occurrence skips.
539
+
540
+ ## [0.9.0-beta.10] - 2026-06-15
541
+
542
+ ### Added
543
+
544
+ - **#603** — `akm health` pool-saturation advisory. Instead of alerting on the
545
+ raw `sessionsScanned` count (which false-alarmed on normal cadence changes),
546
+ a new `pool-saturation` advisory reports the ratio of new (unseen) sessions
547
+ to the total session pool: informational below 10% (expected steady state),
548
+ warning below 2% (possible discovery/dedup bug). Heuristic, never gates
549
+ overall status.
550
+ - **#576** — the `akm health` HTML report now renders the real per-stage LLM
551
+ token/time aggregate (a "🧠 LLM Work" KPI card + LLM token/call/wall-time
552
+ summary rows) from the captured `llm_usage` events, replacing the GPU-time
553
+ proxy.
554
+ - **Built-in `akm health --format html` report overhaul** — the report is now a
555
+ strict superset of (and supersedes) the external `akm-health-report` stash
556
+ skill. Restored the interactive filter bar (time-slice 1d–21d, task, status)
557
+ with client-side chart/table re-render and the Last-10 "Task" column;
558
+ reordered sections to a decision-first flow (verdict → action items → KPIs →
559
+ table → charts); added a synthesized one-sentence **Verdict** (status + 2–3
560
+ drivers) and a freshness line; merged the duplicate Advisories / What-to-Watch
561
+ into one prioritized, de-duplicated **Action Items** list (P1/P2/P3 +
562
+ remediation command); added a per-stage **LLM token** stacked-bar chart and
563
+ `dataZoom` sliders on dense charts; fixed the failed-run scatter x-alignment
564
+ (now shape-encoded); KPI-card colors are now health signals (not decoration);
565
+ added metric-glossary tooltips, chart `aria-label`s, contrast fixes, and
566
+ empty-state overlays. Deterministic output preserved.
567
+
568
+ ### Fixed
569
+
570
+ - **Health report accuracy** (follow-ups to the overhaul): the per-run **Task**
571
+ column/filter now show the real scheduled task (`akm-improve-frequent`, …) via
572
+ a ±5min `task_history` join instead of the run's scope (which is `all` for
573
+ every scheduled run); the time-**slice** filter options are now derived from
574
+ the report's `--since` window (e.g. All/3d/1d/12h/6h for a 7d report) and
575
+ default to "All" — replacing the hard-coded 1d–21d list that didn't match the
576
+ window; and the trend **deltas** now default their compare window to `--since`
577
+ (like-for-like, e.g. last 7d vs prior 7d) instead of a fixed 24h, which had
578
+ produced nonsensical period-over-period percentages on multi-day reports.
579
+ - **Inflated stash-snapshot metrics in `akm health`.** `memorySummary`
580
+ (derived/eligible) and `profileFilteredRefs` are whole-stash snapshots recorded
581
+ on every run, but the window aggregator was **summing** them across all runs —
582
+ e.g. "915,258 of 1,226,025 eligible" and a 2.4M filtered-ref count. They now
583
+ take the most recent run's snapshot (the current state). Per-run *work* metrics
584
+ (promoted, MI written, graph entities, …) remain genuine window sums.
585
+ - **Health report polish:** the akm version is stamped in the header (under the
586
+ AKM logo) and footer; the steady-state `no new signal since last proposal`
587
+ distill reason is excluded from the skip-reason chart (it drowned out the
588
+ actionable reasons); and the Consolidation Output chart now draws Promoted as a
589
+ line on a secondary right-hand axis (it dwarfs merged/deleted) with merged and
590
+ deleted as bars on the left axis.
591
+
592
+ - **#598** — process-level tuning fields (`consolidate.incrementalSince`,
593
+ `minPoolSize`, `neighborsPerChanged`, `extract.minContentChars`, per-process
594
+ `enabled` flags) now survive an `akm config` rewrite. They are first-class
595
+ typed `ImproveProcessConfigSchema` fields, so the load→save round trip no
596
+ longer silently drops them. Unknown process sub-keys hard-error at load
597
+ (`ConfigError`) rather than being silently discarded — the deliberate,
598
+ documented resolution. Regression-guarded by
599
+ `tests/config-process-roundtrip.test.ts`.
600
+
601
+ ## [0.9.0-beta.9] - 2026-06-14
602
+
603
+ Restore and instrument `akm improve` steady-state output. The reflect/distill
604
+ self-improvement lanes had been near-zero in steady state because the
605
+ signal-delta eligibility gate was the only lane (cache "no-access = no-work"
606
+ pathology) and the high-retrieval fallback was structurally dead. This release
607
+ revives proactive improvement, adds attribution + a measurement/kill-criterion
608
+ system so the lane must prove its value, and right-sizes reflect budgets to
609
+ their task timeouts.
610
+
611
+ ### Added
612
+
613
+ - **Proactive maintenance selector** (`proactiveMaintenance` improve process):
614
+ due-gated, composite-priority (`importance × log(1+retrievalFreq) ×
615
+ recencyDecay / log(size)`), bounded rotating top-N reflect/distill over
616
+ stale/never-reflected assets. **Disabled by default**; enable per profile.
617
+ - **Eligibility attribution**: every reflect/distill proposal is stamped
618
+ `eligibilitySource ∈ {signal-delta, high-retrieval, proactive, scope,
619
+ unknown}` on `reflect_invoked`/`distill_invoked`/`promoted` events and the
620
+ proposal record, so outcomes are sliceable by lane.
621
+ - **Measurement system** under `scripts/akm-eval/`: a real-query retrieval suite
622
+ generated from `usage_events`, and `akm-eval-proactive-verdict` — a read-only
623
+ kill-criterion runner comparing the proactive lane (treatment) vs due-but-
624
+ untouched assets (control). Emits PASS/FAIL/INCONCLUSIVE and recommends
625
+ disabling the lane on FAIL. New `proactive_selected` event +
626
+ `proactiveSelected`/`proactiveDueTotal`/`proactiveNeverReflected` fields on
627
+ `improve_completed`.
628
+
629
+ ### Fixed
630
+
631
+ - Revived the P0-A high-retrieval fallback: genuinely zero-feedback assets were
632
+ routed to the fully-skipped branch one phase before the fallback could see
633
+ them, so frequently-retrieved-but-never-rated assets were never improved.
634
+ - `getRetrievalCounts` now normalizes bare vs `origin//`-prefixed refs (it was
635
+ dropping ~half the retrieval signal) and counts `curate` events
636
+ (`akm curate` now records per-item `entry_ref`).
637
+ - The fully-skipped `no_new_signal` branch emitted one `improve_skipped` event
638
+ per ref (~11K writes/run, ~400K rows/day) — a contributor to 900s improve
639
+ timeouts and state.db bloat. Collapsed into one aggregated counted event.
640
+
641
+ ## [0.9.0-beta.8] - 2026-06-13
642
+
643
+ Fix multi-process SQLite contention in `index.db` and harden concurrent proposal
644
+ queue mutations.
645
+
646
+ ### Changed
647
+
648
+ - Added a global `index.db` writer lease used by foreground indexing,
649
+ background auto-index, improve maintenance index writers, graph updates, and
650
+ feedback writes.
651
+ - Replaced the racy background index PID-file dedup flow with lease-based
652
+ coordination and explicit handoff to the spawned worker.
653
+ - `akm feedback` now uses blocking index preparation and writes under the same
654
+ `index.db` lease, avoiding self-inflicted `database is locked` failures.
655
+ - Proposal queue create/archive/gate-decision mutations now run under
656
+ `BEGIN IMMEDIATE` state.db transactions so concurrent processes serialize on
657
+ live queue state.
658
+
659
+ ## [0.9.0-beta.7] - 2026-06-13
660
+
661
+ Fix the `akm improve` regression introduced by background `ensureIndex`.
662
+
663
+ ### Changed
664
+
665
+ - Added an explicit `ensureIndex` mode so callers choose `background` or
666
+ `blocking` behavior directly instead of relying on hidden environment state.
667
+ - `akm improve` now uses blocking index preparation before collecting eligible
668
+ refs, restoring the post-upgrade empty-index recovery path.
669
+ - Removed the `AKM_INDEX_INLINE` test-only override so tests exercise the same
670
+ index behavior model as production.
671
+
672
+ ## [0.9.0-beta.6] - 2026-06-12
673
+
674
+ Pipeline optimization: new per-process config fields wire up the consolidation
675
+ and improve pipeline knobs exposed by the optimization report — incremental
676
+ consolidation, pool caps, distill gating, and memory inference throttling.
677
+
678
+ ### Added
679
+
680
+ - **`consolidate.incrementalSince`** — profile config field that narrows the
681
+ consolidation candidate pool to memories modified within the given window
682
+ (e.g. `"1h"`, `"4h"`) plus their graph neighbours. Enables frequent
683
+ consolidation passes (e.g. `quick-shredder` every 15 min) without full-pool
684
+ sweeps. Absent = full-pool sweep (correct for nightly runs).
685
+ - **`consolidate.limit`** — hard cap on memories processed per consolidation
686
+ pass, applied after incremental narrowing. Prevents runaway full-pool sweeps
687
+ in the nightly default profile.
688
+ - **`consolidate.neighborsPerChanged`** — configurable graph-neighbour count
689
+ per changed memory during incremental consolidation (was hardcoded to 5).
690
+ `quick-shredder` sets this to 3 for a 40% candidate reduction per burst.
691
+ - **`distill.requirePlannedRefs`** — when `true`, the distill process is
692
+ skipped entirely for distill-only refs when the reflect phase produced zero
693
+ planned refs. Eliminates hundreds of `distill-skipped` events on quiet passes
694
+ where all refs are on reflect cooldown.
695
+ - **`memoryInference.minPendingCount`** — minimum pending split-parent memory
696
+ count below which the inference pass is skipped entirely (zero LLM calls).
697
+ Prevents lock acquisition on passes where there is nothing to infer.
698
+ - **`reflect.limit`** — per-process ref limit for the reflect/distill loop,
699
+ applied as the improve run limit when no CLI `--limit` is given.
700
+ - **New `reflect-distill` improve profile** — dedicated reflect + distill +
701
+ memoryInference + triage profile for the every-4h `akm-improve-frequent`
702
+ task. `reflect.limit: 25` bounds LLM cost per pass.
703
+
704
+ ### Changed
705
+
706
+ - **`quick-shredder` profile tuned**: `incrementalSince` `4h` → `1h`,
707
+ `maxChunkSize` 25 → 35, added `minPoolSize: 10`, `neighborsPerChanged: 3`,
708
+ `memoryInference.minPendingCount: 5`. All `profile: "qwen-9b-shredder"`
709
+ process references removed — falls back to default LLM.
710
+ - **`default` improve profile** (nightly): extract disabled (dedicated
711
+ `akm-extract` task runs at 01:48), consolidate gets `limit: 500`,
712
+ reflect gets `limit: 100` and `allowedTypes`, distill gets
713
+ `requirePlannedRefs: true`, triage enabled at 50 accepts/run,
714
+ graphExtraction explicitly enabled.
715
+ - **Cron schedule optimised**: extract reverted to `8,28,48 * * * *` (3×/hr),
716
+ quick-shredder shifted to `4,19,34,49` (4-min extract gap), health-report
717
+ shifted to `:03` (avoids `:00` collision), `akm-improve-frequent` re-enabled
718
+ at `45 */4` with `reflect-distill` profile.
719
+
9
720
  ## [0.9.0-beta.3] - 2026-06-12
10
721
 
11
722
  Stabilization batch closing the remaining 0.9.0 milestone: DB-locking and