Files
git.stella-ops.org/docs/modules/ui/search-chip-context-contract.md
2026-03-07 20:58:52 +02:00

3.9 KiB

Search Chip Context Contract

Purpose

  • Define one deterministic contract for page-aware search chips.
  • Let feature teams add page suggestions without editing AmbientContextService logic.
  • Blend route context, last few page actions, and bounded suggestion randomization.
  • Answer-first self-serve questions and fallback states are governed separately in docs/modules/ui/search-self-serve-contract.md.

Rule (mandatory for page teams)

  • Every page that needs custom search chips must declare a context entry in SEARCH_CONTEXT_DEFINITIONS.
  • Page components should implement SearchContextComponent with searchContextId so ownership is explicit.
  • Context definitions must provide:
    • id
    • routePrefixes
    • presentation (titleKey/titleFallback, descriptionKey/descriptionFallback) for the search context rail
    • optional domain
    • optional searchSuggestions[]
    • optional chatSuggestions[]
    • optional chatRoutePattern
  • Search suggestion entries should provide:
    • key / fallback for the executable query text
    • reasonKey / reasonFallback for the visible "why this suggestion" line
    • optional kind (page, recent, strategy) when page teams need non-default styling/priority
  • Suggestion arrays must stay deterministic and bounded:
    • at most 3 base chips per page context
    • short, action-oriented text
    • no tenant/user secrets in fallback text
    • keep the query label distinct from rationale text so suggestion clicks always submit the intended query only
    • fallback copy should stand on its own; do not encode hidden ranking or workflow instructions into the raw query text

Source of truth

  • Contract and registry:
    • src/Web/StellaOps.Web/src/app/core/services/search-context.registry.ts
  • Runtime composition:
    • src/Web/StellaOps.Web/src/app/core/services/ambient-context.service.ts

Runtime behavior

  • The empty-state search panel renders a context rail with:
    • page title/description from presentation
    • active domain token when available
    • last-action token when recent scoped action history exists
  • Base chips come from the page context array.
  • A deterministic rotation (session + route scope + 5-minute bucket) varies chip order.
  • Last few actions for the current page scope are tracked (bounded history, TTL 15 minutes).
  • Up to 2 follow up: ... chips are generated from recent actions and prioritized above base chips.
  • One strategic chip is generated from dominant/recent action intent.
  • Generated chips must also expose rationale metadata:
    • recent chips explain they came from last-page actions
    • strategy chips explain they came from recent intent on the same page scope
  • Ranking behavior:
    • chip order is driven by page context, recent scoped actions, deterministic rotation, and backend viability
    • page teams should not create hidden forks that assume the user chose an internal search mode
  • Search-surface control rule:
    • buttons inside the search surface (assistant launcher, correction suggestions, starter chips, answer follow-ups, card actions) are part of the same command workspace
    • focus transitions into those controls must not collapse the search panel
    • teams adding new controls inside the panel must preserve this containment rule in tests

Page ownership workflow

  1. Add/adjust a context in SEARCH_CONTEXT_DEFINITIONS.
  2. Ensure page component exposes the same searchContextId (implements SearchContextComponent).
  3. Define or update presentation copy for the context rail.
  4. Add or update reasonFallback text for each base chip.
  5. Add/adjust unit tests in ambient-context.service.spec.ts.
  6. Add/adjust Playwright tests for route chips + action-driven chips.
  7. If the page adds custom controls inside the search panel, add focus-containment coverage so those controls do not dismiss the panel.

Non-goals

  • No unbounded per-page suggestion memory.
  • No runtime remote fetch for chip definitions.
  • No randomization based on Math.random() (must remain replayable).