akm-cli 0.9.30 → 0.9.32

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 (165) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +10 -10
  3. package/ROADMAP.md +112 -0
  4. package/dist/commands/feedback-cli.js +21 -13
  5. package/dist/commands/improve/improve-cli.js +33 -12
  6. package/dist/core/errors.js +1 -0
  7. package/dist/core/file-lock.js +10 -1
  8. package/dist/indexer/usage/usage-events.js +6 -2
  9. package/dist/llm/client.js +33 -3
  10. package/dist/scripts/akm-migrate-node.js +6 -1
  11. package/dist/scripts/akm-migrate.js +6 -1
  12. package/docs/README.md +42 -41
  13. package/docs/agents/README.md +15 -0
  14. package/docs/agents/agent-install.md +14 -0
  15. package/docs/architecture/README.md +67 -0
  16. package/docs/architecture/adapters.md +272 -0
  17. package/docs/architecture/akm-architecture-decision-history.md +2055 -0
  18. package/docs/architecture/akm-core-principles.md +126 -0
  19. package/docs/architecture/architecture.md +579 -0
  20. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
  21. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
  22. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
  23. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
  24. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
  25. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
  26. package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
  27. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
  28. package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
  29. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
  30. package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
  31. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
  32. package/docs/architecture/decisions/README.md +62 -0
  33. package/docs/architecture/improvement.md +440 -0
  34. package/docs/architecture/internals/classification.md +154 -0
  35. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
  36. package/docs/architecture/internals/functional-contract-patterns.md +340 -0
  37. package/docs/architecture/internals/health-advisories.md +110 -0
  38. package/docs/architecture/internals/improve-workflow.md +713 -0
  39. package/docs/architecture/internals/indexing.md +498 -0
  40. package/docs/architecture/internals/registry-network-boundary.md +57 -0
  41. package/docs/architecture/internals/search.md +249 -0
  42. package/docs/architecture/internals/storage-locations.md +943 -0
  43. package/docs/architecture/persisted-data-compat.md +59 -0
  44. package/docs/architecture/reviews/env-secret-access.md +319 -0
  45. package/docs/architecture/runtime-boundary-design.md +153 -0
  46. package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
  47. package/docs/architecture/specs/0.9.0-decisions.md +528 -0
  48. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
  49. package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
  50. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
  51. package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
  52. package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
  53. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
  54. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
  55. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
  56. package/docs/architecture/specs/di-seams-plan.md +539 -0
  57. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
  58. package/docs/architecture/specs/fact-asset-type.md +142 -0
  59. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
  60. package/docs/architecture/specs/okf-support.md +148 -0
  61. package/docs/architecture/specs/ref.md +329 -0
  62. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
  63. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
  64. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
  65. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
  66. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
  67. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
  68. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
  69. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
  70. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
  71. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
  72. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
  73. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
  74. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
  75. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
  76. package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
  77. package/docs/architecture/specs/stash-organization-conventions.md +377 -0
  78. package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
  79. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
  80. package/docs/architecture/specs/workflow-format-unification.md +437 -0
  81. package/docs/architecture/testing/manual-testing-checklist.md +3420 -0
  82. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
  83. package/docs/architecture/testing/task-workflow-manual-tests.md +1817 -0
  84. package/docs/architecture/testing/testing-workflow.md +446 -0
  85. package/docs/architecture/workflow-engine.md +274 -0
  86. package/docs/guides/README.md +33 -0
  87. package/docs/guides/author-bundles.md +310 -0
  88. package/docs/guides/author-workflows.md +520 -0
  89. package/docs/guides/bundles.md +321 -0
  90. package/docs/guides/capture-knowledge.md +136 -0
  91. package/docs/guides/claude-code-vs-akm-workflows.md +30 -0
  92. package/docs/guides/concepts.md +93 -0
  93. package/docs/guides/discover-and-load.md +158 -0
  94. package/docs/guides/getting-started.md +240 -0
  95. package/docs/guides/improve-the-library.md +246 -0
  96. package/docs/guides/knowledge-management.md +18 -0
  97. package/docs/guides/recipes/headless-install.md +97 -0
  98. package/docs/guides/recipes/website-source.md +28 -0
  99. package/docs/guides/run-workflows.md +279 -0
  100. package/docs/guides/scheduling.md +123 -0
  101. package/docs/guides/use-with-any-agent.md +129 -0
  102. package/docs/guides/wikis.md +81 -0
  103. package/docs/maintainers/README.md +19 -0
  104. package/docs/maintainers/curate-workmap.md +336 -0
  105. package/docs/maintainers/eval.md +3 -0
  106. package/docs/maintainers/local-development.md +108 -0
  107. package/docs/maintainers/release-checklist.md +103 -0
  108. package/docs/migration/README.md +2 -2
  109. package/docs/migration/release-notes/0.0.13.md +1 -1
  110. package/docs/migration/release-notes/0.1.0.md +1 -1
  111. package/docs/migration/release-notes/0.2.0.md +1 -1
  112. package/docs/migration/release-notes/0.3.0.md +1 -1
  113. package/docs/migration/release-notes/0.5.0.md +1 -1
  114. package/docs/migration/release-notes/0.6.0.md +1 -1
  115. package/docs/migration/release-notes/0.7.0.md +1 -1
  116. package/docs/migration/release-notes/0.7.3.md +2 -2
  117. package/docs/migration/release-notes/0.7.4.md +1 -1
  118. package/docs/migration/release-notes/0.7.5.md +1 -1
  119. package/docs/migration/release-notes/0.8.0.md +1 -1
  120. package/docs/migration/release-notes/0.9.0.md +1 -1
  121. package/docs/migration/release-notes/0.9.14.md +1 -1
  122. package/docs/migration/release-notes/0.9.15.md +1 -1
  123. package/docs/migration/release-notes/0.9.16.md +1 -1
  124. package/docs/migration/release-notes/0.9.17.md +1 -1
  125. package/docs/migration/release-notes/0.9.19.md +1 -1
  126. package/docs/migration/release-notes/0.9.2.md +1 -1
  127. package/docs/migration/release-notes/README.md +27 -31
  128. package/docs/migration/v0.5-to-v0.6.md +759 -0
  129. package/docs/migration/v0.8-to-v0.9.md +1 -1
  130. package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
  131. package/docs/posts/README.md +30 -0
  132. package/docs/posts/akm-command-tour.md +495 -0
  133. package/docs/posts/akm-logo-sized.webp +0 -0
  134. package/docs/posts/belief-aware-memory-12.md +139 -0
  135. package/docs/posts/config-v2-lessons-16.md +191 -0
  136. package/docs/posts/improve-loop-autonomous-10.md +225 -0
  137. package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
  138. package/docs/posts/intro-part-01.md +76 -0
  139. package/docs/posts/intro-part-02.md +221 -0
  140. package/docs/posts/intro-part-03.md +190 -0
  141. package/docs/posts/intro-part-04.md +221 -0
  142. package/docs/posts/llm-mode-reflect-13.md +166 -0
  143. package/docs/posts/openpalm-built-on-akm.md +86 -0
  144. package/docs/posts/progressive-disclosure-06.md +129 -0
  145. package/docs/posts/proposal-safety-net-15.md +112 -0
  146. package/docs/posts/release-0.5.0.md +247 -0
  147. package/docs/posts/release-0.6.0.md +117 -0
  148. package/docs/posts/release-0.7.0.md +159 -0
  149. package/docs/posts/release-0.8.0.md +332 -0
  150. package/docs/posts/scattered-skills-05.md +100 -0
  151. package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
  152. package/docs/posts/team-skills-07.md +198 -0
  153. package/docs/posts/wiki-agent-knowledge-08.md +223 -0
  154. package/docs/posts/workflows-vaults-09.md +321 -0
  155. package/docs/product-surface.md +54 -0
  156. package/docs/reference/README.md +1 -2
  157. package/docs/reference/asset-types.md +168 -0
  158. package/docs/reference/cli.md +5 -6
  159. package/docs/reference/data-and-telemetry.md +6 -0
  160. package/docs/reference/env-and-secrets.md +126 -0
  161. package/docs/reference/memory.md +124 -0
  162. package/docs/reference/refs.md +156 -0
  163. package/docs/reference/registry.md +489 -0
  164. package/docs/reference/website-sources.md +141 -0
  165. package/package.json +12 -15
