akm-cli 0.10.261016-beta → 0.10.261017-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/CHANGELOG.md +100 -0
  2. package/README.md +8 -10
  3. package/SECURITY.md +7 -99
  4. package/STABILITY.md +57 -98
  5. package/dist/assets/stash-skeleton/facts/conventions/backlinks.md +2 -2
  6. package/dist/assets/stash-skeleton/facts/conventions/organization.md +11 -17
  7. package/dist/cli.js +32 -41
  8. package/dist/commands/feedback-cli.js +5 -4
  9. package/dist/commands/health.js +15 -16
  10. package/dist/commands/proposal/proposal-cli.js +1 -1
  11. package/dist/commands/read/registry-search.js +4 -1
  12. package/dist/commands/read/search.js +6 -6
  13. package/dist/commands/sources/self-update.js +9 -5
  14. package/dist/commands/tasks/tasks.js +12 -13
  15. package/dist/core/adapter/adapters/agent-skills-adapter.js +7 -7
  16. package/dist/core/adapter/adapters/akm-task-adapter.js +1 -1
  17. package/dist/core/adapter/adapters/akm-workflow-adapter.js +1 -1
  18. package/dist/core/adapter/adapters/claude-adapter.js +1 -1
  19. package/dist/core/adapter/adapters/dotenv-adapter.js +1 -1
  20. package/dist/core/adapter/adapters/generic-files-adapter.js +1 -1
  21. package/dist/core/adapter/adapters/llm-wiki-adapter.js +1 -1
  22. package/dist/core/adapter/adapters/opencode-adapter.js +1 -1
  23. package/dist/core/adapter/adapters/website-snapshot-adapter.js +1 -1
  24. package/dist/core/bundle-rename.js +17 -7
  25. package/dist/indexer/bundle-identity-guard.js +6 -6
  26. package/dist/indexer/index-written-assets.js +3 -3
  27. package/dist/indexer/indexer.js +12 -8
  28. package/dist/indexer/materialize-embeddings.js +12 -2
  29. package/dist/indexer/passes/metadata.js +103 -46
  30. package/dist/indexer/search/ranking.js +7 -3
  31. package/dist/indexer/search/search-fields.js +1 -1
  32. package/dist/indexer/usage/usage-events.js +30 -8
  33. package/dist/indexer/walk/walker.js +1 -1
  34. package/dist/output/shapes/helpers.js +3 -3
  35. package/dist/output/text/command-format.js +5 -6
  36. package/dist/scripts/akm-migrate-node.js +31 -3
  37. package/dist/scripts/akm-migrate.js +31 -3
  38. package/dist/sources/providers/git-provider.js +2 -4
  39. package/dist/sources/providers/provider-utils.js +8 -13
  40. package/dist/sources/snapshot-fetchers/website-ingest.js +6 -7
  41. package/dist/storage/repositories/events-repository.js +15 -0
  42. package/dist/storage/repositories/improve-ledger-repository.js +13 -0
  43. package/dist/storage/repositories/index-entries-repository.js +43 -5
  44. package/dist/storage/repositories/outcome-repository.js +12 -0
  45. package/dist/storage/repositories/proposals-repository.js +5 -5
  46. package/dist/storage/repositories/salience-repository.js +13 -0
  47. package/dist/storage/repositories/task-history-repository.js +8 -7
  48. package/dist/storage/repositories/workflow-runs-repository.js +10 -8
  49. package/docs/README.md +24 -60
  50. package/docs/agents/README.md +3 -8
  51. package/docs/architecture/README.md +18 -20
  52. package/docs/architecture/adapters.md +40 -43
  53. package/docs/architecture/architecture.md +171 -151
  54. package/docs/architecture/improvement.md +95 -91
  55. package/docs/architecture/internals/classification.md +101 -119
  56. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +33 -35
  57. package/docs/architecture/internals/health-advisories.md +59 -46
  58. package/docs/architecture/internals/improve-workflow.md +166 -693
  59. package/docs/architecture/internals/indexing.md +65 -465
  60. package/docs/architecture/internals/storage-locations.md +301 -848
  61. package/docs/architecture/persisted-data-compat.md +6 -6
  62. package/docs/architecture/specs/okf-support.md +67 -95
  63. package/docs/architecture/specs/ref.md +67 -125
  64. package/docs/architecture/testing/testing-workflow.md +107 -167
  65. package/docs/architecture/workflow-engine.md +34 -33
  66. package/docs/guides/README.md +5 -5
  67. package/docs/guides/author-bundles.md +72 -98
  68. package/docs/guides/author-workflows.md +11 -18
  69. package/docs/guides/bundles.md +91 -123
  70. package/docs/guides/capture-knowledge.md +10 -9
  71. package/docs/guides/concepts.md +45 -30
  72. package/docs/guides/discover-and-load.md +39 -32
  73. package/docs/guides/getting-started.md +24 -24
  74. package/docs/guides/improve-the-library.md +98 -80
  75. package/docs/guides/recipes/headless-install.md +4 -4
  76. package/docs/guides/recipes/website-source.md +9 -5
  77. package/docs/guides/run-workflows.md +10 -11
  78. package/docs/guides/scheduling.md +11 -17
  79. package/docs/guides/use-with-any-agent.md +15 -14
  80. package/docs/guides/wikis.md +6 -9
  81. package/docs/integration/bundling-akm.md +21 -26
  82. package/docs/maintainers/README.md +2 -2
  83. package/docs/maintainers/local-development.md +3 -16
  84. package/docs/migration/README.md +2 -1
  85. package/docs/migration/release-notes/0.10.0.md +167 -22
  86. package/docs/migration/release-notes/README.md +2 -2
  87. package/docs/migration/v0.9-to-v0.10.md +47 -7
  88. package/docs/product-surface.md +10 -8
  89. package/docs/reference/README.md +26 -15
  90. package/docs/reference/asset-types.md +11 -12
  91. package/docs/reference/bundle-types.md +6 -5
  92. package/docs/reference/cli.md +52 -28
  93. package/docs/reference/configuration.md +46 -60
  94. package/docs/reference/data-and-telemetry.md +204 -421
  95. package/docs/reference/env-and-secrets.md +80 -83
  96. package/docs/reference/memory.md +1 -1
  97. package/docs/reference/refs.md +23 -36
  98. package/docs/reference/registry.md +138 -348
  99. package/docs/reference/supported-formats.md +8 -8
  100. package/docs/reference/tasks.md +13 -19
  101. package/docs/reference/website-sources.md +23 -15
  102. package/docs/reference/workflow-schema.md +28 -47
  103. package/docs/reference/workflows.md +33 -37
  104. package/package.json +1 -1
