# Semantic Batch Writer

Status: Frozen architecture note
Owner: Search Platform Architecture

## 1. Scope

This document explains the execution design of `Semantic Batch Writer` (SBW), the semantic/vector batch writer named by the canonical architecture for admitted `MutationBatch` work.

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_batch_writer.proto`
- ingest/query semantics: `docs/specs/core/search-fabric-api.md`
- system topology and state ownership: `docs/specs/core/search-fabric-architecture.md`
- canonical error registry: `docs/specs/core/error-codes.md`

It must not redefine public `MutationBatch`, `MutationAck`, vector-schema semantics, snapshot-session RPC semantics, visibility boundaries, or durable replay-anchor ownership defined by those documents.

## 2. Canonical Position in the Write Path

SBW is one write-plane executor in the canonical path:

```text
Ingest Gateway
  -> Ingest Broker
  -> Write Planner
  -> Commit Coordinator
  -> Semantic Batch Writer
  -> Visibility Barrier
  -> Manifest Publisher
```

SBW receives already-admitted semantic work after:
- request authz, schema, truth-mode, and replay checks are complete,
- `Commit Coordinator` has already preserved or injected canonical `commit_ts`,
- canonical batch identity, stream identity, and source order have already been fixed,
- `Write Planner` has already chosen the semantic batch lane for the admitted work.

SBW does not:
- accept source-facing API requests directly,
- own `StartSnapshotSync -> UploadSnapshotChunks -> CommitSnapshotSync`,
- invent a second durable batch identity or ACK contract,
- define public vector-schema semantics beyond the admitted collection schema,
- mutate manifests or publish visibility by itself,
- own `wal_replay_start_anchor` / `wal_durable_anchor`,
- own background compaction, GC, or cross-collection lifecycle orchestration.

If a workload needs sessionized offline import with upload/cutover semantics, it belongs to the canonical snapshot-sync lifecycle and `docs/architecture/snapshot-import-worker.md`, not to this note.

## 3. Contract Binding

SBW executes against the canonical admitted ingest carriers rather than writer-local envelopes:
- batch scope: `MutationBatch`
- row scope: `MutationEntry` and `RowMutation`
- write policy: `IngestPolicy`
- progress/reporting: `MutationAck.success.item_outcomes`, `MutationAck.success.rejection_context`, and `MutationAck.success.applied_watermarks`
- stream identity: `MutationBatch.stream_identity`
- replay identity: `batch_id` plus `batch_fingerprint`

Coordinator-private slicing or internal build-partitioning may exist as execution units, but those units remain subordinate to one original admitted batch:
- original `entry_index` and `source_seq` mapping must be preserved,
- canonical winner order stays `(commit_ts, source_partition, source_epoch, source_seq)`,
- downstream still owes exactly one truthful `MutationAck` per original admitted batch.

`INGEST_MODE_BATCH` on `StreamMutations` is distinct from the snapshot-sync RPC lifecycle:
- `MutationBatch` batch ingest is `MutationAck`-based and source-order preserving,
- snapshot sync is session/chunk/cutover based and uses `SnapshotChunkAck` plus `CommitSnapshotSync`.

## 4. Responsibilities

SBW is responsible for semantic/vector batch execution only:

1. Consume already-admitted semantic row-bearing work selected for batch execution.
2. Build immutable semantic search artifacts from that admitted work.
3. Persist immutable artifacts to object storage.
4. Emit produced key-range coverage plus `max_commit_ts` metadata needed for downstream conflict checks and publish.
5. Emit completion signals needed by `Visibility Barrier` and downstream `MutationAck` reconstruction.

SBW is not responsible for:
- metadata/text execution,
- public request validation or schema-latest lookup with CCS,
- public compatibility response shaping at the gateway,
- snapshot upload/session leases or resume tokens,
- manifest CAS publication,
- reader-side visibility waiting,
- compaction rewrites after publish,
- cross-collection storage/layout coupling.

## 5. Execution Model

### 5.1 Input Ordering

SBW executes admitted row-bearing work in the canonical order:
- primary key: `commit_ts`
- tie-breaks: `source_partition`, `source_epoch`, `source_seq`

That ordering is already defined by the canonical ingest contract and must not be replaced by writer-local arbitration rules such as vector-partition ownership, graph-build worker order, or post-sort neighborhood merge order.

### 5.2 Internal Chunking and Semantic Artifact Build

SBW may internally chunk one admitted batch for resource management or artifact sizing, for example to:
- bound peak memory,
- build multiple immutable semantic segment objects,
- pipeline encoding, graph build, and upload preparation.

Those internal chunks are execution details only. They must not:
- change canonical apply order,
- become independent public ACK units,
- reorder item outcomes relative to original batch entry order,
- require gateways or callers to reason about writer-local chunk IDs,
- weaken the original admitted batch atomicity contract.

Atomicity remains owned by the original admitted `MutationBatch`:
- under `ATOMICITY_POLICY_ALL_OR_NOTHING`, SBW may build or stage multiple semantic artifacts internally, but no row from the batch may become visible before the entire original batch succeeds.
- under `ATOMICITY_POLICY_FAIL_FAST_PARTIAL`, any incremental publish must remain a canonical accepted prefix only; SBW must not publish rows after an unresolved earlier position.

Unlike `Semantic Stream Writer`, SBW does not define a near-real-time delta-visibility path:
- `INGEST_MODE_BATCH` bypasses NRT delta-store pressure and keeps data query-invisible until downstream manifest publication,
- semantic batch execution therefore prepares immutable publish candidates only; it does not create an independent early-visible semantic surface.

SBW may use bounded RAM/NVMe spill while preparing immutable artifacts, but spill state is disposable:
- it is not a durable authority,
- it must not become a second replay source,
- crash recovery must remain pointer-driven from canonical published state and idempotent object-store artifacts.

### 5.3 Semantic Artifact Constraints

SBW may use semantic-batch-specific build techniques such as:
- immutable graph construction,
- quantized payload generation,
- optional residual or higher-fidelity rerank payload preservation,
- doc-key and commit-metadata packing,
- tombstone-sidecar or visibility-bitset materialization.

Those techniques are implementation details only. They must remain subordinate to the admitted vector contract:
- `VectorTypeSpec.dimensions`
- `VectorTypeSpec.element_type`
- `VectorTypeSpec.distance_metric`
- `VectorSearchTuningSpec`
- `VectorRetrievalIntent`

In particular, SBW must not:
- assume one universal normalization rule across all collections,
- force one quantization format for every collection regardless of admitted schema/tuning,
- drop rerank-capable payload required by the effective retrieval-intent and quality contract,
- expose implementation-specific graph or encoding terminology as if it were part of the public ingest contract.

### 5.4 Durability, Publish Readiness, and ACK Boundary

In the current public contract:
- `INGEST_MODE_BATCH` is admitted only with `visibility=VISIBILITY_POLICY_WAIT_FOR_VISIBLE`,
- `INGEST_MODE_BATCH` is admitted only with `durability=DURABILITY_POLICY_INGEST_ACK`,
- the batch lane does not expose a separate pre-visibility durable ACK anchor.

Therefore SBW may report internal completion only after the immutable semantic artifacts and publish-coverage metadata required for the admitted batch scope are durably persisted.

Public success still requires downstream completion of:
1. touched-modality internal persistence,
2. `Visibility Barrier` publish-readiness,
3. `Manifest Publisher` immutable manifest write and committed-pointer CAS publish.

SBW does not by itself satisfy a public durable or read-visible ACK boundary.
Any internal publish granularity must remain subordinate to the same atomicity rule described above; writer-local chunks are not public commit units.

### 5.5 Conflict and Coverage Reporting

SBW must report produced coverage in the canonical batch-conflict vocabulary:
- produced key ranges,
- relevant `max_commit_ts`,
- admitted batch lineage needed for downstream conflict checks.

SBW must not:
- move `wal_replay_start_anchor`,
- invent collection-global `snapshot_ts` replacement rules,
- rewrite already-published streaming objects during batch publish,
- introduce source-kind-specific winner rules where canonical conflict policy is already commit-order based.

Those concerns remain owned by the canonical manifest/WAL model, the batch conflict rules, or the snapshot-session cutover semantics outside this note.

## 6. Watermarks and ACK Reconstruction

SBW must preserve the canonical one-batch / one-ACK model.

For admitted row-bearing work:
- `item_outcomes[]` remains complete for every admitted entry,
- `rejection_context` remains tied to the first rejected canonical cursor when failures occur,
- `acked_cursor` remains reconstructible against the original admitted source order,
- `applied_watermarks` remains the downstream collection frontier after the admitted write scope completes.

For semantic execution, the relevant modality frontier is:
- `applied_ts_semantic_search`

Because of that contract, SBW must not describe itself as an asynchronous background job that returns early while later ACK reconstruction happens out of band.

## 7. Scheduling and Resource Isolation

SBW is a write-plane executor, not a `Background Compute Coordinator` authority.

Implementations may still use lane-specific placement or larger workers for semantic batch execution, for example:
- dedicated pools for whale tenants,
- per-collection affinity,
- separate queue budgets from streaming writers,
- isolated CPU or memory budgets for graph build versus quantization stages.

Those are scheduling details only. They must not:
- redefine plane ownership,
- turn the user-facing batch ACK path into a background job contract,
- require a second public scheduler or job handle,
- create durable writer-local partition identities that leak into recovery or publish semantics.

## 8. Storage and Spill Behavior

Durable identity remains collection-scoped and manifest-scoped.

SBW must not introduce:
- ad hoc public scratch prefixes,
- cross-collection shared immutable objects,
- manifest append-in-place semantics,
- writer-owned rewrites of already-published semantic objects outside canonical background compaction.

If temporary staging is used internally, it must remain:
- subordinate to the canonical collection/object-store layout,
- disposable before publish,
- safe to orphan-clean without becoming visibility truth.

## 9. Failure Handling

SBW failure handling must preserve owner boundaries:
- upstream schema fence validation stays owned by the canonical write-admission contract; SBW must not replace it with a writer-local "latest READY generation" rule,
- stale or conflicting schema/version conditions remain canonical `SF_VERSION_CONFLICT` behavior, not `SF_OWNERSHIP_CONFLICT`,
- write-side `wait_for_visible` expiry remains a stream-level retryable `SF_BACKEND_UNAVAILABLE` failure on `MutationAck.error`,
- orphan unpublished semantic artifacts are cleanup concerns for lifecycle/GC flows, not visibility truth.

Crash recovery must be idempotent and pointer-driven:
- read committed manifest lineage,
- trust only published state plus idempotent persisted artifacts for the same admitted batch lineage,
- ignore unpublished or unreferenced leftovers.

Implementation tuning may still vary for semantic segment sizing, internal chunk sizing, graph-build parallelism, spill thresholds, quantization layout selection, and tenant scheduling budgets for bulk semantic rebuilds, but any such choice must preserve the canonical ingest, ordering, ACK, vector-schema, durability, and visibility contracts described above.

## 10. Rust Implementation Notes

The checked-in Rust implementation lives in `rust/crates/semantic-batch-writer` and is hosted by
`rust/bins/sf-semantic-batch-writer`.

The first production slice implements:
- `SemanticBatchWriterInternalService.ApplySemanticBatchSlice` with `BATCH` / `INGEST_ACK` /
  `WAIT_FOR_VISIBLE` validation, per-entry outcomes, idempotent replay classification, optimistic
  `expected_commit_ts` checks, and rollback of in-memory apply state when durability reporting fails.
- Immutable semantic segment v2 objects, one direct query-serving object per F32 vector field, with
  deterministic document ordering, deterministic linear adjacency, scalar/list row-field payloads
  for sort/filter projection, and whole-document delete tombstone rows.
- A slice-level immutable produced-artifact descriptor object. The descriptor is the durable anchor
  reported to Visibility Barrier and contains the direct semantic segment artifacts that Manifest
  Publisher should commit into the manifest. The RPC response exposes one descriptor-level logical
  semantic artifact with non-overlapping document coverage; per-vector-field segment partitioning
  stays below that boundary.
- Pointer-driven apply-state prewarm from the latest committed manifest. Recovery trusts only the
  committed manifest pointer and the exact semantic segment objects referenced by that manifest.
- Idempotent object-store writes. Replayed object keys are accepted only when the existing bytes
  match the expected BLAKE3 checksum.

This implementation intentionally fails closed on live vector-field removal, for example an upsert or
patch that would leave the document alive while removing a previously indexed vector field. Whole-row
delete tombstones are supported, but field-level vector tombstones need a schema-aware representation
that recovery and query readers can distinguish from row deletes. Until that format is added, failing
closed prevents stale vectors from being committed silently.
