akm-cli 0.9.0-beta.2 → 0.9.0-beta.27

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 (120) hide show
  1. package/CHANGELOG.md +660 -0
  2. package/dist/assets/prompts/consolidate-system.md +23 -0
  3. package/dist/assets/prompts/contradiction-judge.md +33 -0
  4. package/dist/assets/prompts/distill-knowledge-system.md +22 -0
  5. package/dist/assets/prompts/distill-lesson-system.md +36 -0
  6. package/dist/assets/prompts/extract-session.md +5 -1
  7. package/dist/assets/prompts/graph-extract-system.md +1 -0
  8. package/dist/assets/prompts/memory-infer-system.md +1 -0
  9. package/dist/assets/prompts/memory-infer-user.md +5 -0
  10. package/dist/assets/prompts/metadata-enhance-system.md +1 -0
  11. package/dist/assets/prompts/procedural-system.md +44 -0
  12. package/dist/assets/prompts/recombine-system.md +40 -0
  13. package/dist/assets/prompts/staleness-detect-system.md +6 -0
  14. package/dist/assets/prompts/validate-summary-judge.md +1 -0
  15. package/dist/assets/templates/html/default.html +78 -0
  16. package/dist/assets/templates/html/health.html +730 -0
  17. package/dist/assets/templates/html/vendor/echarts.min.js +45 -0
  18. package/dist/cli/shared.js +21 -5
  19. package/dist/cli.js +47 -5
  20. package/dist/commands/agent/contribute-cli.js +16 -3
  21. package/dist/commands/feedback-cli.js +15 -6
  22. package/dist/commands/graph/graph.js +75 -71
  23. package/dist/commands/health/checks.js +48 -0
  24. package/dist/commands/health/html-report.js +790 -0
  25. package/dist/commands/health.js +478 -15
  26. package/dist/commands/improve/calibration.js +161 -0
  27. package/dist/commands/improve/consolidate.js +634 -111
  28. package/dist/commands/improve/dedup.js +482 -0
  29. package/dist/commands/improve/distill.js +145 -69
  30. package/dist/commands/improve/encoding-salience.js +205 -0
  31. package/dist/commands/improve/extract-cli.js +115 -1
  32. package/dist/commands/improve/extract-prompt.js +33 -2
  33. package/dist/commands/improve/extract-watch.js +140 -0
  34. package/dist/commands/improve/extract.js +280 -35
  35. package/dist/commands/improve/feedback-valence.js +54 -0
  36. package/dist/commands/improve/homeostatic.js +467 -0
  37. package/dist/commands/improve/improve-auto-accept.js +139 -6
  38. package/dist/commands/improve/improve-profiles.js +12 -0
  39. package/dist/commands/improve/improve.js +2079 -608
  40. package/dist/commands/improve/memory/memory-contradiction-detect.js +23 -28
  41. package/dist/commands/improve/outcome-loop.js +256 -0
  42. package/dist/commands/improve/proactive-maintenance.js +87 -0
  43. package/dist/commands/improve/procedural.js +409 -0
  44. package/dist/commands/improve/recombine.js +488 -0
  45. package/dist/commands/improve/reflect-noise.js +0 -0
  46. package/dist/commands/improve/reflect.js +51 -1
  47. package/dist/commands/improve/related-sessions.js +120 -0
  48. package/dist/commands/improve/salience.js +386 -0
  49. package/dist/commands/improve/triage.js +95 -0
  50. package/dist/commands/lint/agent-linter.js +19 -24
  51. package/dist/commands/lint/base-linter.js +173 -60
  52. package/dist/commands/lint/command-linter.js +19 -24
  53. package/dist/commands/lint/env-key-rules.js +34 -1
  54. package/dist/commands/lint/fact-linter.js +39 -0
  55. package/dist/commands/lint/index.js +31 -13
  56. package/dist/commands/lint/memory-linter.js +1 -1
  57. package/dist/commands/lint/registry.js +7 -2
  58. package/dist/commands/lint/task-linter.js +3 -3
  59. package/dist/commands/lint/workflow-linter.js +26 -1
  60. package/dist/commands/proposal/drain.js +73 -6
  61. package/dist/commands/proposal/proposal-cli.js +22 -10
  62. package/dist/commands/proposal/proposal.js +17 -1
  63. package/dist/commands/proposal/validators/proposals.js +369 -329
  64. package/dist/commands/read/curate.js +344 -80
  65. package/dist/commands/read/search-cli.js +7 -0
  66. package/dist/commands/read/search.js +1 -0
  67. package/dist/commands/read/show.js +67 -2
  68. package/dist/commands/remember.js +6 -2
  69. package/dist/commands/sources/installed-stashes.js +5 -1
  70. package/dist/commands/sources/stash-cli.js +10 -2
  71. package/dist/core/asset/asset-registry.js +2 -0
  72. package/dist/core/asset/asset-spec.js +14 -0
  73. package/dist/core/asset/frontmatter.js +166 -167
  74. package/dist/core/asset/markdown.js +8 -0
  75. package/dist/core/config/config-schema.js +255 -2
  76. package/dist/core/config/config.js +2 -2
  77. package/dist/core/logs-db.js +305 -0
  78. package/dist/core/paths.js +3 -0
  79. package/dist/core/state-db.js +706 -42
  80. package/dist/indexer/db/db.js +364 -38
  81. package/dist/indexer/db/graph-db.js +129 -86
  82. package/dist/indexer/ensure-index.js +152 -17
  83. package/dist/indexer/graph/graph-boost.js +51 -41
  84. package/dist/indexer/graph/graph-extraction.js +203 -3
  85. package/dist/indexer/index-writer-lock.js +99 -0
  86. package/dist/indexer/indexer.js +114 -111
  87. package/dist/indexer/passes/memory-inference.js +71 -25
  88. package/dist/indexer/passes/staleness-detect.js +2 -5
  89. package/dist/indexer/search/db-search.js +15 -4
  90. package/dist/indexer/search/ranking-contributors.js +22 -0
  91. package/dist/indexer/search/ranking.js +4 -0
  92. package/dist/indexer/walk/matchers.js +9 -0
  93. package/dist/integrations/agent/prompts.js +1 -0
  94. package/dist/integrations/harnesses/claude/session-log.js +27 -5
  95. package/dist/integrations/harnesses/opencode/session-log.js +9 -0
  96. package/dist/integrations/session-logs/index.js +16 -0
  97. package/dist/llm/client.js +38 -4
  98. package/dist/llm/embedder.js +27 -3
  99. package/dist/llm/embedders/local.js +66 -2
  100. package/dist/llm/graph-extract.js +2 -1
  101. package/dist/llm/memory-infer.js +4 -8
  102. package/dist/llm/metadata-enhance.js +9 -1
  103. package/dist/llm/usage-persist.js +77 -0
  104. package/dist/llm/usage-telemetry.js +103 -0
  105. package/dist/output/context.js +3 -2
  106. package/dist/output/html-render.js +73 -0
  107. package/dist/output/renderers.js +73 -1
  108. package/dist/output/shapes/curate.js +14 -2
  109. package/dist/output/shapes/helpers.js +17 -1
  110. package/dist/output/text/helpers.js +78 -1
  111. package/dist/runtime.js +25 -1
  112. package/dist/scripts/migrate-storage.js +1262 -591
  113. package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +485 -270
  114. package/dist/sources/providers/tar-utils.js +16 -8
  115. package/dist/storage/sqlite-pragmas.js +146 -0
  116. package/dist/tasks/runner.js +99 -16
  117. package/dist/workflows/db.js +5 -2
  118. package/dist/workflows/validate-summary.js +2 -7
  119. package/docs/data-and-telemetry.md +1 -0
  120. package/package.json +9 -6
