# Query Broker

Date: 2026-03-18
Status: Frozen architecture note
Authors: Search Platform Architecture

Component: Query Plane (Read Path)

Scale target: 1B collections | 5 EB raw vector + metadata

## 1. Executive Summary

Query Broker is the stateless execution brain of the Search Fabric query plane. It accepts an already admitted canonical query from the Query Gateway component—entered through either `QueryGatewayService` or `RelationalQueryService` but always carrying canonical `QueryRequest` semantics by the time broker execution starts—pins one immutable query-serving snapshot lineage, selects a physical plan for metadata search and semantic search, dispatches parallel reader work, optionally overlays TiDB-private incremental rows for immediate SQL visibility, fuses results deterministically, and returns stateless continuation carriers.

Three properties dominate the design:

1. Persistent truth is S3 plus committed manifests. Broker state is ephemeral.
2. Visibility is determined only by TiDB TSO timestamps (`commit_ts`, `read_ts`, `effective_read_ts`, `safe_ts`).
3. Query continuity is token-driven, not registry-driven.

This document explains how Query Broker realizes the checked-out canonical contracts at production scale. It does not redefine public RPC fields, error codes, token legality, or query semantics owned by:

- `api/proto/searchfabric/v1/search_fabric.proto`
- internal owner file for this boundary: `api/proto/searchfabric/v1/query_broker.proto`
- `docs/specs/core/search-fabric-api.md`
- `docs/specs/core/error-codes.md`
- `docs/specs/core/search-fabric-architecture.md`

## 2. Scope and Boundary

### 2.1 In Scope

- Execution-time snapshot pinning and manifest resolution.
- Physical planning over canonical `QueryRequest` and `RetrievalNode` trees.
- Selectivity-aware planning for filtered vector retrieval.
- Parallel dispatch to Metadata Search Reader and Semantic Search Reader.
- Deterministic result fusion, LWW conflict resolution, and pagination.
- Broker-owned readiness lane internals.
- Stateless token interpretation and token minting from the broker side.
- Query-path interaction with background compaction and delayed GC.

### 2.2 Out of Scope

- Public query API design.
- Source DSL parsing or compatibility lowering.
- Public `GetCollectionStatistics` semantics. That product remains CCS-owned.
- Write-path ingestion, OCC, or single-document current-version authority.

Selector-materialization clarification:
- selector-driven write routes are not a second query API surface and do not make Query Broker write-authoritative,
- however, Ingest Gateway private by-query lanes may use the dedicated internal selector-materialization RPC after lowering their selector to canonical `QueryRequest` semantics.

### 2.3 Upstream and Downstream Boundaries

```text
Public / compatibility / TiDB callers
  -> Query Gateway
     -> Query Broker
     <- internal `QueryBrokerStreamEvent{public_frame|terminal_receipt}`
        -> Metadata Search Reader
        -> Semantic Search Reader
        -> Catalog Control Service (private readiness + governance reads only)
```

Broker never serves as a public metadata catalog, never owns query-session durability, and never bypasses Query Gateway for external/public traffic.

## 3. Local Design Invariants

This document applies the architecture input as local implementation rules while staying inside the repository's canonical contract stack.

| Driver | Local consequence inside Query Broker |
|---|---|
| S3 is the only durable truth | Broker may use RAM/NVMe caches, but must reconstruct correctness from committed manifests and S3 objects only. |
| TSO is the only time contract | Every visibility, tombstone, LWW, and continuation decision is expressed on `commit_ts` / `read_ts` / `effective_read_ts`. |
| Stateless broker | No PIT registry, page-session table, or close-tombstone store. Tokens are self-contained. |
| Strict CQRS | Broker reads query-plane snapshots only. It never becomes a write-authoritative source. |
| Delayed GC | Pinned manifests and their referenced objects must survive for at least the maximum admitted token lifetime. |
| Deterministic consistency | Strict and bounded-staleness semantics may fail closed, but must never silently degrade into best-effort execution. |
| Intent-driven schema | Broker consumes schema/storage intent and manifest tier annotations; it does not invent ad hoc storage-policy semantics per query. |

Two additional boundary rules are important:

1. Query Gateway remains the public admission and route-resolution owner.
2. Query Broker remains the authoritative interpreter of query continuation/search-context tokens and the authoritative owner of physical execution strategy.

## 4. Responsibilities and Non-Responsibilities

### 4.1 Responsibilities

- Interpret admitted canonical query requests.
- Pin the latest query-serving `READY` schema generation for first-page execution.
- Reconstruct pinned snapshot state from validated continuation tokens.
- Resolve the reader-visible committed manifest via `manifest_committed_head.json` on first page.
- Re-open the exact pinned manifest on continuation without depending on mutable gateway or CCS state.
- Build a physical execution DAG from the recursive retrieval tree.
- Choose vector-filter execution strategy using selectivity-aware planning.
- Dispatch subplans on one immutable snapshot-lineage boundary.
- Combine reader outputs that already reflect S3-backed segments plus query-visible modality-local `NRT Delta Store` state.
- Optionally merge a TiDB-private in-memory overlay for immediate SQL visibility on the trusted TiDB path.
- Apply canonical request-level fusion semantics and deterministic final ordering.
- Mint fresh `next_page.token`, `search_context.token`, and `composite_page.token` carriers when the response contract requires them.

### 4.2 Non-Responsibilities

- Public authentication, caller identity derivation, or collection name binding lookup.
- Public RCU lease ownership or 1B-collection negative caching. Query Gateway owns those.
- Public query-shape lowering from Elasticsearch, Turbopuffer, or TiDB SQL syntax.
- Persisting per-query cursor state, PIT registries, or invalidation tombstones.
- Synthesizing public descriptor metadata, schema lineage, or statistics products outside their canonical owners.

## 5. Runtime Model

### 5.1 Stateless Process, Ephemeral Caches

Broker instances are stateless with respect to correctness. They may keep ephemeral, rebuildable caches such as:

- recently read `manifest_committed_head.json` objects,
- immutable manifest blobs,
- reader capability snapshots,
- short-lived score-normalization statistics,
- broker-membership and namespace-pool routing hints.

None of these caches may be required to continue a query after restart. If all caches are lost, the broker must still be able to resume a valid continuation from the token payload, the pinned manifest/S3 objects, and the broker-owned retained-lineage admissibility checks required by the canonical continuation contract.

### 5.2 Recommended Tokio Shape

- one request task owns immutable request context and cancellation fanout,
- one planning task builds the execution DAG,
- subqueries run as bounded `tokio::spawn` tasks inside the namespace's assigned isolation pool,
- one reducer/finalizer task performs fusion, broker-local aggregation reduction, final ordering/page slicing, and token minting,
- any fatal lane failure triggers cooperative cancellation of sibling tasks.

