akm-cli 0.9.14 → 0.9.15-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/CHANGELOG.md +559 -0
  2. package/STABILITY.md +6 -3
  3. package/dist/akm +54 -1
  4. package/dist/akm-migrate +34 -1
  5. package/dist/assets/prompts/reflect-feedback-framing.md +1 -0
  6. package/dist/assets/prompts/reflect-llm-framed-contract.md +2 -0
  7. package/dist/assets/prompts/reflect-llm-schema-contract.md +2 -0
  8. package/dist/assets/tasks/core/improve.yml +1 -1
  9. package/dist/assets/tasks/core/index-refresh.yml +1 -1
  10. package/dist/assets/tasks/improve/akm-graph-refresh-weekly.yml +1 -1
  11. package/dist/assets/tasks/improve/akm-improve-catchup.yml +1 -1
  12. package/dist/assets/tasks/improve/akm-improve-consolidate.yml +1 -1
  13. package/dist/assets/tasks/improve/akm-improve-frequent.yml +1 -1
  14. package/dist/assets/tasks/improve/akm-improve-nightly.yml +1 -1
  15. package/dist/cli/retired-commands.js +0 -1
  16. package/dist/cli/shared.js +9 -0
  17. package/dist/cli/unknown-flags.js +1 -0
  18. package/dist/cli.js +40 -3
  19. package/dist/commands/config-cli.js +85 -3
  20. package/dist/commands/env/env-cli.js +1 -42
  21. package/dist/commands/env/env.js +1 -1
  22. package/dist/commands/env/secret-cli.js +1 -2
  23. package/dist/commands/health/checks.js +357 -63
  24. package/dist/commands/health/engine-usage.js +45 -0
  25. package/dist/commands/health/improve-metrics.js +18 -0
  26. package/dist/commands/health/llm-usage.js +41 -1
  27. package/dist/commands/health/plugin-staleness.js +7 -3
  28. package/dist/commands/health/version-drift.js +93 -0
  29. package/dist/commands/health/windows.js +3 -1
  30. package/dist/commands/health.js +44 -9
  31. package/dist/commands/improve/consolidate/chunking.js +4 -2
  32. package/dist/commands/improve/improve-cli.js +99 -5
  33. package/dist/commands/improve/improve-report.js +154 -0
  34. package/dist/commands/improve/improve-result-file.js +45 -33
  35. package/dist/commands/improve/improve-strategies.js +133 -3
  36. package/dist/commands/improve/improve-usage-report.js +182 -0
  37. package/dist/commands/improve/improve.js +40 -3
  38. package/dist/commands/improve/locks.js +28 -78
  39. package/dist/commands/improve/planner.js +1 -0
  40. package/dist/commands/improve/preparation.js +9 -1
  41. package/dist/commands/improve/reflect.js +44 -4
  42. package/dist/commands/models-cli.js +50 -1
  43. package/dist/commands/proposal/repository.js +8 -3
  44. package/dist/commands/proposal/validators/proposal-quality-validators.js +41 -6
  45. package/dist/commands/proposal/validators/proposal-validators.js +24 -0
  46. package/dist/commands/read/search-cli.js +38 -2
  47. package/dist/commands/read/show.js +103 -4
  48. package/dist/commands/sources/info.js +5 -1
  49. package/dist/commands/sources/installed-stashes.js +58 -16
  50. package/dist/commands/sources/self-update.js +2 -2
  51. package/dist/commands/sources/stash-cli.js +48 -0
  52. package/dist/commands/tasks/tasks-cli.js +49 -2
  53. package/dist/commands/workflow-cli.js +86 -12
  54. package/dist/core/asset/markdown-fragments.js +35 -0
  55. package/dist/core/config/config-schema.js +14 -0
  56. package/dist/core/config/config.js +302 -24
  57. package/dist/core/config/schema/embedding.js +41 -0
  58. package/dist/core/env-secret-ref.js +58 -5
  59. package/dist/core/errors.js +30 -0
  60. package/dist/core/file-lock.js +49 -15
  61. package/dist/core/improve-result.js +51 -0
  62. package/dist/core/loopback.js +17 -0
  63. package/dist/core/parent-watchdog.js +64 -0
  64. package/dist/core/paths.js +11 -0
  65. package/dist/core/run-lock.js +107 -0
  66. package/dist/core/sensitive-marker-path.js +19 -0
  67. package/dist/core/state-db.js +74 -14
  68. package/dist/indexer/index-rebuild-lock.js +73 -0
  69. package/dist/indexer/index-writer-lock.js +40 -1
  70. package/dist/indexer/index-written-assets.js +29 -1
  71. package/dist/indexer/indexer.js +93 -29
  72. package/dist/indexer/materialize-embeddings.js +564 -48
  73. package/dist/indexer/search/db-search.js +49 -2
  74. package/dist/indexer/search/search-source.js +23 -1
  75. package/dist/integrations/agent/engine-resolution.js +96 -6
  76. package/dist/integrations/agent/execution-definitions.js +6 -15
  77. package/dist/integrations/agent/execution-lowering.js +6 -1
  78. package/dist/integrations/agent/execution-preparation.js +1 -1
  79. package/dist/integrations/agent/model-map.js +123 -20
  80. package/dist/integrations/agent/prompts.js +40 -8
  81. package/dist/integrations/agent/runner-dispatch.js +9 -3
  82. package/dist/integrations/agent/runner.js +2 -0
  83. package/dist/llm/client.js +8 -3
  84. package/dist/llm/embedder.js +20 -8
  85. package/dist/llm/embedders/local.js +10 -2
  86. package/dist/llm/embedders/remote.js +497 -32
  87. package/dist/output/shapes/helpers.js +38 -2
  88. package/dist/output/shapes/models-list.js +16 -0
  89. package/dist/output/shapes/passthrough.js +2 -0
  90. package/dist/output/shapes.js +4 -0
  91. package/dist/output/text/command-format.js +29 -0
  92. package/dist/output/text/helpers.js +1 -1
  93. package/dist/output/text/improve-report.js +27 -0
  94. package/dist/{commands/env/marker-path.js → output/text/models.js} +4 -3
  95. package/dist/output/text/show-format.js +4 -0
  96. package/dist/output/text.js +4 -0
  97. package/dist/scripts/akm-migrate-node.js +25146 -21759
  98. package/dist/scripts/akm-migrate.js +24271 -20885
  99. package/dist/storage/repositories/embedding-salvage-repository.js +184 -0
  100. package/dist/storage/repositories/improve-runs-repository.js +34 -0
  101. package/dist/storage/repositories/index-fts-repository.js +49 -6
  102. package/dist/storage/repositories/index-schema.js +16 -0
  103. package/dist/storage/repositories/index-vec-repository.js +30 -0
  104. package/dist/storage/repositories/workflow-runs-repository.js +55 -18
  105. package/dist/tasks/backends/cron.js +14 -7
  106. package/dist/tasks/run/run-native-task.js +23 -1
  107. package/dist/tasks/run/run-workflow-task.js +16 -0
  108. package/dist/workflows/exec/child-workflow.js +2 -2
  109. package/dist/workflows/exec/dispatch-redaction.js +21 -9
  110. package/dist/workflows/exec/run-workflow.js +6 -5
  111. package/dist/workflows/runtime/runs.js +33 -5
  112. package/docs/migration/release-notes/0.9.15.md +133 -0
  113. package/docs/migration/release-notes/README.md +5 -0
  114. package/docs/reference/cli.md +271 -30
  115. package/docs/reference/configuration.md +234 -21
  116. package/docs/reference/data-and-telemetry.md +8 -0
  117. package/docs/reference/tasks.md +16 -1
  118. package/docs/reference/workflow-schema.md +5 -1
  119. package/package.json +1 -1
  120. package/schemas/akm-config.json +47 -0
