@design-ai/cli 4.59.0 → 4.61.0

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.
@@ -0,0 +1,435 @@
1
+ // Type declarations for the design-ai Agent SDK — Phase A (read-only).
2
+ // Hand-written, zero-build: no TypeScript toolchain is required to produce or
3
+ // ship these. They mirror the runtime shapes exercised by cli/sdk/index.test.mjs
4
+ // (the semver anchor). The eight exported names and their return-shape top-level
5
+ // keys are the stable, semver-covered surface; deeper nested shapes follow the
6
+ // same JSON the CLI's `--json` mode emits.
7
+ //
8
+ // Import: `import { route, search } from "@design-ai/cli/sdk";`
9
+
10
+ // ── Shared building blocks ────────────────────────────────────────────────
11
+
12
+ /** A curated reference (skill/agent/knowledge/command) and whether it exists on disk. */
13
+ export interface RouteReference {
14
+ path: string;
15
+ exists: boolean;
16
+ }
17
+
18
+ /** One reference-coverage tally (available vs total) within a route explanation. */
19
+ export interface CoverageCount {
20
+ available: number;
21
+ total: number;
22
+ }
23
+
24
+ export interface ReferenceCoverage {
25
+ command: CoverageCount;
26
+ skills: CoverageCount;
27
+ agents: CoverageCount;
28
+ knowledge: CoverageCount;
29
+ total: CoverageCount;
30
+ }
31
+
32
+ export interface ScoreBreakdownEntry {
33
+ label: string;
34
+ value: number;
35
+ }
36
+
37
+ export interface RouteExplanation {
38
+ summary: string;
39
+ scoreBreakdown: ScoreBreakdownEntry[];
40
+ referenceCoverage: ReferenceCoverage;
41
+ missingReferences: string[];
42
+ }
43
+
44
+ /** Advisory brief-relevant knowledge recalled by the shared lexical scorer. */
45
+ export interface RelatedKnowledge {
46
+ id: string;
47
+ score: number;
48
+ matchedTokens: string[];
49
+ }
50
+
51
+ export type RouteConfidence = "high" | "medium" | "low" | "catalog" | "forced";
52
+
53
+ export interface RouteResult {
54
+ id: string;
55
+ label: string;
56
+ score: number;
57
+ confidence: RouteConfidence;
58
+ matchedKeywords: string[];
59
+ command: RouteReference | null;
60
+ skills: RouteReference[];
61
+ agents: RouteReference[];
62
+ knowledge: RouteReference[];
63
+ keywords: string[];
64
+ explanation: RouteExplanation;
65
+ /** Present only when `route(brief, { explain: true })` is requested. */
66
+ relatedKnowledge?: RelatedKnowledge[];
67
+ /** Present only when a route was forced via `routeId`. */
68
+ forced?: boolean;
69
+ /** Present only when the fallback route was used. */
70
+ fallback?: boolean;
71
+ }
72
+
73
+ export interface RouteCatalog {
74
+ version: string;
75
+ routes: RouteResult[];
76
+ }
77
+
78
+ // ── prompt / pack ─────────────────────────────────────────────────────────
79
+
80
+ export interface ReferenceExample {
81
+ relPath: string;
82
+ title: string;
83
+ category: string;
84
+ score: number;
85
+ preview: string;
86
+ }
87
+
88
+ /** A single recalled learning-profile entry. */
89
+ export interface LearningRecallEntry {
90
+ id: string;
91
+ category: string;
92
+ score: number;
93
+ matchedTokens: string[];
94
+ text: string;
95
+ }
96
+
97
+ /** A single recalled shipped-corpus knowledge entry. */
98
+ export interface CorpusRecallEntry {
99
+ id: string;
100
+ score: number;
101
+ matchedTokens: string[];
102
+ }
103
+
104
+ /** Recall block embedded in a prompt plan when `withRecall: true`. */
105
+ export interface PromptRecall {
106
+ query: string;
107
+ mode: string;
108
+ candidateCount: number;
109
+ selectedCount: number;
110
+ selected: CorpusRecallEntry[];
111
+ markdown: string;
112
+ }
113
+
114
+ /** A stored learning-profile entry, as surfaced in a prompt plan's learning context. */
115
+ export interface LearningEntry {
116
+ id: string;
117
+ category: string;
118
+ text: string;
119
+ source: string;
120
+ createdAt: string;
121
+ }
122
+
123
+ export interface LearningSelection {
124
+ mode: string;
125
+ query: string;
126
+ candidateCount: number;
127
+ matchedCount: number;
128
+ queryTokenCount: number;
129
+ fallbackEnabled: boolean;
130
+ selectedCount: number;
131
+ fallbackCount: number;
132
+ selected: LearningRecallEntry[];
133
+ }
134
+
135
+ export interface LearningAuditSummary {
136
+ status: string;
137
+ failures: number;
138
+ warnings: number;
139
+ }
140
+
141
+ /** Learning context embedded in a prompt plan when `withLearning: true`. */
142
+ export interface LearningContext {
143
+ file: string;
144
+ category: string;
145
+ limit: number;
146
+ query: string;
147
+ selection: LearningSelection;
148
+ entries: LearningEntry[];
149
+ empty: boolean;
150
+ auditSummary: LearningAuditSummary;
151
+ markdown: string;
152
+ }
153
+
154
+ export interface PromptPlan {
155
+ brief: string;
156
+ version: string;
157
+ route: RouteResult;
158
+ slashCommand: string;
159
+ referenceExamples: ReferenceExample[];
160
+ filesToRead: string[];
161
+ checklist: string[];
162
+ qualityCommand: string;
163
+ prompt: string;
164
+ /** Present only when `prompt(brief, { withLearning: true })` is requested. */
165
+ learningContext?: LearningContext;
166
+ /** Present only when `prompt(brief, { withRecall: true })` is requested. */
167
+ recall?: PromptRecall;
168
+ }
169
+
170
+ export interface PackSummary {
171
+ totalFiles: number;
172
+ includedFiles: number;
173
+ truncatedFiles: number;
174
+ missingFiles: number;
175
+ usedBytes: number;
176
+ maxBytes: number;
177
+ remainingBytes: number;
178
+ usedRatio: number;
179
+ status: string;
180
+ }
181
+
182
+ export interface PackFile {
183
+ path: string;
184
+ bytes: number;
185
+ includedBytes: number;
186
+ included: boolean;
187
+ truncated: boolean;
188
+ content: string;
189
+ }
190
+
191
+ export interface Pack {
192
+ brief: string;
193
+ version: string;
194
+ maxBytes: number;
195
+ usedBytes: number;
196
+ summary: PackSummary;
197
+ warnings: string[];
198
+ plan: PromptPlan;
199
+ files: PackFile[];
200
+ markdown: string;
201
+ }
202
+
203
+ // ── search ────────────────────────────────────────────────────────────────
204
+
205
+ /** A plain line hit (default `search`). */
206
+ export interface SearchHit {
207
+ file: string;
208
+ lineNumber: number;
209
+ relPath: string;
210
+ preview: string;
211
+ }
212
+
213
+ /** A ranked hit (`search(query, { ranked: true })`) with a BM25 score. */
214
+ export interface RankedSearchHit {
215
+ relPath: string;
216
+ file: string;
217
+ score: number;
218
+ matchedTokens: string[];
219
+ preview: string;
220
+ }
221
+
222
+ // ── recall ────────────────────────────────────────────────────────────────
223
+
224
+ export interface RecallCorpus {
225
+ candidateCount: number;
226
+ selectedCount: number;
227
+ selected: CorpusRecallEntry[];
228
+ }
229
+
230
+ export interface RecallLearning {
231
+ mode: string;
232
+ candidateCount: number;
233
+ selectedCount: number;
234
+ selected: LearningRecallEntry[];
235
+ }
236
+
237
+ export interface RecallResult {
238
+ corpus: RecallCorpus;
239
+ learning: RecallLearning;
240
+ }
241
+
242
+ // ── check ─────────────────────────────────────────────────────────────────
243
+
244
+ export type CheckLevel = "pass" | "warn" | "fail";
245
+
246
+ export interface CheckResult {
247
+ id: string;
248
+ title: string;
249
+ level: CheckLevel;
250
+ passed: boolean;
251
+ message: string;
252
+ /** Present only when the check has supporting evidence. */
253
+ evidence?: string;
254
+ }
255
+
256
+ export interface CheckReport {
257
+ filePath: string;
258
+ /** Present only when a `routeId` was supplied. */
259
+ routeId?: string;
260
+ status: CheckLevel;
261
+ passes: number;
262
+ warnings: number;
263
+ failures: number;
264
+ total: number;
265
+ /** Human-readable ratio, e.g. "6/8". */
266
+ score: string;
267
+ results: CheckResult[];
268
+ }
269
+
270
+ export interface VersionReport {
271
+ cli: string;
272
+ corpus: string;
273
+ }
274
+
275
+ // ── Option objects ────────────────────────────────────────────────────────
276
+
277
+ export interface RouteOptions {
278
+ /** Max routes to return. Default 3, range 1–10. */
279
+ limit?: number;
280
+ /** Include the advisory explanation + related-knowledge section. Default false. */
281
+ explain?: boolean;
282
+ }
283
+
284
+ export interface PromptOptions {
285
+ /** Force a specific route id instead of scoring the brief. */
286
+ routeId?: string;
287
+ /** Include local learning-profile guidance (read-only; never recorded). Default false. */
288
+ withLearning?: boolean;
289
+ /** Restrict learning guidance to a category. */
290
+ learningCategory?: string;
291
+ /** Max learning entries. Range 1–100. */
292
+ learningLimit?: number;
293
+ /** Include a brief-relevant corpus recall block. Default false. */
294
+ withRecall?: boolean;
295
+ /** Max recall entries. Range 1–20. */
296
+ recallLimit?: number;
297
+ }
298
+
299
+ export interface PackOptions extends PromptOptions {
300
+ /** Byte budget for the bundled context files. Default 120000, range 1000–1000000. */
301
+ maxBytes?: number;
302
+ }
303
+
304
+ export interface SearchOptions {
305
+ /** Restrict to one corpus directory (must be a known search dir). */
306
+ dir?: string;
307
+ /** Max hits. Default 20, range 1–500. */
308
+ limit?: number;
309
+ /** Return ranked BM25 hits (score + matchedTokens) instead of plain line hits. */
310
+ ranked?: boolean;
311
+ }
312
+
313
+ export interface RecallOptions {
314
+ /** Max entries per side (corpus/learning). Range 1–20. */
315
+ limit?: number;
316
+ /** Restrict learning recall to a category. */
317
+ category?: string;
318
+ }
319
+
320
+ export interface CheckOptions {
321
+ /** Apply route-specific requirements for this route id. */
322
+ routeId?: string;
323
+ /** Validated for parity with the CLI; has no effect in-process (no exit code). */
324
+ strict?: boolean;
325
+ }
326
+
327
+ // ── The eight Phase A verbs ─────────────────────────────────────────────────
328
+
329
+ /** Recommend the best route(s), commands, skills, and knowledge for a brief. */
330
+ export function route(brief: string, opts?: RouteOptions): RouteResult[];
331
+
332
+ /** List the full route catalog independent of any brief. */
333
+ export function routes(): RouteCatalog;
334
+
335
+ /** Build a ready-to-use agent prompt plan from a brief. */
336
+ export function prompt(brief: string, opts?: PromptOptions): PromptPlan;
337
+
338
+ /** Build a prompt plan plus a bounded context-file bundle from a brief. */
339
+ export function pack(brief: string, opts?: PackOptions): Pack;
340
+
341
+ /** Search the local corpus. Returns ranked hits when `ranked: true`, else plain line hits. */
342
+ export function search(query: string, opts: SearchOptions & { ranked: true }): RankedSearchHit[];
343
+ export function search(query: string, opts?: SearchOptions): SearchHit[];
344
+
345
+ /** Recall brief-relevant corpus knowledge and local learning-profile entries. */
346
+ export function recall(query: string, opts?: RecallOptions): RecallResult;
347
+
348
+ /** Check a Markdown artifact's content for grounding, accessibility, and route requirements. */
349
+ export function check(artifact: string, opts?: CheckOptions): CheckReport;
350
+
351
+ /** Report the CLI package version and the plugin/corpus version. */
352
+ export function version(): VersionReport;
353
+
354
+ // ── learn.* (Phase B — local writes) ─────────────────────────────────────
355
+ //
356
+ // The ONLY writing verbs in the SDK. Each writes exclusively to the local
357
+ // learning profile (`DESIGN_AI_LEARNING_FILE` / defaultLearningFile()), never
358
+ // the network. There is no `filePath` or `now`/timestamp option on any of
359
+ // these — target a specific profile via the `DESIGN_AI_LEARNING_FILE` env
360
+ // var, exactly like the CLI. See docs/SDK.md "Phase B — local writes".
361
+
362
+ export interface LearnRememberOptions {
363
+ /** Learning-profile category. Default "preference". */
364
+ category?: string;
365
+ }
366
+
367
+ export interface LearnFeedbackOptions {
368
+ /** Feedback outcome; normalized by the underlying lib (e.g. "keep" | "avoid" | "improve"). Default "improve". */
369
+ outcome?: string;
370
+ /** Learning-profile category. Default "workflow". */
371
+ category?: string;
372
+ }
373
+
374
+ export interface LearnCaptureOptions {
375
+ /** Add route-specific check requirements for this route id before capturing. */
376
+ routeId?: string;
377
+ }
378
+
379
+ /** A single stored learning-profile entry, as written by learn.remember/feedback/captureFromCheck. */
380
+ export interface LearningProfileEntry {
381
+ id: string;
382
+ category: string;
383
+ text: string;
384
+ source: string;
385
+ createdAt: string;
386
+ }
387
+
388
+ export interface LearningProfile {
389
+ version: number;
390
+ updatedAt: string;
391
+ entries: LearningProfileEntry[];
392
+ }
393
+
394
+ /** Return shape of learn.remember() and learn.feedback(). */
395
+ export interface RememberResult {
396
+ file: string;
397
+ entry: LearningProfileEntry;
398
+ profile: LearningProfile;
399
+ }
400
+
401
+ /** An entry skipped by learn.captureFromCheck() because it duplicates an existing profile entry. */
402
+ export interface CaptureSkippedEntry {
403
+ category: string;
404
+ text: string;
405
+ source: string;
406
+ reason: string;
407
+ }
408
+
409
+ /** Return shape of learn.captureFromCheck() — the `captureLearningEntries` result. */
410
+ export interface CaptureResult {
411
+ file: string;
412
+ dryRun: boolean;
413
+ applied: boolean;
414
+ source: string;
415
+ candidateCount: number;
416
+ addedCount: number;
417
+ skippedCount: number;
418
+ count: number;
419
+ entries: LearningProfileEntry[];
420
+ skipped: CaptureSkippedEntry[];
421
+ }
422
+
423
+ /**
424
+ * Phase B: the explicit, opt-in LOCAL-WRITE namespace. `learn.remember`,
425
+ * `learn.feedback`, and `learn.captureFromCheck` are the only SDK verbs that
426
+ * write files — each writes only the local learning profile.
427
+ */
428
+ export declare const learn: {
429
+ /** Record a local learning-profile preference. */
430
+ remember(text: string, opts?: LearnRememberOptions): RememberResult;
431
+ /** Record feedback (keep/avoid/improve) as a local learning-profile entry. */
432
+ feedback(text: string, opts?: LearnFeedbackOptions): RememberResult;
433
+ /** Check a Markdown artifact and capture its non-pass results as local learning-profile entries. */
434
+ captureFromCheck(artifact: string, opts?: LearnCaptureOptions): CaptureResult;
435
+ };
package/cli/sdk/index.mjs CHANGED
@@ -1,19 +1,27 @@
1
- // design-ai Agent SDK — Phase A (read-only). See docs/AGENT-SDK.md and docs/SDK.md.
1
+ // design-ai Agent SDK — Phase A (read-only) + Phase B (local writes). See
2
+ // docs/AGENT-SDK.md and docs/SDK.md.
2
3
  //
