diff --git a/internal/app/tool_search_integration_test.go b/internal/app/tool_search_integration_test.go index 852448b5..0c406c38 100644 --- a/internal/app/tool_search_integration_test.go +++ b/internal/app/tool_search_integration_test.go @@ -73,13 +73,10 @@ func TestToolSearchDeliveryDiagnosticTrustAndChineseSlices(t *testing.T) { if err != nil { t.Fatalf("BuildDeliveryToolSearchDiagnosticComparison() error = %v", err) } - // Absolute population/hit counts (1123 intent cases, 990/1123 Recall@5, 1098 - // tools, 402 chinese_only, 721 mixed_chinese_ascii) were removed: they were - // pinned to one Catalog generation and went stale as soon as main added - // tools, which turned a real trust gate into a size-drift failure. The - // observed values are logged for CI forensics and the headline numbers stay - // in the generated comparison JSON (make generate-tool-search-comparison); - // what this test enforces is the size-independent trust contract below. + // Do not pin absolute Catalog-size counts here: they go stale when main + // adds tools. Enforce the size-independent trust contract below; observed + // counts are logged for CI forensics and live in the generated comparison + // JSON (make generate-tool-search-comparison). t.Logf("intent proxy = %+v excluded_over_budget=%d", report.IntentProxy, report.IntentExcludedOverBudget) t.Logf("language slices = %+v", report.IntentLanguageSlices) t.Logf("context comparison = %+v", report.Context) diff --git a/internal/cli/schema_catalog.go b/internal/cli/schema_catalog.go index 3e761dc0..307bdb22 100644 --- a/internal/cli/schema_catalog.go +++ b/internal/cli/schema_catalog.go @@ -325,7 +325,10 @@ func schemaPayloadFromLoadedCatalog(loaded loadedSchemaCatalog, args []string) ( } } } - return nil, apperrors.NewValidation("unknown runtime schema path " + strconvQuote(raw)) + // The message carries the path-form boundary because it is the one surface + // an Agent is guaranteed to see at failure time. + return nil, apperrors.NewValidation("unknown runtime schema path " + strconvQuote(raw) + + ": canonical paths contain dots and must be a single argument (e.g. dws schema chat.query_msg_read_status), while space-separated tokens resolve as a CLI path") } func schemaCatalogSnapshotHash(snapshot SchemaCatalogSnapshot) string { diff --git a/internal/cli/tool_search.go b/internal/cli/tool_search.go index c9bc1c7c..078e5fd5 100644 --- a/internal/cli/tool_search.go +++ b/internal/cli/tool_search.go @@ -268,9 +268,11 @@ func resetDeliveryToolSearchEngineState() { // The default engine is memoized per Catalog generation: the delivery Catalog // is frozen and DWS_TOOL_SEARCH_* overrides are read once, so the engine is // built once per process (a CLI invocation constructs it exactly once either -// way; long-lived hosts such as a daemon or MCP server reuse it). -// Re-registering the source root resets Catalog delivery, which invalidates -// this memo so the engine can never serve a stale Catalog or stale hashes. +// way; long-lived hosts such as a daemon or MCP server reuse it). The memo +// serves long-lived hosts: the shipped schema search command rebuilds per +// invocation through NewDeliveryToolSearchEngineWithConfig. Re-registering +// the source root resets Catalog delivery, which invalidates this memo so +// the engine can never serve a stale Catalog or stale hashes. func NewDeliveryToolSearchEngine() (*ToolSearchEngine, error) { deliveryToolSearchEngineOnce.Do(func() { deliveryToolSearchEngine, deliveryToolSearchEngineErr =