### 5.3 Recommended Internal Data Structures

```text
PinnedSnapshot {
  lineage {
    collection_ref
    collection_truth_mode
    read_mode
    required_read_ts
    effective_read_ts
    applied_schema_generation_token
    applied_schema_version
    committed_manifest_version
    committed_manifest_etag_or_checksum
    continuation_lineage_id
    canonical_query_digest
    overlay {
      snapshot_read_ts
      overlay_digest
      row_count
    }
  }
  execution {
    observed_safe_ts
    issued_token_expires_at_unix_ms
  }
}

ExecutionPlan {
  target_tiers
  metadata_subplans[]
  semantic_subplans[]
  aggregation_subplans[]
  facet_subplans[]
  overlay_plan?
  overfetch_limit
  lane_timeout_ms
}
```

The broker may choose different concrete Rust types, but `PinnedSnapshot.lineage`, not an abridged subset, is the immutable correctness boundary for token validation, continuation replay, and relational overlay replay. `PinnedSnapshot.execution` is per-dispatch observability and token-issuance state; it may vary across broker executions in the same lineage without changing continuation correctness.

## 6. Request Classes

Broker execution is easiest to reason about when requests are split into six internal classes:

1. `ExecuteQuery` first page.
2. `ExecuteQuery` hit continuation using `next_page.token` and optional same-lineage `search_context.token`.
3. `ExecuteQuery` composite continuation using optional compatibility `CompositeAggregation.after_key` plus authoritative `composite_page.token`.
4. `StreamQuery` first page or resume using `continuation_page.next_page_token`.
5. `GetReadiness` via the broker-owned readiness lane.
6. Private selector-materialization probes for by-query write lanes through the dedicated internal selector-materialization RPC.

Important contract clarifications:

- `search_context.token` binds one snapshot lineage and carries no page-local cursor state.
- `next_page.token` is page-local cursor state plus the same immutable snapshot-lineage tuple.
- `composite_page.token` is composite-page-local cursor state plus the same immutable snapshot-lineage tuple.
- The broker must not collapse those token families into one state carrier.
- ordinary unary continuation may carry both token families at once; when both are present, they must reference the same immutable snapshot lineage and `search_context.token` remains keep-alive / lineage state rather than cursor state.
- `StreamQuery` does not accept request-side `search_context.token` in the active canonical contract.
- public `StreamQuery` may omit `RequestMeta.idempotency_key` and `RequestMeta.correlation_id`; if supplied, those fields remain logical request/audit correlation metadata only.
- Query Gateway must mint a separate per-attempt `StreamAttemptId` before every internal broker stream dispatch/resume, and the internal proto validation layer must reject empty `StreamAttemptId.value` values on both the request and terminal-receipt paths, so stream-side reconciliation has one deterministic execution key.
- selector-driven write lanes use a dedicated broker RPC that returns only deterministic selector materialization outputs (`doc_id`, `version_commit_ts`, `has_more_hits`) plus the normal execution receipt.

### 6.1 Private Selector-Materialization RPC

The checked-out owner set already routes selector-driven write lanes through Query Broker snapshot semantics (`docs/specs/core/search-fabric-architecture.md`) and requires canonical selector-materialization outputs (`docs/specs/core/search-fabric-api.md`). The concrete boundary choice in this revision is:

- Ingest Gateway lowers selector-style write probes (`update_by_query`, `delete_by_query`, `patch_by_filter`, and equivalent compatibility lanes) into canonical unary `QueryRequest` semantics,
- those probes dispatch through the dedicated internal RPC `QueryBrokerInternalService.MaterializeSelector`,
- the general unary query RPC remains query-serving-only.

Selector-materialization expectations on that dedicated path:

- broker must return hits in deterministic `doc_id asc` order when the selector lowering asks for ordered materialization,
- returned `(doc_id, version_commit_ts)` pairs are consumed as internal materialization inputs so later mutations can carry deterministic compare-and-set fences,
- the response-local `has_more_hits` bit is the broker-side signal used by Gateway to enforce the canonical selector-materialization clamp contract (`selector_materialization_limit`) without reusing general query response payload semantics.

## 7. Snapshot Pinning and Environment Reconstruction

### 7.1 First-Page Execution

For a first-page unary query, the Query Gateway component has already normalized the request and performed public admission through either `QueryGatewayService` or `RelationalQueryService`. Broker then performs execution-time pinning over either a shared canonical `QueryRequest`, a trusted `ProjectedQueryRequest`, or a trusted `PrimaryQueryRequest`, with the inner `query` remaining the canonical semantic payload. For projected-query dispatches, the Query Gateway component has also already resolved every projected-route fallback branch before broker dispatch, so the broker path is success/error only:

1. Re-validate collection identity, `collection_truth_mode`, and expected query-serving lineage.
2. Resolve the latest query-serving `READY` schema generation through the broker readiness lane.
3. Use the admitted `effective_read_ts` from the gateway-owned consistency flow. Overlay-bearing projected best-effort dispatches are the explicit exception to the default best-effort `safe_ts` rule: they must already arrive with `effective_read_ts = snapshot_read_ts`.
4. Read the private committed-manifest pointer object:

```text
s3://bucket/catalog/{hash(namespace_id)[0:2]}/{namespace_id}/{collection_id}/manifest_committed_head.json
```

5. Open the committed manifest object referenced by that pointer and pin:
   - collection identity and resolved `collection_truth_mode`,
   - original caller consistency fence (`read_mode`, `required_read_ts`),
   - admitted `effective_read_ts`,
   - applied schema generation token/version,
   - committed manifest version,
   - immutable checksum or object etag,
   - canonical query digest,
   - continuation lineage identity,
   - relational overlay snapshot binding (`snapshot_read_ts`, overlay digest, row count) when the admitted request carries `snapshot_overlay`,
   - execution-local observed `safe_ts`,
   - execution-local token-expiry horizon when this dispatch may mint `next_page.token`, `composite_page.token`, or `search_context.token`.

The output of this phase is one `PinnedSnapshot`. Its `lineage` remains unchanged for the entire request and any derived continuation chain. Its `execution` portion is scoped to the current broker dispatch and may differ across later keep-alive refreshes or continuation executions in the same lineage.

### 7.2 Continuation Execution

Continuation requests must reconstruct the full environment from validated token payloads rather than any mutable server-side registry.

Rules:

- validate signature, caller binding, scope binding, token purpose, and expiry via the shared query-token library,
- verify `canonical_query_digest` matches the admitted request,
- verify collection identity and client identity bindings,
- verify the token-bound `applied_schema_generation_token` still names a retained query-admissible serving lineage; this check MUST NOT require equality with the current newest `READY` generation, but it MUST fail closed through the canonical query-schema/token conflict path (`SF_VERSION_CONFLICT`) once the token-bound lineage is no longer retained/query-admissible, and when continuation supplied both page and search-context token families the canonical `conflict_source` must resolve to the page-token branch for that request shape (`continuation_page_token` for ordinary continuation, `composite_page_token` for composite continuation) rather than `search_context_token`,
- if the continuation route resubmits `snapshot_overlay`, verify the resubmitted canonical columnar overlay semantics and `snapshot_read_ts` still match the token-bound overlay digest/binding before any planning or reader work starts; see Section `10`,
- use the token-bound `effective_read_ts` and manifest pin,
- derive the requested continuation depth from the token-bound page ordinal before planning any reader work; if that next depth exceeds service-configured `max_page_depth`, reject deterministically as canonical `SF_TOO_MANY_OPERATIONS` with `limit_scope=max_page_depth`, `configured_limit`, and `requested_value`,
- open the exact pinned manifest version directly and validate the pinned checksum/etag,
- reject drifted or mismatched continuation attempts before any reader work starts.

The broker must not require the current `manifest_committed_head.json` to still point at the pinned manifest version. Head advancement is expected. Delayed GC is what preserves the old version long enough for valid continuation.

### 7.3 Best-Effort Continuation Rule

For best-effort first pages, the gateway normally admits execution at current `safe_ts`. Overlay-bearing projected-query execution is the explicit exception: gateway admits it at `snapshot_read_ts`, and broker must re-validate `effective_read_ts = snapshot_read_ts` before planning. Continuations must reuse the token-bound `effective_read_ts`; they must not recompute against a newer `safe_ts`.

## 8. Physical Planning Pipeline

Broker transforms the recursive logical retrieval tree into a bounded execution DAG.

```text
PinnedSnapshot
  -> Logical tree inspection
  -> Selectivity-aware CBO
  -> Reader dispatch DAG
  -> Parallel execution
  -> Fusion
  -> Aggregation Reduction
  -> Page Slicing
  -> Token Minting
```

### 8.1 Phase 1: Context and Snapshot Pinning

Inputs:

- admitted canonical `QueryRequest`, or trusted relational route request (`ProjectedQueryRequest` / `PrimaryQueryRequest`) whose inner `query` remains the canonical semantic payload,
- optional `next_page.token`,
- optional `search_context.token`,
- broker-internal execution budget and pool assignment.

Projected-route boundary note:

- a `ProjectedQueryRequest` reaches this phase only after Query Gateway has already selected between canonical error, caller-native fallback, and broker dispatch,
- therefore this planning pipeline never synthesizes the projected-route fallback normal result itself.

Outputs:

- immutable `PinnedSnapshot.lineage`,
- per-execution `PinnedSnapshot.execution`,
- lane deadlines derived from the remaining RPC deadline,
- runtime feature gates (for example tier pruning support and nested-query support).

Defensive checks performed here:

- token family legality,
- request-vs-token digest drift,
- token-bound schema-generation lineage no longer retained or no longer query-admissible,
- projected best-effort overlay equality (`projected_query.snapshot_overlay.rows` non-empty + `BEST_EFFORT` => `admission.effective_read_ts = projected_query.snapshot_read_ts`),
- schema bootstrap and schema-convergence availability,
- manifest availability,
- namespace pool availability,
- broker-local overload or stale admission envelope.

If a streamed attempt terminates fatally during these checks before `PinnedSnapshot` exists, the internal terminal receipt must use the explicit early-failure execution-context branch rather than fabricating a partial `QueryBrokerExecutionReceipt`.

### 8.2 Phase 2: Selectivity-Aware CBO

For vector retrieval with hard filters, the planner must respect the canonical rule that `global_filter` remains authoritative and clause-local filters are conjuncted before ANN candidate generation.

Recommended planning flow:

1. Normalize the hard filter that applies to the vector clause:
   - top-level vector clause -> `global_filter AND local_filter`
   - nested vector clause -> `child_filter AND local_filter`
2. Ask Metadata Search Reader for a same-snapshot `ESTIMATE` or `COUNT`.
3. Choose one of the following physical paths:

```text
Path A: Highly selective
  -> materialize exact doc_id set
  -> run bounded brute-force / flat distance computation

Path B: Moderately selective
  -> build roaring bitmap or bloom filter
  -> run masked ANN traversal

Path C: Not selective
  -> pure ANN traversal
  -> apply final eligibility check after candidate materialization
```

Recommended initial threshold:

- `N_filtered < 10_000` -> Path A

This threshold is not part of the public contract. It is a calibrated production default that must be trace-tuned.

Additional planning rules:

- broker must dispatch one explicit semantic physical path to the semantic reader (`brute-force doc set`, `masked ANN`, or `pure ANN`) rather than delegating that choice back through an `auto` subplan mode,
- read the admitted `search_payload.routing.target_tiers[]` filter from the canonical request and apply it against manifest-published `DataTier` annotations in the pinned snapshot before opening irrelevant objects,
- keep request-level fusion semantics authoritative,
- overfetch only enough to cover later fusion, LWW resolution, and sort pruning,
- never use planning shortcuts that weaken filter correctness.

### 8.3 Phase 3: Parallel Dispatch and Hybrid Gathering

After planning, broker dispatches reader-facing lane work and prepares any broker-local post-lane reducers:

- Metadata Search Reader for scalar filters, text ranking, and bucket-local pre-aggregation work,
- Semantic Search Reader for vector retrieval,
- optional overlay evaluator for TiDB-private immediate-visibility rows,
- optional broker-local aggregation reducers that consume metadata partials and/or the broker-final candidate set after lane gathering.

Dispatch rules:

- each lane carries the same `PinnedSnapshot`,
- each lane gets an explicit timeout derived from the remaining request deadline,
- each lane runs inside the namespace's isolation pool,
- strict failure handling cancels sibling lanes immediately when any required lane fails.

The broker must treat object-store timeouts, reader RPC timeouts, and in-process overlay failures as execution failures. Strict or bounded-staleness request modes do not authorize fallback to a different snapshot or silent downgrade to best-effort.

Reader-facing aggregation rule:

- reader RPCs must return shard-local partial aggregation state, not final public `AggregationResponse` trees,
- metadata-reader aggregation pushdown remains the only reader-side aggregation path in this revision,
- shard-local metric partials must stay mergeable for every metric family delegated to broker-final reduction:
  - `COUNT` / `SUM` / `MIN` / `MAX` may use one reduced scalar contribution,
  - `AVG` must carry at least shard-local `sum + count`,
  - `cardinality` must carry a mergeable sketch state rather than a display-ready scalar,
