# CHANNEL_CONTRACT (v1, frozen)

A Channel pairs a client-owned input atom (a query buffer) with server-owned result
content, across the network boundary, without letting stale results be treated as
current.

## The definition that matters

> **A Channel is not merely a version stamp. A Channel requires the server render path
> to enforce monotonicity; otherwise stale results can still enter the DOM.**

This is the frozen upgrade from the earlier "version stamp" framing. The spike proved
the distinction empirically (guard on vs off).

## Contract

A conforming Channel MUST:

1. **version-through-render.** The client stamps each query emission with a
   monotonically increasing version. The server stamps each result render with the
   version it answers, in the rendered markup (so the version round-trips through the
   render, not just the event).
2. **server-enforced monotonicity (the construction-grade requirement).** The server
   render path MUST refuse to render results for a superseded version. This is a
   **server obligation** — the guarantee lives in server-side logic, not in a stamp.
3. **client-side stale detection as a secondary guard only.** The client MAY compare
   rendered version to last-emitted version and mark staleness, but this is
   **convention, not construction**: without requirement 2, stale results still enter
   the DOM and are only flagged after the fact.
4. **not emit during IME composition.** No query snapshot is emitted while a
   composition session is active on the input; exactly one versioned emission may
   follow `compositionend`. *(The rule is frozen; real-device IME verification is a
   `BACKLOG.md` item — the current evidence is synthetic events only.)*

## What the harness can and cannot verify

The harness verifies that the **reference fixture** upholds the contract (server drops
superseded versions; client detects the convention-only case). It **cannot** verify
that an arbitrary application upholds requirement 2 — a developer who omits the server
guard silently drops to convention-only correctness. This limit is intrinsic: the
guarantee is a server obligation, not a framework feature.

## Why it stays in the kernel

Combobox correctness is impossible without it, and the version must round-trip through
the server render — coupling it to the rendering pipeline in a way generic
application-level request/response versioning is not. It is the kernel's least
ergonomic member (the guarantee is developer-upheld), but not reducible to Cursor or
Delegation.

## The shipped helper (v1; upgrades convention → construction)

The early caveat — "the guarantee is developer-upheld" — is now closed by library
code in the v1 surface: `LiveInteractionContracts.Channel` ships the server-side
monotonic guard.

- `Channel.new/0` → put in an assign.
- `Channel.note_query(ch, v)` → call on every incoming versioned query.
- `Channel.accept(ch, v)` → THE guard, called on the render path: `{:ok, ch}` renders
  and stamps `rendered_version`; `{:stale, ch}` refuses (counted in `stale_drops`).

A LiveView that routes every result render through `accept/2` gets prevention **by
construction**; the component stamps `data-version={rendered_version}` so the client
can still detect (as telemetry, never authority) if an app bypasses the guard. The
combobox reference suite asserts both directions: guard-on drops the stale version
server-side; the fixture's guard-off negative control shows the stale render reaching
the DOM with only client telemetry flagging it — i.e. the helper is precisely what
buys the construction-grade guarantee.

*Evidence: `reports/COMBOBOX_SPIKE_REPORT.md` (test 2, and tests 3–5 for Cursor × Channel
composition).*
