LiveInteractionContracts.Channel (live_interaction_contracts v1.0.0)

Copy Markdown View Source

Server-side monotonic guard for versioned async query/result exchange — the piece that upgrades the Channel contract from convention to construction (CHANNEL_CONTRACT.md; combobox spike verdict 1).

A client emits queries stamped with a monotonically increasing version. Results may complete out of order (async work, network). The rule: the server render path must refuse to render results for a superseded version. A client-side version check alone only detects staleness after the DOM was already wrong; this guard prevents it from ever reaching the render.

Usage in a LiveView

def mount(_, _, socket), do: {:ok, assign(socket, channel: Channel.new(), results: [])}

def handle_event("query", %{"q" => q, "v" => v}, socket) do
  channel = Channel.note_query(socket.assigns.channel, v)
  # ... compute or start async work for {q, v} ...
  {:noreply, assign(socket, channel: channel)}
end

# When a result set for version v is ready to render (sync or async):
case Channel.accept(socket.assigns.channel, v) do
  {:ok, channel} -> assign(socket, channel: channel, results: results)
  {:stale, channel} -> assign(socket, channel: channel)   # dropped, by construction
end

The component stamps the rendered version into the DOM (data-version={@channel.rendered_version}) so the client can detect — as telemetry, not authority — if a stale render ever slips through.

Summary

Functions

The guard. Ask whether a result set for version v may render.

Fresh channel state (put it in an assign).

Record that a query stamped v was seen. Call on every incoming query event. Accepts integer or numeric-string versions (LiveView params are strings).

Types

t()

@type t() :: %LiveInteractionContracts.Channel{
  rendered_version: non_neg_integer(),
  stale_drops: non_neg_integer(),
  version_seen: non_neg_integer()
}

Functions

accept(ch, v)

The guard. Ask whether a result set for version v may render.

Returns {:ok, channel} (render it; the channel records it as the rendered version) or {:stale, channel} (a newer query has been seen — do NOT assign the results; the drop is counted).

new()

Fresh channel state (put it in an assign).

note_query(ch, v)

Record that a query stamped v was seen. Call on every incoming query event. Accepts integer or numeric-string versions (LiveView params are strings).