3
4
  // A curated, semver-stable adapter over the same cli/lib functions the CLI and
4
5
  // MCP server call. Each verb validates its inputs, resolves the package root
5
6
  // the same way the CLI does, and returns a plain JSON-serializable object —
6
7
  // the same shape the CLI's --json mode emits. No network calls, no runtime
7
- // dependencies. Phase A performs no file writes: no learning-usage sidecar
8
- // writes, even from prompt/pack's withLearning option.
8
+ // dependencies. The 8 Phase A verbs below perform no file writes: no
9
+ // learning-usage sidecar writes, even from prompt/pack's withLearning option.
10
+ //
11
+ // Phase B adds a single `learn` namespace grouping the three explicit,
12
+ // opt-in LOCAL-WRITE verbs (`learn.remember`, `learn.feedback`,
13
+ // `learn.captureFromCheck`). This keeps the write boundary explicit: the 8
14
+ // Phase A verbs stay read-only and unchanged; `learn` is the only place the
15
+ // SDK writes files, and it only ever writes the local learning profile.
9
16
  //
10
17
  // Import path: `@design-ai/cli/sdk` (see the "exports" map in package.json).
11
18
  // `cli/lib/*` stays internal and unstable; this barrel is the only supported
12
19
  // public surface. Do not import `cli/lib/*.mjs` directly from outside this