- `AggregationReductionPlan` stages are reserved for broker reduction of metadata-reader partial aggregation outputs; semantic/vector-dominant result-set facets do not create reader-fed aggregation reducer stages in this revision,
- composite aggregation execution therefore stays split as documented by the canonical API:
  - readers prune local buckets against `after_key`,
  - readers emit sorted shard-local partial buckets,
  - broker performs the final k-way merge, truncation accounting, and public `after_key` minting.

Semantic top-K facet rule:

- when an admitted query shape is semantic-only or vector-dominant, broker must treat `aggregations` as result-set facets over the broker-final candidate set instead of dispatching aggregation work to the semantic reader,
- semantic reader RPCs therefore stay retrieval/materialization/total-hit only and must not return shard-local aggregation partials,
- broker must evaluate semantic/vector-dominant facets only after same-snapshot lane gathering, visibility filtering, LWW resolution, request-level fusion, query-level deduplication, the canonical final `search_payload.global_filter` re-check, and any optional admitted rerank,
- broker must evaluate those facets before final page slicing so request pagination controls do not perturb facet outputs for one fixed admitted candidate set,
- the supported semantic/vector-dominant facet subset is intentionally narrow in this revision: scalar metric nodes plus non-composite top-level bucket nodes (`terms` / `histogram` / `date_histogram` / `range`) over top-level scalar fields,
- broker must fail closed as `SF_UNSUPPORTED_QUERY_FEATURE` if a semantic-only or vector-dominant result-set facet tree contains `composite`, `top_hits`, `nested`, or `reverse_nested`,
- these semantic/vector-dominant facets are UX-oriented search-result summaries, not exact full-match-set analytics.
- `QUERY_BROKER_PHASE_AGGREGATION_REDUCTION` covers both metadata partial aggregation merge and semantic/vector-dominant broker-local facet evaluation, while `QUERY_BROKER_PHASE_FUSION` is reserved for hit fusion / LWW / dedup / final ordering.
- `QueryBrokerExecutionPlan.aggregation_subplans[]` remains metadata-reader partial reduction only, while `QueryBrokerExecutionPlan.facet_subplans[]` is reserved for semantic/vector-dominant broker-local facet evaluation.
- `QueryBrokerExecutionReceipt` must therefore split `fusion_input_candidates` / `fusion_output_candidates` from `metadata_aggregation_partial_inputs` / `metadata_aggregation_output_entries` and `facet_reduction_input_candidates` / `facet_reduction_output_entries`.
- aggregation-reduction / terminal-receipt observability invariants for this component note are tracked by `docs/testing/api-contract-conformance-matrix.md` invariant `SF-CONF-QUERY-004`.

Reader-facing materialization rule:

- metadata readers and semantic readers may both populate `Candidate.materialized_hit` when the broker dispatches explicit materialization intent for that lane,
- any reader response that populates `materialized_hit` must also return response-local `projected_field_names`, `highlight_field_names`, and `inner_hits_headers` dictionaries so the broker can reconstruct the canonical top-level projection contract deterministically,
- semantic-only plans therefore remain legal without forcing a metadata materialization lane, but they must satisfy the same header-alignment contract as metadata materialization.

Reader-facing total-hit rule:

- every lane that can be the sole producer for an admitted query shape must also be able to contribute canonical top-level total-hit state,
- metadata readers return that contribution through `local_total_hits`,
- semantic readers must return the same `local_total_hits` carrier whenever the dispatched semantic subplan is responsible for satisfying top-level `track_total_hits`,
- whenever a lane emits `local_total_hits`, `relation` must be `TOTAL_HITS_RELATION_EQUAL_TO` or `TOTAL_HITS_RELATION_GREATER_THAN_OR_EQUAL_TO`; `TOTAL_HITS_RELATION_UNSPECIFIED` is an internal contract violation,
- broker finalization therefore computes `QuerySuccessPayload.total_hits` symmetrically from metadata and semantic lane contributions instead of assuming a metadata lane is always present.

### 8.4 Phase 4: Fusion

Lane outputs are reduced in five deterministic steps:

1. Convert all candidates to one reduction shape:

```text
Candidate {
  doc_id
  commit_ts
  is_deleted
  per_lane_scores
  projected_values?
  sort_values?
}
```

2. Resolve version conflicts by `commit_ts`:
   - keep only the highest-`commit_ts` version per `doc_id`,
   - if the highest version is a tombstone, suppress the document.

3. Apply request-level fusion semantics:
   - `GROUP_FUSION_STRATEGY_WEIGHTED_SUM` remains additive and is still the default for `UNSPECIFIED`,
   - `GROUP_FUSION_STRATEGY_RRF` remains request-visible when the canonical request asked for it,
   - broker-local heuristic RRF is permitted only when it realizes an internally synthesized physical split without changing the externally admitted fusion semantics.

4. Re-check canonical final eligibility and apply optional rerank:
   - re-check `search_payload.global_filter` on the materialized candidate set after LWW / fusion / dedup, regardless of which physical path produced those candidates,
   - do this before any semantic/vector-dominant facet evaluation, page slicing, token minting, or response/frame emission,
   - when admitted by profile/policy, execute `fetch_spec.rescore` only after that final eligibility gate and keep deterministic `doc_id asc` tie-break behavior on equal rerank scores.

5. Apply deterministic final ordering:
   - use admitted `fetch_spec.sort_keys`,
   - always preserve the canonical final `doc_id asc` tie-break,
   - emit caller-visible order only after LWW, any query-level `dedup_by_doc_id` collapse, the final `global_filter` re-check, and any admitted rerank.

Recommended guardrail:

- retain at most `ceil(K * 1.5)` candidates after intermediate fusion and before final sort, where `K` is the effective hit window needed by the response.

Observability rule:

- `QUERY_BROKER_PHASE_FUSION` and the receipt counters `fusion_input_candidates` / `fusion_output_candidates` describe only candidate-shape work through LWW, request-level fusion, dedup, the canonical final `global_filter` re-check, any admitted rerank, and final ordering.

### 8.5 Phase 5: Aggregation Reduction

Broker-local aggregation reduction runs after lane gathering has produced all reader partials and, when the admitted aggregation universe is result-set-facet-based, after fusion establishes the broker-final candidate universe. It completes before page slicing or final unary response emission.

Rules:

- metadata-reader partial aggregation merge runs under `QUERY_BROKER_PHASE_AGGREGATION_REDUCTION` and is described by `QueryBrokerExecutionPlan.aggregation_subplans[]`,
- semantic/vector-dominant broker-local facet evaluation also runs under `QUERY_BROKER_PHASE_AGGREGATION_REDUCTION` and is described by `QueryBrokerExecutionPlan.facet_subplans[]`,
- admitted rerank, when enabled, already completed in Phase 4 before this reduction phase starts and does not create a separate execution phase in the current revision,
- `metadata_aggregation_partial_inputs` counts the shard-local partial aggregation roots / merge units consumed by metadata partial reduction,
- `metadata_aggregation_output_entries` counts the final aggregation buckets / metric leaves emitted by metadata partial reduction,
- `facet_reduction_input_candidates` counts the broker-final candidate universe inspected by semantic/vector-dominant facet evaluation,
- `facet_reduction_output_entries` counts the final aggregation buckets / metric leaves emitted by semantic/vector-dominant facet evaluation,
- page slicing must happen only after this phase completes whenever the admitted query carries aggregations.

