# Semantic Search Reader

Status: Frozen architecture note
Owner: Search Platform Architecture

## 1. Scope

This document defines the execution design of `Semantic Search Reader`, the stateless query-plane component that evaluates semantic/vector retrieval work under one broker-pinned snapshot.

This document is subordinate to the canonical owner stack:
- machine-readable contract: `api/proto/searchfabric/v1/search_fabric.proto`
- internal owner file for this boundary: `api/proto/searchfabric/v1/semantic_search_reader.proto`
- broker-owned shared execution carriers: `api/proto/searchfabric/v1/query_broker.proto`
- semantic-reader RPC boundary: `api/proto/searchfabric/v1/semantic_search_reader.proto`
- public query semantics: `docs/specs/core/search-fabric-api.md`
- canonical error registry: `docs/specs/core/error-codes.md`
- system topology and state ownership: `docs/specs/core/search-fabric-architecture.md`

It must not redefine:
- public `QueryRequest`, `VectorClause`, `QueryHit`, `TotalHits`, or `AggregationResponse` semantics,
- query admission, `safe_ts`, `effective_read_ts`, continuation-token semantics, or `max_page_depth` enforcement,
- broker-owned selectivity planning or final fusion behavior,
- manifest publication, schema-serving readiness, or watermark derivation ownership,
- write-path durability/visibility semantics owned by writers, `Visibility Barrier`, and `Manifest Publisher`,
- metadata/text retrieval behavior owned by `Metadata Search Reader`.

## 2. Canonical Position in the System

`Semantic Search Reader` sits on the query plane behind `Query Broker`:

```text
Query Gateway
  -> Query Broker
       -> Semantic Search Reader
            -> Local Cache
            -> Object Store
            -> NRT Delta Store (Semantic Search)
```

The component is the execution engine for:
- vector retrieval over manifest-published semantic artifacts,
- semantic candidate generation under one admitted snapshot,
- broker-selected filtered-ANN physical paths,
- same-snapshot merge of immutable semantic segments plus publish-admitted semantic NRT deltas,
- optional semantic-side hit materialization requested by broker,
- stage-local total-hit contribution for semantic-only or vector-dominant plans.

It is not:
- a public RPC surface,
- a second snapshot-admission authority,
- a metadata/text execution engine,
- the owner of final query-wide `global_filter` equivalence,
- the owner of final cross-reader fusion, deduplication, rerank, or pagination,
- a manifest-head, token, or schema-generation owner.

Primary source anchors:
- `docs/specs/core/search-fabric-architecture.md` Section `8`, Section `9.3`, Section `10.2`, Section `10.3`
- `docs/architecture/query-broker.md`
- `docs/architecture/query-gateway.md`
- `docs/architecture/nrt-delta-store.md`
- `api/proto/searchfabric/v1/query_broker.proto`
- `api/proto/searchfabric/v1/semantic_search_reader.proto`

## 3. Design Anchors

The design below is constrained by the following checked-out facts:

1. Read execution never bypasses `Query Broker`; admitted flow remains `Query Gateway -> Query Broker -> Semantic Search Reader`.
2. `Query Gateway` owns consistency admission and derives/validates the execution snapshot; `Query Broker` owns snapshot reconstruction, committed-manifest pinning, continuation validation, and physical-plan selection.
3. `Semantic Search Reader` executes only against the broker-admitted snapshot boundary:
   - exact caller `read_ts` only when strict execution was admitted as-is,
   - otherwise admitted `effective_read_ts`,
   - continuation/search-context execution reuses token-bound snapshot state rather than recomputing from the newest readiness frontier.
4. Reader-visible semantic state is the merge of:
   - immutable semantic artifacts selected by the pinned committed manifest,
   - publish-admitted semantic deltas from `NRT Delta Store (Semantic Search)`.