13
- // package — only the 8 named exports below are covered by the semver
14
- // stability contract described in docs/SDK.md.
20
+ // package — only the 8 named function exports plus the `learn` namespace
21
+ // below are covered by the semver stability contract described in docs/SDK.md.
15
22
 
16
23
  export { check } from "./check-adapter.mjs";
24
+ export { learn } from "./learn-adapter.mjs";
17
25
  export { pack } from "./pack-adapter.mjs";
18
26
  export { prompt } from "./prompt-adapter.mjs";
19
27
  export { recall } from "./recall-adapter.mjs";
@@ -0,0 +1,100 @@
1
+ // SDK namespace: learn.*. See docs/AGENT-SDK.md (Phase B) and docs/SDK.md.
2
+ //
3
+ // The ONLY writing verbs in the SDK. Phase A (route, prompt, pack, search,
4
+ // recall, check, routes, version) stays read-only and unchanged — this
5
+ // namespace is the explicit, opt-in write boundary: every verb here writes
6
+ // only the local learning profile (`DESIGN_AI_LEARNING_FILE` /
7
+ // `defaultLearningFile()`), never the network. Consumers target a specific
8
+ // profile file via the `DESIGN_AI_LEARNING_FILE` env var, exactly like the
9
+ // CLI — there is no `filePath` option and no `now`/timestamp option; the
10
+ // underlying cli/lib functions use their own defaults (the env var and
11
+ // `new Date()`).
12
+
13
+ import { checkArtifactContent, buildCheckLearningCapture } from "../lib/check.mjs";
14
+ import { rememberLearning, recordLearningFeedback } from "../lib/learn-profile.mjs";
15
+ import { assertKnownRouteId } from "../lib/route.mjs";
16
+ import { optionalString, requireNonEmptyString, requireOptions } from "./validate.mjs";
17
+
18
+ /**
19
+ * Record a local learning-profile preference. Writes only the local learning
20
+ * profile (`DESIGN_AI_LEARNING_FILE` / `defaultLearningFile()`); never the
21
+ * network. Pure adapter over `rememberLearning` from cli/lib/learn-profile.mjs.
22
+ *
23
+ * @param {string} text - Preference text to remember.
24
+ * @param {{category?: string}} [opts]
25
+ * @returns {{file: string, entry: object, profile: object}} RememberResult.
26
+ */
27
+ function remember(text, opts = {}) {
28
+ requireNonEmptyString(text, "text");
29
+ const options = requireOptions(opts, "learn.remember");
30
+ const category = optionalString(options.category, "category", "preference");
31
+
32
+ return rememberLearning({
33
+ text,
34
+ category,
35
+ source: "sdk",
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Record feedback (keep/avoid/improve) as a local learning-profile entry.
41
+ * Writes only the local learning profile; never the network. Pure adapter
42
+ * over `recordLearningFeedback` from cli/lib/learn-profile.mjs.
43
+ *
44
+ * @param {string} text - Feedback text.
45
+ * @param {{outcome?: string, category?: string}} [opts]
46
+ * @returns {{file: string, entry: object, profile: object}} RememberResult.
47
+ */
48
+ function feedback(text, opts = {}) {
49
+ requireNonEmptyString(text, "text");
50
+ const options = requireOptions(opts, "learn.feedback");
51
+ // The lib normalizes the outcome itself (normalizeFeedbackOutcome); the SDK
52
+ // only validates that, if given, it is a string.
53
+ const outcome = optionalString(options.outcome, "outcome", "improve");
54
+ const category = optionalString(options.category, "category", "workflow");
55
+
56
+ return recordLearningFeedback({
57
+ text,
58
+ outcome,
59
+ category,
60
+ });
61
+ }
62
+
63
+ /**
64
+ * Check a Markdown artifact, then capture its non-pass results as local
65
+ * learning-profile entries. Writes only the local learning profile; never the
66
+ * network. Pure adapter over `checkArtifactContent` + `buildCheckLearningCapture`
67
+ * from cli/lib/check.mjs (the same path as the CLI's `check --learn --yes`).
68
+ *
69
+ * @param {string} artifact - Markdown artifact content to check and capture.
70
+ * @param {{routeId?: string}} [opts]
71
+ * @returns {object} CaptureResult — the `captureLearningEntries` return shape:
72
+ * { file, dryRun, applied, source, candidateCount, addedCount, skippedCount,
73
+ * count, entries, skipped }.
74
+ */
75
+ function captureFromCheck(artifact, opts = {}) {
76
+ requireNonEmptyString(artifact, "artifact");
77
+ const options = requireOptions(opts, "learn.captureFromCheck");
78
+
79
+ const routeId = optionalString(options.routeId, "routeId");
80
+ if (routeId) assertKnownRouteId(routeId, { allowEmpty: false });
81
+
82
+ const report = checkArtifactContent({
83
+ content: artifact,
84
+ filePath: "sdk",
85
+ routeId,
86
+ });
87
+
88
+ return buildCheckLearningCapture(report, { dryRun: false });
89
+ }
90
+
91
+ /**
92
+ * The Phase B local-write namespace. `learn.remember`, `learn.feedback`, and
93
+ * `learn.captureFromCheck` are the only SDK verbs that write files; all three
94
+ * write exclusively to the local learning profile. See docs/SDK.md.
95
+ */
96
+ export const learn = Object.freeze({
97
+ remember,
98
+ feedback,
99
+ captureFromCheck,
100
+ });
package/docs/AGENT-SDK.md CHANGED
@@ -75,9 +75,9 @@ Verification gates:
75
75
  - `npm run release:check` additions: packed-tarball smoke that imports `@design-ai/cli/sdk` from the installed package and exercises `route`/`search`(ranked)/`recall`, plus a registry-smoke parity check after publish.
76
76
  - `npm run release:metadata` unchanged (README stance preserved).
77
77
 
78
- ### Phase B — optional, explicit local writes
78
+ ### Phase B — explicit local writes (shipped)
79
79
 
80
- Only if demand appears: opt-in adapters that mirror the CLI's local-write commands (`learn.remember`, `learn.feedback`, `check` with capture), each requiring an explicit option and writing only the local learning profile, never the network. Deferred until an adopter needs it; Phase A is read-only.
80
+ Implemented as a single `learn` namespace export grouping three opt-in adapters that mirror the CLI's local-write commands: `learn.remember`, `learn.feedback`, and `learn.captureFromCheck` (capture from a `check()` report). Each writes only the local learning profile (`DESIGN_AI_LEARNING_FILE` / `defaultLearningFile()`), never the network no `filePath` or `now`/timestamp option; consumers target a profile via the env var, exactly like the CLI. The 8 Phase A verbs are unchanged and stay read-only; `check()` in particular gets no capture option — capture is reached only through the separate, explicitly-named `learn.captureFromCheck`, keeping the write boundary visible at the call site. See [`SDK.md`](SDK.md#phase-b--local-writes) for the full reference.
81
81
 
82
82
  ## Integration points
83
83
 
@@ -202,8 +202,8 @@ This review answers the five open questions against the shipped Phase A implemen
202
202
 
203
203
  - **FU-1 (Q4, before Phase B):** Add Hangul-aware tokenization (CJK bigramming or particle stripping) in `cli/lib/lexical.mjs` behind Korean `learn --eval` checkpoints; regression-test that `버튼`, `버튼을`, `버튼이` converge on the same button docs. _Done (2026-07-03): Hangul runs >= 2 chars now emit overlapping character bigrams alongside the surface form; the review's zero-hit queries recover (`버튼` 0 → 3 ranked hits, `접근성이` 0 → 3), with unit regression coverage in `lexical.test.mjs`._
204
204
  - **FU-2 (Q2, before Phase B index-as-source-of-truth):** Key the corpus index by corpus digest / record `designAiPath` in the payload so multiple checkouts do not overwrite each other once the index is read for content rather than staleness. _Done (2026-07-03): sidecar format bumped to version 2 (auto-invalidating v1 files); the corpus payload records resolved `designAiPath` and the learning payload the resolved `learningFile`, and `index --status` reports `sourceMatch` and treats identity mismatch as not fresh._
205
- - **FU-3 (Q1, gates default promotion):** Land a ranked-search eval checkpoint so any future `--ranked` default promotion is evidence-backed and announced.
206
- - **FU-4 (Q5, Phase B):** Specify and implement `~/.design-ai/config.json` as the Phase B provider config home with per-invocation `--embeddings` still required.
205
+ - **FU-3 (Q1, gates default promotion):** Land a ranked-search eval checkpoint so any future `--ranked` default promotion is evidence-backed and announced. _Done (2026-07-05): `design-ai search --eval-template`/`--eval [--strict]` landed in `cli/lib/search-eval.mjs` and `cli/commands/search.mjs`, mirroring the route/prompt/pack/learn eval-checkpoint pattern; cases run through `rankedSearchCorpus` (the shipped ranked path), and the default template includes the three Korean particle-form regression cases from FU-1 (버튼/버튼을/접근성이) plus English cases, doubling as a retrieval-level Hangul-bigram regression check._
206
+ - **FU-4 (Q5, Phase B):** Specify and implement `~/.design-ai/config.json` as the Phase B provider config home with per-invocation `--embeddings` still required. _Done (2026-07-03, Phase B): `cli/lib/local-config.mjs` reads `~/.design-ai/config.json` (or `DESIGN_AI_CONFIG_FILE`); the config only supplies the provider — every invocation still requires the explicit `--embeddings` flag to arm it._
207
207
 
208
208
  ### Phase B gate: cleared-with-conditions
209
209
 
@@ -118,8 +118,17 @@ Cross-check any surface work against the release gate: `npm run audit` (8/8, lin
118
118
 
119
119
  ## Decision record
120
120
 
121
- - **Status:** proposedawaiting maintainer sign-off
122
- - **Date:** 2026-07-03
121
+ - **Status:** accepted and executed both recommended surfaces shipped
122
+ - **Date:** 2026-07-03 (proposed) / 2026-07-05 (executed)
123
123
  - **Deciders:** maintainer
124
124
  - **Supersedes:** none
125
125
  - **Related:** [`PRODUCT-READINESS.md`](PRODUCT-READINESS.md), [`external-status.md`](external-status.md), `AI-LEARNING-PHASE2.md`
126
+
127
+ ### Outcome (2026-07-05)
128
+
129
+ The recommendation was executed in order and both surfaces are live:
130
+
131
+ - **CLI deepening** shipped as v4.57.0 (local retrieval memory: `index`, `search --ranked`, `--with-recall`, `learn --recall`, opt-in embedding rerank) and v4.58.0 (`route --explain` related knowledge, recall quality, corpus depth). The dogfood friction this document identified — missing corpus and missing flags — is retired; retrieval spans every CLI/MCP surface.
132
+ - **Agent SDK** followed exactly on the stated revisit trigger (recall interface stable across two releases): design doc [`AGENT-SDK.md`](AGENT-SDK.md), then v4.59.0 (Phase A — read-only `@design-ai/cli/sdk`, 8 verbs) and v4.60.0 (TypeScript declarations + the opt-in `learn.*` local-write namespace).
133
+
134
+ The deferred surfaces (VS Code deepening, Web UI, Figma plugin) remain deferred with the same revisit triggers. The next-surface question is open again; re-evaluate against the criteria above when the next planning window opens, factoring in that the SDK now also serves programmatic adopters.