@@ -0,0 +1,579 @@
1
+ # Architecture
2
+
3
+ akm is a Bun-based CLI for discovering and using agent assets from local
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
+ ---
11
+
12
+ ## Asset Types
13
+
14
+ The `akm` adapter's fourteen built-in asset types (`skill`, `command`,
15
+ `agent`, `knowledge`, `instruction`, `workflow`, `script`, `memory`,
16
+ `lesson`, `fact`, `env`, `secret`, `task`, `session`) each map to a
17
+ canonical source directory through `src/core/asset/asset-placement.ts`'s
18
+ `PLACEMENT_SPECS` map. See
19
+ [Bundle Types](../reference/bundle-types.md) for the full table.
20
+
21
+ The deprecated `vault` type was removed in 0.9.0 and replaced by `env` (whole
22
+ `.env` files) and `secret` (single-value secret files). `wiki` is not an item
23
+ type: multi-page wikis are a bundle *format* owned by the `llm-wiki` adapter,
24
+ not a per-file type stamped by the classifier (see
25
+ [Classification](internals/classification.md) and
26
+ [Bundle Types](../reference/bundle-types.md) for the other ten formats akm
27
+ recognizes besides its own native one).
28
+
29
+ ---
30
+
31
+ ## Sources and Source Providers
32
+
33
+ A **source** is a directory plus a way to refresh it from upstream. There are
34
+ exactly four source provider types:
35
+
36
+ - `filesystem` — a local path the user owns
37
+ - `git` — a git working tree mirrored under akm's cache
38
+ - `website` — recrawled and converted to markdown
39
+ - `npm` — installed into the cache
40
+
41
+ All four kinds expose the same minimal `SourceProvider` interface
42
+ (`src/sources/provider.ts`):
43
+
44
+ ```ts
45
+ interface SourceProvider {
46
+ readonly name: string;
47
+ readonly kind: string; // "filesystem" | "git" | "website" | "npm"
48
+ path(): string; // directory the indexer walks
49
+ sync?(options?: { force?: boolean }): Promise<void>; // refresh from upstream (no-op for filesystem)
50
+ }
51
+ ```
52
+
53
+ Providers do **not** implement `search`, `show`, `canShow`, or any read method.
54
+ The indexer walks `path()`, classifies files, and answers all queries from the
55
+ local FTS5 index.
56
+
57
+ File semantics are owned by the selected bundle adapter. OKF is the first-class
58
+ least-common-denominator for Markdown path identity, open types, content, links,
59
+ and heading fragments. The `akm` format is an OKF-compatible Markdown superset:
60
+ its adapter progressively adds native command, script, workflow, task,
61
+ environment, secret, memory, and lesson behavior. Non-Markdown formats retain
62
+ their native serialization and every adapter retains its own capability,
63
+ validation, redaction, and placement rules.
64
+
65
+ The legacy `LiveStashProvider` / `SyncableStashProvider` split is gone, as is
66
+ any "remote-only" provider tier. API-backed sources (mem0, Notion, etc.) are
67
+ deferred to a separate `QuerySource` tier post-v1.
68
+
69
+ ### Cache-backed sources are still indexed locally
70
+
71
+ `git`, `website`, and `npm` all materialise files into a cache directory under
72
+ `$XDG_CACHE_HOME/akm/`. Once mirrored, they participate in the same local
73
+ indexing pipeline as filesystem sources. There is no parallel scoring system
74
+ for "remote" content.
75
+
76
+ ---
77
+
78
+ ## Refs
79
+
80
+ User-facing item refs are path-identified:
81
+
82
+ ```text
83
+ [bundle//]conceptId[#fragment]
84
+ ```
85
+
86
+ - the subdir-qualified `conceptId` (`skills/code-review`) identifies an item
87
+ within a bundle; `type` is no longer a separate ref segment
88
+ - the fully qualified `bundle//conceptId` spelling is canonical in index rows
89
+ and durable state; index-backed local read output keeps the short form for the
90
+ default bundle and qualifies every non-default bundle
91
+ - optional `bundle//` narrows input lookup to a configured bundle
92
+ - refs are parsed by `parseBundleRef` in `src/core/asset/asset-ref.ts`
93
+ - markdown-backed items strip `.md` from canonical names
94
+ - `#fragment` is input-only and never stored: on markdown-document items the
95
+ core resolves it as a section selector (`knowledge/api-guide#authentication`);
96
+ elsewhere it is an adapter-owned selector opaque to the core
97
+ - refs embedded in prose must be fully qualified (`bundle//conceptId`) or a
98
+ native adapter link form — a bare conceptId in prose is ordinary text, and no
99
+ tool rewrites it
100
+ - a rename is delete plus create: the new path is a new identity and learned
101
+ state does not follow it
102
+
103
+ See [`specs/ref.md`](./specs/ref.md) for the normative grammar and
104
+ [`specs/0.9.0-decisions.md`](./specs/0.9.0-decisions.md) for the rationale
105
+ behind the 0.9.0 changes.
106
+
107
+ Each indexed entry stores that identity as `item_ref`, with `bundle_id` and
108
+ `concept_id` provenance, and stores its absolute materialized local file as
109
+ `file_path`. Search and show return these index-backed refs and paths, so two
110
+ bundles containing the same concept remain distinguishable. Rows without the
111
+ current identity columns are ignored until they are reindexed.
112
+
113
+ Examples:
114
+
115
+ - `skills/code-review`
116
+ - `workflows/release/train`
117
+ - `team//commands/deploy`
118
+
119
+ URI schemes (`viking://...`, `github://...`) are **not** asset refs. Install
120
+ locators like `github:owner/repo`, `git+https://...`, `npm:@scope/pkg`,
121
+ `skills.sh:slug`, and `./local/path` are a separate grammar parsed by
122
+ `parseRegistryRef` in `src/registry/resolve.ts` and consumed by
123
+ `akm add` / `akm clone`.
124
+
125
+ ---
126
+
127
+ ## Search Pipeline
128
+
129
+ There is **one** ranking for all indexed content: reciprocal rank fusion
130
+ (k = 60, equal weights) of two candidate lists, 100 each.
131
+
132
+ 1. lexical: BM25 over the whole-document FTS5 table (`entries_fts`, columns
133
+ `name`, `description`, `tags`, `hints`, `content`, equal weights),
134
+ matching any of the query's non-stopword tokens
135
+ 2. vector: the document vectors nearest to the query embedding, embedded
136
+ through the model's embedding profile (`src/llm/embedders/profile.ts`)
137
+
138
+ Filters (type, source, scope, belief, proposed quality, default-excluded
139
+ types) and one-hit-per-file deduplication narrow the fused list without
140
+ reordering it, and a hit's `score` is its fused score. Of entries with
141
+ identical indexed content only the highest-ranked is kept. Nothing else — name,
142
+ type, tag, graph, usage or belief-state signals — changes the order. The
143
+ design and its measured alternatives are in the retrieval evaluation
144
+ (`akm/eval/retrieval/reports/baseline-2026-09-27.md`).
145
+
146
+ Notes:
147
+
148
+ - lexical queries are tokenized once with Unicode letter/number semantics,
149
+ lowercased, and stripped of English stopwords unless the query is nothing
150
+ else; the tokens are quoted and OR-ed
151
+ - the query embedding is requested before the FTS query runs and is bounded
152
+ by `embedding.queryTimeoutMs` (default 3000); on timeout or failure the
153
+ search is served by the lexical list alone
154
+ - Markdown fragments are not searched; `akm show` reads the stored safe
155
+ Markdown (`entry_fragments`) for `#fragment` refs
156
+ - `hints` includes `searchHints`, `examples`, `usage`, intent fields, wiki
157
+ cross-references, and page-kind hints
158
+ - `content` is bounded low-weight body prose plus TOC headings and parameter metadata; secret/env/session material is excluded at the adapter boundary
159
+ - registry results live in `registryHits`, never in `hits`
160
+ - `--from all` keeps registry results in `registryHits` — they are not
161
+ rank-merged with source hits
162
+ - local search and curate expose the actual execution in `searchMode`:
163
+ `semantic`, intentional/unavailable `keyword`, or `fts-fallback` when a
164
+ ready semantic runtime failed during the query; that degradation also
165
+ surfaces as one sanitized `warnings[]` entry, including in agent shape
166
+ - usage-event writes are best-effort; `EROFS`/`EACCES` from a read-only
167
+ environment are silent and never affect `searchMode`
168
+
169
+ `akm search` is implemented in `src/commands/read/search.ts` and queries the
170
+ indexer's local search (`src/indexer/search/db-search.ts`). Provider fan-out is gone.
171
+
172
+ ---
173
+
174
+ ## Show Resolution
175
+
176
+ `akm show` queries the local FTS5 index, then reads the file from disk.
177
+
178
+ Local show flow (`src/commands/read/show.ts`):
179
+
180
+ 1. parse `[bundle//]conceptId`
181
+ 2. `lookup(ref)` by indexed `item_ref` and materialized source root
182
+ 3. return the indexed canonical ref and read `file_path` from disk
183
+ 4. report not-found when no index row resolves; physical ownership can prevent
184
+ a lower-priority source from retargeting a ref, but never authorizes a
185
+ direct file read
186
+ 5. apply generic Markdown content/fragment presentation for OKF, or the owning
187
+ adapter's progressively enhanced native presentation
188
+
189
+ An opaque `#akm-fragment-...` selector resolves against the safe Markdown
190
+ revision persisted in `entry_fragments`, so search and show remain consistent
191
+ if the materialized file changes before it is reindexed. Bare fragment show is
192
+ still exact. The opt-in `--context lead` projection assembles the first and
193
+ selected fragments from that same indexed revision under a hard character
194
+ budget, labels the selected match, and keeps it last. It never reconstructs
195
+ neighbors from the current raw file. Fragment search plus opaque/contextual show
196
+ JSON exposes the selected and parent refs, one-based ordinal/count and
197
+ source-line bounds, neighbor refs, and separate fragment/parent size estimates;
198
+ canonical `ref` remains the parent on show. A default exact friendly-heading
199
+ show stays source-live and omits indexed provenance.
200
+
201
+ There is **no provider or local-disk fallback** for asset refs. The file is
202
+ read only after an indexed row resolves it.
203
+
204
+ ### Local access metadata
205
+
206
+ Agent-shaped search, show, and curate results for materialized local assets
207
+ include the index-backed `ref`, absolute `path`, and `editable`. `editHint` is
208
+ included only when `editable` is `false`; it is secondary guidance and never
209
+ replaces the normal show, run, or use action (or curate follow-up). Registry-only
210
+ results have no local path or editability fields.
211
+
212
+ `editable` means current AKM source policy authorizes direct in-place
213
+ modification of that exact file. It is computed when the response is built from
214
+ the current resolved source roots and effective `writable` policy, is not stored
215
+ in the index, and fails closed when no configured source owns the path.
216
+
217
+ ---
218
+
219
+ ## Writing to Sources
220
+
221
+ Writes go through one helper: `src/core/write-source.ts`. This is the only
222
+ place in the codebase that branches on `source.kind`, and that is **enforced**
223
+ by `scripts/lint-write-source-chokepoint.ts` in the `lint` chain — it was
224
+ prose-only until 0.9.0, and drifted.
225
+
226
+ Command layers therefore never hold provider knowledge. Where a step is
227
+ meaningful only for a publication-backed target, `write-source.ts` exposes a
228
+ kind-neutral wrapper that absorbs the guard and no-ops otherwise:
229
+ `prepareWriteTargetForMutation`, `commitWriteTargetBoundary`, and
230
+ `withWriteTargetMutation` (write the file, `git add` exactly the recorded
231
+ paths, commit, push with `--force-with-lease` when configured). Recording a
232
+ kind for display (`targetKind: target.source.kind`) is not branching and stays
233
+ in the command layer.
234
+
235
+ ```ts
236
+ writeAssetToSource(source, config, ref, content)
237
+ deleteAssetFromSource(source, config, ref)
238
+ ```
239
+
240
+ The flow:
241
+
242
+ 1. Refuse if the source is not `writable`.
243
+ 2. Plain filesystem write to `path.join(source.path, …)` — for **every** kind,
244
+ with no commit (0.9.0, issue #507).
245
+
246
+ Git-backed targets are committed in a single batch at the operation boundary via
247
+ `commitWriteTargetBoundary(target, message, { push })`, which delegates to
248
+ `saveGitStash`: write/delete helpers carry their exact changed paths to the
249
+ boundary, which stages and commits only those files (so unrelated staged work,
250
+ including work under the same asset directory, is not included). Improve
251
+ auto-sync similarly subtracts the Git dirty-path baseline captured at invocation
252
+ start. Push remains gated on `writable && hasRemote && push !== false`. The old
253
+ per-asset commit/push path (`options.pushOnCommit`) is **hard-rejected** at
254
+ config load — not merely deprecated — by a `superRefine` on the bundle/source
255
+ schema (`src/core/config/schema/sources-bundles.ts`).
256
+
257
+ `writable` is a config flag, not an interface concern. Defaults: `true` for
258
+ `filesystem`, `false` for everything else. `writable: true` on `website` or
259
+ `npm` is rejected at config load — `sync()` would clobber edits on the next
260
+ refresh.
261
+
262
+ Write-target resolution (`resolveWriteTarget`) follows: an explicit
263
+ destination flag (`--bundle` on `remember`/`clone`/`improve`, `--target` on
264
+ `import`/env/secret mutations) -> `config.defaultWriteTarget` -> working
265
+ bundle (`defaultBundle`) -> `ConfigError`. The resolved target keeps the
266
+ optional configured selector separate from the stable `source.name`: APIs
267
+ that must re-resolve a destination use the selector, while durable refs and
268
+ state rows always use `source.name`. The implicit working bundle therefore has
269
+ no selector but has durable identity `stash`.
270
+
271
+ On mutation surfaces that accept asset refs, a qualified ref implies its
272
+ bundle as the write target. A matching explicit destination flag is allowed;
273
+ a different one is a usage error. This applies, for example, to env/secret
274
+ mutations, ref-scoped improve, and qualified `--supersedes` refs on
275
+ remember/import.
276
+
277
+ `akm clone` uses the same managed write-target fallback and accepts
278
+ `--bundle` for an explicit managed destination. `--dest` is the unmanaged
279
+ path escape hatch: it bypasses managed target resolution and cannot be
280
+ combined with `--bundle`.
281
+
282
+ ### Improve durable-state identity
283
+
284
+ Improve readers and writers use one current key for each asset. A resolved
285
+ indexed entry uses its fully qualified `item_ref`; a direct or provenance-free
286
+ ref uses its current concept ID. There is no alternate root-based key, bare alias
287
+ merge, or second state lookup. This prevents a duplicate concept in another
288
+ installation from inheriting unrelated feedback, proposal-cursor, salience, or
289
+ convergence state.
290
+
291
+ Retrieval demand is scoped separately through usage-event entry IDs and selected
292
+ source roots, with qualified refs covering detached events. Improve never merges
293
+ retrieval counts or last-use timestamps solely by bare ref across sources.
294
+
295
+ ### Proposal queues and destinations
296
+
297
+ `akm proposal ... --queue <source>` selects the configured writable source root
298
+ whose proposal queue is read or adjudicated; it does not override the proposal's
299
+ write destination. Qualified proposals and unqualified proposals created in a
300
+ configured secondary queue record the destination source name and materialized
301
+ root. Diff, accept, and revert use that binding by default and reject an explicit
302
+ `--target` that resolves elsewhere. An unbound short proposal requires either
303
+ an explicit `--target` or an authenticated `--queue` context; it does not
304
+ inherit a default write target.
305
+
306
+ ---
307
+
308
+ ## Registry Providers
309
+
310
+ Registry providers are read-only catalogs of installable kits. The interface
311
+ lives in `src/registry/providers/types.ts`:
312
+
313
+ ```ts
314
+ interface RegistryProvider {
315
+ readonly type: string; // "static-index" | "skills-sh"
316
+ search(options: RegistryProviderSearchOptions): Promise<RegistryProviderResult>;
317
+ }
318
+ ```
319
+
320
+ The contract is a single `search()` method — the orchestrator's only entry
321
+ point. Implementations must never throw; errors are returned as
322
+ `warnings[]` on the result.
323
+
324
+ Built-in registries:
325
+
326
+ | Kind | Role |
327
+ | --- | --- |
328
+ | `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. |
329
+ | `skills-sh` | Wraps the skills.sh REST API. |
330
+
331
+ Context Hub is **not** a registry provider type. It is just a recommended git
332
+ kit installable through the official static-index registry like any other
333
+ source.
334
+
335
+ ---
336
+
337
+ ## Workflow Runtime State
338
+
339
+ Workflow definitions live in `workflows/`, but workflow run state is separate
340
+ durable runtime state. `workflow_runs`, `workflow_run_steps`,
341
+ `workflow_run_units`, and `workflow_run_unit_attempts` live in `state.db`
342
+ alongside events, tasks, and proposals. `index.db` remains rebuildable search
343
+ state, while `logs.db` stores task/run log lines.
344
+
345
+ - workflow discovery and search use the shared asset index
346
+ - workflow run records survive index rebuilds
347
+ - workflow run state is not derived from the FTS index
348
+
349
+ ---
350
+
351
+ ## Utility Scoring
352
+
353
+ Utility is feedback-driven and rebuilt from `usage_events`. It orders
354
+ improve's salience work; search ranking does not read it.
355
+
356
+ - usage history is preserved across schema resets and full rebuilds
357
+ - detached events are re-linked to fresh entry ids by ref
358
+ - decay is time-proportional, not tied to index frequency
359
+
360
+ ---
361
+
362
+ ## Errors
363
+
364
+ `ConfigError`, `UsageError`, and `NotFoundError` (in `src/core/errors.ts`) each
365
+ carry a stable `code` and a `hint(): string | undefined` method. The CLI
366
+ surfaces hints by calling `error.hint()` directly — there is no regex chain
367
+ parsing error messages.
368
+
369
+ All three extend a shared abstract base `AkmError` carrying a `kind`
370
+ discriminant (`"config" | "usage" | "not-found"`). The CLI exit-code classifier
371
+ (`classifyExitCode` in `src/cli/shared.ts`) switches exhaustively on `kind`
372
+ (`never`-checked via `assertNever`), so adding a new error class is a
373
+ compile-time error until its exit code is mapped. Any thrown value that is
374
+ **not** an `AkmError` is treated as an unexpected internal failure and maps to
375
+ the distinct INTERNAL exit code **70** (sysexits `EX_SOFTWARE`) — this lets
376
+ scripts tell "akm threw unexpectedly" apart from an ordinary `NotFoundError`
377
+ (exit 1).
378
+
379
+ | Class / case | Exit code |
380
+ | --- | --- |
381
+ | `ConfigError` (`kind: "config"`) | 78 |
382
+ | `UsageError` (`kind: "usage"`) | 2 |
383
+ | `NotFoundError` (`kind: "not-found"`) | 1 |
384
+ | unclassified / non-`AkmError` (INTERNAL) | 70 |
385
+
386
+ ---
387
+
388
+ ## Engine Boundary
389
+
390
+ The approved target semantics that connect native agent and command assets to
391
+ this engine boundary are specified in
392
+ [Agent, Command, Engine, and Model Resolution](specs/agent-command-engine-model-design.md).
393
+ The WP2/WP3 model-map and common-cascade path, WP4 command surface, WP5 runtime
394
+ lowering convergence, WP6 task v3, and WP7 source-to-frozen durable workflow
395
+ IR are implemented. Direct commands, task execution, and new workflow freezes
396
+ share this boundary.
397
+
398
+ Public execution selection uses named `engines`, never profiles. An engine is
399
+ either `kind: "llm"` (an OpenAI-compatible chat-completions connection) or
400
+ `kind: "agent"` (a registered harness platform). The SDK runtime is an
401
+ internal transport kind for an `opencode-sdk` agent engine, not a public engine
402
+ kind.
403
+
404
+ Every execution — direct commands, tasks, workflows, improve, proposals, and
405
+ index passes — takes the same four steps
406
+ (`src/integrations/agent/execution.ts`, `runner-dispatch.ts`):
407
+
408
+ ```text
409
+ adapter-rendered or anonymous work
410
+ -> resolveExecution(input) -> { request, runner (a RunnerSpec), provenance }
411
+ -> buildExecution(request, runner) -> harness argv inputs or chat messages
412
+ -> runExecution(built, options) -> agent CLI, OpenCode SDK, or direct LLM
413
+ ```
414
+
415
+ `resolveExecution` picks the engine from an ordered list — the nearest layer
416
+ that names one, then `defaults.engine`, then the implicit `opencode-sdk`
417
+ fallback when its binary is present — and merges one set of defaults, nearest
418
+ wins: the selected engine's own model/inference/timeout/workspace, then the
419
+ persona, the command, the invocation defaults, and the current call. Omitted,
420
+ explicit `null`, zero, and empty values stay distinct. A recognized
421
+ `models.json` alias expands once, at the layer that chose it; the request
422
+ records the exact final model. Tools are authorized against the host-local
423
+ `execution.allowedTools` ceiling; no asset grants itself a tool. The result's
424
+ `provenance` names the layer behind each field (`akm task explain`,
425
+ `command run --dry-run`).
426
+
427
+ `buildExecution` hands the request to the selected harness's own builder
428
+ (registered on `HARNESS_REGISTRY`), or builds chat messages for a direct LLM.
429
+ A tool policy the transport cannot enforce, or a denied tool selection, stops
430
+ here; any other field the transport cannot carry becomes a secret-free
431
+ `untranslated-field` notice and dispatch continues.
432
+
433
+ Credentials stay symbolic (an env descriptor, an `apiKeyFile` path, or a
434
+ `secret://` reference) in everything `resolveExecution` returns, so a request
435
+ and runner can be journaled. `runExecution()` reads the current value at each
436
+ dispatch and scrubs it, with every other secret-looking value the child could
437
+ see, from the result. Workflow resume calls `buildExecutionFromWire()` on the
438
+ journaled `{ request, runner }` and never reads config, `models.json`, or
439
+ credentials, so a config edit after the freeze cannot change a resumed unit.
440
+ The prompt-free interactive `akm agent` launch resolves its engine directly.
441
+ An explicit missing or incompatible engine is an error and never falls
442
+ through to another configured engine.
443
+
444
+ Task-v3 execution and durable workflow-v4 dispatch use this runtime boundary.
445
+ Markdown and GitHub-shaped YAML compile through source IR v1; new starts freeze
446
+ v4-family `irVersion: 5`. A stored plan that decodes runs whatever release
447
+ froze it; one that does not is marked abandoned and `akm workflow run <ref>`
448
+ starts afresh. Only a plan a newer akm froze is refused, naming the upgrade.
449
+ AKM does not support full GitHub Actions semantics or arbitrary remote action
450
+ execution.
451
+
452
+ ### In-tree LLM helpers (`src/llm/`)
453
+
454
+ Every helper under `src/llm/` is a **bounded, single-shot, stateless** call.
455
+ Concretely:
456
+
457
+ - Each public export is either a pure function (`chatCompletion`,
458
+ `splitMemoryIntoAtomicFacts`, `resolveIndexPassExecution`,
459
+ `resolveIndexPassRunner`, `parseJsonResponse`, …) or a factory that returns
460
+ a one-shot client tied to the symbolic runner/config the caller passes in.
461
+ - No module under `src/llm/` keeps session, conversation, or response state at
462
+ module scope. The only module-level singleton is the local embedder
463
+ pipeline in `src/llm/embedder.ts`, which is an expensive-to-build but
464
+ stateless model handle (see the comment in that file). It exposes
465
+ `resetLocalEmbedder()` so tests can construct a fresh pipeline.
466
+ - `callStructured()` is the common bounded structured-LLM seam: it adapts an
467
+ already-resolved symbolic runner into an inline resolved request, lowers it,
468
+ and dispatches without re-reading aliases or credentials. Index callers use
469
+ `resolveIndexPassExecution()` to freeze the typed `{ runner, notices }`
470
+ selection once per invocation. `resolveIndexPassRunner()` is its readiness
471
+ projection, not a second dispatch seam. Improve processes are selected
472
+ through `improve.strategies`; see `docs/reference/configuration.md` for
473
+ canonical config paths.
474
+
475
+ The seam is locked by `tests/architecture/llm-stateless-seam.test.ts`, which
476
+ inspects the module shape of each `src/llm/*` entry — not the source text.
477
+
478
+ ### External agents (`src/integrations/agent/`)
479
+
480
+ External coding agents are reachable via two execution paths:
481
+
482
+ **Spawn path** (`src/integrations/agent/spawn.ts`):
483
+
484
+ - `runAgent(profile, prompt, options)` is the single shell-out entry point.
485
+ It owns process spawn, captured/interactive stdio, hard timeout, and
486
+ structured failure reasons.
487
+ - The `AgentRunResult` envelope carries `{ ok, exitCode, stdout, stderr,
488
+ durationMs, reason?, error?, parsed? }` where `reason` is one of
489
+ `"timeout" | "spawn_failed" | "non_zero_exit" | "parse_error"`. Callers
490
+ never see raw process errors.
491
+
492
+ **SDK path** (`src/integrations/harnesses/opencode-sdk/sdk-runner.ts`):
493
+
494
+ - `runOpencodeSdk(profile, prompt, opts, llmConfig?)` uses the embedded
495
+ `@opencode-ai/sdk` instead of `Bun.spawn`. No agent CLI binary is required.
496
+ - Selected by an agent engine whose `platform` is `"opencode-sdk"`. Its optional
497
+ `llmEngine` supplies the LLM fallback connection. With none, there is no
498
+ fallback and opencode uses its own configuration; `defaults.llmEngine` is not
499
+ borrowed.
500
+ - Manages a single per-process singleton server, creating one fresh session
501
+ per call to avoid history accumulation and unbounded token growth.
502
+ - Concurrent calls share startup by server material, but each call races that
503
+ startup against its own deadline (including `null`); no caller's timeout is
504
+ stored in the shared lifecycle.
505
+
506
+ New tasks are task source v4 `.yml` assets. Normal execution rejects v2 and v3
507
+ and directs operators to the explicit preview/apply migrator. Task source v4
508
+ command, workflow, script, and shell targets use the common resolved/lowered
509
+ execution boundary. Historical task-run metadata remains readable.
510
+
511
+ Long-lived mutable operations each take their own lock file, one `O_EXCL`
512
+ create (`src/core/file-lock.ts`): index writers, improve and extract process
513
+ locks, lockfile writers, the scheduler lock, and each workflow run's lock. A
514
+ lock whose holder process is gone is reclaimed. There is no shared barrier or
515
+ activity registry around them; `state.db` writers serialize on SQLite's own
516
+ `BEGIN IMMEDIATE`.
517
+
518
+ ---
519
+
520
+ ## Module Boundaries
521
+
522
+ | Module | Responsibility |
523
+ | --- | --- |
524
+ | `src/cli.ts` | composition root; per-family parsing lives in `commands/<family>/*-cli.ts` |
525
+ | `src/cli/` | citty composition helpers |
526
+ | `src/core/asset/asset-placement.ts` | asset type registry and canonical source directories (`PLACEMENT_SPECS`) |
527
+ | `src/core/asset/asset-ref.ts` | asset ref parsing and normalization (`parseBundleRef`) |
528
+ | `src/core/config/config.ts` | config loading, validation, env resolution |
529
+ | `src/core/errors.ts` | error classes with stable codes and hints |
530
+ | `src/core/parse.ts` | shared JSON parsing: think/fence stripping, balanced-brace extraction |
531
+ | `src/core/concurrent.ts` | bounded concurrency pool (`concurrentMap`, default 1 worker) |
532
+ | `src/core/write-source.ts` | the single write helper (branches on `source.kind`) |
533
+ | `src/execution/resolved-request.ts` | the versioned resolved execution request and its canonical wire form (tolerant decode) |
534
+ | `src/integrations/agent/execution.ts` | `resolveExecution` (engine, layers, model alias, tools) and `buildExecution` / `buildExecutionFromWire` |
535
+ | `src/integrations/agent/runner-dispatch.ts` | `runExecution`: reads credentials at dispatch, runs agent/SDK/LLM, redacts the result |
536
+ | `src/integrations/agent/request-lowering.ts` | shared factory for the harness-owned request builders |
537
+ | `src/sources/provider.ts` | minimal `SourceProvider` interface |
538
+ | `src/sources/providers/` | filesystem / git / website / npm implementations |
539
+ | `src/sources/resolve.ts` | filesystem path resolution for refs |
540
+ | `src/indexer/indexer.ts` | walking, metadata generation, index rebuilds, embeddings, utility recompute |
541
+ | `src/indexer/walk/` | walker, matchers, path/file/index context — the walk phase |
542
+ | `src/indexer/db/` | `llm-cache` — the persistence phase (entries/embeddings persistence lives in `src/storage/repositories/`) |
543
+ | `src/indexer/search/` | `db-search`, ranking, search-fields, search-source, enrichers — the search phase |
544
+ | `src/indexer/passes/` | memory-inference, dir-staleness, metadata — LLM/metadata passes |
545
+ | `src/indexer/usage/` | usage-events |
546
+ | `src/commands/read/search.ts` | `akm search` orchestration |
547
+ | `src/commands/read/show.ts` | `akm show` orchestration |
548
+ | `src/commands/improve/` | knowledge-evolution slice (improve/consolidate/distill/extract/reflect + `memory/`) |
549
+ | `src/commands/proposal/` | proposal-queue slice (proposal/propose + `validators/` core 3-cycle) |
550
+ | `src/commands/sources/` | source/stash lifecycle command surface |
551
+ | `src/commands/env/` | env/secret command surface |
552
+ | `src/commands/tasks/` | scheduled-task command surface |
553
+ | `src/commands/agent/` | contribute/agent command surface |
554
+ | `src/registry/providers/` | registry provider implementations (static-index, skills-sh) |
555
+ | `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 |
556
+ | `src/workflows/authoring/` | workflow authoring + scope-key helpers |
557
+ | `src/workflows/runtime/runs.ts` | workflow run persistence (raw SQL lives in `src/storage/repositories/workflow-runs-repository.ts`) |
558
+ | `src/workflows/runtime/` | run lifecycle: runs, checkin, agent-identity |
559
+ | `src/llm/client.ts` | OpenAI-compatible chat completions client (stateless, single request/response) |
560
+ | `src/llm/index-passes.ts` | per-pass LLM config resolution for `akm index` |
561
+ | `src/llm/memory-infer.ts` | atomic-fact split helper (selected through `improve.strategies.<name>.processes.memoryInference`) |
562
+ | `src/llm/embedder.ts` | local + remote embedder facade with cached pipeline |
563
+ | `src/integrations/agent/spawn.ts` | agent CLI shell-out entry point (`runAgent`) |
564
+ | `src/integrations/harnesses/opencode-sdk/sdk-runner.ts` | embedded SDK runner selected by an SDK `RunnerSpec` |
565
+ | `src/integrations/agent/runner-dispatch.ts` | low-level exhaustive `RunnerSpec` transport dispatch and dispatch-time credential redaction |
566
+ | `src/integrations/agent/profiles.ts` | internal spawn descriptors used after agent-engine lowering |
567
+ | `src/integrations/agent/engine-resolution.ts` | named engine and symbolic transport-material resolution; exact request model projection happens in execution lowering |
568
+ | `src/llm/structured-call.ts` | bounded structured-LLM adapter through the common resolved/lowered execution seam |
569
+ | `src/integrations/agent/detect.ts` | PATH-based agent CLI detection for `akm setup` |
570
+
571
+ ---
572
+
573
+ ## Tech Stack
574
+
575
+ - Runtime: Bun
576
+ - Language: TypeScript (ESM, strict)
577
+ - Database: `bun:sqlite` with FTS5; vectors are float32 BLOBs scanned in JavaScript
578
+ - Testing: `bun:test`
579
+ - Formatting/linting: Biome