5. Row visibility remains MVCC-based: only versions with `commit_ts <= effective_read_ts` and not hidden by a winning tombstone at `commit_ts <= effective_read_ts` may contribute to semantic results.
6. Broker dispatches one explicit semantic physical path through `SemanticSearchSubplan.strategy`:
   - `SEMANTIC_EXECUTION_STRATEGY_BRUTE_FORCE_DOC_SET`
   - `SEMANTIC_EXECUTION_STRATEGY_MASKED_ANN`
   - `SEMANTIC_EXECUTION_STRATEGY_PURE_ANN`
   The reader executes the chosen path; it does not rerun the CBO.
7. Metadata-produced prefilter payloads are internal optimization carriers only. They may narrow reader work, but they must not weaken the final canonical hard-filter semantics that broker rechecks before emit/paginate.
8. Reader outputs remain broker-facing execution products such as candidates, local total-hit contribution, warnings, stats, and optional materialized hit fragments; public response assembly remains broker-owned.

## 4. Responsibilities and Non-Responsibilities

### 4.1 Responsibilities

`Semantic Search Reader` is responsible for:

1. Executing broker-dispatched semantic/vector subplans against one immutable snapshot.
2. Reading the pinned committed manifest and its referenced semantic objects through disposable local cache layers.
3. Executing the broker-selected semantic physical strategy:
   - pure ANN,
   - masked ANN with a metadata-produced prefilter,
   - brute-force evaluation over an explicit document set.
4. Applying semantic-side visibility, tombstone suppression, and last-write-wins winner selection under the admitted snapshot boundary.
5. Merging publish-admitted semantic NRT deltas under the same `effective_read_ts` and committed-manifest frontier.
6. Producing broker-consumable outputs such as:
   - `(doc_id, commit_ts, semantic score/features)` candidates,
   - stage-local total-hit contribution,
   - optional materialized public-hit fragments,
   - execution warnings and statistics.
7. Preserving deterministic ordering and deterministic response-local dictionary alignment whenever it materializes hits.
8. Remaining stateless for correctness: cache loss may hurt latency, but execution must remain reconstructible from the pinned manifest, referenced objects, and publish-admitted semantic NRT state.

### 4.2 Non-Responsibilities

`Semantic Search Reader` is not responsible for:
- parsing Elasticsearch / Turbopuffer / SQL source syntax,
- collection-name resolution or catalog admission,
- deriving `safe_ts`, waiting for visibility, or interpreting public continuation/search-context tokens,
- choosing `VectorFilterStrategy` or re-planning filtered-ANN paths after broker dispatch,
- publishing manifests or advancing collection-visible watermarks,
- final fusion across metadata, semantic, and overlay lanes,
- final rerank / rescore ownership when broker applies a later stage,
- final aggregation reduction,
- metadata/text retrieval, selector materialization, or current-version write authority.

## 5. Runtime Placement and Request Model

### 5.1 Upstream / Downstream Boundary

```text
Query Broker
  -> Semantic Search Reader
       -> pinned committed manifest objects
       -> semantic-search NRT deltas
       -> local cache / object-store fetches
  <- Semantic Search Reader response
```

The reader consumes broker-owned dispatch context that already fixes:
- collection identity,
- applied schema lineage,
- pinned committed manifest identity,
- admitted `effective_read_ts`,
- one semantic physical subplan,
- optional metadata-produced prefilter payload,
- materialization and local budgeting intent.

Because these inputs are already fixed upstream, the reader must not reopen:
- query-admission choices,
- serving-generation selection,
- continuation legality,
- strategy selection or selectivity thresholds.

### 5.2 Reader RPC Shape

The dedicated semantic-reader RPC contract lives in `api/proto/searchfabric/v1/semantic_search_reader.proto`:
- `SemanticSearchReaderService.ExecuteSearch(SemanticSearchRequest) returns (SemanticSearchResponse)`
- `SemanticSearchReaderService.GetServingWatermarks(ReaderWatermarkRequest) returns (SemanticReaderWatermarks)`