@@ -2,12 +2,7 @@
2
2
 
3
3
  akm is a Bun-based CLI for discovering and using agent assets from local
4
4
  filesystem sources, cache-backed sources (git, website, npm), and registry
5
- catalogs.
6
-
7
- This document is the operating summary of the current architecture and is the
8
- current-truth reference.
9
-
10
- ---
5
+ catalogs. This page summarizes how it is put together.
11
6
 
12
7
  ## Invariants
13
8
 
@@ -119,12 +114,11 @@ canonical source directory through `src/core/asset/asset-placement.ts`'s
119
114
  `PLACEMENT_SPECS` map. See
120
115
  [Bundle Types](../reference/bundle-types.md) for the full table.
121
116
 
122
- The deprecated `vault` type was removed in 0.9.0 and replaced by `env` (whole
123
- `.env` files) and `secret` (single-value secret files). `wiki` is not an item
124
- type: multi-page wikis are a bundle *format* owned by the `llm-wiki` adapter,
125
- not a per-file type stamped by the classifier (see
117
+ `env` holds whole `.env` files and `secret` holds single-value secret files.
118
+ `wiki` is not an item type: multi-page wikis are a bundle *format* owned by the
119
+ `llm-wiki` adapter, not a per-file type stamped by the classifier (see
126
120
  [Classification](internals/classification.md) and
127
- [Bundle Types](../reference/bundle-types.md) for the other ten formats akm
121
+ [Bundle Types](../reference/bundle-types.md) for the ten other formats akm
128
122
  recognizes besides its own native one).
129
123
 
130
124
  ---
@@ -145,9 +139,9 @@ All four kinds expose the same minimal `SourceProvider` interface
145
139
  ```ts
146
140
  interface SourceProvider {
147
141
  readonly name: string;
148
- readonly kind: string; // "filesystem" | "git" | "website" | "npm"
142
+ readonly kind: SourceKind; // "filesystem" | "git" | "website" | "npm"
149
143
  path(): string; // directory the indexer walks
150
- sync?(options?: { force?: boolean }): Promise<void>; // refresh from upstream (no-op for filesystem)
144
+ sync?(options?: SyncOptions): Promise<void>; // refresh from upstream (no-op for filesystem)
151
145
  }
152
146
  ```
153
147
 
@@ -155,17 +149,14 @@ Providers do **not** implement `search`, `show`, `canShow`, or any read method.
155
149
  The indexer walks `path()`, classifies files, and answers all queries from the
156
150
  local FTS5 index.
157
151
 
158
- File semantics are owned by the selected bundle adapter. OKF is the first-class
159
- least-common-denominator for Markdown path identity, open types, content, links,
160
- and heading fragments. The `akm` format is an OKF-compatible Markdown superset:
161
- its adapter progressively adds native command, script, workflow, task,
162
- environment, secret, memory, and lesson behavior. Non-Markdown formats retain
163
- their native serialization and every adapter retains its own capability,
164
- validation, redaction, and placement rules.
165
-
166
- The legacy `LiveStashProvider` / `SyncableStashProvider` split is gone, as is
167
- any "remote-only" provider tier. API-backed sources (mem0, Notion, etc.) are
168
- deferred to a separate `QuerySource` tier post-v1.
152
+ File semantics are owned by the selected bundle adapter (see
153
+ [Adapters](adapters.md)). OKF is the first-class least-common-denominator for
154
+ Markdown path identity, open types, content, links, and heading fragments. The
155
+ `akm` format is an OKF-compatible Markdown superset: its adapter progressively
156
+ adds native command, script, workflow, task, environment, secret, memory, and
157
+ lesson behavior. Non-Markdown formats retain their native serialization and
158
+ every adapter retains its own capability, validation, redaction, and placement
159
+ rules.
169
160
 
170
161
  ### Cache-backed sources are still indexed locally
171
162
 
@@ -174,6 +165,19 @@ deferred to a separate `QuerySource` tier post-v1.
174
165
  indexing pipeline as filesystem sources. There is no parallel scoring system
175
166
  for "remote" content.
176
167
 
168
+ A cache-backed source is fetched when it is added and when `akm bundle update`
169
+ forces a sync. `akm index` calls `sync()` without `force`, so it fetches a
170
+ source only if it has never been materialized on this host; nothing refreshes
171
+ on a timer. A git or npm install recorded in the lockfile whose files are
172
+ missing is reported with a warning that names `akm bundle update`; index does
173
+ not fetch it. A configured npm bundle with no lock entry has no walkable path,
174
+ so index skips it with a warning that names `akm bundle update` instead of
175
+ calling `sync()`.
176
+
177
+ `akm index` indexes each source through its bundle's adapter. A source that
178
+ could not be walked completely keeps its previously indexed rows and the index
179
+ run warns; a bundle whose adapter id is unknown is skipped with a warning.
180
+
177
181
  ---
178
182
 
179
183
  ## Refs
@@ -185,16 +189,17 @@ User-facing item refs are path-identified:
185
189
  ```
186
190
 
187
191
  - the subdir-qualified `conceptId` (`skills/code-review`) identifies an item
188
- within a bundle; `type` is no longer a separate ref segment
192
+ within a bundle; `type` is not a ref segment
189
193
  - the fully qualified `bundle//conceptId` spelling is canonical in index rows
190
194
  and durable state; index-backed local read output keeps the short form for the
191
195
  default bundle and qualifies every non-default bundle
192
196
  - optional `bundle//` narrows input lookup to a configured bundle
193
197
  - refs are parsed by `parseBundleRef` in `src/core/asset/asset-ref.ts`
194
198
  - markdown-backed items strip `.md` from canonical names
195
- - `#fragment` is input-only and never stored: on markdown-document items the
196
- core resolves it as a section selector (`knowledge/api-guide#authentication`);
197
- elsewhere it is an adapter-owned selector opaque to the core
199
+ - `#fragment` is input-only and never stored, and `akm show` is the only command
200
+ that accepts one: on markdown-document items the core resolves it as a
201
+ section selector (`knowledge/api-guide#authentication`); on any other item
202
+ `akm show` ignores the fragment, warns, and shows the whole item
198
203
  - refs embedded in prose must be fully qualified (`bundle//conceptId`) or a
199
204
  native adapter link form — a bare conceptId in prose is ordinary text, and no
200
205
  tool rewrites it
@@ -215,11 +220,10 @@ Examples:
215
220
  - `workflows/release/train`
216
221
  - `team//commands/deploy`
217
222
 
218
- URI schemes (`viking://...`, `github://...`) are **not** asset refs. Install
219
- locators like `github:owner/repo`, `git+https://...`, `npm:@scope/pkg`,
220
- `skills.sh:slug`, and `./local/path` are a separate grammar parsed by
221
- `parseRegistryRef` in `src/registry/resolve.ts` and consumed by
222
- `akm add` / `akm clone`.
223
+ URI schemes are **not** asset refs. Install locators like `github:owner/repo`,
224
+ `git+https://...`, `npm:@scope/pkg`, and `./local/path` are a
225
+ separate grammar parsed by `parseRegistryRef` in `src/registry/resolve.ts` and
226
+ consumed by `akm bundle add` and `akm clone`.
223
227
 
224
228
  ---
225
229
 
@@ -235,12 +239,17 @@ There is **one** ranking for all indexed content: reciprocal rank fusion
235
239
  through the model's embedding profile (`src/llm/embedders/profile.ts`)
236
240
 
237
241
  Filters (type, source, scope, belief, proposed quality, default-excluded
238
- types) and one-hit-per-file deduplication narrow the fused list without
239
- reordering it, and a hit's `score` is its fused score. Of entries with
240
- identical indexed content only the highest-ranked is kept. Nothing else — name,
241
- type, tag, graph, usage or belief-state signals — changes the order. The
242
- design and its measured alternatives are in the retrieval evaluation
243
- (`akm/eval/retrieval/reports/baseline-2026-09-27.md`).
242
+ types) drop ineligible entries from each list before its 100 candidates are
243
+ taken. The fused score is then multiplied by
244
+ `1 + 0.03 × (ln(1 + positives) − ln(1 + negatives))` over the user's recorded
245
+ feedback for the asset (`applyFeedback` in `src/indexer/search/ranking.ts`;
246
+ [`akm feedback`](../reference/cli.md#feedback) says which feedback counts, and
247
+ an asset with none keeps its fused score), and the list is re-sorted, equal
248
+ scores by fully qualified ref (`<bundle>//<conceptId>`). A hit's `score` is
249
+ that adjusted score. One-hit-per-file deduplication, and keeping only
250
+ the highest-ranked of entries with identical indexed content, then drop entries
251
+ from the sorted list without reordering it. Nothing else — name, type, tag,
252
+ graph, utility score or belief-state signals — changes the order.
244
253
 
245
254
  Notes:
246
255
 
@@ -250,11 +259,13 @@ Notes:
250
259
  - the query embedding is requested before the FTS query runs and is bounded
251
260
  by `embedding.queryTimeoutMs` (default 3000); on timeout or failure the
252
261
  search is served by the lexical list alone
253
- - Markdown fragments are not searched; `akm show` reads the stored safe
254
- Markdown (`entry_fragments`) for `#fragment` refs
255
- - `hints` includes `searchHints`, `examples`, `usage`, intent fields, wiki
256
- cross-references, and page-kind hints
257
- - `content` is bounded low-weight body prose plus TOC headings and parameter metadata; secret/env/session material is excluded at the adapter boundary
262
+ - Markdown fragments are not searched; they are a `show` feature (see
263
+ [Show Resolution](#show-resolution))
264
+ - `hints` carries search hints, examples, usage, intent fields, cross-references,
265
+ page kind, and when-to-use text
266
+ - `content` is bounded body text plus TOC headings and parameter metadata; `env`
267
+ and `secret` entries index key names and file names only, and session bodies
268
+ are not indexed
258
269
  - registry results live in `registryHits`, never in `hits`
259
270
  - `--from all` keeps registry results in `registryHits` — they are not
260
271
  rank-merged with source hits
@@ -265,12 +276,12 @@ Notes:
265
276
  - local search and curate expose the actual execution in `searchMode`:
266
277
  `semantic`, intentional/unavailable `keyword`, or `fts-fallback` when a
267
278
  ready semantic runtime failed during the query; that degradation also
268
- surfaces as one sanitized `warnings[]` entry, including in agent shape
269
- - usage-event writes are best-effort; `EROFS`/`EACCES` from a read-only
270
- environment are silent and never affect `searchMode`
279
+ surfaces as one `warnings[]` entry, including in agent shape
280
+ - usage-event writes are best-effort; a read-only filesystem drops them
281
+ silently and does not change `searchMode`
271
282
 
272
283
  `akm search` is implemented in `src/commands/read/search.ts` and queries the
273
- indexer's local search (`src/indexer/search/db-search.ts`). Provider fan-out is gone.
284
+ indexer's local search (`src/indexer/search/db-search.ts`).
274
285
 
275
286
  ---
276
287
 
@@ -283,23 +294,21 @@ Local show flow (`src/commands/read/show.ts`):
283
294
  1. parse `[bundle//]conceptId`
284
295
  2. `lookup(ref)` by indexed `item_ref` and materialized source root
285
296
  3. return the indexed canonical ref and read `file_path` from disk
286
- 4. report not-found when no index row resolves; physical ownership can prevent
287
- a lower-priority source from retargeting a ref, but never authorizes a
288
- direct file read
297
+ 4. report not-found when no index row resolves
289
298
  5. apply generic Markdown content/fragment presentation for OKF, or the owning
290
299
  adapter's progressively enhanced native presentation
291
300
 
292
- An opaque `#akm-fragment-...` selector resolves against the safe Markdown
293
- revision persisted in `entry_fragments`, so search and show remain consistent
294
- if the materialized file changes before it is reindexed. Bare fragment show is
295
- still exact. The opt-in `--context lead` projection assembles the first and
296
- selected fragments from that same indexed revision under a hard character
297
- budget, labels the selected match, and keeps it last. It never reconstructs
298
- neighbors from the current raw file. Fragment search plus opaque/contextual show
299
- JSON exposes the selected and parent refs, one-based ordinal/count and
300
- source-line bounds, neighbor refs, and separate fragment/parent size estimates;
301
- canonical `ref` remains the parent on show. A default exact friendly-heading
302
- show stays source-live and omits indexed provenance.
301
+ A friendly heading fragment (`#authentication`) selects a section of the live
302
+ file. An opaque `#akm-fragment-...` selector resolves against the safe Markdown
303
+ revision persisted in `entry_fragments`, so it stays consistent if the
304
+ materialized file changes before it is reindexed. The opt-in `--context lead`
305
+ projection assembles the first and selected fragments from that same indexed
306
+ revision under a character budget (`--max-chars` or `--max-tokens`), labels the
307
+ selected match, and keeps it last. Opaque and contextual show JSON exposes the
308
+ selected and parent refs, one-based ordinal and count, source-line bounds,
309
+ neighbor refs, and separate fragment and parent size estimates; the canonical
310
+ `ref` remains the parent. An exact friendly-heading show stays source-live and
311
+ omits indexed provenance.
303
312
 
304
313
  There is **no provider or local-disk fallback** for asset refs. The file is
305
314
  read only after an indexed row resolves it.
@@ -308,26 +317,25 @@ read only after an indexed row resolves it.
308
317
 
309
318
  Agent-shaped search, show, and curate results for materialized local assets
310
319
  include the index-backed `ref`, absolute `path`, and `editable`. `editHint` is
311
- included only when `editable` is `false`; it is secondary guidance and never
312
- replaces the normal show, run, or use action (or curate follow-up). Registry-only
320
+ included only when `editable` is `false`; it is secondary guidance and does not
321
+ replace the normal show, run, or use action (or curate follow-up). Registry-only
313
322
  results have no local path or editability fields.
314
323
 
315
- `editable` means current AKM source policy authorizes direct in-place
316
- modification of that exact file. It is computed when the response is built from
317
- the current resolved source roots and effective `writable` policy, is not stored
318
- in the index, and fails closed when no configured source owns the path.
324
+ `editable` means the asset's file belongs to a configured source whose
325
+ effective `writable` policy is true. It is computed when the response is built
326
+ from the current resolved source roots, is not stored in the index, and is
327
+ `false` when no configured source owns the path.
319
328
 
320
329
  ---
321
330
 
322
331
  ## Writing to Sources
323
332
 
324
- Writes go through one helper: `src/core/write-source.ts`. This is the only
325
- place in the codebase that branches on `source.kind`. Review holds that line
326
- (core principle 2); the lint script that once checked it was deleted.
327
-
328
- Command layers therefore never hold provider knowledge. Where a step is
329
- meaningful only for a publication-backed target, `write-source.ts` exposes a
330
- kind-neutral wrapper that absorbs the guard and no-ops otherwise:
333
+ Writes go through one helper: `src/core/write-source.ts`. It is the only
334
+ place that should branch on `source.kind`; command layers do not hold provider
335
+ knowledge. That is a convention that code review holds (core principle 2), not
336
+ a lint rule. Where a step is meaningful only for a publication-backed target,
337
+ `write-source.ts` exposes a kind-neutral wrapper that does the work for a git
338
+ target and does nothing otherwise:
331
339
  `prepareWriteTargetForMutation`, `commitWriteTargetBoundary`, and
332
340
  `withWriteTargetMutation` (write the file, `git add` exactly the recorded
333
341
  paths, commit, push with `--force-with-lease` when configured). Recording a
@@ -341,9 +349,11 @@ deleteAssetFromSource(source, config, ref)
341
349
 
342
350
  The flow:
343
351
 
344
- 1. Refuse if the source is not `writable`.
345
- 2. Plain filesystem write to `path.join(source.path, …)` — for **every** kind,
346
- with no commit (0.9.0, issue #507).
352
+ 1. Refuse if the source is not `writable`, is not a `filesystem` or `git`
353
+ source, or belongs to a bundle whose adapter is not on the write allowlist
354
+ (see [Adapters](adapters.md#write-allowlists)).
355
+ 2. Atomic filesystem write to `path.join(source.path, …)` with no commit. A git
356
+ target records the path for the boundary commit.
347
357
 
348
358
  Git-backed targets are committed in a single batch at the operation boundary via
349
359
  `commitWriteTargetBoundary(target, message, { push })`, which delegates to
@@ -351,10 +361,9 @@ Git-backed targets are committed in a single batch at the operation boundary via
351
361
  boundary, which stages and commits only those files (so unrelated staged work,
352
362
  including work under the same asset directory, is not included). Improve
353
363
  auto-sync similarly subtracts the Git dirty-path baseline captured at invocation
354
- start. Push remains gated on `writable && hasRemote && push !== false`. The old
355
- per-asset commit/push path (`options.pushOnCommit`) is **hard-rejected** at
356
- config load — not merely deprecated — by a `superRefine` on the bundle/source
357
- schema (`src/core/config/schema/sources-bundles.ts`).
364
+ start. Push happens when the target is writable, has an upstream, and `push` is
365
+ not `false`. `options.pushOnCommit` is not supported: config load reports it
366
+ (`src/core/config/schema/sources-bundles.ts`).
358
367
 
359
368
  `writable` is a config flag, not an interface concern. Defaults: `true` for
360
369
  `filesystem`, `false` for everything else. `writable: true` on `website` or
@@ -363,11 +372,10 @@ refresh.
363
372
 
364
373
  Write-target resolution (`resolveWriteTarget`) follows: an explicit
365
374
  destination flag (`--bundle`) -> `config.defaultWriteTarget` -> working
366
- bundle (`defaultBundle`) -> `ConfigError`. The resolved target keeps the
367
- optional configured selector separate from the stable `source.name`: APIs
368
- that must re-resolve a destination use the selector, while durable refs and
369
- state rows always use `source.name`. The implicit working bundle therefore has
370
- no selector but has durable identity `stash`.
375
+ bundle (`defaultBundle`) -> `ConfigError`. There is no fallback to the first
376
+ writable source. The resolved target keeps the optional configured selector
377
+ separate from the stable `source.name`: APIs that must re-resolve a destination
378
+ use the selector, while durable refs and state rows always use `source.name`.
371
379
 
372
380
  On mutation surfaces that accept asset refs, a qualified ref implies its
373
381
  bundle as the write target. A matching explicit destination flag is allowed;
@@ -382,16 +390,16 @@ combined with `--bundle`.
382
390
 
383
391
  ### Improve durable-state identity
384
392
 
385
- Improve readers and writers use one current key for each asset. A resolved
386
- indexed entry uses its fully qualified `item_ref`; a direct or provenance-free
387
- ref uses its current concept ID. There is no alternate root-based key, bare alias
388
- merge, or second state lookup. This prevents a duplicate concept in another
389
- installation from inheriting unrelated feedback, proposal-cursor, salience, or
390
- convergence state.
393
+ Improve readers and writers use one current key for each asset (`stateKey`,
394
+ `src/commands/improve/ledger.ts`). A resolved indexed entry uses its fully
395
+ qualified `item_ref`; a direct or provenance-free ref uses its current concept
396
+ ID. There is no alternate root-based key, bare alias merge, or second state
397
+ lookup, so a duplicate concept in another installation does not inherit
398
+ unrelated feedback, proposal-cursor, salience, or convergence state.
391
399
 
392
400
  Retrieval demand is scoped separately through usage-event entry IDs and selected
393
- source roots, with qualified refs covering detached events. Improve never merges
394
- retrieval counts or last-use timestamps solely by bare ref across sources.
401
+ source roots, with qualified refs covering detached events. Improve does not
402
+ merge retrieval counts or last-use timestamps by bare ref across sources.
395
403
 
396
404
  ### Proposal queues and destinations
397
405
 
@@ -399,10 +407,9 @@ retrieval counts or last-use timestamps solely by bare ref across sources.
399
407
  whose proposal queue is read or adjudicated; it does not override the proposal's
400
408
  write destination. Qualified proposals and unqualified proposals created in a
401
409
  configured secondary queue record the destination source name and materialized
402
- root. Diff, accept, and revert use that binding by default and reject an explicit
403
- `--bundle` that resolves elsewhere. An unbound short proposal requires either
404
- an explicit `--bundle` or an authenticated `--queue` context; it does not
405
- inherit a default write target.
410
+ root. Diff, accept, and revert use that binding by default and refuse an
411
+ explicit `--bundle` that resolves elsewhere. An unbound short proposal needs
412
+ either `--bundle` or `--queue`; it does not inherit a default write target.
406
413
 
407
414
  ---
408
415
 
@@ -419,19 +426,17 @@ interface RegistryProvider {
419
426
  ```
420
427
 
421
428
  The contract is a single `search()` method — the orchestrator's only entry
422
- point. Implementations must never throw; errors are returned as
423
- `warnings[]` on the result.
429
+ point. Implementations return errors as `warnings[]` on the result instead of
430
+ throwing.
424
431
 
425
432
  Built-in registries:
426
433
 
427
434
  | Kind | Role |
428
435
  | --- | --- |
429
- | `static-index` | Reads the v2 or v3 JSON index schema, same `stashes[]` wire format (official akm registry, team mirrors). `scripts/build-registry-index.ts` emits v3; the live official registry currently publishes v2. |
436
+ | `static-index` | Reads the v2 or v3 JSON index schema, same `stashes[]` wire format (official akm registry, team mirrors). `scripts/build-registry-index.ts` emits v3. |
430
437
  | `skills-sh` | Wraps the skills.sh REST API. |
431
438
 
432
- Context Hub is **not** a registry provider type. It is just a recommended git
433
- kit installable through the official static-index registry like any other
434
- source.
439
+ These two are the only registry provider types, and Context Hub is **not** a registry provider type.
435
440
 
436
441
  ---
437
442
 
@@ -441,7 +446,9 @@ Workflow definitions live in `workflows/`, but workflow run state is separate
441
446
  durable runtime state. `workflow_runs`, `workflow_run_steps`,
442
447
  `workflow_run_units`, and `workflow_run_unit_attempts` live in `state.db`
443
448
  alongside events, tasks, and proposals. `index.db` remains rebuildable search
444
- state, while `logs.db` stores task/run log lines.
449
+ state, while `logs.db` stores task/run log lines. See
450
+ [The Workflow Engine](workflow-engine.md) for how a run is frozen, driven, and
451
+ resumed.
445
452
 
446
453
  - workflow discovery and search use the shared asset index
447
454
  - workflow run records survive index rebuilds
@@ -462,28 +469,31 @@ improve's salience work; search ranking does not read it.
462
469
 
463
470
  ## Errors
464
471
 
465
- `ConfigError`, `UsageError`, and `NotFoundError` (in `src/core/errors.ts`) each
466
- carry a stable `code` and a `hint(): string | undefined` method. The CLI
467
- surfaces hints by calling `error.hint()` directly — there is no regex chain
468
- parsing error messages.
469
-
470
- All three extend a shared abstract base `AkmError` carrying a `kind`
471
- discriminant (`"config" | "usage" | "not-found"`). The CLI exit-code classifier
472
- (`classifyExitCode` in `src/cli/shared.ts`) switches exhaustively on `kind`
473
- (`never`-checked via `assertNever`), so adding a new error class is a
474
- compile-time error until its exit code is mapped. Any thrown value that is
475
- **not** an `AkmError` is treated as an unexpected internal failure and maps to
476
- the distinct INTERNAL exit code **70** (sysexits `EX_SOFTWARE`) — this lets
477
- scripts tell "akm threw unexpectedly" apart from an ordinary `NotFoundError`
478
- (exit 1).
472
+ `ConfigError`, `UsageError`, `NotFoundError`, and `TransientError` (in
473
+ `src/core/errors.ts`) each carry a stable `code` and a `hint(): string |
474
+ undefined` method. The CLI surfaces hints by calling `error.hint()` directly —
475
+ there is no regex chain parsing error messages.
476
+
477
+ All four extend a shared abstract base `AkmError` carrying a `kind`
478
+ discriminant (`"config" | "usage" | "not-found" | "transient"`). The CLI
479
+ exit-code classifier (`classifyExitCode` in `src/cli/shared.ts`) switches
480
+ exhaustively on `kind` (`never`-checked via `assertNever`), so adding a new
481
+ error class is a compile-time error until its exit code is mapped. Any thrown
482
+ value that is **not** an `AkmError` is treated as an unexpected internal
483
+ failure and maps to the distinct INTERNAL exit code **70** (sysexits
484
+ `EX_SOFTWARE`) — this lets scripts tell "akm threw unexpectedly" apart from an
485
+ ordinary `NotFoundError` (exit 1).
479
486
 
480
487
  | Class / case | Exit code |
481
488
  | --- | --- |
482
489
  | `ConfigError` (`kind: "config"`) | 78 |
483
490
  | `UsageError` (`kind: "usage"`) | 2 |
484
491
  | `NotFoundError` (`kind: "not-found"`) | 1 |
492
+ | `TransientError` (`kind: "transient"`) — another akm process holds a lock; retry shortly | 75 |
485
493
  | unclassified / non-`AkmError` (INTERNAL) | 70 |
486
494
 
495
+ `akm health` exits 4 when it reports a warning (`EXIT_CODES.HEALTH_WARN`).
496
+
487
497
  ---
488
498
 
489
499
  ## Connections and Harnesses
@@ -539,6 +549,11 @@ explicit `null`, zero, and empty values stay distinct. The result's
539
549
  `provenance` names the layer behind each field (`akm task explain`,
540
550
  `command run --dry-run`).
541
551
 
552
+ An agent asset is a persona (a system prompt), not executable work. An
553
+ `agents/<name>` selector (optionally `bundle//`-qualified) is resolved by akm
554
+ into a persona; any other agent string is a native harness selector that
555
+ replaces the persona.
556
+
542
557
  `buildExecution` hands the request to the selected harness's own builder
543
558
  (registered on `HARNESS_REGISTRY`), to the runtime's lowerer, or builds chat
544
559
  messages for a direct request. A harness builder is a thin shell: it names the
@@ -582,32 +597,37 @@ which includes every earlier plan with a command step, is marked abandoned and
582
597
  refused, naming the upgrade. AKM does not support full GitHub Actions semantics
583
598
  or arbitrary remote action execution.
584
599
 
600
+ Tasks are `tasks/<name>.yml` documents in task source v4 (`parseTaskSource`).
601
+ Version 2 and 3 files are not read at runtime: they are refused with a message
602
+ naming `akm migrate apply`, which converts them. Historical task-run metadata
603
+ remains readable.
604
+
585
605
  ### In-tree LLM helpers (`src/llm/`)
586
606
 
587
607
  Every helper under `src/llm/` is a **bounded, single-shot, stateless** call.
588
608
  Concretely:
589
609
 
590
610
  - Each public export is either a stateless function (`chatCompletion` performs
591
- one HTTP request and keeps nothing between calls; `splitMemoryIntoAtomicFacts`,
592
- `resolveIndexPassExecution`, `parseJsonResponse`, …) or a factory that returns
593
- a one-shot client tied to the symbolic runner/config the caller passes in.
611
+ one HTTP request and keeps nothing between calls; `resolveIndexPassExecution`,
612
+ `parseJsonResponse` in `src/core/parse.ts`, …) or a factory that returns a
613
+ one-shot client tied to the symbolic runner/config the caller passes in.
594
614
  - No module under `src/llm/` keeps session, conversation, or response state at
595
- module scope. The only module-level singleton is the local embedder
596
- pipeline in `src/llm/embedder.ts`, which is an expensive-to-build but
597
- stateless model handle (see the comment in that file). It exposes
598
- `resetLocalEmbedder()` so tests can construct a fresh pipeline.
599
- - `callStructured()` is the common bounded structured-LLM seam: it adapts an
600
- already-resolved symbolic runner into an inline resolved request, lowers it,
601
- and dispatches it through `runExecution`, which materializes the connection's
602
- key at dispatch like every other dispatch and re-runs no alias resolution.
603
- Index callers use `resolveIndexPassExecution()` to freeze the typed
615
+ module scope. The module-level state is the local embedder pipeline and the
616
+ embedding cache in `src/llm/embedder.ts` — an expensive-to-build but
617
+ stateless model handle and an LRU of vectors. `resetLocalEmbedder()` lets
618
+ tests construct a fresh pipeline.
619
+ - `callStructured()` (`src/llm/structured-call.ts`) is the common bounded
620
+ structured-LLM seam: it adapts an already-resolved symbolic runner into an
621
+ inline resolved request, lowers it, and dispatches it through `runExecution`,
622
+ which materializes the connection's key at dispatch like every other dispatch
623
+ and re-runs no alias resolution. Index callers use
624
+ `resolveIndexPassExecution()` (`src/llm/index-passes.ts`) to freeze the typed
604
625
  `{ runner, notices }` selection once per invocation; there is no separate
605
- readiness projection. Which improve processes run is chosen
606
- by `improve.strategies`, and where each runs by `improve.processes`; see
626
+ readiness projection. Which improve processes run is chosen by
627
+ `improve.strategies`, and where each runs by `improve.processes`; see
607
628
  `docs/reference/configuration.md` for canonical config paths.
608
629
 
609
- Review holds the seam; the test that once inspected the module shape of each
610
- `src/llm/*` entry was deleted.
630
+ Review holds the seam.
611
631
 
612
632
  ### External agents (`src/integrations/agent/`, `src/integrations/harnesses/`)
613
633
 
@@ -628,8 +648,7 @@ Agent programs are reachable by two execution paths, both behind
628
648
  ([How akm runs a harness](../reference/configuration.md#how-akm-runs-a-harness)).
629
649
  - The `AgentRunResult` envelope carries `{ ok, exitCode, stdout, stderr,
630
650
  durationMs, reason?, error?, ... }` where `reason` is a typed code such as
631
- `"timeout"`, `"spawn_failed"`, `"non_zero_exit"` or `"parse_error"`. Callers
632
- never see raw process errors.
651
+ `"timeout"`, `"spawn_failed"`, `"non_zero_exit"` or `"parse_error"`.
633
652
 
634
653
  **akm's OpenCode runtime** (`src/integrations/harnesses/opencode/runtime/`):
635
654
 
@@ -687,17 +706,16 @@ Agent programs are reachable by two execution paths, both behind
687
706
  (no binary and no registry to fetch it from) fails as `spawn_failed`, naming
688
707
  the package, the URL and the cache path.
689
708
 
690
- New tasks are task source v4 `.yml` assets. Normal execution rejects v2 and v3
691
- and directs operators to the explicit preview/apply migrator. Task source v4
692
- command, workflow, script, and shell targets use the common resolved/lowered
693
- execution boundary. Historical task-run metadata remains readable.
709
+ ---
710
+
711
+ ## Locks
694
712
 
695
713
  Long-lived mutable operations each take their own lock file, one `O_EXCL`
696
- create (`src/core/file-lock.ts`): index writers, improve and extract process
697
- locks, lockfile writers, the scheduler lock, and each workflow run's lock. A
698
- lock whose holder process is gone is reclaimed. There is no shared barrier or
699
- activity registry around them; `state.db` writers serialize on SQLite's own
700
- `BEGIN IMMEDIATE`.
714
+ create (`src/core/file-lock.ts`, `src/core/run-lock.ts`): index writers, improve
715
+ and extract process locks, lockfile writers, the scheduler lock, and each
716
+ workflow run's lock. A lock whose holder process is gone is reclaimed; nothing
717
+ expires by age. There is no shared barrier or activity registry around them;
718
+ `state.db` writers serialize on SQLite's own `BEGIN IMMEDIATE`.
701
719
 
702
720
  ---
703
721
 
@@ -709,12 +727,13 @@ activity registry around them; `state.db` writers serialize on SQLite's own
709
727
  | `src/cli/` | citty composition helpers |
710
728
  | `src/core/asset/asset-placement.ts` | asset type registry and canonical source directories (`PLACEMENT_SPECS`) |
711
729
  | `src/core/asset/asset-ref.ts` | asset ref parsing and normalization (`parseBundleRef`) |
730
+ | `src/core/adapter/` | the `BundleAdapter` contract and the built-in adapters (see [Adapters](adapters.md)) |
712
731
  | `src/core/config/config.ts` | config loading, validation, env resolution; the default connection and harness |
713
732
  | `src/core/config/credentials.ts` | `credentials.json` and the dispatch-time resolution of a connection's key (`$VAR`, else the stored entry) |
714
733
  | `src/core/config/convert-engines.ts` | the one conversion of the older `engines` / `defaults` form to `connections` / `harnesses`, called by the loader and by `akm migrate apply` |
715
734
  | `src/core/errors.ts` | error classes with stable codes and hints |
716
735
  | `src/core/parse.ts` | shared JSON parsing: think/fence stripping, balanced-brace extraction |
717
- | `src/core/concurrent.ts` | bounded concurrency pool (`concurrentMap`, default 1 worker) |
736
+ | `src/core/concurrent.ts` | bounded concurrency pool (`concurrentMap`, default 1 worker) and keyed serialization |
718
737
  | `src/core/write-source.ts` | the single write helper (branches on `source.kind`) |
719
738
  | `src/execution/resolved-request.ts` | the versioned resolved execution request and its canonical wire form (tolerant decode) |
720
739
  | `src/integrations/agent/resolution.ts` | `resolveTarget`: the connection or harness a call runs on, and its model, timeout and request fields |
@@ -726,10 +745,11 @@ activity registry around them; `state.db` writers serialize on SQLite's own
726
745
  | `src/sources/provider.ts` | minimal `SourceProvider` interface |
727
746
  | `src/sources/providers/` | filesystem / git / website / npm implementations |
728
747
  | `src/sources/resolve.ts` | filesystem path resolution for refs |
748
+ | `src/storage/repositories/` | SQL for `index.db`, `state.db`, and `logs.db` (entries, FTS, proposals, task history, workflow runs, …) |
729
749
  | `src/indexer/indexer.ts` | walking, metadata generation, index rebuilds, embeddings, utility recompute |
730
750
  | `src/indexer/walk/` | walker, matchers, path/file/index context — the walk phase |
731
751
  | `src/indexer/db/` | `llm-cache` — the persistence phase (entries/embeddings persistence lives in `src/storage/repositories/`) |
732
- | `src/indexer/search/` | `db-search`, ranking, search-fields, search-source, enrichers — the search phase |
752
+ | `src/indexer/search/` | `db-search`, `fts-query`, ranking, search-fields, search-source, enrichers — the search phase |
733
753
  | `src/indexer/passes/` | memory-inference, dir-staleness, metadata — LLM/metadata passes |
734
754
  | `src/indexer/usage/` | usage-events |
735
755
  | `src/commands/read/search.ts` | `akm search` orchestration |
@@ -741,10 +761,10 @@ activity registry around them; `state.db` writers serialize on SQLite's own
741
761
  | `src/commands/tasks/` | scheduled-task command surface |
742
762
  | `src/commands/agent/` | contribute/agent command surface |
743
763
  | `src/registry/providers/` | registry provider implementations (static-index, skills-sh) |
744
- | `src/output/shapes/`, `src/output/text/` | JSON-envelope and text-output registries per command (#490) — a parallel concern to `src/output/renderers.ts`'s per-asset-type `show` renderers, which is still live and self-registering, not replaced |
764
+ | `src/output/shapes/`, `src/output/text/` | JSON-envelope and text-output registries per command — a parallel concern to `src/output/renderers.ts`'s per-asset-type `show` renderers, which register themselves when the module loads |
745
765
  | `src/workflows/authoring/` | workflow authoring |
746
766
  | `src/workflows/runtime/runs.ts` | workflow run persistence (raw SQL lives in `src/storage/repositories/workflow-runs-repository.ts`) |
747
- | `src/workflows/runtime/` | run lifecycle: runs, checkin, agent-identity |
767
+ | `src/workflows/runtime/` | run lifecycle: runs, run-plan decoding, run outputs, agent-identity |
748
768
  | `src/llm/client.ts` | OpenAI-compatible chat completions client (stateless, single request/response) |
749
769
  | `src/llm/index-passes.ts` | per-pass connection or harness resolution for `akm index` (`index.*`) |
750
770
  | `src/llm/memory-infer.ts` | atomic-fact split helper (runs where `improve.processes.memoryInference` or `index.memory` says) |