@design-ai/cli 4.56.0 → 4.57.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.
- package/.claude-plugin/plugin.json +109 -25
- package/CHANGELOG.md +36 -0
- package/README.ko.md +9 -26
- package/README.md +10 -23
- package/cli/bin/design-ai.mjs +1 -0
- package/cli/commands/help.mjs +7 -4
- package/cli/commands/index.mjs +292 -0
- package/cli/commands/learn-help.mjs +149 -0
- package/cli/commands/learn-print-profile.mjs +373 -0
- package/cli/commands/learn-print-restore.mjs +349 -0
- package/cli/commands/learn-print-signals.mjs +453 -0
- package/cli/commands/learn.mjs +60 -1281
- package/cli/commands/pack.mjs +7 -3
- package/cli/commands/prompt.mjs +7 -3
- package/cli/commands/search.mjs +94 -1
- package/cli/lib/dispatch.mjs +3 -0
- package/cli/lib/embedding-index.mjs +199 -0
- package/cli/lib/embedding-provider.mjs +121 -0
- package/cli/lib/embedding-rerank.mjs +52 -0
- package/cli/lib/learn-args.mjs +490 -0
- package/cli/lib/learn-backup.mjs +748 -0
- package/cli/lib/learn-curation.mjs +612 -0
- package/cli/lib/learn-eval.mjs +459 -0
- package/cli/lib/learn-profile.mjs +763 -0
- package/cli/lib/learn-select.mjs +223 -0
- package/cli/lib/learn-shared.mjs +158 -0
- package/cli/lib/learn-test-support.mjs +218 -0
- package/cli/lib/learn-usage.mjs +360 -0
- package/cli/lib/learn.mjs +83 -3694
- package/cli/lib/lexical.mjs +137 -0
- package/cli/lib/local-config.mjs +110 -0
- package/cli/lib/mcp-server.mjs +13 -3
- package/cli/lib/pack.mjs +31 -4
- package/cli/lib/prompt.mjs +42 -4
- package/cli/lib/recall.mjs +193 -0
- package/cli/lib/retrieval-index.mjs +0 -0
- package/cli/lib/search-ranked.mjs +170 -0
- package/cli/lib/search.mjs +11 -1
- package/cli/lib/signals-backlog-commands.mjs +673 -0
- package/cli/lib/signals-backlog.mjs +361 -0
- package/cli/lib/signals-eval.mjs +176 -0
- package/cli/lib/signals-readiness.mjs +247 -0
- package/cli/lib/signals-registry.mjs +377 -0
- package/cli/lib/signals-render.mjs +478 -0
- package/cli/lib/signals-shared.mjs +75 -0
- package/cli/lib/signals.mjs +16 -2337
- package/cli/lib/site-bundle-handoff-expected.mjs +173 -0
- package/cli/lib/site-bundle-handoff-runbook-action-summary.mjs +332 -0
- package/cli/lib/site-bundle-handoff-runbook-human-lines.mjs +167 -0
- package/cli/lib/site-bundle-handoff-runbook-maps.mjs +238 -0
- package/cli/lib/site-bundle-handoff-runbook-next-step.mjs +278 -0
- package/cli/lib/site-bundle-handoff-runbook.mjs +44 -931
- package/cli/lib/site-test-support.mjs +68 -0
- package/cli/lib/skill-proposals-apply-commands.mjs +135 -0
- package/cli/lib/skill-proposals-apply-contract.mjs +750 -0
- package/cli/lib/skill-proposals-apply-plan.mjs +118 -0
- package/cli/lib/skill-proposals-generate.mjs +298 -0
- package/cli/lib/skill-proposals-render.mjs +532 -0
- package/cli/lib/skill-proposals-review.mjs +262 -0
- package/cli/lib/skill-proposals.mjs +15 -2046
- package/cli/lib/workspace-args.mjs +83 -0
- package/cli/lib/workspace-git.mjs +169 -0
- package/cli/lib/workspace-learning.mjs +483 -0
- package/cli/lib/workspace-repo.mjs +139 -0
- package/cli/lib/workspace-report.mjs +283 -0
- package/cli/lib/workspace-test-support.mjs +99 -0
- package/cli/lib/workspace.mjs +30 -1056
- package/docs/AI-LEARNING-PHASE2.md +215 -0
- package/docs/DISTRIBUTION.ko.md +2 -2
- package/docs/DISTRIBUTION.md +2 -2
- package/docs/NEXT-SURFACE-DECISION.md +125 -0
- package/docs/PRODUCT-READINESS.md +3 -3
- package/docs/RELEASE-GATES.ko.md +35 -0
- package/docs/RELEASE-GATES.md +234 -0
- package/docs/ROADMAP.md +53 -8
- package/docs/external-status.md +6 -8
- package/docs/inspection-20260630.md +169 -0
- package/docs/integrations/design-ai-mcp-server.md +4 -4
- package/docs/reference/ant-design.md +413 -0
- package/docs/reference/awesome-design-md.md +439 -0
- package/docs/reference/mui.md +783 -0
- package/docs/reference/shadcn-ui.md +298 -0
- package/examples/component-accordion-actions.md +5 -5
- package/examples/component-accordion-details.md +5 -5
- package/examples/component-accordion-summary.md +5 -5
- package/examples/component-accordion.md +3 -3
- package/examples/component-affix.md +1 -1
- package/examples/component-alert-dialog.md +1 -1
- package/examples/component-alert-title.md +1 -1
- package/examples/component-alert.md +3 -3
- package/examples/component-anchor.md +1 -1
- package/examples/component-app-bar.md +1 -1
- package/examples/component-aspect-ratio.md +1 -1
- package/examples/component-auto-complete.md +2 -2
- package/examples/component-avatar-group.md +5 -5
- package/examples/component-avatar.md +3 -3
- package/examples/component-back-top.md +1 -1
- package/examples/component-backdrop.md +1 -1
- package/examples/component-badge.md +3 -3
- package/examples/component-border-beam.md +3 -3
- package/examples/component-bottom-navigation.md +1 -1
- package/examples/component-box.md +1 -1
- package/examples/component-breadcrumb.md +3 -3
- package/examples/component-button-base.md +3 -3
- package/examples/component-button-group.md +2 -2
- package/examples/component-button.md +3 -3
- package/examples/component-calendar.md +2 -2
- package/examples/component-card-actions.md +1 -1
- package/examples/component-card-content.md +1 -1
- package/examples/component-card-header.md +1 -1
- package/examples/component-card-media.md +1 -1
- package/examples/component-card.md +3 -3
- package/examples/component-carousel.md +2 -2
- package/examples/component-cascader.md +1 -1
- package/examples/component-chart.md +1 -1
- package/examples/component-checkbox.md +3 -3
- package/examples/component-click-away-listener.md +1 -1
- package/examples/component-code.md +1 -1
- package/examples/component-collapsible.md +1 -1
- package/examples/component-color-picker.md +1 -1
- package/examples/component-combobox.md +1 -1
- package/examples/component-command.md +1 -1
- package/examples/component-config-provider.md +3 -3
- package/examples/component-context-menu.md +1 -1
- package/examples/component-css-baseline.md +3 -3
- package/examples/component-date-picker.md +2 -2
- package/examples/component-descriptions.md +1 -1
- package/examples/component-dialog-actions.md +1 -1
- package/examples/component-dialog-content-text.md +1 -1
- package/examples/component-dialog-content.md +1 -1
- package/examples/component-dialog-title.md +1 -1
- package/examples/component-dialog.md +2 -2
- package/examples/component-divider.md +3 -3
- package/examples/component-drawer.md +3 -3
- package/examples/component-dropdown.md +3 -3
- package/examples/component-empty.md +2 -2
- package/examples/component-fade.md +1 -1
- package/examples/component-field.md +1 -1
- package/examples/component-filled-input.md +1 -1
- package/examples/component-flex.md +1 -1
- package/examples/component-float-button.md +2 -2
- package/examples/component-form-control-label.md +1 -1
- package/examples/component-form-control.md +1 -1
- package/examples/component-form-controls.md +1 -1
- package/examples/component-form-group.md +1 -1
- package/examples/component-form-helper-text.md +1 -1
- package/examples/component-form-label.md +1 -1
- package/examples/component-form.md +2 -2
- package/examples/component-grid.md +2 -2
- package/examples/component-grow.md +1 -1
- package/examples/component-hover-card.md +1 -1
- package/examples/component-icon-button.md +1 -1
- package/examples/component-icon.md +1 -1
- package/examples/component-image-list.md +1 -1
- package/examples/component-image.md +1 -1
- package/examples/component-input-adornment.md +1 -1
- package/examples/component-input-base.md +1 -1
- package/examples/component-input-number.md +1 -1
- package/examples/component-input-otp.md +2 -2
- package/examples/component-input.md +3 -3
- package/examples/component-item.md +1 -1
- package/examples/component-label.md +1 -1
- package/examples/component-layout.md +1 -1
- package/examples/component-link.md +1 -1
- package/examples/component-list-item-avatar.md +1 -1
- package/examples/component-list-item-button.md +1 -1
- package/examples/component-list-item-icon.md +1 -1
- package/examples/component-list-item-text.md +1 -1
- package/examples/component-list-item.md +1 -1
- package/examples/component-list-subheader.md +1 -1
- package/examples/component-list.md +2 -2
- package/examples/component-masonry.md +1 -1
- package/examples/component-mentions.md +1 -1
- package/examples/component-menu-item.md +1 -1
- package/examples/component-menu-list.md +1 -1
- package/examples/component-menu.md +1 -1
- package/examples/component-menubar.md +1 -1
- package/examples/component-message.md +1 -1
- package/examples/component-mobile-stepper.md +1 -1
- package/examples/component-modal.md +5 -5
- package/examples/component-navigation-menu.md +1 -1
- package/examples/component-notification.md +1 -1
- package/examples/component-outlined-input.md +1 -1
- package/examples/component-pagination.md +3 -3
- package/examples/component-paper.md +1 -1
- package/examples/component-popconfirm.md +1 -1
- package/examples/component-popover.md +3 -3
- package/examples/component-popper.md +1 -1
- package/examples/component-progress.md +2 -2
- package/examples/component-qr-code.md +1 -1
- package/examples/component-radio.md +3 -3
- package/examples/component-rate.md +2 -2
- package/examples/component-resizable.md +1 -1
- package/examples/component-result.md +1 -1
- package/examples/component-scroll-area.md +1 -1
- package/examples/component-segmented.md +3 -3
- package/examples/component-select.md +3 -3
- package/examples/component-separator.md +1 -1
- package/examples/component-sheet.md +1 -1
- package/examples/component-sidebar.md +1 -1
- package/examples/component-skeleton.md +3 -3
- package/examples/component-slide.md +1 -1
- package/examples/component-slider.md +3 -3
- package/examples/component-snackbar-content.md +1 -1
- package/examples/component-snackbar.md +1 -1
- package/examples/component-sonner.md +1 -1
- package/examples/component-space.md +1 -1
- package/examples/component-speed-dial-action.md +1 -1
- package/examples/component-speed-dial.md +1 -1
- package/examples/component-spin.md +2 -2
- package/examples/component-spinner.md +1 -1
- package/examples/component-splitter.md +2 -2
- package/examples/component-stack.md +1 -1
- package/examples/component-statistic.md +1 -1
- package/examples/component-step-button.md +4 -4
- package/examples/component-step-connector.md +4 -4
- package/examples/component-step-content.md +1 -1
- package/examples/component-step-icon.md +1 -1
- package/examples/component-step-label.md +1 -1
- package/examples/component-step.md +2 -2
- package/examples/component-steps.md +2 -2
- package/examples/component-swipeable-drawer.md +1 -1
- package/examples/component-switch.md +3 -3
- package/examples/component-tab-scroll-button.md +5 -5
- package/examples/component-tab.md +1 -1
- package/examples/component-table-body.md +1 -1
- package/examples/component-table-cell.md +1 -1
- package/examples/component-table-container.md +1 -1
- package/examples/component-table-footer.md +1 -1
- package/examples/component-table-head.md +1 -1
- package/examples/component-table-pagination.md +1 -1
- package/examples/component-table-row.md +1 -1
- package/examples/component-table-sort-label.md +1 -1
- package/examples/component-table.md +2 -2
- package/examples/component-tabs.md +3 -3
- package/examples/component-tag-badge.md +2 -2
- package/examples/component-tag.md +1 -1
- package/examples/component-textarea-autosize.md +1 -1
- package/examples/component-textarea.md +1 -1
- package/examples/component-time-picker.md +1 -1
- package/examples/component-timeline.md +1 -1
- package/examples/component-toast.md +4 -4
- package/examples/component-toggle-button.md +1 -1
- package/examples/component-toggle.md +1 -1
- package/examples/component-toolbar.md +1 -1
- package/examples/component-tooltip.md +3 -3
- package/examples/component-tour.md +1 -1
- package/examples/component-transfer.md +1 -1
- package/examples/component-tree-select.md +1 -1
- package/examples/component-tree.md +1 -1
- package/examples/component-typography.md +2 -2
- package/examples/component-upload.md +1 -1
- package/examples/component-watermark.md +1 -1
- package/examples/component-zoom.md +1 -1
- package/knowledge/COVERAGE.md +4 -3
- package/knowledge/components/INDEX.md +202 -202
- package/knowledge/patterns/brand-references.md +72 -72
- package/package.json +1 -1
- package/tools/audit/local-ci.py +16 -1
- package/tools/audit/package-smoke.py +611 -9
- package/tools/audit/registry-smoke.py +534 -0
- package/tools/audit/release-metadata.py +47 -1
- package/tools/audit/smoke_assertions.py +915 -13
- package/tools/migrations/refs-links-to-reference-pages.py +179 -0
|
@@ -34,6 +34,8 @@ from smoke_assertions import (
|
|
|
34
34
|
EXPECTED_HELP_ALIASES,
|
|
35
35
|
EXPECTED_NUMERIC_VALUE_SMOKES,
|
|
36
36
|
EXPECTED_PACK_MAX_BYTES,
|
|
37
|
+
EXPECTED_RANKED_SEARCH_LIMIT,
|
|
38
|
+
EXPECTED_RANKED_SEARCH_NOT_BUILT_NOTICE,
|
|
37
39
|
EXPECTED_REPOSITORY_URL,
|
|
38
40
|
EXPECTED_ROUTE_BRIEF,
|
|
39
41
|
EXPECTED_ROUTE_ID,
|
|
@@ -58,6 +60,9 @@ from smoke_assertions import (
|
|
|
58
60
|
assert_force_overwrite_replaced,
|
|
59
61
|
assert_functional_alias_smokes,
|
|
60
62
|
assert_help_topic_output,
|
|
63
|
+
assert_index_build_json,
|
|
64
|
+
assert_index_status_json,
|
|
65
|
+
assert_index_verify_json,
|
|
61
66
|
assert_install_doctor_lifecycle_output,
|
|
62
67
|
assert_install_output,
|
|
63
68
|
assert_list_catalog_output,
|
|
@@ -76,6 +81,8 @@ from smoke_assertions import (
|
|
|
76
81
|
assert_prompt_json_component_spec,
|
|
77
82
|
assert_prompt_markdown_body_component_spec,
|
|
78
83
|
assert_prompt_markdown_component_spec,
|
|
84
|
+
assert_ranked_search_determinism,
|
|
85
|
+
assert_ranked_search_json,
|
|
79
86
|
assert_route_catalog_json,
|
|
80
87
|
assert_route_explain_human_output,
|
|
81
88
|
assert_route_json_component_spec,
|
|
@@ -5155,6 +5162,97 @@ def assert_learning_stats_human(raw: str, *, context: str, cmd: list[str]) -> No
|
|
|
5155
5162
|
)
|
|
5156
5163
|
|
|
5157
5164
|
|
|
5165
|
+
def assert_learning_recall_json(
|
|
5166
|
+
raw: str,
|
|
5167
|
+
*,
|
|
5168
|
+
context: str,
|
|
5169
|
+
cmd: list[str],
|
|
5170
|
+
) -> None:
|
|
5171
|
+
assert_no_ansi(raw, cmd)
|
|
5172
|
+
try:
|
|
5173
|
+
payload = json.loads(raw)
|
|
5174
|
+
except json.JSONDecodeError as error:
|
|
5175
|
+
raise SystemExit(f"{context}: failed to parse learn recall JSON") from error
|
|
5176
|
+
|
|
5177
|
+
require_package_smoke(
|
|
5178
|
+
isinstance(payload, dict) and payload.get("query") == "korean mobile",
|
|
5179
|
+
context=context,
|
|
5180
|
+
cmd=cmd,
|
|
5181
|
+
message="learn recall JSON must echo the query",
|
|
5182
|
+
)
|
|
5183
|
+
corpus = payload.get("corpus")
|
|
5184
|
+
require_package_smoke(
|
|
5185
|
+
isinstance(corpus, dict)
|
|
5186
|
+
and isinstance(corpus.get("candidateCount"), int)
|
|
5187
|
+
and corpus.get("candidateCount") > 0
|
|
5188
|
+
and isinstance(corpus.get("selectedCount"), int)
|
|
5189
|
+
and isinstance(corpus.get("selected"), list),
|
|
5190
|
+
context=context,
|
|
5191
|
+
cmd=cmd,
|
|
5192
|
+
message="learn recall corpus block shape changed",
|
|
5193
|
+
)
|
|
5194
|
+
learning = payload.get("learning")
|
|
5195
|
+
require_package_smoke(
|
|
5196
|
+
isinstance(learning, dict)
|
|
5197
|
+
and learning.get("mode") == "brief-relevance"
|
|
5198
|
+
and learning.get("candidateCount") == 3
|
|
5199
|
+
and isinstance(learning.get("selected"), list),
|
|
5200
|
+
context=context,
|
|
5201
|
+
cmd=cmd,
|
|
5202
|
+
message="learn recall learning block shape changed",
|
|
5203
|
+
)
|
|
5204
|
+
selected = learning.get("selected")
|
|
5205
|
+
require_package_smoke(
|
|
5206
|
+
len(selected) >= 1
|
|
5207
|
+
and isinstance(selected[0], dict)
|
|
5208
|
+
and selected[0].get("id") == "learn-korean"
|
|
5209
|
+
and selected[0].get("category") == "korean"
|
|
5210
|
+
and isinstance(selected[0].get("matchedTokens"), list),
|
|
5211
|
+
context=context,
|
|
5212
|
+
cmd=cmd,
|
|
5213
|
+
message="learn recall should rank the Korean learning entry for a Korean query",
|
|
5214
|
+
)
|
|
5215
|
+
|
|
5216
|
+
|
|
5217
|
+
def assert_learning_recall_smoke(
|
|
5218
|
+
command_factory,
|
|
5219
|
+
profile_path: Path,
|
|
5220
|
+
*,
|
|
5221
|
+
env: dict[str, str],
|
|
5222
|
+
cwd: Path | None = None,
|
|
5223
|
+
context: str,
|
|
5224
|
+
) -> None:
|
|
5225
|
+
write_learning_stats_fixture(profile_path)
|
|
5226
|
+
|
|
5227
|
+
json_cmd = command_factory(
|
|
5228
|
+
"learn",
|
|
5229
|
+
"--recall",
|
|
5230
|
+
"korean mobile",
|
|
5231
|
+
"--file",
|
|
5232
|
+
str(profile_path),
|
|
5233
|
+
"--json",
|
|
5234
|
+
)
|
|
5235
|
+
json_result = run_plain(json_cmd, cwd=cwd, env=env)
|
|
5236
|
+
assert_learning_recall_json(json_result.stdout, context=f"{context} JSON", cmd=json_cmd)
|
|
5237
|
+
|
|
5238
|
+
before = profile_path.read_text(encoding="utf-8")
|
|
5239
|
+
human_cmd = command_factory("learn", "--recall", "korean mobile", "--file", str(profile_path))
|
|
5240
|
+
human_result = run_plain(human_cmd, cwd=cwd, env=env)
|
|
5241
|
+
require_package_smoke(
|
|
5242
|
+
"Recall (read-only)" in human_result.stdout
|
|
5243
|
+
and "Privacy: recall is read-only" in human_result.stdout,
|
|
5244
|
+
context=f"{context} human",
|
|
5245
|
+
cmd=human_cmd,
|
|
5246
|
+
message="learn recall human output missing read-only markers",
|
|
5247
|
+
)
|
|
5248
|
+
require_package_smoke(
|
|
5249
|
+
profile_path.read_text(encoding="utf-8") == before,
|
|
5250
|
+
context=f"{context} read-only",
|
|
5251
|
+
cmd=human_cmd,
|
|
5252
|
+
message="learn recall must not mutate the learning profile",
|
|
5253
|
+
)
|
|
5254
|
+
|
|
5255
|
+
|
|
5158
5256
|
def assert_learning_stats_smoke(
|
|
5159
5257
|
command_factory,
|
|
5160
5258
|
profile_path: Path,
|
|
@@ -8215,7 +8313,7 @@ def assert_learning_relevance_context(payload: dict[str, object], *, context: st
|
|
|
8215
8313
|
message="learning selection explanation should mark the relevant entry as a brief match",
|
|
8216
8314
|
)
|
|
8217
8315
|
require_package_smoke(
|
|
8218
|
-
|
|
8316
|
+
type(selected_entry.get("score")) in (int, float) and selected_entry.get("score") > 0,
|
|
8219
8317
|
context=context,
|
|
8220
8318
|
cmd=cmd,
|
|
8221
8319
|
message="learning selection explanation should include a positive relevance score",
|
|
@@ -8268,6 +8366,85 @@ def assert_learning_relevance_context(payload: dict[str, object], *, context: st
|
|
|
8268
8366
|
)
|
|
8269
8367
|
|
|
8270
8368
|
|
|
8369
|
+
def assert_recall_context(payload: dict[str, object], *, context: str, cmd: list[str]) -> None:
|
|
8370
|
+
recall = payload.get("recall")
|
|
8371
|
+
require_package_smoke(
|
|
8372
|
+
isinstance(recall, dict),
|
|
8373
|
+
context=context,
|
|
8374
|
+
cmd=cmd,
|
|
8375
|
+
message="recall should be present when --with-recall is used",
|
|
8376
|
+
)
|
|
8377
|
+
|
|
8378
|
+
require_package_smoke(
|
|
8379
|
+
recall.get("mode") == "lexical",
|
|
8380
|
+
context=context,
|
|
8381
|
+
cmd=cmd,
|
|
8382
|
+
message="recall should use the deterministic lexical scorer",
|
|
8383
|
+
)
|
|
8384
|
+
require_package_smoke(
|
|
8385
|
+
isinstance(recall.get("candidateCount"), int) and recall.get("candidateCount") > 0,
|
|
8386
|
+
context=context,
|
|
8387
|
+
cmd=cmd,
|
|
8388
|
+
message="recall should report the corpus candidate count it scanned",
|
|
8389
|
+
)
|
|
8390
|
+
selected_count = recall.get("selectedCount")
|
|
8391
|
+
require_package_smoke(
|
|
8392
|
+
isinstance(selected_count, int) and selected_count >= 1,
|
|
8393
|
+
context=context,
|
|
8394
|
+
cmd=cmd,
|
|
8395
|
+
message="recall should select at least one corpus file for the Button brief",
|
|
8396
|
+
)
|
|
8397
|
+
|
|
8398
|
+
selected = recall.get("selected")
|
|
8399
|
+
require_package_smoke(
|
|
8400
|
+
isinstance(selected, list) and len(selected) == selected_count and len(selected) >= 1,
|
|
8401
|
+
context=context,
|
|
8402
|
+
cmd=cmd,
|
|
8403
|
+
message="recall selected list should match the reported selected count",
|
|
8404
|
+
)
|
|
8405
|
+
top = selected[0]
|
|
8406
|
+
require_package_smoke(
|
|
8407
|
+
isinstance(top, dict) and isinstance(top.get("id"), str) and top.get("id"),
|
|
8408
|
+
context=context,
|
|
8409
|
+
cmd=cmd,
|
|
8410
|
+
message="recall selection should cite the corpus relPath as its id",
|
|
8411
|
+
)
|
|
8412
|
+
require_package_smoke(
|
|
8413
|
+
type(top.get("score")) in (int, float) and top.get("score") > 0,
|
|
8414
|
+
context=context,
|
|
8415
|
+
cmd=cmd,
|
|
8416
|
+
message="recall selection should include a positive relevance score",
|
|
8417
|
+
)
|
|
8418
|
+
require_package_smoke(
|
|
8419
|
+
isinstance(top.get("matchedTokens"), list) and len(top.get("matchedTokens")) >= 1,
|
|
8420
|
+
context=context,
|
|
8421
|
+
cmd=cmd,
|
|
8422
|
+
message="recall selection should list the matched brief tokens",
|
|
8423
|
+
)
|
|
8424
|
+
|
|
8425
|
+
markdown = recall.get("markdown")
|
|
8426
|
+
require_package_smoke(
|
|
8427
|
+
isinstance(markdown, str) and "## Recalled design knowledge" in markdown,
|
|
8428
|
+
context=context,
|
|
8429
|
+
cmd=cmd,
|
|
8430
|
+
message="recall markdown should carry the Recalled design knowledge section",
|
|
8431
|
+
)
|
|
8432
|
+
require_package_smoke(
|
|
8433
|
+
top.get("id") in markdown,
|
|
8434
|
+
context=context,
|
|
8435
|
+
cmd=cmd,
|
|
8436
|
+
message="recall markdown should cite the top recalled corpus file",
|
|
8437
|
+
)
|
|
8438
|
+
|
|
8439
|
+
prompt = payload.get("prompt")
|
|
8440
|
+
require_package_smoke(
|
|
8441
|
+
isinstance(prompt, str) and "## Recalled design knowledge" in prompt,
|
|
8442
|
+
context=context,
|
|
8443
|
+
cmd=cmd,
|
|
8444
|
+
message="prompt markdown should render the recalled knowledge section",
|
|
8445
|
+
)
|
|
8446
|
+
|
|
8447
|
+
|
|
8271
8448
|
def assert_learning_usage_payload(
|
|
8272
8449
|
payload: dict[str, object],
|
|
8273
8450
|
*,
|
|
@@ -11067,7 +11244,7 @@ def assert_learning_query_json(
|
|
|
11067
11244
|
require_package_smoke(
|
|
11068
11245
|
selected[0].get("id") == "learn-relevant"
|
|
11069
11246
|
and selected[0].get("reason") == "brief-match"
|
|
11070
|
-
and
|
|
11247
|
+
and type(selected[0].get("score")) in (int, float)
|
|
11071
11248
|
and selected[0].get("score") > 0,
|
|
11072
11249
|
context=context,
|
|
11073
11250
|
cmd=cmd,
|
|
@@ -11093,7 +11270,7 @@ def assert_learning_query_human(raw: str, *, context: str, cmd: list[str]) -> No
|
|
|
11093
11270
|
"Limit: 2",
|
|
11094
11271
|
"Explain: selection score, matched tokens, and reason",
|
|
11095
11272
|
"[accessibility] Prioritize keyboard accessibility details for Button component API specs",
|
|
11096
|
-
"matched
|
|
11273
|
+
"matched accessibility, keyboard",
|
|
11097
11274
|
"reason brief-match",
|
|
11098
11275
|
):
|
|
11099
11276
|
require_package_smoke(
|
|
@@ -11303,6 +11480,49 @@ def assert_learning_relevance_smoke(
|
|
|
11303
11480
|
cmd=pack_cmd,
|
|
11304
11481
|
)
|
|
11305
11482
|
|
|
11483
|
+
recall_prompt_cmd = command_factory(
|
|
11484
|
+
"prompt",
|
|
11485
|
+
EXPECTED_ROUTE_BRIEF,
|
|
11486
|
+
"--route",
|
|
11487
|
+
EXPECTED_ROUTE_ID,
|
|
11488
|
+
"--with-recall",
|
|
11489
|
+
"--recall-limit",
|
|
11490
|
+
"3",
|
|
11491
|
+
"--json",
|
|
11492
|
+
)
|
|
11493
|
+
recall_prompt_result = run_plain(recall_prompt_cmd, cwd=cwd, env=relevance_env)
|
|
11494
|
+
try:
|
|
11495
|
+
recall_prompt_payload = json.loads(recall_prompt_result.stdout)
|
|
11496
|
+
except json.JSONDecodeError as error:
|
|
11497
|
+
raise SystemExit(f"{context}: failed to parse prompt recall JSON") from error
|
|
11498
|
+
assert_recall_context(recall_prompt_payload, context=f"{context} prompt recall", cmd=recall_prompt_cmd)
|
|
11499
|
+
|
|
11500
|
+
recall_pack_cmd = command_factory(
|
|
11501
|
+
"pack",
|
|
11502
|
+
EXPECTED_ROUTE_BRIEF,
|
|
11503
|
+
"--route",
|
|
11504
|
+
EXPECTED_ROUTE_ID,
|
|
11505
|
+
"--with-recall",
|
|
11506
|
+
"--recall-limit",
|
|
11507
|
+
"3",
|
|
11508
|
+
"--max-bytes",
|
|
11509
|
+
str(EXPECTED_PACK_MAX_BYTES),
|
|
11510
|
+
"--json",
|
|
11511
|
+
)
|
|
11512
|
+
recall_pack_result = run_plain(recall_pack_cmd, cwd=cwd, env=relevance_env)
|
|
11513
|
+
try:
|
|
11514
|
+
recall_pack_payload = json.loads(recall_pack_result.stdout)
|
|
11515
|
+
except json.JSONDecodeError as error:
|
|
11516
|
+
raise SystemExit(f"{context}: failed to parse pack recall JSON") from error
|
|
11517
|
+
recall_plan = recall_pack_payload.get("plan")
|
|
11518
|
+
require_package_smoke(
|
|
11519
|
+
isinstance(recall_plan, dict),
|
|
11520
|
+
context=f"{context} pack recall",
|
|
11521
|
+
cmd=recall_pack_cmd,
|
|
11522
|
+
message="pack recall plan missing",
|
|
11523
|
+
)
|
|
11524
|
+
assert_recall_context(recall_plan, context=f"{context} pack recall plan", cmd=recall_pack_cmd)
|
|
11525
|
+
|
|
11306
11526
|
usage_human_cmd = command_factory(
|
|
11307
11527
|
"learn",
|
|
11308
11528
|
"--usage",
|
|
@@ -12454,6 +12674,258 @@ def assert_learning_relevance_smoke(
|
|
|
12454
12674
|
)
|
|
12455
12675
|
|
|
12456
12676
|
|
|
12677
|
+
def assert_index_roundtrip_smoke(
|
|
12678
|
+
command_factory,
|
|
12679
|
+
index_dir: Path,
|
|
12680
|
+
profile_path: Path,
|
|
12681
|
+
*,
|
|
12682
|
+
env: dict[str, str],
|
|
12683
|
+
cwd: Path | None = None,
|
|
12684
|
+
context: str,
|
|
12685
|
+
) -> None:
|
|
12686
|
+
index_dir.mkdir(parents=True, exist_ok=True)
|
|
12687
|
+
write_learning_relevance_fixture(profile_path)
|
|
12688
|
+
index_env = env.copy()
|
|
12689
|
+
index_env["DESIGN_AI_INDEX_DIR"] = str(index_dir)
|
|
12690
|
+
index_env["DESIGN_AI_LEARNING_FILE"] = str(profile_path)
|
|
12691
|
+
|
|
12692
|
+
build_cmd = command_factory("index", "--build", "--json")
|
|
12693
|
+
build_result = run_plain(build_cmd, cwd=cwd, env=index_env)
|
|
12694
|
+
assert_index_build_json(
|
|
12695
|
+
build_result.stdout,
|
|
12696
|
+
index_dir=str(index_dir),
|
|
12697
|
+
context=f"{context} build",
|
|
12698
|
+
cmd=build_cmd,
|
|
12699
|
+
)
|
|
12700
|
+
|
|
12701
|
+
status_cmd = command_factory("index", "--status", "--json")
|
|
12702
|
+
status_result = run_plain(status_cmd, cwd=cwd, env=index_env)
|
|
12703
|
+
assert_index_status_json(
|
|
12704
|
+
status_result.stdout,
|
|
12705
|
+
index_dir=str(index_dir),
|
|
12706
|
+
context=f"{context} status",
|
|
12707
|
+
cmd=status_cmd,
|
|
12708
|
+
)
|
|
12709
|
+
|
|
12710
|
+
verify_cmd = command_factory("index", "--verify", "--json")
|
|
12711
|
+
verify_result = run_plain(verify_cmd, cwd=cwd, env=index_env)
|
|
12712
|
+
assert_index_verify_json(
|
|
12713
|
+
verify_result.stdout,
|
|
12714
|
+
index_dir=str(index_dir),
|
|
12715
|
+
context=f"{context} verify",
|
|
12716
|
+
cmd=verify_cmd,
|
|
12717
|
+
)
|
|
12718
|
+
|
|
12719
|
+
|
|
12720
|
+
def assert_ranked_search_determinism_smoke(
|
|
12721
|
+
command_factory,
|
|
12722
|
+
index_dir: Path,
|
|
12723
|
+
*,
|
|
12724
|
+
env: dict[str, str],
|
|
12725
|
+
cwd: Path | None = None,
|
|
12726
|
+
context: str,
|
|
12727
|
+
) -> None:
|
|
12728
|
+
index_dir.mkdir(parents=True, exist_ok=True)
|
|
12729
|
+
ranked_env = env.copy()
|
|
12730
|
+
ranked_env["DESIGN_AI_INDEX_DIR"] = str(index_dir)
|
|
12731
|
+
|
|
12732
|
+
ranked_cmd = command_factory(
|
|
12733
|
+
"search",
|
|
12734
|
+
EXPECTED_CORPUS_SEARCH_QUERY,
|
|
12735
|
+
"--dir",
|
|
12736
|
+
"knowledge",
|
|
12737
|
+
"--limit",
|
|
12738
|
+
str(EXPECTED_RANKED_SEARCH_LIMIT),
|
|
12739
|
+
"--ranked",
|
|
12740
|
+
"--json",
|
|
12741
|
+
)
|
|
12742
|
+
first_result = run_plain(ranked_cmd, cwd=cwd, env=ranked_env)
|
|
12743
|
+
second_result = run_plain(ranked_cmd, cwd=cwd, env=ranked_env)
|
|
12744
|
+
assert_ranked_search_json(
|
|
12745
|
+
first_result.stdout,
|
|
12746
|
+
context=f"{context} payload",
|
|
12747
|
+
cmd=ranked_cmd,
|
|
12748
|
+
expected_notice=EXPECTED_RANKED_SEARCH_NOT_BUILT_NOTICE,
|
|
12749
|
+
)
|
|
12750
|
+
assert_ranked_search_determinism(
|
|
12751
|
+
first_result.stdout,
|
|
12752
|
+
second_result.stdout,
|
|
12753
|
+
context=context,
|
|
12754
|
+
cmd=ranked_cmd,
|
|
12755
|
+
)
|
|
12756
|
+
|
|
12757
|
+
|
|
12758
|
+
def assert_embeddings_off_by_default_smoke(
|
|
12759
|
+
command_factory,
|
|
12760
|
+
index_dir: Path,
|
|
12761
|
+
profile_path: Path,
|
|
12762
|
+
*,
|
|
12763
|
+
env: dict[str, str],
|
|
12764
|
+
cwd: Path | None = None,
|
|
12765
|
+
context: str,
|
|
12766
|
+
) -> None:
|
|
12767
|
+
"""Phase B is opt-in: a plain `index --build` never touches embeddings, and
|
|
12768
|
+
`index --status` reports `embeddings: null` (the feature is simply unused)."""
|
|
12769
|
+
index_dir.mkdir(parents=True, exist_ok=True)
|
|
12770
|
+
write_learning_relevance_fixture(profile_path)
|
|
12771
|
+
index_env = env.copy()
|
|
12772
|
+
index_env["DESIGN_AI_INDEX_DIR"] = str(index_dir)
|
|
12773
|
+
index_env["DESIGN_AI_LEARNING_FILE"] = str(profile_path)
|
|
12774
|
+
# Point at a config file that is guaranteed not to exist, rather than relying on
|
|
12775
|
+
# the ambient environment lacking DESIGN_AI_CONFIG_FILE — a real ~/.design-ai/
|
|
12776
|
+
# config.json on the machine running this smoke must not leak into the test.
|
|
12777
|
+
index_env["DESIGN_AI_CONFIG_FILE"] = str(index_dir.parent / "no-config-here" / "config.json")
|
|
12778
|
+
|
|
12779
|
+
build_cmd = command_factory("index", "--build", "--json")
|
|
12780
|
+
build_result = run_plain(build_cmd, cwd=cwd, env=index_env)
|
|
12781
|
+
build_payload = json.loads(build_result.stdout)
|
|
12782
|
+
if "embeddings" in build_payload:
|
|
12783
|
+
raise SystemExit(f"index build JSON after {context} unexpectedly ran provider execution with plain --build")
|
|
12784
|
+
|
|
12785
|
+
status_cmd = command_factory("index", "--status", "--json")
|
|
12786
|
+
status_result = run_plain(status_cmd, cwd=cwd, env=index_env)
|
|
12787
|
+
assert_index_status_json(
|
|
12788
|
+
status_result.stdout,
|
|
12789
|
+
index_dir=str(index_dir),
|
|
12790
|
+
context=f"{context} status",
|
|
12791
|
+
cmd=status_cmd,
|
|
12792
|
+
expect_embeddings=False,
|
|
12793
|
+
)
|
|
12794
|
+
|
|
12795
|
+
|
|
12796
|
+
def assert_search_embeddings_no_provider_fallback_smoke(
|
|
12797
|
+
command_factory,
|
|
12798
|
+
*,
|
|
12799
|
+
env: dict[str, str],
|
|
12800
|
+
cwd: Path | None = None,
|
|
12801
|
+
context: str,
|
|
12802
|
+
config_file: Path,
|
|
12803
|
+
) -> None:
|
|
12804
|
+
"""`search --ranked --embeddings` with no provider configured (flag or config)
|
|
12805
|
+
degrades to the lexical backend with a notice and exit code 0."""
|
|
12806
|
+
fallback_env = env.copy()
|
|
12807
|
+
# Same isolation rationale as assert_embeddings_off_by_default_smoke: point at a
|
|
12808
|
+
# config path guaranteed not to exist rather than trusting the ambient environment.
|
|
12809
|
+
fallback_env["DESIGN_AI_CONFIG_FILE"] = str(config_file)
|
|
12810
|
+
|
|
12811
|
+
search_cmd = command_factory(
|
|
12812
|
+
"search",
|
|
12813
|
+
EXPECTED_CORPUS_SEARCH_QUERY,
|
|
12814
|
+
"--dir",
|
|
12815
|
+
"knowledge",
|
|
12816
|
+
"--limit",
|
|
12817
|
+
str(EXPECTED_RANKED_SEARCH_LIMIT),
|
|
12818
|
+
"--ranked",
|
|
12819
|
+
"--embeddings",
|
|
12820
|
+
"--json",
|
|
12821
|
+
)
|
|
12822
|
+
result = run_plain(search_cmd, cwd=cwd, env=fallback_env)
|
|
12823
|
+
assert_ranked_search_json(
|
|
12824
|
+
result.stdout,
|
|
12825
|
+
context=f"{context} payload",
|
|
12826
|
+
cmd=search_cmd,
|
|
12827
|
+
expected_backend="lexical",
|
|
12828
|
+
)
|
|
12829
|
+
payload = json.loads(result.stdout)
|
|
12830
|
+
if "embedding provider" not in payload.get("notice", "") and "no embedding provider configured" not in payload.get("notice", ""):
|
|
12831
|
+
raise SystemExit(f"search ranked JSON after {context} fallback notice does not mention the missing provider")
|
|
12832
|
+
|
|
12833
|
+
|
|
12834
|
+
STUB_EMBEDDING_PROVIDER_SCRIPT = """
|
|
12835
|
+
const chunks = [];
|
|
12836
|
+
process.stdin.on("data", (c) => chunks.push(c));
|
|
12837
|
+
process.stdin.on("end", () => {
|
|
12838
|
+
const lines = Buffer.concat(chunks).toString("utf8").trim().split("\\n").filter(Boolean);
|
|
12839
|
+
for (const line of lines) {
|
|
12840
|
+
const { id } = JSON.parse(line);
|
|
12841
|
+
let seed = 0;
|
|
12842
|
+
for (const ch of id) seed = (seed * 31 + ch.charCodeAt(0)) % 997;
|
|
12843
|
+
const vector = [seed / 997, (seed + 1) / 997, (seed + 2) / 997];
|
|
12844
|
+
process.stdout.write(JSON.stringify({ id, vector }) + "\\n");
|
|
12845
|
+
}
|
|
12846
|
+
});
|
|
12847
|
+
"""
|
|
12848
|
+
|
|
12849
|
+
|
|
12850
|
+
def write_stub_embedding_provider(root: Path) -> Path:
|
|
12851
|
+
root.mkdir(parents=True, exist_ok=True)
|
|
12852
|
+
script_path = root / "stub-embedding-provider.mjs"
|
|
12853
|
+
script_path.write_text(STUB_EMBEDDING_PROVIDER_SCRIPT, encoding="utf-8")
|
|
12854
|
+
return script_path
|
|
12855
|
+
|
|
12856
|
+
|
|
12857
|
+
def assert_embedding_stub_provider_roundtrip_smoke(
|
|
12858
|
+
command_factory,
|
|
12859
|
+
index_dir: Path,
|
|
12860
|
+
profile_path: Path,
|
|
12861
|
+
script_dir: Path,
|
|
12862
|
+
*,
|
|
12863
|
+
env: dict[str, str],
|
|
12864
|
+
cwd: Path | None = None,
|
|
12865
|
+
context: str,
|
|
12866
|
+
) -> None:
|
|
12867
|
+
"""A tiny deterministic node stub provider round-trips through `index --build
|
|
12868
|
+
--embeddings --provider`, `index --status`, `index --verify`, and
|
|
12869
|
+
`search --ranked --embeddings` (installed-bin path; documents the --provider
|
|
12870
|
+
"command args" convention of splitting a single string on spaces)."""
|
|
12871
|
+
index_dir.mkdir(parents=True, exist_ok=True)
|
|
12872
|
+
write_learning_relevance_fixture(profile_path)
|
|
12873
|
+
script_path = write_stub_embedding_provider(script_dir)
|
|
12874
|
+
provider_flag = f"node {script_path}"
|
|
12875
|
+
|
|
12876
|
+
index_env = env.copy()
|
|
12877
|
+
index_env["DESIGN_AI_INDEX_DIR"] = str(index_dir)
|
|
12878
|
+
index_env["DESIGN_AI_LEARNING_FILE"] = str(profile_path)
|
|
12879
|
+
index_env.pop("DESIGN_AI_CONFIG_FILE", None)
|
|
12880
|
+
|
|
12881
|
+
build_cmd = command_factory("index", "--build", "--embeddings", "--provider", provider_flag, "--json")
|
|
12882
|
+
build_result = run_plain(build_cmd, cwd=cwd, env=index_env)
|
|
12883
|
+
build_payload = json.loads(build_result.stdout)
|
|
12884
|
+
if build_payload.get("embeddings", {}).get("ok") is not True:
|
|
12885
|
+
raise SystemExit(f"index build JSON after {context} embeddings did not build successfully")
|
|
12886
|
+
|
|
12887
|
+
status_cmd = command_factory("index", "--status", "--json")
|
|
12888
|
+
status_result = run_plain(status_cmd, cwd=cwd, env=index_env)
|
|
12889
|
+
assert_index_status_json(
|
|
12890
|
+
status_result.stdout,
|
|
12891
|
+
index_dir=str(index_dir),
|
|
12892
|
+
context=f"{context} status",
|
|
12893
|
+
cmd=status_cmd,
|
|
12894
|
+
expect_embeddings=True,
|
|
12895
|
+
)
|
|
12896
|
+
|
|
12897
|
+
verify_cmd = command_factory("index", "--verify", "--provider", provider_flag, "--json")
|
|
12898
|
+
verify_result = run_plain(verify_cmd, cwd=cwd, env=index_env)
|
|
12899
|
+
assert_index_verify_json(
|
|
12900
|
+
verify_result.stdout,
|
|
12901
|
+
index_dir=str(index_dir),
|
|
12902
|
+
context=f"{context} verify",
|
|
12903
|
+
cmd=verify_cmd,
|
|
12904
|
+
expect_embeddings_check=True,
|
|
12905
|
+
)
|
|
12906
|
+
|
|
12907
|
+
search_cmd = command_factory(
|
|
12908
|
+
"search",
|
|
12909
|
+
EXPECTED_CORPUS_SEARCH_QUERY,
|
|
12910
|
+
"--dir",
|
|
12911
|
+
"knowledge",
|
|
12912
|
+
"--limit",
|
|
12913
|
+
str(EXPECTED_RANKED_SEARCH_LIMIT),
|
|
12914
|
+
"--ranked",
|
|
12915
|
+
"--embeddings",
|
|
12916
|
+
"--provider",
|
|
12917
|
+
provider_flag,
|
|
12918
|
+
"--json",
|
|
12919
|
+
)
|
|
12920
|
+
search_result = run_plain(search_cmd, cwd=cwd, env=index_env)
|
|
12921
|
+
assert_ranked_search_json(
|
|
12922
|
+
search_result.stdout,
|
|
12923
|
+
context=f"{context} search payload",
|
|
12924
|
+
cmd=search_cmd,
|
|
12925
|
+
expected_backend="embeddings",
|
|
12926
|
+
)
|
|
12927
|
+
|
|
12928
|
+
|
|
12457
12929
|
def run_self_test() -> None:
|
|
12458
12930
|
context = "package smoke self-test"
|
|
12459
12931
|
cmd = ["design-ai", "doctor", "--json"]
|
|
@@ -14059,8 +14531,8 @@ def run_self_test() -> None:
|
|
|
14059
14531
|
{
|
|
14060
14532
|
"id": "learn-relevant",
|
|
14061
14533
|
"category": "accessibility",
|
|
14062
|
-
"score":
|
|
14063
|
-
"matchedTokens": ["
|
|
14534
|
+
"score": 2.114533,
|
|
14535
|
+
"matchedTokens": ["accessibility", "keyboard"],
|
|
14064
14536
|
"reason": "brief-match",
|
|
14065
14537
|
},
|
|
14066
14538
|
],
|
|
@@ -14126,7 +14598,7 @@ def run_self_test() -> None:
|
|
|
14126
14598
|
"",
|
|
14127
14599
|
"1. [accessibility] Prioritize keyboard accessibility details for Button component API specs",
|
|
14128
14600
|
" learn-relevant · 2026-05-22T00:00:01.000Z",
|
|
14129
|
-
" score
|
|
14601
|
+
" score 2.114533 · matched accessibility, keyboard · reason brief-match",
|
|
14130
14602
|
]),
|
|
14131
14603
|
context=context,
|
|
14132
14604
|
cmd=learn_query_human_cmd,
|
|
@@ -14140,7 +14612,7 @@ def run_self_test() -> None:
|
|
|
14140
14612
|
"Limit: 2",
|
|
14141
14613
|
"Explain: selection score, matched tokens, and reason",
|
|
14142
14614
|
"[accessibility] Prioritize keyboard accessibility details for Button component API specs",
|
|
14143
|
-
"matched
|
|
14615
|
+
"matched accessibility, keyboard",
|
|
14144
14616
|
]),
|
|
14145
14617
|
context=context,
|
|
14146
14618
|
cmd=learn_query_human_cmd,
|
|
@@ -14167,8 +14639,8 @@ def run_self_test() -> None:
|
|
|
14167
14639
|
{
|
|
14168
14640
|
"id": "learn-relevant",
|
|
14169
14641
|
"category": "accessibility",
|
|
14170
|
-
"score":
|
|
14171
|
-
"matchedTokens": ["
|
|
14642
|
+
"score": 2.114533,
|
|
14643
|
+
"matchedTokens": ["accessibility", "keyboard"],
|
|
14172
14644
|
"reason": "brief-match",
|
|
14173
14645
|
},
|
|
14174
14646
|
],
|
|
@@ -14319,6 +14791,59 @@ def run_self_test() -> None:
|
|
|
14319
14791
|
scope="package smoke",
|
|
14320
14792
|
)
|
|
14321
14793
|
|
|
14794
|
+
recall_payload = {
|
|
14795
|
+
"recall": {
|
|
14796
|
+
"query": EXPECTED_ROUTE_BRIEF,
|
|
14797
|
+
"mode": "lexical",
|
|
14798
|
+
"candidateCount": 42,
|
|
14799
|
+
"selectedCount": 2,
|
|
14800
|
+
"selected": [
|
|
14801
|
+
{
|
|
14802
|
+
"id": "knowledge/components/INDEX.md",
|
|
14803
|
+
"score": 9.5,
|
|
14804
|
+
"matchedTokens": ["button", "component"],
|
|
14805
|
+
},
|
|
14806
|
+
{
|
|
14807
|
+
"id": "knowledge/a11y/keyboard-and-focus.md",
|
|
14808
|
+
"score": 4.2,
|
|
14809
|
+
"matchedTokens": ["accessibility"],
|
|
14810
|
+
},
|
|
14811
|
+
],
|
|
14812
|
+
"markdown": (
|
|
14813
|
+
"## Recalled design knowledge\n\n"
|
|
14814
|
+
"- knowledge/components/INDEX.md\n"
|
|
14815
|
+
" - Component index\n"
|
|
14816
|
+
"- knowledge/a11y/keyboard-and-focus.md\n"
|
|
14817
|
+
" - Keyboard and focus"
|
|
14818
|
+
),
|
|
14819
|
+
},
|
|
14820
|
+
"prompt": (
|
|
14821
|
+
"Recalled corpus knowledge:\n\n"
|
|
14822
|
+
"## Recalled design knowledge\n\n"
|
|
14823
|
+
"- knowledge/components/INDEX.md"
|
|
14824
|
+
),
|
|
14825
|
+
}
|
|
14826
|
+
recall_cmd = ["design-ai", "prompt", EXPECTED_ROUTE_BRIEF, "--with-recall", "--json"]
|
|
14827
|
+
assert_recall_context(recall_payload, context=context, cmd=recall_cmd)
|
|
14828
|
+
expect_self_test_failure(
|
|
14829
|
+
lambda: assert_recall_context(
|
|
14830
|
+
{**recall_payload, "recall": {**recall_payload["recall"], "mode": "embeddings"}},
|
|
14831
|
+
context=context,
|
|
14832
|
+
cmd=recall_cmd,
|
|
14833
|
+
),
|
|
14834
|
+
expected="recall should use the deterministic lexical scorer",
|
|
14835
|
+
scope="package smoke",
|
|
14836
|
+
)
|
|
14837
|
+
expect_self_test_failure(
|
|
14838
|
+
lambda: assert_recall_context(
|
|
14839
|
+
{**recall_payload, "recall": {**recall_payload["recall"], "selectedCount": 0, "selected": []}},
|
|
14840
|
+
context=context,
|
|
14841
|
+
cmd=recall_cmd,
|
|
14842
|
+
),
|
|
14843
|
+
expected="recall should select at least one corpus file",
|
|
14844
|
+
scope="package smoke",
|
|
14845
|
+
)
|
|
14846
|
+
|
|
14322
14847
|
learning_usage_path = Path(tmp) / "learning.usage.json"
|
|
14323
14848
|
learning_usage_path.write_text(
|
|
14324
14849
|
json.dumps({
|
|
@@ -20565,6 +21090,40 @@ def smoke_tarball(tarball: Path) -> None:
|
|
|
20565
21090
|
env=smoke_env,
|
|
20566
21091
|
context="package smoke installed bin search human corpus",
|
|
20567
21092
|
)
|
|
21093
|
+
assert_index_roundtrip_smoke(
|
|
21094
|
+
lambda *args: [str(bin_path), *args],
|
|
21095
|
+
tmp_root / "installed-retrieval-index",
|
|
21096
|
+
tmp_root / "installed-index-learning.json",
|
|
21097
|
+
env=smoke_env,
|
|
21098
|
+
context="package smoke installed bin index round-trip",
|
|
21099
|
+
)
|
|
21100
|
+
assert_ranked_search_determinism_smoke(
|
|
21101
|
+
lambda *args: [str(bin_path), *args],
|
|
21102
|
+
tmp_root / "installed-ranked-index",
|
|
21103
|
+
env=smoke_env,
|
|
21104
|
+
context="package smoke installed bin ranked search determinism",
|
|
21105
|
+
)
|
|
21106
|
+
assert_embeddings_off_by_default_smoke(
|
|
21107
|
+
lambda *args: [str(bin_path), *args],
|
|
21108
|
+
tmp_root / "installed-embeddings-off-index",
|
|
21109
|
+
tmp_root / "installed-embeddings-off-learning.json",
|
|
21110
|
+
env=smoke_env,
|
|
21111
|
+
context="package smoke installed bin embeddings off by default",
|
|
21112
|
+
)
|
|
21113
|
+
assert_search_embeddings_no_provider_fallback_smoke(
|
|
21114
|
+
lambda *args: [str(bin_path), *args],
|
|
21115
|
+
env=smoke_env,
|
|
21116
|
+
context="package smoke installed bin search embeddings no-provider fallback",
|
|
21117
|
+
config_file=tmp_root / "installed-no-config-here" / "config.json",
|
|
21118
|
+
)
|
|
21119
|
+
assert_embedding_stub_provider_roundtrip_smoke(
|
|
21120
|
+
lambda *args: [str(bin_path), *args],
|
|
21121
|
+
tmp_root / "installed-embedding-index",
|
|
21122
|
+
tmp_root / "installed-embedding-learning.json",
|
|
21123
|
+
tmp_root / "installed-embedding-provider",
|
|
21124
|
+
env=smoke_env,
|
|
21125
|
+
context="package smoke installed bin embedding stub provider round-trip",
|
|
21126
|
+
)
|
|
20568
21127
|
assert_show_smoke(
|
|
20569
21128
|
[str(bin_path), "show", EXPECTED_CORPUS_SHOW_TARGET, "--context", "0", "--json"],
|
|
20570
21129
|
env=smoke_env,
|
|
@@ -21012,6 +21571,12 @@ def smoke_tarball(tarball: Path) -> None:
|
|
|
21012
21571
|
env=smoke_env,
|
|
21013
21572
|
context="package smoke installed bin learn stats",
|
|
21014
21573
|
)
|
|
21574
|
+
assert_learning_recall_smoke(
|
|
21575
|
+
lambda *args: [str(bin_path), *args],
|
|
21576
|
+
tmp_root / "installed-recall-learning.json",
|
|
21577
|
+
env=smoke_env,
|
|
21578
|
+
context="package smoke installed bin learn recall",
|
|
21579
|
+
)
|
|
21015
21580
|
assert_learning_audit_cleanup_smoke(
|
|
21016
21581
|
lambda *args: [str(bin_path), *args],
|
|
21017
21582
|
tmp_root / "installed-learning.json",
|
|
@@ -21943,6 +22508,36 @@ def smoke_tarball(tarball: Path) -> None:
|
|
|
21943
22508
|
env=npx_env,
|
|
21944
22509
|
context="package smoke npm exec search human corpus",
|
|
21945
22510
|
)
|
|
22511
|
+
assert_index_roundtrip_smoke(
|
|
22512
|
+
lambda *args: npm_exec_cmd(tarball, *args),
|
|
22513
|
+
npx_root / "npx-retrieval-index",
|
|
22514
|
+
npx_root / "npx-index-learning.json",
|
|
22515
|
+
cwd=npx_root,
|
|
22516
|
+
env=npx_env,
|
|
22517
|
+
context="package smoke npm exec index round-trip",
|
|
22518
|
+
)
|
|
22519
|
+
assert_ranked_search_determinism_smoke(
|
|
22520
|
+
lambda *args: npm_exec_cmd(tarball, *args),
|
|
22521
|
+
npx_root / "npx-ranked-index",
|
|
22522
|
+
cwd=npx_root,
|
|
22523
|
+
env=npx_env,
|
|
22524
|
+
context="package smoke npm exec ranked search determinism",
|
|
22525
|
+
)
|
|
22526
|
+
assert_embeddings_off_by_default_smoke(
|
|
22527
|
+
lambda *args: npm_exec_cmd(tarball, *args),
|
|
22528
|
+
npx_root / "npx-embeddings-off-index",
|
|
22529
|
+
npx_root / "npx-embeddings-off-learning.json",
|
|
22530
|
+
cwd=npx_root,
|
|
22531
|
+
env=npx_env,
|
|
22532
|
+
context="package smoke npm exec embeddings off by default",
|
|
22533
|
+
)
|
|
22534
|
+
assert_search_embeddings_no_provider_fallback_smoke(
|
|
22535
|
+
lambda *args: npm_exec_cmd(tarball, *args),
|
|
22536
|
+
cwd=npx_root,
|
|
22537
|
+
env=npx_env,
|
|
22538
|
+
context="package smoke npm exec search embeddings no-provider fallback",
|
|
22539
|
+
config_file=npx_root / "npx-no-config-here" / "config.json",
|
|
22540
|
+
)
|
|
21946
22541
|
assert_show_smoke(
|
|
21947
22542
|
npm_exec_cmd(tarball, "show", EXPECTED_CORPUS_SHOW_TARGET, "--context", "0", "--json"),
|
|
21948
22543
|
cwd=npx_root,
|
|
@@ -22435,6 +23030,13 @@ def smoke_tarball(tarball: Path) -> None:
|
|
|
22435
23030
|
env=npx_env,
|
|
22436
23031
|
context="package smoke npm exec learn stats",
|
|
22437
23032
|
)
|
|
23033
|
+
assert_learning_recall_smoke(
|
|
23034
|
+
lambda *args: npm_exec_cmd(tarball, *args),
|
|
23035
|
+
npx_root / "npx-recall-learning.json",
|
|
23036
|
+
cwd=npx_root,
|
|
23037
|
+
env=npx_env,
|
|
23038
|
+
context="package smoke npm exec learn recall",
|
|
23039
|
+
)
|
|
22438
23040
|
assert_learning_audit_cleanup_smoke(
|
|
22439
23041
|
lambda *args: npm_exec_cmd(tarball, *args),
|
|
22440
23042
|
npx_root / "npx-learning.json",
|