# `Replicant.AssemblerServer`
[🔗](https://github.com/baselabs/replicant/blob/v0.3.1/lib/replicant/assembler_server.ex#L1)

The serial process shell over the pure `Replicant.Assembler` (spec §4). Receives
**decoded** pgoutput messages from `Replicant.Connection` (which decodes behind
the value-free boundary and never applies the sink), assembles transactions, and
applies the sink **synchronously** — blocking THIS process, off the Connection's
keepalive path. On a durable sink commit (or a watermark skip) it messages the
Connection so the ack advances asynchronously; on a fail-closed condition
(destructive schema change, sink WRITE fault, an unidentifiable-relation row) it
halts the whole pipeline permanently (spec §6/§9).

It is a single serial `GenServer` (not a Task per transaction) so transactions
apply strictly in commit order — the correctness baseline of synchronous
per-transaction delivery.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `deliver_snapshot_chunk`

```elixir
@spec deliver_snapshot_chunk(GenServer.server(), Replicant.SnapshotWindow.chunk()) ::
  :ok | {:error, :window_reset} | {:error, :table_discarded}
```

Hand a read chunk to the applier. The reply is DEFERRED while max_pending_chunks are buffered.
`{:error, :window_reset}` on a reconnect; `{:error, :table_discarded}` on a contention discard
(drop-cap breach / a PK-less table's concurrent write) — the reader re-reads from durable progress.

# `finish_snapshot_table`

```elixir
@spec finish_snapshot_table(GenServer.server(), String.t(), non_neg_integer()) ::
  :ok | {:error, :window_reset} | {:error, :table_discarded}
```

A PK-less table's whole-read BARRIER (spec §6.4): after the reader streams all of a keyless
table's provisional batches, it calls this to block until those batches have APPLIED (converged
past any late contention) — replying `:ok` — or the table was contention-discarded — replying
`{:error, :table_discarded}` so the reader redoes the whole table. Without this barrier a
single-batch keyless read can complete before a late-arriving concurrent write discards its
still-pending batch, silently losing the rows.

`epoch` is the window GENERATION the reader captured at `open_snapshot_window/2`. If a reconnect
reset re-seated the window (bumped the epoch) since the reader opened this table, its provisional
batches were WIPED (not applied): the barrier MUST reply `{:error, :window_reset}` — never a
spurious `:ok` (which would mark a never-delivered table done → data loss). The stale-generation
check comes FIRST because a reset also clears `discarded`/`pending`, so a wiped table otherwise
reads as clean. `{:error, :window_reset}` also fires when a reconnect releases a deferred barrier.

# `open_snapshot_window`

```elixir
@spec open_snapshot_window(GenServer.server(), String.t()) ::
  {:ok, non_neg_integer()} | {:error, :window_reset | :table_discarded}
```

Open the drop-set tracking window for a table BEFORE the reader captures LW
(spec §2 R1). The reply is DEFERRED while the assembler-local in-flight estimate
(frontier − last applied LSN) exceeds max_inflight_lag ÷ 2 — the pacing gate
(spec §4 R2): stream drain gets priority, chunks fill idle capacity.

Replies `{:ok, epoch}` — the window GENERATION the reader now operates under. The
keyless reader threads this back to `finish_snapshot_table/3` so the barrier can
reject a stale generation (a reconnect reset bumped the epoch since the reader
opened → its batches were WIPED, not applied — spec §4/§6.4, the 85672f1
sender-tags-epoch discipline). Replies `{:error, :table_discarded}` if the table was
contention-discarded (reader re-reads); `{:error, :window_reset}` if a reconnect
released the deferred open.

# `start_link`

```elixir
@spec start_link(keyword()) :: GenServer.on_start()
```

# `via`

```elixir
@spec via(String.t()) :: {:via, module(), term()}
```

The Registry via-name a pipeline's AssemblerServer registers under.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
