Skip to content

UXS-005: Semantic Search Panel

Alignment (2026-06-04). The backend shipped: hybrid retrieval (BM25 + dense via RRF over LanceDB, with compound results) is the default — FAISS is the fallback. The surface plan is re-grounded in PRD-033 (FR1) over the RFC-094 query layer; this surface is tracked in #884.

  • Implementation paths:
  • web/gi-kg-viewer/src/components/shell/LeftPanel.vue (hosts the search column)
  • web/gi-kg-viewer/src/components/search/SearchPanel.vue
  • web/gi-kg-viewer/src/components/search/ResultCard.vue
  • web/gi-kg-viewer/src/components/search/SearchResultsVizDialog.vue
  • web/gi-kg-viewer/src/components/search/SemanticSearchTip.vue
  • web/gi-kg-viewer/src/stores/search.ts (exposes queryType for FR1.4)
  • src/podcast_scraper/server/routes/search.py (#884) — surfaces source_tier per hit + response query_type (additive; FAISS + hybrid consistent)
  • Shell IA: VIEWER_IA.md — left panel Search + Explore, navigation axes, subject rail, status bar

Placement

Search is the default surface of the left query column; Explore is an alternate mode (slide transition) entered via a quiet footer control (Explore corpus →) and exited via ← Search — see VIEWER_IA.md. Mode is shell.leftPanelSurface (search | explore). / expands the column if needed, switches to Search, then focuses #search-q (LeftPanelfocusQuery). LeftPanel.vue hosts SearchPanel.vue + ExplorePanel.vue at w-72 when expanded. Search results stay visible when a hit opens in the subject rail (mode switches do not clear the subject). Deprecated: right-rail-only search, episodeRail, paneKind = tools as placement drivers.


Summary

For shell layout, the three navigation axes, subject rail persistence and clearing, status bar, and first-run empty corpus behavior, see VIEWER_IA.md. This document specifies the Search and Explore panel content only (query form, advanced filters, result cards, insights modal).

The search panel provides corpus search (hybrid by default, FAISS fallback) in the left shell column (Semantic search card: form + scrollable results; Explore corpus → sits in LeftPanel below that card). This UXS defines the visual contract for the search form, advanced filters, result cards, and the search result insights modal. All tokens reference UXS-001.

Track shell work in GitHub #606 and RFC-062. When the viewer changes, update this Active UXS in the same PR — see Living documents and ship boundary.

RFC-075: When corpus clustering JSON is available, Show on graph from search still selects the leaf node id (e.g. topic:…) as today. The graph node rail shows Topic cluster: for Topic nodes (Phase 1; see UXS-004). Search result cards may show a Topic cluster: line when the API joined metadata.topic_cluster (canonical label and compound id). Show on graph may widen the camera to include the tc: compound parent while keeping selection on the leaf. The pan/zoom animation and minimum zoom floor for that hand-off match other graph focus paths (UXS-004 — Camera framing and selection).

Dashboard topic clusters: On the Dashboard → Intelligence sub-tab, the Topic clusters status block reflects GET /api/corpus/topic-clusters as soon as Corpus path is set and health is OK — you do not need to wait for GI/KG artifacts to finish loading into the graph. While the request is in flight, Status shows Checking…. Then: Loaded, Not built (404 — optional topic-clusters CLI), request error, or Local files only when the graph came from the file picker (no API fetch for clustering). Unknown schema_version values get a non-blocking warning line.


Primary flow

Search query field (no separate label; placeholder + Semantic search heading) — Enter submits the same as Search (disabled while loading / no corpus / API down); Shift+Enter inserts a newline; IME composition does not trigger submit. Then a filter chip bar (#671, data-testid="search-filter-bar") with four chips — Since (search-chip-since, shared DateChip), Top‑k (search-chip-topk, default 10), Doc types (search-chip-doctypes, empty = all), More (search-chip-more, opens the slim Advanced search dialog). Each chip's label switches from Label ▾ (default) to Label: detail ▾ when active; the More chip shows More: N reflecting the count of non-default fields it hosts. Replaces the legacy inline Since (date) + Top-k row, the "Advanced search" underline link, and the read-only "Active advanced filters" summary block (which is gone — chip labels carry the active state instead). Search / Clear sit below the chip bar; scrollable results (errors + hit cards) follow in the middle. Explore mode has been retired — see the "Explore mode retired" note below for the current state (filters folded into the Workspace filter chip bar).


Advanced search dialog (slim, hosted by the More chip)

Opened by clicking the More filter chip; same backdrop pattern as before. After #671 the dialog hosts only the low-traffic fields that aren't worth a top-level chip:

  • Feed (substring on catalog feed_id for the API; Library -> Prefill semantic search shows the feed title from the feeds catalog when known, with hover/title for the id until edited)
  • Grounded insights only
  • Speaker contains
  • Embedding model
  • Merge duplicate KG surfaces (default on: same behavior family as graph Entity/Topic dedupe for kg_entity / kg_topic vector rows)

The legacy in-dialog Doc Types fieldset moved to the dedicated Doc types chip on the filter bar.


Search result insights

After at least one hit, an underlined Search result insights control opens a modal (same backdrop pattern as Advanced search) titled "Search result insights" -- one scroll, no tabs: a short insight line (dominant doc type); Doc types and Publish month in a two-column row (small multiples); Episodes / Feeds with top rows (episode title / feed title from hit metadata, or loaded from the episode's *.metadata.json when the index row omitted them) plus "+N other..." tail counts; Similarity bars proportional to score / max(score) in the list (captioned); Terms with a top-token insight (word frequency; heuristic, not KG).


Results

A muted "N results" / "1 result" line only (the query stays in the textarea; it is not repeated here). When the API returns lift_stats and at least one transcript row is on the page, a compact Lift: applied / transcript rows ratio appears on the same row (native title explains RFC-072 lift coverage).

Each hit can expose:

  • G (graph focus, GI token) and L (Library episode) as separate controls
  • L requires a healthy API check + corpus path and source_metadata_relative_path on the hit (vector indexer stamps it on rebuild)
  • corpus_library_api in health can still be No while L shows; the Library tab surfaces errors if catalog routes are unavailable
  • E (episode id chip) is informational
  • When Merge duplicate KG surfaces merged a row (kg_surface_match_count >= 2), G only -- L and E are hidden so actions are not tied to a single representative episode
  • Transcript hits may include a collapsible region Lifted GI insight (linked insight id/text, speaker/topic labels, quote time range) when the server attaches lifted (#528). When lifted.quote has at least one finite timestamp_*_ms but lifted.speaker has no usable display label, a muted line shows the same visible copy as supporting quotes — No speaker detected (GI_QUOTE_SPEAKER_UNAVAILABLE_HINT; #541). data-testid="search-lifted-quote-speaker-unavailable".
  • Supporting quotes (Show / Hide N supporting quote(s)): when the API returns supporting_quotes and a quote has no speaker_name / speaker_id, a muted line shows No speaker detected (same GI_QUOTE_SPEAKER_UNAVAILABLE_HINT as the graph Quote rail; #541). data-testid="supporting-quote-speaker-unavailable".

Two-tier results, evidence toggle, and intent (PRD-033 FR1)

The platform now indexes and ranks two primary tiers (synthesized insights + raw transcript segments) plus an auxiliary tier (kg_entity / kg_topic / quote / summary). The search surface makes that legible without re-querying:

  • Tier badge (FR1.1). Every hit card carries a small tier badge (search-result-tier): Insight (primary), Transcript (success), or Reference (muted), from the server-derived source_tier. Named-entity queries surface exact matches through retrieval quality (FR1.2) — no separate UI.
  • Compound result (FR1.1). A transcript (segment) hit that lifts a linked GI insight is a compound — the card shows both the segment text and the linked insight (the existing Lifted GI insight block) and is marked with a + insight badge (search-result-compound).
  • Evidence toggle (FR1.3). A small Insights / Transcript / Both segmented control (search-evidence-toggle) in the results header constrains the rendered cards to a tier (Both = no constraint). Client-side over the returned page; the active button uses aria-pressed.
  • Query-type indicator (FR1.4). When the response carries a detected intent, an Intent: <label> chip (search-query-type) sits in the results header (Entity lookup / Raw evidence / Temporal tracking / Cross-show synthesis / Semantic). Transparency only — the platform adapts its retrieval strategy per the rules router (RFC-090 §3.6); it does not change what the user must do.
  • Entity links to Detail (FR1.5). Canonical speaker / topic / entity names resolve to their Detail rail: lifted speaker → Person Landing (search-result-lifted-speaker-link), lifted topic → Topic Entity View (search-result-lifted-topic-link), and a kg_entity / kg_topic hit links its source_id via Open Person/Topic panel → (search-result-entity-link). Supporting-quote speakers keep their existing Person link.

E2E contract

E2E surface map -- search panel surfaces and selectors.


Compact launcher (RFC-107)

Once Search v3 (RFC-107) lands, the primary query surface moves to the full-width Query Workspace (UXS-016). UXS-005 continues to govern the compact launcher the LeftPanel becomes on non-Search tabs:

  • Single query field (#search-q — same testid as today).
  • Last-N recent queries (from USERPREFS-1 search.recentQueries — see ADR-125).
  • "Open in Workspace" chip; Enter submits and switches the main tab to Search.
  • Explore mode retired. The slide mode-switch, shell.leftPanelSurface = 'explore', and the left-panel-enter-explore / left-panel-back-search test IDs are removed. Explore's filters (topic contains / speaker contains / limit / min confidence / grounded only / presets) fold into the Workspace filter chip bar (UXS-016 §Header).
  • On the Search tab, the LeftPanel is hidden (Workspace owns full width).
  • Enriched answers are never invoked from the compact launcher — Workspace only.

The Advanced-search dialog treatment described above continues to apply on the Workspace filter chip bar's More chip; the Enriched answers toggle previously in that dialog is retired (replaced by the header Enriched chip in UXS-016).


Revision history

Date Change
2026-04-21 Query: Enter submits search; Shift+Enter newline; IME-safe.
2026-04-21 Explore CTA: LeftPanel footer below search card (clears status bar).
2026-04-21 Left column: Search default; Explore mode (slide); / restores Search + focus.
2026-04-21 Search: Since + Top-k row uses grid so fields stay in panel.
2026-04-21 Show on graph: UXS-004 camera + min zoom (cross-link).
2026-04-06 Initial content (in UXS-001)
2026-04-13 Extracted from UXS-001 into standalone UXS-005
2026-04-13 Document lift_stats summary line + Lifted GI insight region
2026-04-15 Supporting quotes: muted hint when speaker missing (#541)
2026-06-05 PRD-033 FR1: tier/compound badges, evidence toggle, intent chip, entity links (#884)
2026-04-15 Lifted GI: muted hint + testid when speaker display missing (#541)
2026-04-15 Lifted hint only when lifted.quote has finite timestamp_*_ms (matches E2E)
2026-04-15 #541: No speaker detected (graph + Search + Explore; semantics unchanged)
2026-04-16 Lifted GI: explicit same visible string as supporting quotes (No speaker detected)
2026-04-19 Shell IA: left query column copy; topic clusters card under Dashboard workspace (#606)