That file intentionally reuses broker-owned shared execution carriers from `api/proto/searchfabric/v1/query_broker.proto`, including snapshot pinning, semantic subplan selection, lane budgets, and readiness-carrier types.

This internal boundary makes the reader a broker-owned execution worker, not an independent product surface. The request/response carriers are internal-only and remain subordinate to the public query contract.

## 6. State Model

### 6.1 Immutable Truth + Short-Lived Semantic Delta Overlay

For one admitted subplan, the reader reconstructs semantic state from two sources:

```text
visible_semantic_snapshot
  = pinned committed manifest semantic artifacts
    + publish-admitted semantic NRT delta window
```

The immutable side typically includes manifest-selected semantic artifacts such as:
- quantized vector codes,
- optional residual or higher-fidelity rerank payloads,
- graph adjacency/layer structures,
- doc-key to vector-entry mappings,
- commit/tombstone metadata,
- manifest-published ordinal domains used by bitmap-aligned prefilter transfer.

The mutable side is limited to `NRT Delta Store (Semantic Search)` and remains bounded by the flush-window retention and replay rules described in `docs/architecture/nrt-delta-store.md`.

### 6.2 Identity and Versioning

Semantic execution uses canonical collection-scoped identity:
- row identity: `(namespace_id, collection_id, doc_id)`
- visible-version order: `commit_ts`, then canonical tie-breaks `(source_partition, source_epoch, source_seq)` when needed upstream

Reader-local segment IDs, graph node IDs, cache keys, or shard partitions are implementation details only. They must not become cross-request correctness identity.

### 6.3 Prefilter Carriers

The internal broker/reader contract admits three prefilter payload forms:
- `ExplicitDocumentSet`
- `RoaringBitmapPrefilter`
- `BloomFilterPrefilter`

Reader requirements by payload class:
- `ExplicitDocumentSet` is executed as a bounded deterministic document set; it is the natural companion to `SEMANTIC_EXECUTION_STRATEGY_BRUTE_FORCE_DOC_SET`.
- `RoaringBitmapPrefilter` may be used only when its `ordinal_domain_id` matches the manifest-published ordinal space selected by the pinned semantic artifacts; mismatches must fail closed rather than silently reinterpret ordinals.
- `BloomFilterPrefilter` is an optimization-only pruning structure and may admit false positives; it must never be treated as a final correctness filter.

## 7. Snapshot and Visibility Rules

### 7.1 Reader-Local Invariant

`Semantic Search Reader` must evaluate every candidate under the broker-admitted snapshot boundary:

```text
visible(row_version) =
  pinned_committed_manifest_contains_base_state
  AND row_version.commit_ts <= effective_read_ts
  AND no later winning tombstone with commit_ts <= effective_read_ts hides the row
```

The exact storage representation may differ between immutable semantic artifacts and NRT state, but the visibility predicate must stay equivalent.

### 7.2 What the Reader Must Not Do

The reader must not:
- compute a fresh `safe_ts`,
- treat raw request `read_ts` as the execution boundary when broker supplied `effective_read_ts`,
- read unpublished semantic deltas,
- widen visibility beyond the pinned committed-manifest frontier,
- reinterpret continuation token lineage from current cluster state,
- silently degrade strict or bounded execution into best-effort locally.

If the requested snapshot was not admissible, that failure should already have been handled by `Query Gateway` / `Query Broker` through the canonical `SF_VISIBILITY_TIMEOUT` or related canonical error paths; the reader does not own those branches.

## 8. Execution Strategies and Data Flow

The semantic reader executes one explicit broker-selected path per subplan:

```text
broker request
  -> validate internal request coherence
  -> open pinned manifest-backed semantic artifacts
  -> validate optional prefilter payload against the pinned artifact domain
  -> execute selected semantic path
  -> apply MVCC visibility and tombstones
  -> merge publish-admitted semantic NRT deltas
  -> optional higher-fidelity rerank inside the semantic lane
  -> produce candidates / local_total_hits / materialized hits
  -> return broker-facing response
```