package/CHANGELOG.md CHANGED
@@ -4,6 +4,565 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
 
7
+ ## [0.9.15-beta.2] - 2026-09-09
8
+
9
+ ### Added
10
+
11
+ - **`akm workflow list`/`status --all-scopes` (#942).** Workflow runs are
12
+ partitioned by `scope_key`, a hash of the working directory a run was started
13
+ from, so a scheduled task and a human shell working in different directories
14
+ can each believe a ref has no active run and start one, while `akm workflow
15
+ list` from either scope shows only its own. `--all-scopes` searches every scope
16
+ instead of only the current one, and both commands' JSON envelope now carries a
17
+ top-level `scopeKey` naming the scope that was searched (`null` under
18
+ `--all-scopes`), so an empty result is no longer indistinguishable from
19
+ "nothing anywhere." Default output is unchanged.
20
+ - **`akm health`'s `task-fail-rate` advisory names the dominant command-task
21
+ failure reason (#943).** Evidence now always includes
22
+ `agentFailureReasonCounts`, a breakdown of `detail.reason` values (`timeout`,
23
+ `non_zero_exit`, `spawn_failed`, …) across command-task failures in the window,
24
+ and when the check is already `warn` and one reason covers at least half of
25
+ those failures the message names it, e.g. `(timeout-dominant: 9/12 command-task
26
+ failures)`. Investigating the reported issue found the underlying propagation
27
+ was already correct on this branch — a command dispatch that times out, is
28
+ killed, or returns a non-zero/`ok:false` result was already recorded `failed`
29
+ with its reason, and `akm task run` already exited non-zero for it — so that
30
+ contract is now pinned by a regression test instead of left implicit.
31
+ - **Every real `improve` run persists a `usageReport` field summarizing LLM usage
32
+ by process, engine, and model (#944).** `usageReport: {byProcessEngineModel,
33
+ noCalls}` folds a new process x engine x model cross-tab of the run's
34
+ `llm_usage` events together with the resolved process routing table (#947); it
35
+ is omitted from the result when both halves would be empty, matching the
36
+ envelope's existing convention. `noCalls` lists every LLM-backed process the
37
+ active strategy enabled but that made zero calls, each with a `reason` drawn
38
+ from the existing skip-reason vocabulary (`"engine_unavailable"`,
39
+ `"autonomy_gated"`, `"strategy_filtered_all_passes"`, a reflect/distill
40
+ dominant skip reason, or `"no_signal"`) — never a fabricated category. The same
41
+ table is printed to stderr after every real run.
42
+ - **`akm improve report [--run <id>] [--since <window>]` reads a run's LLM usage
43
+ back without hand-written SQLite against `state.db` (#944).** With no flags it
44
+ shows the most recent real run; `--run <id>` targets one specific
45
+ `improve_runs` row; `--since <window>` sums `usageReport` across every real run
46
+ started in the window. `"report"` is a reserved `improve` scope value matching
47
+ zero real assets, so it dispatches to the report before any lock, log, or index
48
+ side effect. A run recorded before 0.9.15 (or one whose result cannot be
49
+ decoded) has no persisted `usageReport`: the command recomputes the cross-tab
50
+ from that run's own `llm_usage` events and adds a `notes` entry explaining the
51
+ degradation instead of erroring or fabricating eligibility reasons.
52
+ `--run`/`--since` only mean anything with this scope, so passing either with
53
+ any other scope (or none) is now rejected instead of being silently ignored.
54
+ - **The health `llm_usage` aggregate (`akm health --since <window>`'s `llmUsage`
55
+ field) now tracks a `failures` count alongside calls and token totals (#944).**
56
+ Every existing breakdown (`byStage`, `byProcess`, `byEngine`, and the window
57
+ total) gains this count for free from the same aggregator; a call whose
58
+ `llm_usage` event records an error outcome increments it.
59
+ - **A config file can inherit a shared base via `extends` (#945).** Setting
60
+ `extends: <path|bundle//path>` deep-merges a base config underneath the local
61
+ file — local keys always win. The base resolves without the search index: a
62
+ filesystem path relative to the declaring file (`~` expanding), or an
63
+ already-synced bundle-relative file (`bundle//<path>`, resolved against that
64
+ bundle's content root, needing no asset type). It runs through its own version
65
+ and legacy-shape shims independently and may itself set `extends` (chained); a
66
+ cycle is a `ConfigError` naming the ref. A fleet of instances can now keep one
67
+ shared `engines`/`improve.strategies` block instead of hand-syncing it across
68
+ hosts.
69
+ - **`akm config get --show-source` and `akm config diff` inspect an effective,
70
+ `extends`-merged config (#945).** `config get <key> --show-source` wraps the
71
+ value as `{ value, source }`, reporting whether it came from `local`,
72
+ `extends:<ref>`, or `default`. `config diff <path|bundle//path>` prints sorted
73
+ `{ path, local, other }` rows for every leaf that differs between this
74
+ instance's effective config and another config file or bundle-relative file
75
+ (loaded through the same loader, so its own `extends` is honoured too),
76
+ redacting secrets on both sides first. Both are additive: `config get`'s
77
+ default bare-value shape is unchanged, and `config set`/`unset` still edit only
78
+ the local file.
79
+ - **A model-map column can borrow its model from a configured engine (#946).** A
80
+ `models.json` profile may now set `"engine": "<name>"` instead of a literal
81
+ `model` string, e.g. `"fast": { "opencode": { "engine": "local-fast" } }`, to
82
+ reuse `engines.local-fast`'s own `model` (and, for an `llm`-kind engine, its
83
+ `temperature`/`maxTokens`/`enableThinking`/etc. as `inference` defaults) rather
84
+ than hand-typing the value a second time. `model` and `engine` are mutually
85
+ exclusive on one profile; a reference to a missing engine, or one with no
86
+ usable model, fails with the same "a usable model is required after overlay"
87
+ error, now checked at `akm health` as well as at dispatch. Engine selection
88
+ itself is unchanged: `engine` is only an indirection for the model/inference
89
+ *value* of one column, never an override of which engine
90
+ `--engine`/`defaults.engine` actually dispatches to.
91
+ - **`akm models list` shows the effective model-alias table (#946).** It prints
92
+ every resolved (alias, column) pair with its `model`, `source` (`default` vs
93
+ `user`), and `via` (`literal` vs `engine`, naming the engine when applicable),
94
+ so an operator can see at a glance which mappings a local `models.json`
95
+ overrides and which fall through to AKM's installed defaults.
96
+ - **`akm improve --dry-run` reports the resolved process -> engine -> model
97
+ routing before anything runs, in a new `plan.processes` field (#947).** One row
98
+ per improve process (`reflect`, `distill`, `consolidate`, `memoryInference`,
99
+ `graphExtraction`, `extract`, `validation`, `triage`, `proactiveMaintenance`),
100
+ plus a `triage.judgment` row when configured: `enabled`, the resolved
101
+ `engine`/`model` for llm-backed processes, this process's own lowering
102
+ `notices`, an `unavailable: {configKey, reason}` when the engine or its
103
+ credential could not be resolved, and — for reflect/distill/consolidate —
104
+ `eligibleRefs`, the count of this run's effective refs the process would act
105
+ on. None of this is new resolution: the plan already computes it,
106
+ dispatch-free, before every invocation (dry or live); this only projects it
107
+ into the result. `--strategy` overrides are honored for free, unlike `akm
108
+ health`'s `active-improve-strategy` check, which still reads the configured
109
+ default strategy (`defaults.improveStrategy`) and reports no model.
110
+ - **`--plan` is a new alias for `--dry-run`, for previewing `plan.processes`
111
+ (#947).** It sets the exact same internal flag — no separate code path, no
112
+ additional network reachability probe; pair with `akm health --probe` to check
113
+ whether a named engine actually answers.
114
+ - **`akm workflow run --skip-if-locked` (#948).** Extends `improve
115
+ --skip-if-locked`'s skip-gracefully-instead-of-failing semantics to `workflow
116
+ run`: when another engine invocation already holds the run's lease
117
+ (`RUN_LEASE_HELD`) or `state.db` is contended (`STATE_DB_CONTENDED`), the flag
118
+ turns the failure into one warn line plus `{ ok: true, skipped: { reason:
119
+ "lock-held" | "state-db-contended" } }` at exit 0 instead of exit 75. Every
120
+ other failure — a bad flag, an unresolvable target — still fails loudly
121
+ regardless of the flag. Use it on high-frequency scheduled workflow runs so
122
+ they don't pile up failures while a longer-running invocation is in progress.
123
+ Not extended to `task run`.
124
+ - **`akm health` reports engines whose thinking-off control was ignored (#949).**
125
+ A new advisory, `thinking-control`, warns per configured LLM engine with
126
+ `enableThinking: false` when the report window's recorded usage shows reasoning
127
+ tokens anyway. It is passive — it re-reads the existing `llm_usage` window
128
+ aggregate rather than issuing a completion of its own, so a cold local model is
129
+ never woken just to run `akm health`. `unknown` when no engine sets
130
+ `enableThinking: false`, or when a configured one made no calls in the window.
131
+ - **`akm health` reports version drift, idle-but-bound engines, and which env
132
+ asset supplies a missing credential (#950).** Three new checks close a
133
+ fleet-awareness gap where `akm health` could pass on a stale host running
134
+ behind its peers, or on an engine that is configured and reachable but has not
135
+ actually been invoked in weeks. The new `cli-version` advisory compares the
136
+ installed akm-cli version against the latest GitHub release (the same source
137
+ `akm upgrade` already trusts) and warns when a newer release exists; it is
138
+ gated behind `--probe`/`--no-probe` like the engine-reachability checks, and
139
+ degrades to `unknown` (never a false warn) when offline or rate-limited. The
140
+ new `engine-last-used` advisory folds `llm_usage` events over a fixed 30-day
141
+ lookback — independent of `--since` — against the active improve strategy's
142
+ process-to-engine bindings, and warns when a bound engine has no recorded use
143
+ in that window; it stays `unknown` rather than warning until at least one
144
+ improve run has been recorded (started) in the same window, so a fresh install
145
+ is not noisy. Finally, when `default-llm-engine` or `configured-engines`
146
+ reports a required credential missing from the shell, the warn now names the
147
+ env asset (never the variable name) that supplies the same key when one exists
148
+ — for example `env asset env/lab supplies it — run under it (akm env run
149
+ env/lab -- ...)` — instead of a bare "unavailable" that hides the real remedy
150
+ for an operator whose normal workflow is `akm env run env/lab -- akm improve`.
151
+ - **`akm task list` is an alias for `akm search --type task` (#951).** 0.9.0
152
+ removed the `task list` command as a redundant second implementation of task
153
+ listing; this reintroduces only the spelling, not the logic. It delegates to
154
+ the same `akmSearch`/`parseSearchSource` path `akm search --type task` uses,
155
+ passing the query, `--limit`, and `--from` flags through unchanged and
156
+ returning the exact same envelope, including the `results` alias. `task show`
157
+ and `task remove` stay retired.
158
+ - **`akm info` exposes `dataDir`, `configDir`, `cacheDir`, and `stateDir`
159
+ (#951).** These are akm's resolved data/config/cache/state directories, so a
160
+ script can read `akm info --format json | jq -r .dataDir` instead of hardcoding
161
+ a path that differs between a host install and a container.
162
+ - **`akm index --reembed` forces a full re-embed (#955).** Bypasses the
163
+ compatibility check above entirely and purges + regenerates every stored
164
+ embedding, for the rare case where the check's verdict should not be trusted. A
165
+ targeted post-write embedding pass (after `akm remember`, `akm improve`, etc.)
166
+ never forces a rebuild on its own.
167
+ - **`akm index --skip-if-locked` lets a scheduled or opportunistic run step aside
168
+ instead of contending with one already in progress (#956).** Every explicit
169
+ `akm index` run now acquires an opt-in, PID-liveness-only rebuild lock (no
170
+ age-based stale reclaim — the same #872 lesson `akm improve`'s lock already
171
+ applies) and releases it on exit. This is **not** the blocking index-rebuild
172
+ lock #872 removed: a plain `akm index` with no flag is never gated by it — a
173
+ held lock only warns and the run proceeds unlocked, exactly as before.
174
+ `--skip-if-locked` mirrors `akm improve --skip-if-locked`: when the lock is
175
+ already held by a live process it skips gracefully (exit 0, `{ ok: true,
176
+ skipped: { reason: "lock-held", pid, launcherPid, startedAt } }` —
177
+ `launcherPid` is the holder's launcher pid when known, `null` otherwise,
178
+ #956) instead of piling up behind the other run. The shipped
179
+ `index-refresh` scheduled task now passes it.
180
+ - **`akm improve` reports which processes it skipped for an unavailable engine,
181
+ instead of dispatching with a doomed credential (#957).** A process whose
182
+ engine was configured but whose credential could not be resolved in this
183
+ process's own environment (a scheduler that strips `env/user.env` from the task
184
+ environment, for example) used to keep its runner and proceed to an
185
+ unauthenticated dispatch, with no run-level signal beyond a stderr warning. The
186
+ plan builder now folds a resolved-but-uncredentialed engine (including the
187
+ triage judgment engine) into the same `engineUnavailable` handling as "no
188
+ engine selected," and the result carries a new `skippedProcesses: [{process,
189
+ configKey, reason}]` field (omitted when nothing was skipped) naming which
190
+ engine and which credential reference — never its value — is missing. `ok` and
191
+ the exit code are unchanged, matching the `extract`/`skipReasons` contract
192
+ (#912): a scheduler must not start failing because one LLM process's credential
193
+ is temporarily missing while others still run. `--dry-run`/`--plan` never
194
+ dispatches, so a preview no longer aborts when every enabled process is
195
+ credential-unavailable either: it reports the affected processes in
196
+ `skippedProcesses` and shows them as unavailable (with their structurally
197
+ resolved engine and model) in `plan.processes`, the same as a run that does
198
+ dispatch.
199
+ - **`--require-engines` opts a run out of that degrade-and-continue behavior, and
200
+ is now set on all six shipped scheduled improve task templates (#957).** It
201
+ aborts (exit 78) right after the plan resolves, before any lock, log, or index
202
+ side effect, listing every unavailable process and its unresolved credential
203
+ reference — recommended alongside `--skip-if-locked` for scheduled runs, since
204
+ the operator's own shell can pass config validation while a scheduler's
205
+ stripped-down environment cannot.
206
+ - **`embedding.timeoutMs` configures the per-request embedding timeout
207
+ (#954).** The prior fixed 30s timeout cut off a slow local model server on
208
+ a large token-budget-bounded batch mid-response. Default 120s, used by both
209
+ the single-text and batch embedding request paths; it scales down for a
210
+ smaller-than-budget request (`clamp(timeoutMs × requestTokens /
211
+ tokenBudget, 30s, timeoutMs)`), so a dead endpoint is detected in seconds
212
+ on the common case of small documents. A request timeout no longer drops
213
+ its batch immediately: field confirmation showed the endpoint keeps
214
+ computing an abandoned request regardless, so akm now backs off (5s,
215
+ doubling, capped at 60s) and retries the SAME request once before ever
216
+ splitting or skipping it; a second timeout splits the batch in half (like
217
+ a context-size rejection) and retries each half the same way, down to
218
+ individual documents, and a single document that times out twice is
219
+ finally skipped.
220
+ - **`embedding.concurrency` overrides the fixed in-flight embedding request
221
+ window (#954).** Bounded 1-16; unset behavior is unchanged (1 for a
222
+ loopback endpoint, 2 for a remote one). 0.9.15-beta.1 shipped with no
223
+ config override for this window; the final release adds one after field
224
+ evidence that a multi-slot local server (llama.cpp `--parallel N`, vLLM)
225
+ sat idle behind the fixed default. Request size — `embedding.batchSize`
226
+ and `embedding.maxTokens` — remains the first throughput
227
+ lever; set this only for an endpoint that genuinely serves parallel
228
+ requests.
229
+ - **The embedding phase stops after 3 consecutive transport failures instead
230
+ of grinding through every remaining batch (#954).** A dead or hung
231
+ provider used to burn hours on a large stash, one request timeout at a
232
+ time, with no signal until a single aggregate warning at the end and
233
+ `ok: true`, exit 0. The pass now stops dispatching further requests and
234
+ reports failure after 3 consecutive failures at single-document size
235
+ (timeout or network error — a multi-document timeout is retried and split
236
+ smaller before it can ever count, so it is not by itself evidence the
237
+ endpoint is dead) or 3 consecutive network errors at any size (never
238
+ retried, so trusted immediately); a `context-window-exceeded` skip never
239
+ counts and resets both streaks. The failure message names how many
240
+ embeddings were stored before it gave up. Batches already committed are
241
+ kept.
242
+ - **`embedding.maxInputTokens` caps a single document's embedded text
243
+ instead of letting it fail a whole batch (#956).** llama.cpp rejects a
244
+ single sequence longer than its physical batch (`--ubatch-size`, default
245
+ 512) with HTTP 500 "input is too large to process," and the only
246
+ per-entry cap before this was 1,000,000 characters. `akm index` now
247
+ truncates a document's embedded text to `embedding.maxInputTokens`
248
+ (default 512, head only, unicode-safe) before batching rather than
249
+ skipping it; a document is skipped only when its truncated head is empty.
250
+ `embedding.contextLength` is Ollama's `num_ctx` only now — it used to also
251
+ silently set the per-request token budget (`embedding.maxTokens`), so
252
+ setting it for the server's context window changed request batching too.
253
+ The request budget is `embedding.maxTokens` (default 8000), so a request
254
+ carries about 16 documents alongside the new per-document cap by default.
255
+ - **`akm index` reports where its embedding credential came from, before the
256
+ first provider request (#953).** A field report suspected a gateway was
257
+ receiving unauthenticated embedding requests despite `embedding.apiKey`
258
+ being set to a `secret://` reference. Auditing and reproducing every path
259
+ that reaches `RemoteEmbedder` — plain `akm index`, the CLI as a real child
260
+ process, an `extends`-inherited config with adapter detection persisting
261
+ mid-run (#945), `akm bundle update`'s post-commit embedding pass, and the
262
+ `akm remember` write path's targeted re-embed — found every one already
263
+ resolves `secret://` through the same store lookup, now pinned by
264
+ integration and contract tests so a future config-flow change cannot drop
265
+ `apiKey` unnoticed. `akm index` now prints one default-level line before
266
+ its first provider request naming the endpoint, model, and credential
267
+ SOURCE — `secret://lab-api-key (store)`, `$LAB_API_KEY (env)`, `literal
268
+ apiKey`, or `none configured` — never the credential's value, so a field
269
+ run can compare it directly against what the gateway actually logged.
270
+ `--verbose` also names the config file the run loaded.
271
+
272
+ ### Changed
273
+
274
+ - **BREAKING: `RUN_LEASE_HELD` now exits 75, not 2 (#948).** A held workflow
275
+ run-lease refusal — `akm workflow run` or `akm workflow complete` racing
276
+ another engine invocation on the same run — was a `UsageError` (exit 2), which
277
+ schedulers and cron wrappers read as "fix the command line" rather than "try
278
+ again shortly." It is now a `TransientError` (exit 75, sysexits `EX_TEMPFAIL`);
279
+ the message, hint, and `RUN_LEASE_HELD` code are unchanged. A script or
280
+ scheduler that special-cased exit 2 to detect a held lease must switch to exit
281
+ 75, or check the JSON envelope's `code` field instead.
282
+ - **Thinking-control wire forms no longer depend on `provider` (#949).**
283
+ `chat_template_kwargs.enable_thinking` was previously sent only when `provider:
284
+ "vllm"` was set; every other provider (including none) got a bare top-level
285
+ `enable_thinking` that nothing was observed to honor. AKM now sends both wire
286
+ forms whenever `enableThinking` resolves to a value, regardless of `provider`.
287
+ An engine relying on `provider: "vllm"` purely for this side effect keeps
288
+ working identically. An engine configured with `provider: "openai"`, another
289
+ provider name, or no `provider` at all now also receives
290
+ `chat_template_kwargs`, which it did not before — this is what lets the same
291
+ engine block turn thinking off consistently behind a direct vhost, freellmapi,
292
+ or Bifrost, without a gateway silently dropping the one wire form it happened
293
+ to send.
294
+ - **`akm health --no-probe` now also skips the version-drift check (#950).**
295
+ `--probe`/`--no-probe` previously gated only LLM engine reachability; it now
296
+ also gates the `cli-version` advisory's GitHub release lookup, so an offline or
297
+ air-gapped host's existing `--no-probe` habit suppresses both network calls.
298
+ This is the second deliberate network exception in `akm health`, alongside the
299
+ pre-existing `plugin-version` advisory's `git ls-remote`.
300
+ - **The direct-LLM reflect path sizes its asset-content budget from the target
301
+ engine's `contextLength` instead of a flat 12,000-character cap (#952).** The
302
+ flat cap only exists to keep the prompt under OS `ARG_MAX` when passed as CLI
303
+ argv to an agent or SDK runner; the direct-LLM HTTP path never touches argv, so
304
+ agent and SDK runners are unaffected. The budget reserves half of the usable
305
+ window for the model's response, since a reflect rewrite returns a body roughly
306
+ the size of the input. Configure `engines.<name>.contextLength` to raise the
307
+ budget for a given engine; unconfigured engines keep today's effective
308
+ ~12,000-character behavior.
309
+ - **`akm index` commits embeddings per provider batch instead of buffering the
310
+ whole run for one final write (#954).** Earlier releases wrote every generated
311
+ vector in a single `db.transaction()` after the entire embedding pass finished,
312
+ so an interruption partway through (a competing indexer collision, a killed
313
+ process, any thrown provider error) discarded every embedding already computed,
314
+ not just the ones still in flight. Each request batch now commits inside its
315
+ own short transaction as it lands. This holds on every path that embeds: plain
316
+ `akm index`, the implicit reindex, the write path (`akm remember`/`import`/
317
+ `proposal accept`/`source clone`), and `akm bundle update` (see below).
318
+ - **A batch rejected for exceeding the endpoint's context window is split and
319
+ retried instead of skipped outright (#954).** `akm index`'s embedding pass now
320
+ recognizes HTTP 413 and known context-size error bodies and halves the failing
321
+ batch, retrying each half recursively down to individual documents. Only a
322
+ single document that still fails this way is skipped, as
323
+ `context-window-exceeded`; every other failure (network error, 5xx, malformed
324
+ response) keeps the prior skip-the-whole-batch behavior.
325
+ - **Embedding requests are dispatched through a small in-flight window instead of
326
+ strictly sequentially (#954).** The window defaults to 1 request at a time for
327
+ a loopback endpoint and 2 for a remote one; the actual throughput knob is
328
+ request size, via the existing `embedding.batchSize` (document cap) and
329
+ `embedding.maxTokens` (token budget), since a larger batch
330
+ takes about the same wall time as a single one. `embedding.concurrency`
331
+ (see Added, above) overrides this default for a server that genuinely serves
332
+ parallel requests.
333
+ - **`akm index` reports embedding progress and throughput in more detail as it
334
+ runs (#954).** A default-level line reports each provider batch as it
335
+ completes — document count, token count, elapsed time, and outcome
336
+ (`stored`/`failed: <reason>`/`retrying after <n> s`) — and a final line
337
+ reports total throughput (`entries/s`, `tokens/s`) plus every outcome: how
338
+ many embeddings were stored (and reused from a prior generation, when
339
+ salvage applied — see #955 below), oversized-skipped, timed out, and
340
+ failed, with the affected refs listed (first 20 by default, all of them
341
+ under `--verbose`).
342
+ - **A rename of `embedding.model` no longer forces a full re-embed by itself
343
+ (#955).** `akm index` used to purge and rebuild the entire vector index on any
344
+ change to the fingerprint it derives from `embedding.model`, including a pure
345
+ config rename that still resolves to the same underlying model (for example a
346
+ gateway prefixing `provider/model` onto an unchanged server). On a mismatch,
347
+ `akm index` now re-embeds a small sample of already-stored entries and keeps
348
+ the index when either the endpoint's reported model identity matches what it
349
+ reported last time, or the median cosine similarity between stored and freshly
350
+ re-embedded vectors is at least 0.999; otherwise it purges and rebuilds as
351
+ before, logging why. A genuine dimension change bypasses this check entirely
352
+ and still rebuilds unconditionally.
353
+ - **A canary that cannot reach the embedding endpoint leaves the index untouched
354
+ instead of purging it (#955).** When the fingerprint-rename canary's re-embed
355
+ attempt fails outright, `akm index` keeps the existing vectors and the old
356
+ fingerprint and reports the run as `unverifiable`, rather than destroying a
357
+ working index because the server happened to be down. The next `akm index` run
358
+ retries the canary once the endpoint is reachable again.
359
+ - **A purge now writes the new fingerprint before any embedding request, so an
360
+ interrupted rebuild resumes instead of restarting from zero (#955).**
361
+ Previously the fingerprint was only written at the very end of a successful
362
+ embedding pass, so an interruption partway through a fingerprint-triggered
363
+ rebuild left the old fingerprint in place — the next `akm index` saw the same
364
+ mismatch and purged again, discarding whatever had already been re-embedded.
365
+ The new fingerprint (and the observed identity) are now written in the same
366
+ transaction as the purge, before any vectors are requested; a restart then sees
367
+ a matching fingerprint and only re-embeds the entries still missing a vector.
368
+ - **`akm index --full` and an index-generation bump no longer re-embed
369
+ unchanged content (#955).** A full rebuild deleted every embedding
370
+ unconditionally and re-inserted entries under new ids, and the v22→v23
371
+ generation bump did the same on first open under a new binary — both
372
+ forced a full re-embed of the whole corpus even when nothing changed, the
373
+ likely cause of the multi-hour post-upgrade run reported against 0.9.14.
374
+ Vectors about to be discarded are now copied into a transient
375
+ `embedding_salvage` table (keyed by a hash of `search_text` plus the
376
+ fingerprint they were generated under) in the same transaction as the
377
+ discard — read back in bounded chunks rather than loaded wholesale, so a
378
+ large corpus does not spike memory, and a run with nothing to reuse costs
379
+ a single indexed lookup — and handed back to unchanged entries at the
380
+ start of the next embedding pass with zero provider calls — a progress
381
+ line reports the split (`Reused N embeddings from the previous
382
+ generation; embedding M new.`). Content that changed by even one byte, or
383
+ a fingerprint that no longer matches, still goes through the provider
384
+ normally. `akm index --reembed` and a canary "rebuild" verdict purge the
385
+ salvage table along with the stored embeddings; a canary "keep" verdict (a
386
+ fingerprint-string rename resolving to the same model) relabels it instead
387
+ so it stays reusable. An interrupted pass leaves the table intact for the
388
+ next attempt.
389
+ - **A write-path index update (`akm remember`, `akm import`, `akm proposal
390
+ accept`, `akm source clone`, extract session assets) never contends with a full
391
+ rebuild in progress; it skips and lets the rebuild heal the entry instead
392
+ (#956).** These commands make a just-written asset searchable immediately via a
393
+ targeted index upsert that opens `index.db` under a 5-second busy timeout — far
394
+ shorter than a full rebuild's single transaction. It now checks the new rebuild
395
+ lock first: a live holder means the upsert and embedding are skipped outright
396
+ (one log line naming the pid and the file that will be indexed by the next
397
+ pass), and every caller — including `akm proposal accept`, which used to report
398
+ a spurious "index finalization failed" — treats the skip as success, since the
399
+ file write itself already completed. A rebuild lock left by a dead process is
400
+ not treated as held here — reclaiming it stays `akm index`'s job.
401
+ - **A blocked `akm remember` (and other synchronous asset-mutation-lease writers)
402
+ now prints a wait notice instead of hanging silently for up to ten minutes
403
+ (#956).** The sync lease boundary (`withAssetMutationLeaseSync`) had no
404
+ progress feedback at all; a contended lease looked identical to a hang until it
405
+ either acquired or timed out. It now logs `waiting for <holder purpose> (pid N,
406
+ started T) — Ns` every 15 seconds, naming who actually holds the lease — the
407
+ same cadence the async path's `onWait` hook already had, but wired to an actual
408
+ warning since nothing called that hook. The 10-minute wait bound is unchanged;
409
+ this only makes an existing wait visible.
410
+ - **`akm health`'s `active-improve-strategy` check fails, not warns, when the
411
+ active strategy's LLM-backed work would be a total no-op (#957).** Previously
412
+ this check stayed `warn` regardless of how many of the strategy's enabled
413
+ processes were unavailable, so a nightly `improve` job that could not run a
414
+ single LLM-backed process still reported the same severity as one with a single
415
+ missing credential and several working ones. It now escalates to `fail`
416
+ specifically when every enabled `capability: "llm"` process in the strategy is
417
+ unavailable; a partial failure with at least one working process stays `warn`.
418
+ The check is also a direct projection of the same credential-aware plan
419
+ `improve` itself now builds (see Added, above), rather than a separate
420
+ re-derivation that could disagree with what a real run in the same environment
421
+ would do.
422
+ - **A failed embedding batch and `akm index`'s progress are visible without
423
+ `--verbose` (#954).** A failed provider batch used to log only under
424
+ `--verbose`; it now logs at the default `warn` level, naming the batch size
425
+ and reason. `akm index`'s `Embedded N/M entries.` line now fires after every
426
+ committed batch instead of every 500 stored entries, and the heartbeat names
427
+ the failed count too. In non-verbose JSON/yaml output mode, phase-start
428
+ messages and the heartbeat now reach stderr (via `info()`); text mode keeps
429
+ its spinner instead, and `--verbose` is unchanged. A silently grinding,
430
+ hours-long `akm index` run against a dead provider — with no output until
431
+ one aggregate warning at the very end — was the field report this fixes.
432
+ Source-cache hydration (which runs before `index.db` is even opened) now
433
+ reports its own progress the same way: `Hydrating source i/n: <name>` per
434
+ source, plus a 15s heartbeat while a sync is in flight.
435
+
436
+ ### Fixed
437
+
438
+ - **Starting a workflow ref that already has an active run in a different scope
439
+ now warns instead of silently duplicating it (#942).** `akm workflow run
440
+ <ref>`'s per-scope concurrency guard is unchanged by design — two unrelated
441
+ projects sharing one `state.db` can still run the same-named workflow
442
+ independently — but before starting a new run it now also checks for an active
443
+ run of the same ref in any OTHER scope and, if one exists, warns with that
444
+ run's id, scope, and start time, plus the same `akm workflow run <id>` / `akm
445
+ workflow abandon <id>` remedy. The existing "already active" guard errors now
446
+ also name the blocking run's scope, not just its id. `akm workflow
447
+ status`/`resume`/`abandon <run-id>` already worked from any scope (#919) and
448
+ are unchanged.
449
+ - **`akm workflow status <ref>` names the scope it searched when nothing is found
450
+ there (#942).** When the ref lookup finds no runs in the current scope and
451
+ `--all-scopes` was not passed, the not-found error now names the scope that was
452
+ searched and suggests retrying with `--all-scopes`, instead of a bare "no runs
453
+ found."
454
+ - **Concurrent akm commands writing `state.db` no longer crash with a bare
455
+ "database is locked" (#948).** An unrelated `akm improve` run, `akm workflow
456
+ run`, or scheduled task writing `state.db` at the same time used to exhaust the
457
+ write retry window and throw the raw SQLite driver error, surfacing as
458
+ `{"ok":false,"error":"database is locked"}` at exit 70 (internal/unclassified)
459
+ instead of a retryable failure. On exhaustion the error is now reclassified
460
+ into a `TransientError` with a dedicated `STATE_DB_CONTENDED` code (exit 75),
461
+ modelled on `RUN_LEASE_HELD`'s precedent (#924), with the original driver text
462
+ preserved as `cause`; a genuinely unrelated error (real corruption, a
463
+ body-thrown failure) is never reclassified and still surfaces as itself. The
464
+ improve run's own `improve_runs` ledger writes, previously bare
465
+ single-statement writes with no retry, now go through the same retry path.
466
+ - **`akm show env/<name>` lists key names in plain-text output (#951).** The
467
+ plain-text `show` renderer never read the response's `keys` field, so the
468
+ default (non-`--format json`) output for an env asset gave no way to audit
469
+ which keys a script depends on; `--format json` already carried them correctly.
470
+ - **The cron scheduler backend truncates its raw per-task log instead of
471
+ appending forever (#951).** The crontab entry installed for each task
472
+ redirected stdout/stderr with `>>` (append), so the log file grew without
473
+ bound; it is only a bootstrap safety net, since akm's own per-run log already
474
+ separates runs and keeps history in `logs.db`, so the redirect now truncates
475
+ (`>`) and the file holds exactly the latest run's raw output. An
476
+ already-installed `>>` row still parses correctly. `launchd`'s equivalent log
477
+ path is OS-managed append-only with no truncate mode, so it is left unchanged —
478
+ a wrapper-script rewrite is not justified by evidence that was Linux-only.
479
+ - **Removed the false `akm curate --rerank` / "curate reranks by intent" claims
480
+ from the docs and the `curate` command's own description (#951).** `akm curate`
481
+ never implemented reranking; a rerank engine kind is deferred to its own issue,
482
+ and the referenced Discord health-report embed script is outside this
483
+ repository.
484
+ - **Reflect no longer treats feedback lines as verified facts to insert into the
485
+ rewrite (#952).** A harness run against the reflect prompt on two model quants
486
+ showed the model inventing whole new sections — fabricated incident dates,
487
+ ports, disk layouts — whenever a feedback line asserted a claim the source
488
+ asset never made. Feedback is now framed as a signal to investigate, not a fact
489
+ to insert, and missing information gets a `TODO: verify …` placeholder instead
490
+ of an invented answer.
491
+ - **A leaked content-truncation marker can no longer end up in a proposal body
492
+ (#952).** Asset content over the active budget is capped with a marker the
493
+ model is told never to echo back; when a model echoes it anyway, the proposal
494
+ is now deferred for human review (`reflect-truncation-leak`) instead of
495
+ shipping silently. As a second layer, `proposal accept` — including drain
496
+ promotion — now rejects any reflect-sourced proposal whose body still contains
497
+ the marker, since a truncated body silently replacing a full asset is data
498
+ loss; re-run reflect on the ref to clear it.
499
+ - **`secret://<name>` engine credentials now resolve on the `akm improve` /
500
+ agent-dispatch and health-probe paths (#953).** 0.9.13's #917 CHANGELOG entry
501
+ claimed engine credentials could resolve from the secret store, but that was
502
+ only ever true for direct LLM calls (`llm/client.ts`) and, after a same-week
503
+ follow-up fix, embedding calls — `engine-resolution.ts`, the sole path `akm
504
+ improve`, workflow LLM steps, and `akm health`'s engine probes use, still threw
505
+ `Engine "<name>" has an invalid symbolic apiKey reference.` for any `secret://`
506
+ value and aborted the run. `secret://<name>` now resolves through the same
507
+ store lookup as those other call sites, deferred to actual dispatch so frozen
508
+ plans stay secret-free, and `akm health`'s credential check now probes the
509
+ store instead of reporting a `secret://`-backed engine as available
510
+ unconditionally.
511
+ - **An unset or empty `$VAR` referenced by an engine's `apiKey` now warns once,
512
+ naming the variable (#953).** Previously it silently sent an empty
513
+ `Authorization` header instead of surfacing the misconfiguration.
514
+ - **`akm bundle update` now commits its embedding pass durably instead of
515
+ nesting it inside its own transaction (#954).** Its coordinator called
516
+ `akm index` for its embedding phase too, INSIDE the same unified
517
+ `BEGIN IMMEDIATE` that covers content/lock/index/state — so every per-batch
518
+ commit (above) nested as an unobservable SAVEPOINT, and a SIGKILL mid-run
519
+ lost every embedding of the run rather than just the one in flight.
520
+ 0.9.15-beta.1 shipped claiming per-batch commits held on this path; the
521
+ final release makes it true: `generateEmbeddingsForDb` now refuses to run
522
+ against a connection that already has a transaction open (an internal
523
+ contract error, not a user-facing one), and `akm bundle update` runs its
524
+ embedding phase on a fresh connection AFTER its own commit instead. A
525
+ failing post-commit pass (provider down) still leaves the update itself
526
+ successful — content, lock, and index generation are already durably
527
+ committed — with the response's `index.semanticStatus` (new field) the
528
+ only sign semantic search fell behind (`"blocked"`), exactly like a plain
529
+ `akm index` run today.
530
+ - **A batch rejected by llama.cpp for exceeding its physical batch size is now
531
+ recognized as a context-size rejection (#954).** llama.cpp reports this as
532
+ an HTTP 500 with a body like "input is too large to process. increase the
533
+ physical batch size", which the existing context-size pattern
534
+ (`exceed_context_size_error`, "context size", …) did not match, so the
535
+ whole batch was dropped instead of being split and retried like a 413.
536
+ - **A `kill <launcher-pid>` no longer orphans the running `akm` process
537
+ (#956).** The published launcher (`scripts/node-runtime/akm`/
538
+ `akm-migrate`) now forwards SIGTERM/SIGINT/SIGHUP to its bun/node child
539
+ and exits alongside it, instead of leaving the child running — one field
540
+ report found 40 orphaned `bun …/dist/cli.js` processes in a single day,
541
+ some hours old, still hammering the embedding endpoint and holding the
542
+ rebuild lock. Every command also polls for reparenting (a launcher that
543
+ dies without delivering a signal — SIGKILL, an out-of-memory kill) and
544
+ re-raises SIGTERM on itself the moment it notices, reusing the same abort
545
+ path a real signal already takes. Lock messages ("another index run is
546
+ active...", "akm improve is already running...") and `akm index
547
+ --skip-if-locked`'s JSON result now name the launcher pid alongside the
548
+ pid that actually holds the lock — `pid 4242 (launcher 4240)` — since
549
+ every process listing and task log shows the launcher pid, not the
550
+ child's.
551
+ - **An index run interrupted before its first embedding pass ever completes
552
+ could force an unnecessary full re-embed on the next `akm index --full`
553
+ (#956).** A fingerprint-rename rebuild already wrote `embeddingFingerprint`
554
+ immediately, before any provider call, so an interruption right after that
555
+ decision still left a consistent record — but the common
556
+ first-pass/unchanged-fingerprint path deferred that write to a fully
557
+ successful run. A per-batch commit is durable the instant it lands
558
+ regardless, so an interrupted first-ever pass left real, already-embedded
559
+ vectors with no recorded fingerprint to tag them by, and a later full
560
+ rebuild's salvage-before-discard step (#955, above) treated the missing
561
+ fingerprint as "nothing was ever verified" and re-embedded everything
562
+ instead of reusing them. A plain `akm index` resume after an interruption
563
+ now embeds only the entries still missing a vector, with no purge and no
564
+ canary.
565
+
7
566
  ## [0.9.14] - 2026-09-04
8
567
 
9
568
  ### Added
package/STABILITY.md CHANGED
@@ -57,9 +57,9 @@ enumeration of the whole `proposal` noun group.
57
57
  | `akm search` | Stable | |
58
58
  | `akm curate` | Stable | |
59
59
  | `akm show` | Stable | |
60
- | `akm workflow status` | Stable | |
60
+ | `akm workflow status` | Stable | `--all-scopes` added in 0.9.15 (#942), additive. |
61
61
  | `akm workflow plan` | Evolving | New in 0.9.2; secret-free provenance output; envelope shape may change. |
62
- | `akm workflow list` | Stable | |
62
+ | `akm workflow list` | Stable | `--all-scopes` and a top-level `scopeKey` envelope field added in 0.9.15 (#942), both additive. |
63
63
  | `akm workflow create` | Stable | |
64
64
  | `akm workflow resume` | Stable | |
65
65
  | `akm workflow abandon` | Stable | |
@@ -75,9 +75,10 @@ enumeration of the whole `proposal` noun group.
75
75
  | `akm migrate apply` | Internal | Forwards to the standalone `akm-migrate` tool; renders its result through the normal `--format` pipeline (not exempt — see below). Listed (not hidden) in `--help`/completions. |
76
76
  | `akm config path` | Stable | |
77
77
  | `akm config list` | Stable | |
78
- | `akm config get` | Stable | |
78
+ | `akm config get` | Stable | Default bare-value shape unchanged; `--show-source` (0.9.15, opt-in) wraps it as `{ value, source }`. |
79
79
  | `akm config set` | Stable | |
80
80
  | `akm config unset` | Stable | |
81
+ | `akm config diff` | Evolving | New in 0.9.15; compares effective (post-`extends`) configs, secrets redacted. |
81
82
  | `akm feedback` | Stable | |
82
83
  | `akm log` | Evolving | |
83
84
  | `akm agent` | Evolving | |
@@ -113,6 +114,7 @@ enumeration of the whole `proposal` noun group.
113
114
  | `akm task doctor` | Evolving | |
114
115
  | `akm task explain` | Evolving | New in 0.9.2; secret-shaped values in provenance output are redacted on a best-effort heuristic basis (not a guarantee). |
115
116
  | `akm task validate` | Evolving | New in 0.9.11; read-only, and the only `task` subcommand that takes a bare filesystem path instead of a ref — the file need not belong to any configured bundle. |
117
+ | `akm task list` | Evolving | New in 0.9.15 (#951); a pure delegating alias for `akm search --type task` — same envelope, no separate implementation. |
116
118
 
117
119
  ## Stable
118
120
 
@@ -218,6 +220,7 @@ enumeration of the whole `proposal` noun group.
218
220
  | `2` | Usage / bad input |
219
221
  | `4` | Health warning (`akm health` only) |
220
222
  | `70` | Internal / unclassified |
223
+ | `75` | Transient — retry shortly (`TransientError`, sysexits `EX_TEMPFAIL`); another akm process holds a lock or is writing `state.db` right now |
221
224
  | `78` | Configuration error |
222
225
 
223
226
  **From 0.9.12**, every success envelope produced by the passthrough stamp