### 8.6 `StreamQuery` Frame Realization

`StreamQuery` reuses the same snapshot-pinning, planning, dispatch, and reduction pipeline as unary hit execution, but the final broker stage emits an ordered frame stream instead of one unary body.

Rules:

- one admitted stream attempt pins one immutable `PinnedSnapshot.lineage`; reconnect/resume reconstructs that same lineage from the latest non-empty `next_page.token` rather than opening a newer snapshot,
- Query Gateway must forward the internal broker stream RPC with a validator-enforced non-empty per-attempt `StreamAttemptId.value`; public `RequestMeta.correlation_id` is mirrored only as optional logical correlation metadata when present,
- broker slices the already reduced, LWW-clean, fully ordered hit stream into transport frames without changing canonical sort order,
- the first successful frame carrying one or more hits must emit the response-local dictionaries (`projected_field_names`, `highlight_field_names`, `inner_hits_headers`),
- later hit-bearing success frames in the same RPC attempt must leave those dictionaries empty,
- every successful frame carrying one or more hits must mint a non-empty `success.pagination.next_page.token` that resumes immediately after the last emitted hit in that frame,
- terminal zero-hit success is legal only for the final no-more-hits completion frame; it must not consume the dictionary-emission opportunity and must leave `next_page.token` empty,
- only the terminal success frame may carry `warnings` or `total_hits`; if terminal semantics are degraded/non-exact, that frame must use the canonical `partial` branch,
- StreamQuery token/dictionary invariants for this component note are tracked by `docs/testing/api-contract-conformance-matrix.md` invariant `SF-CONF-QUERY-001`,
- broker-side execution accounting stays on the same internal stream as one final `QueryBrokerStreamEvent.terminal_receipt` keyed by the same internal `stream_attempt_id.value`; broker emits that terminal receipt for every terminal streamed attempt it successfully classifies: terminal success, terminal in-band error after one or more frames, broker-classified post-admission downstream/gateway termination through the internal-only `cancelled` branch, and pre-first-frame fatal failure classification,
- that terminal receipt is stage-aware:
  - attempts that already established `PinnedSnapshot` use the full `receipt` branch carrying `QueryBrokerExecutionReceipt`,
  - fatal attempts classified before `PinnedSnapshot` exists use the explicit early-failure branch,
  - `terminal_outcome` still carries the authoritative final attempt outcome for both shapes.
- if the first internal broker event is a terminal error receipt, Query Gateway maps it back to the public pre-first-frame non-`OK` status contract rather than forwarding a synthetic in-band error frame.
- once a terminal stream outcome becomes locally observable at Query Gateway (terminal success/error frame consumed, client-side termination classified, or pre-first-frame broker failure classified), Gateway keeps reading until it observes that same attempt's terminal receipt or the internal broker stream ends; if the stream ends first, Gateway must run deterministic fallback reconciliation (pessimistic lease release, estimator sample marked incomplete/non-authoritative, metric/alert emission).

## 9. Score Alignment

Hybrid retrieval often returns incomparable raw score domains. Broker therefore needs one normalization layer before final fusion.

Recommended order of preference:

1. If request-level fusion is explicit, honor it exactly.
2. Otherwise use deterministic per-lane local normalization that preserves request-level semantics.
3. Use broker-local heuristic RRF only when the physical plan was synthesized internally and doing so does not change the public canonical fusion contract.

This document intentionally does not depend on a CCS-published score-normalization artifact.
If a future revision introduces one, that dependency must be defined first by the canonical API/architecture owners rather than inferred here.

This preserves the core-architecture rule: broker heuristics may improve physical execution, but they must not replace admitted request semantics.

## 10. TiDB Immediate-Visibility Overlay (Private RYW Path)

### 10.1 Why This Exists

The canonical core architecture already states that immediate read-your-write is a TiDB SQL contract on the trusted TiDB query surface when TiDB provides private DeltaRows for the SQL snapshot being served. Retrieval-native public query APIs remain governed by `safe_ts`-bounded asynchronous projection semantics.

Broker therefore supports a private, trusted overlay path for TiDB compute without changing the public query contract.

### 10.2 Boundary Rule

This overlay is not a field on shared `QueryRequest`.
It is carried only inside the projected-route `ProjectedQueryRequest` envelope on trusted relational query hops. In the current deployment, TiDB is the caller that uses this wrapper.

### 10.3 Recommended Overlay Shape

The overlay should stay scoped to the fields the admitted query actually needs.
The checked-in contract shape is:

```text
ProjectedQueryRequest {
  query
  snapshot_overlay
  snapshot_read_ts
}

SnapshotOverlay {
  columns[]
  rows[]
}

SnapshotOverlayColumn {
  field_path
}

SnapshotOverlayRow {
  doc_id
  commit_ts
  is_deleted
  values[]   // index-aligned with columns[]
}
```

The overlay must be small enough that brute-force evaluation is acceptable.

Mandatory guardrail:

- hard cap: 1000 rows per request

Recommended additional guardrail:

- hard cap the serialized overlay payload as part of the internal TiDB integration policy, even if row count stays below 1000.
- require `snapshot_overlay.columns[]` to be byte-unique after normalization and sorted by byte-ascending `field_path`.
- require no-overlay state to follow the canonical implementation-validated/conformance-backed split: `snapshot_overlay.columns=[]`, `snapshot_overlay.rows=[]`, `snapshot_read_ts=0`.
- require overlay-present state to use non-empty canonicalized `snapshot_overlay.columns[]`, non-empty `snapshot_overlay.rows[]`, and `snapshot_read_ts>0`.
- reject duplicate `(doc_id, commit_ts)` row identities as malformed before planning begins.

### 10.4 Overlay Evaluation

`snapshot_read_ts` is the authoritative relational-engine visibility fence for overlay rows.
For strict/bounded reads it matches the canonical query `read_ts`.
For best-effort reads, it stays separate from shared `QueryRequest.consistency.read_ts=0`, closes the otherwise-undefined snapshot rule for TiDB-private overlay visibility, and becomes the authoritative broker execution snapshot (`effective_read_ts = snapshot_read_ts`) for the whole projected-query dispatch.

When `snapshot_overlay.rows` is non-empty, the admitted query shape is intentionally restricted.
Overlay-bearing requests are flat hit-retrieval only:

