Search — Improvement & Growth Plan
Package: capell-app/search · Kind: package · Tier: premium · Product group: Capell Search & SEO · Bundle: search-seo · Status: Complete
1. Snapshot
Section titled “1. Snapshot”capell-app/search adds a public frontend search surface (GET /search, autocomplete JSON, click-tracking beacon) plus an admin analytics layer (Top/Trending/Zero-result widgets, three overview stats, a settings page). Domain logic lives entirely in src/Actions behind a Capell\Search\Contracts\Search driver contract with three implementations: DatabaseSearch (LIKE/FULLTEXT over a flat table, default pages), SiteDiscoverySearch (canonical-URL registry metadata), and ScoutSearch (multi-source Scout). Key Actions: RunSearchAction (orchestrator), ResolveExpandedSearchQueriesAction (synonyms + Levenshtein/explicit typo correction), ApplySearchResultEnhancementsAction (promotions, source weighting, exact-match/recency/click boosts), SanitizeSearchResultAction (URL + meta scrubbing), RecordSearchAction (hashed query logging). One table search_logs (model SearchLog), one settings class SearchSettings, one command search:purge (scheduled monthly). Runtime deps are light: lorisleiva/laravel-actions, spatie/laravel-data, spatie/laravel-package-tools; Scout/Typesense and capell-app/site-discovery are suggest-only.
Current marketplace summary: “Production-grade site search for Capell — relevance-tuned results with synonyms, typo tolerance, and curated promotions, plus admin insights into what visitors search for and where you have no answers.” Manifest now declares the extension card, frontend search results, header search, settings, top-searches, trending-searches, zero-result widget captures in light and dark mode, plus an annotated curation screenshot for synonyms, typo corrections, and promoted results.
2. Improvements (existing functionality)
Section titled “2. Improvements (existing functionality)”Prioritized; effort S/M/L.
-
Done — Fix the outdated
Searchcontract example in docs —docs/drivers-and-logging.md“Swap the Search Driver” now importsSearchFilterData, shows the full six-argumentSearch::search()signature ($siteId, $languageId, $filters), and demonstrates a public-safe escapinghighlight()implementation. Evidence:docs/drivers-and-logging.md,src/Contracts/Search.php. — Shipped -
Shipped: expanded query search is bounded and first-page only.
RunSearchActionnow caps synonym/typo expansion breadth throughcapell-search.query_expansion.max_queriesand uses the primary driver’s native paginator for page 2+ instead of refetchingperPage * pagerows for every expanded query. First-page expansion still merges and de-dupes the bounded result window; deeper pages keep accurate driver totals/current page state and avoid multiplicative DB/Scout round-trips. Evidence:config/capell-search.php,src/Actions/RunSearchAction.php,tests/Feature/Actions/SearchFeatureGapSliceTest.php. — M -
Shipped: click-count boosts use scoped cache and a collapsed enhancement pass.
ApplySearchResultEnhancementsActionnow cachesclicked_result_urlaggregates behind a short TTL scoped by site/language, invalidates affected scopes when clicks are recorded, and applies type labels, weights, exact-match, recency, and click boosts in one enhancement pass after promotion de-dupe/sanitization. Evidence:src/Actions/ApplySearchResultEnhancementsAction.php,src/Actions/RecordSearchResultClickAction.php,tests/Feature/Actions/SearchFeatureGapSliceTest.php. — M -
Shipped: public result Blade receives hydrated highlight data.
SearchControllerresolves the configuredSearchdriver, buildshighlightedResults, and passes that hydrated collection into the public result view;resources/views/components/results.blade.phpno longer resolvesSearchfrom Blade. Evidence:src/Http/Controllers/SearchController.php,resources/views/components/results.blade.php,tests/Feature/Http/SearchControllerTest.php. — M -
Shipped:
DatabaseSearch::canUseFullText()memoizes schema detection per connection/table/column set. Theinformation_schema.STATISTICSlookup now runs once per compatible driver/database/table/column combination and caches both positive and fallback results. Evidence:src/Drivers/DatabaseSearch.php,tests/Unit/Search/DatabaseSearchTest.php. — S/M -
Shipped: FULLTEXT index matching now accepts covering indexes regardless of order. Configured search columns only need to be present in one FULLTEXT index, so indexes with additional columns or different ordering no longer silently fall back to LIKE; incomplete indexes still fall back. Evidence:
src/Drivers/DatabaseSearch.php,tests/Unit/Search/DatabaseSearchTest.php. — M -
Shipped: autocomplete uses a lightweight driver path.
RunAutocompleteSearchActionnow calls the configuredSearchdriver directly with normalized query, filters, site, and language, then applies only cheap response mapping/type-label hydration. It skips promotions, click-count aggregates, recency boosts, and the rest ofRunSearchAction’s enhancement pipeline for keystroke traffic. Evidence:src/Actions/RunAutocompleteSearchAction.php,tests/Feature/Http/SearchControllerTest.php. Query-suggestion UX remains a separate §3 roadmap item. — M -
Shipped: result type labels resolve through translations with config overrides. The default config no longer ships hardcoded English labels;
ResolveSearchResultTypeLabelActionhonors site-configured labels first, thencapell-search::types.*, then a headline fallback for unknown types. Search results and autocomplete both use the same resolver. Evidence:src/Actions/ResolveSearchResultTypeLabelAction.php,resources/lang/en/types.php,src/Actions/ApplySearchResultEnhancementsAction.php,src/Actions/RunAutocompleteSearchAction.php,tests/Feature/Actions/ResolveSearchResultTypeLabelActionTest.php. — S/M -
Shipped: click tracking uses a token-first log match.
GenerateSearchClickTokenActionemits an encrypted per-render token with normalized query/site/language context, result links echo it through the beacon, andRecordSearchResultClickActionresolves the latest matching log from the token before falling back to the legacy visitor tuple. Proxy IP or user-agent churn no longer drops result-page clicks, while old markup/autocomplete payloads remain compatible. Evidence:src/Actions/GenerateSearchClickTokenAction.php,src/Actions/RecordSearchResultClickAction.php,resources/views/components/results.blade.php,tests/Feature/Actions/RecordSearchActionTest.php,tests/Feature/Http/SearchControllerTest.php. — M -
Shipped curation UI; advanced ranking/driver tuning remains config-only.
SearchSettingsSchemanow exposes synonyms, typo corrections/terms, typo max distance, and promoted results as admin-editable runtime settings. Source weights, ranking boosts, database column mapping, and database column weights still live inconfig/capell-search.php; those lower-level tuning knobs can remain a later admin polish item. Evidence:src/Filament/Settings/SearchSettingsSchema.php,src/Settings/SearchSettings.php,config/capell-search.php,tests/Unit/Filament/SearchSettingsSchemaTest.php,tests/Feature/Actions/SearchCurationSettingsTest.php. — L
3. Missing Features (gaps)
Section titled “3. Missing Features (gaps)”Tied to capabilities[] (search, search-admin/console/frontend, search-synonyms, search-promoted-results, search-typo-corrections, search-source-weighting, search-zero-result-reporting, search-site-discovery-indexing) and search-category norms.
- Shipped: faceted filtering UI with live counts.
BuildSearchFacetGroupsActionbuilds type/source facet groups from enabled searchable sources, computes counts through the configuredSearchdriver with the active site/language/filter context, andpages/search.blade.phprenders neutral public filter links before results. Evidence:src/Actions/BuildSearchFacetGroupsAction.php,src/Data/SearchFacetGroupData.php,src/Data/SearchFacetOptionData.php,resources/views/components/facets.blade.php,resources/views/pages/search.blade.php,tests/Feature/Actions/SearchFacetGroupsActionTest.php,tests/Feature/Http/SearchControllerTest.php. - Shipped: admin settings manage synonyms, promoted results, and typo dictionaries.
SearchSettingsSchemanow exposes curation controls for synonyms, explicit typo corrections, typo dictionary terms/max distance, and promoted best-bet results. The curation actions read persistedSearchSettingsthroughResolveSearchSettingActionbefore config fallbacks, so admin changes affect runtime behavior. Evidence:src/Filament/Settings/SearchSettingsSchema.php,src/Settings/SearchSettings.php,src/Actions/ResolveExpandedSearchQueriesAction.php,src/Actions/ResolveCorrectedSearchQueryAction.php,src/Actions/ResolvePromotedSearchResultsAction.php,tests/Unit/Filament/SearchSettingsSchemaTest.php,tests/Feature/Actions/SearchCurationSettingsTest.php. - Shipped: zero-result terms can become persisted curation rules.
CreateSynonymFromZeroResultSearchActionturns a zero-result query into a synonym alias for an existing target query, andCreatePromotedResultFromZeroResultSearchActioncreates or replaces a promoted best-bet result for that zero-result query. Both updateSearchSettings, so the existing expansion/promotion runtime consumes the fix immediately. Evidence:src/Actions/CreateSynonymFromZeroResultSearchAction.php,src/Actions/CreatePromotedResultFromZeroResultSearchAction.php,tests/Feature/Actions/ZeroResultCurationActionsTest.php. - Shipped: autocomplete includes query suggestions from search logs.
BuildAutocompleteQuerySuggestionsActionreturns popular normalized-query completions by prefix, scoped by site/language, andRunAutocompleteSearchActionexposes them asquerySuggestionsalongside result hits. The header autocomplete renders query suggestions before result rows. Evidence:src/Actions/BuildAutocompleteQuerySuggestionsAction.php,src/Data/AutocompleteQuerySuggestionData.php,src/Actions/RunAutocompleteSearchAction.php,resources/views/components/header/search-dialog.blade.php,tests/Feature/Actions/SearchSuggestionActionsTest.php,tests/Feature/Http/SearchControllerTest.php. - Shipped: did-you-mean correction metadata is surfaced.
ResolveCorrectedSearchQueryActionresolves explicit and dictionary typo corrections,RunAutocompleteSearchActionexposesmetadata.corrected, and the header autocomplete renders the corrected query as a selectable suggestion. Evidence:src/Actions/ResolveCorrectedSearchQueryAction.php,src/Data/SearchQueryMetadataData.php,resources/views/components/header/autocomplete-results.blade.php,tests/Feature/Actions/SearchSuggestionActionsTest.php,tests/Feature/Http/SearchControllerTest.php. - Shipped: Scout honors engine scores and totals where available.
ScoutSearchnow reads common engine relevance fields (_rankingScore,_score,scout_score,text_match) before falling back to its local substring score, and preserves Scout paginator totals when the engine reports more matches than the fetched window. Public visibility filtering still shrinks totals when hidden payloads are dropped locally. Evidence:src/Drivers/ScoutSearch.php,tests/Unit/Search/ScoutSearchTest.php. - Shipped: Scout index maintenance commands and freshness ownership docs.
search:indeximports enabled Scout-backed searchable sources viamakeAllSearchable()using the configured/default chunk size, andsearch:flushremoves configured sources viaremoveAllFromSearch(). Both commands support--sourcefor targeted maintenance, are listed incapell.json, anddocs/drivers-and-logging.mdnow documents that source packages own public payloads, observers, queue setup, and external index freshness. Evidence:src/Actions/IndexScoutSearchSourcesAction.php,src/Actions/FlushScoutSearchSourcesAction.php,src/Console/Commands/IndexSearchCommand.php,src/Console/Commands/FlushSearchCommand.php,docs/drivers-and-logging.md,tests/Feature/Actions/SearchScoutMaintenanceActionsTest.php,tests/Feature/Console/SearchScoutMaintenanceCommandsTest.php. - Shipped: Database fallback ranking supports field weights.
DatabaseSearchnow acceptscapell-search.database.column_weightsand ordersLIKEfallback results by a generatedsearch_score, so title/excerpt/body relevance can be tuned without a custom driver. Global boosts (exact, recency, click) remain config scalars, and Scout delegates field relevance to the engine/source config. Evidence:config/capell-search.php,src/Drivers/DatabaseSearch.php,src/Providers/SearchServiceProvider.php,tests/Unit/Search/DatabaseSearchTest.php. - Shipped: runtime health probes cover driver resolution and search-log writes.
SearchHealthCheckverifies the configuredSearchdriver resolves, the search log table exists,SearchLogis registered, a synthetic query-log write/delete succeeds, and logging configuration is sane. Evidence:src/Health/SearchHealthCheck.php,tests/Feature/Health/SearchHealthCheckTest.php.
4. Issues / Risks
Section titled “4. Issues / Risks”- Runtime health probe now covers local failures.
SearchHealthChecknow runs diagnostics for storage table presence, model registration, configured driver resolution, synthetic log write/delete, and logging configuration. —src/Health/SearchHealthCheck.php - Shipped guardrail — Site Discovery is now the safe default driver. New installs default to
site_discoverythrough config, settings defaults, settings migration, and provider fallback, so the package searches Capell’s canonical public URL registry instead of assuming public page content lives in flatpages.title/excerpt/bodycolumns. The database driver remains available as an explicit opt-in for flat index tables/views. Evidence:config/capell-search.php,src/Settings/SearchSettings.php,database/settings/2026_05_10_190869_01_add_search_settings.php,src/Providers/SearchServiceProvider.php,tests/Feature/Providers/SearchServiceProviderTest.php,docs/drivers-and-logging.md. - Shipped guardrail — Scout drops common non-public payload markers.
ScoutSearchnow filters indexed payloads marked draft, unpublished, private, restricted, or non-public before mapping public results, and focused tests cover those leak paths. Model-specifictoSearchableArray()implementations should still avoid indexing sensitive fields, and site/language scoping remains best-effort through Scoutwhere()support. Evidence:src/Drivers/ScoutSearch.php,tests/Unit/Search/ScoutSearchTest.php. - Shipped guardrail — public result meta is now explicit allow-list output.
SanitizeSearchResultAction::safeMeta()only preserves keys listed incapell-search.public_urls.allowed_meta_keysand drops nested object/array payloads, so internal IDs, owner references, admin URLs, signed preview tokens, and arbitrary model metadata cannot ride throughSearchResultData->meta. URL sanitization blocks configured private path prefixes and strips signed/preview query keys. Evidence:config/capell-search.php,src/Actions/SanitizeSearchResultAction.php,tests/Feature/Actions/SanitizeSearchResultActionTest.php. - Shipped guardrail —
highlight()must escape before adding markup.results.blade.php:53-57prints{!! $search->highlight(...) !!}. All three built-in drivershtmlspecialchars/e()the text before wrapping<mark>, tests assert escaping, andsrc/Contracts/Search.phpnow documents that custom implementations must escape the full input text and only add trusted<mark>markup. Evidence:src/Contracts/Search.php,docs/drivers-and-logging.md,tests/Unit/Search/DatabaseSearchTest.php,tests/Unit/Search/ScoutSearchTest.php,tests/Unit/Search/SiteDiscoverySearchTest.php. - Shipped: click-tracking beacon no longer depends on cached-page CSRF state.
search/clickis now explicitly CSRF-exempt and throttled throughcapell-search.click_tracking.rate_limiter; the header and result click beacons post without a meta CSRF token, and the result component owns a guarded beacon listener so search-result pages still track clicks when the header search modal is absent. Evidence:routes/web.php,config/capell-search.php,resources/views/components/header/search-dialog.blade.php,resources/views/components/results.blade.php,tests/Feature/Http/SearchControllerTest.php. - Shipped guardrail — search log writes are deferred after response.
capell.jsonsetsfrontendRenderBudgetMs: 20andadminQueryBudget: 40. The frontend path still does driver query work, but expanded queries are bounded/first-page only, click-count aggregates are cached per site/language, and the analytics insert is no longer inline:SearchControllernow callsRecordSearchAction::dispatchAfterResponse()with scalar visitor metadata, andRecordSearchActionstill owns the actual write. Evidence:src/Http/Controllers/SearchController.php,src/Actions/RecordSearchAction.php,tests/Feature/Http/SearchControllerTest.php,tests/Feature/Actions/RecordSearchActionTest.php,tests/Feature/Actions/SearchFeatureGapSliceTest.php.cacheSafety.cacheable=falseis correct (per-query). - Shipped i18n guardrail — Site Discovery titles flow into Search. Result type labels resolve through translations/config overrides, root URL titles translate through
capell-search::generic.site_discovery_home_title, andSiteDiscoverySearchnow prefersPublicUrlRegistryEntryData::titlebefore deriving a slug title. The core CMS Site Discovery contributor passes localized discoverable page titles into the registry, and non-CMS Blog, Events, Knowledge Base, and Campaign Studio public URL contributors now populatePublicUrlData::titlefrom their localized/model titles. Evidence:packages/site-discovery/src/Data/PublicUrlData.php,packages/site-discovery/src/Data/PublicUrlRegistryEntryData.php,packages/site-discovery/src/Support/PublicUrls/CmsPagePublicUrlContributor.php,packages/blog/src/Support/PublicUrls/BlogPublicUrlContributor.php,packages/events/src/Support/PublicUrls/EventsPublicUrlContributor.php,packages/knowledge-base/src/Support/PublicUrls/KnowledgeBasePublicUrlContributor.php,packages/campaign-studio/src/Support/PublicUrls/CampaignLandingPagePublicUrlContributor.php,src/Drivers/SiteDiscoverySearch.php,tests/Unit/Search/SiteDiscoverySearchTest.php,packages/site-discovery/tests/Integration/Discovery/PublicUrlRegistryActionTest.php. - Test coverage — covered:
DatabaseSearch(escape, clamp, site/lang/status filter, pagination, highlight, FULLTEXT index compatibility),ScoutSearch(registry merge, weight, absolute URL, highlight) via in-memory fakes,SiteDiscoverySearch(indexability, metadata match, paginate/highlight),RecordSearchAction/RecordSearchResultClickAction,PurgeSearchLogsAction,ResolveExpandedSearchQueries(synonyms + typos),ApplySearchResultEnhancements(promotions + weights + dedupe),RunSearchAction(synonym dedupe),SearchController(autocomplete limits, blank query, normalized passthrough, configured-page view, frontend-shell view, click POST endpoint, click rate limiter, “no package identifiers in markup”), click-beacon route CSRF exemption and tokenless header/results markup, provider binding/driver selection, settings defaults, manifest requirements, generated-output coverage source, insights actions, widgets, and non-CMS Site Discovery title propagation. - Manifest alignment shipped.
BuildTopClickedResultsQueryActionis now listed incapell.jsonactions[]and covered byManifestRequirementsTest;drivers-and-logging.mdnow listssite_discoveryas a first-class driver.
5. Marketplace & Selling
Section titled “5. Marketplace & Selling”Shipped copy refresh. composer.json, capell.json, and resources/lang/en/package.php now lead with the production search value proposition instead of the old feature-dump copy. Evidence: composer description “Production-grade site search for Capell with relevance-tuned results, synonyms, typo tolerance, curated promotions, and admin search insights.” Marketplace summary “Production-grade site search for Capell — relevance-tuned results with synonyms, typo tolerance, and curated promotions, plus admin insights into what visitors search for and where you have no answers.”
Improved one-sentence summary:
Production-grade site search for Capell — relevance-tuned results with synonyms, typo tolerance, and curated promotions, plus admin insights into what visitors search for and where you have no answers.
Improved 3–4 sentence description:
Search gives every Capell site a fast, themeable search experience: a results page, header search field, and type-ahead autocomplete, backed by a pluggable driver contract (database, Site Discovery URL registry, or Laravel Scout/Meilisearch/Typesense). Curate results with synonyms, typo corrections, and promoted “best bet” answers, and tune ranking with source weighting plus exact-match, recency, and click-through boosts. The admin dashboard surfaces top searches, trending terms, and — critically — zero-result queries, so editors see exactly where content is missing. Privacy-first by default: visitor identifiers are hashed and query logs auto-purge on a configurable retention window.
Shipped screenshot/media promotion. capell.json.marketplace.screenshots now promotes the extension card plus frontend search, header search, search settings, top-searches, trending-searches, zero-result widget captures in light and dark mode, and an annotated curation screenshot for synonyms, typo corrections, and promoted results.
Pricing / tier / bundle positioning. premium tier in the search-seo bundle is right: search + SEO is a natural “growth” pairing. Position Search as the demand-side half (what visitors look for) against seo-suite as the supply-side half (how content is found by engines). Cross-sell is already wired: suggest lists capell-app/site-discovery (URL-registry indexing + generated-output coverage) and docs/README.md “Read Next” points at seo-suite and site-discovery. Make the bundle explicit: Search + SEO Suite + Site Discovery = “Findability”. Zero-result analytics is the upsell hook into content strategy because zero-result terms can become synonyms or promoted best-bet results.
Differentiators / value props / target buyer. Differentiators vs. table-stakes CMS search: (1) editable result curation (synonyms, promotions, typo dictionaries) — most CMS search can’t do this; (2) zero-result + trending analytics that turn search into a content-strategy signal; (3) driver-agnostic contract so a site starts on DB search and graduates to Typesense without touching the frontend. Target buyer: agencies and content teams running multi-site Capell installs who treat on-site search as a conversion/retention surface, not a checkbox.
Keywords/tags (8–12): site search, full-text search, laravel scout, meilisearch, typesense, autocomplete, synonyms, typo tolerance, search analytics, zero-result tracking, promoted results, faceted search.
6. Prioritized Roadmap
Section titled “6. Prioritized Roadmap”| Item | Bucket | Effort | Impact | Section ref |
|---|---|---|---|---|
Shipped: Fix outdated 3-arg Search contract example in driver docs | Done | S | Med (prevents broken integrations) | §2.1 |
| Shipped: Promote real Search frontend/header/widget screenshots and an annotated curation screenshot in marketplace media. | Done | S | Med (free conversion lift) | §5 |
Shipped: Rewrite composer description + marketplace summary | Done | S | Med | §5 |
Shipped: Document/guarantee highlight() must escape | Done | S | High (XSS guardrail) | §4 |
Shipped: Add Scout unpublished/private-exclusion test + DB no-status-column leak test. Evidence: ScoutSearch drops indexed payloads marked draft/private/restricted; DatabaseSearch fails closed when the configured published-status guard column is missing; driver tests cover both leak paths. | Done | M | High (result-visibility safety) | §4 |
Shipped: Cache clickCounts() aggregate + collapse enhancement map chain. Evidence: click-count boosts use a short TTL cache scoped by site/language, click recording invalidates the affected scopes, promoted-result conversion no longer remaps promotions twice, and focused action tests prove cached aggregates preserve scoped ranking. | Done | M | High (frontend budget) | §2.3 |
Shipped: Move RecordSearchAction write to after-response dispatch. Evidence: SearchController dispatches after response; RecordSearchAction receives scalar visitor metadata; controller/action tests cover deferred dispatch and write behavior. | Done | M | High (20ms render budget) | §4 |
Shipped: Fix click beacon CSRF on cached pages. Evidence: search/click is CSRF-exempt + throttled, header/results beacons post without CSRF meta state, and controller tests cover the route middleware plus tokenless markup. | Done | M | High (analytics actually works) | §4 |
Shipped: Memoize FULLTEXT detection and accept covering indexes regardless of column order. Evidence: DatabaseSearch caches schema compatibility by connection/database/table/columns and unit tests cover covering vs incomplete index sets. | Done | M | Med (DB driver perf + correctness) | §2.5, §2.6 |
Shipped: Resolve driver in controller and pass hydrated highlight data into Blade. Evidence: SearchController now supplies highlightedResults; public result Blade no longer resolves Search; controller tests assert highlighted view data/rendered markup. | Done | M | Med (convention compliance) | §2.4 |
| Shipped: Lightweight autocomplete path skips enhancement pipeline. Evidence: autocomplete calls the driver directly and focused controller coverage proves promoted results are not injected into type-ahead responses. | Done | M | Med (public endpoint load) | §2.7, §3 |
Shipped: Translate result type labels with config overrides. Evidence: both search results and autocomplete use ResolveSearchResultTypeLabelAction; tests cover configured, translated, and fallback labels. | Done | S/M | Med (localized result chips) | §2.8 |
Shipped: Did-you-mean surface + query-completion suggestions from logs. Evidence: autocomplete exposes metadata.corrected and querySuggestions; header autocomplete renders corrected/popular query rows; focused action/controller tests cover corrections and scoped log-backed suggestions. | Done | M | High (search norm + UX) | §3 |
| Shipped: Admin UI for synonyms / promoted results / typo dictionaries. Evidence: settings schema exposes editable curation fields and curation actions read persisted settings before config fallback; focused tests cover schema fields and runtime overrides. | Done | L | High (premium differentiator) | §2.10, §3 |
| Shipped: Faceted-filter frontend with live counts. Evidence: type/source facet groups compute counts through the active search driver and render as public-safe filter links on the search page; action/controller tests cover counts, selected toggles, URLs, and markup safety. | Done | L | High (table-stakes surface) | §3 |
Shipped: Honor Scout engine scores/total where available, and document Scout source freshness ownership. | Done | L | High (premium Scout story) | §3 |
Shipped: Runtime health probe for driver resolution and search-log writes. Evidence: health checks now resolve the configured Search driver and perform a synthetic query-log write/delete; tests cover successful probes and failure cases. | Done | M | Med (priority-support SKU) | §3, §4 |
| Shipped: Zero-result → create synonym/promotion curation actions. Evidence: actions persist zero-result terms into synonym aliases or promoted best-bet results consumed by runtime expansion/promotion logic; focused tests cover dedupe and runtime effects. | Done | M | High (cross-sell + stickiness) | §3, §5 |