akm-cli 0.9.30 → 0.9.31

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 (161) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +10 -10
  3. package/ROADMAP.md +112 -0
  4. package/dist/commands/improve/improve-cli.js +33 -12
  5. package/dist/core/file-lock.js +10 -1
  6. package/dist/llm/client.js +33 -3
  7. package/dist/scripts/akm-migrate-node.js +5 -1
  8. package/dist/scripts/akm-migrate.js +5 -1
  9. package/docs/README.md +42 -41
  10. package/docs/agents/README.md +15 -0
  11. package/docs/agents/agent-install.md +14 -0
  12. package/docs/architecture/README.md +67 -0
  13. package/docs/architecture/adapters.md +272 -0
  14. package/docs/architecture/akm-architecture-decision-history.md +2055 -0
  15. package/docs/architecture/akm-core-principles.md +126 -0
  16. package/docs/architecture/architecture.md +579 -0
  17. package/docs/architecture/comparisons/claude-code-vs-akm-workflows-full.md +550 -0
  18. package/docs/architecture/decisions/0001-no-interpolation-attached-structured-context.md +72 -0
  19. package/docs/architecture/decisions/0002-unit-reuse-and-input-hash-scope.md +142 -0
  20. package/docs/architecture/decisions/0003-child-env-allowlist-and-provenance.md +189 -0
  21. package/docs/architecture/decisions/0004-task-input-contract-and-flag-coercion.md +97 -0
  22. package/docs/architecture/decisions/0005-task-result-vocabulary-and-legacy-read-mapping.md +108 -0
  23. package/docs/architecture/decisions/0006-task-source-version-routing.md +124 -0
  24. package/docs/architecture/decisions/0007-workflow-composition-bounds.md +83 -0
  25. package/docs/architecture/decisions/0008-task-binding-normalization.md +88 -0
  26. package/docs/architecture/decisions/0009-child-run-publication-column-parity.md +80 -0
  27. package/docs/architecture/decisions/0010-driver-protocol-cut.md +36 -0
  28. package/docs/architecture/decisions/0011-engine-run-loop-invariants.md +125 -0
  29. package/docs/architecture/decisions/README.md +62 -0
  30. package/docs/architecture/improvement.md +440 -0
  31. package/docs/architecture/internals/classification.md +154 -0
  32. package/docs/architecture/internals/fresh-host-rebuild-runbook.md +40 -0
  33. package/docs/architecture/internals/functional-contract-patterns.md +340 -0
  34. package/docs/architecture/internals/health-advisories.md +110 -0
  35. package/docs/architecture/internals/improve-workflow.md +713 -0
  36. package/docs/architecture/internals/indexing.md +498 -0
  37. package/docs/architecture/internals/registry-network-boundary.md +57 -0
  38. package/docs/architecture/internals/search.md +249 -0
  39. package/docs/architecture/internals/storage-locations.md +943 -0
  40. package/docs/architecture/persisted-data-compat.md +59 -0
  41. package/docs/architecture/reviews/env-secret-access.md +319 -0
  42. package/docs/architecture/runtime-boundary-design.md +153 -0
  43. package/docs/architecture/specs/0.9.0-close-out-plan.md +361 -0
  44. package/docs/architecture/specs/0.9.0-decisions.md +528 -0
  45. package/docs/architecture/specs/0.9.0-docs-code-drift-register.md +745 -0
  46. package/docs/architecture/specs/0.9.0-open-items-register.md +135 -0
  47. package/docs/architecture/specs/0.9.0-public-api-issue-backlog.md +740 -0
  48. package/docs/architecture/specs/0.9.0-release-surface-review.md +636 -0
  49. package/docs/architecture/specs/agent-command-engine-model-design.md +513 -0
  50. package/docs/architecture/specs/akm-0.9.0-bundle-adapter-spec.md +501 -0
  51. package/docs/architecture/specs/akm-0.9.0-ref-grammar-decision.md +291 -0
  52. package/docs/architecture/specs/akm-format-neutral-bundle-workspace-spec.md +2059 -0
  53. package/docs/architecture/specs/di-seams-plan.md +539 -0
  54. package/docs/architecture/specs/driver-protocol-keep-or-cut.md +275 -0
  55. package/docs/architecture/specs/fact-asset-type.md +142 -0
  56. package/docs/architecture/specs/improve-collapse-churn-detector-design.md +634 -0
  57. package/docs/architecture/specs/okf-support.md +148 -0
  58. package/docs/architecture/specs/ref.md +329 -0
  59. package/docs/architecture/specs/reviews/task-workflow-unification/README.md +72 -0
  60. package/docs/architecture/specs/reviews/task-workflow-unification/findings-consolidated.md +152 -0
  61. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-alternatives.md +352 -0
  62. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-bunqueue.md +300 -0
  63. package/docs/architecture/specs/reviews/task-workflow-unification/library-eval-openworkflow.md +402 -0
  64. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-1.md +758 -0
  65. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-2.md +645 -0
  66. package/docs/architecture/specs/reviews/task-workflow-unification/opus-review-3.md +602 -0
  67. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-adversarial.md +27 -0
  68. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-architecture.md +27 -0
  69. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-coverage.md +74 -0
  70. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r1-security.md +15 -0
  71. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-adversarial.md +132 -0
  72. package/docs/architecture/specs/reviews/task-workflow-unification/panel-r2-verification.md +147 -0
  73. package/docs/architecture/specs/stash-conventions-code-spec.md +275 -0
  74. package/docs/architecture/specs/stash-organization-conventions.md +377 -0
  75. package/docs/architecture/specs/task-workflow-format-unification.md +721 -0
  76. package/docs/architecture/specs/workflow-engine-buy-vs-build.md +231 -0
  77. package/docs/architecture/specs/workflow-format-unification.md +437 -0
  78. package/docs/architecture/testing/manual-testing-checklist.md +3420 -0
  79. package/docs/architecture/testing/okf-v0.2-conformance-runbook.md +924 -0
  80. package/docs/architecture/testing/task-workflow-manual-tests.md +1817 -0
  81. package/docs/architecture/testing/testing-workflow.md +446 -0
  82. package/docs/architecture/workflow-engine.md +274 -0
  83. package/docs/guides/README.md +33 -0
  84. package/docs/guides/author-bundles.md +310 -0
  85. package/docs/guides/author-workflows.md +520 -0
  86. package/docs/guides/bundles.md +321 -0
  87. package/docs/guides/capture-knowledge.md +136 -0
  88. package/docs/guides/claude-code-vs-akm-workflows.md +30 -0
  89. package/docs/guides/concepts.md +93 -0
  90. package/docs/guides/discover-and-load.md +158 -0
  91. package/docs/guides/getting-started.md +240 -0
  92. package/docs/guides/improve-the-library.md +246 -0
  93. package/docs/guides/knowledge-management.md +18 -0
  94. package/docs/guides/recipes/headless-install.md +97 -0
  95. package/docs/guides/recipes/website-source.md +28 -0
  96. package/docs/guides/run-workflows.md +279 -0
  97. package/docs/guides/scheduling.md +123 -0
  98. package/docs/guides/use-with-any-agent.md +129 -0
  99. package/docs/guides/wikis.md +81 -0
  100. package/docs/maintainers/README.md +19 -0
  101. package/docs/maintainers/curate-workmap.md +336 -0
  102. package/docs/maintainers/eval.md +3 -0
  103. package/docs/maintainers/local-development.md +108 -0
  104. package/docs/maintainers/release-checklist.md +103 -0
  105. package/docs/migration/README.md +2 -2
  106. package/docs/migration/release-notes/0.0.13.md +1 -1
  107. package/docs/migration/release-notes/0.1.0.md +1 -1
  108. package/docs/migration/release-notes/0.2.0.md +1 -1
  109. package/docs/migration/release-notes/0.3.0.md +1 -1
  110. package/docs/migration/release-notes/0.5.0.md +1 -1
  111. package/docs/migration/release-notes/0.6.0.md +1 -1
  112. package/docs/migration/release-notes/0.7.0.md +1 -1
  113. package/docs/migration/release-notes/0.7.3.md +2 -2
  114. package/docs/migration/release-notes/0.7.4.md +1 -1
  115. package/docs/migration/release-notes/0.7.5.md +1 -1
  116. package/docs/migration/release-notes/0.8.0.md +1 -1
  117. package/docs/migration/release-notes/0.9.0.md +1 -1
  118. package/docs/migration/release-notes/0.9.14.md +1 -1
  119. package/docs/migration/release-notes/0.9.15.md +1 -1
  120. package/docs/migration/release-notes/0.9.16.md +1 -1
  121. package/docs/migration/release-notes/0.9.17.md +1 -1
  122. package/docs/migration/release-notes/0.9.19.md +1 -1
  123. package/docs/migration/release-notes/0.9.2.md +1 -1
  124. package/docs/migration/release-notes/README.md +27 -31
  125. package/docs/migration/v0.5-to-v0.6.md +759 -0
  126. package/docs/migration/v0.8-to-v0.9.md +1 -1
  127. package/docs/plans/0.9.2-agent-command-workflow-plan.md +423 -0
  128. package/docs/posts/README.md +30 -0
  129. package/docs/posts/akm-command-tour.md +495 -0
  130. package/docs/posts/akm-logo-sized.webp +0 -0
  131. package/docs/posts/belief-aware-memory-12.md +139 -0
  132. package/docs/posts/config-v2-lessons-16.md +191 -0
  133. package/docs/posts/improve-loop-autonomous-10.md +225 -0
  134. package/docs/posts/improve-pipeline-debugging-0803.md +179 -0
  135. package/docs/posts/intro-part-01.md +76 -0
  136. package/docs/posts/intro-part-02.md +221 -0
  137. package/docs/posts/intro-part-03.md +190 -0
  138. package/docs/posts/intro-part-04.md +221 -0
  139. package/docs/posts/llm-mode-reflect-13.md +166 -0
  140. package/docs/posts/openpalm-built-on-akm.md +86 -0
  141. package/docs/posts/progressive-disclosure-06.md +129 -0
  142. package/docs/posts/proposal-safety-net-15.md +112 -0
  143. package/docs/posts/release-0.5.0.md +247 -0
  144. package/docs/posts/release-0.6.0.md +117 -0
  145. package/docs/posts/release-0.7.0.md +159 -0
  146. package/docs/posts/release-0.8.0.md +332 -0
  147. package/docs/posts/scattered-skills-05.md +100 -0
  148. package/docs/posts/task-assets-persistent-workflows-11.md +222 -0
  149. package/docs/posts/team-skills-07.md +198 -0
  150. package/docs/posts/wiki-agent-knowledge-08.md +223 -0
  151. package/docs/posts/workflows-vaults-09.md +321 -0
  152. package/docs/product-surface.md +54 -0
  153. package/docs/reference/README.md +1 -2
  154. package/docs/reference/asset-types.md +168 -0
  155. package/docs/reference/cli.md +5 -6
  156. package/docs/reference/env-and-secrets.md +126 -0
  157. package/docs/reference/memory.md +124 -0
  158. package/docs/reference/refs.md +156 -0
  159. package/docs/reference/registry.md +489 -0
  160. package/docs/reference/website-sources.md +141 -0
  161. package/package.json +12 -15