### 8.1 Pure ANN

`SEMANTIC_EXECUTION_STRATEGY_PURE_ANN` is the unconstrained semantic path:
- traverse the manifest-selected semantic index without a metadata-produced prefilter,
- respect any broker-pruned `target_tiers`,
- apply semantic-side visibility before emit,
- return the top candidate set requested by the subplan and lane budget.

This path is appropriate when no selective metadata-first pruning was requested or when broker concluded that metadata-side prefiltering would not improve total cost.

### 8.2 Masked ANN

`SEMANTIC_EXECUTION_STRATEGY_MASKED_ANN` executes ANN traversal with a metadata-produced pruning structure:
- use a Roaring bitmap when metadata and semantic ordinals are aligned for the pinned segment bundle,
- use a Bloom filter when an exact bitmap is unavailable but a compact pruning structure is still beneficial,
- enforce any broker-carried `final_filter` locally when the reader can evaluate it without changing canonical semantics,
- keep Bloom membership as a pruning hint only; final equivalence remains broker-owned.

The reader must not reinterpret a masked-ANN request as brute-force execution simply because the prefilter happens to be small. Strategy selection remains broker-owned.

### 8.3 Brute-Force Document Set Evaluation

`SEMANTIC_EXECUTION_STRATEGY_BRUTE_FORCE_DOC_SET` evaluates a bounded explicit document set produced upstream:
- skip graph traversal entirely,
- compute semantic distance over the explicit document set only,
- use the admitted semantic payload policy from the pinned manifest,
- preserve deterministic candidate ordering and tie-breaks within the bounded set.

This is the execution counterpart of the architecture's high-selectivity filtered-ANN path. It is correctness-equivalent to a semantic search restricted to the same document set; it is not a second public query mode.

### 8.4 Optional Semantic-Lane Rerank

The semantic lane may use higher-fidelity vector payload referenced by the pinned manifest to refine candidate ordering before returning to broker, for example when the first-stage semantic index is quantized but the admitted collection policy retains a richer rerank payload.

This local rerank remains subordinate to the public query contract:
- it must not invent a new public rerank mode,
- it must stay inside the same pinned snapshot,
- it must preserve deterministic tie-break behavior,
- later broker-owned `fetch_spec.rescore` remains a separate stage and is not replaced by this local refinement.

## 9. Cache, Object Access, and Warmup

### 9.1 Cache / Object Access

Local cache exists only as a latency optimization. Correctness must remain reconstructible from:
- the pinned committed manifest,
- the referenced immutable semantic objects,
- the publish-admitted semantic NRT window.

A cache miss may increase latency, but it must not change results.

### 9.2 Warmup and Ready-State Inputs

The core architecture already states that cold-start readers warm mandatory hot objects before they are treated as ready for routed traffic. For semantic readers, these mandatory hot objects usually include:
- ANN entry layers or equivalent traversal bootstrap state,
- bitmap/ordinal-domain metadata needed for masked ANN,
- enough manifest-selected segment headers to open the current semantic snapshot without cold-path ambiguity.

`GetServingWatermarks` is one input to broker readiness orchestration, but reader-local warmup does not create a second readiness authority. Broker still combines reader watermarks with the query-serving `latest READY` schema generation upstream.

## 10. Broker-Facing Response Construction

### 10.1 Candidate Output

The semantic reader returns broker-facing `Candidate` records, not final public hits. Depending on the subplan, each candidate may include:
- `doc_id`,
- `commit_ts`,
- semantic lane score contributions,
- optional sort values aligned to broker-requested sort keys,
- optional `materialized_hit` fragments for semantic-only or vector-dominant plans.

If `materialized_hit` is populated, the response must also carry the response-local dictionaries required to decode it deterministically:
- `projected_field_names`
- `highlight_field_names`
- `inner_hits_headers`