- no `aggregations`,
- no nested retrieval or nested predicates,
- no `top_hits` / `inner_hits`,
- no highlighting,
- no rescore.

If a TiDB overlay-bearing request asks for any of those features, admission must fail closed as unsupported rather than silently dropping semantics.

For each overlay row:

1. Ignore rows with `commit_ts > snapshot_read_ts`.
2. Evaluate the same canonical hard filter used for S3/NRT candidates.
3. For text clauses, reuse the same analyzer selection, text mode semantics, and clause-scoring pipeline that the admitted query would use on persisted reader lanes. In this revision, projected-overlay text evaluation supports analyzers `builtin_keyword_v1`, `builtin_standard_v1`, `builtin_standard_folded_v1`, and `builtin_ngram_v1`; `builtin_mecab_ja_v1` MUST fail closed as `SF_UNSUPPORTED_OPTION` on overlay-bearing requests.
4. For vector clauses, use exact brute-force distance on the in-memory vectors when the row supplies the queried vector field.
5. Emit overlay candidates in the same reduction format and lane-local score scale as reader results so later fusion and pagination stay canonical.

Overlay rows represent the authoritative latest visible version for a document at `snapshot_read_ts`.
If an overlay row is visible but fails the canonical hard filter or retrieval matching, Query Broker MUST
NOT return an older persisted match for the same `doc_id`. Instead, it should treat that overlay row as
a query-time tombstone at the overlay row's `(doc_id, commit_ts)` so later LWW fusion suppresses older
versions deterministically.

Then merge overlay candidates with S3/NRT candidates using the same LWW rule:

- greater `commit_ts` wins,
- tombstone wins if it is the latest visible version.

The overlay is therefore a third read layer:

```text
S3 committed snapshot
  + query-visible NRT deltas
  + TiDB-private overlay rows
```

Only the last layer is TiDB-private.
Continuation correctness requires the same canonical columnar overlay semantics plus the same `snapshot_read_ts` on every follow-up page request.
Broker-side token validation should therefore bind a canonical overlay digest computed over:

- `snapshot_overlay.columns[]` in canonical column order,
- overlay rows sorted by `(doc_id, commit_ts)` ascending,
- per-cell `FieldData` values under the same float/NaN normalization rules used by `canonical_query_digest`.

Continuation legality must compare that semantic digest rather than caller-preserved row order or protobuf/container encoding accidents.

## 11. Stateless Token Model

### 11.1 Token Families

Broker works with three query continuation carriers:

1. `search_context.token`
2. `next_page.token`
3. `composite_page.token`

They share the same immutable snapshot-lineage binding tuple, but they are not interchangeable.
If one broker execution mints multiple families in the same response, they still remain distinct carriers but must share one execution-local expiry horizon.

### 11.2 `search_context.token`

Purpose:

- pin one snapshot lineage across unary pagination,
- support keep-alive refresh,
- support unary keep-alive lineage refresh independently from composite continuation, whose authoritative cursor state lives in `composite_page.token`.

Required semantic content:

- immutable snapshot-lineage tuple:
  - `canonical_query_digest`
  - `effective_read_ts`
  - `applied_schema_generation_token`
  - committed manifest pin
  - collection identity
  - continuation lineage identity
- `snapshot_read_ts` and overlay digest when the projected-query envelope carried `snapshot_overlay`
- token expiry

This token must not encode page-local cursor or page ordinal state.

### 11.3 `next_page.token`

Purpose:

- continue ordinary page-by-page hit iteration at the same snapshot.

Required semantic content:

- the same immutable snapshot-lineage tuple as `search_context.token`,
- page ordinal,
- the last-emitted canonical sort cursor,
- the forced final `doc_id asc` tie-break state.
- when the request uses `ProjectedQueryRequest.snapshot_overlay`, the same `snapshot_read_ts` and overlay digest binding.

### 11.4 `composite_page.token`

Purpose:

- continue composite-aggregation bucket iteration at the same snapshot,
- bind composite page depth deterministically so `max_page_depth` does not depend on hidden server state.

Required semantic content:

- the same immutable snapshot-lineage tuple as `search_context.token`,
- composite page ordinal,
- the canonical emitted composite cursor (`BucketResult.composite.after_key.fields`) used as the authoritative continuation cursor; any request-side `CompositeAggregation.after_key` is only an optional compatibility echo and, when present, must match this token-bound cursor,
- when the request uses `ProjectedQueryRequest.snapshot_overlay`, the same `snapshot_read_ts` and overlay digest binding.

This token must not be replaced by `search_context.token`; `search_context.token` remains lineage-only state.

### 11.5 Signature and Rotation

Broker should consume and mint tokens only through the shared query-token library.

Recommended realization:

- one shared signed binary wrapper format:
  - `Version | Key_ID | Payload_Length | Canonical_Payload | Signature`
- explicit `key_id`,
- deterministic typed payloads per token purpose (`query_page`, `query_search_context`, `query_composite_page`),
- signature/MAC over the wrapper bytes excluding the trailing `Signature` (plus any fixed domain-separation prefix); the Rust runtime currently uses a 32-byte keyed BLAKE3 MAC,
- bounded verification overlap during key rotation,
- no ad hoc JSON maps assembled in handlers,
- no broker-local competing token envelope or broker-only token family.

One broker execution may mint `next_page.token`, `search_context.token`, `composite_page.token`, or any combination of them. When it mints more than one, all emitted tokens MUST carry the same expiry; that shared expiry belongs to execution-local issuance state rather than immutable snapshot lineage.

### 11.6 Continuation Rules

```text
First page:
  request.keep_alive_ms? -> broker may return search_context.token
  more_hits?             -> broker may return next_page.token

Ordinary continuation:
  request.next_page.token + optional search_context.token -> broker resumes from last-emitted sort cursor on the same snapshot lineage and may refresh keep-alive state

Composite continuation:
  request.composite_page.token + optional request.after_key -> broker resumes same snapshot and uses the token-bound cursor as authoritative continuation state
```

If the response emits a non-empty composite `after_key`, broker must return `composite_page.token` for the continuation chain; `search_context.token` remains an optional unary keep-alive carrier rather than a mandatory composite companion.
For any unary or stream resume path that presents a continuation token, broker owns the next-depth check from the token-bound page ordinal and must perform the canonical `max_page_depth` rejection before planning or reader dispatch. That deterministic check applies to both ordinary `next_page.token` and composite `composite_page.token`.

Keep-alive behavior:

- refreshing `search_context.token` must not change `PinnedSnapshot.lineage`; it may only advance token expiry for the same lineage,
- older unexpired tokens in the same lineage remain valid until natural expiry,
- no close registry or invalidate-all side effect exists in the broker.