package/CHANGELOG.md CHANGED
@@ -6,6 +6,505 @@ 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-beta.27] — 2026-06-20
10
+
11
+ All new behavior is **opt-in / default-preserving** — default runs are byte-identical.
12
+
13
+ ### Added
14
+
15
+ - **#624 P2 — priority-ranked graph extraction.** `processes.graphExtraction.topN`:
16
+ when set, the graph-extraction pass ranks eligible files by asset utility
17
+ (`utility_scores`, read-only join) and processes only the top-N per run, so
18
+ high-value assets get graphed first instead of a ~55h full-corpus sweep. Unset
19
+ (default) = no ranking, byte-identical.
20
+ - **#624 P3 — lazy on-demand graph extraction.** New `graph_extraction_queue` table
21
+ + `enqueueGraphExtraction`/`drainExtractionQueue`/`extractGraphForSingleFile`.
22
+ `akm curate` enqueues an ungraphed hit (non-blocking); `akm show` can extract a
23
+ missing graph inline — gated on `index.graph.lazyGraphExtraction: true`
24
+ (**default off**: `show` makes no LLM call by default), model-guarded, and bounded
25
+ by a 30s timeout so it never hangs. The pass drains the queue before the ranked
26
+ sweep. This **closes #624** (all three layers shipped).
27
+ - **#616 — bounded multi-cycle phasing.** `profiles.improve.<name>.maxCycles`
28
+ (default 1): when > 1, the improve passes run in an N-cycle loop so gate-accepted
29
+ output of cycle N feeds cycle N+1 within the same run (re-running ensureIndex +
30
+ ref selection each cycle), stopping at a fixed point and respecting the run budget.
31
+ `maxCycles: 1` = byte-identical to today.
32
+
33
+ ### Fixed
34
+
35
+ - **Release CI unblocked.** `runCliCapture` (test harness) restored `process.exitCode`
36
+ to a captured `undefined`, which under `bun test` does not clear a previously-set
37
+ non-zero exit code — so the unit suite exited 1 with 0 failures at `TEST_PARALLEL=1`
38
+ (exactly how `release.yml` runs), silently blocking every npm publish since beta.11.
39
+ Fixed to restore to `0`. (This is why beta.26 was the first successful workflow publish.)
40
+
41
+ ### Changed
42
+
43
+ - **CI/release tests sharded across runner jobs (~15 min → ~2 min).** Bun 1.3.x
44
+ in-process test parallelism (`--parallel=N`, N>1) hits an intermittent
45
+ `epoll_ctl EEXIST` race / busy-spin hang on the `--isolate` workers, which had
46
+ forced fully-sequential (`TEST_PARALLEL=1`) runs. Tests now shard across separate
47
+ runner jobs (each a separate process tree, so no cross-shard fd/epoll collisions)
48
+ with `--parallel=1` within each shard; the matrix runs shards concurrently. The
49
+ release gate runs the identical set of tests. Local `bun run check` defaults to
50
+ sequential too (the only safe mode on this Bun version). Coverage unchanged.
51
+ Each shard runs through `scripts/run-test-shard.sh`, which retries **only on a
52
+ hang/timeout** (the busy-spin can rarely fire even at `--parallel=1`) and never
53
+ on a real test failure, so genuine red tests still fail fast and are never masked.
54
+
55
+ ## [0.9.0-beta.26] — 2026-06-20
56
+
57
+ ### Added
58
+
59
+ - **#628 — configurable SQLite journal mode (`AKM_SQLITE_JOURNAL_MODE`) for network
60
+ filesystems.** AKM previously opened every database with `PRAGMA journal_mode = WAL`
61
+ unconditionally, which cannot run on a network filesystem (NFS/SMB/Azure Files) —
62
+ WAL's `-shm` shared-memory wal-index can't be `mmap`'d over a network mount. You can
63
+ now set `AKM_SQLITE_JOURNAL_MODE` to `WAL` (default), `DELETE`, or `TRUNCATE`, applied
64
+ at **all five** db openers (`state.db`, `index.db` ×2 paths, `workflow.db`, `logs.db`).
65
+ At the `WAL` default AKM auto-detects a network mount for the data dir and transparently
66
+ falls back to `DELETE` (rollback journal + `synchronous = FULL`) with a one-line warning;
67
+ invalid values warn once and fall back to `WAL`. **Default behavior is byte-identical.**
68
+ This lets the AKM database subtree live on a shared volume (e.g. Azure Files under
69
+ Azure Container Apps). New docs section "Hosting AKM databases on a network share
70
+ (NFS/SMB)" in `docs/configuration.md`.
71
+
72
+ ## [0.9.0-beta.25] — 2026-06-19
73
+
74
+ Completes the recombine / extract-efficiency / graph thread. All new improve
75
+ passes are **opt-in (default off)**, so default behavior is unchanged.
76
+
77
+ ### Added
78
+
79
+ - **#606 — event-driven extract (`akm extract --watch`).** Opt-in watch mode: an
80
+ injectable, debounced watcher triggers extraction shortly after a session file
81
+ appears, with a clean `stop()` handle. The `8,28,48` cron remains the fallback;
82
+ no daemon is auto-launched.
83
+ - **#625 — recombine second pass (hypothesis → lesson).** The opt-in `recombine`
84
+ process (#609) now consumes `confirmThreshold` (default 2): a generalization
85
+ re-induced that many consecutive runs is promoted from a `type: hypothesis`
86
+ proposal to a `type: lesson` proposal through the normal queue + quality gate
87
+ (never a direct stash write). Hypotheses that stop recurring decay. Backed by a
88
+ new `recombine_hypotheses` table in `state.db`.
89
+
90
+ ### Changed
91
+
92
+ - **#624 (P1) — graph storage decoupled from `entries.id`.** `graph_files` is
93
+ re-keyed on `(stash_root, file_path, body_hash)`, so extracted graph data now
94
+ **survives a reindex** of unchanged files instead of being cascade-wiped. The
95
+ upgrade is migrated in a **targeted, graph-only path** that preserves existing
96
+ graph data and leaves the entry index, embeddings, FTS, and LLM-enrichment cache
97
+ untouched — **no full index rebuild and no re-embed** on upgrade. (P2 priority-
98
+ ranked extraction and P3 lazy/on-demand extraction remain deferred.)
99
+
100
+ ### Fixed
101
+
102
+ - Graph re-key migration no longer triggers a destructive full-index rebuild: it
103
+ is a graph-scoped table migration (no `DB_VERSION` bump), and it **copies** the
104
+ existing graph rows into the new schema rather than dropping them.
105
+ - Test-suite `/tmp` hygiene: sandbox teardown now fires on `SIGINT`/`SIGTERM`/
106
+ `SIGHUP` (not just clean exit), and a `sweep:tmp` step reclaims stale `akm-*`
107
+ sandbox dirs left by force-killed workers — eliminating the tmpfs accumulation
108
+ that caused intermittent `EEXIST: epoll_ctl` test flakes.
109
+
110
+ ## [0.9.0-beta.20] — 2026-06-18
111
+
112
+ ### Fixed
113
+
114
+ - **`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.
115
+
116
+ ## [0.9.0-beta.19] — 2026-06-17
117
+
118
+ ### Fixed
119
+
120
+ - **`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.
121
+ - **`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.
122
+
123
+ ## [0.9.0-beta.18] — 2026-06-17
124
+
125
+ ### Changed
126
+
127
+ - **Health report: Recent Runs table now shows all filtered runs in descending order** (newest first) instead of capping at the last 10.
128
+ - **Health report: Removed "Command Set Used" section.**
129
+ - **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.
130
+
131
+ ### Changed (migration required)
132
+
133
+ - **WS-2 outcome loop (#613) — default-off weight change (state.db migration 010).**
134
+ Every `akm improve` run now writes an `asset_outcome` row per processed asset
135
+ (state.db migration `010`) and computes a differential usefulness signal
136
+ (`outcome_score`) per ref. The outcome signal is persisted and visible in the
137
+ health report, but the **weight change is gated behind a config flag** (see
138
+ below). Ranking is unchanged from WS-1 by default.
139
+
140
+ **Opt-in weight change.** The WS-2 projection weights (`w_e=0.25, w_o=0.15,
141
+ w_r=0.60`) affect ranking only when you explicitly set
142
+ `improve.salience.outcomeWeightEnabled: true` in your `akm.yaml`. The default
143
+ (`false`) keeps WS-1 parity weights (`w_e=0.30, w_r=0.70`, `w_o=0`), so
144
+ existing users see no ranking change on upgrade.
145
+
146
+ **Part-V measurement gate.** Before enabling the weight change, run the Part-V
147
+ T0 baseline (`scripts/akm-eval` + `akm health`; confirm proactive accept
148
+ ≥ 0.9× reactive; reversion ≤ 0.15; retrieval-delta ≥ 0; coverage not
149
+ regressed). That gate requires a running production stash and cannot be
150
+ exercised in CI. Once confirmed, set
151
+ `improve.salience.outcomeWeightEnabled: true` to activate the three-way split.
152
+
153
+ **Outcome loop mechanics.** `outcome_score` is a differential prediction-error
154
+ signal: `(retrieval_delta − expected_delta) − PENALTY × retrieval_delta × (1 −
155
+ accepted_change_rate) + valence`, tracked via an EMA (α=0.3). New rows are
156
+ warm-started from the utility EMA score (clipped to 0.3) so the signal is
157
+ non-zero from launch. A stash-wide diversity floor (10% of the max score) prevents
158
+ rare-but-correct assets from being permanently outcompeted. An inverted-proxy
159
+ tripwire (`corr(outcome_score, accepted_change_rate) < −0.3`) emits an
160
+ `outcome_proxy_inverted` health event when the signal degrades.
161
+
162
+ `review_pressure` is computed and persisted per asset but is **not yet wired into
163
+ the admission policy** — that is deferred to a later work stream per plan §Part-VI
164
+ #613. The column is present and populated; routing it into the consolidation-
165
+ selection filter is the next step.
166
+
167
+ - **WS-1 salience vector (#618) — default-on ranking change.** The eligibility sort
168
+ for all `akm improve` runs (whole-stash, type, and ref scope) has changed from
169
+ `combinedEligibilityScore = utility·0.7 + negativeOnlyRatio·0.3` to
170
+ `rankScore = (0.3·encodingSalience + 0.7·retrievalSalience) × sizePenalty`
171
+ (feedback valence and utility EMA dropped from ordering until WS-2 re-introduces
172
+ outcome salience). Assets are now ranked by retrieval frequency × recency × type
173
+ importance rather than by feedback magnitude. Because the old
174
+ `combinedEligibilityScore` ordering was never persisted, a forgetting comparison is
175
+ not possible on the first run; instead a one-time `improve_salience_first_run` marker
176
+ event is emitted to record the transition. On every subsequent run a stash-wide
177
+ `improve_salience_rank_change` drift report (including `stashSize`) is emitted so
178
+ rank movement under the new scoring can be tracked over time.
179
+ The Part-V measurement protocol (T0 baseline via `scripts/akm-eval` + health report,
180
+ throughput/quality gate) is deferred to the WS-2 milestone, when outcome salience
181
+ re-joins the projection and re-tuning is triggered.
182
+
183
+ ## [0.9.0-beta.12] - 2026-06-15
184
+
185
+ Improve-tuning work streams (all **default-off / parity-preserving** — no behavior
186
+ change until explicitly enabled).
187
+
188
+ ### Added
189
+
190
+ - **#617 — deterministic near-duplicate memory dedup** (`processes.consolidate.dedup`,
191
+ default off). A cheap no-LLM pre-pass in front of consolidation collapses obvious
192
+ duplicates — `.derived`+origin pairs and content twins (normalized content-hash
193
+ equality, or embedding cosine ≥ `cosineThreshold`, default 0.97). Each dropped
194
+ variant is archived + backed up before deletion; hot memories are never
195
+ collapsed; distinct-but-related memories fall through to the LLM.
196
+ - **#581 — judged-state cache for consolidation** (`processes.consolidate.judgedCache`,
197
+ default off). New state.db table (`consolidation_judged`) records each memory's
198
+ content hash + outcome when the LLM judges it; subsequent runs skip
199
+ judged-unchanged memories, converting coverage from O(time-window) to
200
+ O(changed/new) so a run can sweep the full corpus. Fails open; failed chunks
201
+ and dry-runs never poison the cache. (state.db migration `007`.)
202
+ - **#612 — auto-accept gate calibration** (`improve.calibration`, auto-tune default
203
+ off). Joins predicted gate confidence to realized accept/reject outcomes into a
204
+ reliability table + calibration gap, surfaced in `akm health` (+ summary rows in
205
+ the HTML report). Opt-in bounded threshold auto-tune nudges the accept threshold
206
+ within a configured band toward a target accept rate, logged via a
207
+ `calibration_autotune` event. (Replay-prioritization from prediction error is
208
+ deferred — it depends on the #610 replay budget, a 0.10 item.)
209
+
210
+ ### Fixed
211
+
212
+ - **#614 — symmetric valence weighting** (`profiles.improve.*.symmetricValence`,
213
+ default off). The eligibility sort weighted feedback negative-only; when enabled
214
+ it uses a symmetric `|valence|` magnitude so strong positive and strong negative
215
+ feedback both drive attention (utility stays the dominant factor), routing
216
+ high-negative → fix and high-positive → reinforce lanes.
217
+
218
+ ## [0.9.0-beta.11] - 2026-06-15
219
+
220
+ ### Added
221
+
222
+ - **`extract.maxSessionsPerRun`** (default 25) — caps the NEW sessions the
223
+ extract pass LLM-processes in a single run so a backlog (e.g. after downtime)
224
+ can't push one run past its scheduled-task timeout. Overflow sessions stay
225
+ unseen and are picked up by later runs, so coverage is preserved. `0` disables.
226
+
227
+ ### Fixed
228
+
229
+ - **Auto-accept validation failures are no longer a blind leak.** When a
230
+ confidence-passing proposal fails promotion validation, the gate now captures
231
+ the reason (the `validateProposal` finding kind, e.g. `validation:description-quality`),
232
+ records it on the proposal (`akm proposal show` explains the rejection), logs
233
+ it, and exposes `failedByReason` on the gate result — so the ~5% leak is
234
+ diagnosable instead of silently warned-and-dropped.
235
+ - **Inflated skip-reason aggregates in `akm health`.** `no_new_signal` /
236
+ `profile_filtered_all_passes` are per-run snapshots of a stable set; the
237
+ window aggregator summed their per-run counts (≈2.7M / 3M). It now uses the
238
+ most recent run's count for these aggregated-snapshot reasons while still
239
+ summing genuine per-occurrence skips.
240
+
241
+ ## [0.9.0-beta.10] - 2026-06-15
242
+
243
+ ### Added
244
+
245
+ - **#603** — `akm health` pool-saturation advisory. Instead of alerting on the
246
+ raw `sessionsScanned` count (which false-alarmed on normal cadence changes),
247
+ a new `pool-saturation` advisory reports the ratio of new (unseen) sessions
248
+ to the total session pool: informational below 10% (expected steady state),
249
+ warning below 2% (possible discovery/dedup bug). Heuristic, never gates
250
+ overall status.
251
+ - **#576** — the `akm health` HTML report now renders the real per-stage LLM
252
+ token/time aggregate (a "🧠 LLM Work" KPI card + LLM token/call/wall-time
253
+ summary rows) from the captured `llm_usage` events, replacing the GPU-time
254
+ proxy.
255
+ - **Built-in `akm health --format html` report overhaul** — the report is now a
256
+ strict superset of (and supersedes) the external `akm-health-report` stash
257
+ skill. Restored the interactive filter bar (time-slice 1d–21d, task, status)
258
+ with client-side chart/table re-render and the Last-10 "Task" column;
259
+ reordered sections to a decision-first flow (verdict → action items → KPIs →
260
+ table → charts); added a synthesized one-sentence **Verdict** (status + 2–3
261
+ drivers) and a freshness line; merged the duplicate Advisories / What-to-Watch
262
+ into one prioritized, de-duplicated **Action Items** list (P1/P2/P3 +
263
+ remediation command); added a per-stage **LLM token** stacked-bar chart and
264
+ `dataZoom` sliders on dense charts; fixed the failed-run scatter x-alignment
265
+ (now shape-encoded); KPI-card colors are now health signals (not decoration);
266
+ added metric-glossary tooltips, chart `aria-label`s, contrast fixes, and
267
+ empty-state overlays. Deterministic output preserved.
268
+
269
+ ### Fixed
270
+
271
+ - **Health report accuracy** (follow-ups to the overhaul): the per-run **Task**
272
+ column/filter now show the real scheduled task (`akm-improve-frequent`, …) via
273
+ a ±5min `task_history` join instead of the run's scope (which is `all` for
274
+ every scheduled run); the time-**slice** filter options are now derived from
275
+ the report's `--since` window (e.g. All/3d/1d/12h/6h for a 7d report) and
276
+ default to "All" — replacing the hard-coded 1d–21d list that didn't match the
277
+ window; and the trend **deltas** now default their compare window to `--since`
278
+ (like-for-like, e.g. last 7d vs prior 7d) instead of a fixed 24h, which had
279
+ produced nonsensical period-over-period percentages on multi-day reports.
280
+ - **Inflated stash-snapshot metrics in `akm health`.** `memorySummary`
281
+ (derived/eligible) and `profileFilteredRefs` are whole-stash snapshots recorded
282
+ on every run, but the window aggregator was **summing** them across all runs —
283
+ e.g. "915,258 of 1,226,025 eligible" and a 2.4M filtered-ref count. They now
284
+ take the most recent run's snapshot (the current state). Per-run *work* metrics
285
+ (promoted, MI written, graph entities, …) remain genuine window sums.
286
+ - **Health report polish:** the akm version is stamped in the header (under the
287
+ AKM logo) and footer; the steady-state `no new signal since last proposal`
288
+ distill reason is excluded from the skip-reason chart (it drowned out the
289
+ actionable reasons); and the Consolidation Output chart now draws Promoted as a
290
+ line on a secondary right-hand axis (it dwarfs merged/deleted) with merged and
291
+ deleted as bars on the left axis.
292
+
293
+ - **#598** — process-level tuning fields (`consolidate.incrementalSince`,
294
+ `minPoolSize`, `neighborsPerChanged`, `extract.minContentChars`, per-process
295
+ `enabled` flags) now survive an `akm config` rewrite. They are first-class
296
+ typed `ImproveProcessConfigSchema` fields, so the load→save round trip no
297
+ longer silently drops them. Unknown process sub-keys hard-error at load
298
+ (`ConfigError`) rather than being silently discarded — the deliberate,
299
+ documented resolution. Regression-guarded by
300
+ `tests/config-process-roundtrip.test.ts`.
301
+
302
+ ## [0.9.0-beta.9] - 2026-06-14
303
+
304
+ Restore and instrument `akm improve` steady-state output. The reflect/distill
305
+ self-improvement lanes had been near-zero in steady state because the
306
+ signal-delta eligibility gate was the only lane (cache "no-access = no-work"
307
+ pathology) and the high-retrieval fallback was structurally dead. This release
308
+ revives proactive improvement, adds attribution + a measurement/kill-criterion
309
+ system so the lane must prove its value, and right-sizes reflect budgets to
310
+ their task timeouts.
311
+
312
+ ### Added
313
+
314
+ - **Proactive maintenance selector** (`proactiveMaintenance` improve process):
315
+ due-gated, composite-priority (`importance × log(1+retrievalFreq) ×
316
+ recencyDecay / log(size)`), bounded rotating top-N reflect/distill over
317
+ stale/never-reflected assets. **Disabled by default**; enable per profile.
318
+ - **Eligibility attribution**: every reflect/distill proposal is stamped
319
+ `eligibilitySource ∈ {signal-delta, high-retrieval, proactive, scope,
320
+ unknown}` on `reflect_invoked`/`distill_invoked`/`promoted` events and the
321
+ proposal record, so outcomes are sliceable by lane.
322
+ - **Measurement system** under `scripts/akm-eval/`: a real-query retrieval suite
323
+ generated from `usage_events`, and `akm-eval-proactive-verdict` — a read-only
324
+ kill-criterion runner comparing the proactive lane (treatment) vs due-but-
325
+ untouched assets (control). Emits PASS/FAIL/INCONCLUSIVE and recommends
326
+ disabling the lane on FAIL. New `proactive_selected` event +
327
+ `proactiveSelected`/`proactiveDueTotal`/`proactiveNeverReflected` fields on
328
+ `improve_completed`.
329
+
330
+ ### Fixed
331
+
332
+ - Revived the P0-A high-retrieval fallback: genuinely zero-feedback assets were
333
+ routed to the fully-skipped branch one phase before the fallback could see
334
+ them, so frequently-retrieved-but-never-rated assets were never improved.
335
+ - `getRetrievalCounts` now normalizes bare vs `origin//`-prefixed refs (it was
336
+ dropping ~half the retrieval signal) and counts `curate` events
337
+ (`akm curate` now records per-item `entry_ref`).
338
+ - The fully-skipped `no_new_signal` branch emitted one `improve_skipped` event
339
+ per ref (~11K writes/run, ~400K rows/day) — a contributor to 900s improve
340
+ timeouts and state.db bloat. Collapsed into one aggregated counted event.
341
+
342
+ ## [0.9.0-beta.8] - 2026-06-13
343
+
344
+ Fix multi-process SQLite contention in `index.db` and harden concurrent proposal
345
+ queue mutations.
346
+
347
+ ### Changed
348
+
349
+ - Added a global `index.db` writer lease used by foreground indexing,
350
+ background auto-index, improve maintenance index writers, graph updates, and
351
+ feedback writes.
352
+ - Replaced the racy background index PID-file dedup flow with lease-based
353
+ coordination and explicit handoff to the spawned worker.
354
+ - `akm feedback` now uses blocking index preparation and writes under the same
355
+ `index.db` lease, avoiding self-inflicted `database is locked` failures.
356
+ - Proposal queue create/archive/gate-decision mutations now run under
357
+ `BEGIN IMMEDIATE` state.db transactions so concurrent processes serialize on
358
+ live queue state.
359
+
360
+ ## [0.9.0-beta.7] - 2026-06-13
361
+
362
+ Fix the `akm improve` regression introduced by background `ensureIndex`.
363
+
364
+ ### Changed
365
+
366
+ - Added an explicit `ensureIndex` mode so callers choose `background` or
367
+ `blocking` behavior directly instead of relying on hidden environment state.
368
+ - `akm improve` now uses blocking index preparation before collecting eligible
369
+ refs, restoring the post-upgrade empty-index recovery path.
370
+ - Removed the `AKM_INDEX_INLINE` test-only override so tests exercise the same
371
+ index behavior model as production.
372
+
373
+ ## [0.9.0-beta.6] - 2026-06-12
374
+
375
+ Pipeline optimization: new per-process config fields wire up the consolidation
376
+ and improve pipeline knobs exposed by the optimization report — incremental
377
+ consolidation, pool caps, distill gating, and memory inference throttling.
378
+
379
+ ### Added
380
+
381
+ - **`consolidate.incrementalSince`** — profile config field that narrows the
382
+ consolidation candidate pool to memories modified within the given window
383
+ (e.g. `"1h"`, `"4h"`) plus their graph neighbours. Enables frequent
384
+ consolidation passes (e.g. `quick-shredder` every 15 min) without full-pool
385
+ sweeps. Absent = full-pool sweep (correct for nightly runs).
386
+ - **`consolidate.limit`** — hard cap on memories processed per consolidation
387
+ pass, applied after incremental narrowing. Prevents runaway full-pool sweeps
388
+ in the nightly default profile.
389
+ - **`consolidate.neighborsPerChanged`** — configurable graph-neighbour count
390
+ per changed memory during incremental consolidation (was hardcoded to 5).
391
+ `quick-shredder` sets this to 3 for a 40% candidate reduction per burst.
392
+ - **`distill.requirePlannedRefs`** — when `true`, the distill process is
393
+ skipped entirely for distill-only refs when the reflect phase produced zero
394
+ planned refs. Eliminates hundreds of `distill-skipped` events on quiet passes
395
+ where all refs are on reflect cooldown.
396
+ - **`memoryInference.minPendingCount`** — minimum pending split-parent memory
397
+ count below which the inference pass is skipped entirely (zero LLM calls).
398
+ Prevents lock acquisition on passes where there is nothing to infer.
399
+ - **`reflect.limit`** — per-process ref limit for the reflect/distill loop,
400
+ applied as the improve run limit when no CLI `--limit` is given.
401
+ - **New `reflect-distill` improve profile** — dedicated reflect + distill +
402
+ memoryInference + triage profile for the every-4h `akm-improve-frequent`
403
+ task. `reflect.limit: 25` bounds LLM cost per pass.
404
+
405
+ ### Changed
406
+
407
+ - **`quick-shredder` profile tuned**: `incrementalSince` `4h` → `1h`,
408
+ `maxChunkSize` 25 → 35, added `minPoolSize: 10`, `neighborsPerChanged: 3`,
409
+ `memoryInference.minPendingCount: 5`. All `profile: "qwen-9b-shredder"`
410
+ process references removed — falls back to default LLM.
411
+ - **`default` improve profile** (nightly): extract disabled (dedicated
412
+ `akm-extract` task runs at 01:48), consolidate gets `limit: 500`,
413
+ reflect gets `limit: 100` and `allowedTypes`, distill gets
414
+ `requirePlannedRefs: true`, triage enabled at 50 accepts/run,
415
+ graphExtraction explicitly enabled.
416
+ - **Cron schedule optimised**: extract reverted to `8,28,48 * * * *` (3×/hr),
417
+ quick-shredder shifted to `4,19,34,49` (4-min extract gap), health-report
418
+ shifted to `:03` (avoids `:00` collision), `akm-improve-frequent` re-enabled
419
+ at `45 */4` with `reflect-distill` profile.
420
+
421
+ ## [0.9.0-beta.3] - 2026-06-12
422
+
423
+ Stabilization batch closing the remaining 0.9.0 milestone: DB-locking and
424
+ improve-pipeline perf backports, extract/reflect gate fixes, SQLite-first
425
+ proposal and log storage, `--format html` output, and per-stage LLM telemetry.
426
+
427
+ ### Added
428
+
429
+ - **`--format html` output with per-command templates** (#582). `akm health
430
+ --format html` renders the full interactive health report (ECharts inlined by
431
+ default, or via CDN with `AKM_ECHARTS=cdn`); every other command falls back to
432
+ a dark-mode default template that pretty-prints its JSON. A global `--output
433
+ <path>` flag writes the rendered HTML to a file instead of stdout. Token
434
+ replacement only — no template engine. The standalone health-report skill is
435
+ now folded into core.
436
+ - **Per-stage LLM telemetry** (#576). Every `chatCompletion` call now records
437
+ tokens (prompt/completion/total/reasoning), wall-time, model, and
438
+ finish_reason as an `llm_usage` event, attributed to the pipeline stage via an
439
+ ambient `AsyncLocalStorage` context (`withLlmStage`) set once per phase — no
440
+ `stage` parameter threaded through call sites. `akm health` exposes per-stage
441
+ token and time aggregates. Telemetry is best-effort and can never fail a run;
442
+ capture is forward-only.
443
+ - **Per-proposal gate decision + confidence** (#577). When a proposal passes
444
+ through the auto-accept/triage gate, its outcome (`auto-accepted` /
445
+ `deferred` / `auto-rejected`), reason, confidence, measured value, and the
446
+ thresholds in effect are persisted on the proposal (in the SQLite metadata).
447
+ `akm proposal show`/`list` surface them with reconstructable comparisons
448
+ (e.g. `0.72 < 0.90`), so tooling can explain *why* each proposal is pending
449
+ instead of relying on a run-level aggregate. Forward-only; legacy proposals
450
+ render `unknown`.
451
+
452
+ ### Fixed
453
+
454
+ - **`SQLITE_BUSY` / "database is locked" under concurrent runs** (#584, #585,
455
+ #589). `busy_timeout` raised from 5 s to 30 s on every SQLite open path
456
+ (index.db and state.db); the improve maintenance pass now closes its index.db
457
+ handle before each reindex (which opens its own writer to the same WAL file);
458
+ and the post-loop purge reuses the long-lived events connection instead of
459
+ opening a second state.db writer. Together these eliminate all observed
460
+ lock failures from overlapping cron improve runs. (Backports of 0.8.8.)
461
+ - **Extract gate ignored the active profile's `extract.enabled: false`** (#593,
462
+ #594). The session-extraction gate hardcoded the `default` profile, so a
463
+ non-default profile (e.g. a quick pass) ran extract anyway — 300–600 s of
464
+ redundant work per run when a dedicated extract task also exists. The gate
465
+ now resolves `extract` against the active improve profile. (Backport of
466
+ 0.8.11.)
467
+ - **Memory inference burned LLM calls on already-derived parents** (#588). The
468
+ primary pass now checks for the `<parent>.derived.md` child on disk *before*
469
+ the LLM/cache call, and opportunistically marks the parent processed so it
470
+ never re-pends. Previously ~55 % of the inference budget was spent
471
+ rediscovering children that already existed.
472
+ - **Reflect no longer queues empty-diff or cosmetic-only proposals** (#580).
473
+ A deterministic, LLM-free noise gate diffs each candidate against the current
474
+ asset; byte-identical edits are dropped and changes that are pure formatting
475
+ (whitespace reflow, hard-wrap changes, code-fence language hints, YAML scalar
476
+ re-folding) are suppressed, each recorded via summary events so suppression
477
+ rates are visible in `akm health`.
478
+
479
+ ### Added
480
+
481
+ - **`minContentChars` pre-LLM extract gate** (#595, #596). Sessions whose raw
482
+ size is below `profiles.improve.<name>.processes.extract.minContentChars`
483
+ (default 10 — only truly empty sessions/journal files) skip the extract LLM
484
+ call entirely. Gates on raw input size, not post-noise-filter size.
485
+ (Backports of 0.8.12–0.8.14.)
486
+ - **Structured logs database** (#579). Task and run log lines now land in a
487
+ dedicated `logs.db` (WAL, 30 s busy_timeout) keyed by task, run, stream, and
488
+ time, with retention/purge wired into the existing purge pass and `ATTACH`
489
+ support for joining log lines to `state.db` rows (e.g. a failed
490
+ `task_history` row to its log output). The scattered-log audit and per-source
491
+ keep/move/drop decisions are documented in `docs/technical/logs-audit.md`.
492
+
493
+ ### Changed
494
+
495
+ - **Proposals are now stored canonically in SQLite** (#578). The previously
496
+ bypassed `proposals` table in state.db is the single source of truth; all
497
+ proposal commands (`list`/`show`/`diff`/`accept`/`reject`/`revert`/`drain`),
498
+ the improve auto-accept gate, and health metrics read and write it through
499
+ one storage layer. Pending file-based proposals are imported on first read;
500
+ `akm proposal *` UX is unchanged. Design and migration notes live in
501
+ `docs/technical/proposal-storage.md`.
502
+ - **Improve planning no longer does per-ref DB lookups or per-ref skip events**
503
+ (#591, #592). Eligible refs carry a pre-resolved `filePath`, removing a
504
+ serial async lookup per ref (~500 s on 9 k-ref stashes), and the
505
+ profile-filtered skip loop emits one summary event with a count instead of
506
+ thousands of rows. (Backports of 0.8.9–0.8.10.)
507
+
9
508
  ## [0.9.0-beta.2] - 2026-06-09
10
509
 
11
510
  ### Fixed
@@ -254,6 +753,167 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
254
753
  `migrate-storage` change is pinned by a sha256 + file-mode fixture-stash
255
754
  differential test.
256
755
 
756
+ ## [0.8.14] - 2026-06-11
757
+
758
+ ### Fixed
759
+
760
+ - **`akm extract` minContentChars default lowered from 500 to 10.** The 500-char
761
+ threshold used inputCount (raw session size) but analysis showed 209 of 218
762
+ candidate-producing sessions had inputCount < 500 — tiny agent sessions (22–368
763
+ chars) regularly yield 1–5 candidates. The only reliably skippable sessions are
764
+ empty ones (0 chars, journal files). Default lowered to 10 to catch only
765
+ truly empty sessions while preserving all signal-bearing content. Closes #597.
766
+
767
+ ## [0.8.13] - 2026-06-11
768
+
769
+ ### Fixed
770
+
771
+ - **`akm extract` minContentChars gate filtered all sessions.** The threshold was
772
+ checked against `filtered.stats.outputCount` (post-noise-filter chars), but the
773
+ pre-filter strips so much boilerplate that even signal-bearing sessions end up
774
+ below 500 chars of output. All 75 sessions in the first post-deploy run were
775
+ filtered, dropping candidates from 4–13 to 0. Fix: gate on `inputCount` (raw
776
+ session size) instead — a session with < 500 raw chars has nothing worth
777
+ extracting regardless of what the pre-filter produces. Closes #596.
778
+
779
+ ## [0.8.12] - 2026-06-11
780
+
781
+ ### Fixed
782
+
783
+ - **`akm extract` calling the LLM for noise sessions that never yield candidates.**
784
+ 96% of processed sessions (72/75 measured) produced zero candidates, consuming
785
+ ~330 s of LLM time per run. The pre-filter had no minimum content threshold —
786
+ sessions as short as 50 chars were sent to the LLM regardless. A new
787
+ `minContentChars` gate (default 500) skips the LLM call when post-filter
788
+ content falls below threshold, cutting extract LLM calls by ~95% on typical
789
+ stashes. Configurable via `profiles.improve.<name>.processes.extract.minContentChars`.
790
+ Closes #595.
791
+
792
+ ## [0.8.11] - 2026-06-11
793
+
794
+ ### Fixed
795
+
796
+ - **`akm improve --profile <name>` ignored profile's `extract.enabled: false` setting.**
797
+ The session-extraction gate in the preparation stage called
798
+ `isLlmFeatureEnabled(config, "session_extraction")`, which hardcodes a lookup
799
+ against `profiles.improve.default.processes.extract.enabled`. Any non-default
800
+ profile that set `extract.enabled: false` (e.g. `quick-shredder`) was silently
801
+ ignored, causing the extract pass to run regardless. The fix adds a
802
+ `resolveProcessEnabled("extract", improveProfile)` check so the active
803
+ resolved profile gates the pass correctly. Closes #593.
804
+
805
+ ## [0.8.10] - 2026-06-11
806
+
807
+ ### Fixed
808
+
809
+ - **`akm improve` taking 8–10 minutes per run due to O(n) DB writes for
810
+ profile-filtered refs.** When a profile disables reflect and distill for
811
+ certain asset types, `collectEligibleRefs` marks those refs as
812
+ `profile_filtered_all_passes`. The caller then emitted one `improve_skipped`
813
+ event per ref — a sequential DB write for each. On a ~9 000-ref stash this
814
+ was ~500 s of SQLite writes before any consolidation or memory inference
815
+ began. The fix collapses the per-ref loop into a single summary event
816
+ carrying a `count` field, eliminating ~9 000 sequential writes per run.
817
+ Closes #590.
818
+
819
+ ## [0.8.9] - 2026-06-11
820
+
821
+ ### Fixed
822
+
823
+ - **`akm improve` validation pass was O(n) in stash size, causing ~510 s overhead
824
+ on large stashes.** For every indexed ref, the preparation phase called
825
+ `findAssetFilePath()` — an async round-trip to the index DB followed by a
826
+ filesystem probe — serially inside a `for…await` loop. With ~9 000 indexed
827
+ refs at ~55 ms each, this loop consumed the entire 600–900 s run budget before
828
+ any reflect, triage, or memory-inference work began. The fix threads
829
+ `filePath` from the planning stage (`collectEligibleRefs`) through
830
+ `ImproveEligibleRef` so the validation pass and the disk-existence guard can
831
+ use the pre-resolved path directly. The async lookup is retained only as a
832
+ fallback for refs that enter via a narrow scope (e.g. `--scope ref:foo`).
833
+ Closes #587.
834
+
835
+ ## [0.8.8] - 2026-06-11
836
+
837
+ ### Fixed
838
+
839
+ - **SQLite `SQLITE_BUSY` errors under concurrent improve runs.** `busy_timeout`
840
+ was set to 5 000 ms in all three database open paths (`openDatabase`,
841
+ `openExistingDatabase`, `openStateDatabase`). Under a busy cron schedule — or
842
+ when a reindex triggered by memory inference ran concurrently with an event
843
+ write — the 5 s window was routinely exhausted, producing "database is locked"
844
+ failures. Raised to 30 000 ms across all three paths so transient lock
845
+ contention is retried for up to 30 s before surfacing as an error.
846
+
847
+ ## [0.8.7] - 2026-06-09
848
+
849
+ ### Fixed
850
+
851
+ - **`incrementalSince` duration strings were silently ignored.** Values like
852
+ `"30m"`, `"24h"`, `"7d"` were passed raw to `narrowToIncrementalCandidates`,
853
+ which compared them against ISO timestamps via string sort. All `2026-...`
854
+ timestamps are lexicographically less than `"30m"` (`'2' < '3'`) and `"24h"`
855
+ (`"20" < "24"`), so `isChanged()` always returned `false` and the candidate
856
+ pool was silently emptied rather than filtered to the window. The fix adds
857
+ `parseSinceToIso()`, which resolves human duration strings to absolute ISO
858
+ timestamps before comparison. Values that already look like ISO timestamps
859
+ are passed through unchanged.
860
+
861
+ ## [0.8.6] - 2026-06-09
862
+
863
+ ### Added
864
+
865
+ - **`consolidate.incrementalSince` profile config field.** Setting
866
+ `incrementalSince: "7d"` (or any duration string) in the `consolidate` block
867
+ of an improve profile narrows the candidate pool to memories modified within
868
+ that window plus their top-5 graph neighbours, keeping each pass focused on
869
+ recent changes. This makes it practical to run consolidation more often than
870
+ once per day (e.g. via `akm-improve-consolidate` every 4 h) without
871
+ re-scanning the full pool every time. The nightly default profile leaves this
872
+ unset (full-pool sweep, same as before). The `incrementalSince` option already
873
+ existed in `akmConsolidate()` but was hardcoded off at the call site; the
874
+ field is now surfaced in the config schema and read from the profile.
875
+
876
+ ## [0.8.5] - 2026-06-09
877
+
878
+ ### Fixed
879
+
880
+ - **Consolidation starved merge recall; the memory pool grew unbounded.** Commit
881
+ `633ece41` made the `incrementalSince` narrowing unconditional, so every
882
+ consolidation run only judged memories changed since the last run plus their
883
+ immediate vector-neighbors. Stale-but-unmerged duplicate clusters were never
884
+ re-examined, so the eligible pool grew monotonically and never shrank, and
885
+ contradiction detection (which rides on the consolidation pass) went dark.
886
+ Consolidation only runs on the nightly default-profile pass (`quick`/`frequent`
887
+ disable it), so a full-pool sweep is correct and affordable; the override is
888
+ removed. `lastConsolidateTs` still gates whether the pass runs.
889
+
890
+ ## [0.8.4] - 2026-06-08
891
+
892
+ ### Fixed
893
+
894
+ - **`akm tasks sync` ignored schedule changes.** Sync classified any task already
895
+ present in the OS scheduler as "unchanged" without comparing its installed
896
+ entry, so editing a task's `schedule:` in the `.yml` never reached the crontab —
897
+ the only way to apply a new schedule was to `remove` and re-`add` the task. The
898
+ same gap affected `tasks enable`/`disable`, which merely toggled the existing
899
+ cron line's comment and so re-enabled a stale schedule. Sync now compares the
900
+ backend's installed signature against the signature the current definition would
901
+ produce and reinstalls on drift (reported in a new `updated[]` field);
902
+ `enable`/`disable` reinstall from the current `.yml` instead of toggling in
903
+ place. Backends that can't cheaply read their installed form fall back to an
904
+ idempotent reinstall, so the fix is correct on launchd/schtasks too. The cron
905
+ backend gains `expectedSignature()` and a signature on each `list()` entry.
906
+
907
+ ### Added
908
+
909
+ - **`akm improve --skip-if-locked`.** When another improve run already holds the
910
+ lock, the run logs and exits 0 with a no-op result (`skipped.reason:
911
+ "lock-held"`) instead of failing with the "already running" config error
912
+ (exit 78). Intended for high-frequency scheduled runs (e.g. an every-30-min
913
+ `quick` pass) that would otherwise pile up exit-78 failures whenever a longer
914
+ run overlaps them. Default off — the hard error is preserved for interactive
915
+ use. The result is still recorded so the skip is auditable.
916
+
257
917
  ## [0.8.3] - 2026-06-08
258
918
 
259
919
  ### Fixed