### 10.2 Local Total Hits and Warnings

`SemanticSearchResponse.local_total_hits` is stage-local only. It exists so semantic-only or vector-dominant plans can contribute to canonical top-level `track_total_hits` behavior when broker selected this lane as the truthful total-hit producer. It is not the final public `TotalHits` payload.

Warnings and stats are similarly broker-facing execution products. Public warning projection remains outside this component.

## 11. Failure Semantics and Guardrails

Reader failures still project through canonical `ErrorStatus`, but the exact outward mapping remains broker/gateway-owned. Local design rules:

1. Internal contract incoherence must fail closed before heavy work starts.
2. Prefilter ordinal-domain mismatch must fail closed rather than falling back to silent reinterpretation.
3. Cache/object-store unavailability is an execution failure, not a reason to widen visibility or skip the pinned manifest.
4. Reader-local budget pressure may truncate internal work only when that still preserves the canonical response contract chosen by broker; otherwise the reader must fail the lane and let broker classify the request outcome.
5. Query/read-side snapshot waiting remains upstream-owned; reader must not emit its own version of `SF_VISIBILITY_TIMEOUT`.

## 12. Non-Semantic Implementation Guidance

The checked-out architecture intentionally leaves some implementation choices open as long as they preserve the contract above:
- exact ANN data structure layout inside the semantic segment bundle,
- quantized-code organization and residual-payload packing,
- cache hierarchy between RAM and NVMe,
- local traversal parallelism and scheduling,
- the internal scoring kernels used to evaluate exact or approximate semantic distance.

Those choices may evolve for performance, but they must remain subordinate to:
- broker-owned physical-path selection,
- the pinned committed-manifest frontier,
- admitted `effective_read_ts`,
- canonical vector-schema and response-shape semantics.

## Appendix A: Rust MVP implementation (2026-04-22)

This repository contains a Rust MVP implementation of `Semantic Search Reader` intended to satisfy
the internal broker/reader RPC contract and broker readiness wiring.

Code anchors:
- Runtime crate: `rust/crates/semantic-search-reader`
- Binary: `rust/bins/sf-semantic-search-reader`
- Transport surface: `rust/crates/semantic-search-reader/src/service.rs`
- `GetServingWatermarks` implementation: `rust/crates/semantic-search-reader/src/watermarks.rs`
- `ExecuteSearch` baseline engine: `rust/crates/semantic-search-reader/src/execute.rs`
  - retrieval evaluator subset: `rust/crates/semantic-search-reader/src/retrieval.rs`
  - predicate evaluation subset: `rust/crates/semantic-search-reader/src/predicate.rs`
  - schema-generation object decoding: `rust/crates/semantic-search-reader/src/schema.rs` and `rust/crates/semantic-search-reader/src/schema_generation.rs`

MVP limitations (implementation notes, not contract changes):
- Immutable semantic segment execution supports vector-only retrieval trees (`VectorClause` plus `RetrievalGroup` children) for the immutable-artifact strategies (`PURE_ANN`, `MASKED_ANN`, `BRUTE_FORCE_DOC_SET`). Retrieval trees that contain non-vector clauses (text/nested/scalar) are still evaluated over the NRT delta window only in this revision.
- Immutable segment candidates support scalar/keyword field-sort extraction when semantic segment artifacts include the optional row-field payloads (semantic segment format v2).
- Semantic segment objects currently store raw F32 vectors plus a minimal adjacency graph and doc metadata only (no quantized codes or higher-fidelity rerank payload); semantic segment format v2 additionally carries optional row-wise scalar/list field payloads used for field-sort extraction.
- Field-sort value extraction supports scalar/keyword fields, multi-valued `SortMode` (`MIN/MAX`), empty-array-as-missing rules, and end-to-end `missing_value_policy` ordering semantics when combined with the broker comparator.