@@ -0,0 +1,539 @@
1
+ # DI seams plan — replace `mock.module` with swap-and-restore module seams
2
+
3
+ Branch: `refactor/di-seams`
4
+
5
+ > **Implementation note (added later):** this plan has landed — the seam
6
+ > pattern (`_set…ForTests` delegators, `tests/_helpers/seams.ts`,
7
+ > `overrideSeam`/`withSeam`) is in production use (e.g.
8
+ > `_setChatCompletionForTests` in `src/llm/client.ts`, consumed by multiple
9
+ > test files), `mock.module(` no longer appears as live code anywhere under
10
+ > `tests/` (only two historical comments reference the retired pattern), and
11
+ > `src/indexer/db/db.ts` — flagged below as needing its stale mock deleted —
12
+ > no longer exists at all; its functionality (`openIndexDatabase`) now lives
13
+ > in `src/storage/repositories/index-connection.ts`. Several of the specific
14
+ > test file paths named per-module below were renamed or consolidated by a
15
+ > later, unrelated test reorganization; those are annotated in place rather
16
+ > than silently repointed, since this document's per-module breakdown is
17
+ > itself the historical record of how the migration was planned.
18
+
19
+ ## Guardrails (binding, non-negotiable)
20
+
21
+ This workstream was previously done wrong (~900 dependency-injection parameter
22
+ threads, reverted). The rules for THIS attempt:
23
+
24
+ - **Swap-and-restore, not parameter threading.** Model: `withMockedFetch` in
25
+ `tests/_helpers/sandbox.ts` (swap `globalThis.fetch` → run → finally-restore)
26
+ and `src/storage/database.ts`'s "plain module, not an adapter layer" stance.
27
+ - Each seamed module keeps its normal exports. Internally the exported function
28
+ delegates through one module-level binding. The module gains exactly ONE small
29
+ test-only override function (`_set…ForTests(fake | undefined)`); passing
30
+ `undefined` restores the real implementation.
31
+ - **Zero changes to production call sites.** No context objects, no new
32
+ parameters on existing functions, no interfaces/ports/adapters, no factories,
33
+ no new wrapper modules. If a module seems to need call-site changes, STOP.
34
+ - Net diff per module: tens of lines in `src`, offset by deleted `mock.module`
35
+ boilerplate in tests.
36
+ - The override function is inert unless a test calls it. Production behavior is
37
+ byte-identical.
38
+
39
+ ## The seam pattern (canonical shape)
40
+
41
+ For a module exporting `foo(a, b)`:
42
+
43
+ ```ts
44
+ // ── Test seam ────────────────────────────────────────────────────────────────
45
+ // Swap-and-restore override. Inert in production; only tests call the setter.
46
+ let fooOverride: typeof fooReal | undefined;
47
+
48
+ /** TEST-ONLY. Swap the implementation of `foo`; pass undefined to restore. */
49
+ export function _setFooForTests(fake?: typeof fooReal): void {
50
+ fooOverride = fake;
51
+ }
52
+
53
+ export function foo(a: A, b: B): R {
54
+ if (fooOverride) return fooOverride(a, b);
55
+ return fooReal(a, b);
56
+ }
57
+
58
+ function fooReal(a: A, b: B): R {
59
+ // …existing body, renamed, NOT exported…
60
+ }
61
+ ```
62
+
63
+ Notes on the shape:
64
+
65
+ - The existing exported function body is renamed to a private `…Real` function;
66
+ the export becomes a 2-line delegator. Importers see the identical signature.
67
+ - Intra-module calls to the exported name (e.g. `client.ts` calls its own
68
+ `chatCompletion` from `probeCapabilities`) go through the delegator too.
69
+ This is a *superset* of what `mock.module` guarantees for internal callers
70
+ and matches the tests' intent (no network, ever).
71
+ - One setter per module. When a module has several overridable exports, the
72
+ setter takes a partial overrides object (see per-module specs) — still ONE
73
+ test-only export, one internal binding.
74
+ - Naming convention: `_set<Thing>ForTests`, prefixed `_` so the
75
+ it's self-evidently not public API. Never call it from `src`.
76
+
77
+ ## Shared test helper — `tests/_helpers/seams.ts` (new file, ~40 lines)
78
+
79
+ ```ts
80
+ // tests/_helpers/seams.ts
81
+ type SeamSetter<T> = (fake: T | undefined) => void;
82
+
83
+ /** Setters that currently hold a fake; drained by resetAllSeams(). */
84
+ const active = new Set<SeamSetter<unknown>>();
85
+
86
+ /**
87
+ * Install a fake for the current test. Restoration is automatic: the
88
+ * tests/_preload.ts afterEach calls resetAllSeams(). Use this for
89
+ * file-scoped or beforeEach-scoped fakes (the common case, mirroring
90
+ * today's top-of-file mock.module blocks).
91
+ */
92
+ export function overrideSeam<T>(set: SeamSetter<T>, fake: T): void {
93
+ set(fake);
94
+ active.add(set as SeamSetter<unknown>);
95
+ }
96
+
97
+ /** Scoped swap → run → finally-restore, for fakes needed in one test only. */
98
+ export async function withSeam<T, R>(
99
+ set: SeamSetter<T>,
100
+ fake: T,
101
+ run: () => R | Promise<R>,
102
+ ): Promise<R> {
103
+ set(fake);
104
+ active.add(set as SeamSetter<unknown>);
105
+ try {
106
+ return await run();
107
+ } finally {
108
+ set(undefined);
109
+ active.delete(set as SeamSetter<unknown>);
110
+ }
111
+ }
112
+
113
+ /** Safety net: restore every active seam. Called by tests/_preload.ts. */
114
+ export function resetAllSeams(): void {
115
+ for (const set of active) set(undefined);
116
+ active.clear();
117
+ }
118
+ ```
119
+
120
+ ### Wiring into `tests/_preload.ts`
121
+
122
+ Two one-line additions to the existing harness (no new lifecycle machinery):
123
+
124
+ 1. `resetSingletons()` (tests/_preload.ts:294) gains `resetAllSeams();` — a
125
+ leaked seam from a previous file is cleared before every test, exactly like
126
+ `resetConfigCache()` / `resetLocalEmbedder()` today.
127
+ 2. The existing `afterEach` (tests/_preload.ts:329) gains `resetAllSeams();`
128
+ so a fake never survives past the test that installed it.
129
+
130
+ Because tests always install fakes via `overrideSeam`/`withSeam`, the preload
131
+ needs no knowledge of individual `_set…ForTests` functions — the registry is
132
+ the reset list. A test that calls a `_set…ForTests` setter directly (bypassing
133
+ the helper) is a review-reject; grep-able (`_set.*ForTests` outside
134
+ `tests/_helpers/seams.ts` usage must go through the helper).
135
+
136
+ Typical file migration shape (replaces a top-of-file `mock.module` block):
137
+
138
+ ```ts
139
+ import { overrideSeam } from "../_helpers/seams";
140
+ import { _setChatCompletionForTests } from "../../src/llm/client";
141
+
142
+ let chatResponder: (userContent: string) => string | Promise<string> = () => "";
143
+
144
+ beforeEach(() => {
145
+ overrideSeam(_setChatCompletionForTests, async (_conn, messages) => {
146
+ const user = messages.find((m) => m.role === "user");
147
+ return chatResponder(user?.content ?? "");
148
+ });
149
+ });
150
+ ```
151
+
152
+ Static imports of the module under test become safe again (no more
153
+ "`mock.module` must run before the module under test is imported" ordering
154
+ comments, no more dynamic-import contortions).
155
+
156
+ ---
157
+
158
+ ## Per-module seam designs
159
+
160
+ Ordered for sequential implementation, most-used-by-tests first. Implement one
161
+ module per commit: add seam → migrate its test files → delete the
162
+ `mock.module` blocks → gate green → commit.
163
+
164
+ ### 0. Helper first: `tests/_helpers/seams.ts` + `tests/_preload.ts` wiring
165
+
166
+ As specified above. Land with module 1 (the helper is exercised immediately).
167
+
168
+ ### 1. `src/llm/client` — 5 test files (easiest, highest fan-out)
169
+
170
+ - **Binding**: `let chatCompletionOverride: ChatCompletionFn | undefined;` next
171
+ to `chatCompletion` (client.ts:263). Real body → private `chatCompletionReal`.
172
+ - **Hook**:
173
+ ```ts
174
+ export function _setChatCompletionForTests(fake?: typeof chatCompletion): void;
175
+ ```
176
+ - Internal callers at client.ts:445 (`isLlmAvailable`) and :463
177
+ (`probeCapabilities`-adjacent) route through the delegator — intended.
178
+ - **Test migration** (all five spread the real module and override only
179
+ `chatCompletion`; the spread + `mock.module` block deletes, the responder
180
+ var + `overrideSeam` call replaces it):
181
+ - `tests/indexer/staleness-detect.test.ts` — deterministic validator fake →
182
+ `overrideSeam(_setChatCompletionForTests, fake)` in `beforeEach`. *(This
183
+ path no longer exists; no equivalently-named test was found by a later
184
+ search of `tests/indexer/` and `tests/integration/indexer/`.)*
185
+ - `tests/llm/memory-infer.test.ts` — `chatResponder` + `chatCalls` counter
186
+ pattern translates directly (see canonical migration above).
187
+ - `tests/llm/metadata-enhance.test.ts` — same shape.
188
+ - `tests/integration/commands/reflect-response-schema.test.ts` — same shape.
189
+ (Landed under `tests/integration/commands/`, not `tests/commands/`.)
190
+ - `tests/commands/consolidate/consolidate-judged-cache.test.ts` — same shape
191
+ (`chatCalls` recording preserved by the fake closure). *(This path no
192
+ longer exists; `tests/commands/consolidate/goldens-consolidate-journal.test.ts`
193
+ and `tests/commands/consolidate/goldens-consolidate-ops.test.ts` both use
194
+ `_setChatCompletionForTests` today and are the closest surviving
195
+ candidates, but neither is confirmed as this file's direct successor.)*
196
+ - No module state, no extra reset needed beyond the registry.
197
+
198
+ ### 2. `src/core/warn` — 3 test files
199
+
200
+ The module already has real hooks for all its *state*
201
+ (`setQuiet/resetQuiet/setVerbose/resetVerbose/setLogFile/clearLogFile`). Tests
202
+ mock it only to **capture output**. So the seam is a single sink intercept,
203
+ not 13 overridable functions:
204
+
205
+ - **Binding** (warn.ts, next to `quiet`/`verbose`/`logFilePath` at :20-22):
206
+ ```ts
207
+ export type WarnSinkForTests = (
208
+ level: "info" | "warn" | "error" | "warnVerbose",
209
+ args: unknown[],
210
+ ) => void;
211
+ let sinkOverride: WarnSinkForTests | undefined;
212
+ ```
213
+ - **Hook**:
214
+ ```ts
215
+ export function _setWarnSinkForTests(fake?: WarnSinkForTests): void;
216
+ ```
217
+ - Each of `info`/`warn`/`error`/`warnVerbose` gets a first line:
218
+ `if (sinkOverride) { sinkOverride("<level>", args); return; }`
219
+ Interception happens **before** the quiet/verbose gates — deliberately
220
+ matching today's `mock.module` full-replacement semantics (the existing
221
+ fakes capture every call regardless of flags). All other exports
222
+ (`setQuiet`, `isVerbose`, `getLogFile`, …) stay untouched — the real hooks
223
+ already serve tests.
224
+ - **Test migration**:
225
+ - `tests/llm/memory-infer.test.ts` — spread + override of `warn` →
226
+ `overrideSeam(_setWarnSinkForTests, (level, args) => { if (level === "warn") warnCalls.push(join(args)); })`.
227
+ - `tests/llm/metadata-enhance.test.ts` — same, also captures `warnVerbose`
228
+ level.
229
+ - `tests/agent/agent-builders.test.ts` — currently reimplements ALL 13
230
+ exports with parallel `mockedQuiet`/`mockedVerbose`/`mockedLogFile` state.
231
+ Migration = delete the whole reimplementation; use the REAL module's
232
+ `setQuiet`/`setVerbose` hooks for flag control plus the sink to capture
233
+ `warnings`. Assertions about gating move from "did console.warn fire" to
234
+ "what the sink captured + real `isQuiet()`/`isVerbose()`". Largest test
235
+ rewrite in this group but pure subtraction (~60 lines of fake module
236
+ deleted).
237
+
238
+ ### 3. `src/llm/embedder` — 3 test files
239
+
240
+ Facade module; several mocked names are re-exports of pure constants/math
241
+ that need **no seam** (`DEFAULT_LOCAL_MODEL` constant, `cosineSimilarity`
242
+ math — the real ones satisfy every test).
243
+
244
+ - **Binding** (embedder.ts, after the `_localEmbedder` singleton at :50):
245
+ ```ts
246
+ interface EmbedderOverridesForTests {
247
+ embed?: typeof embed;
248
+ embedBatch?: typeof embedBatch;
249
+ resolveEmbeddingModelId?: typeof resolveEmbeddingModelId;
250
+ checkEmbeddingAvailability?: typeof checkEmbeddingAvailability;
251
+ isTransformersAvailable?: () => boolean;
252
+ }
253
+ let embedderOverrides: EmbedderOverridesForTests | undefined;
254
+ ```
255
+ - **Hook**:
256
+ ```ts
257
+ export function _setEmbedderForTests(fakes?: EmbedderOverridesForTests): void;
258
+ ```
259
+ - Delegation: each listed export gains
260
+ `if (embedderOverrides?.embedBatch) return embedderOverrides.embedBatch(...)`
261
+ as its first line (bodies are already thin dispatchers; no `…Real` rename
262
+ needed except `isTransformersAvailable`, which is currently a re-export from
263
+ `./embedders/local` (embedder.ts:39) and must become a 3-line delegating
264
+ wrapper in the facade — importers unchanged).
265
+ - **Implementation-time check**: grep that consumers under these tests import
266
+ from the facade (`src/llm/embedder`), not `./embedders/local` directly; a
267
+ direct submodule import bypasses the facade seam. (indexer and setup do use
268
+ the facade per the facade's own doc comment.)
269
+ - **Test migration**:
270
+ - `tests/integration/indexer.test.ts` — spreads real via specifier
271
+ `'../../src/llm/embedder.js'` with a mutable `embedBatch` impl var →
272
+ `overrideSeam(_setEmbedderForTests, { embedBatch: (...) => impl(...) })`.
273
+ Kills the `.js`-specifier fragility entirely. *(This flat path no longer
274
+ exists; indexer tests now live under `tests/indexer/` and
275
+ `tests/integration/indexer/` as multiple smaller files.)*
276
+ - `tests/commands/improve/dedup-cache-wiring.test.ts` — mocks
277
+ `{embedBatch, resolveEmbeddingModelId, cosineSimilarity}` WITHOUT
278
+ spreading → seam `{embedBatch, resolveEmbeddingModelId}`; real
279
+ `cosineSimilarity` is used as-is. Deletes the "mock.module must run before
280
+ import" dance; `dedup` can be statically imported. *(This path no longer
281
+ exists; `tests/integration/commands/consolidate/consolidate-promote-dedup.test.ts`
282
+ is the closest surviving dedup-focused test but is not confirmed as this
283
+ file's direct successor.)*
284
+ - `tests/integration/setup-run.test.ts` — replaces with only
285
+ `{DEFAULT_LOCAL_MODEL, isTransformersAvailable, checkEmbeddingAvailability}`
286
+ → seam `{isTransformersAvailable, checkEmbeddingAvailability}`; real
287
+ `DEFAULT_LOCAL_MODEL`.
288
+ - Existing state hooks (`resetLocalEmbedder`, `clearEmbeddingCache`) already
289
+ run in `_preload.ts:resetSingletons` — unchanged.
290
+
291
+ ### 4. `@huggingface/transformers` — 2 test files (seam lives in src, NOT the package)
292
+
293
+ Third-party, but the ONLY consumer is the dynamic
294
+ `await import("@huggingface/transformers")` inside
295
+ `LocalEmbedder.getPipeline` (src/llm/embedders/local.ts:220). That import is a
296
+ perfect internal binding:
297
+
298
+ - **Binding** (embedders/local.ts, module level):
299
+ ```ts
300
+ type TransformersLoader = () => Promise<{ pipeline: unknown }>;
301
+ const realTransformersLoader: TransformersLoader = () =>
302
+ import("@huggingface/transformers") as Promise<{ pipeline: unknown }>;
303
+ let transformersLoader: TransformersLoader = realTransformersLoader;
304
+ ```
305
+ - **Hook**:
306
+ ```ts
307
+ export function _setTransformersLoaderForTests(fake?: TransformersLoader): void {
308
+ transformersLoader = fake ?? realTransformersLoader;
309
+ }
310
+ ```
311
+ Re-export from the facade `src/llm/embedder.ts` so tests import one module.
312
+ - `getPipeline` replaces the inline `await import(...)` with
313
+ `await transformersLoader()`. The MODULE_NOT_FOUND error-shaping branch
314
+ (local.ts:222-241) is untouched — a fake loader that throws a
315
+ `Cannot find module` error still exercises the binary-hint path.
316
+ - **Test migration**: `tests/integration/embedder.test.ts` and
317
+ `tests/integration/embedding-model-config.test.ts` both mock
318
+ `{pipeline}` today and already call `resetLocalEmbedder()` in `beforeEach`
319
+ (required — the cached `pipelinePromise` must not carry a previous loader's
320
+ result; the preload also resets it). Their fake `pipeline` factory moves
321
+ verbatim into `overrideSeam(_setTransformersLoaderForTests, async () => ({ pipeline: fakePipeline }))`.
322
+ - `isTransformersAvailable` (local.ts:289, `resolveModule` probe) is NOT
323
+ seamed here; the facade seam from module 3 covers the one test that fakes it.
324
+
325
+ ### 5. `src/setup/registry-stash-loader` — 1 test file
326
+
327
+ - **Binding**: real body of `loadSetupStashes` (registry-stash-loader.ts:82)
328
+ → private `loadSetupStashesReal`; exported delegator.
329
+ - **Hook**:
330
+ ```ts
331
+ export function _setLoadSetupStashesForTests(fake?: typeof loadSetupStashes): void;
332
+ ```
333
+ - `DEFAULT_SELECTED_STASH_IDS` is a plain exported const — no seam; the test's
334
+ mocked copy is deleted and the real constant used.
335
+ - **Test migration**: `tests/setup-wizard.test.ts:64` block →
336
+ `overrideSeam(_setLoadSetupStashesForTests, async () => fakeStashEntries)`.
337
+ (Alternative considered: `withMockedFetch` against the real fetch path —
338
+ rejected: couples the test to the registry JSON wire shape for no gain.)
339
+ NOTE: setup-wizard.test.ts also fakes `@clack/prompts` — originally deferred
340
+ as a kept `mock.module`, now migrated onto the `src/cli/clack` seam (see the
341
+ resolved deferred section at the bottom).
342
+
343
+ ### 6. `src/tasks/backends` — 1 test file
344
+
345
+ `selectBackend(options)` already accepts injected backends
346
+ (src/tasks/backends/index.ts:59-69) but production callers invoke it with no
347
+ args — threading a fake backend through `akmTasksAdd` would be a call-site
348
+ change (forbidden). Module seam instead:
349
+
350
+ - **Binding** (index.ts):
351
+ ```ts
352
+ interface BackendsOverridesForTests {
353
+ selectBackend?: typeof selectBackend;
354
+ backendNameForPlatform?: typeof backendNameForPlatform;
355
+ }
356
+ let backendsOverrides: BackendsOverridesForTests | undefined;
357
+ ```
358
+ - **Hook**:
359
+ ```ts
360
+ export function _setBackendsForTests(fakes?: BackendsOverridesForTests): void;
361
+ ```
362
+ Both exports gain a first-line delegation check (bodies are small switches;
363
+ no rename needed).
364
+ - **Test migration**: `tests/integration/commands/tasks-write-target.test.ts` →
365
+ `overrideSeam(_setBackendsForTests, { selectBackend: () => fakeBackend, backendNameForPlatform: () => "cron" })`.
366
+ (Landed under `tests/integration/commands/`, not `tests/commands/`.)
367
+
368
+ ### 7-14. The `tests/integration/setup-run.test.ts` cluster (one file, 74 `mock.module` calls across 9 near-identical blocks)
369
+
370
+ This file repeats the same ~10 mocks in every test block. Migrate it LAST, in
371
+ one pass, after all seams below exist. Each block's mock stanza collapses to a
372
+ single `installSetupSeams(overrides)` local helper (test-file-local function
373
+ calling `overrideSeam` per seam) — deleting ~600 lines of repeated mock
374
+ boilerplate. Per module:
375
+
376
+ #### 7. `src/core/config/config` — NO SEAM (subtract the mock)
377
+
378
+ `resetConfigCache()` exists (config.ts:124) and already runs in the preload.
379
+ The module is XDG-env-driven and the sandbox helpers
380
+ (`withIsolatedAkmStorage` / `sandboxXdgConfigHome` in tests/_helpers/sandbox.ts)
381
+ already isolate it. Migration: delete the mock; let the wizard read/write the
382
+ REAL config in the sandboxed XDG home. Assertions on "what was saved" read the
383
+ config file (or `loadUserConfig()`) from the sandbox instead of spying on
384
+ `saveConfig`. Fallback ONLY if an assertion truly needs call interception: a
385
+ minimal `_setSaveConfigForTests` — decide during implementation, default is
386
+ no seam.
387
+
388
+ #### 8. `src/core/paths` — NO SEAM (subtract the mock)
389
+
390
+ Pure functions of `process.env`; `getConfigDir`/`getDataDir` already take
391
+ `(env, platform)`. With sandboxed XDG env vars the mock is dead weight.
392
+ Migration: delete the mock block; assert against the sandbox paths.
393
+
394
+ #### 9. `src/setup/detect` — seam for the two network/host probes
395
+
396
+ - **Binding** (detect.ts):
397
+ ```ts
398
+ interface DetectOverridesForTests {
399
+ detectOllama?: typeof detectOllama;
400
+ detectAgentPlatforms?: typeof detectAgentPlatforms;
401
+ }
402
+ let detectOverrides: DetectOverridesForTests | undefined;
403
+ ```
404
+ - **Hook**: `export function _setDetectForTests(fakes?: DetectOverridesForTests): void;`
405
+ - `detectOllama` (detect.ts:44, network probe) and `detectAgentPlatforms`
406
+ (detect.ts:157, host scan) get first-line delegation. `detectLMStudio`,
407
+ `scanProviderEnvVars` (already env-injected), `pickDefaultModel` untouched.
408
+
409
+ #### 10. `src/commands/sources/init` — seam for `akmInit`
410
+
411
+ Single export, no state. Real body → `akmInitReal`; delegator +
412
+ `export function _setAkmInitForTests(fake?: typeof akmInit): void;`.
413
+
414
+ #### 11. `src/indexer/indexer` — seam for `akmIndex`
415
+
416
+ Today's full-module replacement leaves every OTHER export (e.g.
417
+ `buildFileBasenameMap`) `undefined` for the module under test — a latent bug
418
+ the seam fixes for free. Real body → `akmIndexReal`; delegator +
419
+ `export function _setAkmIndexForTests(fake?: typeof akmIndex): void;`. All
420
+ other exports stay real.
421
+
422
+ #### 12. `src/indexer/db/db` — DELETE THE STALE MOCK (no seam, decide fallback in-flight)
423
+
424
+ The current mock is **broken**: it exports `openDatabase`, but the real module
425
+ and setup.ts:41 use `openIndexDatabase` — under the mock, `openIndexDatabase`
426
+ is `undefined` and the vec-probe try/catch (setup.ts:582-600) silently
427
+ swallows the TypeError. The test has never exercised this path. Migration:
428
+ delete the mock and let the vec probe open a REAL index DB inside the
429
+ sandboxed tmp dirs (this is an integration test; that is the honest behavior
430
+ and un-swallows the probe). Fallback only if a real open proves too slow or
431
+ vec-extension-dependent in CI: minimal
432
+ `_setIndexDbForTests({ openIndexDatabase?, isVecAvailable? })` delegators.
433
+ Default is deletion. *(Landed as deletion, confirmed: `tests/integration/setup-run.test.ts`
434
+ has zero `mock.module` calls today. `src/indexer/db/db.ts` no longer exists —
435
+ `openIndexDatabase` now lives in `src/storage/repositories/index-connection.ts`
436
+ — though `setup-run.test.ts` still has a stale comment naming the old path.)*
437
+
438
+ #### 13. `src/integrations/agent` — seam in the DEFINING module
439
+
440
+ The test spreads the real barrel and overrides `detectAgentCliProfiles` +
441
+ `pickDefaultAgentProfile`. Both are defined in
442
+ `src/integrations/agent/detect.ts` (:81, :106) and re-exported by the barrel
443
+ (index.ts:33). **The seam must live in `detect.ts`** (a barrel re-export of a
444
+ delegator carries the seam automatically; seaming the barrel itself would not
445
+ affect direct `./detect` importers).
446
+
447
+ - **Binding** (integrations/agent/detect.ts):
448
+ ```ts
449
+ interface AgentDetectOverridesForTests {
450
+ detectAgentCliProfiles?: typeof detectAgentCliProfiles;
451
+ pickDefaultAgentProfile?: typeof pickDefaultAgentProfile;
452
+ }
453
+ ```
454
+ - **Hook**: `export function _setAgentDetectForTests(fakes?: AgentDetectOverridesForTests): void;`
455
+ (re-exported from the barrel so tests import one module).
456
+
457
+ #### 14. `src/commands/tasks/default-tasks` — seam for the three mocked exports
458
+
459
+ `registerDefaultTasks(deps)` and `isCiEnvironment(env = process.env)` already
460
+ have DI params, but setup.ts:19 imports and calls them bare — using the DI
461
+ params would be a call-site change (forbidden). Module seam:
462
+
463
+ - **Binding** (default-tasks.ts):
464
+ ```ts
465
+ interface DefaultTasksOverridesForTests {
466
+ detectServerDefault?: typeof detectServerDefault;
467
+ isCiEnvironment?: typeof isCiEnvironment;
468
+ registerDefaultTasks?: typeof registerDefaultTasks;
469
+ }
470
+ ```
471
+ - **Hook**: `export function _setDefaultTasksForTests(fakes?: DefaultTasksOverridesForTests): void;`
472
+ - First-line delegation on all three (note default-tasks.ts:179/:184 call
473
+ `isCiEnvironment()`/`detectServerDefault()` internally — through the
474
+ delegators, so overriding `detectServerDefault` alone still steers a real
475
+ `registerDefaultTasks`, which today's all-or-nothing mock cannot do).
476
+ - The existing `installDefaultTasksMock` beforeEach helper in setup-run
477
+ becomes an `overrideSeam` call.
478
+
479
+ ---
480
+
481
+ ## Deferred — RESOLVED (deferral is over)
482
+
483
+ ### `@clack/prompts` — src seam SHIPPED (`src/cli/clack.ts`); `mock.module` is at ZERO
484
+
485
+ Originally deferred because seaming a third-party package needs a src-side
486
+ wrapper module and the guardrails forbid new wrappers. The deferral was
487
+ overturned by an adversarial review that BLOCKED landing the branch on proof:
488
+
489
+ - **The leak proof made zero-mock mandatory.** A two-file probe showed Bun
490
+ `mock.module` registrations ARE visible in later test files in the same
491
+ process, and the preload's `mock.restore()` does NOT clear them. `--isolate`
492
+ was deleted on this branch (63a58d4a), and that deletion is only sound at
493
+ ZERO `mock.module` — three clack mocks was three too many.
494
+ - **A latent restore bug existed by construction:** setup-scheduled-tasks
495
+ captured `REAL_CLACK` via `await import("@clack/prompts")` AFTER its fake
496
+ was installed, so the `afterAll` "restore" re-registered the fake. The
497
+ mock/restore dance cannot be made safe; only a seam deletes the bug class.
498
+
499
+ Resolution (same pattern as the `@huggingface/transformers` loader seam in
500
+ `src/llm/embedders/local.ts` — a third-party module gets its seam in src):
501
+
502
+ - `src/cli/clack.ts` re-exports the clack surface the codebase uses (`intro`,
503
+ `outro`, `cancel`, `confirm`, `select`, `multiselect`, `text`, `spinner`,
504
+ `note`, `isCancel`, `log`) through call-time delegators over ONE binding,
505
+ with the canonical single `_setClackForTests(fake | undefined)` hook.
506
+ - The four src importers (setup.ts, cli/confirm.ts, sources/add-cli.ts,
507
+ sources/stash-cli.ts) switched their import specifier to the seam —
508
+ import-specifier changes only, zero signature/behavior changes.
509
+ - The three test files migrated onto `overrideSeam(_setClackForTests, …)`;
510
+ the REAL_CLACK capture/restore blocks and cache-busting dynamic module
511
+ reloads were deleted (static imports are safe again).
512
+
513
+ Standing rule: `mock.module(` must never reappear in `tests/` (grep-enforced
514
+ at review; the preload comment documents why). New third-party dependencies
515
+ that tests need to fake get a src-side seam module like this one.
516
+
517
+ ---
518
+
519
+ ## Implementation order (one commit per numbered step, gate green each time)
520
+
521
+ 1. `tests/_helpers/seams.ts` + `tests/_preload.ts` wiring **+** `src/llm/client`
522
+ seam, migrating its 5 test files (proves the whole pattern end-to-end on the
523
+ easiest group).
524
+ 2. `src/core/warn` sink seam — 3 files (includes the agent-builders rewrite).
525
+ 3. `src/llm/embedder` facade seam — indexer.test.ts + dedup-cache-wiring
526
+ migrations (setup-run's embedder block waits for step 7).
527
+ 4. `@huggingface/transformers` loader seam in `embedders/local.ts` — 2 files.
528
+ 5. `src/setup/registry-stash-loader` — setup-wizard partial migration
529
+ (clack mock stays).
530
+ 6. `src/tasks/backends` — tasks-write-target.
531
+ 7. setup-run cluster, one seam-module at a time inside the single file:
532
+ add seams for detect / init / indexer / agent-detect / default-tasks;
533
+ delete the config + paths + indexer-db mocks (sandbox/real replacements);
534
+ dedupe the clack mock; collapse the 9 stanzas into `installSetupSeams()`.
535
+
536
+ Per-commit gate: `bun run check` (types + lint + unit/integration per repo
537
+ release-gate rule) — 0 errors, 0 warnings, 0 failures. Expected net effect:
538
+ ~15-20 added lines per seamed src module vs hundreds of deleted mock lines in
539
+ tests (setup-run alone should shrink by roughly half).