## 12. Broker-Owned Readiness Lane

Broker owns the internal join used by `GetReadiness` and by first-page visibility admission.

```text
Query Gateway
  -> Query Broker readiness lane
     -> reader watermarks
     -> CCS serving-generation facts
  -> readiness response / admission decision
```

Returned readiness state must reflect:

- current query-serving `safe_ts`,
- latest `READY` schema generation,
- serving watermarks by enabled modality.

This lane exists so Query Gateway and execution share one query-serving truth, rather than two drifting implementations.

## 13. Storage Evolution and Delayed GC Contract

Broker correctness depends on background compaction and GC honoring token lifetimes.

### 13.1 Why Delayed GC Exists

Once compaction publishes manifest `vN+1`, manifest `vN` is no longer the current head, but valid continuations may still reference it.

Broker therefore relies on a two-step retirement model:

1. logical dereference at manifest publication time,
2. physical deletion only after the delayed-GC lease expires.

### 13.2 Safety Rule

This document does not redefine the physical-deletion contract.
Actual deletion authority remains the canonical core-architecture GC rule, including the `global_safe_ts`- / retention-based horizon and lifecycle-retention constraints.

Broker-specific consequence:

- the configured eviction horizon used by that canonical GC rule must be greater than or equal to the maximum admitted query snapshot token lifetime,
- compaction and GC must therefore preserve pinned manifests and their referenced objects long enough for valid continuation to complete,
- broker logic must never assume a weaker wall-clock-only deletion rule.

### 13.3 Recovery Consequence

Because manifests and tokens are self-contained:

- broker restart does not lose query continuity,
- compaction restart can reconstruct live references from committed manifests and recovery segments,
- pinned but no-longer-current manifests remain readable long enough for valid continuation to complete.

### 13.4 Manifest-First Object Discovery

Broker must never rely on S3 `LIST` to discover live query objects. It opens only:

- `manifest_committed_head.json` on first-page pinning,
- the exact immutable manifest version named by the pinned snapshot,
- objects referenced by that manifest.

### 13.5 Lifecycle Tiers and Fidelity

Broker consumes three canonical governance axes:

| Axis | Axis values | Source of truth | Primary use |
|---|---|---|---|
| Temporal | `DataTier` (`HOT`, `WARM`, `COLD`) | canonical `CollectionSettings.lifecycle_policy` + committed manifest tier annotations | query-time object pruning, row/window placement, compaction policy |
| Structural | `FieldAccessIntent` (`CRITICAL`, `FREQUENT`, `ARCHIVAL`) | canonical `FieldConfig.access_intent` | cache residency weighting, prefetch priority, projection prioritization |
| Quality | `VectorRetrievalIntent` (`HIGH_FIDELITY`, `STANDARD`, `ECONOMY`) | canonical `VectorTypeSpec.retrieval_intent` | compaction fidelity, residual preservation, exact-rescore eligibility, billing |

These three axes are orthogonal. Query Broker must not collapse them into one implicit storage tier:

- temporal placement answers where the row/window lives,
- structural intent answers how aggressively the field should be kept hot,
- quality intent answers how much fidelity the vector path must preserve.
- absent or `UNSPECIFIED` structural intent resolves to effective `FREQUENT`; absent or `UNSPECIFIED` vector quality intent resolves to effective `STANDARD`.

Compaction implications:

- tier transitions may rewrite vector storage formats,
- `HIGH_FIDELITY` fields must preserve exact payload needed for later audit/exact retrieval,
- lower-fidelity cold tiers may drop residual payloads when schema intent allows it,
- broker must respect the resulting manifest-published capabilities at query time rather than guessing.

## 14. Resource Governance and Isolation

### 14.1 Gateway vs Broker Admission Split

Query Gateway remains the public owner of:

- tenant negative caching,
- route resolution,
- public RCU lease checks,
- namespace-scoped quota enforcement.

Gateway admission uses the canonical factorized RCU model owned by `docs/specs/core/search-fabric-architecture.md`.
Broker consumes the resulting admitted RCU envelope and the same effective three-axis tuple, but it does not own or redefine the public estimation formula.

Broker still performs defensive local enforcement for:

- broker queue saturation,
- namespace pool exhaustion,
- shard-local runtime cost overrun,
- hot-shard protection,
- lane-level concurrency caps.

This keeps the public budget owner stable while protecting the cluster from shard-local overload.

### 14.2 Namespace Isolation Pools

Broker execution pools must be partitionable by `namespace_id`.

Goals:

- prevent one whale tenant from monopolizing the broker,
- support exclusive pools for high-spend or latency-sensitive namespaces,
- align scheduler isolation with gateway routing and quota policy.

The same namespace pool assignment must cap:

- maximum subquery fanout,
- maximum concurrent reader RPCs,
- maximum concurrent overlay evaluation jobs,
- maximum in-flight reduction memory.

### 14.3 Runtime Pre-Emption

If actual runtime cost diverges far above the admitted estimate on a hot shard, broker may cancel already-admitted work early and surface canonical throttling failure rather than allowing zombie scans to starve the cluster.
This is a shard-local protection mechanism, not a second tenant-budget or public quota authority.

Recommended broker runtime accounting:

```text
RCU_runtime
  = (W_cpu * cpu_ms)
  + (W_io * object_store_gets)
  + (W_bytes * bytes_scanned)
  + (W_rerank * exact_rerank_docs)
  + (W_overlay * overlay_rows_merged)
```

Broker compares `RCU_runtime` against the gateway-admitted envelope and local shard safety limits. Runtime coefficients are internal policy, but their interpretation must stay aligned with the same temporal/structural/quality inputs used by gateway admission.

Recommended initial trigger:

- actual runtime cost exceeds admitted estimate by `5x`

This is a production policy value, not part of the public API contract.

### 14.4 Compaction Feedback Loop

Broker should emit compaction pressure signals when query plans repeatedly observe:

- more than 20 S3 segments merged for one modality on one request,
- excessive tombstone density,
- repeated cold-tier overfetch caused by fragmentation.

That signal is advisory to the background compute plane; it does not mutate manifests directly.

Broker should also tag those signals with the effective three-axis tuple so background rewrite policy can distinguish:

- `COLD + ARCHIVAL + ECONOMY`: strong candidate for dense quantization and residual drop,
- `WARM/HOT + CRITICAL + HIGH_FIDELITY`: preserve rerank payload and prioritize hotter resident artifacts,
- `COLD + HIGH_FIDELITY`: relocation to slower objects is acceptable, residual dropping is not.

## 15. Failure Semantics and Observability

### 15.1 Required Fail-Closed Behaviors

| Condition | Broker action | Public projection |
|---|---|---|
| malformed token envelope | reject before planning | canonical malformed-token error mapping |
| caller/scope/token binding mismatch | reject before planning | canonical forbidden/error mapping |
| expired query continuation token | reject before planning | canonical query-token expiry mapping |
| no query-serving `READY` lineage | reject before planning | canonical `SF_CATALOG_UNAVAILABLE` branch |
| pinned manifest object missing before token expiry | fail closed; do not substitute latest head | backend-unavailable class failure |
| required subquery timeout or fatal reader failure | cancel sibling lanes; return explicit error | fail closed; never downgrade snapshot semantics |
| shard-local runtime overrun after admission | cancel work | canonical `SF_RATE_LIMITED` |

Internal RPC failure transport rule:

- unary internal RPCs (`gateway -> broker`, `broker -> reader`, readiness lane) MUST use transport `OK` for typed success bodies and non-`OK` gRPC status plus canonical `ErrorStatus` for failures,
- the internal stream hop MUST preserve the canonical `StreamQueryFrame.outcome` contract in-band and use non-`OK` gRPC status plus canonical `ErrorStatus` only for failures that occur before the first stream frame is emitted.

### 15.2 Required Telemetry

Broker must emit:

- per-phase latency aligned to `QueryBrokerPhase` (`pinning`, `planning`, `metadata_selectivity`, `metadata_execution`, `semantic_execution`, `overlay_evaluation`, `fusion`, `aggregation_reduction`, `token_mint`, and `readiness` when that lane runs),
- `QueryBrokerPhase` numeric values are wire identifiers rather than a chronology contract; dashboards/traces must use the enum names because `readiness` is a separate lane and any one execution may observe only a subset of phases,
- any coarser human-readable `dispatch` metric must be derived from the concrete lane-execution phases above; it is not a separate receipt phase name,
- reader-lane timeout and cancellation counters,
- token validation failure counters split by reason,
- manifest pin age and continuation age histograms,
- overlay row count and overlay hit/tombstone replacement counters,
- filter-selectivity estimates vs actual cardinality,
- object-store GET counts and bytes by `DataTier`,
- runtime cost estimate vs actual execution cost.

Logs and traces must not emit raw token payloads or sensitive predicate constants.

### 15.3 Execution Stats Returned Upstream

Broker returns an internal execution receipt to Query Gateway containing:

- CPU time,
- reader RPC counts,
- object-store touches,
- vector work,
- bytes scanned,
- fusion candidate counts,
- metadata partial-aggregation input / output-entry counts,
- facet-reduction candidate / output-entry counts.

Gateway uses that receipt for asynchronous lease reconciliation and estimator tuning.

For unary execution, the internal broker->gateway hop returns this receipt together with the final success body; projected-query success paths also return a separate broker-computed `relational_execution_digest`, which is produced only after projected-route overlay binding has been validated. Successful unary broker responses are not complete without these carriers.
For streamed execution, the broker MUST emit the same telemetry through the final internal `QueryBrokerStreamEvent.terminal_receipt` rather than through a reverse callback or an extra public frame. Query Gateway must ensure the internal broker stream request always carries a validator-enforced non-empty per-attempt `StreamAttemptId.value`, and broker must emit one typed `QueryBrokerStreamExecutionReceipt` keyed by that same value so gateway can deterministically join one final receipt back to one admitted stream attempt for lease reconciliation and estimator tuning. The receipt outcome may be terminal `ok`, terminal `partial`, canonical `error`, or internal-only `cancelled` for broker-classified post-admission downstream/gateway termination. The execution-context branch is stage-aware: attempts that already established `PinnedSnapshot` use the full `receipt` branch carrying `QueryBrokerExecutionReceipt`, while fatal attempts classified before pinning completes use the explicit early-failure branch and therefore do not fabricate a partial pinned snapshot. Receipt emission is required for every broker-classified terminal streamed attempt, including post-frame terminal error and pre-first-frame fatal failure. If the internal broker stream ends before the terminal receipt is observed for that attempt, Query Gateway must perform deterministic fallback reconciliation. The optional public `RequestMeta.correlation_id` may be mirrored into that receipt for logical cross-surface correlation but is not the authoritative execution key.

## 16. Recommended Rust Module Boundaries

The repository model places production query code under `rust/crates/query/*`. A clean implementation split is:

- `query_broker::pinning`
  - token validation bridge
  - readiness lineage resolution
  - manifest pinning
- `query_broker::planner`
  - retrieval-tree inspection
  - selectivity-aware CBO
  - lane DAG construction
- `query_broker::dispatch`
  - reader clients
  - timeout and cancellation orchestration
  - namespace-pool scheduling
- `query_broker::overlay`
  - TiDB-private overlay evaluator
  - in-memory text/vector brute-force helpers
- `query_broker::fusion`
  - score alignment
  - LWW reduction
  - sort and pagination
- `query_broker::tokens`
  - typed token payloads
  - mint/refresh helpers
- `query_broker::observability`
  - metrics, spans, execution receipts

The important point is not the exact file names. The important point is keeping snapshot pinning, planning, dispatch, overlay evaluation, and fusion as distinct units so correctness-sensitive logic is not hidden inside one giant handler.

## 17. Rejected Alternatives

### 17.1 Stateful PIT Registry

Rejected because it violates the stateless broker requirement, complicates restart recovery, and adds a second GC problem for query sessions.

### 17.2 Single Token Family for Snapshot and Page Cursor

Rejected because the canonical contract separates:

- snapshot-lineage state (`search_context.token`)
- page-local cursor state (`next_page.token`)

Combining them would make keep-alive page-coupled and would reintroduce state invalidation ambiguity.

### 17.3 Always-Masked ANN

Rejected because highly selective filters are cheaper and more exact as bounded brute-force over an exact `doc_id` set, while low-selectivity filters are better served by masked ANN traversal.

### 17.4 Broker-Owned Public Quota Authority

Rejected because public quota leases, negative caching, and route-resolution admission already belong to Query Gateway and CCS. Broker needs local protection, not a second public quota owner.

## 18. Summary

Query Broker should be implemented as a stateless, manifest-first, token-driven execution tier that pins one immutable query-serving snapshot lineage, plans filtered metadata/vector execution intelligently, merges committed data with query-visible deltas and optional TiDB-private overlays, and emits deterministic paginated results.

The design relies on a strict split of concerns:

- Query Gateway owns public admission and route resolution.
- Query Broker owns snapshot reconstruction, physical planning, and execution.
- Readers own modality-specific retrieval.
- Background compute owns compaction and delayed physical deletion.

If those boundaries are preserved, the system can provide:

- strict fail-closed visibility semantics,
- scalable horizontal broker replacement,
- restart-safe continuation without session registries,
- TiDB immediate visibility on the trusted TiDB query path,
- and S3-backed correctness even under compaction and head advancement.
